DOGFATHER UNIVERSE: erste lauffähige Website + Bewerbungs-Postfach + Hosting-Vorbereitung
This commit is contained in:
@@ -0,0 +1,450 @@
|
||||
/* =====================================================================
|
||||
DOGFATHER UNIVERSE — main.css
|
||||
Gemeinsames Design-System für alle drei Welten (Dogfather / HasiDog /
|
||||
Manager Dogfather). Farben kommen aus den theme-*.css Dateien über
|
||||
CSS-Variablen — dieses File ist bewusst themen-neutral.
|
||||
===================================================================== */
|
||||
|
||||
/* ---------- Reset ---------- */
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
html, body { margin: 0; padding: 0; }
|
||||
img, svg { max-width: 100%; display: block; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font: inherit; }
|
||||
ul { list-style: none; margin: 0; padding: 0; }
|
||||
|
||||
/* ---------- Fallback-Theme (Dogfather) — wird von theme-*.css überschrieben ---------- */
|
||||
:root {
|
||||
--bg: #0b0d10;
|
||||
--bg-alt: #12151a;
|
||||
--card-bg: #171b21;
|
||||
--text: #f3f5f7;
|
||||
--text-muted: #9aa4b2;
|
||||
--border: #262c35;
|
||||
--accent: #8fd9ea; /* Babyblau */
|
||||
--accent-2: #8b5cf6; /* Lila */
|
||||
--accent-contrast: #0b0d10;
|
||||
--radius: 14px;
|
||||
--font-head: "Poppins", "Segoe UI", system-ui, sans-serif;
|
||||
--font-body: "Inter", "Segoe UI", system-ui, sans-serif;
|
||||
--shadow: 0 10px 30px rgba(0,0,0,.35);
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: var(--font-body);
|
||||
line-height: 1.6;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-family: var(--font-head);
|
||||
line-height: 1.2;
|
||||
margin: 0 0 .5em;
|
||||
font-weight: 700;
|
||||
}
|
||||
h1 { font-size: clamp(2rem, 5vw, 3.2rem); }
|
||||
h2 { font-size: clamp(1.5rem, 3.5vw, 2.3rem); }
|
||||
h3 { font-size: 1.3rem; }
|
||||
p { margin: 0 0 1em; color: var(--text-muted); }
|
||||
.lead { font-size: 1.15rem; color: var(--text); }
|
||||
|
||||
.container {
|
||||
width: min(1180px, 92vw);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
section { padding: clamp(3rem, 6vw, 5.5rem) 0; }
|
||||
.section-tight { padding: clamp(1.5rem, 3vw, 2.5rem) 0; }
|
||||
|
||||
.eyebrow {
|
||||
display: inline-block;
|
||||
font-size: .78rem;
|
||||
letter-spacing: .14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
margin-bottom: .8em;
|
||||
}
|
||||
|
||||
/* ---------- Nebel/Partikel Hintergrund ---------- */
|
||||
.fog-layer {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.fog-layer span {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, var(--accent) 0%, transparent 70%);
|
||||
opacity: .08;
|
||||
filter: blur(4px);
|
||||
animation: drift 26s linear infinite;
|
||||
}
|
||||
@keyframes drift {
|
||||
0% { transform: translateY(0) translateX(0); }
|
||||
50% { transform: translateY(-40px) translateX(20px); }
|
||||
100% { transform: translateY(0) translateX(0); }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fog-layer span { animation: none; }
|
||||
}
|
||||
|
||||
/* ---------- Header / Navigation ---------- */
|
||||
header.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
background: rgba(11,13,16,.82);
|
||||
backdrop-filter: blur(10px);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.nav-bar {
|
||||
width: min(1180px, 92vw);
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: .9rem 0;
|
||||
}
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .6rem;
|
||||
font-family: var(--font-head);
|
||||
font-weight: 800;
|
||||
font-size: 1.15rem;
|
||||
letter-spacing: .02em;
|
||||
}
|
||||
.brand .heart { color: var(--accent); }
|
||||
.brand small {
|
||||
display: block;
|
||||
font-size: .6rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: .1em;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1.6rem;
|
||||
}
|
||||
.nav-group { position: relative; }
|
||||
.nav-group > button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .3rem;
|
||||
padding: .5rem 0;
|
||||
}
|
||||
.nav-group > button:hover, .nav-group.open > button { color: var(--accent); }
|
||||
.nav-flat { font-weight: 600; padding: .5rem 0; }
|
||||
.nav-flat:hover, .nav-flat.active { color: var(--accent); }
|
||||
.nav-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
min-width: 220px;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: .5rem;
|
||||
box-shadow: var(--shadow);
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
}
|
||||
.nav-group.open .nav-dropdown { display: flex; }
|
||||
.nav-dropdown a {
|
||||
padding: .55rem .7rem;
|
||||
border-radius: 8px;
|
||||
font-size: .92rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.nav-dropdown a:hover, .nav-dropdown a.active { background: var(--bg-alt); color: var(--accent); }
|
||||
|
||||
.nav-cta {
|
||||
padding: .55rem 1.1rem;
|
||||
border-radius: 999px;
|
||||
background: var(--accent);
|
||||
color: var(--accent-contrast);
|
||||
font-weight: 700;
|
||||
font-size: .88rem;
|
||||
}
|
||||
|
||||
.nav-toggle {
|
||||
display: none;
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
color: var(--text);
|
||||
padding: .5rem .7rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 880px) {
|
||||
.nav-toggle { display: block; }
|
||||
.nav-links {
|
||||
position: fixed;
|
||||
inset: 62px 0 0 0;
|
||||
background: var(--bg);
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
padding: 1.2rem 6vw;
|
||||
gap: .3rem;
|
||||
transform: translateY(-110%);
|
||||
transition: transform .25s ease;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.nav-links.open { transform: translateY(0); }
|
||||
.nav-group > button { width: 100%; justify-content: space-between; padding: .9rem 0; border-bottom: 1px solid var(--border); }
|
||||
.nav-flat { display: block; padding: .9rem 0; border-bottom: 1px solid var(--border); }
|
||||
.nav-dropdown { position: static; transform: none; box-shadow: none; width: 100%; }
|
||||
.nav-group.open .nav-dropdown { display: flex; }
|
||||
.nav-cta { align-self: flex-start; margin: 1rem 0; }
|
||||
}
|
||||
|
||||
/* ---------- Buttons ---------- */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .5rem;
|
||||
padding: .8rem 1.5rem;
|
||||
border-radius: 999px;
|
||||
font-weight: 700;
|
||||
font-size: .95rem;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: transform .15s ease, opacity .15s ease;
|
||||
}
|
||||
.btn:hover { transform: translateY(-2px); }
|
||||
.btn-primary { background: var(--accent); color: var(--accent-contrast); }
|
||||
.btn-secondary { background: var(--accent-2); color: #fff; }
|
||||
.btn-outline { background: transparent; border-color: var(--border); color: var(--text); }
|
||||
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.btn-row { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 1.5rem; }
|
||||
|
||||
/* ---------- Hero ---------- */
|
||||
.hero {
|
||||
position: relative;
|
||||
padding: clamp(4rem, 10vw, 7rem) 0;
|
||||
text-align: center;
|
||||
}
|
||||
.hero .worlds {
|
||||
margin: .6rem 0 1.2rem;
|
||||
color: var(--text-muted);
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
.hero-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1.2rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
@media (max-width: 760px) { .hero-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
/* ---------- Cards ---------- */
|
||||
.card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.6rem;
|
||||
box-shadow: var(--shadow);
|
||||
height: 100%;
|
||||
transition: border-color .15s ease, transform .15s ease;
|
||||
}
|
||||
.card:hover { border-color: var(--accent); transform: translateY(-3px); }
|
||||
.card h3 { margin-bottom: .4rem; }
|
||||
.card .tag {
|
||||
display: inline-block;
|
||||
font-size: .72rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .08em;
|
||||
color: var(--accent);
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: 999px;
|
||||
padding: .15rem .6rem;
|
||||
margin-bottom: .8rem;
|
||||
}
|
||||
|
||||
.grid { display: grid; gap: 1.4rem; }
|
||||
.grid-2 { grid-template-columns: repeat(2, 1fr); }
|
||||
.grid-3 { grid-template-columns: repeat(3, 1fr); }
|
||||
.grid-4 { grid-template-columns: repeat(4, 1fr); }
|
||||
@media (max-width: 980px) { .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 620px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }
|
||||
|
||||
/* ---------- Profil-Karten (Team/Creator) ---------- */
|
||||
.profile-card {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
padding: 1.6rem 1.2rem;
|
||||
}
|
||||
.avatar {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
border-radius: 50%;
|
||||
margin: 0 auto .9rem;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent-2));
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--font-head);
|
||||
font-weight: 800;
|
||||
font-size: 1.6rem;
|
||||
color: var(--accent-contrast);
|
||||
overflow: hidden;
|
||||
}
|
||||
.avatar img { width: 100%; height: 100%; object-fit: cover; }
|
||||
.profile-card .role { color: var(--accent); font-weight: 600; font-size: .85rem; margin-bottom: .6rem; }
|
||||
.profile-card .quote { font-style: italic; font-size: .9rem; }
|
||||
.profile-card .socials { display: flex; justify-content: center; gap: .6rem; margin-top: .8rem; }
|
||||
.profile-card .socials a {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: .35rem .8rem;
|
||||
font-size: .78rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.profile-card .socials a:hover { color: var(--accent); border-color: var(--accent); }
|
||||
|
||||
/* ---------- Placeholder-Bild-Boxen (bis echte Fotos da sind) ---------- */
|
||||
.img-placeholder {
|
||||
aspect-ratio: 4 / 3;
|
||||
border-radius: var(--radius);
|
||||
background: repeating-linear-gradient(135deg, var(--bg-alt), var(--bg-alt) 10px, var(--card-bg) 10px, var(--card-bg) 20px);
|
||||
border: 1px dashed var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-muted);
|
||||
font-size: .8rem;
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* ---------- Timeline (Zeitreise) ---------- */
|
||||
.timeline { position: relative; margin: 2rem 0; padding-left: 2rem; border-left: 2px solid var(--border); }
|
||||
.timeline-item { position: relative; padding-bottom: 2.4rem; }
|
||||
.timeline-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -2.45rem;
|
||||
top: .2rem;
|
||||
width: 14px; height: 14px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 0 4px var(--bg);
|
||||
}
|
||||
.timeline-item .date { color: var(--accent); font-weight: 700; font-size: .85rem; }
|
||||
|
||||
/* ---------- Gallery / Lightbox ---------- */
|
||||
.filter-bar { display: flex; gap: .6rem; flex-wrap: wrap; margin-bottom: 1.6rem; }
|
||||
.filter-btn {
|
||||
border: 1px solid var(--border);
|
||||
background: transparent;
|
||||
color: var(--text-muted);
|
||||
padding: .5rem 1rem;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
font-size: .85rem;
|
||||
}
|
||||
.filter-btn.active, .filter-btn:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.gallery-item { cursor: pointer; }
|
||||
.lightbox {
|
||||
position: fixed; inset: 0; background: rgba(0,0,0,.85);
|
||||
display: none; align-items: center; justify-content: center; z-index: 200; padding: 5vh 5vw;
|
||||
}
|
||||
.lightbox.open { display: flex; }
|
||||
.lightbox img, .lightbox .img-placeholder { max-height: 85vh; max-width: 90vw; }
|
||||
.lightbox-close {
|
||||
position: absolute; top: 20px; right: 30px; color: #fff; font-size: 2rem; cursor: pointer; background: none; border: none;
|
||||
}
|
||||
|
||||
/* ---------- Formulare ---------- */
|
||||
form.app-form { display: grid; gap: 1rem; max-width: 640px; }
|
||||
.field { display: grid; gap: .4rem; }
|
||||
.field label { font-weight: 600; font-size: .9rem; }
|
||||
.field input, .field select, .field textarea {
|
||||
background: var(--bg-alt);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: .75rem .9rem;
|
||||
color: var(--text);
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
.field textarea { min-height: 120px; resize: vertical; }
|
||||
.field .error-msg { color: #ff6b6b; font-size: .8rem; display: none; }
|
||||
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: #ff6b6b; }
|
||||
.field.has-error .error-msg { display: block; }
|
||||
.form-status { padding: .9rem 1.1rem; border-radius: 10px; font-size: .9rem; display: none; }
|
||||
.form-status.show { display: block; }
|
||||
.form-status.success { background: rgba(143,217,234,.12); border: 1px solid var(--accent); }
|
||||
.form-status.error { background: rgba(255,107,107,.1); border: 1px solid #ff6b6b; }
|
||||
.tabs { display: flex; gap: .6rem; margin-bottom: 1.8rem; flex-wrap: wrap; }
|
||||
.tab-btn {
|
||||
border: 1px solid var(--border); background: var(--card-bg); color: var(--text-muted);
|
||||
padding: .7rem 1.3rem; border-radius: 999px; cursor: pointer; font-weight: 600;
|
||||
}
|
||||
.tab-btn.active { border-color: var(--accent); color: var(--accent); }
|
||||
.tab-panel { display: none; }
|
||||
.tab-panel.active { display: block; }
|
||||
|
||||
/* ---------- Footer ---------- */
|
||||
footer.site-footer {
|
||||
border-top: 1px solid var(--border);
|
||||
padding: 3rem 0 2rem;
|
||||
margin-top: 4rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 2rem; }
|
||||
@media (max-width: 760px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
|
||||
.footer-grid h4 { font-size: .85rem; text-transform: uppercase; letter-spacing: .08em; color: var(--accent); }
|
||||
.footer-grid ul { display: grid; gap: .5rem; }
|
||||
.footer-grid a { color: var(--text-muted); font-size: .9rem; }
|
||||
.footer-grid a:hover { color: var(--accent); }
|
||||
.footer-bottom {
|
||||
margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border);
|
||||
display: flex; justify-content: space-between; flex-wrap: wrap; gap: .8rem;
|
||||
font-size: .8rem; color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ---------- Badges / Hierarchy ---------- */
|
||||
.badge { display: inline-block; font-size: .7rem; text-transform: uppercase; letter-spacing: .06em; padding: .2rem .6rem; border-radius: 999px; background: var(--bg-alt); border: 1px solid var(--border); color: var(--text-muted); }
|
||||
.badge.rank-1 { border-color: var(--accent); color: var(--accent); }
|
||||
|
||||
/* ---------- QR ---------- */
|
||||
.qr-card { text-align: center; }
|
||||
.qr-card .img-placeholder { width: 160px; height: 160px; margin: 0 auto 1rem; aspect-ratio: auto; }
|
||||
|
||||
/* ---------- Utility ---------- */
|
||||
.text-center { text-align: center; }
|
||||
.mt-0 { margin-top: 0; }
|
||||
.muted { color: var(--text-muted); }
|
||||
.small { font-size: .85rem; }
|
||||
.todo-note {
|
||||
border: 1px dashed var(--accent-2);
|
||||
background: rgba(139,92,246,.08);
|
||||
padding: 1rem 1.2rem;
|
||||
border-radius: 10px;
|
||||
font-size: .85rem;
|
||||
color: var(--text-muted);
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
.todo-note strong { color: var(--accent-2); }
|
||||
@@ -0,0 +1,16 @@
|
||||
/* =====================================================================
|
||||
theme-dogfather.css — Welt 1: DOGFATHER (Streamer/Person)
|
||||
Babyblau / Schwarz / Lila — die "Core"-Welt, sitzt auch als globaler
|
||||
Default darunter (main.css Fallback = diese Werte).
|
||||
===================================================================== */
|
||||
:root[data-theme="dogfather"] {
|
||||
--bg: #0a0c10;
|
||||
--bg-alt: #12151b;
|
||||
--card-bg: #171b22;
|
||||
--text: #f4f6f8;
|
||||
--text-muted: #9aa4b3;
|
||||
--border: #262b35;
|
||||
--accent: #8fd9ea; /* Babyblau — Markenfarbe */
|
||||
--accent-2: #8b5cf6; /* Lila */
|
||||
--accent-contrast: #0a0c10;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* =====================================================================
|
||||
theme-hasidog.css — Welt 2: HASIDOG (Community/Maskottchen/Kasper)
|
||||
Babyblau / Silber / Nachtblau — verspielt, weicher, "Familie"-Gefühl.
|
||||
===================================================================== */
|
||||
:root[data-theme="hasidog"] {
|
||||
--bg: #0d1420; /* Nachtblau */
|
||||
--bg-alt: #131b29;
|
||||
--card-bg: #182233;
|
||||
--text: #f2f6fa;
|
||||
--text-muted: #a9b6c9;
|
||||
--border: #263248;
|
||||
--accent: #8fd9ea; /* Babyblau */
|
||||
--accent-2: #c7ccd1; /* Silber */
|
||||
--accent-contrast: #0d1420;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/* =====================================================================
|
||||
theme-spicymedia.css — Welt 3: MANAGER DOGFATHER (Spicy Media)
|
||||
Schwarz / Rot / Orange — professionell, Agentur-Charakter.
|
||||
===================================================================== */
|
||||
:root[data-theme="spicymedia"] {
|
||||
--bg: #0a0a0a;
|
||||
--bg-alt: #131313;
|
||||
--card-bg: #191717;
|
||||
--text: #f6f5f4;
|
||||
--text-muted: #ab9f9c;
|
||||
--border: #2c2726;
|
||||
--accent: #e0263f; /* Rot */
|
||||
--accent-2: #ff7a1a; /* Orange */
|
||||
--accent-contrast: #0a0a0a;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/* =====================================================================
|
||||
data-aktionen.js — Aktionen & Projekte-Datenobjekt
|
||||
|
||||
aktionen.html rendert daraus die Übersichtskarten, aktion-detail.html
|
||||
liest per URL-Parameter (?id=slug) den passenden Eintrag aus und
|
||||
zeigt die Detailansicht — so gibt es EIN Template für alle Aktionen,
|
||||
ohne für jede neue Aktion eine neue HTML-Datei zu brauchen.
|
||||
|
||||
Felder:
|
||||
id – eindeutiger Slug (für aktion-detail.html?id=...)
|
||||
title, summary, description (Array von Absätzen)
|
||||
status – "geplant" | "läuft" | "abgeschlossen"
|
||||
tag – kurzer Kategorie-Text fürs Badge
|
||||
photo – Pfad zu assets/img/... oder "" für Platzhalter
|
||||
===================================================================== */
|
||||
|
||||
const AKTIONEN = [
|
||||
{
|
||||
id: "weihnachtsaktion-portugal",
|
||||
title: "Weihnachtsaktion & Portugal-Trip",
|
||||
tag: "Tierwohl",
|
||||
status: "geplant",
|
||||
summary: "Weihnachtsaktion für Tierheime in Portugal — Team Dogi hilft Tieren in Not.",
|
||||
description: [
|
||||
"Jedes Jahr organisiert Team Dogi eine Weihnachtsaktion zugunsten von Tierheimen in Portugal.",
|
||||
// TODO: genaue Details (Spendenziel, Ablauf, Partner-Tierheime, Termin) ergänzen.
|
||||
],
|
||||
photo: "",
|
||||
},
|
||||
{
|
||||
id: "community-treffen",
|
||||
title: "Community-Treffen (Geburtstag)",
|
||||
tag: "Community",
|
||||
status: "geplant",
|
||||
summary: "Ein persönliches Treffen mit der Community rund um Dogfathers Geburtstag.",
|
||||
description: [
|
||||
"Team Dogi ist Familie — deshalb gibt es ein reales Treffen mit der Community.",
|
||||
// TODO: Ort, Datum, Anmeldeprozess ergänzen.
|
||||
],
|
||||
photo: "",
|
||||
},
|
||||
{
|
||||
id: "vanvan-teddys",
|
||||
title: "Kooperation: VanVan Teddys",
|
||||
tag: "Merchandise",
|
||||
status: "läuft",
|
||||
summary: "Merchandise-Kooperation rund um VanVan — Teddys für die Community.",
|
||||
description: [
|
||||
"Gemeinsam mit VanVan entstehen limitierte Teddys als Teil des Merchandise-Angebots.",
|
||||
// TODO: Preis, Verfügbarkeit, Bestellweg ergänzen.
|
||||
],
|
||||
photo: "",
|
||||
},
|
||||
{
|
||||
id: "online-shop",
|
||||
title: "Online-Shop (in Planung)",
|
||||
tag: "Merchandise",
|
||||
status: "geplant",
|
||||
summary: "Der eigene Online-Shop für Tassen, Decken, Popsockets, Schlüsselanhänger, Sticker & mehr.",
|
||||
description: [
|
||||
"Aktuell läuft der Merchandise-Vertrieb nur über private Einzelbestellungen.",
|
||||
"Ziel: ein eigener Online-Shop, damit die Community jederzeit bestellen kann.",
|
||||
// TODO: Launch-Termin, Plattform (z.B. Shopify) ergänzen.
|
||||
],
|
||||
photo: "",
|
||||
},
|
||||
// TODO: weitere Aktionen/Projekte hier nach gleichem Muster ergänzen.
|
||||
];
|
||||
|
||||
if (typeof window !== "undefined") window.AKTIONEN = AKTIONEN;
|
||||
@@ -0,0 +1,27 @@
|
||||
/* =====================================================================
|
||||
data-creator.js — Creator-Datenobjekt (Creator im Spicy-Media-Team)
|
||||
|
||||
TODO: Noch keine Creator-Profile final geklärt (siehe Masterplan-
|
||||
Fragenkatalog, Block Creator/Team). Sobald Namen + Infos da sind,
|
||||
einfach nach dem Muster unten Objekte ins Array einfügen —
|
||||
creator.html liest automatisch aus und rendert Filter/Karten.
|
||||
|
||||
Felder:
|
||||
name, category (z.B. "Gaming", "Lifestyle", "Comedy" ...),
|
||||
role, photo, quote, social, status
|
||||
===================================================================== */
|
||||
|
||||
const CREATORS = [
|
||||
// Beispiel-Struktur (auskommentiert, zur Orientierung beim Befüllen):
|
||||
// {
|
||||
// name: "Name",
|
||||
// category: "Comedy",
|
||||
// role: "Creator bei Spicy Media",
|
||||
// photo: "assets/img/creator-name.jpg",
|
||||
// quote: "Kurzes Zitat",
|
||||
// social: { tiktok: "https://tiktok.com/@handle" },
|
||||
// status: "aktiv",
|
||||
// },
|
||||
];
|
||||
|
||||
if (typeof window !== "undefined") window.CREATORS = CREATORS;
|
||||
@@ -0,0 +1,94 @@
|
||||
/* =====================================================================
|
||||
data-modis.js — Modi-/Core-Team-Datenobjekt
|
||||
|
||||
Hierarchie (wichtig, bitte bei Änderungen respektieren):
|
||||
VanVan (rechte Hand, eigene Seite vanvan.html)
|
||||
> Diene (rechte Hand von VanVan)
|
||||
> Funny (Modi)
|
||||
> Miss (Modi + Cutterin)
|
||||
> Marina / Michi (Kommentare auf Clip-Accounts)
|
||||
|
||||
Feld "rank" steuert nur die Sortier-/Anzeigereihenfolge (1 = höchste
|
||||
Stufe). VanVan wird auf team-modis.html separat hervorgehoben UND hat
|
||||
eine eigene exklusive Seite (vanvan.html) — hier trotzdem gelistet,
|
||||
damit die Hierarchie an einer Stelle vollständig bleibt.
|
||||
|
||||
Felder: name, role, rank, photo, quote, social, page (optionale eigene
|
||||
Unterseite), status
|
||||
===================================================================== */
|
||||
|
||||
const MODIS = [
|
||||
{
|
||||
name: "VanVan",
|
||||
role: "Rechte Hand von Dogfather · Musik-Produzentin (Van-Van, Spotify)",
|
||||
rank: 1,
|
||||
photo: "",
|
||||
quote: "", // TODO: Zitat von VanVan einholen
|
||||
social: {},
|
||||
page: "vanvan.html",
|
||||
status: "aktiv",
|
||||
},
|
||||
{
|
||||
name: "Diene",
|
||||
role: "Organisiert & plant gemeinsam mit VanVan",
|
||||
rank: 2,
|
||||
photo: "",
|
||||
quote: "",
|
||||
social: {},
|
||||
page: "",
|
||||
status: "aktiv",
|
||||
},
|
||||
{
|
||||
name: "Funny",
|
||||
role: "Modi",
|
||||
rank: 3,
|
||||
photo: "",
|
||||
quote: "",
|
||||
social: {},
|
||||
page: "",
|
||||
status: "aktiv",
|
||||
},
|
||||
{
|
||||
name: "Miss",
|
||||
role: "Modi & Cutterin",
|
||||
rank: 4,
|
||||
photo: "",
|
||||
quote: "",
|
||||
social: {},
|
||||
page: "",
|
||||
status: "aktiv",
|
||||
},
|
||||
{
|
||||
name: "Marina",
|
||||
role: "Kommentare auf den Clip-Accounts",
|
||||
rank: 5,
|
||||
photo: "",
|
||||
quote: "",
|
||||
social: {},
|
||||
page: "",
|
||||
status: "aktiv",
|
||||
},
|
||||
{
|
||||
name: "Michi",
|
||||
role: "Kommentare auf den Clip-Accounts",
|
||||
rank: 5,
|
||||
photo: "",
|
||||
quote: "",
|
||||
social: {},
|
||||
page: "",
|
||||
status: "aktiv",
|
||||
},
|
||||
{
|
||||
name: "Dan",
|
||||
role: "Team Dogi — Rolle noch zu klären", // TODO: genaue Rolle von Dan im Team klären
|
||||
rank: 6,
|
||||
photo: "",
|
||||
quote: "",
|
||||
social: {},
|
||||
page: "",
|
||||
status: "aktiv",
|
||||
},
|
||||
// TODO: weitere Modis/Team-Mitglieder hier nach gleichem Muster ergänzen.
|
||||
];
|
||||
|
||||
if (typeof window !== "undefined") window.MODIS = MODIS;
|
||||
@@ -0,0 +1,42 @@
|
||||
/* =====================================================================
|
||||
data-scouts.js — Scout-Datenobjekt
|
||||
Scouts arbeiten mit Dogfather als Manager zusammen (Spicy Media),
|
||||
50/50 Gewinn-Sharing-Modell (Details siehe manager.html / TODO).
|
||||
|
||||
NEUEN SCOUT HINZUFÜGEN: einfach ein neues Objekt unten ins Array
|
||||
kopieren und Felder ausfüllen. Kein Code-Umbau nötig — team-scouts.html
|
||||
liest dieses Array automatisch aus.
|
||||
|
||||
Felder:
|
||||
name – Anzeigename (Groß-/Kleinschreibung korrekt!)
|
||||
role – Kurzrolle / Fokus
|
||||
photo – Pfad zu assets/img/... oder "" für Platzhalter-Avatar
|
||||
quote – kurzes Zitat/Motto (optional, "" wenn keins)
|
||||
social – { platform: "url", ... } optional
|
||||
status – "aktiv" | "neu" | "pausiert"
|
||||
===================================================================== */
|
||||
|
||||
const SCOUTS = [
|
||||
{
|
||||
name: "Patrick",
|
||||
role: "Scout — Team Dogi",
|
||||
photo: "",
|
||||
quote: "", // TODO: Zitat von Patrick einholen
|
||||
social: {},
|
||||
status: "aktiv",
|
||||
},
|
||||
{
|
||||
name: "Bananenstift",
|
||||
role: "Scout — Team Dogi",
|
||||
photo: "",
|
||||
quote: "", // TODO: Zitat von Bananenstift einholen
|
||||
social: {},
|
||||
status: "aktiv",
|
||||
},
|
||||
// TODO: Dogi liefert die vollständige, aktuelle Scout-Liste (Namen +
|
||||
// Kurzrolle) per Text nach — hier weitere Objekte nach obigem Muster
|
||||
// ergänzen. Struktur bleibt gleich, egal wie viele dazukommen.
|
||||
];
|
||||
|
||||
// Für <script>-Einbindung ohne Modul-Bundler global verfügbar machen
|
||||
if (typeof window !== "undefined") window.SCOUTS = SCOUTS;
|
||||
@@ -0,0 +1,129 @@
|
||||
/* =====================================================================
|
||||
forms.js — Validierung + Versand für die drei Bewerbungsformulare
|
||||
(Creator / Scout / Kooperation) auf bewerben.html
|
||||
|
||||
Versand-Logik:
|
||||
1) Wenn API_BASE_URL gesetzt ist (Cloudflare-Worker-Postfach deployed,
|
||||
siehe cloudflare-worker/README.md): Bewerbung wird per POST an
|
||||
`${API_BASE_URL}/submit` gesendet und landet im code-geschützten
|
||||
Postfach (postfach.html).
|
||||
2) Solange API_BASE_URL leer ist ODER der Versand fehlschlägt (z.B.
|
||||
offline, Worker noch nicht deployed): Fallback auf einen
|
||||
vorausgefüllten "mailto:"-Link, wie bisher.
|
||||
===================================================================== */
|
||||
|
||||
// TODO: nach dem Deploy des Cloudflare Workers hier die Worker-URL ohne
|
||||
// abschließenden Slash eintragen, z.B.
|
||||
// "https://dogfather-universe-postfach.DEIN-ACCOUNT.workers.dev"
|
||||
const API_BASE_URL = "";
|
||||
|
||||
const CONTACT_EMAIL = "[email protected]"; // TODO: echte Kontakt-E-Mail eintragen
|
||||
|
||||
const FORM_TYPES = {
|
||||
"form-creator": "creator",
|
||||
"form-scout": "scout",
|
||||
"form-kooperation": "kooperation",
|
||||
};
|
||||
|
||||
function validateField(field) {
|
||||
const wrapper = field.closest(".field");
|
||||
if (!wrapper) return true;
|
||||
let valid = field.checkValidity();
|
||||
|
||||
if (valid && field.type === "email" && field.value) {
|
||||
valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(field.value);
|
||||
}
|
||||
|
||||
wrapper.classList.toggle("has-error", !valid);
|
||||
return valid;
|
||||
}
|
||||
|
||||
function buildMailto(subjectLabel, data) {
|
||||
const lines = [...data.entries()].map(([k, v]) => `${k}: ${v}`).join("\n");
|
||||
return `mailto:${CONTACT_EMAIL}?subject=${encodeURIComponent(
|
||||
"Bewerbung: " + subjectLabel
|
||||
)}&body=${encodeURIComponent(lines)}`;
|
||||
}
|
||||
|
||||
async function submitToWorker(type, data) {
|
||||
if (!API_BASE_URL) return false;
|
||||
const payload = { type, data: Object.fromEntries(data.entries()) };
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/submit`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
if (!res.ok) return false;
|
||||
const json = await res.json();
|
||||
return !!json.ok;
|
||||
} catch {
|
||||
return false; // z.B. offline oder Worker nicht erreichbar -> Fallback greift
|
||||
}
|
||||
}
|
||||
|
||||
function setupForm(formId, subjectLabel) {
|
||||
const form = document.getElementById(formId);
|
||||
if (!form) return;
|
||||
|
||||
const status = form.querySelector(".form-status");
|
||||
const type = FORM_TYPES[formId];
|
||||
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
const fields = [...form.querySelectorAll("input[required], select[required], textarea[required], input[type=email]")];
|
||||
const allValid = fields.map(validateField).every(Boolean);
|
||||
|
||||
if (!allValid) {
|
||||
if (status) {
|
||||
status.textContent = "Bitte prüfe die markierten Felder — es fehlt noch etwas.";
|
||||
status.className = "form-status show error";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const data = new FormData(form);
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
if (submitBtn) submitBtn.disabled = true;
|
||||
|
||||
const delivered = await submitToWorker(type, data);
|
||||
|
||||
if (status) {
|
||||
if (delivered) {
|
||||
status.innerHTML = `Danke, das sieht super aus! 🩵 Deine Bewerbung ist sicher im Postfach von Team Dogi angekommen.`;
|
||||
status.className = "form-status show success";
|
||||
} else {
|
||||
const mailto = buildMailto(subjectLabel, data);
|
||||
status.innerHTML = `Danke, das sieht super aus! 🩵 Solange noch kein automatischer Versand eingerichtet ist:
|
||||
<a href="${mailto}" class="btn btn-outline" style="margin-top:.6rem;">E-Mail jetzt senden</a>`;
|
||||
status.className = "form-status show success";
|
||||
}
|
||||
}
|
||||
if (submitBtn) submitBtn.disabled = false;
|
||||
form.reset();
|
||||
});
|
||||
|
||||
form.querySelectorAll("input, select, textarea").forEach((field) => {
|
||||
field.addEventListener("blur", () => validateField(field));
|
||||
});
|
||||
}
|
||||
|
||||
function setupTabs() {
|
||||
const tabs = document.querySelectorAll(".tab-btn");
|
||||
const panels = document.querySelectorAll(".tab-panel");
|
||||
tabs.forEach((tab) => {
|
||||
tab.addEventListener("click", () => {
|
||||
tabs.forEach((t) => t.classList.remove("active"));
|
||||
panels.forEach((p) => p.classList.remove("active"));
|
||||
tab.classList.add("active");
|
||||
document.getElementById(tab.dataset.tab).classList.add("active");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
setupTabs();
|
||||
setupForm("form-creator", "Creator");
|
||||
setupForm("form-scout", "Scout");
|
||||
setupForm("form-kooperation", "Kooperation");
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
/* =====================================================================
|
||||
gallery.js — filterbare Mediengalerie + Lightbox (für medien.html)
|
||||
|
||||
MEDIA-Array unten mit echten Bildern befüllen sobald verfügbar
|
||||
(assets/img/...). Bis dahin rendert jeder Eintrag ohne "src" einen
|
||||
Platzhalter-Block mit passendem Alt-Text, damit die Struktur schon
|
||||
steht und nur noch Bilder eingehängt werden müssen.
|
||||
===================================================================== */
|
||||
|
||||
const MEDIA = [
|
||||
{ id: 1, category: "dogfather", alt: "Dogfather Profilbild", src: "" },
|
||||
{ id: 2, category: "dogfather", alt: "Dogfather beim Stream", src: "" },
|
||||
{ id: 3, category: "team", alt: "Team Dogi Gruppenbild", src: "" },
|
||||
{ id: 4, category: "team", alt: "Patrick (Scout)", src: "" },
|
||||
{ id: 5, category: "team", alt: "Bananenstift (Scout)", src: "" },
|
||||
{ id: 6, category: "team", alt: "VanVan", src: "" },
|
||||
{ id: 7, category: "hasidog", alt: "HasiDog Logo/Maskottchen", src: "" },
|
||||
{ id: 8, category: "hasidog", alt: "Husky Casper", src: "" },
|
||||
{ id: 9, category: "hasidog", alt: "Casper & Kater Simba", src: "" },
|
||||
{ id: 10, category: "manager", alt: "Spicy Media Logo", src: "" },
|
||||
{ id: 11, category: "aktionen", alt: "Weihnachtsaktion Portugal", src: "" },
|
||||
{ id: 12, category: "aktionen", alt: "VanVan Teddys Merch", src: "" },
|
||||
// TODO: echte Bilder aus den vom User gelieferten Assets ergänzen
|
||||
// (assets/img/... Pfad eintragen, dann wird automatisch das Bild
|
||||
// statt des Platzhalters angezeigt).
|
||||
];
|
||||
|
||||
function renderGalleryItem(item) {
|
||||
const media = item.src
|
||||
? `<img src="${item.src}" alt="${item.alt}" loading="lazy" />`
|
||||
: `<div class="img-placeholder">${item.alt}<br><span class="small">Foto folgt</span></div>`;
|
||||
return `
|
||||
<figure class="gallery-item card" data-category="${item.category}" data-index="${item.id}">
|
||||
${media}
|
||||
<figcaption class="small muted" style="margin-top:.6rem;">${item.alt}</figcaption>
|
||||
</figure>`;
|
||||
}
|
||||
|
||||
function initGallery() {
|
||||
const grid = document.getElementById("gallery-grid");
|
||||
const filterBar = document.getElementById("gallery-filters");
|
||||
if (!grid || !filterBar) return;
|
||||
|
||||
const categories = ["alle", ...new Set(MEDIA.map((m) => m.category))];
|
||||
filterBar.innerHTML = categories
|
||||
.map(
|
||||
(c, i) =>
|
||||
`<button class="filter-btn${i === 0 ? " active" : ""}" data-filter="${c}">${c[0].toUpperCase() + c.slice(1)}</button>`
|
||||
)
|
||||
.join("");
|
||||
|
||||
function draw(filter) {
|
||||
const items = filter === "alle" ? MEDIA : MEDIA.filter((m) => m.category === filter);
|
||||
grid.innerHTML = items.map(renderGalleryItem).join("") || `<p class="muted">Keine Bilder in dieser Kategorie.</p>`;
|
||||
bindLightbox();
|
||||
}
|
||||
|
||||
filterBar.addEventListener("click", (e) => {
|
||||
const btn = e.target.closest(".filter-btn");
|
||||
if (!btn) return;
|
||||
filterBar.querySelectorAll(".filter-btn").forEach((b) => b.classList.remove("active"));
|
||||
btn.classList.add("active");
|
||||
draw(btn.dataset.filter);
|
||||
});
|
||||
|
||||
function bindLightbox() {
|
||||
const lb = document.getElementById("lightbox");
|
||||
const lbContent = document.getElementById("lightbox-content");
|
||||
if (!lb || !lbContent) return;
|
||||
grid.querySelectorAll(".gallery-item").forEach((el) => {
|
||||
el.addEventListener("click", () => {
|
||||
lbContent.innerHTML = el.innerHTML;
|
||||
lb.classList.add("open");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
draw("alle");
|
||||
|
||||
const lb = document.getElementById("lightbox");
|
||||
if (lb) {
|
||||
lb.addEventListener("click", (e) => {
|
||||
if (e.target === lb || e.target.closest(".lightbox-close")) {
|
||||
lb.classList.remove("open");
|
||||
}
|
||||
});
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") lb.classList.remove("open");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", initGallery);
|
||||
@@ -0,0 +1,193 @@
|
||||
/* =====================================================================
|
||||
DOGFATHER UNIVERSE — main.js
|
||||
Gemeinsame Navigation, mobiles Menü, Footer, Nebel-Effekt.
|
||||
Läuft rein clientseitig (kein Build-Tool, kein Fetch von Partials),
|
||||
damit jede Seite auch direkt per Doppelklick (file://) funktioniert.
|
||||
===================================================================== */
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
/* ---------- Navigationsstruktur (an einer Stelle pflegen) ---------- */
|
||||
const NAV = [
|
||||
{ label: "Start", href: "index.html" },
|
||||
{
|
||||
label: "Dogfather",
|
||||
children: [
|
||||
{ label: "Die Person", href: "dogfather.html" },
|
||||
{ label: "Streamer", href: "streamer.html" },
|
||||
{ label: "Streamplan", href: "streamplan.html" },
|
||||
{ label: "Zeitreise", href: "zeitreise.html" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "HasiDog",
|
||||
children: [
|
||||
{ label: "HasiDog-Welt", href: "hasidog.html" },
|
||||
{ label: "Kasper", href: "kasper.html" },
|
||||
{ label: "Community", href: "community.html" },
|
||||
{ label: "Stimmen", href: "stimmen.html" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Manager",
|
||||
children: [
|
||||
{ label: "Manager Dogfather", href: "manager.html" },
|
||||
{ label: "Werte", href: "werte.html" },
|
||||
{ label: "Scout-Team", href: "team-scouts.html" },
|
||||
{ label: "Modi-Team", href: "team-modis.html" },
|
||||
{ label: "VanVan", href: "vanvan.html" },
|
||||
{ label: "Creator", href: "creator.html" },
|
||||
{ label: "Bewerben", href: "bewerben.html" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Medien",
|
||||
children: [
|
||||
{ label: "Galerie", href: "medien.html" },
|
||||
{ label: "Videos", href: "videos.html" },
|
||||
{ label: "Aktionen", href: "aktionen.html" },
|
||||
],
|
||||
},
|
||||
{ label: "Links", href: "links.html" },
|
||||
];
|
||||
|
||||
const currentFile = (location.pathname.split("/").pop() || "index.html");
|
||||
|
||||
function isActive(href) {
|
||||
return href === currentFile;
|
||||
}
|
||||
|
||||
function renderNavItem(item) {
|
||||
if (!item.children) {
|
||||
return `<a class="nav-flat${isActive(item.href) ? " active" : ""}" href="${item.href}">${item.label}</a>`;
|
||||
}
|
||||
const activeChild = item.children.some((c) => isActive(c.href));
|
||||
return `
|
||||
<div class="nav-group${activeChild ? " open-hint" : ""}">
|
||||
<button type="button" aria-expanded="false">${item.label} <span aria-hidden="true">▾</span></button>
|
||||
<div class="nav-dropdown">
|
||||
${item.children
|
||||
.map(
|
||||
(c) =>
|
||||
`<a href="${c.href}"${isActive(c.href) ? ' class="active"' : ""}>${c.label}</a>`
|
||||
)
|
||||
.join("")}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function renderHeader() {
|
||||
const el = document.getElementById("site-header");
|
||||
if (!el) return;
|
||||
el.innerHTML = `
|
||||
<header class="site-header">
|
||||
<div class="nav-bar">
|
||||
<a class="brand" href="index.html">
|
||||
<span class="heart">🩵</span>
|
||||
<span>DOGFATHER UNIVERSE
|
||||
<small>Streamer · Community · HasiDog · Manager</small>
|
||||
</span>
|
||||
</a>
|
||||
<button class="nav-toggle" type="button" aria-label="Menü öffnen" aria-expanded="false">☰</button>
|
||||
<nav class="nav-links">
|
||||
${NAV.map(renderNavItem).join("")}
|
||||
<a class="nav-cta" href="bewerben.html">Bewerben 🩵</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>`;
|
||||
|
||||
const toggle = el.querySelector(".nav-toggle");
|
||||
const links = el.querySelector(".nav-links");
|
||||
toggle.addEventListener("click", () => {
|
||||
const open = links.classList.toggle("open");
|
||||
toggle.setAttribute("aria-expanded", String(open));
|
||||
});
|
||||
|
||||
el.querySelectorAll(".nav-group > button").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const group = btn.parentElement;
|
||||
const wasOpen = group.classList.contains("open");
|
||||
el.querySelectorAll(".nav-group.open").forEach((g) => g.classList.remove("open"));
|
||||
if (!wasOpen) group.classList.add("open");
|
||||
btn.setAttribute("aria-expanded", String(!wasOpen));
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!el.contains(e.target)) {
|
||||
el.querySelectorAll(".nav-group.open").forEach((g) => g.classList.remove("open"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderFooter() {
|
||||
const el = document.getElementById("site-footer");
|
||||
if (!el) return;
|
||||
const year = new Date().getFullYear();
|
||||
el.innerHTML = `
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-grid">
|
||||
<div>
|
||||
<div class="brand"><span class="heart">🩵</span> DOGFATHER UNIVERSE</div>
|
||||
<p class="small" style="margin-top:.8rem;max-width:32ch;">
|
||||
Streamer. Community. HasiDog. Manager. Gemeinsam stark.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Dogfather</h4>
|
||||
<ul>
|
||||
<li><a href="dogfather.html">Die Person</a></li>
|
||||
<li><a href="streamer.html">Streamer</a></li>
|
||||
<li><a href="zeitreise.html">Zeitreise</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4>HasiDog & Manager</h4>
|
||||
<ul>
|
||||
<li><a href="hasidog.html">HasiDog-Welt</a></li>
|
||||
<li><a href="manager.html">Manager Dogfather</a></li>
|
||||
<li><a href="team-scouts.html">Scouts</a></li>
|
||||
<li><a href="team-modis.html">Modis</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Mehr</h4>
|
||||
<ul>
|
||||
<li><a href="links.html">Alle Links</a></li>
|
||||
<li><a href="bewerben.html">Bewerben</a></li>
|
||||
<li><a href="kontakt.html">Kontakt</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container footer-bottom">
|
||||
<span>© ${year} Dogfather Universe. Team Dogi ist Familie 🩵</span>
|
||||
<span><a href="kontakt.html">Impressum & Datenschutz</a> · <a href="postfach.html">Team-Postfach 🔒</a></span>
|
||||
</div>
|
||||
</footer>`;
|
||||
}
|
||||
|
||||
function renderFog() {
|
||||
if (document.querySelector(".fog-layer")) return;
|
||||
const layer = document.createElement("div");
|
||||
layer.className = "fog-layer";
|
||||
layer.setAttribute("aria-hidden", "true");
|
||||
const blobs = 6;
|
||||
let html = "";
|
||||
for (let i = 0; i < blobs; i++) {
|
||||
const size = 220 + Math.round(Math.random() * 260);
|
||||
const top = Math.round(Math.random() * 100);
|
||||
const left = Math.round(Math.random() * 100);
|
||||
const delay = (Math.random() * 10).toFixed(1);
|
||||
html += `<span style="width:${size}px;height:${size}px;top:${top}%;left:${left}%;animation-delay:${delay}s;"></span>`;
|
||||
}
|
||||
layer.innerHTML = html;
|
||||
document.body.prepend(layer);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
renderHeader();
|
||||
renderFooter();
|
||||
renderFog();
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user