diff --git a/.assetsignore b/.assetsignore
index 63b041a..e684784 100644
--- a/.assetsignore
+++ b/.assetsignore
@@ -3,4 +3,5 @@
.wrangler
cloudflare-worker
wrangler.toml
+gate-worker.js
node_modules
diff --git a/404.html b/404.html
index 3d41027..823e336 100644
--- a/404.html
+++ b/404.html
@@ -10,12 +10,12 @@
-
-
+
+
-
+
statt eines dritten Pseudo-Elements (::before/::after sind
+ an .card-brand schon für Aurora/Schimmer vergeben) — bewusst NICHT
+ rotiert (siehe .btn-tiktok-Notiz weiter oben: ein rotierender Masken-Ring
+ verzerrt sich auf länglichen/runden Formen), sondern ein ruhiger,
+ statischer Farbverlaufs-Rand. */
+.card-feature-ring {
+ position: absolute;
+ inset: 0;
+ z-index: 3;
+ border-radius: inherit;
+ padding: 1.5px;
+ background: conic-gradient(from 0deg, var(--brand-1), var(--brand-2), var(--brand-1) 66%, var(--brand-2) 100%);
+ -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
+ -webkit-mask-composite: xor;
+ mask-composite: exclude;
+ opacity: .8;
+ pointer-events: none;
+}
+.card-feature-badge {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ z-index: 4;
+ font-size: .66rem;
+ font-weight: 800;
+ letter-spacing: .05em;
+ text-transform: uppercase;
+ padding: .38rem .75rem;
+ border-radius: 999px;
+ background: rgba(8, 10, 14, .55);
+ border: 1px solid rgba(255, 255, 255, .28);
+ backdrop-filter: blur(4px);
+ color: #fff;
+}
+.card-feature:hover {
+ transform: translateY(-3px);
+}
+@media (prefers-reduced-motion: reduce) {
+ .card-feature { transition: none; }
+}
+.card-abo { --brand-1: rgba(212, 175, 55, .9); --brand-2: rgba(139, 92, 246, .75); }
+.card-abo .card-feature-badge { color: #ffe9a8; border-color: rgba(255, 214, 102, .5); }
+.card-rabatt { --brand-1: rgba(52, 211, 153, .9); --brand-2: rgba(251, 146, 60, .8); }
+.card-rabatt .card-feature-badge { color: #baf7d6; border-color: rgba(94, 234, 167, .5); }
+
+/* Breite Variante von .card-feature — nimmt die volle Container-Breite ein
+ (statt im 2er-Grid neben einer zweiten Karte zu stehen) und wechselt
+ dafür auf ein horizontales Layout: Icon links, Titel/Text rechts daneben
+ statt zentriert übereinander, damit die Breite bewusst genutzt wird statt
+ nur Leerraum links/rechts vom zentrierten Text zu erzeugen. */
+.card-feature-wide {
+ display: flex;
+ align-items: center;
+ gap: 1.8rem;
+ text-align: left;
+ padding: 2.2rem 2.8rem;
+ min-height: auto;
+}
+.card-feature-wide .icon-badge {
+ flex-shrink: 0;
+ margin-bottom: 0;
+}
+.card-feature-wide .card-feature-text {
+ position: relative;
+ z-index: 2;
+}
+.card-feature-wide .card-feature-desc {
+ margin: .4rem 0 0;
+ max-width: 60ch;
+}
+@media (max-width: 640px) {
+ .card-feature-wide {
+ flex-direction: column;
+ text-align: center;
+ padding: 2.4rem 1.6rem;
+ }
+ .card-feature-wide .card-feature-desc { margin: .4rem auto 0; }
+}
+
.nav-toggle {
display: none;
background: none;
@@ -1218,6 +1415,22 @@ header.site-header {
overflow: hidden;
}
.hero > .container { position: relative; z-index: 1; }
+/* Zusätzlicher, garantiert deckender Schleier NUR hinter dem Hero-Text auf
+ Seiten mit Hintergrundfoto (.page-bg) — Ergänzung zum globalen
+ body.mit-hintergrund-Verlauf oben. Grund (BFSG/WCAG-Audit 04.08.2026):
+ der globale Verlauf allein reichte bei hellen Fotomotiven nicht überall
+ für AA-Kontrast (Eyebrow/H1/Lead direkt im Hero betroffen). Sitzt NUR
+ um den Text-Container, deckt also NICHT das scharfe .hero-banner-Foto
+ darüber ab (das bleibt unverändert sichtbar). */
+.hero.page-bg > .container::before {
+ content: "";
+ position: absolute;
+ inset: -1.4rem -1.6rem;
+ z-index: -1;
+ border-radius: 28px;
+ background: color-mix(in srgb, var(--bg) 55%, transparent);
+ pointer-events: none;
+}
.hero .worlds {
margin: .6rem 0 1.2rem;
color: var(--text-muted);
@@ -1390,7 +1603,7 @@ header.site-header {
padding: 1rem;
}
-/* ---------- Timeline (Zeitreise) ---------- */
+/* ---------- Timeline (Zeitreise) — alte, einfache Punkt+Linie-Variante ---------- */
.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 {
@@ -1405,6 +1618,177 @@ header.site-header {
}
.timeline-item .date { color: var(--accent); font-weight: 700; font-size: .85rem; }
+/* ---------- Zeitbaum (Zeitreise) — die Reise als wachsender Baum ----------
+ Ein Stamm (::before, per clip-path unten dicker als oben = "Wachstum")
+ läuft mittig durch alle Stationen. Jede Station ("Ast") hängt sich
+ abwechselnd links/rechts an den Stamm, verbunden über eine glühende
+ Astlinie. Meilenstein-Stationen bekommen eine goldene statt babyblaue
+ Aura. Oben ein Keim (🌱, Anfang), unten eine Krone (🌳, Fortsetzung). */
+.zeit-baum {
+ position: relative;
+ margin: 3rem 0 1rem;
+ padding: 2.4rem 0;
+}
+.zeit-baum::before {
+ content: "";
+ position: absolute;
+ left: 50%;
+ top: 0; bottom: 0;
+ width: 44px;
+ transform: translateX(-50%);
+ background: linear-gradient(180deg,
+ rgba(143,217,234,.55) 0%,
+ rgba(139,92,246,.55) 45%,
+ rgba(217,196,90,.6) 100%);
+ clip-path: polygon(47% 0%, 53% 0%, 66% 100%, 34% 100%);
+ filter: blur(.2px);
+ box-shadow: 0 0 60px 6px rgba(143,217,234,.18);
+ opacity: .85;
+}
+.zeit-baum-kappe {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: .3rem;
+ margin-bottom: .5rem;
+ text-align: center;
+ font-size: .85rem;
+ font-weight: 700;
+ color: var(--accent);
+ text-shadow: 0 0 14px rgba(143,217,234,.55);
+}
+.zeit-baum-kappe span:first-child { font-size: 1.6rem; filter: drop-shadow(0 0 10px rgba(143,217,234,.6)); }
+.zeit-baum-wurzel {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: .3rem;
+ margin-top: 1rem;
+ text-align: center;
+ font-size: .85rem;
+ font-weight: 700;
+ color: #e8d78f;
+ text-shadow: 0 0 14px rgba(217,196,90,.5);
+}
+.zeit-baum-wurzel span:first-child { font-size: 1.9rem; filter: drop-shadow(0 0 14px rgba(217,196,90,.6)); }
+
+.zeit-ast {
+ position: relative;
+ display: flex;
+ width: 100%;
+ padding: 1.1rem 0;
+}
+.zeit-ast:nth-child(odd) { justify-content: flex-start; }
+.zeit-ast:nth-child(even) { justify-content: flex-end; }
+
+.zeit-node {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ width: 16px; height: 16px;
+ transform: translate(-50%, -50%);
+ border-radius: 50%;
+ background: radial-gradient(circle at 35% 30%, #fff, var(--accent) 60%, var(--accent-2) 100%);
+ box-shadow: 0 0 0 4px var(--bg), 0 0 18px 3px rgba(143,217,234,.6);
+ z-index: 2;
+ animation: zeit-node-pulse 3.2s ease-in-out infinite;
+}
+.zeit-ast.meilenstein .zeit-node {
+ background: radial-gradient(circle at 35% 30%, #fff, #e8d78f 55%, #b8892f 100%);
+ box-shadow: 0 0 0 4px var(--bg), 0 0 22px 5px rgba(232,215,143,.7);
+}
+@keyframes zeit-node-pulse {
+ 0%, 100% { box-shadow: 0 0 0 4px var(--bg), 0 0 14px 2px rgba(143,217,234,.5); }
+ 50% { box-shadow: 0 0 0 4px var(--bg), 0 0 24px 6px rgba(143,217,234,.85); }
+}
+
+.zeit-karte {
+ position: relative;
+ width: calc(50% - 2.6rem);
+ background: linear-gradient(160deg, rgba(23,27,33,.82), rgba(23,27,33,.55));
+ backdrop-filter: blur(6px);
+ border: 1px solid rgba(143,217,234,.25);
+ border-radius: 16px;
+ padding: 1.1rem 1.3rem;
+ box-shadow: 0 10px 26px rgba(0,0,0,.35);
+ transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease;
+}
+.zeit-ast.meilenstein .zeit-karte { border-color: rgba(232,215,143,.4); }
+.zeit-karte::before {
+ content: "";
+ position: absolute;
+ top: 50%;
+ width: 2.6rem;
+ height: 2px;
+ background: linear-gradient(90deg, rgba(143,217,234,.05), rgba(143,217,234,.65));
+ transform: translateY(-50%);
+}
+.zeit-ast:nth-child(odd) .zeit-karte::before { right: -2.6rem; }
+.zeit-ast:nth-child(even) .zeit-karte::before { left: -2.6rem; background: linear-gradient(270deg, rgba(143,217,234,.05), rgba(143,217,234,.65)); }
+.zeit-ast.meilenstein:nth-child(odd) .zeit-karte::before { background: linear-gradient(90deg, rgba(232,215,143,.05), rgba(232,215,143,.75)); }
+.zeit-ast.meilenstein:nth-child(even) .zeit-karte::before { background: linear-gradient(270deg, rgba(232,215,143,.05), rgba(232,215,143,.75)); }
+
+.zeit-karte:hover {
+ transform: translateY(-4px);
+ border-color: rgba(143,217,234,.55);
+ box-shadow: 0 16px 34px rgba(0,0,0,.45), 0 0 24px rgba(143,217,234,.2);
+}
+.zeit-ast.meilenstein .zeit-karte:hover {
+ border-color: rgba(232,215,143,.7);
+ box-shadow: 0 16px 34px rgba(0,0,0,.45), 0 0 26px rgba(232,215,143,.3);
+}
+
+.zeit-datum {
+ color: var(--accent);
+ font-weight: 700;
+ font-size: .82rem;
+ letter-spacing: .02em;
+ margin-bottom: .25rem;
+}
+.zeit-ast.meilenstein .zeit-datum { color: #e8d78f; }
+.zeit-karte h3 { margin: 0 0 .4rem; font-size: 1.15rem; }
+.zeit-karte p { margin: 0; color: var(--text-muted); font-size: .93rem; line-height: 1.55; }
+
+.zeit-ast.zeit-final .zeit-karte { width: calc(100% - 4rem); max-width: 640px; text-align: center; }
+.zeit-ast.zeit-final { justify-content: center !important; }
+.zeit-ast.zeit-final .zeit-karte::before { display: none; }
+.zeit-zitat {
+ font-weight: 700;
+ color: var(--text);
+ font-style: italic;
+ margin: .8rem 0 .5rem !important;
+}
+.zeit-familie {
+ font-weight: 800;
+ font-size: 1.05rem !important;
+ color: var(--accent) !important;
+ text-shadow: 0 0 14px rgba(143,217,234,.5);
+}
+.zeit-signoff {
+ margin-top: .8rem !important;
+ color: var(--text-muted);
+ font-style: italic;
+}
+
+/* Reveal-Animation (per IntersectionObserver in zeitreise.html getriggert) */
+.zeit-ast { opacity: 0; transform: translateY(28px); transition: opacity .7s ease, transform .7s ease; }
+.zeit-ast.zeit-sichtbar { opacity: 1; transform: translateY(0); }
+
+@media (max-width: 780px) {
+ .zeit-baum::before { left: 22px; width: 30px; clip-path: polygon(44% 0%, 56% 0%, 70% 100%, 30% 100%); }
+ .zeit-ast, .zeit-ast:nth-child(odd), .zeit-ast:nth-child(even) { justify-content: flex-start; padding-left: 3rem; }
+ .zeit-node { left: 22px; }
+ .zeit-karte { width: 100%; }
+ .zeit-karte::before,
+ .zeit-ast:nth-child(even) .zeit-karte::before { left: -2.2rem; right: auto; width: 2.2rem; background: linear-gradient(270deg, rgba(143,217,234,.05), rgba(143,217,234,.65)); }
+ .zeit-ast.meilenstein .zeit-karte::before,
+ .zeit-ast.meilenstein:nth-child(even) .zeit-karte::before { background: linear-gradient(270deg, rgba(232,215,143,.05), rgba(232,215,143,.75)); }
+ .zeit-ast.zeit-final .zeit-karte { width: 100%; text-align: left; }
+ .zeit-ast.zeit-final { padding-left: 3rem; }
+}
+
/* ---------- Gallery / Lightbox ---------- */
.filter-bar { display: flex; gap: .6rem; flex-wrap: wrap; margin-bottom: 1.6rem; }
.filter-btn {
@@ -1464,6 +1848,11 @@ footer.site-footer {
margin-top: 4rem;
position: relative;
z-index: 1;
+ /* Voll deckender Hintergrund (nicht color-mix/transparent) — auf Seiten
+ mit body.mit-hintergrund lag der Footer sonst direkt über dem fixen,
+ nur teilweise abgedunkelten Hintergrundfoto, was bei axe-core einen
+ color-contrast-Verstoß für .footer-grid .brand auslöste (04.08.2026). */
+ background: var(--bg);
}
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 2rem; }
@media (max-width: 760px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@@ -1928,12 +2317,22 @@ body.mit-hintergrund::before {
background-size: cover;
background-position: center;
background-repeat: no-repeat;
- /* Weichzeichnen + leicht abdunkeln: ruhiger Untergrund, der die Augen
- beim Lesen nicht anstrengt. */
- filter: blur(4px) brightness(.62) saturate(.95);
+ /* Weichzeichnen + abdunkeln: ruhiger Untergrund, der die Augen beim Lesen
+ nicht anstrengt. brightness(.45) statt vorher .62 — angepasst nach dem
+ BFSG/WCAG-Kontrast-Audit vom 04.08.2026 (axe-core fand color-contrast-
+ Verstöße auf allen Seiten mit Hintergrundfoto: helle Bildstellen
+ schimmerten durch die Abdunkelung, besonders im Hero-Bereich oben, wo
+ der ::after-Verlauf am schwächsten war). */
+ filter: blur(4px) brightness(.45) saturate(.95);
}
/* Abdunkelung für den Seitenkörper — bewusst kräftig, damit langes Lesen
- angenehm bleibt. Das Motiv bleibt als ruhige Stimmung im Hintergrund. */
+ angenehm bleibt UND Text zuverlässig AA-Kontrast erreicht (siehe oben).
+ Werte am 04.08.2026 zweimal nachgeschärft (80/92/95% → 90/97/99% →
+ 94/98/99.5%, volle Deckung schon ab 30% statt erst bei 100%) — nach dem
+ zusätzlichen Hero-Schleier (oben) waren vereinzelt noch Elemente knapp
+ unterhalb des Heros betroffen (z.B. `.bewerben-side-label`,
+ `h2.text-center` auf team-modis.html), wo der Verlauf noch nicht voll
+ angekommen war. Mit axe-core auf allen betroffenen Seiten gegengeprüft. */
body.mit-hintergrund::after {
content: "";
position: fixed;
@@ -1941,9 +2340,9 @@ body.mit-hintergrund::after {
z-index: -2;
pointer-events: none;
background: linear-gradient(to bottom,
- color-mix(in srgb, var(--bg) 80%, transparent) 0%,
- color-mix(in srgb, var(--bg) 92%, transparent) 45%,
- color-mix(in srgb, var(--bg) 95%, transparent) 100%);
+ color-mix(in srgb, var(--bg) 94%, transparent) 0%,
+ color-mix(in srgb, var(--bg) 98%, transparent) 30%,
+ color-mix(in srgb, var(--bg) 99.5%, transparent) 100%);
}
/* ---- 2) Kopfbereich: ECHTES
statt CSS-Hintergrund ----
@@ -2622,6 +3021,119 @@ body.start-seite .music-toggle:focus-visible {
.music-toggle { transition: none; }
}
+/* =========================================================================
+ Vorschau-Umschalter (nur für Dogi/VanVan, siehe main.js
+ renderVorschauSchalter/gate-worker.js) — unten LINKS, spiegelbildlich zum
+ Musik-Knopf unten rechts, exakt dieselbe Bauart (Kreisknopf mit
+ wandernden Farbwolken + Lichtschimmer, aufklappbares Panel). Eigenes,
+ festes Farbpaar (Gold/Lila, das Supporter-Abo-Motiv) statt der
+ Chamäleon-Farben des Musik-Knopfs — dieser Knopf soll bewusst IMMER
+ gleich aussehen, unabhängig vom Seitenbild. Symbol: 🎭 (Theatermasken —
+ "als jemand anderes ansehen", passt zum Rollen-Vorschau-Zweck). Nutzer-
+ Feedback 03.08.2026: statt einer dauerhaft sichtbaren Text-Knopfleiste
+ sollte es "so ein Button wie bei VanVan" sein, der sich erst bei Klick
+ öffnet. ========================================================== */
+.vorschau-widget {
+ position: fixed;
+ left: 22px;
+ bottom: 22px;
+ z-index: 120;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 12px;
+}
+.vorschau-toggle {
+ position: relative;
+ isolation: isolate;
+ overflow: hidden;
+ width: 54px;
+ height: 54px;
+ flex: none;
+ border-radius: 999px;
+ font-size: 1.45rem;
+ line-height: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ color: #fff;
+ text-shadow: 0 1px 3px rgba(0, 0, 0, .55);
+ background: #0b0e14;
+ border: 1px solid rgba(212, 175, 55, .55);
+ box-shadow: 0 12px 30px rgba(0, 0, 0, .45), 0 0 24px rgba(212, 175, 55, .3);
+ transition: transform .25s ease, filter .25s ease, box-shadow .25s ease;
+}
+.vorschau-toggle::before {
+ content: "";
+ position: absolute;
+ inset: -50%;
+ z-index: 0;
+ background:
+ radial-gradient(circle at 25% 30%, rgba(255, 214, 102, .95), transparent 58%),
+ radial-gradient(circle at 75% 72%, rgba(139, 92, 246, .9), transparent 58%);
+ filter: blur(7px) saturate(1.3);
+ animation: btn-aurora 10s ease-in-out infinite alternate;
+}
+.vorschau-toggle::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ z-index: 1;
+ border-radius: 999px;
+ background: linear-gradient(115deg, transparent 35%, rgba(255, 255, 255, .35) 49%, transparent 63%);
+ background-size: 240% 100%;
+ mix-blend-mode: overlay;
+ animation: btn-shimmer 5s linear infinite;
+ pointer-events: none;
+}
+.vorschau-toggle:hover, .vorschau-toggle:focus-visible {
+ transform: translateY(-2px) scale(1.05);
+ filter: brightness(1.12);
+ box-shadow: 0 16px 38px rgba(0, 0, 0, .5), 0 0 34px rgba(212, 175, 55, .5);
+}
+.vorschau-widget.open .vorschau-toggle { transform: rotate(-12deg); }
+.vorschau-toggle span { position: relative; z-index: 2; }
+@media (prefers-reduced-motion: reduce) { .vorschau-toggle::before, .vorschau-toggle::after { animation: none; } }
+
+.vorschau-panel {
+ width: min(260px, calc(100vw - 44px));
+ padding: 1rem 1.1rem 1.1rem;
+ border-radius: var(--radius);
+ border: 1px solid var(--border);
+ background: var(--card-bg);
+ box-shadow: 0 24px 60px rgba(0, 0, 0, .6);
+ animation: music-panel-in .22s ease-out;
+}
+.vorschau-panel[hidden] { display: none; }
+.vorschau-panel-head {
+ display: flex; align-items: center; justify-content: space-between; gap: .6rem;
+ margin-bottom: .8rem;
+}
+.vorschau-panel-head strong { font-size: .92rem; color: #ffe9a8; }
+.vorschau-close {
+ background: none; border: 0; color: var(--text-muted); font-size: 1rem;
+ line-height: 1; padding: .2rem .3rem; cursor: pointer; border-radius: 6px;
+}
+.vorschau-close:hover { color: var(--accent); }
+.vorschau-optionen { display: flex; flex-direction: column; gap: .4rem; }
+.vorschau-optionen button {
+ display: block; width: 100%; text-align: left; font-size: .88rem; font-weight: 600;
+ padding: .6rem .8rem; border-radius: 9px; cursor: pointer;
+ background: transparent; border: 1px solid transparent; color: var(--text-muted);
+ transition: border-color .15s ease, background .15s ease, color .15s ease;
+}
+.vorschau-optionen button:hover { border-color: var(--border); color: var(--text); }
+.vorschau-optionen button.aktiv {
+ border-color: rgba(212, 175, 55, .6); color: #ffe9a8; background: rgba(212, 175, 55, .1);
+}
+@media (max-width: 620px) {
+ .vorschau-widget { left: 14px; bottom: 14px; }
+ .vorschau-toggle { width: 48px; height: 48px; font-size: 1.25rem; }
+}
+@media print { .vorschau-widget { display: none !important; } }
+@media (prefers-reduced-motion: reduce) { .vorschau-panel { animation: none; } }
+
/* ---------- Live-Status-Punkt ---------- */
.live-dot {
display: inline-block;
diff --git a/assets/img/avatar-patrick.jpg b/assets/img/avatar-patrick.jpg
index 848a499..1ef48c5 100644
Binary files a/assets/img/avatar-patrick.jpg and b/assets/img/avatar-patrick.jpg differ
diff --git a/assets/img/bg-abonnieren-mobile.jpg b/assets/img/bg-abonnieren-mobile.jpg
new file mode 100644
index 0000000..7d93ac9
Binary files /dev/null and b/assets/img/bg-abonnieren-mobile.jpg differ
diff --git a/assets/img/bg-abonnieren.jpg b/assets/img/bg-abonnieren.jpg
new file mode 100644
index 0000000..7d93ac9
Binary files /dev/null and b/assets/img/bg-abonnieren.jpg differ
diff --git a/assets/img/bg-community-mobile.jpg b/assets/img/bg-community-mobile.jpg
index 3cf721d..7012588 100644
Binary files a/assets/img/bg-community-mobile.jpg and b/assets/img/bg-community-mobile.jpg differ
diff --git a/assets/img/bg-community.jpg b/assets/img/bg-community.jpg
index 36ff873..7012588 100644
Binary files a/assets/img/bg-community.jpg and b/assets/img/bg-community.jpg differ
diff --git a/assets/img/bg-tt-muddi.jpg b/assets/img/bg-tt-muddi.jpg
new file mode 100644
index 0000000..5bda0bd
Binary files /dev/null and b/assets/img/bg-tt-muddi.jpg differ
diff --git a/assets/img/card-bananenstift.jpg b/assets/img/card-bananenstift.jpg
index d879851..c131b02 100644
Binary files a/assets/img/card-bananenstift.jpg and b/assets/img/card-bananenstift.jpg differ
diff --git a/assets/img/gate-siegel-hasidog.jpg b/assets/img/gate-siegel-hasidog.jpg
new file mode 100644
index 0000000..fc46d32
Binary files /dev/null and b/assets/img/gate-siegel-hasidog.jpg differ
diff --git a/assets/img/hasidog-kopf-siegel.jpg b/assets/img/hasidog-kopf-siegel.jpg
new file mode 100644
index 0000000..fc46d32
Binary files /dev/null and b/assets/img/hasidog-kopf-siegel.jpg differ
diff --git a/assets/img/logo-dogfather-transparent.png b/assets/img/logo-dogfather-transparent.png
new file mode 100644
index 0000000..801f025
Binary files /dev/null and b/assets/img/logo-dogfather-transparent.png differ
diff --git a/assets/img/tt-muddi-1.jpg b/assets/img/tt-muddi-1.jpg
new file mode 100644
index 0000000..9f8d19a
Binary files /dev/null and b/assets/img/tt-muddi-1.jpg differ
diff --git a/assets/img/tt-muddi-2.jpg b/assets/img/tt-muddi-2.jpg
new file mode 100644
index 0000000..9878498
Binary files /dev/null and b/assets/img/tt-muddi-2.jpg differ
diff --git a/assets/img/tt-muddi-3.jpg b/assets/img/tt-muddi-3.jpg
new file mode 100644
index 0000000..9633ce7
Binary files /dev/null and b/assets/img/tt-muddi-3.jpg differ
diff --git a/assets/img/vw-gate-dogi.jpg b/assets/img/vw-gate-dogi.jpg
new file mode 100644
index 0000000..2c30148
Binary files /dev/null and b/assets/img/vw-gate-dogi.jpg differ
diff --git a/assets/img/vw-gate-vanvan.jpg b/assets/img/vw-gate-vanvan.jpg
new file mode 100644
index 0000000..6ed0f8d
Binary files /dev/null and b/assets/img/vw-gate-vanvan.jpg differ
diff --git a/assets/js/admin-auth.js b/assets/js/admin-auth.js
new file mode 100644
index 0000000..2ade123
--- /dev/null
+++ b/assets/js/admin-auth.js
@@ -0,0 +1,104 @@
+/* =====================================================================
+ admin-auth.js — gemeinsame Session-Logik fürs interne Team-Login
+ (dasselbe Owner-/Rollen-System wie schon fürs frühere Bewerbungs-
+ Postfach).
+
+ Session (Token + Rolle/Rechte) liegt in localStorage (Nutzer-Wunsch
+ 04.08.2026: "den Zugangscode auf der Verwaltungsseite nur einmal am Tag
+ eingeben müssen") — localStorage übersteht anders als sessionStorage
+ auch das Schließen des Tabs/Browsers, sonst wäre "einmal am Tag" nicht
+ möglich. Eigener 24-Stunden-Ablauf hier im Client (siehe SESSION_MAX_AGE_MS)
+ zusätzlich zum server-seitigen Session-Timeout (lib/auth.js, ebenfalls
+ 24 Std) — beide zusammen sorgen dafür, dass wirklich niemand länger als
+ einen Tag mit einem alten Code eingeloggt bleiben kann.
+
+ Ursprünglich für postfach.html/zugaenge.html gebaut (Bewerbungs-
+ Verwaltung) — die wurden am 01.08.2026 auf Dogis Wunsch entfernt ("nicht
+ mehr gebraucht"). Diese Datei selbst ist aber ein reines, seitenneutrales
+ Login-Werkzeug (kein Bewerbungscode) und wurde am 04.08.2026 reaktiviert
+ für verwaltung.html (Supporter-Abo-Administration) — nutzt exakt
+ denselben, weiterhin voll funktionsfähigen Backend-Login
+ (POST /auth/login), nur eine neue Seite drumherum.
+ ===================================================================== */
+
+const SESSION_KEY = "dogfather_admin_session";
+const SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1000;
+
+function getSession() {
+ try {
+ const session = JSON.parse(localStorage.getItem(SESSION_KEY) || "null");
+ if (!session) return null;
+ if (!session.loggedInAt || Date.now() - session.loggedInAt > SESSION_MAX_AGE_MS) {
+ clearSession(); // älter als 24 Std -> als abgelaufen behandeln, neuer Code nötig
+ return null;
+ }
+ return session;
+ } catch {
+ return null;
+ }
+}
+
+function setSession(data) {
+ localStorage.setItem(SESSION_KEY, JSON.stringify({ ...data, loggedInAt: Date.now() }));
+}
+
+function clearSession() {
+ localStorage.removeItem(SESSION_KEY);
+}
+
+/** Prüft, ob die aktuelle Session (Owner oder Rolle+Overrides) ein Recht hat. */
+function sessionHasPermission(session, key) {
+ if (!session) return false;
+ if (session.isOwner) return true;
+ const overrides = session.overrides || {};
+ if (Object.prototype.hasOwnProperty.call(overrides, key)) return !!overrides[key];
+ return Array.isArray(session.permissions) && session.permissions.includes(key);
+}
+
+async function apiCall(apiBaseUrl, path, body, { auth = true } = {}) {
+ const session = getSession();
+ const headers = { "Content-Type": "application/json" };
+ if (auth && session?.token) headers["Authorization"] = `Bearer ${session.token}`;
+
+ const res = await fetch(`${apiBaseUrl}${path}`, {
+ method: "POST",
+ headers,
+ body: JSON.stringify(body || {}),
+ });
+ let data;
+ try {
+ data = await res.json();
+ } catch {
+ data = { ok: false, error: "Ungültige Antwort vom Server." };
+ }
+ if (res.status === 401 && auth) {
+ clearSession(); // Session ist abgelaufen/ungültig -> zurück zum Login zwingen
+ }
+ return { httpOk: res.ok, ...data };
+}
+
+async function doLogin(apiBaseUrl, code) {
+ const res = await apiCall(apiBaseUrl, "/auth/login", { code }, { auth: false });
+ if (res.ok) {
+ setSession({
+ token: res.token,
+ isOwner: res.isOwner,
+ name: res.name,
+ username: res.username,
+ roleName: res.roleName,
+ permissions: res.permissions || [],
+ overrides: res.overrides || {},
+ });
+ }
+ return res;
+}
+
+async function doLogout(apiBaseUrl) {
+ await apiCall(apiBaseUrl, "/auth/logout", {});
+ clearSession();
+}
+
+// Für Einbindung ohne Modul-Bundler global verfügbar machen.
+if (typeof window !== "undefined") {
+ window.AdminAuth = { getSession, setSession, clearSession, sessionHasPermission, apiCall, doLogin, doLogout };
+}
diff --git a/assets/js/data-scouts.js b/assets/js/data-scouts.js
index daba2bf..fc018c6 100644
--- a/assets/js/data-scouts.js
+++ b/assets/js/data-scouts.js
@@ -39,6 +39,80 @@ const SCOUTS = [
fr: "Patrick est scout chez Team Dogi. Avec Bananenstift, il recherche et accompagne de nouveaux talents pour DogFather en tant que manager.",
pt: "Patrick é scout na Team Dogi. Junto com Bananenstift, ele busca e acompanha novos talentos para o DogFather como manager.",
},
+ // Kurzer Einstiegstext, direkt NEBEN dem Foto (siehe profil.html /
+ // person.intro). Die restliche, ausführlichere Vorstellung (bioHtml)
+ // folgt darunter im breiten Bereich — gleiches Muster wie Bananenstift.
+ intro: {
+ de: `
+ Hallo, mein Name ist Patrick. Geboren wurde ich am 18. Mai 1985 im schönen Harz. 🏔️
+ Seit Mai 2021 bin ich auf TikTok aktiv. Mein erstes Video veröffentlichte ich am 29. Mai 2021. Zu diesem Zeitpunkt hätte ich vermutlich selbst nicht erwartet, wie viele besondere Erfahrungen, Begegnungen und Erinnerungen mich auf dieser Reise noch erwarten würden.
+ `,
+ "de-CH": `
+ Hoi, ich heisse Patrick. Gebore bin ich am 18. Mai 1985 im schöne Harz. 🏔️
+ Sit Mai 2021 bin ich uf TikTok aktiv. Mis erste Video han ich am 29. Mai 2021 veröffentlicht. Zu dem Zytpunkt hätt ich wahrschindlich sälber nöd erwartet, wie vill bsundrigi Erfahrige, Begägnige und Erinnerige mich uf dere Reise no erwarted.
+ `,
+ en: `
+ Hi, my name is Patrick. I was born on May 18, 1985 in the beautiful Harz region. 🏔️
+ I've been active on TikTok since May 2021. I published my first video on May 29, 2021. Back then, I probably wouldn't have expected just how many special experiences, encounters, and memories this journey would still bring me.
+ `,
+ fr: `
+ Bonjour, je m'appelle Patrick. Je suis né le 18 mai 1985 dans la belle région du Harz. 🏔️
+ Je suis actif sur TikTok depuis mai 2021. J'ai publié ma première vidéo le 29 mai 2021. À l'époque, je ne m'attendais probablement pas à toutes les expériences, rencontres et souvenirs particuliers que ce parcours allait encore m'offrir.
+ `,
+ pt: `
+ Olá, o meu nome é Patrick. Nasci em 18 de maio de 1985, na bela região do Harz. 🏔️
+ Estou ativo no TikTok desde maio de 2021. Publiquei o meu primeiro vídeo em 29 de maio de 2021. Naquela altura, provavelmente eu mesmo não esperava quantas experiências, encontros e memórias especiais essa jornada ainda me traria.
+ `,
+ },
+ // Ausführliche Fortsetzung von Patricks Vorstellung — erscheint UNTER
+ // dem Foto im breiten Lesebereich (siehe profil.html / person.bioHtml).
+ bioHtml: {
+ de: `
+ Im Oktober 2024 wagte ich schließlich den nächsten Schritt und ging zum ersten Mal live. Seitdem durfte ich zahlreiche Menschen kennenlernen und einige von ihnen fest in mein Herz schließen. Genau diese Begegnungen und der persönliche Austausch machen TikTok für mich zu etwas Besonderem.
+ Auf den ersten Blick wirke ich gelegentlich etwas grimmig oder nachdenklich. Davon sollte man sich jedoch nicht täuschen lassen, denn mein Gesicht vermittelt manchmal einen ganz anderen Eindruck als meine tatsächliche Stimmung. 😅
+ Wer mich wirklich kennt, weiß, dass ich ein offener, humorvoller und herzlicher Mensch bin. Ich lache gerne, bin für jeden Spaß zu haben und unterstütze die Menschen, die mir wichtig sind. Ehrlichkeit, Zusammenhalt und gegenseitige Unterstützung haben für mich einen besonders hohen Stellenwert.
+ Als Scout unterstütze ich das Team dabei, neue Talente zu entdecken, ihnen zur Seite zu stehen und sie auf ihrem persönlichen Weg zu begleiten. Dabei geht es mir nicht nur um Zahlen oder Erfolge, sondern vor allem um die Menschen, ihre Ziele und ihre Entwicklung.
+ Ich freue mich auf alles, was noch vor mir liegt: neue Begegnungen, besondere Erinnerungen und viele weitere gemeinsame Momente.
+ Das bin ich – Patrick. Nicht perfekt, manchmal mit einem grimmigen Blick, aber immer mit Herz. ❤️
+ – euer Patrick ❤️
+ `,
+ "de-CH": `
+ Im Oktober 2024 han ich schliesslich de nöchscht Schritt gwagt und bi zum erste Mal live gange. Sithär han ich zahlriichi Mönsche cha kenneleärne und e paar dervo fescht i mis Herz gschlosse. Genau die Begägnige und dr persönlich Uustuusch mached TikTok für mich zu öppisem Bsundrigem.
+ Uf de erschte Blick wirk ich mängisch chli grimmig oder nachdenklich. Dervo sött me sich aber nöd teusche la, dänn mis Gsicht vermittlet mängisch en ganz andere Yidruck als mini tatsächlichi Stimmig. 😅
+ Wer mich würklich känt, weiss, dass ich en offene, humorvolle und härzliche Mönsch bin. Ich lach gern, bin für jede Spass ztue und unterstütze d Mönsche, wo mir wichtig sind. Ehrlichkeit, Zämehalt und gägesitigi Unterstützig händ für mich en bsunders hohe Stellewert.
+ Als Scout unterstütz ich s Team derbi, neui Talänt z'entdecke, ihne zur Syte z'stah und sie uf ihrem persönliche Wäg z'begleite. Debii gaht's mir nöd nume um Zahle oder Erfolg, sondern vor allem um d Mönsche, ihri Ziel und ihri Entwicklig.
+ Ich fröi mich uf alles, wo no vor mir liit: neui Begägnige, bsundrigi Erinnerige und no vill gmeinsami Momänt.
+ Das bin ich – Patrick. Nöd perfekt, mängisch mit emene grimmige Blick, aber immer mit Herz. ❤️
+ – öie Patrick ❤️
+ `,
+ en: `
+ In October 2024, I finally took the next step and went live for the first time. Since then, I've gotten to know so many people, and some of them have found a firm place in my heart. It's exactly these encounters and the personal exchange that make TikTok something special to me.
+ At first glance, I sometimes come across as a bit grumpy or lost in thought. But don't be fooled by that — my face sometimes gives a very different impression than my actual mood. 😅
+ Anyone who really knows me knows that I'm an open, humorous, and warm-hearted person. I love to laugh, I'm always up for a bit of fun, and I support the people who matter to me. Honesty, togetherness, and mutual support mean a great deal to me.
+ As a scout, I help the team discover new talent, stand by their side, and guide them on their personal journey. For me, it's not just about numbers or success — it's above all about the people, their goals, and their growth.
+ I'm looking forward to everything still ahead of me: new encounters, special memories, and many more moments together.
+ That's me – Patrick. Not perfect, sometimes with a grumpy look, but always with heart. ❤️
+ – your Patrick ❤️
+ `,
+ fr: `
+ En octobre 2024, j'ai finalement franchi l'étape suivante et j'ai fait mon premier live. Depuis, j'ai pu rencontrer de nombreuses personnes et certaines d'entre elles ont pris une vraie place dans mon cœur. Ce sont justement ces rencontres et ces échanges personnels qui rendent TikTok si particulier pour moi.
+ Au premier abord, je peux parfois paraître un peu grognon ou pensif. Il ne faut cependant pas s'y fier, car mon visage donne parfois une impression bien différente de mon humeur réelle. 😅
+ Ceux qui me connaissent vraiment savent que je suis quelqu'un d'ouvert, plein d'humour et chaleureux. J'aime rire, je suis toujours partant pour m'amuser et je soutiens les personnes qui comptent pour moi. L'honnêteté, la cohésion et le soutien mutuel ont une valeur particulièrement importante à mes yeux.
+ En tant que scout, j'aide l'équipe à découvrir de nouveaux talents, à les épauler et à les accompagner sur leur propre chemin. Ce qui compte pour moi, ce n'est pas seulement les chiffres ou le succès, mais avant tout les personnes, leurs objectifs et leur évolution.
+ Je me réjouis de tout ce qui m'attend encore : de nouvelles rencontres, des souvenirs particuliers et bien d'autres moments partagés.
+ C'est moi, Patrick. Pas parfait, parfois avec un regard grognon, mais toujours avec du cœur. ❤️
+ – votre Patrick ❤️
+ `,
+ pt: `
+ Em outubro de 2024, finalmente dei o próximo passo e fiz a minha primeira live. Desde então, pude conhecer muitas pessoas e algumas delas conquistaram um lugar especial no meu coração. São exatamente esses encontros e a troca pessoal que tornam o TikTok algo especial para mim.
+ À primeira vista, às vezes pareço um pouco carrancudo ou pensativo. No entanto, não se deixe enganar por isso, porque o meu rosto às vezes transmite uma impressão bem diferente do meu humor real. 😅
+ Quem realmente me conhece sabe que sou uma pessoa aberta, bem-humorada e carinhosa. Gosto de rir, estou sempre pronto para me divertir e apoio as pessoas que são importantes para mim. Honestidade, união e apoio mútuo têm um valor muito especial para mim.
+ Como scout, ajudo a equipe a descobrir novos talentos, a estar ao lado deles e a acompanhá-los no seu caminho pessoal. Para mim, não se trata apenas de números ou sucesso, mas sobretudo das pessoas, dos seus objetivos e do seu desenvolvimento.
+ Estou animado com tudo o que ainda está por vir: novos encontros, memórias especiais e muitos outros momentos em conjunto.
+ Este sou eu – Patrick. Não sou perfeito, às vezes com um olhar carrancudo, mas sempre com coração. ❤️
+ – o seu Patrick ❤️
+ `,
+ },
quote: "", // TODO: Zitat von Patrick einholen
social: {},
status: "aktiv",
diff --git a/assets/js/forms.js b/assets/js/forms.js
index b6c2bdb..a6120c7 100644
--- a/assets/js/forms.js
+++ b/assets/js/forms.js
@@ -17,7 +17,7 @@
const API_BASE_URL = "https://dogfather-universe-postfach.dogfather1608.workers.dev";
-const CONTACT_EMAIL = "kontakt@dogfather-universe.example"; // TODO: echte Kontakt-E-Mail eintragen
+const CONTACT_EMAIL = "dogfather1608@gmail.com";
const FORM_TYPES = {
"form-creator": "creator",
diff --git a/assets/js/i18n-abonnieren.js b/assets/js/i18n-abonnieren.js
new file mode 100644
index 0000000..176c508
--- /dev/null
+++ b/assets/js/i18n-abonnieren.js
@@ -0,0 +1,181 @@
+/* =====================================================================
+ i18n-abonnieren.js — Übersetzungs-Wörterbuch NUR für abonnieren.html
+ Alle 5 Sprachen vollständig gepflegt (de, de-CH, en, fr, pt).
+ ===================================================================== */
+
+window.I18N_PAGE = {
+ ab_eyebrow: {
+ de: "👑 DogiCrew 🩵", "de-CH": "👑 DogiCrew 🩵",
+ en: "👑 DogiCrew 🩵", fr: "👑 DogiCrew 🩵", pt: "👑 DogiCrew 🩵",
+ },
+ ab_h1: {
+ de: "Mehr als ein Abo — deine Unterstützung wird Teil unserer Geschichte",
+ "de-CH": "Meh als es Abo — dini Unterstützig wird Teil vo üser Gschicht",
+ en: "More than a subscription — your support becomes part of our story",
+ fr: "Plus qu'un abonnement — ton soutien devient une part de notre histoire",
+ pt: "Mais do que uma assinatura — seu apoio se torna parte da nossa história",
+ },
+ ab_lead: {
+ de: "Kein gewöhnlicher Shop. Ein exklusives Dankeschön- und Treueprogramm für alle, die Dogfather dauerhaft unterstützen.",
+ "de-CH": "Kein gwöhnlicher Shop. Es exklusives Dankeschön- und Treueprogramm für alli, wo Dogfather duurhaft unterstütze.",
+ en: "Not a regular shop. An exclusive thank-you and loyalty program for everyone who supports Dogfather long-term.",
+ fr: "Pas une boutique ordinaire. Un programme exclusif de remerciement et de fidélité pour tous ceux qui soutiennent Dogfather durablement.",
+ pt: "Não é uma loja comum. Um programa exclusivo de agradecimento e fidelidade para todos que apoiam Dogfather a longo prazo.",
+ },
+ ab_text1: {
+ de: "Dieses Abonnement ist kein gewöhnlicher Shop und kein einfacher Kauf. Es ist für die Menschen gedacht, die Dogfather dauerhaft unterstützen und ein besonderer Teil dieser gemeinsamen Reise sein möchten.",
+ "de-CH": "Das Abonnement isch kein gwöhnlicher Shop und kein eifache Kauf. Es isch für die Mönsche gedacht, wo Dogfather duurhaft unterstütze und en bsundere Teil vo dere gmeinsame Reise si wänd.",
+ en: "This subscription is not a regular shop and not a simple purchase. It's for the people who support Dogfather long-term and want to be a special part of this shared journey.",
+ fr: "Cet abonnement n'est ni une boutique ordinaire ni un simple achat. Il est destiné aux personnes qui soutiennent Dogfather durablement et souhaitent faire partie de ce voyage commun.",
+ pt: "Esta assinatura não é uma loja comum nem uma compra simples. É para as pessoas que apoiam Dogfather a longo prazo e querem ser parte especial dessa jornada em comum.",
+ },
+ ab_text2: {
+ de: "Für 4,99 € im Monat erhältst du Zugang zu einem exklusiven Supporter-Bereich, besonderen Inhalten und einer persönlichen Treuekollektion. Mit jedem erfolgreich bezahlten Meilenstein wächst deine Sammlung: vom kleinen Hasen-Teddy über besondere Jubiläumsprämien bis zum exklusiven Hoodie nach 30 Monaten. Danach beginnt für dich eine neue Kollektion — mit neuen Designs, neuen Erinnerungen und derselben Bedeutung.",
+ "de-CH": "Für 4,99 € im Monat überchunnsch du Zugang zu emene exklusive Supporter-Bereich, bsundere Inhalt und ere persönliche Treuekollektion. Mit jedem erfolgriich bezahlte Meilestei wachst dini Sammlig: vom chline Hase-Teddy über bsundere Jubiläumsprämie bis zum exklusive Hoodie nach 30 Monet. Dernah fangt für dich e neui Kollektion a — mit neue Designs, neue Erinnerige und derselbe Bedeutig.",
+ en: "For 4.99 € a month, you get access to an exclusive supporter area, special content, and a personal loyalty collection. With every successfully paid milestone, your collection grows — from the small bunny teddy through special anniversary rewards up to the exclusive hoodie after 30 months. After that, a new collection begins for you, with new designs, new memories, and the same meaning.",
+ fr: "Pour 4,99 € par mois, tu as accès à un espace supporter exclusif, à du contenu spécial et à une collection de fidélité personnelle. Avec chaque mois payé avec succès, ta collection grandit : du petit lapin en peluche aux récompenses d'anniversaire jusqu'au hoodie exclusif après 30 mois. Ensuite, une nouvelle collection commence pour toi — avec de nouveaux designs, de nouveaux souvenirs et la même signification.",
+ pt: "Por 4,99 € por mês, você tem acesso a uma área exclusiva de apoiadores, conteúdo especial e uma coleção pessoal de fidelidade. A cada mês pago com sucesso, sua coleção cresce: do pequeno coelhinho de pelúcia até recompensas especiais de aniversário e o hoodie exclusivo após 30 meses. Depois disso, começa uma nova coleção para você — com novos designs, novas memórias e o mesmo significado.",
+ },
+ ab_vorteil1: {
+ de: "Exklusiver Supporter-Bereich", "de-CH": "Exklusive Supporter-Bereich",
+ en: "Exclusive supporter area", fr: "Espace supporter exclusif", pt: "Área exclusiva de apoiador",
+ },
+ ab_vorteil2: {
+ de: "Kleiner Hasen-Teddy nach 6 bezahlten Monaten", "de-CH": "Chline Hase-Teddy nach 6 bezahlte Monet",
+ en: "Small bunny teddy after 6 paid months", fr: "Petit lapin en peluche après 6 mois payés", pt: "Coelhinho de pelúcia pequeno após 6 meses pagos",
+ },
+ ab_vorteil3: {
+ de: "Mittelgroßer Hasen-Teddy nach 12 bezahlten Monaten", "de-CH": "Mittelgrosse Hase-Teddy nach 12 bezahlte Monet",
+ en: "Medium bunny teddy after 12 paid months", fr: "Lapin en peluche moyen après 12 mois payés", pt: "Coelhinho de pelúcia médio após 12 meses pagos",
+ },
+ ab_vorteil4: {
+ de: "Großer Hasen-Teddy nach 18 bezahlten Monaten", "de-CH": "Grosse Hase-Teddy nach 18 bezahlte Monet",
+ en: "Large bunny teddy after 18 paid months", fr: "Grand lapin en peluche après 18 mois payés", pt: "Coelhinho de pelúcia grande após 18 meses pagos",
+ },
+ ab_vorteil5: {
+ de: "Tasse & Autogrammkarte nach 24 bezahlten Monaten", "de-CH": "Tasse & Autogrammchärtli nach 24 bezahlte Monet",
+ en: "Mug & autograph card after 24 paid months", fr: "Tasse et carte autographe après 24 mois payés", pt: "Caneca e cartão autografado após 24 meses pagos",
+ },
+ ab_vorteil6: {
+ de: "Exklusiver Hoodie nach 30 bezahlten Monaten", "de-CH": "Exklusive Hoodie nach 30 bezahlte Monet",
+ en: "Exclusive hoodie after 30 paid months", fr: "Hoodie exclusif après 30 mois payés", pt: "Hoodie exclusivo após 30 meses pagos",
+ },
+ ab_vorteil7: {
+ de: "Neue Supporter-Kollektion nach jedem abgeschlossenen Zyklus", "de-CH": "Neui Supporter-Kollektion nach jedem abgschlossene Zyklus",
+ en: "New supporter collection after every completed cycle", fr: "Nouvelle collection supporter après chaque cycle terminé", pt: "Nova coleção de apoiador após cada ciclo concluído",
+ },
+ ab_vanvan_h2: {
+ de: "Die Teddy-Kollektion — in Kooperation mit VanVan", "de-CH": "Die Teddy-Kollektion — i Kooperation mit VanVan",
+ en: "The Teddy Collection — in Cooperation with VanVan", fr: "La collection de peluches — en coopération avec VanVan", pt: "A coleção de pelúcias — em cooperação com VanVan",
+ },
+ ab_vanvan_text: {
+ de: "Die DogiCrew 🩵 gehört Dogfather. Die Hasen-Teddys und die dazugehörigen Sammlerkollektionen entstehen jedoch in Zusammenarbeit mit VanVan — der rechten Hand von Dogfather, seiner wichtigsten Vertrauensperson und einem festen Bestandteil der Team-Dogi-Familie. Sie stehen für ihre besondere Rolle, den Zusammenhalt innerhalb der Community und die Verbindung zwischen Dogfather, VanVan und allen Menschen, die diesen gemeinsamen Weg dauerhaft unterstützen.",
+ "de-CH": "D DogiCrew 🩵 ghört Dogfather. D Hase-Teddys und die dezueghörige Sammlerkollektione entstönd aber i Zämearbeit mit VanVan — dr rechte Hand vo Dogfather, sinere wichtigste Vertrouensperson und emene feste Bestandteil vo dr Team-Dogi-Familie. Sie stönd für ihri bsundrigi Roll, dr Zämehalt innerhalb vo dr Community und d Verbindig zwüsche Dogfather, VanVan und alle Mönsche, wo dää gmeinsame Wäg duurhaft unterstütze.",
+ en: "The DogiCrew 🩵 belongs to Dogfather. The bunny teddies and their collector collections, however, are created in collaboration with VanVan — Dogfather's right hand, his most important trusted person, and a fixed part of the Team Dogi family. They represent her special role, the togetherness within the community, and the connection between Dogfather, VanVan, and everyone who supports this shared journey long-term.",
+ fr: "La DogiCrew 🩵 appartient à Dogfather. Les lapins en peluche et leurs collections de collectionneur sont cependant créés en collaboration avec VanVan — le bras droit de Dogfather, sa personne de confiance la plus importante et un membre à part entière de la famille Team Dogi. Ils représentent son rôle particulier, la cohésion au sein de la communauté et le lien entre Dogfather, VanVan et toutes les personnes qui soutiennent durablement ce parcours commun.",
+ pt: "A DogiCrew 🩵 pertence ao Dogfather. Os coelhinhos de pelúcia e suas coleções de colecionador, porém, nascem em colaboração com VanVan — a mão direita de Dogfather, sua pessoa de confiança mais importante e parte fixa da família Team Dogi. Eles representam seu papel especial, a união dentro da comunidade e a conexão entre Dogfather, VanVan e todas as pessoas que apoiam essa jornada em comum a longo prazo.",
+ },
+ ab_kollektion_coming_soon: {
+ de: `Coming soon…`, "de-CH": `Coming soon…`,
+ en: `Coming soon…`, fr: `Coming soon…`, pt: `Coming soon…`,
+ },
+ ab_preis_eyebrow: {
+ de: "DogiCrew 🩵", "de-CH": "DogiCrew 🩵",
+ en: "DogiCrew 🩵", fr: "DogiCrew 🩵", pt: "DogiCrew 🩵",
+ },
+ ab_preis_pro_monat: {
+ de: "/ Monat", "de-CH": "/ Monet",
+ en: "/ month", fr: "/ mois", pt: "/ mês",
+ },
+ ab_bed1: {
+ de: "Monatliche automatische Zahlung über PayPal", "de-CH": "Monetlichi automatischi Zahlig über PayPal",
+ en: "Monthly automatic payment via PayPal", fr: "Paiement mensuel automatique via PayPal", pt: "Pagamento mensal automático via PayPal",
+ },
+ ab_bed2: {
+ de: "Keine Mindestlaufzeit, jederzeit kündbar", "de-CH": "Kei Mindestlouffzyt, jederzyt kündbar",
+ en: "No minimum term, cancel anytime", fr: "Aucune durée minimale, résiliable à tout moment", pt: "Sem prazo mínimo, cancele quando quiser",
+ },
+ ab_bed3: {
+ de: "Kündigung zum Ende des bereits bezahlten Zeitraums", "de-CH": "Kündigung zum Ändi vom scho bezahlte Zytruum",
+ en: "Cancellation takes effect at the end of the already paid period", fr: "La résiliation prend effet à la fin de la période déjà payée", pt: "O cancelamento vale até o fim do período já pago",
+ },
+ ab_bed4: {
+ de: "Keine versteckten Zusatzkosten", "de-CH": "Kei versteckti Zuestzchöschte",
+ en: "No hidden extra costs", fr: "Aucun coût caché supplémentaire", pt: "Sem custos adicionais ocultos",
+ },
+ ab_laedt: {
+ de: "Einen Moment …", "de-CH": "En Momänt …",
+ en: "One moment …", fr: "Un instant …", pt: "Um momento …",
+ },
+ ab_feld_name: {
+ de: "Name oder Anzeigename", "de-CH": "Name oder Aazeigename",
+ en: "Name or display name", fr: "Nom ou pseudo", pt: "Nome ou nome de exibição",
+ },
+ ab_feld_tiktok: {
+ de: "TikTok-Benutzername", "de-CH": "TikTok-Benutzername",
+ en: "TikTok username", fr: "Nom d'utilisateur TikTok", pt: "Nome de usuário do TikTok",
+ },
+ ab_feld_email: {
+ de: "E-Mail-Adresse", "de-CH": "E-Mail-Adrässe",
+ en: "Email address", fr: "Adresse e-mail", pt: "Endereço de e-mail",
+ },
+ ab_btn_register: {
+ de: "Registrieren", "de-CH": "Registriere",
+ en: "Register", fr: "S'inscrire", pt: "Registrar",
+ },
+ ab_switch_login: {
+ de: "Schon Supporter? Hier einloggen", "de-CH": "Scho Supporter? Da yylogge",
+ en: "Already a supporter? Log in here", fr: "Déjà supporter ? Connecte-toi ici", pt: "Já é apoiador? Faça login aqui",
+ },
+ ab_btn_login_code: {
+ de: "Login-Code anfordern", "de-CH": "Login-Code aafordere",
+ en: "Request login code", fr: "Demander un code de connexion", pt: "Solicitar código de login",
+ },
+ ab_switch_register: {
+ de: "Noch kein Konto? Jetzt registrieren", "de-CH": "No kein Konto? Jetzt registriere",
+ en: "No account yet? Register now", fr: "Pas encore de compte ? Inscris-toi maintenant", pt: "Ainda não tem conta? Registre-se agora",
+ },
+ ab_code_hinweis: {
+ de: "Wir haben dir einen 6-stelligen Code per E-Mail geschickt.", "de-CH": "Mir händ dir en 6-stellige Code per E-Mail gschickt.",
+ en: "We've sent you a 6-digit code by email.", fr: "Nous t'avons envoyé un code à 6 chiffres par e-mail.", pt: "Enviamos um código de 6 dígitos para o seu e-mail.",
+ },
+ ab_feld_code: {
+ de: "Code", "de-CH": "Code",
+ en: "Code", fr: "Code", pt: "Código",
+ },
+ ab_btn_code_verify: {
+ de: "Bestätigen", "de-CH": "Bestätige",
+ en: "Confirm", fr: "Confirmer", pt: "Confirmar",
+ },
+ ab_eingeloggt_als: {
+ de: "Eingeloggt. Noch ein letzter Schritt:", "de-CH": "Yyglogget. No en letschte Schritt:",
+ en: "Logged in. One last step:", fr: "Connecté. Une dernière étape :", pt: "Conectado. Um último passo:",
+ },
+ ab_oder_email: {
+ de: "oder mit E-Mail", "de-CH": "oder mit E-Mail",
+ en: "or with email", fr: "ou avec e-mail", pt: "ou com e-mail",
+ },
+ ab_google_consent_btn: {
+ de: "Mit Google anmelden", "de-CH": "Mit Google aamelde",
+ en: "Continue with Google", fr: "Continuer avec Google", pt: "Continuar com o Google",
+ },
+ ab_google_consent_hint: {
+ de: "Beim Klick wird eine Verbindung zu Google hergestellt (deine IP-Adresse wird dabei übertragen).",
+ "de-CH": "Bim Klick wird e Verbindig zu Google hergstellt (dini IP-Adrässe wird debii übertreit).",
+ en: "Clicking this connects to Google (your IP address is transmitted in the process).",
+ fr: "En cliquant, une connexion à Google est établie (ton adresse IP est alors transmise).",
+ pt: "Ao clicar, é feita uma conexão com o Google (seu endereço IP é transmitido nesse processo).",
+ },
+ ab_paypal_laedt: {
+ de: "Zahlungsoptionen werden geladen …", "de-CH": "Zahligsoptione wärded gladen …",
+ en: "Loading payment options …", fr: "Chargement des options de paiement …", pt: "Carregando opções de pagamento …",
+ },
+ ab_bereits_supporter: {
+ de: "Du bist bereits Supporter! 🩵", "de-CH": "Du bisch scho Supporter! 🩵",
+ en: "You're already a supporter! 🩵", fr: "Tu es déjà supporter ! 🩵", pt: "Você já é apoiador! 🩵",
+ },
+ ab_btn_zum_bereich: {
+ de: "Zum Supporter-Bereich", "de-CH": "Zum Supporter-Bereich",
+ en: "Go to supporter area", fr: "Aller à l'espace supporter", pt: "Ir para a área do apoiador",
+ },
+};
diff --git a/assets/js/i18n-index.js b/assets/js/i18n-index.js
index 8950bc3..e461778 100644
--- a/assets/js/i18n-index.js
+++ b/assets/js/i18n-index.js
@@ -177,8 +177,8 @@ window.I18N_PAGE = {
},
idx_team_h2: { de: "Team Dogi 🩵", "de-CH": "Team Dogi 🩵", en: "Team Dogi 🩵", fr: "Team Dogi 🩵", pt: "Team Dogi 🩵" },
idx_team_lead: {
- de: "Von VanVan & die Modis — die Menschen, die DOGFATHER UNIVERSE tragen.",
- "de-CH": "Vo VanVan & de Modis — die Mönsche, wo DOGFATHER UNIVERSE tröget.",
+ de: "VanVan & Modis — die Menschen, die DOGFATHER UNIVERSE tragen.",
+ "de-CH": "VanVan & Modis — die Mönsche, wo DOGFATHER UNIVERSE tröget.",
en: "From VanVan & the Modis — the people who carry DOGFATHER UNIVERSE.",
fr: "De VanVan et des Modis — les personnes qui portent DOGFATHER UNIVERSE.",
pt: "De VanVan e dos Modis — as pessoas que sustentam o DOGFATHER UNIVERSE.",
diff --git a/assets/js/i18n-kontakt.js b/assets/js/i18n-kontakt.js
index 9d4962a..68c0ee3 100644
--- a/assets/js/i18n-kontakt.js
+++ b/assets/js/i18n-kontakt.js
@@ -38,42 +38,15 @@ window.I18N_PAGE = {
de: "Impressum", "de-CH": "Impressum",
en: "Legal Notice", fr: "Mentions légales", pt: "Aviso legal",
},
- kt_impressum_p: {
- de: `Angaben gemäß § 5 TMG
- [Firmenname / Betreiber]
- [Anschrift]
- [Kontakt]
- Vertreten durch: [Name]`,
- "de-CH": `Angabe gmäss § 5 TMG
- [Firmename / Betriiber]
- [Adrässe]
- [Kontakt]
- Vertrete durch: [Name]`,
- en: `Information according to § 5 TMG (German Telemedia Act)
- [Company name / operator]
- [Address]
- [Contact]
- Represented by: [Name]`,
- fr: `Mentions légales selon l'article § 5 TMG (loi allemande sur les télémédias)
- [Nom de l'entreprise / exploitant]
- [Adresse]
- [Contact]
- Représenté par : [Nom]`,
- pt: `Informações conforme o § 5 TMG (lei alemã de telemídia)
- [Nome da empresa / responsável]
- [Endereço]
- [Contato]
- Representado por: [Nome]`,
+ kt_impressum_hinweis: {
+ de: `Rechtstexte werden auf Deutsch bereitgestellt, um Übersetzungsfehler bei rechtlich verbindlichen Angaben zu vermeiden.`,
+ "de-CH": `Rächtstäxt wärded uf Dütsch bereitgstellt, dass käni Übersetzigsfähler bi rächtlich verbindlechne Angabe entstönd.`,
+ en: `Legal texts are provided in German only, to avoid translation errors in legally binding information.`,
+ fr: `Les mentions légales ne sont fournies qu'en allemand, afin d'éviter toute erreur de traduction dans des informations juridiquement contraignantes.`,
+ pt: `Os textos legais são fornecidos apenas em alemão, para evitar erros de tradução em informações juridicamente vinculativas.`,
},
kt_datenschutz_h2: {
de: "Datenschutz", "de-CH": "Datenschutz",
en: "Privacy Policy", fr: "Protection des données", pt: "Privacidade",
},
- kt_datenschutz_p: {
- de: "Diese Website wird aktuell lokal/offline entwickelt und ist noch nicht öffentlich gehostet. Sobald ein Hosting-Anbieter feststeht und Formulare (z.B. Bewerbung) echte Daten versenden, wird hier eine vollständige Datenschutzerklärung ergänzt (Verarbeitungszwecke, Rechtsgrundlage, Speicherdauer, Empfänger, Rechte der Nutzer:innen).",
- "de-CH": "Die Website wird aktuell lokal/offline entwicklet und isch no nöd öffentlich gehostet. Sobald en Hosting-Aabieter feststoht und Formulär (z.B. Bewärbig) echti Date versände, wird da e vollständigi Dateschutzerklärig ergänzt (Verarbeitigszwäck, Rächtsgrundlag, Speicherduur, Empfänger, Rächt vo de Nutzer:inne).",
- en: "This website is currently being developed locally/offline and is not yet publicly hosted. Once a hosting provider is finalized and forms (e.g. applications) send real data, a complete privacy policy will be added here (processing purposes, legal basis, retention period, recipients, users' rights).",
- fr: "Ce site est actuellement développé en local/hors ligne et n'est pas encore hébergé publiquement. Dès qu'un hébergeur sera fixé et que les formulaires (p. ex. candidature) transmettront de vraies données, une déclaration de confidentialité complète sera ajoutée ici (finalités du traitement, base légale, durée de conservation, destinataires, droits des utilisateurs·rices).",
- pt: "Este site está atualmente sendo desenvolvido localmente/offline e ainda não está hospedado publicamente. Assim que um provedor de hospedagem for definido e os formulários (por exemplo, candidatura) enviarem dados reais, uma política de privacidade completa será adicionada aqui (finalidades do tratamento, base legal, prazo de armazenamento, destinatários, direitos dos usuários).",
- },
};
diff --git a/assets/js/i18n-links.js b/assets/js/i18n-links.js
index 11eb61b..c096dd4 100644
--- a/assets/js/i18n-links.js
+++ b/assets/js/i18n-links.js
@@ -23,6 +23,36 @@ window.I18N_PAGE = {
de: "Nur auf Anfrage", "de-CH": "Nur uf Aafrag",
en: "By request only", fr: "Uniquement sur demande", pt: "Somente mediante solicitação",
},
+ lk_rabatt_badge: {
+ de: "🔒 Nur für Supporter", "de-CH": "🔒 Nur für Supporter",
+ en: "🔒 Supporters only", fr: "🔒 Réservé aux Supporters", pt: "🔒 Somente para apoiadores",
+ },
+ lk_rabatt_badge_frei: {
+ de: "✓ Freigeschaltet", "de-CH": "✓ Freigschalte",
+ en: "✓ Unlocked", fr: "✓ Débloqué", pt: "✓ Desbloqueado",
+ },
+ lk_rabatt_h3: {
+ de: "Rabattcodes", "de-CH": "Rabattcodes",
+ en: "Discount Codes", fr: "Codes de réduction", pt: "Códigos de desconto",
+ },
+ lk_rabatt_desc: {
+ de: "Codes von Dogis Partnern — exklusiv für Dogfather-Supporter.",
+ "de-CH": "Codes vo Dogis Partner — exklusiv für Dogfather-Supporter.",
+ en: "Codes from Dogi's partners — exclusive to Dogfather supporters.",
+ fr: "Des codes chez les partenaires de Dogi — exclusifs aux Supporters Dogfather.",
+ pt: "Códigos dos parceiros do Dogi — exclusivos para apoiadores do Dogfather.",
+ },
+ lk_rabatt_gesperrt_hinweis: {
+ de: "🔒 Rabattcodes sind nur für Dogfather-Supporter zugänglich. Werde jetzt Supporter, um sie freizuschalten.",
+ "de-CH": "🔒 Rabattcodes sind nur für Dogfather-Supporter zuegänglich. Wird jetzt Supporter, zum sie freizschalte.",
+ en: "🔒 Discount codes are only available to Dogfather supporters. Become a supporter now to unlock them.",
+ fr: "🔒 Les codes de réduction sont réservés aux Supporters Dogfather. Deviens Supporter dès maintenant pour les débloquer.",
+ pt: "🔒 Os códigos de desconto são exclusivos para apoiadores do Dogfather. Torne-se apoiador agora para desbloqueá-los.",
+ },
+ lk_rabatt_btn_abo: {
+ de: "Jetzt Supporter werden 👑", "de-CH": "Jetzt Supporter werde 👑",
+ en: "Become a supporter now 👑", fr: "Devenir Supporter maintenant 👑", pt: "Tornar-se apoiador agora 👑",
+ },
lk_shop_hint: {
de: "Folgt in Kürze", "de-CH": "Chunnt bald",
en: "Coming soon", fr: "Bientôt disponible", pt: "Em breve",
diff --git a/assets/js/i18n-rabattcodes.js b/assets/js/i18n-rabattcodes.js
new file mode 100644
index 0000000..bf304c6
--- /dev/null
+++ b/assets/js/i18n-rabattcodes.js
@@ -0,0 +1,64 @@
+/* =====================================================================
+ i18n-rabattcodes.js — Übersetzungs-Wörterbuch NUR für rabattcodes.html
+ Alle 5 Sprachen vollständig gepflegt (de, de-CH, en, fr, pt).
+ ===================================================================== */
+
+window.I18N_PAGE = {
+ rc_eyebrow: {
+ de: "🔒 Nur für Supporter", "de-CH": "🔒 Nur für Supporter",
+ en: "🔒 Supporters only", fr: "🔒 Réservé aux Supporters", pt: "🔒 Somente para apoiadores",
+ },
+ rc_h1: {
+ de: "Rabattcodes", "de-CH": "Rabattcodes",
+ en: "Discount Codes", fr: "Codes de réduction", pt: "Códigos de desconto",
+ },
+ rc_lead: {
+ de: "Codes von Dogis Partnern — exklusiv für Dogfather-Supporter.",
+ "de-CH": "Codes vo Dogis Partner — exklusiv für Dogfather-Supporter.",
+ en: "Codes from Dogi's partners — exclusive to Dogfather supporters.",
+ fr: "Des codes chez les partenaires de Dogi — exclusifs aux Supporters Dogfather.",
+ pt: "Códigos dos parceiros do Dogi — exclusivos para apoiadores do Dogfather.",
+ },
+ rc_gesperrt_titel: {
+ de: "Nur für Dogfather-Supporter", "de-CH": "Nur für Dogfather-Supporter",
+ en: "Dogfather supporters only", fr: "Réservé aux Supporters Dogfather", pt: "Somente para apoiadores do Dogfather",
+ },
+ rc_gesperrt_text: {
+ de: "Rabattcodes sind ein exklusiver Vorteil der DogiCrew 🩵. Werde jetzt Teil davon, um sie freizuschalten, sobald die ersten Kooperationen live sind.",
+ "de-CH": "Rabattcodes sind e exklusive Vorteil vo dr DogiCrew 🩵. Wird jetzt Teil devo, zum sie freizschalte, sobald die erste Kooperatione live sind.",
+ en: "Discount codes are an exclusive perk of the DogiCrew 🩵. Join now to unlock them as soon as the first collaborations go live.",
+ fr: "Les codes de réduction sont un avantage exclusif de la DogiCrew 🩵. Rejoins-la dès maintenant pour les débloquer dès que les premières collaborations seront lancées.",
+ pt: "Os códigos de desconto são um benefício exclusivo da DogiCrew 🩵. Junte-se agora para desbloqueá-los assim que as primeiras parcerias forem lançadas.",
+ },
+ rc_btn_abo: {
+ de: "Jetzt Supporter werden 👑", "de-CH": "Jetzt Supporter werde 👑",
+ en: "Become a supporter now 👑", fr: "Devenir Supporter maintenant 👑", pt: "Tornar-se apoiador agora 👑",
+ },
+ rc_titel: {
+ de: "Codes folgen in Kürze", "de-CH": "Codes chömed bald",
+ en: "Codes coming soon", fr: "Codes bientôt disponibles", pt: "Códigos em breve",
+ },
+ rc_text: {
+ de: "Als Creator bekommt DogFather eigene Rabattcodes bei ausgewählten Partnern — die gibt er direkt an Team Dogi weiter. Sobald die ersten Kooperationen live sind, stehen die Codes genau hier.",
+ "de-CH": "Als Creator überchunnt DogFather eigeni Rabattcodes bi usgwählte Partner — die gitt er direkt a Team Dogi wyter. Sobald die erste Kooperatione live sind, stönd die Codes genau da.",
+ en: "As a creator, DogFather gets his own discount codes from selected partners — and passes them straight on to Team Dogi. As soon as the first collaborations go live, the codes will show up right here.",
+ fr: "En tant que créateur, DogFather reçoit ses propres codes de réduction chez des partenaires sélectionnés — et les transmet directement à Team Dogi. Dès que les premières collaborations seront lancées, les codes apparaîtront ici.",
+ pt: "Como criador, DogFather recebe seus próprios códigos de desconto de parceiros selecionados — e os repassa direto para a Team Dogi. Assim que as primeiras parcerias forem lançadas, os códigos aparecerão bem aqui.",
+ },
+ rc_beispiel_marke: {
+ de: "Partner-Marke", "de-CH": "Partner-Marke",
+ en: "Partner brand", fr: "Marque partenaire", pt: "Marca parceira",
+ },
+ rc_beispiel_code: {
+ de: `Code „DOGFATHER" · –10 %`, "de-CH": `Code „DOGFATHER" · –10 %`,
+ en: `Code "DOGFATHER" · –10%`, fr: `Code « DOGFATHER » · –10 %`, pt: `Código "DOGFATHER" · –10%`,
+ },
+ rc_btn_kontakt: {
+ de: "Auf dem Laufenden bleiben", "de-CH": "Am Bau blibe",
+ en: "Stay in the loop", fr: "Rester informé(e)", pt: "Fique por dentro",
+ },
+ rc_btn_zurueck: {
+ de: "← Zurück zu allen Links", "de-CH": "← Zrugg zu alle Links",
+ en: "← Back to all links", fr: "← Retour à tous les liens", pt: "← Voltar a todos os links",
+ },
+};
diff --git a/assets/js/i18n-supporter.js b/assets/js/i18n-supporter.js
new file mode 100644
index 0000000..79b86c4
--- /dev/null
+++ b/assets/js/i18n-supporter.js
@@ -0,0 +1,105 @@
+/* =====================================================================
+ i18n-supporter.js — Übersetzungs-Wörterbuch NUR für supporter.html
+ Alle 5 Sprachen vollständig gepflegt (de, de-CH, en, fr, pt).
+
+ Hinweis: Die dynamisch aus der API befüllten Inhalte (Status-Labels,
+ Prämien-Namen, Archiv-Texte) werden aktuell noch fest auf Deutsch
+ generiert (siehe
+