Compare commits
@@ -18,10 +18,10 @@
|
|||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
<title>Seite nicht gefunden — DOGFATHER UNIVERSE</title>
|
<title>Seite nicht gefunden — DOGFATHER UNIVERSE</title>
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-404.js"></script>
|
<script src="assets/js/i18n-404.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
|
<meta name="twitter:description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
|
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
|
||||||
@@ -216,6 +216,104 @@
|
|||||||
.abo-switch a { color: var(--accent); cursor: pointer; font-weight: 600; }
|
.abo-switch a { color: var(--accent); cursor: pointer; font-weight: 600; }
|
||||||
.abo-switch a:hover { text-decoration: underline; }
|
.abo-switch a:hover { text-decoration: underline; }
|
||||||
|
|
||||||
|
/* Registrieren/Einloggen-Umschalter (21.08.2026, Nutzer-Wunsch: "die
|
||||||
|
sollen dann sofort immer den anmelde knopf auswählen können") — zwei
|
||||||
|
Segmente in einer Pille, aktiver Tab in Gold hervorgehoben, damit die
|
||||||
|
Wahl sofort ins Auge fällt, noch bevor überhaupt ein Formularfeld zu
|
||||||
|
sehen ist. Augenschonend: kein grelles Weiß, ruhiger Farbwechsel. */
|
||||||
|
/* =====================================================================
|
||||||
|
Aktionsleiste (21.08.2026) — EIN Element für beide Wege, statt vorher
|
||||||
|
doppeltem "Registrieren" (Umschalter oben + Knopf unten). Links die
|
||||||
|
eigentliche Aktion in Gold (unverändert im bestehenden Marken-Look),
|
||||||
|
rechts der Wechsel in Babyblau — der zweiten Hausfarbe der Seite
|
||||||
|
(theme-dogfather.css). Beide Hälften nutzen dieselbe Technik wie die
|
||||||
|
bestehenden Gold-Knöpfe (wandernder Verlauf per background-position +
|
||||||
|
ein durchlaufender Glanzstreifen als ::after) — dadurch wirken sie wie
|
||||||
|
ein zusammengehöriges Paar statt wie zwei fremde Knöpfe.
|
||||||
|
Augenschonend (Projektregel): kein Blinken/Flackern, nur ruhig
|
||||||
|
wandernde Verläufe, und bei prefers-reduced-motion steht alles still.
|
||||||
|
===================================================================== */
|
||||||
|
.abo-aktionsleiste {
|
||||||
|
display: flex; gap: .6rem; margin-top: .4rem;
|
||||||
|
}
|
||||||
|
/* flex-basis "auto" statt "0": jede Hälfte startet bei ihrer eigenen
|
||||||
|
Textbreite und wächst dann anteilig mit. Dadurch bekommt automatisch
|
||||||
|
die Seite mit dem längeren Text mehr Platz — egal ob das gerade links
|
||||||
|
("Login-Code anfordern") oder rechts ("Schon registriert? Einloggen")
|
||||||
|
ist. Mit fester 0-Basis wären beide exakt gleich breit gewesen und der
|
||||||
|
jeweils längere Text wäre auf zwei Zeilen umgebrochen. */
|
||||||
|
.abo-aktionsleiste > button {
|
||||||
|
position: relative; isolation: isolate; overflow: hidden;
|
||||||
|
flex: 1 1 auto; min-width: 0;
|
||||||
|
display: flex; align-items: center; justify-content: center; text-align: center;
|
||||||
|
padding: 1rem .95rem; border-radius: 14px;
|
||||||
|
font-weight: 800; font-size: .92rem; line-height: 1.25;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform .2s ease, box-shadow .2s ease;
|
||||||
|
}
|
||||||
|
.abo-aktionsleiste > button::after {
|
||||||
|
content: ""; position: absolute; inset: 0; z-index: 1;
|
||||||
|
background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.85) 48%, transparent 66%);
|
||||||
|
background-size: 240% 100%;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
animation: abo-btn-glare 3.2s ease-in-out infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.abo-aktionsleiste > button:hover { transform: translateY(-3px) scale(1.015); }
|
||||||
|
|
||||||
|
/* Links: Gold — exakt die bestehende Knopf-Optik der Seite. */
|
||||||
|
.abo-aktion-haupt {
|
||||||
|
color: #1a1408;
|
||||||
|
border: 1.5px solid rgba(255,233,168,.65);
|
||||||
|
background: linear-gradient(120deg, #ffe9a8 0%, #d4af37 30%, #ffe9a8 55%, #fff2c9 78%, #d4af37 100%);
|
||||||
|
background-size: 280% 100%;
|
||||||
|
animation: abo-btn-shine 6s ease-in-out infinite;
|
||||||
|
box-shadow: 0 10px 26px -12px rgba(212,175,55,.55);
|
||||||
|
}
|
||||||
|
.abo-aktion-haupt:hover {
|
||||||
|
box-shadow: 0 16px 34px -10px rgba(212,175,55,.65), 0 0 26px rgba(212,175,55,.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Babyblau — gleiche Machart, kräftigeres Sky-Blau statt des sehr
|
||||||
|
blassen Standard-Babyblaus, damit es neben dem Gold nicht untergeht
|
||||||
|
(dieselbe Lektion wie auf der Verwaltungsseite). Dunkler Tiefsee-Text
|
||||||
|
darauf für klaren Kontrast; zusätzlich ein sanft atmender Ring, der die
|
||||||
|
Kachel leicht leuchten lässt, ohne zu blenden.
|
||||||
|
|
||||||
|
Zwei Namen, eine Optik: ".abo-aktion-neben" ist der Wechsel-Knopf neben
|
||||||
|
einer Gold-Aktion (Registrierungs-Seite), ".abo-aktion-blau" ist ein
|
||||||
|
Knopf, der selbst DIE Hauptaktion ist und trotzdem blau sein soll
|
||||||
|
(Einloggen-Seite, Nutzer-Wunsch 21.08.2026). Bewusst getrennte Namen,
|
||||||
|
damit später klar bleibt, warum ein Knopf blau ist. */
|
||||||
|
.abo-aktion-neben,
|
||||||
|
.abo-aktion-blau {
|
||||||
|
color: #05202b;
|
||||||
|
border: 1.5px solid rgba(224,247,254,.7);
|
||||||
|
background: linear-gradient(120deg, #e0f7fe 0%, #38bdf8 30%, #7dd3fc 55%, #f0fbff 78%, #38bdf8 100%);
|
||||||
|
background-size: 280% 100%;
|
||||||
|
animation: abo-btn-shine 6s ease-in-out infinite, abo-blau-atem 4.5s ease-in-out infinite;
|
||||||
|
box-shadow: 0 10px 26px -12px rgba(56,189,248,.6), 0 0 0 0 rgba(125,211,252,.5);
|
||||||
|
}
|
||||||
|
.abo-aktion-neben:hover,
|
||||||
|
.abo-aktion-blau:hover {
|
||||||
|
box-shadow: 0 16px 34px -10px rgba(56,189,248,.75), 0 0 30px rgba(125,211,252,.55);
|
||||||
|
}
|
||||||
|
@keyframes abo-blau-atem {
|
||||||
|
0%, 100% { box-shadow: 0 10px 26px -12px rgba(56,189,248,.55), 0 0 14px rgba(125,211,252,.25); }
|
||||||
|
50% { box-shadow: 0 12px 30px -12px rgba(56,189,248,.75), 0 0 26px rgba(125,211,252,.5); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.abo-aktionsleiste > button,
|
||||||
|
.abo-aktionsleiste > button::after { animation: none; }
|
||||||
|
}
|
||||||
|
/* Auf schmalen Handys untereinander statt gequetscht nebeneinander. */
|
||||||
|
@media (max-width: 430px) {
|
||||||
|
.abo-aktionsleiste { flex-direction: column; gap: .55rem; }
|
||||||
|
.abo-aktionsleiste > button { font-size: .92rem; }
|
||||||
|
}
|
||||||
|
|
||||||
.abo-oder {
|
.abo-oder {
|
||||||
display: flex; align-items: center; gap: .8rem;
|
display: flex; align-items: center; gap: .8rem;
|
||||||
color: var(--text-muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
|
color: var(--text-muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
|
||||||
@@ -265,7 +363,12 @@
|
|||||||
<div class="abo-logo-glut" aria-hidden="true"></div>
|
<div class="abo-logo-glut" aria-hidden="true"></div>
|
||||||
<div class="abo-logo-wz" aria-hidden="true"></div>
|
<div class="abo-logo-wz" aria-hidden="true"></div>
|
||||||
<span class="eyebrow" data-i18n="ab_preis_eyebrow">DogiCrew 🩵</span>
|
<span class="eyebrow" data-i18n="ab_preis_eyebrow">DogiCrew 🩵</span>
|
||||||
<div class="abo-preis-zahl">4,99 € <small data-i18n="ab_preis_pro_monat">/ Monat</small></div>
|
<!-- Bugfix/Nutzer-Wunsch 20.08.2026: Preis noch NICHT zeigen, solange die
|
||||||
|
Registrierung gesperrt ist (siehe .btn-gold-locked weiter unten) — sonst
|
||||||
|
wäre der Preis schon öffentlich, bevor überhaupt jemand abschließen kann.
|
||||||
|
Gleicher Chrome-Gold-Schimmer-Text wie beim gesperrten Knopf
|
||||||
|
(.btn-gold-locked-text), hier freistehend statt in einem Knopf. -->
|
||||||
|
<div class="abo-preis-zahl"><span class="btn-gold-locked-text" style="font-size:1em;" data-i18n="ab_kollektion_coming_soon">Coming soon…</span></div>
|
||||||
<p class="small muted" style="margin-top:-.6rem;margin-bottom:1rem;">Keine gesondert ausgewiesene MwSt. gemäß Kleinunternehmerregelung (Art. 57 des luxemburgischen TVA-Gesetzes) — keine weiteren Kosten.</p>
|
<p class="small muted" style="margin-top:-.6rem;margin-bottom:1rem;">Keine gesondert ausgewiesene MwSt. gemäß Kleinunternehmerregelung (Art. 57 des luxemburgischen TVA-Gesetzes) — keine weiteren Kosten.</p>
|
||||||
<ul class="abo-bedingungen">
|
<ul class="abo-bedingungen">
|
||||||
<li data-i18n="ab_bed1">Monatliche automatische Zahlung über PayPal</li>
|
<li data-i18n="ab_bed1">Monatliche automatische Zahlung über PayPal</li>
|
||||||
@@ -308,6 +411,12 @@
|
|||||||
|
|
||||||
<!-- Zustand 2: Registrierung -->
|
<!-- Zustand 2: Registrierung -->
|
||||||
<div class="abo-panel" id="panel-register" hidden>
|
<div class="abo-panel" id="panel-register" hidden>
|
||||||
|
<!-- Bugfix/Nutzer-Wunsch 20.08.2026 (Nachpruefung): Nur der Knopf war
|
||||||
|
gesperrt, die drei Felder ließen sich trotzdem antippen und
|
||||||
|
beschreiben ("ich kann immer nur was rein tippen, soll garnicht
|
||||||
|
möglich sein"). Jetzt wie das E-Mail-Feld im Login-Panel: disabled
|
||||||
|
statt nur readonly, damit auch Tastatur-/Screenreader-Nutzung
|
||||||
|
korrekt "nicht bedienbar" meldet. -->
|
||||||
<div class="abo-field">
|
<div class="abo-field">
|
||||||
<label for="reg-name" data-i18n="ab_feld_name">Name oder Anzeigename</label>
|
<label for="reg-name" data-i18n="ab_feld_name">Name oder Anzeigename</label>
|
||||||
<input type="text" id="reg-name" maxlength="60" />
|
<input type="text" id="reg-name" maxlength="60" />
|
||||||
@@ -320,23 +429,68 @@
|
|||||||
<label for="reg-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
|
<label for="reg-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
|
||||||
<input type="email" id="reg-email" maxlength="120" />
|
<input type="email" id="reg-email" maxlength="120" />
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-register" data-i18n="ab_btn_register">Registrieren</button>
|
<!-- Das Zugangscode-Feld stand bis 21.08.2026 hier, direkt neben den
|
||||||
|
Stammdaten. Es ist bewusst in einen eigenen, SPÄTEREN Schritt
|
||||||
|
gewandert (Nutzer-Wunsch: "bei der ersten registrierung sollen
|
||||||
|
die eine email bekommen mit einem einmaligen code damit wir auch
|
||||||
|
wissen dass email stimmt und danach wenn sie den code eingegeben
|
||||||
|
haben sollen die erst ihren zugangscode auswählen/eintippen
|
||||||
|
können"). Ablauf jetzt: hier Stammdaten -> Einmalcode per Mail
|
||||||
|
(#panel-code) -> Zugangscode festlegen (#panel-neuer-code). -->
|
||||||
|
<p class="small muted" style="margin:-.2rem 0 .2rem;" data-i18n="ab_reg_hinweis_bestaetigung">Wir schicken dir gleich einen 6-stelligen Code per E-Mail. Danach legst du deinen eigenen Zugangscode fest.</p>
|
||||||
|
<!-- Nutzer-Wunsch 21.08.2026: "die leute sollen sich auch schon
|
||||||
|
registrieren können, aber der abo soll noch zu sein, da soll
|
||||||
|
dann coming soon stehen." Registrierung/Login sind ab jetzt
|
||||||
|
offen -- Konten können angelegt werden. Gesperrt bleibt NUR
|
||||||
|
noch der eigentliche Zahlungsschritt danach (siehe
|
||||||
|
panel-subscribe weiter unten), weil ohne Dogis echte PayPal-
|
||||||
|
Business-Zugangsdaten niemand wirklich zahlen kann. Wieder
|
||||||
|
normaler .btn-primary-Stil statt .btn-gold-locked. -->
|
||||||
|
<!-- Aktionsleiste (21.08.2026, Nutzer-Wunsch: "bringt nichts 2 mal
|
||||||
|
registrieren da zu haben"). EIN Element statt vorher Umschalter
|
||||||
|
oben + Knopf unten: links die eigentliche Aktion in Gold,
|
||||||
|
rechts der Wechsel zum Einloggen in Babyblau. Beide Hälften
|
||||||
|
sind immer aktiv (kein Tab-Zustand) — links macht etwas,
|
||||||
|
rechts wechselt die Ansicht. -->
|
||||||
|
<div class="abo-aktionsleiste">
|
||||||
|
<button type="button" class="abo-aktion-haupt" id="btn-register" data-i18n="ab_btn_register">Registrieren</button>
|
||||||
|
<button type="button" class="abo-aktion-neben" id="tab-login" data-i18n="ab_tab_login">Schon registriert? Einloggen</button>
|
||||||
|
</div>
|
||||||
<p class="abo-fehler" id="register-fehler"></p>
|
<p class="abo-fehler" id="register-fehler"></p>
|
||||||
<p class="abo-switch"><a id="switch-to-login" data-i18n="ab_switch_login">Schon Supporter? Hier einloggen</a></p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Zustand 3: Login (E-Mail-Code anfordern) -->
|
<!-- Zustand 3: Einloggen mit E-Mail + eigenem Zugangscode.
|
||||||
|
Umgestellt 21.08.2026 (Nutzer-Wunsch: "wieso sollte ich wieder
|
||||||
|
ein code bekommen, mach die anmeldung einfach"): vorher wurde
|
||||||
|
auch hier bei JEDEM Login erst ein Einmalcode per E-Mail
|
||||||
|
verschickt — für längst registrierte Leute ein völlig
|
||||||
|
unnötiger Umweg. Der E-Mail-Weg existiert weiterhin, aber nur
|
||||||
|
noch als "Code vergessen?" (siehe Link unten). -->
|
||||||
<div class="abo-panel" id="panel-login" hidden>
|
<div class="abo-panel" id="panel-login" hidden>
|
||||||
<div class="abo-field">
|
<div class="abo-field">
|
||||||
<label for="login-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
|
<label for="login-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
|
||||||
<input type="email" id="login-email" maxlength="120" />
|
<input type="email" id="login-email" maxlength="120" autocomplete="email" />
|
||||||
|
</div>
|
||||||
|
<div class="abo-field">
|
||||||
|
<label for="login-code" data-i18n="ab_feld_code_eigen">Dein Zugangscode</label>
|
||||||
|
<input type="password" id="login-code" maxlength="200" autocomplete="current-password" />
|
||||||
|
</div>
|
||||||
|
<!-- Nutzer-Wunsch 21.08.2026: "da soll nur noch einloggen sein und
|
||||||
|
der knopf soll auch blau sein, der neu registriert soll weg."
|
||||||
|
Hier steht deshalb nur EIN Knopf, in Babyblau statt Gold —
|
||||||
|
wer auf dieser Ansicht gelandet ist, will einloggen; der Weg
|
||||||
|
zurück zur Registrierung führt über den Zurück-Knopf des
|
||||||
|
Browsers bzw. den blauen Knopf auf der Registrierungs-Seite. -->
|
||||||
|
<div class="abo-aktionsleiste">
|
||||||
|
<button type="button" class="abo-aktion-blau" id="btn-login" data-i18n="ab_btn_login">Einloggen</button>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-login-request" data-i18n="ab_btn_login_code">Login-Code anfordern</button>
|
|
||||||
<p class="abo-fehler" id="login-fehler"></p>
|
<p class="abo-fehler" id="login-fehler"></p>
|
||||||
<p class="abo-switch"><a id="switch-to-register" data-i18n="ab_switch_register">Noch kein Konto? Jetzt registrieren</a></p>
|
<p class="abo-switch"><a id="code-vergessen" data-i18n="ab_code_vergessen">Code vergessen?</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Zustand 4: Code-Eingabe (nach Registrierung ODER Login-Anfrage) -->
|
<!-- Zustand 4: Einmalcode aus der E-Mail eingeben. Kommt seit
|
||||||
|
21.08.2026 NUR noch im "Code vergessen?"-Weg vor — die normale
|
||||||
|
Registrierung/Anmeldung braucht ihn nicht mehr. -->
|
||||||
<div class="abo-panel" id="panel-code" hidden>
|
<div class="abo-panel" id="panel-code" hidden>
|
||||||
<p class="small" id="code-hinweis" data-i18n="ab_code_hinweis">Wir haben dir einen 6-stelligen Code per E-Mail geschickt.</p>
|
<p class="small" id="code-hinweis" data-i18n="ab_code_hinweis">Wir haben dir einen 6-stelligen Code per E-Mail geschickt.</p>
|
||||||
<div class="abo-field">
|
<div class="abo-field">
|
||||||
@@ -347,6 +501,20 @@
|
|||||||
<p class="abo-fehler" id="code-fehler"></p>
|
<p class="abo-fehler" id="code-fehler"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Zustand 4b: neuen dauerhaften Zugangscode festlegen. Letzter
|
||||||
|
Schritt des "Code vergessen?"-Weges — an dieser Stelle ist man
|
||||||
|
durch den Einmalcode bereits eingeloggt, es fehlt nur noch der
|
||||||
|
neue Code für künftige Anmeldungen. -->
|
||||||
|
<div class="abo-panel" id="panel-neuer-code" hidden>
|
||||||
|
<p class="small" data-i18n="ab_neuer_code_hinweis">Fast fertig — leg jetzt deinen neuen Zugangscode fest.</p>
|
||||||
|
<div class="abo-field">
|
||||||
|
<label for="neuer-code" data-i18n="ab_feld_code_neu">Dein Zugangscode (mindestens 6 Zeichen)</label>
|
||||||
|
<input type="password" id="neuer-code" maxlength="200" autocomplete="new-password" />
|
||||||
|
</div>
|
||||||
|
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-neuer-code" data-i18n="ab_btn_code_speichern">Zugangscode speichern</button>
|
||||||
|
<p class="abo-fehler" id="neuer-code-fehler"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Zustand 5: eingeloggt, noch kein Abo -> PayPal Smart Buttons.
|
<!-- Zustand 5: eingeloggt, noch kein Abo -> PayPal Smart Buttons.
|
||||||
Zeigt automatisch alle für die Käuferin/den Käufer verfügbaren
|
Zeigt automatisch alle für die Käuferin/den Käufer verfügbaren
|
||||||
Zahlarten (PayPal-Konto, Kreditkarte als Gast, je nach Land) —
|
Zahlarten (PayPal-Konto, Kreditkarte als Gast, je nach Land) —
|
||||||
@@ -354,24 +522,45 @@
|
|||||||
<div class="abo-panel" id="panel-subscribe" hidden>
|
<div class="abo-panel" id="panel-subscribe" hidden>
|
||||||
<p class="abo-erfolg zeige" data-i18n="ab_eingeloggt_als">Eingeloggt.</p>
|
<p class="abo-erfolg zeige" data-i18n="ab_eingeloggt_als">Eingeloggt.</p>
|
||||||
|
|
||||||
<!-- Button-Lösung (§ 312j BGB / entspr. Regelung): Preis- und
|
<!-- Nutzer-Wunsch 21.08.2026: "die leute sollen sich schon registrieren
|
||||||
Zahlungspflicht-Hinweis unmittelbar vor der Zahlung. -->
|
können, aber der abo soll noch zu sein, da soll dann coming soon
|
||||||
<p class="small" style="text-align:center;font-weight:700;margin-bottom:.8rem;">4,99 € / Monat · zahlungspflichtiges Abo · monatlich kündbar</p>
|
stehen." Konto ist angelegt/Login hat geklappt -- aber die eigent-
|
||||||
|
liche Zahlung (PayPal) bleibt gesperrt, bis Dogis echte PayPal-
|
||||||
|
Business-Zugangsdaten stehen. Die echten Zahlungs-Elemente (Preis-
|
||||||
|
Hinweis, Widerrufs-Checkbox, PayPal-Container) bleiben unten UNVER-
|
||||||
|
ÄNDERT im DOM (nur per hidden versteckt), damit das Skript weiter
|
||||||
|
unten (Checkbox-Listener, ladePaypalButtons() usw.) unangetastet
|
||||||
|
funktioniert -- einfach das hidden am Wrapper unten entfernen,
|
||||||
|
sobald PayPal bereitsteht. -->
|
||||||
|
<div class="btn-gold-locked" style="width:100%;margin-top:.4rem;cursor:default;">
|
||||||
|
<span class="btn-gold-locked-lock" aria-hidden="true">🔒</span>
|
||||||
|
<span class="btn-gold-locked-text" data-i18n="ab_kollektion_coming_soon">Coming soon…</span>
|
||||||
|
</div>
|
||||||
|
<p class="small muted" style="text-align:center;margin-top:.9rem;" data-i18n="ab_subscribe_locked_hinweis">Dein Konto ist bereit — die Bezahlung startet, sobald das Abo offiziell live geht. Schau einfach bald wieder vorbei.</p>
|
||||||
|
|
||||||
<!-- Widerrufsrecht bei digitalen Inhalten (§ 356 Abs. 5 BGB /
|
<!-- Nutzer-Wunsch 21.08.2026: "ich will dass jetzt alles verbunden
|
||||||
entspr. Regelung, siehe agb.html Ziffer 6): ohne aktives
|
ist so dass man mit den klicks pro klick weiter kommt" — vorher
|
||||||
Ankreuzen bleibt der Zahlungsbereich gesperrt, es gibt keine
|
war hier eine Sackgasse (kein Link/Knopf mehr nach dem Login).
|
||||||
Zahlung ohne diese ausdrückliche, nicht vorausgewählte
|
Jetzt zwei klare Wege weiter: zurück zur Startseite, oder
|
||||||
Zustimmung. -->
|
direkt zum Streamplan (naheliegendster nächster Klick für
|
||||||
<label class="abo-widerruf-check" for="widerruf-consent">
|
jemanden, der sich gerade für Dogi interessiert). -->
|
||||||
<input type="checkbox" id="widerruf-consent" />
|
<div class="btn-row" style="justify-content:center;margin-top:1.4rem;">
|
||||||
<span>Ich wünsche ausdrücklich, dass der Zugang zum Supporter-Bereich sofort mit Vertragsschluss beginnt, und weiß, dass mein 14-tägiges Widerrufsrecht dadurch erlischt, sobald der Zugang vollständig bereitgestellt ist (Details in den <a href="agb.html" target="_blank" rel="noopener">AGB</a>, Ziffer 6).</span>
|
<a class="btn btn-outline" href="index.html" data-i18n="ab_weiter_startseite">Zur Startseite →</a>
|
||||||
</label>
|
<a class="btn btn-outline" href="streamplan.html" data-i18n="ab_weiter_streamplan">Zum Streamplan →</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="paypal-button-container" class="paypal-gesperrt"></div>
|
<div hidden>
|
||||||
<p class="small muted" id="paypal-gesperrt-hinweis">Bitte bestätige oben die Checkbox, um die Zahlungsoptionen freizuschalten.</p>
|
<p class="small" style="text-align:center;font-weight:700;margin-bottom:.8rem;">4,99 € / Monat · zahlungspflichtiges Abo · monatlich kündbar</p>
|
||||||
<p class="small" id="paypal-laedt" data-i18n="ab_paypal_laedt" hidden>Zahlungsoptionen werden geladen …</p>
|
<label class="abo-widerruf-check" for="widerruf-consent">
|
||||||
<p class="abo-fehler" id="subscribe-fehler"></p>
|
<input type="checkbox" id="widerruf-consent" />
|
||||||
|
<span>Ich wünsche ausdrücklich, dass der Zugang zum Supporter-Bereich sofort mit Vertragsschluss beginnt, und weiß, dass mein 14-tägiges Widerrufsrecht dadurch erlischt, sobald der Zugang vollständig bereitgestellt ist (Details in den <a href="agb.html" target="_blank" rel="noopener">AGB</a>, Ziffer 6).</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div id="paypal-button-container" class="paypal-gesperrt"></div>
|
||||||
|
<p class="small muted" id="paypal-gesperrt-hinweis">Bitte bestätige oben die Checkbox, um die Zahlungsoptionen freizuschalten.</p>
|
||||||
|
<p class="small" id="paypal-laedt" data-i18n="ab_paypal_laedt" hidden>Zahlungsoptionen werden geladen …</p>
|
||||||
|
<p class="abo-fehler" id="subscribe-fehler"></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Zustand 6: bereits Supporter -->
|
<!-- Zustand 6: bereits Supporter -->
|
||||||
@@ -387,7 +576,10 @@
|
|||||||
<div class="container grid grid-2">
|
<div class="container grid grid-2">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<p data-i18n="ab_text1">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.</p>
|
<p data-i18n="ab_text1">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.</p>
|
||||||
<p data-i18n="ab_text2">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.</p>
|
<!-- Bugfix/Nutzer-Wunsch 20.08.2026: "4,99 €" hier entfernt (nur die Zahl, Rest
|
||||||
|
des Satzes unverändert) — sonst hätte der eigentlich gesperrte Preis
|
||||||
|
weiter oben in der Preis-Box hier im Fließtext trotzdem gestanden. -->
|
||||||
|
<p data-i18n="ab_text2">Mit deinem monatlichen Beitrag 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.</p>
|
||||||
<ul class="abo-vorteile">
|
<ul class="abo-vorteile">
|
||||||
<li data-i18n="ab_vorteil1">Exklusiver Supporter-Bereich</li>
|
<li data-i18n="ab_vorteil1">Exklusiver Supporter-Bereich</li>
|
||||||
<li data-i18n="ab_vorteil2">Kleiner Hasen-Teddy nach 6 bezahlten Monaten</li>
|
<li data-i18n="ab_vorteil2">Kleiner Hasen-Teddy nach 6 bezahlten Monaten</li>
|
||||||
@@ -411,13 +603,13 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-abonnieren.js"></script>
|
<script src="assets/js/i18n-abonnieren.js?v=20260821o"></script>
|
||||||
<script src="assets/js/supporter.js"></script>
|
<script src="assets/js/supporter.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const S = window.DogiSupporter;
|
const S = window.DogiSupporter;
|
||||||
const panels = ["panel-lade", "panel-register", "panel-login", "panel-code", "panel-subscribe", "panel-already"];
|
const panels = ["panel-lade", "panel-register", "panel-login", "panel-code", "panel-neuer-code", "panel-subscribe", "panel-already"];
|
||||||
// panel-social-login (Google-Button) hängt sich an register/login dran,
|
// panel-social-login (Google-Button) hängt sich an register/login dran,
|
||||||
// ist aber kein "richtiger" exklusiver Zustand — separat gesteuert.
|
// ist aber kein "richtiger" exklusiver Zustand — separat gesteuert.
|
||||||
const SOCIAL_LOGIN_PANELS = ["panel-register", "panel-login"];
|
const SOCIAL_LOGIN_PANELS = ["panel-register", "panel-login"];
|
||||||
@@ -455,8 +647,14 @@
|
|||||||
el.classList.remove("zeige");
|
el.classList.remove("zeige");
|
||||||
}
|
}
|
||||||
|
|
||||||
let codePurpose = "verify_email"; // oder "login"
|
// Merkt sich die E-Mail-Adresse zwischen "Code vergessen?" und der
|
||||||
|
// Eingabe des Einmalcodes (der frühere codePurpose-Schalter entfiel mit
|
||||||
|
// der Umstellung 21.08.2026 — es gibt nur noch diesen einen Code-Weg).
|
||||||
let codeEmail = "";
|
let codeEmail = "";
|
||||||
|
// Merkt sich, WARUM gerade ein Einmalcode eingegeben wird (21.08.2026):
|
||||||
|
// "register" = Erstbestätigung der E-Mail, "login" = "Code vergessen?".
|
||||||
|
// Steuert in btn-code-verify, welcher Endpunkt aufgerufen wird.
|
||||||
|
let codeZweck = "login";
|
||||||
|
|
||||||
async function pruefeStatus() {
|
async function pruefeStatus() {
|
||||||
if (!S.getToken()) {
|
if (!S.getToken()) {
|
||||||
@@ -468,11 +666,24 @@
|
|||||||
const status = data.subscription?.status || "none";
|
const status = data.subscription?.status || "none";
|
||||||
S.cacheSubStatus(status);
|
S.cacheSubStatus(status);
|
||||||
S.cacheDisplayName(data.profile?.displayName || "");
|
S.cacheDisplayName(data.profile?.displayName || "");
|
||||||
if (["active", "pending", "suspended", "cancelled"].includes(status)) {
|
/* Nutzer-Wunsch 21.08.2026: "anstatt das was du jetzt auf dem screen
|
||||||
zeige("panel-already");
|
siehst, sollen die leute nicht mehr sehen wenn sie angemeldet sind.
|
||||||
} else {
|
sie sollen die richtige seite sehen die wir schon mal vorbereitet
|
||||||
zeige("panel-subscribe");
|
hatten, wo sie ihre persönliche daten sehen und so."
|
||||||
}
|
|
||||||
|
Wer eingeloggt ist, gehört nicht mehr auf diese Anmeldeseite —
|
||||||
|
egal ob mit oder ohne bezahltes Abo. supporter.html zeigt in BEIDEN
|
||||||
|
Fällen das Richtige: persönliche Daten, Fortschritt mit allen
|
||||||
|
Prämien-Meilensteinen, Sammlungsarchiv, Profil bearbeiten,
|
||||||
|
Ausloggen — bei "kein Abo" eben mit entsprechendem Hinweis statt
|
||||||
|
echter Laufzeit. Vorher landete man hier stattdessen in einer
|
||||||
|
Sackgasse ("Coming soon…"), ohne je die eigentliche Seite zu sehen.
|
||||||
|
|
||||||
|
replace() statt href: die Anmeldeseite verschwindet damit aus dem
|
||||||
|
Verlauf — ein "Zurück" führt sonst sofort wieder hierher und von
|
||||||
|
hier wieder weiter, was sich wie eine Schleife anfühlt. */
|
||||||
|
location.replace("supporter.html");
|
||||||
|
return;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Token ungültig/abgelaufen -> zurück zur Registrierung/Login
|
// Token ungültig/abgelaufen -> zurück zur Registrierung/Login
|
||||||
S.clearToken();
|
S.clearToken();
|
||||||
@@ -585,8 +796,10 @@
|
|||||||
ladeHinweis.hidden = true;
|
ladeHinweis.hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById("switch-to-login").addEventListener("click", () => zeige("panel-login"));
|
// Der frühere Gegenstück-Knopf "Neu registrieren" auf der Einloggen-Seite
|
||||||
document.getElementById("switch-to-register").addEventListener("click", () => zeige("panel-register"));
|
// wurde am 21.08.2026 auf Wunsch entfernt — deshalb hier nur noch der eine
|
||||||
|
// Wechsel von der Registrierung zum Einloggen.
|
||||||
|
document.getElementById("tab-login").addEventListener("click", () => zeige("panel-login"));
|
||||||
|
|
||||||
// Widerrufsrecht-Checkbox: erst nach aktivem Ankreuzen werden die
|
// Widerrufsrecht-Checkbox: erst nach aktivem Ankreuzen werden die
|
||||||
// PayPal-Zahlungsoptionen überhaupt geladen/klickbar (siehe agb.html
|
// PayPal-Zahlungsoptionen überhaupt geladen/klickbar (siehe agb.html
|
||||||
@@ -615,24 +828,44 @@
|
|||||||
const email = document.getElementById("reg-email").value.trim();
|
const email = document.getElementById("reg-email").value.trim();
|
||||||
if (!displayName || !email) return fehlerZeigen(el, "Bitte Name und E-Mail-Adresse angeben.");
|
if (!displayName || !email) return fehlerZeigen(el, "Bitte Name und E-Mail-Adresse angeben.");
|
||||||
try {
|
try {
|
||||||
|
// Schritt 1 von 3 (21.08.2026): /supporter/register legt nur das noch
|
||||||
|
// unbestätigte Konto an und verschickt einen Einmalcode -- es kommt
|
||||||
|
// bewusst KEIN Token zurück, eingeloggt ist man erst nach Schritt 2.
|
||||||
await S.apiFetch("/supporter/register", { displayName, tiktokUsername, email });
|
await S.apiFetch("/supporter/register", { displayName, tiktokUsername, email });
|
||||||
codePurpose = "verify_email";
|
|
||||||
codeEmail = email;
|
codeEmail = email;
|
||||||
|
codeZweck = "register";
|
||||||
zeige("panel-code");
|
zeige("panel-code");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fehlerZeigen(el, err.message);
|
fehlerZeigen(el, err.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById("btn-login-request").addEventListener("click", async () => {
|
// Normale Anmeldung: E-Mail + eigener Zugangscode, ohne Umweg über E-Mail.
|
||||||
|
document.getElementById("btn-login").addEventListener("click", async () => {
|
||||||
const el = document.getElementById("login-fehler");
|
const el = document.getElementById("login-fehler");
|
||||||
fehlerVerstecken(el);
|
fehlerVerstecken(el);
|
||||||
const email = document.getElementById("login-email").value.trim();
|
const email = document.getElementById("login-email").value.trim();
|
||||||
if (!email) return fehlerZeigen(el, "Bitte E-Mail-Adresse angeben.");
|
const accessCode = document.getElementById("login-code").value;
|
||||||
|
if (!email || !accessCode) return fehlerZeigen(el, "Bitte E-Mail-Adresse und Zugangscode eingeben.");
|
||||||
|
try {
|
||||||
|
const data = await S.apiFetch("/supporter/login-code", { email, accessCode });
|
||||||
|
S.setToken(data.token);
|
||||||
|
await pruefeStatus();
|
||||||
|
} catch (err) {
|
||||||
|
fehlerZeigen(el, err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// "Code vergessen?" — hier lebt der alte E-Mail-Einmalcode-Weg weiter.
|
||||||
|
document.getElementById("code-vergessen").addEventListener("click", async () => {
|
||||||
|
const el = document.getElementById("login-fehler");
|
||||||
|
fehlerVerstecken(el);
|
||||||
|
const email = document.getElementById("login-email").value.trim();
|
||||||
|
if (!email) return fehlerZeigen(el, "Bitte zuerst deine E-Mail-Adresse eingeben.");
|
||||||
try {
|
try {
|
||||||
await S.apiFetch("/supporter/login-request", { email });
|
await S.apiFetch("/supporter/login-request", { email });
|
||||||
codePurpose = "login";
|
|
||||||
codeEmail = email;
|
codeEmail = email;
|
||||||
|
codeZweck = "login";
|
||||||
zeige("panel-code");
|
zeige("panel-code");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fehlerZeigen(el, err.message);
|
fehlerZeigen(el, err.message);
|
||||||
@@ -645,9 +878,26 @@
|
|||||||
const code = document.getElementById("code-input").value.trim();
|
const code = document.getElementById("code-input").value.trim();
|
||||||
if (!code) return fehlerZeigen(el, "Bitte Code eingeben.");
|
if (!code) return fehlerZeigen(el, "Bitte Code eingeben.");
|
||||||
try {
|
try {
|
||||||
const path = codePurpose === "login" ? "/supporter/verify-login" : "/supporter/verify-email";
|
// Dieselbe Code-Eingabe bedient zwei Wege (21.08.2026):
|
||||||
const data = await S.apiFetch(path, { email: codeEmail, code });
|
// "register" -> Erstbestätigung der E-Mail bei der Registrierung
|
||||||
|
// "login" -> "Code vergessen?"-Weg für bestehende Konten
|
||||||
|
// Beide enden gleich: eingeloggt, danach Zugangscode festlegen.
|
||||||
|
const endpunkt = codeZweck === "register" ? "/supporter/verify-email" : "/supporter/verify-login";
|
||||||
|
const data = await S.apiFetch(endpunkt, { email: codeEmail, code });
|
||||||
S.setToken(data.token);
|
S.setToken(data.token);
|
||||||
|
zeige("panel-neuer-code");
|
||||||
|
} catch (err) {
|
||||||
|
fehlerZeigen(el, err.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("btn-neuer-code").addEventListener("click", async () => {
|
||||||
|
const el = document.getElementById("neuer-code-fehler");
|
||||||
|
fehlerVerstecken(el);
|
||||||
|
const accessCode = document.getElementById("neuer-code").value;
|
||||||
|
if (!accessCode) return fehlerZeigen(el, "Bitte leg einen Zugangscode fest.");
|
||||||
|
try {
|
||||||
|
await S.apiFetch("/supporter/set-access-code", { accessCode }, true);
|
||||||
await pruefeStatus();
|
await pruefeStatus();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
fehlerZeigen(el, err.message);
|
fehlerZeigen(el, err.message);
|
||||||
@@ -662,6 +912,12 @@
|
|||||||
} catch {
|
} catch {
|
||||||
/* Konfiguration konnte nicht geladen werden — Google-Button/PayPal-Buttons bleiben dann aus. */
|
/* Konfiguration konnte nicht geladen werden — Google-Button/PayPal-Buttons bleiben dann aus. */
|
||||||
}
|
}
|
||||||
|
/* Bugfix 20.08.2026, wieder aktiviert 21.08.2026: "Mit Google anmelden" war ein
|
||||||
|
dritter, bisher übersehener Weg, trotz gesperrtem Registrieren-/Login-Knopf
|
||||||
|
doch ein echtes Konto anzulegen bzw. sich einzuloggen. War deshalb deaktiviert,
|
||||||
|
solange die Registrierung selbst gesperrt war. Registrierung ist jetzt (Nutzer-
|
||||||
|
Wunsch 21.08.2026) bewusst offen -- Google-Login ist nur ein weiterer,
|
||||||
|
gleichwertiger Weg zum selben jetzt erlaubten Ziel, kein Schlupfloch mehr. */
|
||||||
initGoogleConsent();
|
initGoogleConsent();
|
||||||
await pruefeStatus();
|
await pruefeStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -134,6 +134,6 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Detailansicht einer Aktion oder eines Projekts von Team Dogi." />
|
<meta name="twitter:description" content="Detailansicht einer Aktion oder eines Projekts von Team Dogi." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -51,9 +51,9 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-aktionen.js"></script>
|
<script src="assets/js/data-aktionen.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-aktion-detail.js"></script>
|
<script src="assets/js/i18n-aktion-detail.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const params = new URLSearchParams(location.search);
|
const params = new URLSearchParams(location.search);
|
||||||
|
|||||||
@@ -121,17 +121,14 @@ header.site-header {
|
|||||||
braucht durch größere Schrift + mehr Abstand + Polarlicht-Padding
|
braucht durch größere Schrift + mehr Abstand + Polarlicht-Padding
|
||||||
spürbar mehr Platz, ohne dass der restliche Seiteninhalt breiter
|
spürbar mehr Platz, ohne dass der restliche Seiteninhalt breiter
|
||||||
werden soll.
|
werden soll.
|
||||||
Bugfix 04.08.2026: Der feste 1400px-Deckel war zu knapp — der
|
Bugfix 04.08.2026: 1400px -> 1440px angehoben (siehe damalige Analyse).
|
||||||
tatsächliche Inhalt (Logo+Menü+Sprachschalter+DogiCrew-Knopf+
|
Bugfix 20.08.2026: reichte seither wieder nicht mehr — die Nav braucht
|
||||||
Bewerben-Knopf) braucht bei aufgeklapptem Desktop-Menü (ab 1480px
|
inzwischen (Nav-Punkt "DogiCrew" + "BALD"-Badge dazugekommen) real ca.
|
||||||
Breite, siehe Media Query unten) ca. 1417px und lief dadurch IMMER
|
1491px, der Deckel blieb bei 1440px. 1440px -> 1560px, zusammen mit der
|
||||||
um ca. 17px über den Rahmen hinaus — auf breiten Bildschirmen ohne
|
auf 1650px angehobenen Hamburger-Schwelle weiter unten (siehe Kommentar
|
||||||
sichtbaren Schaden (genug Luft drumherum), aber bei schmaleren
|
dort) — diesmal mit echtem Sicherheitsabstand statt wieder nur knapp
|
||||||
Fenstern knapp über der 1480px-Schwelle (z.B. Laptop-Fenster nicht
|
genug für den aktuellen Stand. */
|
||||||
maximiert, oder Browser-Zoom >100%) wurde dadurch der rechte Rand
|
width: min(1560px, 94vw);
|
||||||
(der "Bewerben"-Knopf) sichtbar abgeschnitten. 1400px -> 1440px gibt
|
|
||||||
ausreichend Puffer, ohne auf breiten Screens spürbar größer zu wirken. */
|
|
||||||
width: min(1440px, 94vw);
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -414,11 +411,21 @@ header.site-header {
|
|||||||
0% { background-position: 160% 0; }
|
0% { background-position: 160% 0; }
|
||||||
100% { background-position: -60% 0; }
|
100% { background-position: -60% 0; }
|
||||||
}
|
}
|
||||||
|
/* Bugfix 20.08.2026 (Nutzer-Report per Screenshot-Vergleich: Kategorie-
|
||||||
|
Menüs wie "Medien" sind mittig unter dem Knopf, der Sprachschalter aber
|
||||||
|
rechtsbündig) — war bewusst rechtsbündig, seit der Sprachschalter aber
|
||||||
|
nicht mehr ganz am rechten Rand sitzt (DogiCrew-/Bewerben-Knopf daneben),
|
||||||
|
ist genug Platz für dieselbe zentrierte Ausrichtung wie überall sonst.
|
||||||
|
left/transform hier deshalb NICHT mehr überschrieben (erbt jetzt
|
||||||
|
left:50%/translateX(-50%) von der allgemeinen .nav-dropdown-Regel,
|
||||||
|
genau wie der bereits zentrierte Verbindungs-Pfeil (::before) es die
|
||||||
|
ganze Zeit schon war — vorher zeigte der Pfeil mittig, während die Box
|
||||||
|
selbst daneben saß). Eigene Animation bleibt bestehen (siehe Kommentar
|
||||||
|
der allgemeinen .nav-dropdown-Animation), jetzt aber ebenfalls mit
|
||||||
|
translateX(-50%) in jedem Frame, sonst würde sie die Zentrierung wieder
|
||||||
|
aufheben. */
|
||||||
.sprach-schalter .nav-dropdown {
|
.sprach-schalter .nav-dropdown {
|
||||||
min-width: 190px;
|
min-width: 190px;
|
||||||
left: auto;
|
|
||||||
right: 0;
|
|
||||||
transform: none;
|
|
||||||
padding: .4rem;
|
padding: .4rem;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
border: 1px solid rgba(210, 216, 224, .55);
|
border: 1px solid rgba(210, 216, 224, .55);
|
||||||
@@ -427,12 +434,11 @@ header.site-header {
|
|||||||
0 14px 34px rgba(0, 0, 0, .6),
|
0 14px 34px rgba(0, 0, 0, .6),
|
||||||
0 0 16px rgba(220, 226, 235, .35),
|
0 0 16px rgba(220, 226, 235, .35),
|
||||||
0 0 34px rgba(190, 200, 215, .18);
|
0 0 34px rgba(190, 200, 215, .18);
|
||||||
transform-origin: top right;
|
|
||||||
animation: sprach-dropdown-in .18s ease both, sprach-silber-puls 3.6s ease-in-out infinite;
|
animation: sprach-dropdown-in .18s ease both, sprach-silber-puls 3.6s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
@keyframes sprach-dropdown-in {
|
@keyframes sprach-dropdown-in {
|
||||||
from { opacity: 0; transform: translateY(-6px) scale(.96); }
|
from { opacity: 0; transform: translateX(-50%) translateY(-6px) scale(.96); }
|
||||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
|
||||||
}
|
}
|
||||||
/* Silberner Polarlicht-Schein um den Dropdown-Rand — bewusst neutral
|
/* Silberner Polarlicht-Schein um den Dropdown-Rand — bewusst neutral
|
||||||
silber statt farbig, damit sich der schwarze Kasten von der bunten
|
silber statt farbig, damit sich der schwarze Kasten von der bunten
|
||||||
@@ -613,6 +619,34 @@ header.site-header {
|
|||||||
0 0 22px rgba(143, 217, 234, .35),
|
0 0 22px rgba(143, 217, 234, .35),
|
||||||
0 0 22px rgba(139, 92, 246, .3);
|
0 0 22px rgba(139, 92, 246, .3);
|
||||||
}
|
}
|
||||||
|
/* "Bald"-Badge auf dem Abo-Knopf, solange die Registrierung auf
|
||||||
|
abonnieren.html gesperrt ist (20.08.2026, siehe .btn-gold-locked weiter
|
||||||
|
unten + main.js renderAboButton/soonBadge). Sitzt als zweites Kind IN
|
||||||
|
der Pille (nicht als eigenes Nav-Element), bleibt dadurch Teil desselben
|
||||||
|
Klicks — der Knopf führt bewusst weiterhin ganz normal zu abonnieren.html,
|
||||||
|
nur die Registrierung selbst dort ist gesperrt. Bewusst neutral-silbern
|
||||||
|
(Wiederverwendung von .btn-silver-text/-spark, nur kleiner skaliert)
|
||||||
|
statt Gold, damit es nicht mit der babyblau/lila-Aurora dieser Pille
|
||||||
|
konkurriert — reine Status-Markierung, kein zweiter Marken-Akzent. */
|
||||||
|
.nav-cta-soon {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .22rem;
|
||||||
|
margin-left: .5rem;
|
||||||
|
padding: .15rem .5rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: .64rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: .04em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
background: rgba(255, 255, 255, .07);
|
||||||
|
border: 1px solid rgba(255, 255, 255, .3);
|
||||||
|
box-shadow: 0 0 10px rgba(230, 235, 242, .3);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.nav-cta-soon .btn-silver-spark { font-size: .85em; }
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.nav-cta::before, .nav-cta::after { animation: none; }
|
.nav-cta::before, .nav-cta::after { animation: none; }
|
||||||
}
|
}
|
||||||
@@ -937,6 +971,108 @@ header.site-header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
"Gesperrter Tresor"-Knopf für den DogiCrew-Registrieren-Knopf
|
||||||
|
(abonnieren.html, 20.08.2026, Nutzer-Wunsch: "richtig krank übertrieben
|
||||||
|
geiles... weil es wird ja das geilste Bonus und überhaupt so von mir").
|
||||||
|
Bewusst KEINE Wiederverwendung von .btn-silver — der ist neutral-grau und
|
||||||
|
auf DIESER Seite bereits für die Teddy-Kollektion (Nebensache) vergeben.
|
||||||
|
Chrome-Text-Clip-Schimmer + Schimmer-Sweep + Halo-Puls wie .btn-silver/
|
||||||
|
.btn-legendary, aber in den ECHTEN Gold-Tönen dieser Seite
|
||||||
|
(#ffe9a8/#d4af37, siehe .abo-panel .btn) und mit einem pulsierenden
|
||||||
|
Schloss statt Sternen, damit die Haupt-CTA sichtbar "das Besondere,
|
||||||
|
noch verschlossene" bleibt statt nur "irgendwann verfügbar".
|
||||||
|
WICHTIG: Für den Rand bewusst NICHT die rotierende Conic-Gradient-Maske
|
||||||
|
von .btn-silver/.btn-legendary kopiert — dieser Knopf läuft über
|
||||||
|
`width:100%` auf voller Kartenbreite (~440px) bei nur ~62px Höhe, also
|
||||||
|
ein sehr langgestrecktes Rechteck. Genau für diesen Fall ist die
|
||||||
|
dokumentierte Lektion in Projektregeln.md Punkt 14 gedacht (03.08.2026,
|
||||||
|
"TikTok-Button-Bug"): ein rotierendes maskiertes Pseudo-Element verzieht
|
||||||
|
sich auf länglichen Formen zu einer krummen Schlaufe, weil ein Rechteck
|
||||||
|
beim Rotieren nicht rotationssymmetrisch ist — live mit Playwright-
|
||||||
|
Screenshots über mehrere Frames nachgewiesen (die Schlaufe ist der
|
||||||
|
rotierende Rand bei verschiedenen Winkeln). Stattdessen derselbe sichere
|
||||||
|
Zwei-Layer-Background-Trick wie bei .abo-google-frame weiter oben in
|
||||||
|
dieser Datei (Füllung padding-box + Verlauf border-box, Bewegung nur
|
||||||
|
über background-position) — bleibt geometrisch exakt an der Kontur,
|
||||||
|
unabhängig vom Seitenverhältnis. Reine Deko/Statusanzeige — echte
|
||||||
|
Deaktivierung läuft über das native disabled-Attribut am <button>
|
||||||
|
(siehe abonnieren.html), nicht über pointer-events, damit auch
|
||||||
|
Tastatur-/Screenreader-Nutzung korrekt "nicht bedienbar" meldet. Bewusst
|
||||||
|
KEIN :hover-Zustand (keine Lift-Animation wie bei .btn-silver/
|
||||||
|
.btn-legendary) — soll nicht so wirken, als wäre er doch klickbar. */
|
||||||
|
.btn-gold-locked {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: .65rem;
|
||||||
|
padding: 1rem 1.4rem;
|
||||||
|
border-radius: 14px;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 1.04rem;
|
||||||
|
letter-spacing: .02em;
|
||||||
|
border: 1.5px solid transparent;
|
||||||
|
background:
|
||||||
|
linear-gradient(160deg, #1c1710, #0b0e14 55%, #211a0d) padding-box,
|
||||||
|
linear-gradient(120deg, #d4af37, #ffe9a8, #b8912b, #fff2c9, #d4af37) border-box;
|
||||||
|
background-size: 100% 100%, 260% 100%;
|
||||||
|
cursor: not-allowed;
|
||||||
|
animation: btn-gold-locked-border-shine 6s ease-in-out infinite, btn-gold-locked-halo 3s ease-in-out infinite;
|
||||||
|
box-shadow: 0 8px 22px rgba(0, 0, 0, .45), 0 0 0 1px rgba(212, 175, 55, .08) inset;
|
||||||
|
}
|
||||||
|
.btn-gold-locked-lock {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1.05em;
|
||||||
|
filter: drop-shadow(0 0 6px rgba(255, 214, 102, .85));
|
||||||
|
animation: btn-gold-locked-pulse 2.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.btn-gold-locked-text {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
background: linear-gradient(100deg, #ffe9a8 0%, #ffffff 24%, #d4af37 42%, #ffe9a8 60%, #ffffff 78%, #d4af37 100%);
|
||||||
|
background-size: 260% 100%;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .6));
|
||||||
|
animation: btn-silver-text-shine 3.6s linear infinite; /* bestehendes Keyframe wiederverwendet */
|
||||||
|
}
|
||||||
|
.btn-gold-locked::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: linear-gradient(115deg, transparent 32%, rgba(255, 233, 168, .5) 49%, transparent 66%);
|
||||||
|
background-size: 240% 100%;
|
||||||
|
mix-blend-mode: overlay;
|
||||||
|
animation: nav-cta-shimmer 4s linear infinite; /* bestehendes Keyframe wiederverwendet */
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
@keyframes btn-gold-locked-border-shine {
|
||||||
|
0%, 100% { background-position: 0 0, 0% 50%; }
|
||||||
|
50% { background-position: 0 0, 100% 50%; }
|
||||||
|
}
|
||||||
|
@keyframes btn-gold-locked-pulse {
|
||||||
|
0%, 100% { opacity: .7; transform: scale(.92) rotate(-4deg); }
|
||||||
|
50% { opacity: 1; transform: scale(1.12) rotate(4deg); }
|
||||||
|
}
|
||||||
|
@keyframes btn-gold-locked-halo {
|
||||||
|
0%, 100% { box-shadow: 0 8px 22px rgba(0, 0, 0, .45), 0 0 16px rgba(212, 175, 55, .22); }
|
||||||
|
50% { box-shadow: 0 8px 22px rgba(0, 0, 0, .45), 0 0 32px rgba(255, 214, 102, .45); }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.btn-gold-locked, .btn-gold-locked::after,
|
||||||
|
.btn-gold-locked-text, .btn-gold-locked-lock {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
.btn-holo — zweiter "Mega"-Button-Stil, bewusst MECHANISCH anders als
|
.btn-holo — zweiter "Mega"-Button-Stil, bewusst MECHANISCH anders als
|
||||||
.btn-legendary/.btn-silver/.btn-tiktok (die alle nach demselben Muster
|
.btn-legendary/.btn-silver/.btn-tiktok (die alle nach demselben Muster
|
||||||
@@ -1308,14 +1444,21 @@ header.site-header {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Schwelle von ursprünglich 880px auf 1480px angehoben: nach mehreren
|
/* Bugfix 20.08.2026 (Nutzer-Report per Screenshot: "DogiCrew"-Knopf/Badge
|
||||||
Vergrößerungsrunden (Logo/Nav-Schrift/Sprachschalter/Bewerben-Button,
|
rechts abgeschnitten): die Schwelle war schon zweimal angehoben (880 ->
|
||||||
mehr Abstand zwischen den Nav-Punkten, größerer eigener Nav-Container,
|
1480px, dazu der .nav-bar-Deckel 1400 -> 1440px), reichte aber seither
|
||||||
Polarlicht-Padding je Kategorie) braucht die Desktop-Nav deutlich mehr
|
NICHT mehr aus — seit den neu dazugekommenen Nav-Punkten "DogiCrew" +
|
||||||
Platz als früher. Live bei mehreren Breiten nachgeprüft (siehe Commits
|
"BALD"-Badge braucht die aufgeklappte Desktop-Nav real ca. 1491px
|
||||||
vom 02.08.2026) — Schwelle hier bewusst großzügig, damit nie wieder
|
Breite (per Playwright über mehrere Breiten nachgemessen), der Deckel
|
||||||
Elemente sichtbar aus der Leiste verschwinden. */
|
blieb aber bei 1440px hart begrenzt. Ergebnis: bei JEDER Fensterbreite
|
||||||
@media (max-width: 1480px) {
|
ab ca. 1500px (nicht nur in einer schmalen Übergangszone) lief die Nav
|
||||||
|
permanent ca. 50px über den Rand hinaus. Diesmal mit echtem Sicherheits-
|
||||||
|
abstand behoben statt wieder nur knapp: Schwelle auf 1650px, Deckel auf
|
||||||
|
1560px — bei exakt 1650px ergibt min(1560px, 94vw) ≈ 1551px, spürbar
|
||||||
|
mehr als die gemessenen ~1491px nötigen Breite. Bei jeder Fensterbreite
|
||||||
|
NACH der Schwelle reicht 94vw automatisch weiter aus (nur ganz nah an
|
||||||
|
der Schwelle war die Lücke kritisch). */
|
||||||
|
@media (max-width: 1650px) {
|
||||||
.nav-toggle { display: block; }
|
.nav-toggle { display: block; }
|
||||||
.nav-links {
|
.nav-links {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -1336,6 +1479,22 @@ header.site-header {
|
|||||||
.nav-dropdown::before { display: none; }
|
.nav-dropdown::before { display: none; }
|
||||||
.nav-group.open .nav-dropdown { display: flex; }
|
.nav-group.open .nav-dropdown { display: flex; }
|
||||||
.nav-cta { align-self: flex-start; margin: 1rem 0 .6rem; }
|
.nav-cta { align-self: flex-start; margin: 1rem 0 .6rem; }
|
||||||
|
|
||||||
|
/* Bugfix 20.08.2026 (Nutzer-Report: "auf dem Handy passt nicht immer alles").
|
||||||
|
Per echter Mobil-Emulation (iPhone 15 Pro + Pixel 8, Screenshots) nachgestellt:
|
||||||
|
Die schwebenden Eck-Knöpfe (Musik unten rechts, Vorschau-Umschalter unten links
|
||||||
|
für Dogi/VanVan — siehe .music-widget/.vorschau-widget weiter unten) sind
|
||||||
|
absichtlich IMMER sichtbar (position:fixed, direkte <body>-Kinder, eigener
|
||||||
|
Stacking-Kontext, unabhängig vom Menü). Bei aufgeklapptem Mobil-Menü liegen sie
|
||||||
|
dadurch ÜBER den untersten Menüpunkten (Sprachschalter + "Bewerben"-Knopf) und
|
||||||
|
verdecken/blockieren sie sichtbar. :has() statt einer weiteren main.js-Änderung,
|
||||||
|
weil beide Widgets keinen gemeinsamen Elternknoten mit .nav-links haben (beide
|
||||||
|
hängen direkt unter <body>, .nav-links steckt im Header) — reiner CSS-Fix bleibt
|
||||||
|
an der Stelle, an der auch die restliche Menü-Overlay-Logik dieses Blocks steht. */
|
||||||
|
body:has(.nav-links.open) .music-widget,
|
||||||
|
body:has(.nav-links.open) .vorschau-widget {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Buttons ----------
|
/* ---------- Buttons ----------
|
||||||
@@ -1407,6 +1566,12 @@ header.site-header {
|
|||||||
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }
|
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }
|
||||||
.btn-row { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 1.5rem; }
|
.btn-row { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 1.5rem; }
|
||||||
|
|
||||||
|
/* Kurzes "✓ Link kopiert"-Feedback für dogiTeilen()-Knöpfe (Zwischenablage-
|
||||||
|
Fallback ohne Web-Share-API) -- 21.08.2026. Gleiche Erfolgsfarbe wie
|
||||||
|
.vw-copy-btn.kopiert in der Verwaltung, für ein konsistentes Gefühl
|
||||||
|
zwischen öffentlicher Seite und Admin-Bereich. */
|
||||||
|
.dogi-teilen-kopiert { background: rgba(52,211,153,.15) !important; border-color: rgba(52,211,153,.5) !important; color: #5eeaa7 !important; }
|
||||||
|
|
||||||
/* ---------- Hero ---------- */
|
/* ---------- Hero ---------- */
|
||||||
.hero {
|
.hero {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -1444,6 +1609,185 @@ header.site-header {
|
|||||||
}
|
}
|
||||||
@media (max-width: 760px) { .hero-grid { grid-template-columns: 1fr; } }
|
@media (max-width: 760px) { .hero-grid { grid-template-columns: 1fr; } }
|
||||||
|
|
||||||
|
/* "Event des Jahres" (20.08.2026) — volle Breite wie .hero-grid darüber (eigener Block,
|
||||||
|
kein Grid-Kind), zeigt bewusst IMMER genau zwei Events nebeneinander. Gleiche
|
||||||
|
Karten-/Tag-Optik wie die 3 Welt-Kacheln, damit es wie ein natürlicher Abschluss des
|
||||||
|
Grids wirkt statt wie ein fremdes Element. */
|
||||||
|
.jahres-events {
|
||||||
|
margin-top: 1.2rem;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 1.6rem;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
}
|
||||||
|
.jahres-events-head { margin-bottom: 1.2rem; }
|
||||||
|
.jahres-events-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 1.2rem;
|
||||||
|
}
|
||||||
|
@media (max-width: 760px) { .jahres-events-grid { grid-template-columns: 1fr; } }
|
||||||
|
/* Nutzer-Wunsch 20.08.2026: "die kacheln sollen auch bissl spezieller sein" —
|
||||||
|
goldener Trophäen-Akzent statt der neutralen Standard-Kartenoptik (eigener,
|
||||||
|
gedeckter Farbton, kein grelles Gold — augenschonend), plus ein dezenter
|
||||||
|
wandernder Lichtschimmer oben (".jahres-event-shine"), der die Kachel als
|
||||||
|
etwas Besonderes markiert statt als normale Karte. Jetzt ein <button>
|
||||||
|
(öffnet ein Detail-Fenster) statt eines <a> (navigierte sofort weg) —
|
||||||
|
deshalb hier zusätzlich ein vollständiger Button-Reset. */
|
||||||
|
/* [hidden] explizit VOR der Klassenregel + als eigene Regel danach: ein
|
||||||
|
simples "display:block" in .jahres-event-card hat dieselbe Spezifität
|
||||||
|
wie die eingebaute [hidden]-UA-Regel des Browsers und würde sie sonst im
|
||||||
|
Zweifel überschreiben (identischer Bug wie schon bei .jahres-event-bild
|
||||||
|
weiter unten, hier per Playwright-Screenshot erneut gefunden — eine
|
||||||
|
"leere" Kachel blieb trotz hidden-Attribut sichtbar). */
|
||||||
|
.jahres-event-card[hidden] { display: none; }
|
||||||
|
.jahres-event-card {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
isolation: isolate;
|
||||||
|
padding: 1.2rem 1.4rem;
|
||||||
|
border-radius: calc(var(--radius) - 4px);
|
||||||
|
border: 1px solid rgba(212, 175, 55, .35);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at 15% 0%, rgba(212, 175, 55, .08), transparent 55%),
|
||||||
|
rgba(255, 255, 255, .02);
|
||||||
|
transition: border-color .2s ease, transform .2s ease, box-shadow .2s ease;
|
||||||
|
}
|
||||||
|
.jahres-event-card:hover, .jahres-event-card:focus-visible {
|
||||||
|
border-color: rgba(212, 175, 55, .7);
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 10px 26px rgba(0, 0, 0, .35), 0 0 22px rgba(212, 175, 55, .18);
|
||||||
|
}
|
||||||
|
.jahres-event-shine {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 6%; right: 6%;
|
||||||
|
height: 1px;
|
||||||
|
background: linear-gradient(90deg, transparent, rgba(255, 214, 102, .9), transparent);
|
||||||
|
animation: jahres-event-goldglanz 4.5s ease-in-out infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
@keyframes jahres-event-goldglanz { 0%, 100% { opacity: .3; } 50% { opacity: 1; } }
|
||||||
|
@media (prefers-reduced-motion: reduce) { .jahres-event-shine { animation: none; opacity: .6; } }
|
||||||
|
.jahres-event-datum {
|
||||||
|
display: block;
|
||||||
|
color: #e8c465;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: .82rem;
|
||||||
|
letter-spacing: .02em;
|
||||||
|
margin-bottom: .3rem;
|
||||||
|
}
|
||||||
|
.jahres-event-card h3 { margin: 0 0 .4rem; font-size: 1.15rem; }
|
||||||
|
.jahres-event-card p { margin: 0 0 .6rem; color: var(--text-muted); font-size: .93rem; line-height: 1.55; }
|
||||||
|
.jahres-event-mehr { color: #e8c465; font-weight: 600; font-size: .88rem; }
|
||||||
|
/* Genau EIN aktives Event: zentrierte, etwas größere Einzelkachel statt
|
||||||
|
halbleerem Zwei-Spalten-Raster (Nutzer-Wunsch: "spezieller"). */
|
||||||
|
.jahres-events-grid-einzeln {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
max-width: 480px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.jahres-events-grid-einzeln .jahres-event-card { padding: 1.6rem 1.8rem; }
|
||||||
|
.jahres-events-grid-einzeln .jahres-event-card h3 { font-size: 1.3rem; }
|
||||||
|
/* Optionales Admin-Bild pro Event (20.08.2026, "Event des Jahres" jetzt in
|
||||||
|
der Verwaltung pflegbar) — bleibt per [hidden] unsichtbar, solange kein
|
||||||
|
Bild gesetzt ist, damit die textbasierten Standard-Karten unverändert
|
||||||
|
aussehen. Bugfix (per Playwright-Test gefunden): "display:block" allein
|
||||||
|
hätte dieselbe Spezifität wie die eingebaute [hidden]-Regel des Browsers
|
||||||
|
und würde sie im Zweifel überschreiben (leerer Bild-Rahmen wäre auch OHNE
|
||||||
|
gesetztes Bild sichtbar gewesen) — deshalb hier zusätzlich explizit
|
||||||
|
:not([hidden]) statt nur der Klasse allein. */
|
||||||
|
.jahres-event-bild[hidden] { display: none; }
|
||||||
|
.jahres-event-bild:not([hidden]) {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16/9;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: calc(var(--radius) - 4px);
|
||||||
|
margin-bottom: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- "Event des Jahres" — Detail-Fenster (20.08.2026) ----------
|
||||||
|
Nutzer-Wunsch: "wen ich drauf drücke dass dein ein kleines fenster
|
||||||
|
aufgeht wo das bild bissl größer ist mit einem größeren text und
|
||||||
|
beschreibung, und mit einem link ... bereite das richtig geil krank
|
||||||
|
vor." Augenschonend gehalten (siehe Projektregel): kein grelles Weiß,
|
||||||
|
sanftes Ein-/Ausblenden statt hartem Umschalten, respektiert
|
||||||
|
prefers-reduced-motion. */
|
||||||
|
.jahres-event-modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 200;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 4vh 5vw;
|
||||||
|
background: rgba(5, 7, 11, .78);
|
||||||
|
backdrop-filter: blur(6px);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity .22s ease;
|
||||||
|
}
|
||||||
|
.jahres-event-modal-overlay.offen { opacity: 1; }
|
||||||
|
.jahres-event-modal-overlay[hidden] { display: none; }
|
||||||
|
body.modal-offen { overflow: hidden; }
|
||||||
|
.jahres-event-modal {
|
||||||
|
position: relative;
|
||||||
|
width: min(640px, 100%);
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: var(--card-bg);
|
||||||
|
border: 1px solid rgba(212, 175, 55, .4);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
box-shadow: 0 24px 60px rgba(0, 0, 0, .5), 0 0 40px rgba(212, 175, 55, .12);
|
||||||
|
transform: scale(.94) translateY(10px);
|
||||||
|
transition: transform .25s ease;
|
||||||
|
}
|
||||||
|
.jahres-event-modal-overlay.offen .jahres-event-modal { transform: scale(1) translateY(0); }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.jahres-event-modal-overlay, .jahres-event-modal { transition: none; }
|
||||||
|
}
|
||||||
|
.jahres-event-modal-close {
|
||||||
|
position: absolute;
|
||||||
|
top: .9rem; right: .9rem;
|
||||||
|
z-index: 2;
|
||||||
|
width: 38px; height: 38px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid rgba(255, 255, 255, .25);
|
||||||
|
background: rgba(5, 7, 11, .7);
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color .15s ease, transform .15s ease;
|
||||||
|
}
|
||||||
|
.jahres-event-modal-close:hover { border-color: rgba(212, 175, 55, .7); transform: scale(1.06); }
|
||||||
|
.jahres-event-modal-bild {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 46vh;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: var(--radius) var(--radius) 0 0;
|
||||||
|
}
|
||||||
|
.jahres-event-modal-bild[hidden] { display: none; }
|
||||||
|
.jahres-event-modal-body { padding: 1.8rem 2rem 2.2rem; }
|
||||||
|
.jahres-event-modal-datum {
|
||||||
|
display: block;
|
||||||
|
color: #e8c465;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: .92rem;
|
||||||
|
letter-spacing: .03em;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
.jahres-event-modal-body h2 { font-size: 1.7rem; margin: 0 0 .8rem; }
|
||||||
|
.jahres-event-modal-body p { font-size: 1.08rem; line-height: 1.7; color: var(--text-muted); margin: 0 0 1.4rem; }
|
||||||
|
.jahres-event-modal-link[hidden] { display: none; }
|
||||||
|
|
||||||
/* ---------- Cards ---------- */
|
/* ---------- Cards ---------- */
|
||||||
.card {
|
.card {
|
||||||
background: var(--card-bg);
|
background: var(--card-bg);
|
||||||
@@ -1645,6 +1989,50 @@ header.site-header {
|
|||||||
box-shadow: 0 0 60px 6px rgba(143,217,234,.18);
|
box-shadow: 0 0 60px 6px rgba(143,217,234,.18);
|
||||||
opacity: .85;
|
opacity: .85;
|
||||||
}
|
}
|
||||||
|
/* "Diese Seite wächst noch"-Hinweis (20.08.2026, Nutzer-Wunsch) — bewusst
|
||||||
|
einladend statt wie eine Fehler-/Baustellen-Meldung, im selben Babyblau
|
||||||
|
wie der Rest des Wachstums-Baum-Themas dieser Seite (.zeit-baum-kappe/
|
||||||
|
-wurzel). Sitzt VOR dem Baum, damit Besucher sofort verstehen: hier
|
||||||
|
entsteht noch etwas, ohne dass es wie ein Fehler wirkt. */
|
||||||
|
.zeit-baustelle {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 1rem;
|
||||||
|
max-width: 640px;
|
||||||
|
margin: 0 auto 2.6rem;
|
||||||
|
padding: 1.1rem 1.4rem;
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: rgba(143, 217, 234, .06);
|
||||||
|
border: 1.5px solid rgba(143, 217, 234, .35);
|
||||||
|
box-shadow: 0 0 24px rgba(143, 217, 234, .12);
|
||||||
|
}
|
||||||
|
.zeit-baustelle-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
line-height: 1;
|
||||||
|
filter: drop-shadow(0 0 8px rgba(143, 217, 234, .55));
|
||||||
|
animation: zeit-baustelle-wiegen 3.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
.zeit-baustelle strong {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
margin-bottom: .3rem;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.zeit-baustelle p {
|
||||||
|
margin: 0;
|
||||||
|
font-size: .92rem;
|
||||||
|
color: var(--text-muted);
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
@keyframes zeit-baustelle-wiegen {
|
||||||
|
0%, 100% { transform: rotate(-5deg); }
|
||||||
|
50% { transform: rotate(5deg); }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.zeit-baustelle-icon { animation: none; }
|
||||||
|
}
|
||||||
|
|
||||||
.zeit-baum-kappe {
|
.zeit-baum-kappe {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1754,6 +2142,16 @@ header.site-header {
|
|||||||
.zeit-ast.zeit-final .zeit-karte { width: calc(100% - 4rem); max-width: 640px; text-align: center; }
|
.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 { justify-content: center !important; }
|
||||||
.zeit-ast.zeit-final .zeit-karte::before { display: none; }
|
.zeit-ast.zeit-final .zeit-karte::before { display: none; }
|
||||||
|
/* Bugfix 20.08.2026 (Nutzer-Report: "wieso dieser Punkt da in der Mitte"):
|
||||||
|
.zeit-node ist bei allen normalen Karten position:absolute; left:50%;
|
||||||
|
top:50% relativ zu .zeit-ast — das sitzt korrekt in der Baum-Mitte,
|
||||||
|
WEIL die Karte dort nur die halbe Breite einnimmt. Die Finale-Karte
|
||||||
|
(.zeit-final) wird aber auf fast volle Breite gestreckt und zentriert
|
||||||
|
(siehe .zeit-karte-Regel oben) — der Knoten landete dadurch mitten im
|
||||||
|
Text statt am Rand. Die Verbindungslinie (::before, direkt drüber)
|
||||||
|
wurde dafür schon 2025 ausgeblendet, der Knoten selbst wurde dabei
|
||||||
|
übersehen. */
|
||||||
|
.zeit-ast.zeit-final .zeit-node { display: none; }
|
||||||
.zeit-zitat {
|
.zeit-zitat {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
@@ -1968,6 +2366,51 @@ footer.site-footer {
|
|||||||
(z.B. dem DogFather-Manager-Artwork) nicht flach wirken. */
|
(z.B. dem DogFather-Manager-Artwork) nicht flach wirken. */
|
||||||
filter: saturate(1.26) contrast(1.15) brightness(1.03);
|
filter: saturate(1.26) contrast(1.15) brightness(1.03);
|
||||||
}
|
}
|
||||||
|
/* Sehr hohe Hochkant-Bilder (z.B. streamer-mascot.jpg mit 900x2812 px) würden
|
||||||
|
mit aspect-ratio:auto + height:auto in voller natürlicher Höhe rendern und die
|
||||||
|
danebenstehende Textspalte komplett erschlagen (Nutzer-Report 20.08.2026:
|
||||||
|
"der soll bissl kleiner sein und nicht so riesig"). Ein zu knapper max-height-
|
||||||
|
Deckel (erster Versuch: 460px) machte es dann aber zu klein und ließ eine
|
||||||
|
sichtbare Lücke unter dem Bild, weil die Textspalte daneben normalerweise
|
||||||
|
deutlich mehr Platz braucht (Nutzer-Report 21.08.2026: "die größe vom bild
|
||||||
|
vom husky soll dem text rechts nebendran angepasst werden").
|
||||||
|
Ausprobiert und wieder verworfen: die Höhe per CSS Grid align-items:stretch +
|
||||||
|
height:100% exakt an die Textspalte zu koppeln. Funktioniert bei Grid NICHT
|
||||||
|
robust, weil ein 'auto' sizing Track seine Höhe am MAX-CONTENT-Beitrag beider
|
||||||
|
Felder ausrichtet -- bei einem derart extremen Hochkantbild (Seitenverhältnis
|
||||||
|
~1:3.1) zieht das Bild die ganze Zeile (und damit auch die Textspalte) auf
|
||||||
|
seine eigene, riesige natürliche Höhe hoch, egal was man an min-height
|
||||||
|
probiert (min-height wirkt nur auf den Mindest-, nicht den Wachstums-Beitrag).
|
||||||
|
Robuster Kompromiss stattdessen: ein großzügiger fester max-height-Wert
|
||||||
|
(720px statt 460px), der sich an der TYPISCHEN Höhe der Textspalte in diesem
|
||||||
|
Abschnitt orientiert (auf normalen Desktop-Breiten ~715-850px hoch) -- das
|
||||||
|
Bild wirkt dadurch angemessen groß und auf Augenhöhe mit dem Text, ohne die
|
||||||
|
fragile/rückkoppelnde Grid-Stretch-Mechanik. width:fit-content lässt den
|
||||||
|
Rahmen dabei eng am Bild sitzen, statt breite gestreifte Hintergrund-Balken
|
||||||
|
danebenzulassen.
|
||||||
|
WICHTIG: muss NACH ".collage-photo img" stehen — beide Regeln haben dieselbe
|
||||||
|
Spezifität (eine Klasse + ein Element), die spätere gewinnt. Stünde dieser
|
||||||
|
Block davor, würde width:100%/height:100% oben die Deckelung wieder aufheben. */
|
||||||
|
.collage-photo-kompakt {
|
||||||
|
aspect-ratio: auto;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.collage-photo-kompakt img {
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
max-height: 720px;
|
||||||
|
max-width: 100%;
|
||||||
|
display: block;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
@media (max-width: 780px) {
|
||||||
|
/* Auf Mobilgeräten stehen die Grid-Spalten untereinander -- dort ist die
|
||||||
|
Textspalte durch die volle Breite viel kürzer, ein 720px hohes Bild wäre
|
||||||
|
unangemessen groß für einen schmalen Handy-Screen. Deckel daher kleiner. */
|
||||||
|
.collage-photo-kompakt img { max-height: 420px; }
|
||||||
|
}
|
||||||
/* Cineastischer "Poster-Grade" für JEDES Präsentationsfoto auf der Seite:
|
/* Cineastischer "Poster-Grade" für JEDES Präsentationsfoto auf der Seite:
|
||||||
zwei Farbschein-Blooms in der jeweiligen Rahmenfarbe (oben-links /
|
zwei Farbschein-Blooms in der jeweiligen Rahmenfarbe (oben-links /
|
||||||
unten-rechts, wie ein Studio-Rimlight — nutzt dieselbe --frame-Variable
|
unten-rechts, wie ein Studio-Rimlight — nutzt dieselbe --frame-Variable
|
||||||
@@ -2146,7 +2589,7 @@ footer.site-footer {
|
|||||||
Cover, die jeden Rand auffüllt, den "contain" freilässt — dadurch
|
Cover, die jeden Rand auffüllt, den "contain" freilässt — dadurch
|
||||||
wirkt es trotzdem randlos, obwohl die scharfen Pixel garantiert
|
wirkt es trotzdem randlos, obwohl die scharfen Pixel garantiert
|
||||||
nie beschnitten werden. */
|
nie beschnitten werden. */
|
||||||
background-image: url("../img/universe-trio.jpg"), url("../img/universe-trio-blur.jpg");
|
background-image: url("../img/universe-trio.jpg?v=20260820h"), url("../img/universe-trio-blur.jpg?v=20260820h");
|
||||||
background-size: contain, cover;
|
background-size: contain, cover;
|
||||||
background-position: center, center;
|
background-position: center, center;
|
||||||
background-repeat: no-repeat, no-repeat;
|
background-repeat: no-repeat, no-repeat;
|
||||||
@@ -3188,3 +3631,342 @@ body.start-seite .music-toggle:focus-visible {
|
|||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
}
|
}
|
||||||
.todo-note strong { color: var(--accent-2); }
|
.todo-note strong { color: var(--accent-2); }
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
Streamplan — "Live-Radar" (20.08.2026, Nutzer-Wunsch: "soll viel
|
||||||
|
geiler und spezieller sein, überrasch mich"). Ein 24-Stunden-Ziffernblatt
|
||||||
|
als SVG: der feste 20-Uhr-Termin leuchtet dauerhaft als Fixpunkt, ein
|
||||||
|
"Jetzt"-Zeiger bewegt sich in Echtzeit mit der tatsächlichen Uhrzeit,
|
||||||
|
ein sanft rotierender Lichtschein sorgt für Atmosphäre. Schaltet live
|
||||||
|
auf Rot/Puls um, sobald DogFather laut /live-status wirklich sendet —
|
||||||
|
sonst zählt ein echter Sekunden-Countdown zum nächsten 20-Uhr-Termin
|
||||||
|
runter. Bewusst augenschonend: gedeckte Farben, langsame, sanfte
|
||||||
|
Animation, kompletter Stillstand bei prefers-reduced-motion. ================================================================= */
|
||||||
|
/* Reihe für 1-2 Uhren nebeneinander (20.08.2026: zweite Uhr für ein
|
||||||
|
"spezielles Live-Event", siehe assets/js/streamplan.js) — bricht auf
|
||||||
|
schmalen Bildschirmen automatisch untereinander um. */
|
||||||
|
.stream-radar-reihe {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 2.4rem;
|
||||||
|
margin-top: 1.4rem;
|
||||||
|
}
|
||||||
|
/* .stream-radar-wrap setzt selbst "display", das schlägt bei gleicher
|
||||||
|
Spezifität die eingebaute [hidden]-Regel des Browsers (identisches,
|
||||||
|
inzwischen mehrfach gefundenes Muster auf dieser Seite) — deshalb hier
|
||||||
|
explizit VOR der Klassenregel. */
|
||||||
|
.stream-radar-wrap[hidden] { display: none; }
|
||||||
|
.stream-radar-wrap { display: flex; flex-direction: column; align-items: center; margin: 0 auto; }
|
||||||
|
.stream-radar {
|
||||||
|
position: relative;
|
||||||
|
width: min(300px, 70vw);
|
||||||
|
aspect-ratio: 1;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.stream-radar-sweep {
|
||||||
|
position: absolute;
|
||||||
|
inset: -6%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: conic-gradient(from 0deg,
|
||||||
|
transparent 0deg, rgba(143, 217, 234, .28) 40deg, transparent 90deg,
|
||||||
|
transparent 200deg, rgba(232, 196, 101, .22) 250deg, transparent 300deg,
|
||||||
|
transparent 320deg, rgba(139, 92, 246, .24) 350deg, transparent 360deg);
|
||||||
|
animation: stream-radar-rotate 16s linear infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
/* Farbvariante fürs spezielle Live-Event (Nutzer-Wunsch "bissl Farbe") —
|
||||||
|
Magenta/Violett/Gold statt Cyan/Violett/Gold, damit die zweite Uhr auch
|
||||||
|
auf den ersten Blick als "etwas Besonderes" erkennbar ist. */
|
||||||
|
.stream-radar.event-spezial .stream-radar-sweep {
|
||||||
|
background: conic-gradient(from 0deg,
|
||||||
|
transparent 0deg, rgba(255, 92, 180, .26) 40deg, transparent 90deg,
|
||||||
|
transparent 200deg, rgba(232, 196, 101, .22) 250deg, transparent 300deg,
|
||||||
|
transparent 320deg, rgba(139, 92, 246, .28) 350deg, transparent 360deg);
|
||||||
|
}
|
||||||
|
@keyframes stream-radar-rotate { to { transform: rotate(360deg); } }
|
||||||
|
@media (prefers-reduced-motion: reduce) { .stream-radar-sweep { animation: none; } }
|
||||||
|
.stream-radar-dial { position: relative; width: 100%; height: 100%; display: block; }
|
||||||
|
/* Bunter Verlauf statt reinem Grau-Ton auf dem äußeren Ring (Nutzer-Wunsch
|
||||||
|
"bissl Farbe") — Farbverlauf steckt als <linearGradient> direkt im SVG
|
||||||
|
jeder Uhr (siehe streamplan.html), hier nur die Basiswerte/Deckkraft. */
|
||||||
|
.radar-ring-outer { fill: none; stroke-width: 2; opacity: .8; }
|
||||||
|
.radar-ring-inner { fill: rgba(255, 255, 255, .02); stroke: rgba(255, 255, 255, .07); stroke-width: 1; }
|
||||||
|
.radar-tick { stroke: rgba(255, 255, 255, .22); stroke-width: 1.5; }
|
||||||
|
.radar-tick-major { stroke: rgba(255, 255, 255, .5); stroke-width: 2.2; }
|
||||||
|
.radar-tick-label { fill: var(--text-muted); font-size: 11px; font-family: var(--font-body, inherit); }
|
||||||
|
.radar-fixmarker-dot {
|
||||||
|
fill: #e8c465;
|
||||||
|
filter: drop-shadow(0 0 6px rgba(232, 196, 101, .9)) drop-shadow(0 0 14px rgba(232, 196, 101, .5));
|
||||||
|
animation: stream-radar-fixpuls 3.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes stream-radar-fixpuls { 0%, 100% { opacity: .75; } 50% { opacity: 1; } }
|
||||||
|
@media (prefers-reduced-motion: reduce) { .radar-fixmarker-dot { animation: none; opacity: .9; } }
|
||||||
|
.radar-now-hand {
|
||||||
|
stroke: var(--accent);
|
||||||
|
stroke-width: 2.5;
|
||||||
|
stroke-linecap: round;
|
||||||
|
filter: drop-shadow(0 0 4px var(--accent));
|
||||||
|
transition: x2 .6s ease, y2 .6s ease;
|
||||||
|
}
|
||||||
|
.radar-now-dot {
|
||||||
|
fill: var(--accent);
|
||||||
|
filter: drop-shadow(0 0 6px var(--accent));
|
||||||
|
transition: cx .6s ease, cy .6s ease;
|
||||||
|
}
|
||||||
|
.stream-radar.ist-live .radar-now-hand,
|
||||||
|
.stream-radar.ist-live .radar-now-dot { stroke: #ff5c72; fill: #ff5c72; filter: drop-shadow(0 0 8px rgba(255, 92, 114, .8)); }
|
||||||
|
.radar-center-dot { fill: var(--text); opacity: .8; }
|
||||||
|
.stream-radar-center {
|
||||||
|
position: absolute;
|
||||||
|
inset: 24%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(8, 10, 14, .82);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
padding: .5rem;
|
||||||
|
gap: .25rem;
|
||||||
|
}
|
||||||
|
.stream-radar-dot {
|
||||||
|
width: 10px; height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--text-muted);
|
||||||
|
margin-bottom: .2rem;
|
||||||
|
}
|
||||||
|
.stream-radar.ist-live .stream-radar-dot { background: #ff5c72; box-shadow: 0 0 10px rgba(255, 92, 114, .8); animation: stream-radar-live-puls 1.4s ease-in-out infinite; }
|
||||||
|
@keyframes stream-radar-live-puls { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.35); opacity: .75; } }
|
||||||
|
@media (prefers-reduced-motion: reduce) { .stream-radar.ist-live .stream-radar-dot { animation: none; } }
|
||||||
|
.stream-radar-status { font-size: 1rem; font-weight: 800; color: var(--text); line-height: 1.25; }
|
||||||
|
.stream-radar-countdown {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
font-size: 1.55rem;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: .02em;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.stream-radar.ist-live .stream-radar-countdown { color: #ff5c72; }
|
||||||
|
.stream-radar-sub { font-size: .74rem; color: var(--text-muted); }
|
||||||
|
/* Farbvariante fürs spezielle Live-Event: der Fixpunkt ist hier nicht der
|
||||||
|
tägliche 20-Uhr-Termin, sondern der eigene Zieltermin -- Magenta statt
|
||||||
|
Gold, damit man beide Uhren auf einen Blick unterscheidet. */
|
||||||
|
.stream-radar.event-spezial .radar-fixmarker-dot {
|
||||||
|
fill: #ff5cb8;
|
||||||
|
filter: drop-shadow(0 0 6px rgba(255, 92, 184, .9)) drop-shadow(0 0 14px rgba(255, 92, 184, .5));
|
||||||
|
}
|
||||||
|
.stream-radar.event-spezial:not(.ist-live) .radar-now-hand,
|
||||||
|
.stream-radar.event-spezial:not(.ist-live) .radar-now-dot { stroke: #c9a6ff; fill: #c9a6ff; filter: drop-shadow(0 0 5px rgba(201, 166, 255, .8)); }
|
||||||
|
.stream-radar.event-spezial:not(.ist-live) .stream-radar-countdown { color: #ff5cb8; }
|
||||||
|
.stream-radar-titel { font-size: .95rem; font-weight: 800; color: #ff9ed4; margin-bottom: .3rem; }
|
||||||
|
/* .btn setzt selbst "display", das schlägt bei gleicher Spezifität die
|
||||||
|
eingebaute [hidden]-Regel des Browsers (identisches Muster wie bei
|
||||||
|
.jahres-event-card weiter oben) — deshalb hier explizit. */
|
||||||
|
.stream-radar-watch-btn[hidden] { display: none; }
|
||||||
|
.stream-radar-watch-btn { margin-top: 1.1rem; }
|
||||||
|
.stream-radar-hinweis { margin-top: 1rem; opacity: .8; }
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
"Stimmen" — eigene Nachricht einreichen (20.08.2026, Nutzer-Wunsch:
|
||||||
|
"eine richtig geile kachel ... das wird mega persoenlich zu den fans,
|
||||||
|
bitte wirklich krass geil speziell"). Warmer Babyblau/Rosé/Lila-
|
||||||
|
Farbverlauf statt der neutralen Standardkarte, wandernder Lichtschein
|
||||||
|
am Rand (dieselbe Zwei-Ebenen-Technik wie schon an mehreren Stellen
|
||||||
|
dieses Projekts, siehe .supp-card::before) — bewusst augenschonend
|
||||||
|
(gedeckte Farben, sanfte Bewegung, respektiert reduzierte Bewegung). ================================================================= */
|
||||||
|
/* Nutzer-Wunsch 20.08.2026 (2. Runde): "die kachel soll noch spezieller und
|
||||||
|
geiler aussehen". Deutlich aufgewertet gegenüber der ersten Fassung:
|
||||||
|
wandernder Farbverlauf-Rahmen rundherum (Zwei-Ebenen-Technik mit
|
||||||
|
background-position statt eines rotierenden Rings — siehe dokumentierte
|
||||||
|
Projekt-Lektion: rotierende Masken verzerren auf eckigen Flächen),
|
||||||
|
schwebende Herz-Partikel im Hintergrund, Medaillon statt nacktem Emoji,
|
||||||
|
Farbverlauf-Überschrift und Eingabefelder, die beim Tippen aufleuchten.
|
||||||
|
Bleibt trotzdem augenschonend: gedeckte Töne, langsame Bewegungen,
|
||||||
|
vollständiger Stillstand bei prefers-reduced-motion. */
|
||||||
|
.stimmen-einreichen-kachel {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 580px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 2.4rem 2.4rem 2.6rem;
|
||||||
|
border-radius: calc(var(--radius) + 4px);
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse at 12% 0%, rgba(255, 154, 208, .13), transparent 58%),
|
||||||
|
radial-gradient(ellipse at 88% 100%, rgba(143, 217, 234, .13), transparent 58%),
|
||||||
|
linear-gradient(180deg, rgba(24, 34, 51, .96), rgba(14, 20, 32, .98));
|
||||||
|
box-shadow: 0 30px 70px rgba(0, 0, 0, .45), 0 0 50px rgba(143, 217, 234, .06);
|
||||||
|
}
|
||||||
|
/* Farbverlauf-Rahmen: zwei Ebenen, nur die Hintergrundposition wandert. */
|
||||||
|
.stimmen-einreichen-kachel::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
border-radius: inherit;
|
||||||
|
padding: 1.5px;
|
||||||
|
background: linear-gradient(115deg, rgba(255,154,208,.75), rgba(143,217,234,.75), rgba(199,204,209,.5), rgba(255,154,208,.75));
|
||||||
|
background-size: 300% 100%;
|
||||||
|
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
||||||
|
-webkit-mask-composite: xor;
|
||||||
|
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
||||||
|
mask-composite: exclude;
|
||||||
|
animation: stimmen-rahmen-wandert 9s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes stimmen-rahmen-wandert { to { background-position: 300% 0; } }
|
||||||
|
/* Schwebende Herz-Partikel — reines CSS, keine zusätzlichen Elemente nötig. */
|
||||||
|
.stimmen-einreichen-glanz {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(circle, rgba(255,154,208,.5) 2px, transparent 3px),
|
||||||
|
radial-gradient(circle, rgba(143,217,234,.45) 2px, transparent 3px),
|
||||||
|
radial-gradient(circle, rgba(255,154,208,.35) 1.5px, transparent 3px);
|
||||||
|
background-size: 190px 190px, 260px 260px, 140px 140px;
|
||||||
|
background-position: 12% 18%, 78% 62%, 44% 88%;
|
||||||
|
animation: stimmen-partikel-treiben 16s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
@keyframes stimmen-partikel-treiben {
|
||||||
|
from { background-position: 12% 18%, 78% 62%, 44% 88%; opacity: .55; }
|
||||||
|
to { background-position: 18% 26%, 71% 54%, 50% 79%; opacity: .95; }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.stimmen-einreichen-kachel::before, .stimmen-einreichen-glanz { animation: none; }
|
||||||
|
}
|
||||||
|
.stimmen-einreichen-kopf { text-align: center; margin-bottom: 1.8rem; }
|
||||||
|
/* Medaillon statt nacktem Emoji: Kreis mit Farbschein und sanftem Puls. */
|
||||||
|
.stimmen-einreichen-herz {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 74px; height: 74px;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 2.1rem;
|
||||||
|
background: radial-gradient(circle at 35% 30%, rgba(255,154,208,.28), rgba(143,217,234,.12) 70%);
|
||||||
|
border: 1.5px solid rgba(255, 154, 208, .45);
|
||||||
|
box-shadow: 0 0 26px rgba(255, 154, 208, .3), inset 0 0 22px rgba(255, 154, 208, .12);
|
||||||
|
animation: stimmen-herz-puls 3.4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes stimmen-herz-puls {
|
||||||
|
0%, 100% { transform: scale(1); box-shadow: 0 0 22px rgba(255,154,208,.22), inset 0 0 20px rgba(255,154,208,.1); }
|
||||||
|
50% { transform: scale(1.06); box-shadow: 0 0 40px rgba(255,154,208,.45), inset 0 0 26px rgba(255,154,208,.18); }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) { .stimmen-einreichen-herz { animation: none; } }
|
||||||
|
.stimmen-einreichen-kopf h2 {
|
||||||
|
margin: .9rem 0 .6rem;
|
||||||
|
background: linear-gradient(100deg, #ffd0e8, #ffffff 40%, #a8e4f2);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
.stimmen-einreichen-kopf p { color: var(--text-muted); }
|
||||||
|
#stimmen-formular .field { margin-bottom: 1.1rem; }
|
||||||
|
/* Eingabefelder leuchten beim Tippen in der Kachel-Farbwelt auf. */
|
||||||
|
#stimmen-formular .field input,
|
||||||
|
#stimmen-formular .field textarea {
|
||||||
|
transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
|
||||||
|
}
|
||||||
|
#stimmen-formular .field input:focus,
|
||||||
|
#stimmen-formular .field textarea:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: rgba(255, 154, 208, .75);
|
||||||
|
box-shadow: 0 0 0 3px rgba(255, 154, 208, .14), 0 0 22px rgba(255, 154, 208, .18);
|
||||||
|
background: rgba(255, 255, 255, .06);
|
||||||
|
}
|
||||||
|
.stimmen-erfolg { color: #baf7d6; font-size: .9rem; margin-top: 1rem; display: none; text-align: center; }
|
||||||
|
.stimmen-erfolg.zeige { display: block; }
|
||||||
|
.stimmen-fehler { color: #ff9a9a; font-size: .88rem; margin-top: 1rem; display: none; text-align: center; }
|
||||||
|
.stimmen-fehler.zeige { display: block; }
|
||||||
|
|
||||||
|
/* Freigegebene Stimmen (20.08.2026, 2. Runde: "und auch die die danach
|
||||||
|
kommen wenn ich sie eingebe die sollen viel geiler sein"): echte
|
||||||
|
Präsentationskarten statt schlichter Kästen — Farbschein-Bloom in der
|
||||||
|
Ecke, großes Anführungszeichen als Wasserzeichen, farbige Akzentlinie
|
||||||
|
oben, Avatar-Medaillon mit dem Anfangsbuchstaben, TikTok-Handle als
|
||||||
|
eigener Chip. Beim Überfahren hebt sich die Karte leicht an. */
|
||||||
|
.stimmen-karte {
|
||||||
|
position: relative;
|
||||||
|
isolation: isolate;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 2rem 1.8rem 1.6rem;
|
||||||
|
border-radius: calc(var(--radius) - 2px);
|
||||||
|
border: 1px solid rgba(143, 217, 234, .22);
|
||||||
|
background:
|
||||||
|
radial-gradient(ellipse at 100% 0%, rgba(255, 154, 208, .1), transparent 55%),
|
||||||
|
linear-gradient(180deg, rgba(24, 34, 51, .9), rgba(15, 21, 33, .95));
|
||||||
|
box-shadow: 0 16px 40px rgba(0, 0, 0, .32);
|
||||||
|
transition: transform .25s ease, border-color .25s ease, box-shadow .25s ease;
|
||||||
|
}
|
||||||
|
.stimmen-karte:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
border-color: rgba(255, 154, 208, .5);
|
||||||
|
box-shadow: 0 22px 52px rgba(0, 0, 0, .42), 0 0 30px rgba(255, 154, 208, .14);
|
||||||
|
}
|
||||||
|
/* Farbige Akzentlinie oben — greift die Farbwelt der Einreich-Kachel auf. */
|
||||||
|
.stimmen-karte::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 2.5px;
|
||||||
|
background: linear-gradient(90deg, rgba(255,154,208,.9), rgba(143,217,234,.9));
|
||||||
|
}
|
||||||
|
/* Großes Anführungszeichen als dezentes Wasserzeichen im Hintergrund. */
|
||||||
|
.stimmen-karte::before {
|
||||||
|
content: "„";
|
||||||
|
position: absolute;
|
||||||
|
top: -.9rem; right: .6rem;
|
||||||
|
z-index: -1;
|
||||||
|
font-size: 8rem;
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
color: rgba(143, 217, 234, .09);
|
||||||
|
line-height: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.stimmen-karte-text {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
flex: 1;
|
||||||
|
margin: 0 0 1.3rem;
|
||||||
|
line-height: 1.75;
|
||||||
|
font-size: 1.02rem;
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
.stimmen-karte-fuss {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .7rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, .07);
|
||||||
|
}
|
||||||
|
/* Avatar-Medaillon mit dem Anfangsbuchstaben (wird in stimmen.js gefüllt). */
|
||||||
|
.stimmen-karte-avatar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px; height: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #0d1420;
|
||||||
|
background: linear-gradient(135deg, #ffd0e8, #a8e4f2);
|
||||||
|
box-shadow: 0 0 16px rgba(255, 154, 208, .3);
|
||||||
|
}
|
||||||
|
.stimmen-karte-namen { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
|
||||||
|
.stimmen-karte-name { font-weight: 700; color: var(--text); font-size: .95rem; }
|
||||||
|
.stimmen-karte-tiktok {
|
||||||
|
align-self: flex-start;
|
||||||
|
font-size: .76rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #a8e4f2;
|
||||||
|
padding: .1rem .5rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(143, 217, 234, .12);
|
||||||
|
border: 1px solid rgba(143, 217, 234, .28);
|
||||||
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 349 KiB After Width: | Height: | Size: 482 KiB |
|
Before Width: | Height: | Size: 349 KiB After Width: | Height: | Size: 482 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 413 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 413 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 349 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 246 KiB |
|
Before Width: | Height: | Size: 359 KiB After Width: | Height: | Size: 459 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 349 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 299 KiB After Width: | Height: | Size: 400 KiB |
|
Before Width: | Height: | Size: 344 KiB After Width: | Height: | Size: 403 KiB |
|
Before Width: | Height: | Size: 344 KiB After Width: | Height: | Size: 403 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 576 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 310 KiB After Width: | Height: | Size: 375 KiB |
|
Before Width: | Height: | Size: 415 KiB After Width: | Height: | Size: 576 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 295 KiB After Width: | Height: | Size: 396 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 419 KiB |
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 419 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 244 KiB After Width: | Height: | Size: 276 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 300 KiB |
|
Before Width: | Height: | Size: 408 KiB After Width: | Height: | Size: 477 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 313 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 258 KiB After Width: | Height: | Size: 349 KiB |
|
After Width: | Height: | Size: 384 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 356 KiB |
|
Before Width: | Height: | Size: 508 KiB After Width: | Height: | Size: 356 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 239 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
data-manager.js — Manager-Datenobjekt (21.08.2026)
|
||||||
|
|
||||||
|
DogFather (Filipe) selbst hat weiterhin seine eigene, feste Seite
|
||||||
|
(manager.html) — dieses Array ist für WEITERE Manager gedacht, die
|
||||||
|
künftig dazukommen könnten (siehe zeitreise.html, "Aufbau eines
|
||||||
|
eigenen Creator-Systems": "Eine langfristige Struktur für Creator,
|
||||||
|
Scouts und zukünftige Manager"). Noch niemand final geklärt, deshalb
|
||||||
|
bewusst leer — genau wie data-creator.js, bis echte Namen feststehen.
|
||||||
|
Einfach nach dem Muster in data-scouts.js Objekte ins Array einfügen,
|
||||||
|
sobald es so weit ist — manager.html liest automatisch aus.
|
||||||
|
|
||||||
|
Felder: siehe data-scouts.js (identisches Muster).
|
||||||
|
===================================================================== */
|
||||||
|
|
||||||
|
const MANAGERS = [];
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") window.MANAGERS = MANAGERS;
|
||||||
@@ -68,7 +68,7 @@ const MODIS = [
|
|||||||
bioHtml: {
|
bioHtml: {
|
||||||
de: `
|
de: `
|
||||||
<p>Hallo, ich bin <strong>Diene</strong> und auf TikTok findet ihr mich unter <strong>@_ditschi</strong>.</p>
|
<p>Hallo, ich bin <strong>Diene</strong> und auf TikTok findet ihr mich unter <strong>@_ditschi</strong>.</p>
|
||||||
<p>Ich bin 37 Jahre alt, verheiratet und Mutter von zwei Kindern. Wer mich etwas besser kennt, weiß, dass ich manchmal ziemlich verpeilt und vergesslich sein kann. Und wer mich noch nicht kennt, wird es vermutlich ziemlich schnell bemerken. Genau das gehört aber auch einfach zu mir.</p>
|
<p>Ich bin im August 1988 geboren, verheiratet und Mutter von zwei Kindern. Wer mich etwas besser kennt, weiß, dass ich manchmal ziemlich verpeilt und vergesslich sein kann. Und wer mich noch nicht kennt, wird es vermutlich ziemlich schnell bemerken. Genau das gehört aber auch einfach zu mir.</p>
|
||||||
<p>Auf TikTok bin ich ungefähr seit dem Jahr 2020 aktiv. Anfangs habe ich hauptsächlich Videos angesehen und gelegentlich eigene Videos für mich erstellt.</p>
|
<p>Auf TikTok bin ich ungefähr seit dem Jahr 2020 aktiv. Anfangs habe ich hauptsächlich Videos angesehen und gelegentlich eigene Videos für mich erstellt.</p>
|
||||||
<p>Ende April 2026 war ich zum ersten Mal in einem Livestream von Dogi. Seit Anfang Mai bin ich regelmäßig – meistens sogar täglich – dabei.</p>
|
<p>Ende April 2026 war ich zum ersten Mal in einem Livestream von Dogi. Seit Anfang Mai bin ich regelmäßig – meistens sogar täglich – dabei.</p>
|
||||||
<h3>Warum bin ich geblieben?</h3>
|
<h3>Warum bin ich geblieben?</h3>
|
||||||
@@ -86,7 +86,7 @@ const MODIS = [
|
|||||||
`,
|
`,
|
||||||
"de-CH": `
|
"de-CH": `
|
||||||
<p>Hoi, ich bi <strong>Diene</strong> und uf TikTok findet ihr mich unter <strong>@_ditschi</strong>.</p>
|
<p>Hoi, ich bi <strong>Diene</strong> und uf TikTok findet ihr mich unter <strong>@_ditschi</strong>.</p>
|
||||||
<p>Ich bi 37 Jahr alt, verhürotet und Mueter vo zwöi Chind. Wer mich es bitzeli besser kennt, weiss, dass ich mängisch ziemlich verpeilt und vergesslich sii cha. Und wer mich no nöd kennt, wird's wahrschindlich ziemlich schnäll merke. Aber genau das ghört eifach au zu mir dezue.</p>
|
<p>Ich bi im Auguscht 1988 gebore, verhürotet und Mueter vo zwöi Chind. Wer mich es bitzeli besser kennt, weiss, dass ich mängisch ziemlich verpeilt und vergesslich sii cha. Und wer mich no nöd kennt, wird's wahrschindlich ziemlich schnäll merke. Aber genau das ghört eifach au zu mir dezue.</p>
|
||||||
<p>Uf TikTok bin ich öppe sit 2020 aktiv. Am Afang han ich hauptsächlich Videos aglueget und ab und zue mal es eigets Video für mich gmacht.</p>
|
<p>Uf TikTok bin ich öppe sit 2020 aktiv. Am Afang han ich hauptsächlich Videos aglueget und ab und zue mal es eigets Video für mich gmacht.</p>
|
||||||
<p>Ändi April 2026 bin ich zum erste Mal i-me Livestream vom Dogi gsi. Sit Afang Mai bin ich regelmässig – meistens sogar täglich – debii.</p>
|
<p>Ändi April 2026 bin ich zum erste Mal i-me Livestream vom Dogi gsi. Sit Afang Mai bin ich regelmässig – meistens sogar täglich – debii.</p>
|
||||||
<h3>Wieso bin ich blibe?</h3>
|
<h3>Wieso bin ich blibe?</h3>
|
||||||
@@ -104,7 +104,7 @@ const MODIS = [
|
|||||||
`,
|
`,
|
||||||
en: `
|
en: `
|
||||||
<p>Hi, I'm <strong>Diene</strong>, and you can find me on TikTok as <strong>@_ditschi</strong>.</p>
|
<p>Hi, I'm <strong>Diene</strong>, and you can find me on TikTok as <strong>@_ditschi</strong>.</p>
|
||||||
<p>I'm 37 years old, married, and a mother of two. Anyone who knows me a bit better knows that I can be pretty scatterbrained and forgetful sometimes. And anyone who doesn't know me yet will probably notice pretty quickly. But that's just part of who I am.</p>
|
<p>I was born in August 1988, married, and a mother of two. Anyone who knows me a bit better knows that I can be pretty scatterbrained and forgetful sometimes. And anyone who doesn't know me yet will probably notice pretty quickly. But that's just part of who I am.</p>
|
||||||
<p>I've been active on TikTok since around 2020. At first I mostly watched videos and occasionally made a few of my own just for fun.</p>
|
<p>I've been active on TikTok since around 2020. At first I mostly watched videos and occasionally made a few of my own just for fun.</p>
|
||||||
<p>At the end of April 2026, I joined one of Dogi's livestreams for the very first time. Since the beginning of May, I've been there regularly — usually even daily.</p>
|
<p>At the end of April 2026, I joined one of Dogi's livestreams for the very first time. Since the beginning of May, I've been there regularly — usually even daily.</p>
|
||||||
<h3>Why did I stay?</h3>
|
<h3>Why did I stay?</h3>
|
||||||
@@ -122,7 +122,7 @@ const MODIS = [
|
|||||||
`,
|
`,
|
||||||
fr: `
|
fr: `
|
||||||
<p>Salut, je suis <strong>Diene</strong> et vous me trouverez sur TikTok sous le pseudo <strong>@_ditschi</strong>.</p>
|
<p>Salut, je suis <strong>Diene</strong> et vous me trouverez sur TikTok sous le pseudo <strong>@_ditschi</strong>.</p>
|
||||||
<p>J'ai 37 ans, je suis mariée et maman de deux enfants. Ceux qui me connaissent un peu mieux savent que je peux être plutôt tête en l'air et distraite. Et ceux qui ne me connaissent pas encore le remarqueront sans doute assez vite. Mais ça fait tout simplement partie de moi.</p>
|
<p>Je suis née en août 1988, mariée et maman de deux enfants. Ceux qui me connaissent un peu mieux savent que je peux être plutôt tête en l'air et distraite. Et ceux qui ne me connaissent pas encore le remarqueront sans doute assez vite. Mais ça fait tout simplement partie de moi.</p>
|
||||||
<p>Je suis active sur TikTok depuis environ 2020. Au début, je regardais surtout des vidéos et je créais de temps en temps les miennes, juste pour moi.</p>
|
<p>Je suis active sur TikTok depuis environ 2020. Au début, je regardais surtout des vidéos et je créais de temps en temps les miennes, juste pour moi.</p>
|
||||||
<p>Fin avril 2026, j'ai assisté pour la première fois à un livestream de Dogi. Depuis début mai, j'y suis régulièrement — le plus souvent même tous les jours.</p>
|
<p>Fin avril 2026, j'ai assisté pour la première fois à un livestream de Dogi. Depuis début mai, j'y suis régulièrement — le plus souvent même tous les jours.</p>
|
||||||
<h3>Pourquoi suis-je restée ?</h3>
|
<h3>Pourquoi suis-je restée ?</h3>
|
||||||
@@ -140,7 +140,7 @@ const MODIS = [
|
|||||||
`,
|
`,
|
||||||
pt: `
|
pt: `
|
||||||
<p>Olá, eu sou a <strong>Diene</strong> e podem encontrar-me no TikTok como <strong>@_ditschi</strong>.</p>
|
<p>Olá, eu sou a <strong>Diene</strong> e podem encontrar-me no TikTok como <strong>@_ditschi</strong>.</p>
|
||||||
<p>Tenho 37 anos, sou casada e mãe de dois filhos. Quem me conhece um pouco melhor sabe que às vezes posso ser bastante distraída e esquecida. E quem ainda não me conhece, provavelmente vai reparar nisso bem depressa. Mas isso também faz simplesmente parte de mim.</p>
|
<p>Nasci em agosto de 1988, sou casada e mãe de dois filhos. Quem me conhece um pouco melhor sabe que às vezes posso ser bastante distraída e esquecida. E quem ainda não me conhece, provavelmente vai reparar nisso bem depressa. Mas isso também faz simplesmente parte de mim.</p>
|
||||||
<p>Estou ativa no TikTok desde cerca de 2020. No início, via sobretudo vídeos e, de vez em quando, criava os meus próprios, só para mim.</p>
|
<p>Estou ativa no TikTok desde cerca de 2020. No início, via sobretudo vídeos e, de vez em quando, criava os meus próprios, só para mim.</p>
|
||||||
<p>No final de abril de 2026, assisti pela primeira vez a uma livestream do Dogi. Desde o início de maio que estou presente regularmente — na maioria das vezes até diariamente.</p>
|
<p>No final de abril de 2026, assisti pela primeira vez a uma livestream do Dogi. Desde o início de maio que estou presente regularmente — na maioria das vezes até diariamente.</p>
|
||||||
<h3>Porque é que fiquei?</h3>
|
<h3>Porque é que fiquei?</h3>
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const SCOUTS = [
|
|||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
<p>Ich freue mich auf alles, was noch vor mir liegt: neue Begegnungen, besondere Erinnerungen und viele weitere gemeinsame Momente.</p>
|
<p>Ich freue mich auf alles, was noch vor mir liegt: neue Begegnungen, besondere Erinnerungen und viele weitere gemeinsame Momente.</p>
|
||||||
<p class="quote" style="margin-top:1rem;">Das bin ich – Patrick. Nicht perfekt, manchmal mit einem grimmigen Blick, aber immer mit Herz. ❤️</p>
|
<p class="quote" style="margin-top:1rem;">Das bin ich – Patrick. Nicht perfekt, manchmal mit einem grimmigen Blick, aber immer mit Herz. ❤️</p>
|
||||||
<p style="text-align:right;"><strong>– euer Patrick ❤️</strong></p>
|
<p style="text-align:right;"><strong>– euer Patrick ❤️</strong><br />Scout von Dogfather</p>
|
||||||
`,
|
`,
|
||||||
"de-CH": `
|
"de-CH": `
|
||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
@@ -83,7 +83,7 @@ const SCOUTS = [
|
|||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
<p>Ich fröi mich uf alles, wo no vor mir liit: neui Begägnige, bsundrigi Erinnerige und no vill gmeinsami Momänt.</p>
|
<p>Ich fröi mich uf alles, wo no vor mir liit: neui Begägnige, bsundrigi Erinnerige und no vill gmeinsami Momänt.</p>
|
||||||
<p class="quote" style="margin-top:1rem;">Das bin ich – Patrick. Nöd perfekt, mängisch mit emene grimmige Blick, aber immer mit Herz. ❤️</p>
|
<p class="quote" style="margin-top:1rem;">Das bin ich – Patrick. Nöd perfekt, mängisch mit emene grimmige Blick, aber immer mit Herz. ❤️</p>
|
||||||
<p style="text-align:right;"><strong>– öie Patrick ❤️</strong></p>
|
<p style="text-align:right;"><strong>– öie Patrick ❤️</strong><br />Scout vom Dogfather</p>
|
||||||
`,
|
`,
|
||||||
en: `
|
en: `
|
||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
@@ -92,7 +92,7 @@ const SCOUTS = [
|
|||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
<p>I'm looking forward to everything still ahead of me: new encounters, special memories, and many more moments together.</p>
|
<p>I'm looking forward to everything still ahead of me: new encounters, special memories, and many more moments together.</p>
|
||||||
<p class="quote" style="margin-top:1rem;">That's me – Patrick. Not perfect, sometimes with a grumpy look, but always with heart. ❤️</p>
|
<p class="quote" style="margin-top:1rem;">That's me – Patrick. Not perfect, sometimes with a grumpy look, but always with heart. ❤️</p>
|
||||||
<p style="text-align:right;"><strong>– your Patrick ❤️</strong></p>
|
<p style="text-align:right;"><strong>– your Patrick ❤️</strong><br />Scout for Dogfather</p>
|
||||||
`,
|
`,
|
||||||
fr: `
|
fr: `
|
||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
@@ -101,7 +101,7 @@ const SCOUTS = [
|
|||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
<p>Je me réjouis de tout ce qui m'attend encore : de nouvelles rencontres, des souvenirs particuliers et bien d'autres moments partagés.</p>
|
<p>Je me réjouis de tout ce qui m'attend encore : de nouvelles rencontres, des souvenirs particuliers et bien d'autres moments partagés.</p>
|
||||||
<p class="quote" style="margin-top:1rem;">C'est moi, Patrick. Pas parfait, parfois avec un regard grognon, mais toujours avec du cœur. ❤️</p>
|
<p class="quote" style="margin-top:1rem;">C'est moi, Patrick. Pas parfait, parfois avec un regard grognon, mais toujours avec du cœur. ❤️</p>
|
||||||
<p style="text-align:right;"><strong>– votre Patrick ❤️</strong></p>
|
<p style="text-align:right;"><strong>– votre Patrick ❤️</strong><br />Scout de Dogfather</p>
|
||||||
`,
|
`,
|
||||||
pt: `
|
pt: `
|
||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
@@ -110,11 +110,21 @@ const SCOUTS = [
|
|||||||
<p>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.</p>
|
<p>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.</p>
|
||||||
<p>Estou animado com tudo o que ainda está por vir: novos encontros, memórias especiais e muitos outros momentos em conjunto.</p>
|
<p>Estou animado com tudo o que ainda está por vir: novos encontros, memórias especiais e muitos outros momentos em conjunto.</p>
|
||||||
<p class="quote" style="margin-top:1rem;">Este sou eu – Patrick. Não sou perfeito, às vezes com um olhar carrancudo, mas sempre com coração. ❤️</p>
|
<p class="quote" style="margin-top:1rem;">Este sou eu – Patrick. Não sou perfeito, às vezes com um olhar carrancudo, mas sempre com coração. ❤️</p>
|
||||||
<p style="text-align:right;"><strong>– o seu Patrick ❤️</strong></p>
|
<p style="text-align:right;"><strong>– o seu Patrick ❤️</strong><br />Scout do Dogfather</p>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
quote: "", // TODO: Zitat von Patrick einholen
|
quote: {
|
||||||
social: {},
|
de: "Wege entstehen dadurch, dass man sie geht.",
|
||||||
|
"de-CH": "Wäg entstönd defür, dass mer sie gaht.",
|
||||||
|
en: "Paths are created by walking them.",
|
||||||
|
fr: "Les chemins se créent en marchant.",
|
||||||
|
pt: "Os caminhos se fazem ao caminhar.",
|
||||||
|
},
|
||||||
|
// TikTok-Button auf dem Profil (Nutzer-Wunsch 21.08.2026) -- profil.html
|
||||||
|
// rendert daraus automatisch den echten .btn-tiktok-Button unter dem
|
||||||
|
// Namen/Rolle-Text, kein weiterer Code-Umbau nötig (siehe socialLabels
|
||||||
|
// in profil.html).
|
||||||
|
social: { tiktok: "https://www.tiktok.com/@patrick180585" },
|
||||||
status: "aktiv",
|
status: "aktiv",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -294,7 +304,8 @@ const SCOUTS = [
|
|||||||
de: "Ein Mann, ein Wort.", "de-CH": "Bi mir gilt: gseit isch gseit.",
|
de: "Ein Mann, ein Wort.", "de-CH": "Bi mir gilt: gseit isch gseit.",
|
||||||
en: "My word is my bond.", fr: "Chose promise, chose due.", pt: "A minha palavra é a minha honra.",
|
en: "My word is my bond.", fr: "Chose promise, chose due.", pt: "A minha palavra é a minha honra.",
|
||||||
},
|
},
|
||||||
social: {},
|
// TikTok-Button auf dem Profil (Nutzer-Wunsch 21.08.2026), siehe Kommentar bei Patrick oben.
|
||||||
|
social: { tiktok: "https://www.tiktok.com/@bananenstift009" },
|
||||||
status: "aktiv",
|
status: "aktiv",
|
||||||
},
|
},
|
||||||
// TODO: Dogi liefert die vollständige, aktuelle Scout-Liste (Namen +
|
// TODO: Dogi liefert die vollständige, aktuelle Scout-Liste (Namen +
|
||||||
|
|||||||
@@ -29,12 +29,15 @@ window.I18N_PAGE = {
|
|||||||
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.",
|
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.",
|
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.",
|
||||||
},
|
},
|
||||||
|
/* Bugfix/Nutzer-Wunsch 20.08.2026: exakter Preis entfernt (nur die erste
|
||||||
|
Teilphrase geändert, Rest jeder Sprachfassung unangetastet), solange die
|
||||||
|
Registrierung gesperrt ist — siehe .btn-gold-locked/abo-preis-zahl. */
|
||||||
ab_text2: {
|
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: "Mit deinem monatlichen Beitrag 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.",
|
"de-CH": "Mit dim monetliche Bytrag ü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.",
|
en: "With your monthly contribution, 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.",
|
fr: "Avec ta contribution mensuelle, 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.",
|
pt: "Com sua contribuição mensal, 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: {
|
ab_vorteil1: {
|
||||||
de: "Exklusiver Supporter-Bereich", "de-CH": "Exklusive Supporter-Bereich",
|
de: "Exklusiver Supporter-Bereich", "de-CH": "Exklusive Supporter-Bereich",
|
||||||
@@ -123,10 +126,62 @@ window.I18N_PAGE = {
|
|||||||
de: "Registrieren", "de-CH": "Registriere",
|
de: "Registrieren", "de-CH": "Registriere",
|
||||||
en: "Register", fr: "S'inscrire", pt: "Registrar",
|
en: "Register", fr: "S'inscrire", pt: "Registrar",
|
||||||
},
|
},
|
||||||
|
ab_tab_register: {
|
||||||
|
de: "Neu registrieren", "de-CH": "Nöi registriere",
|
||||||
|
en: "Sign up", fr: "S'inscrire", pt: "Registrar",
|
||||||
|
},
|
||||||
|
ab_tab_login: {
|
||||||
|
de: "Schon registriert? Einloggen", "de-CH": "Scho registriert? Yylogge",
|
||||||
|
en: "Already registered? Log in", fr: "Déjà inscrit ? Se connecter", pt: "Já registrado? Entrar",
|
||||||
|
},
|
||||||
ab_switch_login: {
|
ab_switch_login: {
|
||||||
de: "Schon Supporter? Hier einloggen", "de-CH": "Scho Supporter? Da yylogge",
|
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",
|
en: "Already a supporter? Log in here", fr: "Déjà supporter ? Connecte-toi ici", pt: "Já é apoiador? Faça login aqui",
|
||||||
},
|
},
|
||||||
|
ab_feld_code_neu: {
|
||||||
|
de: "Denk dir jetzt deinen Zugangscode aus (mindestens 6 Zeichen)",
|
||||||
|
"de-CH": "Dänk der jetz din Zuegangscode us (mindeschtens 6 Zeiche)",
|
||||||
|
en: "Now choose your own access code (at least 6 characters)",
|
||||||
|
fr: "Choisis maintenant ton code d'accès (au moins 6 caractères)",
|
||||||
|
pt: "Agora escolha seu código de acesso (mínimo 6 caracteres)",
|
||||||
|
},
|
||||||
|
ab_feld_code_platzhalter: {
|
||||||
|
de: "Frei wählbar — z. B. DogiCrew2026",
|
||||||
|
"de-CH": "Frei wählbar — z. B. DogiCrew2026",
|
||||||
|
en: "Anything you like — e.g. DogiCrew2026",
|
||||||
|
fr: "Comme tu veux — p. ex. DogiCrew2026",
|
||||||
|
pt: "À sua escolha — ex. DogiCrew2026",
|
||||||
|
},
|
||||||
|
ab_code_hinweis_neu: {
|
||||||
|
de: "Mit diesem Code loggst du dich künftig ein — merk ihn dir gut.",
|
||||||
|
"de-CH": "Mit däm Code loggsch di künftig y — merk der en guet.",
|
||||||
|
en: "You'll use this code to log in from now on — keep it safe.",
|
||||||
|
fr: "Tu utiliseras ce code pour te connecter — garde-le en lieu sûr.",
|
||||||
|
pt: "Você usará este código para entrar a partir de agora — guarde-o bem.",
|
||||||
|
},
|
||||||
|
ab_feld_code_eigen: {
|
||||||
|
de: "Dein Zugangscode", "de-CH": "Din Zuegangscode",
|
||||||
|
en: "Your access code", fr: "Ton code d'accès", pt: "Seu código de acesso",
|
||||||
|
},
|
||||||
|
ab_btn_login: {
|
||||||
|
de: "Einloggen", "de-CH": "Yylogge",
|
||||||
|
en: "Log in", fr: "Se connecter", pt: "Entrar",
|
||||||
|
},
|
||||||
|
ab_code_vergessen: {
|
||||||
|
de: "Code vergessen?", "de-CH": "Code vergässe?",
|
||||||
|
en: "Forgot your code?", fr: "Code oublié ?", pt: "Esqueceu seu código?",
|
||||||
|
},
|
||||||
|
ab_neuer_code_hinweis: {
|
||||||
|
de: "Fast fertig — leg jetzt deinen neuen Zugangscode fest.",
|
||||||
|
"de-CH": "Fascht fertig — leg jetz din nöie Zuegangscode fescht.",
|
||||||
|
en: "Almost done — now set your new access code.",
|
||||||
|
fr: "Presque fini — définis maintenant ton nouveau code d'accès.",
|
||||||
|
pt: "Quase pronto — agora defina seu novo código de acesso.",
|
||||||
|
},
|
||||||
|
ab_btn_code_speichern: {
|
||||||
|
de: "Zugangscode speichern", "de-CH": "Zuegangscode spichere",
|
||||||
|
en: "Save access code", fr: "Enregistrer le code d'accès", pt: "Salvar código de acesso",
|
||||||
|
},
|
||||||
ab_btn_login_code: {
|
ab_btn_login_code: {
|
||||||
de: "Login-Code anfordern", "de-CH": "Login-Code aafordere",
|
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",
|
en: "Request login code", fr: "Demander un code de connexion", pt: "Solicitar código de login",
|
||||||
@@ -139,6 +194,16 @@ window.I18N_PAGE = {
|
|||||||
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.",
|
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.",
|
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.",
|
||||||
},
|
},
|
||||||
|
/* Hinweis im Registrierungsformular (21.08.2026): erklärt vorab, dass jetzt
|
||||||
|
zwei weitere Schritte kommen (Code aus der E-Mail, danach eigener
|
||||||
|
Zugangscode) -- sonst wirkt der Sprung zur Code-Eingabe überraschend. */
|
||||||
|
ab_reg_hinweis_bestaetigung: {
|
||||||
|
de: "Wir schicken dir gleich einen 6-stelligen Code per E-Mail. Danach legst du deinen eigenen Zugangscode fest.",
|
||||||
|
"de-CH": "Mir schicked dir grad en 6-stellige Code per E-Mail. Denn legsch du din eigene Zuegangscode fescht.",
|
||||||
|
en: "We'll send you a 6-digit code by email in a moment. After that you'll set your own access code.",
|
||||||
|
fr: "Nous t'enverrons dans un instant un code à 6 chiffres par e-mail. Ensuite, tu définiras ton propre code d'accès.",
|
||||||
|
pt: "Enviaremos em instantes um código de 6 dígitos para o seu e-mail. Depois você define o seu próprio código de acesso.",
|
||||||
|
},
|
||||||
ab_feld_code: {
|
ab_feld_code: {
|
||||||
de: "Code", "de-CH": "Code",
|
de: "Code", "de-CH": "Code",
|
||||||
en: "Code", fr: "Code", pt: "Código",
|
en: "Code", fr: "Code", pt: "Código",
|
||||||
@@ -148,8 +213,23 @@ window.I18N_PAGE = {
|
|||||||
en: "Confirm", fr: "Confirmer", pt: "Confirmar",
|
en: "Confirm", fr: "Confirmer", pt: "Confirmar",
|
||||||
},
|
},
|
||||||
ab_eingeloggt_als: {
|
ab_eingeloggt_als: {
|
||||||
de: "Eingeloggt. Noch ein letzter Schritt:", "de-CH": "Yyglogget. No en letschte Schritt:",
|
de: "Eingeloggt.", "de-CH": "Yyglogget.",
|
||||||
en: "Logged in. One last step:", fr: "Connecté. Une dernière étape :", pt: "Conectado. Um último passo:",
|
en: "Logged in.", fr: "Connecté.", pt: "Conectado.",
|
||||||
|
},
|
||||||
|
ab_subscribe_locked_hinweis: {
|
||||||
|
de: "Dein Konto ist bereit — die Bezahlung startet, sobald das Abo offiziell live geht. Schau einfach bald wieder vorbei.",
|
||||||
|
"de-CH": "Dis Konto isch parat — d Zahlig startet, sobald s Abo offiziell live gaht. Schau eifach bald wieder verbi.",
|
||||||
|
en: "Your account is ready — payment opens as soon as the subscription officially launches. Just check back soon.",
|
||||||
|
fr: "Ton compte est prêt — le paiement s'ouvrira dès le lancement officiel de l'abonnement. Repasse bientôt.",
|
||||||
|
pt: "Sua conta está pronta — o pagamento abre assim que a assinatura for lançada oficialmente. Volte em breve.",
|
||||||
|
},
|
||||||
|
ab_weiter_startseite: {
|
||||||
|
de: "Zur Startseite →", "de-CH": "Zur Startsiite →",
|
||||||
|
en: "To the homepage →", fr: "Vers l'accueil →", pt: "Para a página inicial →",
|
||||||
|
},
|
||||||
|
ab_weiter_streamplan: {
|
||||||
|
de: "Zum Streamplan →", "de-CH": "Zum Streamplan →",
|
||||||
|
en: "To the stream schedule →", fr: "Vers le programme des lives →", pt: "Para a programação de lives →",
|
||||||
},
|
},
|
||||||
ab_oder_email: {
|
ab_oder_email: {
|
||||||
de: "oder mit E-Mail", "de-CH": "oder mit E-Mail",
|
de: "oder mit E-Mail", "de-CH": "oder mit E-Mail",
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ window.I18N_PAGE = {
|
|||||||
de: "Werde Teil vom Team", "de-CH": "Wird Teil vom Team",
|
de: "Werde Teil vom Team", "de-CH": "Wird Teil vom Team",
|
||||||
en: "Join the team", fr: "Rejoins l'équipe", pt: "Faça parte da equipe",
|
en: "Join the team", fr: "Rejoins l'équipe", pt: "Faça parte da equipe",
|
||||||
},
|
},
|
||||||
|
// Nutzer-Wunsch 21.08.2026: das letzte Wort stand auf manchen Bildschirmbreiten
|
||||||
|
// ganz allein in einer eigenen Zeile ("Weg" unter sich, siehe Screenshot) --
|
||||||
|
// sieht typografisch unruhig aus. zwischen den letzten zwei Wörtern
|
||||||
|
// verhindert, dass genau DAZWISCHEN umgebrochen wird: fällt eine Zeile um,
|
||||||
|
// dann nehmen die letzten zwei Wörter sich gegenseitig mit statt eines davon
|
||||||
|
// einsam zurückzulassen.
|
||||||
bw_h1: {
|
bw_h1: {
|
||||||
de: "Community oder Agentur — such dir deinen Weg",
|
de: "Community oder Agentur — such dir deinen Weg",
|
||||||
"de-CH": "Community oder Agentur — such dir dis Wäg",
|
"de-CH": "Community oder Agentur — such dir dis Wäg",
|
||||||
en: "Community or agency — find your path",
|
en: "Community or agency — find your path",
|
||||||
fr: "Communauté ou agence — trouve ta voie",
|
fr: "Communauté ou agence — trouve ta voie",
|
||||||
pt: "Comunidade ou agência — encontre o seu caminho",
|
pt: "Comunidade ou agência — encontre o seu caminho",
|
||||||
},
|
},
|
||||||
bw_lead: {
|
bw_lead: {
|
||||||
de: "Vier Bewerbungen, drei ganz unterschiedliche Rollen. Wähl, was zu dir passt.",
|
de: "Vier Bewerbungen, drei ganz unterschiedliche Rollen. Wähl, was zu dir passt.",
|
||||||
|
|||||||
@@ -38,14 +38,6 @@ window.I18N_PAGE = {
|
|||||||
de: "Discord beitreten", "de-CH": "Bi Discord mitmache",
|
de: "Discord beitreten", "de-CH": "Bi Discord mitmache",
|
||||||
en: "Join Discord", fr: "Rejoindre le Discord", pt: "Entrar no Discord",
|
en: "Join Discord", fr: "Rejoindre le Discord", pt: "Entrar no Discord",
|
||||||
},
|
},
|
||||||
com_bild_platzhalter: {
|
|
||||||
de: "Community-Bild", "de-CH": "Community-Bild",
|
|
||||||
en: "Community Photo", fr: "Photo de la communauté", pt: "Foto da comunidade",
|
|
||||||
},
|
|
||||||
com_foto_folgt: {
|
|
||||||
de: "Foto folgt", "de-CH": "Foto chunnt no",
|
|
||||||
en: "Photo coming soon", fr: "Photo à venir", pt: "Foto em breve",
|
|
||||||
},
|
|
||||||
com_story_h2: {
|
com_story_h2: {
|
||||||
de: "Team Dogi – mehr als eine Community 🩵", "de-CH": "Team Dogi – meh als e Community 🩵",
|
de: "Team Dogi – mehr als eine Community 🩵", "de-CH": "Team Dogi – meh als e Community 🩵",
|
||||||
en: "Team Dogi – More Than a Community 🩵", fr: "Team Dogi – bien plus qu'une communauté 🩵", pt: "Team Dogi – mais do que uma comunidade 🩵",
|
en: "Team Dogi – More Than a Community 🩵", fr: "Team Dogi – bien plus qu'une communauté 🩵", pt: "Team Dogi – mais do que uma comunidade 🩵",
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ window.I18N_PAGE = {
|
|||||||
de: "Profil ansehen", "de-CH": "Profil aluege",
|
de: "Profil ansehen", "de-CH": "Profil aluege",
|
||||||
en: "View profile", fr: "Voir le profil", pt: "Ver perfil",
|
en: "View profile", fr: "Voir le profil", pt: "Ver perfil",
|
||||||
},
|
},
|
||||||
|
/* 21.08.2026: Fallback-Kategorie für über die Verwaltung angelegte
|
||||||
|
Creator ohne eigenes Genre (die Verwaltung fragt aktuell keine
|
||||||
|
Genre-Kategorie ab) -- verhindert einen "undefined"-Filterknopf. */
|
||||||
|
crt_kategorie_sonstige: {
|
||||||
|
de: "Sonstige", "de-CH": "Anders",
|
||||||
|
en: "Other", fr: "Autre", pt: "Outro",
|
||||||
|
},
|
||||||
crt_bewerben_btn: {
|
crt_bewerben_btn: {
|
||||||
de: "Als Creator bewerben", "de-CH": "Als Creator bewärbe",
|
de: "Als Creator bewerben", "de-CH": "Als Creator bewärbe",
|
||||||
en: "Apply as a creator", fr: "Postuler comme créateur", pt: "Candidatar-se como creator",
|
en: "Apply as a creator", fr: "Postuler comme créateur", pt: "Candidatar-se como creator",
|
||||||
|
|||||||
@@ -37,6 +37,10 @@ window.I18N_PAGE = {
|
|||||||
de: "🐰 HasiDog – Eine eigene Welt voller Chaos, Herz und Fantasie", "de-CH": "🐰 HasiDog – E eigeti Wält vollr Chaos, Herz und Fantasie",
|
de: "🐰 HasiDog – Eine eigene Welt voller Chaos, Herz und Fantasie", "de-CH": "🐰 HasiDog – E eigeti Wält vollr Chaos, Herz und Fantasie",
|
||||||
en: "🐰 HasiDog – A world of its own, full of chaos, heart and imagination", fr: "🐰 HasiDog – Un monde à part, plein de chaos, de cœur et d'imagination", pt: "🐰 HasiDog – Um mundo à parte, cheio de caos, coração e imaginação",
|
en: "🐰 HasiDog – A world of its own, full of chaos, heart and imagination", fr: "🐰 HasiDog – Un monde à part, plein de chaos, de cœur et d'imagination", pt: "🐰 HasiDog – Um mundo à parte, cheio de caos, coração e imaginação",
|
||||||
},
|
},
|
||||||
|
hd_btn_tiktok: {
|
||||||
|
de: "Zur HasiDog TikTok-Seite", "de-CH": "Zur HasiDog TikTok-Site",
|
||||||
|
en: "Go to HasiDog's TikTok page", fr: "Voir la page TikTok de HasiDog", pt: "Ir para a página TikTok do HasiDog",
|
||||||
|
},
|
||||||
hd_welt_1: {
|
hd_welt_1: {
|
||||||
de: "HasiDog ist mehr als nur eine Figur. Hinter HasiDog steckt eine eigene Welt aus Humor, Musik, Geschichten, Überraschungen und liebenswertem Chaos. 🩵",
|
de: "HasiDog ist mehr als nur eine Figur. Hinter HasiDog steckt eine eigene Welt aus Humor, Musik, Geschichten, Überraschungen und liebenswertem Chaos. 🩵",
|
||||||
"de-CH": "HasiDog isch meh als nur e Figur. Hinter HasiDog steckt e eigeti Wält us Humor, Musig, Gschichte, Überraschige und liebenswerte Chaos. 🩵",
|
"de-CH": "HasiDog isch meh als nur e Figur. Hinter HasiDog steckt e eigeti Wält us Humor, Musig, Gschichte, Überraschige und liebenswerte Chaos. 🩵",
|
||||||
|
|||||||
@@ -81,6 +81,27 @@ window.I18N_PAGE = {
|
|||||||
fr: "En tant que manager chez Spicy Media : scouts, creators et une collaboration équitable, d'égal à égal.",
|
fr: "En tant que manager chez Spicy Media : scouts, creators et une collaboration équitable, d'égal à égal.",
|
||||||
pt: "Como manager na Spicy Media: scouts, creators e uma parceria justa entre iguais.",
|
pt: "Como manager na Spicy Media: scouts, creators e uma parceria justa entre iguais.",
|
||||||
},
|
},
|
||||||
|
/* ---------- "Event des Jahres" (20.08.2026, Nutzer-Wunsch) — volle Breite unter den
|
||||||
|
3 Welt-Kacheln, zeigt bewusst IMMER genau zwei Events. Texte sind gekürzte, inhaltlich
|
||||||
|
unveränderte Fassungen der ausführlichen Beschreibungen aus i18n-zeitreise.js
|
||||||
|
(zr_ev4_h3/p, zr_ev9_h3/p) — dieselben zwei Events, nur kompakter für die Kachel. ---------- */
|
||||||
|
idx_jahres_events_tag: {
|
||||||
|
de: "🏆 Event des Jahres", "de-CH": "🏆 Event vom Jahr",
|
||||||
|
en: "🏆 Event of the Year", fr: "🏆 Événement de l'année", pt: "🏆 Evento do ano",
|
||||||
|
},
|
||||||
|
idx_jahres_events_mehr: {
|
||||||
|
de: "Mehr erfahren →", "de-CH": "Mehr erfahre →",
|
||||||
|
en: "Learn more →", fr: "En savoir plus →", pt: "Saiba mais →",
|
||||||
|
},
|
||||||
|
idx_jahres_event_modal_link: {
|
||||||
|
de: "Direkt ansehen →", "de-CH": "Direkt aluege →",
|
||||||
|
en: "View directly →", fr: "Voir directement →", pt: "Ver diretamente →",
|
||||||
|
},
|
||||||
|
idx_jahres_event_modal_teilen: {
|
||||||
|
de: "📤 Teilen", "de-CH": "📤 Teile",
|
||||||
|
en: "📤 Share", fr: "📤 Partager", pt: "📤 Compartilhar",
|
||||||
|
},
|
||||||
|
|
||||||
idx_char_eyebrow: {
|
idx_char_eyebrow: {
|
||||||
de: "🩵 Die Gesichter der Welten", "de-CH": "🩵 D Gsichter vo de Wälte",
|
de: "🩵 Die Gesichter der Welten", "de-CH": "🩵 D Gsichter vo de Wälte",
|
||||||
en: "🩵 The Faces of the Worlds", fr: "🩵 Les visages des mondes", pt: "🩵 Os rostos dos mundos",
|
en: "🩵 The Faces of the Worlds", fr: "🩵 Les visages des mondes", pt: "🩵 Os rostos dos mundos",
|
||||||
@@ -187,14 +208,6 @@ window.I18N_PAGE = {
|
|||||||
de: "Modi-Team ansehen", "de-CH": "Modi-Team aluege",
|
de: "Modi-Team ansehen", "de-CH": "Modi-Team aluege",
|
||||||
en: "View Modi-Team", fr: "Voir l'équipe Modi", pt: "Ver equipe Modi",
|
en: "View Modi-Team", fr: "Voir l'équipe Modi", pt: "Ver equipe Modi",
|
||||||
},
|
},
|
||||||
idx_aktionen_h2: {
|
|
||||||
de: "Was gerade läuft", "de-CH": "Was grad lauft",
|
|
||||||
en: "What's happening right now", fr: "Ce qui se passe en ce moment", pt: "O que está rolando agora",
|
|
||||||
},
|
|
||||||
idx_aktionen_btn: {
|
|
||||||
de: "Alle Aktionen & Projekte", "de-CH": "Alli Aktione & Projekt",
|
|
||||||
en: "All actions & projects", fr: "Toutes les actions et projets", pt: "Todas as ações e projetos",
|
|
||||||
},
|
|
||||||
idx_galerie_h2: { de: "Impressionen", "de-CH": "Impressione", en: "Impressions", fr: "Aperçus", pt: "Impressões" },
|
idx_galerie_h2: { de: "Impressionen", "de-CH": "Impressione", en: "Impressions", fr: "Aperçus", pt: "Impressões" },
|
||||||
idx_galerie_btn: {
|
idx_galerie_btn: {
|
||||||
de: "Zur ganzen Galerie", "de-CH": "Zur ganze Galerie",
|
de: "Zur ganzen Galerie", "de-CH": "Zur ganze Galerie",
|
||||||
|
|||||||
@@ -177,4 +177,16 @@ window.I18N_PAGE = {
|
|||||||
fr: "Les scouts trouvent et accompagnent de nouveaux talents pour Dogi.",
|
fr: "Les scouts trouvent et accompagnent de nouveaux talents pour Dogi.",
|
||||||
pt: "Os scouts encontram e acompanham novos talentos para o Dogi.",
|
pt: "Os scouts encontram e acompanham novos talentos para o Dogi.",
|
||||||
},
|
},
|
||||||
|
/* 21.08.2026: Abschnitt für weitere, über die Verwaltung angelegte Manager
|
||||||
|
(siehe zeitreise.html, "Aufbau eines eigenen Creator-Systems" ->
|
||||||
|
"zukünftige Manager"). Bleibt komplett unsichtbar, solange keiner
|
||||||
|
eingetragen ist -- siehe renderWeitereManager() weiter unten. */
|
||||||
|
mgr_weitere_h2: {
|
||||||
|
de: "Weitere Manager", "de-CH": "Witeri Manager",
|
||||||
|
en: "More Managers", fr: "Autres managers", pt: "Outros managers",
|
||||||
|
},
|
||||||
|
mgr_weitere_profil_btn: {
|
||||||
|
de: "Profil ansehen", "de-CH": "Profil aluege",
|
||||||
|
en: "View profile", fr: "Voir le profil", pt: "Ver perfil",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ window.I18N_PAGE = {
|
|||||||
de: "Manager DogFather — Creator", "de-CH": "Manager DogFather — Creator",
|
de: "Manager DogFather — Creator", "de-CH": "Manager DogFather — Creator",
|
||||||
en: "Manager DogFather — Creator", fr: "Manager DogFather — Creator", pt: "Manager DogFather — Creator",
|
en: "Manager DogFather — Creator", fr: "Manager DogFather — Creator", pt: "Manager DogFather — Creator",
|
||||||
},
|
},
|
||||||
|
pr_eyebrow_manager: {
|
||||||
|
de: "Manager DogFather — Spicy Media", "de-CH": "Manager DogFather — Spicy Media",
|
||||||
|
en: "Manager DogFather — Spicy Media", fr: "Manager DogFather — Spicy Media", pt: "Manager DogFather — Spicy Media",
|
||||||
|
},
|
||||||
pr_back_modis: {
|
pr_back_modis: {
|
||||||
de: "← Zurück zum Modi-Team", "de-CH": "← Zrugg zum Modi-Team",
|
de: "← Zurück zum Modi-Team", "de-CH": "← Zrugg zum Modi-Team",
|
||||||
en: "← Back to the Modi Team", fr: "← Retour à l'équipe Modi", pt: "← Voltar à equipe Modi",
|
en: "← Back to the Modi Team", fr: "← Retour à l'équipe Modi", pt: "← Voltar à equipe Modi",
|
||||||
@@ -42,6 +46,10 @@ window.I18N_PAGE = {
|
|||||||
de: "← Zurück zu den Creatorn", "de-CH": "← Zrugg zu de Creator",
|
de: "← Zurück zu den Creatorn", "de-CH": "← Zrugg zu de Creator",
|
||||||
en: "← Back to the Creators", fr: "← Retour aux Creators", pt: "← Voltar aos Creators",
|
en: "← Back to the Creators", fr: "← Retour aux Creators", pt: "← Voltar aos Creators",
|
||||||
},
|
},
|
||||||
|
pr_back_manager: {
|
||||||
|
de: "← Zurück zu Manager DogFather", "de-CH": "← Zrugg zu Manager DogFather",
|
||||||
|
en: "← Back to Manager DogFather", fr: "← Retour à Manager DogFather", pt: "← Voltar a Manager DogFather",
|
||||||
|
},
|
||||||
pr_badge_prefix: {
|
pr_badge_prefix: {
|
||||||
de: "Stufe", "de-CH": "Stuefe",
|
de: "Stufe", "de-CH": "Stuefe",
|
||||||
en: "Level", fr: "Niveau", pt: "Nível",
|
en: "Level", fr: "Niveau", pt: "Nível",
|
||||||
|
|||||||
@@ -16,37 +16,47 @@ window.I18N_PAGE = {
|
|||||||
de: "Was Community und Team sagen.", "de-CH": "Was d Community und s Team säge.",
|
de: "Was Community und Team sagen.", "de-CH": "Was d Community und s Team säge.",
|
||||||
en: "What the community and the team say.", fr: "Ce que disent la communauté et l'équipe.", pt: "O que a comunidade e a equipe dizem.",
|
en: "What the community and the team say.", fr: "Ce que disent la communauté et l'équipe.", pt: "O que a comunidade e a equipe dizem.",
|
||||||
},
|
},
|
||||||
st_quote_community: {
|
/* Eigene Stimme einreichen (20.08.2026) */
|
||||||
de: `„Hier steht bald eine echte Stimme aus der Community. 🩵"`,
|
st_form_titel: {
|
||||||
"de-CH": `„Da staht bald e echti Stimm us de Community. 🩵"`,
|
de: "Deine Stimme zählt", "de-CH": "Dini Stimm zählt",
|
||||||
en: `"A real voice from the community will be here soon. 🩵"`,
|
en: "Your Voice Matters", fr: "Ta voix compte", pt: "Sua voz importa",
|
||||||
fr: `« Un vrai témoignage de la communauté arrivera bientôt ici. 🩵 »`,
|
|
||||||
pt: `„Em breve, uma voz real da comunidade estará aqui. 🩵"`,
|
|
||||||
},
|
},
|
||||||
st_label_community: {
|
st_form_text: {
|
||||||
de: "— Community-Mitglied", "de-CH": "— Community-Mitglied",
|
de: "Was bedeutet dir Team Dogi? Schreib uns deine Geschichte — mit etwas Glück liest DogFather sie persönlich und stellt sie hier auf die Seite.",
|
||||||
en: "— Community Member", fr: "— Membre de la communauté", pt: "— Membro da comunidade",
|
"de-CH": "Was bedütet dir Team Dogi? Schriib eus dini Gschicht — mit chli Glück list DogFather sie persönlich und stellt sie da uf d Site.",
|
||||||
|
en: "What does Team Dogi mean to you? Write us your story — with a bit of luck, DogFather will read it personally and feature it here on the site.",
|
||||||
|
fr: "Que représente Team Dogi pour toi ? Écris-nous ton histoire — avec un peu de chance, DogFather la lira personnellement et la publiera ici.",
|
||||||
|
pt: "O que a Team Dogi significa para você? Escreva sua história — com um pouco de sorte, o DogFather vai ler pessoalmente e publicar aqui.",
|
||||||
},
|
},
|
||||||
st_quote_team: {
|
st_form_name_label: {
|
||||||
de: `„Hier steht bald eine echte Stimme aus dem Team. 🩵"`,
|
de: "Dein Name", "de-CH": "Dis Name",
|
||||||
"de-CH": `„Da staht bald e echti Stimm vom Team. 🩵"`,
|
en: "Your Name", fr: "Ton nom", pt: "Seu nome",
|
||||||
en: `"A real voice from the team will be here soon. 🩵"`,
|
|
||||||
fr: `« Un vrai témoignage de l'équipe arrivera bientôt ici. 🩵 »`,
|
|
||||||
pt: `„Em breve, uma voz real da equipe estará aqui. 🩵"`,
|
|
||||||
},
|
},
|
||||||
st_label_team: {
|
st_form_tiktok_label: {
|
||||||
de: "— Team-Mitglied", "de-CH": "— Team-Mitglied",
|
de: "TikTok-Name (optional)", "de-CH": "TikTok-Name (optional)",
|
||||||
en: "— Team Member", fr: "— Membre de l'équipe", pt: "— Membro da equipe",
|
en: "TikTok Username (optional)", fr: "Nom TikTok (facultatif)", pt: "Nome no TikTok (opcional)",
|
||||||
},
|
},
|
||||||
st_quote_scout: {
|
st_form_nachricht_label: {
|
||||||
de: `„Hier steht bald eine echte Stimme von Scouts & Creatorn. 🩵"`,
|
de: "Deine Nachricht", "de-CH": "Dini Nachricht",
|
||||||
"de-CH": `„Da staht bald e echti Stimm vo Scouts & Creator. 🩵"`,
|
en: "Your Message", fr: "Ton message", pt: "Sua mensagem",
|
||||||
en: `"A real voice from scouts & creators will be here soon. 🩵"`,
|
|
||||||
fr: `« Un vrai témoignage de scouts et créateurs arrivera bientôt ici. 🩵 »`,
|
|
||||||
pt: `„Em breve, uma voz real de scouts e criadores estará aqui. 🩵"`,
|
|
||||||
},
|
},
|
||||||
st_label_scout: {
|
st_form_nachricht_placeholder: {
|
||||||
de: "— Scout/Creator", "de-CH": "— Scout/Creator",
|
de: "Erzähl uns, was dir Team Dogi bedeutet …", "de-CH": "Verzell eus, was dir Team Dogi bedütet …",
|
||||||
en: "— Scout/Creator", fr: "— Scout/Créateur", pt: "— Scout/Criador",
|
en: "Tell us what Team Dogi means to you …", fr: "Dis-nous ce que Team Dogi représente pour toi …", pt: "Conte-nos o que a Team Dogi significa para você …",
|
||||||
|
},
|
||||||
|
st_form_absenden: {
|
||||||
|
de: "💙 Nachricht einreichen", "de-CH": "💙 Nachricht ischicke",
|
||||||
|
en: "💙 Submit Message", fr: "💙 Envoyer le message", pt: "💙 Enviar mensagem",
|
||||||
|
},
|
||||||
|
st_form_erfolg: {
|
||||||
|
de: "Danke dir! Deine Nachricht wurde eingereicht und wird von Dogi persönlich geprüft. 🩵",
|
||||||
|
"de-CH": "Danke dir! Dini Nachricht isch ischickt worde und wird vo Dogi persönlich gprüeft. 🩵",
|
||||||
|
en: "Thank you! Your message has been submitted and will be reviewed by Dogi personally. 🩵",
|
||||||
|
fr: "Merci ! Ton message a été envoyé et sera examiné personnellement par Dogi. 🩵",
|
||||||
|
pt: "Obrigado! Sua mensagem foi enviada e será revisada pessoalmente pelo Dogi. 🩵",
|
||||||
|
},
|
||||||
|
st_echte_titel: {
|
||||||
|
de: "Das sagt die Community", "de-CH": "Das seit d Community",
|
||||||
|
en: "What the Community Says", fr: "Ce que dit la communauté", pt: "O que a comunidade diz",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,48 @@ window.I18N_PAGE = {
|
|||||||
de: "Zur Dogi TikTok-Seite", "de-CH": "Zur Dogi TikTok-Site",
|
de: "Zur Dogi TikTok-Seite", "de-CH": "Zur Dogi TikTok-Site",
|
||||||
en: "Go to Dogi's TikTok page", fr: "Voir la page TikTok de Dogi", pt: "Ir para a página TikTok do Dogi",
|
en: "Go to Dogi's TikTok page", fr: "Voir la page TikTok de Dogi", pt: "Ir para a página TikTok do Dogi",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* Live-Radar (20.08.2026) */
|
||||||
|
sp_radar_loading: {
|
||||||
|
de: "Lädt …", "de-CH": "Lädt …",
|
||||||
|
en: "Loading …", fr: "Chargement …", pt: "Carregando …",
|
||||||
|
},
|
||||||
|
sp_radar_offline: {
|
||||||
|
de: "Nächster Fix-Stream in", "de-CH": "Nächschte Fix-Stream in",
|
||||||
|
en: "Next fixed stream in", fr: "Prochain live fixe dans", pt: "Próxima live fixa em",
|
||||||
|
},
|
||||||
|
sp_radar_countdown_label: {
|
||||||
|
de: "täglich ab 20 Uhr", "de-CH": "täglich ab 20 Uhr",
|
||||||
|
en: "daily from 8 PM", fr: "chaque jour à 20h", pt: "diariamente às 20h",
|
||||||
|
},
|
||||||
|
sp_radar_live: {
|
||||||
|
de: "🔴 DogFather ist LIVE", "de-CH": "🔴 DogFather isch LIVE",
|
||||||
|
en: "🔴 DogFather is LIVE", fr: "🔴 DogFather est EN DIRECT", pt: "🔴 DogFather está AO VIVO",
|
||||||
|
},
|
||||||
|
sp_radar_live_generic: {
|
||||||
|
de: "Jetzt live auf TikTok", "de-CH": "Jetzt live uf TikTok",
|
||||||
|
en: "Live now on TikTok", fr: "En direct maintenant sur TikTok", pt: "Ao vivo agora no TikTok",
|
||||||
|
},
|
||||||
|
sp_radar_watch_btn: {
|
||||||
|
de: "🔴 Jetzt anschauen", "de-CH": "🔴 Jetzt aluege",
|
||||||
|
en: "🔴 Watch now", fr: "🔴 Regarder maintenant", pt: "🔴 Assistir agora",
|
||||||
|
},
|
||||||
|
sp_radar_hinweis: {
|
||||||
|
de: "Alle Zeiten in deiner Ortszeit.", "de-CH": "Alli Ziite in dinere Ortsziit.",
|
||||||
|
en: "All times shown in your local time.", fr: "Tous les horaires affichés dans ton fuseau local.", pt: "Todos os horários na sua hora local.",
|
||||||
|
},
|
||||||
|
sp_radar_hinweis_spezial: {
|
||||||
|
de: "Spezielles Live-Event", "de-CH": "Spezielles Live-Event",
|
||||||
|
en: "Special live event", fr: "Événement en direct spécial", pt: "Evento ao vivo especial",
|
||||||
|
},
|
||||||
|
sp_radar_mehr_erfahren: {
|
||||||
|
de: "Mehr erfahren →", "de-CH": "Mehr erfahre →",
|
||||||
|
en: "Learn more →", fr: "En savoir plus →", pt: "Saiba mais →",
|
||||||
|
},
|
||||||
|
sp_radar_teilen: {
|
||||||
|
de: "📤 Teilen", "de-CH": "📤 Teile",
|
||||||
|
en: "📤 Share", fr: "📤 Partager", pt: "📤 Compartilhar",
|
||||||
|
},
|
||||||
sp_card1_tag: {
|
sp_card1_tag: {
|
||||||
de: "Fixer Termin", "de-CH": "Fixe Termin",
|
de: "Fixer Termin", "de-CH": "Fixe Termin",
|
||||||
en: "Fixed Time", fr: "Rendez-vous fixe", pt: "Horário Fixo",
|
en: "Fixed Time", fr: "Rendez-vous fixe", pt: "Horário Fixo",
|
||||||
|
|||||||
@@ -58,6 +58,17 @@ window.I18N_PAGE = {
|
|||||||
de: "Jetzt abonnieren", "de-CH": "Jetzt abonniere",
|
de: "Jetzt abonnieren", "de-CH": "Jetzt abonniere",
|
||||||
en: "Subscribe now", fr: "S'abonner maintenant", pt: "Assine agora",
|
en: "Subscribe now", fr: "S'abonner maintenant", pt: "Assine agora",
|
||||||
},
|
},
|
||||||
|
su_kein_abo_soon: {
|
||||||
|
de: "Coming soon…", "de-CH": "Coming soon…",
|
||||||
|
en: "Coming soon…", fr: "Coming soon…", pt: "Coming soon…",
|
||||||
|
},
|
||||||
|
su_kein_abo_hinweis: {
|
||||||
|
de: "Alles hier ist schon für dich vorbereitet — sobald das Abo startet, kannst du es mit einem Klick aktivieren.",
|
||||||
|
"de-CH": "Alles da isch scho für di parat — sobald s Abo startet, chasch es mit eim Klick aktiviere.",
|
||||||
|
en: "Everything here is already set up for you — as soon as the subscription starts, you can activate it with one click.",
|
||||||
|
fr: "Tout est déjà prêt pour toi — dès le lancement de l'abonnement, tu pourras l'activer en un clic.",
|
||||||
|
pt: "Tudo aqui já está preparado para você — assim que a assinatura começar, você poderá ativá-la com um clique.",
|
||||||
|
},
|
||||||
su_fenster2_h2: {
|
su_fenster2_h2: {
|
||||||
de: "Mein aktueller Fortschritt", "de-CH": "Mis aktuelle Fortschritt",
|
de: "Mein aktueller Fortschritt", "de-CH": "Mis aktuelle Fortschritt",
|
||||||
en: "My Current Progress", fr: "Ma progression actuelle", pt: "Meu progresso atual",
|
en: "My Current Progress", fr: "Ma progression actuelle", pt: "Meu progresso atual",
|
||||||
@@ -102,4 +113,30 @@ window.I18N_PAGE = {
|
|||||||
de: "Zur DogiCrew 🩵", "de-CH": "Zur DogiCrew 🩵",
|
de: "Zur DogiCrew 🩵", "de-CH": "Zur DogiCrew 🩵",
|
||||||
en: "Go to the DogiCrew 🩵", fr: "Aller à la DogiCrew 🩵", pt: "Ir para a DogiCrew 🩵",
|
en: "Go to the DogiCrew 🩵", fr: "Aller à la DogiCrew 🩵", pt: "Ir para a DogiCrew 🩵",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/* Supporter-Abstimmungen (20.08.2026) */
|
||||||
|
su_poll_eyebrow: {
|
||||||
|
de: "🗳️ Aktuelle Abstimmung", "de-CH": "🗳️ Aktueui Abstimmig",
|
||||||
|
en: "🗳️ Current Poll", fr: "🗳️ Sondage en cours", pt: "🗳️ Enquete atual",
|
||||||
|
},
|
||||||
|
su_poll_btn_abstimmen: {
|
||||||
|
de: "Abstimmen", "de-CH": "Abstimme",
|
||||||
|
en: "Vote", fr: "Voter", pt: "Votar",
|
||||||
|
},
|
||||||
|
su_poll_bitte_waehlen: {
|
||||||
|
de: "Bitte wähle eine Antwort aus.", "de-CH": "Bitte wähl e Antwort us.",
|
||||||
|
en: "Please choose an answer.", fr: "Merci de choisir une réponse.", pt: "Por favor, escolha uma resposta.",
|
||||||
|
},
|
||||||
|
su_poll_danke: {
|
||||||
|
de: "Danke, deine Stimme wurde gezählt! 💙", "de-CH": "Danke, dini Stimm isch zellt worde! 💙",
|
||||||
|
en: "Thanks, your vote was counted! 💙", fr: "Merci, ton vote a été pris en compte ! 💙", pt: "Obrigado, seu voto foi contado! 💙",
|
||||||
|
},
|
||||||
|
su_poll_stimmen: {
|
||||||
|
de: "Stimmen", "de-CH": "Stimme",
|
||||||
|
en: "votes", fr: "votes", pt: "votos",
|
||||||
|
},
|
||||||
|
su_poll_fehler: {
|
||||||
|
de: "Abstimmen hat leider nicht geklappt. Bitte versuch es nochmal.", "de-CH": "Abstimme het leider nid klappt. Bitte versuech's nomol.",
|
||||||
|
en: "Voting didn't work. Please try again.", fr: "Le vote a échoué. Merci de réessayer.", pt: "A votação falhou. Por favor, tente novamente.",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ window.I18N_PAGE = {
|
|||||||
fr: "L'ossature de Team Dogi — de VanVan à Marina.",
|
fr: "L'ossature de Team Dogi — de VanVan à Marina.",
|
||||||
pt: "A espinha dorsal do Team Dogi — de VanVan a Marina.",
|
pt: "A espinha dorsal do Team Dogi — de VanVan a Marina.",
|
||||||
},
|
},
|
||||||
|
tm_btn_tiktok_modigang: {
|
||||||
|
de: "Zur Modi-Gang TikTok-Seite", "de-CH": "Zur Modi-Gang TikTok-Site",
|
||||||
|
en: "Go to the Modi Gang TikTok page", fr: "Voir la page TikTok de la Modi-Gang", pt: "Ir para a página TikTok da Modi-Gang",
|
||||||
|
},
|
||||||
tm_tag_hervorgehoben: {
|
tm_tag_hervorgehoben: {
|
||||||
de: "Hervorgehoben", "de-CH": "Ganz vorne dra",
|
de: "Hervorgehoben", "de-CH": "Ganz vorne dra",
|
||||||
en: "Featured", fr: "En vedette", pt: "Em destaque",
|
en: "Featured", fr: "En vedette", pt: "Em destaque",
|
||||||
|
|||||||
@@ -24,7 +24,24 @@ window.I18N_PAGE = {
|
|||||||
en: "Date coming soon", fr: "Date à venir", pt: "Data em breve",
|
en: "Date coming soon", fr: "Date à venir", pt: "Data em breve",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 1 — Datum folgt ---------- */
|
/* ---------- "Wächst noch"-Hinweis (20.08.2026) ---------- */
|
||||||
|
zr_baustelle_h3: {
|
||||||
|
de: "Diese Zeitreise wächst noch", "de-CH": "Die Ziitreis wachst no",
|
||||||
|
en: "This timeline is still growing", fr: "Ce voyage dans le temps continue de grandir", pt: "Esta linha do tempo ainda está crescendo",
|
||||||
|
},
|
||||||
|
zr_baustelle_p: {
|
||||||
|
de: "Wir ergänzen laufend weitere Kapitel, Daten und Momente aus der Geschichte von DogFather & Team Dogi — schau gern öfter mal wieder vorbei.",
|
||||||
|
"de-CH": "Mir ergänzed laufend wiiteri Kapitel, Date und Momänt us de Gschicht vo DogFather & Team Dogi — schau gärn öppis öfters wieder verbi.",
|
||||||
|
en: "We're continuously adding more chapters, dates, and moments from the story of DogFather & Team Dogi — feel free to check back again soon.",
|
||||||
|
fr: "Nous ajoutons en permanence de nouveaux chapitres, dates et moments de l'histoire de DogFather & Team Dogi — n'hésite pas à revenir régulièrement.",
|
||||||
|
pt: "Estamos adicionando continuamente mais capítulos, datas e momentos da história de DogFather & Team Dogi — volte sempre para conferir.",
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ---------- Station 1 — 24. Oktober 2023 ---------- */
|
||||||
|
zr_ev1_date: {
|
||||||
|
de: "24. Oktober 2023", "de-CH": "24. Oktober 2023",
|
||||||
|
en: "October 24, 2023", fr: "24 octobre 2023", pt: "24 de outubro de 2023",
|
||||||
|
},
|
||||||
zr_ev1_h3: {
|
zr_ev1_h3: {
|
||||||
de: "Start als Content Creator", "de-CH": "Start als Content Creator",
|
de: "Start als Content Creator", "de-CH": "Start als Content Creator",
|
||||||
en: "Start as a Content Creator", fr: "Débuts en tant que créateur de contenu", pt: "Início como criador de conteúdo",
|
en: "Start as a Content Creator", fr: "Débuts en tant que créateur de contenu", pt: "Início como criador de conteúdo",
|
||||||
@@ -37,7 +54,11 @@ window.I18N_PAGE = {
|
|||||||
pt: "DogFather começa sua jornada no TikTok. A partir dos primeiros vídeos e lives, um estilo de conteúdo próprio vai surgindo aos poucos, cheio de humor, personalidade e proximidade real com a comunidade.",
|
pt: "DogFather começa sua jornada no TikTok. A partir dos primeiros vídeos e lives, um estilo de conteúdo próprio vai surgindo aos poucos, cheio de humor, personalidade e proximidade real com a comunidade.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 2 — Datum folgt ---------- */
|
/* ---------- Station 2 — Anfang 2025 ---------- */
|
||||||
|
zr_ev2_date: {
|
||||||
|
de: "Anfang 2025", "de-CH": "Aafang 2025",
|
||||||
|
en: "Early 2025", fr: "Début 2025", pt: "Início de 2025",
|
||||||
|
},
|
||||||
zr_ev2_h3: {
|
zr_ev2_h3: {
|
||||||
de: "Team Dogi entsteht", "de-CH": "Team Dogi entstaht",
|
de: "Team Dogi entsteht", "de-CH": "Team Dogi entstaht",
|
||||||
en: "Team Dogi Is Born", fr: "Naissance de Team Dogi", pt: "Nasce a Team Dogi",
|
en: "Team Dogi Is Born", fr: "Naissance de Team Dogi", pt: "Nasce a Team Dogi",
|
||||||
@@ -84,23 +105,6 @@ window.I18N_PAGE = {
|
|||||||
pt: "DogFather encara um desafio especial e transmite ao vivo por 28 horas seguidas. Uma experiência intensa, cheia de diversão, momentos especiais e muita união na comunidade.",
|
pt: "DogFather encara um desafio especial e transmite ao vivo por 28 horas seguidas. Uma experiência intensa, cheia de diversão, momentos especiais e muita união na comunidade.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 5 — Mai 2026 ---------- */
|
|
||||||
zr_ev5_date: {
|
|
||||||
de: "Mai 2026", "de-CH": "Mai 2026",
|
|
||||||
en: "May 2026", fr: "Mai 2026", pt: "Maio de 2026",
|
|
||||||
},
|
|
||||||
zr_ev5_h3: {
|
|
||||||
de: "Ein neues Kapitel beginnt", "de-CH": "Es neus Kapitel fangt a",
|
|
||||||
en: "A New Chapter Begins", fr: "Un nouveau chapitre commence", pt: "Um novo capítulo começa",
|
|
||||||
},
|
|
||||||
zr_ev5_p: {
|
|
||||||
de: "DogFather beendet die Zusammenarbeit mit seiner bisherigen Agentur und entscheidet sich dazu, seinen weiteren Weg neu auszurichten. Der Fokus liegt dabei auf persönlicher Weiterentwicklung, neuen Zielen und mehr Eigenständigkeit.",
|
|
||||||
"de-CH": "DogFather beendet d Zämearbeit mit sinere bisherige Agentur und entscheidet sich derfür, sim wiitere Wäg neu uszrichte. De Fokus liit derbi uf persönlicher Wiiterentwicklig, neue Ziel und meh Eigeständigkeit.",
|
|
||||||
en: "DogFather ends his collaboration with his previous agency and decides to realign his path. The focus shifts to personal growth, new goals, and greater independence.",
|
|
||||||
fr: "DogFather met fin à sa collaboration avec son agence précédente et décide de réorienter sa trajectoire. L'accent est désormais mis sur son développement personnel, de nouveaux objectifs et plus d'indépendance.",
|
|
||||||
pt: "DogFather encerra a parceria com sua antiga agência e decide reorientar seu caminho. O foco passa a ser o desenvolvimento pessoal, novas metas e mais independência.",
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ---------- Station 6 — 2. bis 15. Juni 2026 ---------- */
|
/* ---------- Station 6 — 2. bis 15. Juni 2026 ---------- */
|
||||||
zr_ev6_date: {
|
zr_ev6_date: {
|
||||||
de: "2. bis 15. Juni 2026", "de-CH": "2. bis 15. Juni 2026",
|
de: "2. bis 15. Juni 2026", "de-CH": "2. bis 15. Juni 2026",
|
||||||
@@ -135,21 +139,21 @@ window.I18N_PAGE = {
|
|||||||
pt: "DogFather assume o cargo de gerente na Spicy Media. Além de atuar como streamer, ele passa a acompanhar criadores, descobrir talentos e construir sua própria equipe forte.",
|
pt: "DogFather assume o cargo de gerente na Spicy Media. Além de atuar como streamer, ele passa a acompanhar criadores, descobrir talentos e construir sua própria equipe forte.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 8 — Juli 2026 ---------- */
|
/* ---------- Station 21 — 25. Juli 2026 (Meilenstein, Nutzer-Wunsch 21.08.2026) ---------- */
|
||||||
zr_ev8_date: {
|
zr_ev21_date: {
|
||||||
de: "Juli 2026", "de-CH": "Juli 2026",
|
de: "25. Juli 2026", "de-CH": "25. Juli 2026",
|
||||||
en: "July 2026", fr: "Juillet 2026", pt: "Julho de 2026",
|
en: "July 25, 2026", fr: "25 juillet 2026", pt: "25 de julho de 2026",
|
||||||
},
|
},
|
||||||
zr_ev8_h3: {
|
zr_ev21_h3: {
|
||||||
de: "Das Manager-Team wächst", "de-CH": "Ds Manager-Team wachst",
|
de: "Erstes Fan-Treffen: Team TiliDog", "de-CH": "Erschts Fan-Träffe: Team TiliDog",
|
||||||
en: "The Manager Team Grows", fr: "L'équipe de management s'agrandit", pt: "A equipe de gerência cresce",
|
en: "First Fan Meetup: Team TiliDog", fr: "Première rencontre fans : Team TiliDog", pt: "Primeiro encontro de fãs: Team TiliDog",
|
||||||
},
|
},
|
||||||
zr_ev8_p: {
|
zr_ev21_p: {
|
||||||
de: "Mit VanVan als rechter Hand und Diene als wichtiger Unterstützung entsteht eine klare Teamstruktur. Gemeinsam werden Creator begleitet, Probleme gelöst und neue Projekte geplant.",
|
de: "In Wuppertal kommt die Community von Dogi & Tili zum allerersten Mal persönlich zusammen – ein richtig mega Tag voller guter Laune. Es ist gleichzeitig etwas ganz Besonderes: das erste Aufeinandertreffen von DogFather und Tili überhaupt.",
|
||||||
"de-CH": "Mit VanVan als rechti Hand und Diene als wichtigi Unterstützig entstaht e klari Teamstruktur. Zäme wärded Creator begleitet, Problem glöst und neui Projekt planet.",
|
"de-CH": "In Wuppertal chunnt d Community vo Dogi & Tili zum allererschte Mal persönlich zäme – en richtig mega Tag voll gueter Luune. Gliichzitig isch es öppis ganz Bsundrigs: s erschte Ufeinanderträffe vo DogFather und Tili überhaupt.",
|
||||||
en: "With VanVan as his right hand and Diene as key support, a clear team structure takes shape. Together, they guide creators, solve problems, and plan new projects.",
|
en: "In Wuppertal, the Dogi & Tili community comes together in person for the very first time – a truly awesome day full of good vibes. It's also something very special: the first time DogFather and Tili ever meet face to face.",
|
||||||
fr: "Avec VanVan comme bras droit et Diene comme soutien essentiel, une structure d'équipe claire se met en place. Ensemble, ils accompagnent les créateurs, résolvent les problèmes et planifient de nouveaux projets.",
|
fr: "À Wuppertal, la communauté de Dogi & Tili se retrouve en personne pour la toute première fois – une journée vraiment géniale et pleine de bonne humeur. C'est aussi un moment très particulier : la toute première rencontre entre DogFather et Tili.",
|
||||||
pt: "Com VanVan como braço direito e Diene como apoio fundamental, surge uma estrutura de equipe clara. Juntos, eles acompanham criadores, resolvem problemas e planejam novos projetos.",
|
pt: "Em Wuppertal, a comunidade de Dogi & Tili se reúne pessoalmente pela primeiríssima vez – um dia realmente incrível e cheio de boas vibrações. É também algo muito especial: o primeiro encontro cara a cara entre DogFather e Tili.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 9 — 27. Juli bis 2. August 2026 (Meilenstein) ---------- */
|
/* ---------- Station 9 — 27. Juli bis 2. August 2026 (Meilenstein) ---------- */
|
||||||
@@ -162,11 +166,11 @@ window.I18N_PAGE = {
|
|||||||
en: "The Creator Cup", fr: "Le Creator Cup", pt: "O Creator Cup",
|
en: "The Creator Cup", fr: "Le Creator Cup", pt: "O Creator Cup",
|
||||||
},
|
},
|
||||||
zr_ev9_p: {
|
zr_ev9_p: {
|
||||||
de: "Gemeinsam mit Cidem organisiert DogFather den Creator Cup für die Streamer von Spicy Media. Liveminuten, Teamherzen und Coins werden zu Punkten – verbunden mit besonderen Preisen und einer starken gemeinsamen Aktion.",
|
de: "Gemeinsam mit Cidgem organisiert DogFather den Creator Cup für die Streamer von Spicy Media. Liveminuten, Teamherzen und Coins werden zu Punkten – verbunden mit besonderen Preisen und einer starken gemeinsamen Aktion.",
|
||||||
"de-CH": "Zäme mit Cidem organisiert DogFather de Creator Cup für d Streamer vo Spicy Media. Liveminute, Teamherze und Coins wärded zu Punkt — verbunde mit bsundere Priise und ere starke gmeinsame Aktion.",
|
"de-CH": "Zäme mit Cidgem organisiert DogFather de Creator Cup für d Streamer vo Spicy Media. Liveminute, Teamherze und Coins wärded zu Punkt — verbunde mit bsundere Priise und ere starke gmeinsame Aktion.",
|
||||||
en: "Together with Cidem, DogFather organizes the Creator Cup for Spicy Media's streamers. Live minutes, team hearts, and coins become points — combined with special prizes and a strong joint event.",
|
en: "Together with Cidgem, DogFather organizes the Creator Cup for Spicy Media's streamers. Live minutes, team hearts, and coins become points — combined with special prizes and a strong joint event.",
|
||||||
fr: "Avec Cidem, DogFather organise le Creator Cup pour les streamers de Spicy Media. Minutes de live, cœurs d'équipe et coins deviennent des points, avec des récompenses spéciales et un fort esprit collectif.",
|
fr: "Avec Cidgem, DogFather organise le Creator Cup pour les streamers de Spicy Media. Minutes de live, cœurs d'équipe et coins deviennent des points, avec des récompenses spéciales et un fort esprit collectif.",
|
||||||
pt: "Junto com Cidem, DogFather organiza o Creator Cup para os streamers da Spicy Media. Minutos ao vivo, corações da equipe e coins viram pontos, com prêmios especiais e uma forte ação em conjunto.",
|
pt: "Junto com Cidgem, DogFather organiza o Creator Cup para os streamers da Spicy Media. Minutos ao vivo, corações da equipe e coins viram pontos, com prêmios especiais e uma forte ação em conjunto.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 10 — 28. Juli 2026 ---------- */
|
/* ---------- Station 10 — 28. Juli 2026 ---------- */
|
||||||
@@ -220,7 +224,11 @@ window.I18N_PAGE = {
|
|||||||
pt: "Começa o planejamento de um site próprio. Ele deve unir pela primeira vez os três grandes universos de DogFather: o streamer DogFather, o gerente na Spicy Media e a marca HasiDog.",
|
pt: "Começa o planejamento de um site próprio. Ele deve unir pela primeira vez os três grandes universos de DogFather: o streamer DogFather, o gerente na Spicy Media e a marca HasiDog.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 13 — Datum folgt ---------- */
|
/* ---------- Station 13 — November 2025 ---------- */
|
||||||
|
zr_ev13_date: {
|
||||||
|
de: "November 2025", "de-CH": "November 2025",
|
||||||
|
en: "November 2025", fr: "Novembre 2025", pt: "Novembro de 2025",
|
||||||
|
},
|
||||||
zr_ev13_h3: {
|
zr_ev13_h3: {
|
||||||
de: `Das Buch „HasiDog & Casper"`, "de-CH": `Ds Buech „HasiDog & Casper"`,
|
de: `Das Buch „HasiDog & Casper"`, "de-CH": `Ds Buech „HasiDog & Casper"`,
|
||||||
en: `The Book "HasiDog & Casper"`, fr: `Le livre « HasiDog & Casper »`, pt: `O livro "HasiDog & Casper"`,
|
en: `The Book "HasiDog & Casper"`, fr: `Le livre « HasiDog & Casper »`, pt: `O livro "HasiDog & Casper"`,
|
||||||
@@ -233,7 +241,11 @@ window.I18N_PAGE = {
|
|||||||
pt: "A história especial de DogFather, HasiDog e seu husky Casper se transforma em um projeto de livro próprio. Uma história sobre amizade, família, união e aventuras compartilhadas.",
|
pt: "A história especial de DogFather, HasiDog e seu husky Casper se transforma em um projeto de livro próprio. Uma história sobre amizade, família, união e aventuras compartilhadas.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 14 — Datum folgt ---------- */
|
/* ---------- Station 14 — 1. Januar 2026 ---------- */
|
||||||
|
zr_ev14_date: {
|
||||||
|
de: "1. Januar 2026", "de-CH": "1. Januar 2026",
|
||||||
|
en: "January 1, 2026", fr: "1er janvier 2026", pt: "1º de janeiro de 2026",
|
||||||
|
},
|
||||||
zr_ev14_h3: {
|
zr_ev14_h3: {
|
||||||
de: "HasiDog startet auf Spotify", "de-CH": "HasiDog startet uf Spotify",
|
de: "HasiDog startet auf Spotify", "de-CH": "HasiDog startet uf Spotify",
|
||||||
en: "HasiDog Launches on Spotify", fr: "HasiDog arrive sur Spotify", pt: "HasiDog estreia no Spotify",
|
en: "HasiDog Launches on Spotify", fr: "HasiDog arrive sur Spotify", pt: "HasiDog estreia no Spotify",
|
||||||
@@ -259,49 +271,10 @@ window.I18N_PAGE = {
|
|||||||
pt: "Começa uma colaboração própria de produtos em torno da VanVan. Estão previstos produtos especiais que refletem sua personalidade e seu papel importante dentro da Team Dogi.",
|
pt: "Começa uma colaboração própria de produtos em torno da VanVan. Estão previstos produtos especiais que refletem sua personalidade e seu papel importante dentro da Team Dogi.",
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ---------- Station 16 — Datum folgt ---------- */
|
/* ---------- Station 19 — 21. August 2026, 21 Uhr (Meilenstein) ---------- */
|
||||||
zr_ev16_h3: {
|
|
||||||
de: "Die ersten betreuten Creator", "de-CH": "Die erste betreute Creator",
|
|
||||||
en: "The First Supported Creators", fr: "Les premiers créateurs accompagnés", pt: "Os primeiros criadores acompanhados",
|
|
||||||
},
|
|
||||||
zr_ev16_p: {
|
|
||||||
de: "Die ersten Creator schließen sich dem Team von Manager DogFather an. Sie erhalten persönliche Unterstützung, Hilfe bei Problemen, Profil-Checks, Schulungen und eine langfristige Begleitung auf TikTok.",
|
|
||||||
"de-CH": "Die erste Creator schliessed sich em Team vom Manager DogFather a. Sie überchömed persönlichi Unterstützig, Hilf bi Problem, Profil-Checks, Schulige und e langfristigi Begleitig uf TikTok.",
|
|
||||||
en: "The first creators join Manager DogFather's team. They receive personal support, help with problems, profile checks, training, and long-term guidance on TikTok.",
|
|
||||||
fr: "Les premiers créateurs rejoignent l'équipe du manager DogFather. Ils bénéficient d'un accompagnement personnalisé, d'aide en cas de problème, de vérifications de profil, de formations et d'un suivi durable sur TikTok.",
|
|
||||||
pt: "Os primeiros criadores se juntam à equipe do gerente DogFather. Eles recebem apoio pessoal, ajuda com problemas, análises de perfil, treinamentos e acompanhamento de longo prazo no TikTok.",
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ---------- Station 17 — Datum folgt ---------- */
|
|
||||||
zr_ev17_h3: {
|
|
||||||
de: "Weitere Scouts und Manager kommen hinzu", "de-CH": "Wiiteri Scouts und Manager chömed dezue",
|
|
||||||
en: "More Scouts and Managers Join", fr: "Plus de scouts et de managers rejoignent l'équipe", pt: "Mais scouts e gerentes se juntam",
|
|
||||||
},
|
|
||||||
zr_ev17_p: {
|
|
||||||
de: "Das Team soll langfristig weiter wachsen. Engagierte Scouts erhalten die Möglichkeit, sich über verschiedene Entwicklungsstufen bis hin zum eigenen Manager weiterzuentwickeln.",
|
|
||||||
"de-CH": "Ds Team söll langfristig wiiter wachse. Engagierti Scouts überchömed d Möglichkeit, sich über verschideni Entwickligsstufe bis zum eigete Manager wiiterzentwickle.",
|
|
||||||
en: "The team is set to keep growing long-term. Dedicated scouts get the chance to develop through various stages all the way up to becoming a manager themselves.",
|
|
||||||
fr: "L'équipe est destinée à continuer de grandir sur le long terme. Les scouts engagés ont la possibilité d'évoluer à travers différents niveaux, jusqu'à devenir eux-mêmes managers.",
|
|
||||||
pt: "A equipe deve continuar crescendo a longo prazo. Scouts dedicados ganham a chance de evoluir por diferentes níveis até se tornarem gerentes.",
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ---------- Station 18 — Datum folgt ---------- */
|
|
||||||
zr_ev18_h3: {
|
|
||||||
de: "Das erste Community-Treffen", "de-CH": "Ds erste Community-Träffe",
|
|
||||||
en: "The First Community Meetup", fr: "La première rencontre communautaire", pt: "O primeiro encontro da comunidade",
|
|
||||||
},
|
|
||||||
zr_ev18_p: {
|
|
||||||
de: "Team Dogi begegnet sich nicht mehr nur im Livestream oder auf Discord. Beim gemeinsamen Treffen entstehen echte Erinnerungen, die zeigen, dass diese Community weit mehr als nur Zahlen und Likes ist.",
|
|
||||||
"de-CH": "Team Dogi begägnet sich nid nur im Livestream oder uf Discord. Bim gmeinsame Träffe entstönd echti Erinnerige, wo zeiged, dass die Community wit meh isch als nume Zahle und Likes.",
|
|
||||||
en: "Team Dogi no longer just meets in livestreams or on Discord. At the shared meetup, real memories are made — proof that this community is far more than just numbers and likes.",
|
|
||||||
fr: "Team Dogi ne se retrouve plus seulement en live ou sur Discord. Lors de la rencontre commune naissent de vrais souvenirs, preuve que cette communauté est bien plus que des chiffres et des likes.",
|
|
||||||
pt: "A Team Dogi não se encontra mais só em lives ou no Discord. No encontro presencial nascem memórias reais, mostrando que essa comunidade é muito mais do que números e curtidas.",
|
|
||||||
},
|
|
||||||
|
|
||||||
/* ---------- Station 19 — 2026 – Datum folgt (Meilenstein) ---------- */
|
|
||||||
zr_ev19_date: {
|
zr_ev19_date: {
|
||||||
de: "2026 – Datum folgt", "de-CH": "2026 – Datum chunnt no",
|
de: "21. August 2026, 21 Uhr", "de-CH": "21. Auguscht 2026, 21 Uhr",
|
||||||
en: "2026 – date coming soon", fr: "2026 – date à venir", pt: "2026 – data em breve",
|
en: "August 21, 2026, 9 PM", fr: "21 août 2026, 21 h", pt: "21 de agosto de 2026, 21h",
|
||||||
},
|
},
|
||||||
zr_ev19_h3: {
|
zr_ev19_h3: {
|
||||||
de: "DOGFATHER UNIVERSE geht online", "de-CH": "DOGFATHER UNIVERSE gaht online",
|
de: "DOGFATHER UNIVERSE geht online", "de-CH": "DOGFATHER UNIVERSE gaht online",
|
||||||
|
|||||||
@@ -76,15 +76,22 @@
|
|||||||
|
|
||||||
const I18N_CORE = {
|
const I18N_CORE = {
|
||||||
nav_start: { de: "Start", "de-CH": "Start", en: "Home", fr: "Accueil", pt: "Início" },
|
nav_start: { de: "Start", "de-CH": "Start", en: "Home", fr: "Accueil", pt: "Início" },
|
||||||
nav_dogfather: { de: "DogFather", "de-CH": "DogFather", en: "DogFather", fr: "DogFather", pt: "DogFather" },
|
/* Umbenennungen 20.08.2026 (Nutzer-Wunsch): Kategorie "DogFather" -> "Filipe",
|
||||||
|
Kategorie "HasiDog" -> "Dogi&Hasi". Die Schlüsselnamen (nav_dogfather...)
|
||||||
|
bleiben bewusst unverändert — sie sind reine interne Bezeichner und werden
|
||||||
|
an mehreren Stellen (Nav, Fußzeile, Farbzuordnung) referenziert; ein
|
||||||
|
Umbenennen würde nur Fehlerquellen schaffen, ohne sichtbaren Nutzen. */
|
||||||
|
nav_dogfather: { de: "Filipe", "de-CH": "Filipe", en: "Filipe", fr: "Filipe", pt: "Filipe" },
|
||||||
nav_dogfather_person: { de: "Die Person", "de-CH": "D Person", en: "The Person", fr: "La personne", pt: "A pessoa" },
|
nav_dogfather_person: { de: "Die Person", "de-CH": "D Person", en: "The Person", fr: "La personne", pt: "A pessoa" },
|
||||||
nav_dogfather_streamer: { de: "Streamer", "de-CH": "Streamer", en: "Streamer", fr: "Streamer", pt: "Streamer" },
|
/* Sitzt seit 20.08.2026 unter "Dogi&Hasi" (nicht mehr unter "Filipe") und heißt
|
||||||
|
dort "DogFather" statt "Streamer" — zeigt weiterhin auf streamer.html. */
|
||||||
|
nav_dogfather_streamer: { de: "DogFather", "de-CH": "DogFather", en: "DogFather", fr: "DogFather", pt: "DogFather" },
|
||||||
nav_dogfather_casper: { de: "Casper", "de-CH": "Casper", en: "Casper", fr: "Casper", pt: "Casper" },
|
nav_dogfather_casper: { de: "Casper", "de-CH": "Casper", en: "Casper", fr: "Casper", pt: "Casper" },
|
||||||
nav_dogfather_vanvan: { de: "VanVan", "de-CH": "VanVan", en: "VanVan", fr: "VanVan", pt: "VanVan" },
|
nav_dogfather_vanvan: { de: "VanVan", "de-CH": "VanVan", en: "VanVan", fr: "VanVan", pt: "VanVan" },
|
||||||
nav_dogfather_zeitreise: { de: "Zeitreise", "de-CH": "Zytreis", en: "Timeline", fr: "Chronologie", pt: "Linha do tempo" },
|
nav_dogfather_zeitreise: { de: "Zeitreise", "de-CH": "Zytreis", en: "Timeline", fr: "Chronologie", pt: "Linha do tempo" },
|
||||||
nav_dogfather_ttmuddi: { de: "TT-Muddi", "de-CH": "TT-Muddi", en: "TT-Muddi", fr: "TT-Muddi", pt: "TT-Muddi" },
|
nav_dogfather_ttmuddi: { de: "TT-Muddi", "de-CH": "TT-Muddi", en: "TT-Muddi", fr: "TT-Muddi", pt: "TT-Muddi" },
|
||||||
nav_hasidog: { de: "HasiDog", "de-CH": "HasiDog", en: "HasiDog", fr: "HasiDog", pt: "HasiDog" },
|
nav_hasidog: { de: "Dogi&Hasi", "de-CH": "Dogi&Hasi", en: "Dogi&Hasi", fr: "Dogi&Hasi", pt: "Dogi&Hasi" },
|
||||||
nav_hasidog_welt: { de: "HasiDog-Welt", "de-CH": "HasiDog-Wält", en: "HasiDog World", fr: "Monde HasiDog", pt: "Mundo HasiDog" },
|
nav_hasidog_welt: { de: "HasiDog", "de-CH": "HasiDog", en: "HasiDog", fr: "HasiDog", pt: "HasiDog" },
|
||||||
nav_hasidog_modi: { de: "Modi-Team", "de-CH": "Modi-Team", en: "Modi-Team", fr: "Modi-Team", pt: "Modi-Team" },
|
nav_hasidog_modi: { de: "Modi-Team", "de-CH": "Modi-Team", en: "Modi-Team", fr: "Modi-Team", pt: "Modi-Team" },
|
||||||
nav_hasidog_community: { de: "Community", "de-CH": "Community", en: "Community", fr: "Communauté", pt: "Comunidade" },
|
nav_hasidog_community: { de: "Community", "de-CH": "Community", en: "Community", fr: "Communauté", pt: "Comunidade" },
|
||||||
nav_hasidog_stimmen: { de: "Stimmen", "de-CH": "Stimme", en: "Voices", fr: "Témoignages", pt: "Depoimentos" },
|
nav_hasidog_stimmen: { de: "Stimmen", "de-CH": "Stimme", en: "Voices", fr: "Témoignages", pt: "Depoimentos" },
|
||||||
@@ -104,6 +111,11 @@
|
|||||||
nav_cta_abo_anonym: { de: "DogiCrew 👑", "de-CH": "DogiCrew 👑", en: "DogiCrew 👑", fr: "DogiCrew 👑", pt: "DogiCrew 👑" },
|
nav_cta_abo_anonym: { de: "DogiCrew 👑", "de-CH": "DogiCrew 👑", en: "DogiCrew 👑", fr: "DogiCrew 👑", pt: "DogiCrew 👑" },
|
||||||
nav_cta_abo_aktivieren: { de: "Abo aktivieren 👑", "de-CH": "Abo aktiviere 👑", en: "Activate Subscription 👑", fr: "Activer l'abonnement 👑", pt: "Ativar assinatura 👑" },
|
nav_cta_abo_aktivieren: { de: "Abo aktivieren 👑", "de-CH": "Abo aktiviere 👑", en: "Activate Subscription 👑", fr: "Activer l'abonnement 👑", pt: "Ativar assinatura 👑" },
|
||||||
nav_cta_abo_bereich: { de: "Supporter-Bereich 👑", "de-CH": "Supporter-Bereich 👑", en: "Supporter Area 👑", fr: "Espace Supporter 👑", pt: "Área do Apoiador 👑" },
|
nav_cta_abo_bereich: { de: "Supporter-Bereich 👑", "de-CH": "Supporter-Bereich 👑", en: "Supporter Area 👑", fr: "Espace Supporter 👑", pt: "Área do Apoiador 👑" },
|
||||||
|
/* Kleines Schimmer-Badge auf dem Abo-Knopf, solange die Registrierung
|
||||||
|
noch gesperrt ist (20.08.2026, Nutzer-Wunsch) — bewusst ein kurzes
|
||||||
|
Wort statt "Coming soon…", weil in der schmalen Nav-Pille (siehe
|
||||||
|
Mobil-Menü-Fix vom selben Tag) kaum Platz ist. */
|
||||||
|
nav_cta_abo_soon: { de: "Bald", "de-CH": "Bald", en: "Soon", fr: "Bientôt", pt: "Em breve" },
|
||||||
nav_toggle_aria: { de: "Menü öffnen", "de-CH": "Menü uftue", en: "Open menu", fr: "Ouvrir le menu", pt: "Abrir menu" },
|
nav_toggle_aria: { de: "Menü öffnen", "de-CH": "Menü uftue", en: "Open menu", fr: "Ouvrir le menu", pt: "Abrir menu" },
|
||||||
sprache_aria: { de: "Sprache wählen", "de-CH": "Sprach uswähle", en: "Choose language", fr: "Choisir la langue", pt: "Escolher idioma" },
|
sprache_aria: { de: "Sprache wählen", "de-CH": "Sprach uswähle", en: "Choose language", fr: "Choisir la langue", pt: "Escolher idioma" },
|
||||||
brand_tagline: {
|
brand_tagline: {
|
||||||
@@ -120,7 +132,9 @@
|
|||||||
fr: "Streamer. Communauté. HasiDog. Manager. Plus forts ensemble.",
|
fr: "Streamer. Communauté. HasiDog. Manager. Plus forts ensemble.",
|
||||||
pt: "Streamer. Comunidade. HasiDog. Manager. Mais fortes juntos.",
|
pt: "Streamer. Comunidade. HasiDog. Manager. Mais fortes juntos.",
|
||||||
},
|
},
|
||||||
footer_h4_hasidog_manager: { de: "HasiDog & Manager", "de-CH": "HasiDog & Manager", en: "HasiDog & Manager", fr: "HasiDog et Manager", pt: "HasiDog e Manager" },
|
/* Fußzeilen-Überschrift zieht mit der Kategorie-Umbenennung mit (20.08.2026):
|
||||||
|
"HasiDog & Manager" -> "Dogi&Hasi & Manager". */
|
||||||
|
footer_h4_hasidog_manager: { de: "Dogi&Hasi & Manager", "de-CH": "Dogi&Hasi & Manager", en: "Dogi&Hasi & Manager", fr: "Dogi&Hasi et Manager", pt: "Dogi&Hasi e Manager" },
|
||||||
footer_h4_mehr: { de: "Mehr", "de-CH": "Meh", en: "More", fr: "Plus", pt: "Mais" },
|
footer_h4_mehr: { de: "Mehr", "de-CH": "Meh", en: "More", fr: "Plus", pt: "Mais" },
|
||||||
footer_scouts: { de: "Scouts", "de-CH": "Scouts", en: "Scouts", fr: "Scouts", pt: "Scouts" },
|
footer_scouts: { de: "Scouts", "de-CH": "Scouts", en: "Scouts", fr: "Scouts", pt: "Scouts" },
|
||||||
footer_alle_links: { de: "Alle Links", "de-CH": "Alli Links", en: "All links", fr: "Tous les liens", pt: "Todos os links" },
|
footer_alle_links: { de: "Alle Links", "de-CH": "Alli Links", en: "All links", fr: "Tous les liens", pt: "Todos os links" },
|
||||||
@@ -133,6 +147,7 @@
|
|||||||
pt: "© {jahr} DogFather Universe. Team Dogi é família 🩵",
|
pt: "© {jahr} DogFather Universe. Team Dogi é família 🩵",
|
||||||
},
|
},
|
||||||
footer_impressum: { de: "Impressum & Datenschutz", "de-CH": "Impressum & Datenschutz", en: "Legal notice & privacy", fr: "Mentions légales et confidentialité", pt: "Aviso legal e privacidade" },
|
footer_impressum: { de: "Impressum & Datenschutz", "de-CH": "Impressum & Datenschutz", en: "Legal notice & privacy", fr: "Mentions légales et confidentialité", pt: "Aviso legal e privacidade" },
|
||||||
|
footer_app_installieren: { de: "Als App installieren", "de-CH": "Als App installiere", en: "Install as app", fr: "Installer comme application", pt: "Instalar como aplicativo" },
|
||||||
music_title: { de: "Musik von Team Dogi 🎵", "de-CH": "Musig vo Team Dogi 🎵", en: "Music from Team Dogi 🎵", fr: "Musique de Team Dogi 🎵", pt: "Música do Team Dogi 🎵" },
|
music_title: { de: "Musik von Team Dogi 🎵", "de-CH": "Musig vo Team Dogi 🎵", en: "Music from Team Dogi 🎵", fr: "Musique de Team Dogi 🎵", pt: "Música do Team Dogi 🎵" },
|
||||||
music_close_aria: { de: "Musik-Feld schließen", "de-CH": "Musig-Feld zuemache", en: "Close music panel", fr: "Fermer le panneau musique", pt: "Fechar painel de música" },
|
music_close_aria: { de: "Musik-Feld schließen", "de-CH": "Musig-Feld zuemache", en: "Close music panel", fr: "Fermer le panneau musique", pt: "Fechar painel de música" },
|
||||||
music_intro: {
|
music_intro: {
|
||||||
@@ -184,6 +199,22 @@
|
|||||||
if (text) el.setAttribute(attr, text);
|
if (text) el.setAttribute(attr, text);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 21.08.2026 (Nutzer-Wunsch: "ich will ich titel und namen von
|
||||||
|
// kategorien und seiten ändern können in der verwaltung"): admin-
|
||||||
|
// editierbare Text-Overrides (siehe window.dogiSiteTexteLaden() weiter
|
||||||
|
// unten). Läuft bewusst NACH den normalen data-i18n-Standardtexten
|
||||||
|
// oben, damit ein gesetzter Override sie überschreiben kann. Fehlt ein
|
||||||
|
// Override (Normalfall, noch nichts in der Verwaltung eingetragen),
|
||||||
|
// bleibt einfach der eingebaute Standardtext stehen -- diese Funktion
|
||||||
|
// hier macht dann schlicht nichts.
|
||||||
|
const siteTexte = window.__siteTexteCache || {};
|
||||||
|
document.querySelectorAll("[data-site-text-key]").forEach((el) => {
|
||||||
|
const eintrag = siteTexte[el.getAttribute("data-site-text-key")];
|
||||||
|
if (!eintrag) return;
|
||||||
|
const text = eintrag[sprache] || eintrag.de;
|
||||||
|
if (text) el.innerHTML = text;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Navigationsstruktur (an einer Stelle pflegen) ---------- */
|
/* ---------- Navigationsstruktur (an einer Stelle pflegen) ---------- */
|
||||||
@@ -193,7 +224,6 @@
|
|||||||
key: "nav_dogfather",
|
key: "nav_dogfather",
|
||||||
children: [
|
children: [
|
||||||
{ key: "nav_dogfather_person", href: "dogfather.html" },
|
{ key: "nav_dogfather_person", href: "dogfather.html" },
|
||||||
{ key: "nav_dogfather_streamer", href: "streamer.html" },
|
|
||||||
{ key: "nav_dogfather_casper", href: "casper.html" },
|
{ key: "nav_dogfather_casper", href: "casper.html" },
|
||||||
{ key: "nav_dogfather_vanvan", href: "vanvan.html" },
|
{ key: "nav_dogfather_vanvan", href: "vanvan.html" },
|
||||||
{ key: "nav_dogfather_ttmuddi", href: "tt-muddi.html" },
|
{ key: "nav_dogfather_ttmuddi", href: "tt-muddi.html" },
|
||||||
@@ -203,6 +233,10 @@
|
|||||||
{
|
{
|
||||||
key: "nav_hasidog",
|
key: "nav_hasidog",
|
||||||
children: [
|
children: [
|
||||||
|
/* "DogFather" (streamer.html) hierher verschoben — Nutzer-Wunsch
|
||||||
|
20.08.2026: gehört unter "Dogi&Hasi" und dort GANZ NACH OBEN,
|
||||||
|
direkt über "HasiDog". */
|
||||||
|
{ key: "nav_dogfather_streamer", href: "streamer.html" },
|
||||||
{ key: "nav_hasidog_welt", href: "hasidog.html" },
|
{ key: "nav_hasidog_welt", href: "hasidog.html" },
|
||||||
{ key: "nav_hasidog_modi", href: "team-modis.html" },
|
{ key: "nav_hasidog_modi", href: "team-modis.html" },
|
||||||
{ key: "nav_hasidog_community", href: "community.html" },
|
{ key: "nav_hasidog_community", href: "community.html" },
|
||||||
@@ -454,6 +488,17 @@
|
|||||||
return `${escapeHtml(kuerzen(sauber, 18))} 👑`;
|
return `${escapeHtml(kuerzen(sauber, 18))} 👑`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Kleines Schimmer-Badge "Bald", solange die eigentliche Registrierung auf
|
||||||
|
abonnieren.html gesperrt ist (20.08.2026, siehe .btn-gold-locked dort).
|
||||||
|
Nur auf den drei Zielen, die dort ankommen — NICHT auf supporter.html
|
||||||
|
(echter, bereits funktionierender Bereich für Dogi/VanVan als Test-
|
||||||
|
Supporter), sonst würde es fälschlich so wirken, als sei der ganze
|
||||||
|
Supporter-Bereich gesperrt. Reine Wiederverwendung von .btn-silver-text/
|
||||||
|
-spark (siehe main.css) in kleinerem Maßstab, kein neuer Mechanismus. */
|
||||||
|
function soonBadge() {
|
||||||
|
return `<span class="nav-cta-soon"><span class="btn-silver-spark" aria-hidden="true">✨</span><span class="btn-silver-text">${t("nav_cta_abo_soon")}</span></span>`;
|
||||||
|
}
|
||||||
|
|
||||||
function renderAboButton() {
|
function renderAboButton() {
|
||||||
const vorschau = getVorschauModus();
|
const vorschau = getVorschauModus();
|
||||||
if (vorschau === "abonnent") {
|
if (vorschau === "abonnent") {
|
||||||
@@ -469,20 +514,28 @@
|
|||||||
return `<a class="nav-cta nav-cta-abo" href="supporter.html"><span>${bereichLabel(echterName || vorschauAnzeigename())}</span></a>`;
|
return `<a class="nav-cta nav-cta-abo" href="supporter.html"><span>${bereichLabel(echterName || vorschauAnzeigename())}</span></a>`;
|
||||||
}
|
}
|
||||||
if (vorschau === "nicht-abonnent") {
|
if (vorschau === "nicht-abonnent") {
|
||||||
return `<a class="nav-cta nav-cta-abo" href="abonnieren.html"><span>${t("nav_cta_abo_anonym")}</span></a>`;
|
return `<a class="nav-cta nav-cta-abo" href="abonnieren.html"><span>${t("nav_cta_abo_anonym")}</span>${soonBadge()}</a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = localStorage.getItem("supporterToken");
|
const token = localStorage.getItem("supporterToken");
|
||||||
const status = localStorage.getItem("supporterSubStatus");
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
return `<a class="nav-cta nav-cta-abo" href="abonnieren.html"><span>${t("nav_cta_abo_anonym")}</span></a>`;
|
return `<a class="nav-cta nav-cta-abo" href="abonnieren.html"><span>${t("nav_cta_abo_anonym")}</span>${soonBadge()}</a>`;
|
||||||
}
|
}
|
||||||
const hatBereich = ["active", "pending", "suspended", "cancelled"].includes(status);
|
/* Nutzer-Wunsch 21.08.2026: "meine seite ändert sich nicht und es gibt
|
||||||
if (hatBereich) {
|
keinen button für registrierte sich normal anzumelden und abzumelden.
|
||||||
const name = localStorage.getItem("supporterDisplayName");
|
wo sie die speziellen sachen sehen als supporter und dass sie auch
|
||||||
return `<a class="nav-cta nav-cta-abo" href="supporter.html"><span>${bereichLabel(name)}</span></a>`;
|
schon den abo sehen aber gesperrt ist." Vorher landeten Leute mit
|
||||||
}
|
Konto, aber ohne bezahltes Abo (status "none"), im Nav-Knopf wieder
|
||||||
return `<a class="nav-cta nav-cta-abo" href="abonnieren.html"><span>${t("nav_cta_abo_aktivieren")}</span></a>`;
|
nur bei abonnieren.html -- man merkte dem Knopf also nie an, dass man
|
||||||
|
überhaupt eingeloggt ist. supporter.html zeigt für genau diesen Fall
|
||||||
|
schon lange den Bereich "Mein Abo" -> #supp-keine-abo ("Du hast noch
|
||||||
|
kein aktives Abo" + Knopf) UND den Ausloggen-Knopf (siehe dort,
|
||||||
|
#btn-logout) -- das musste also nur noch verlinkt werden. Ab jetzt:
|
||||||
|
JEDER mit gültigem Token (egal ob "none", "active", "pending", ...)
|
||||||
|
geht auf supporter.html und sieht dort automatisch den passenden
|
||||||
|
Zustand samt Logout. */
|
||||||
|
const name = localStorage.getItem("supporterDisplayName");
|
||||||
|
return `<a class="nav-cta nav-cta-abo" href="supporter.html"><span>${bereichLabel(name)}</span></a>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderHeader() {
|
function renderHeader() {
|
||||||
@@ -524,6 +577,24 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Bugfix 20.08.2026 (Nutzer-Report per Screenshot): ein per Klick
|
||||||
|
geöffnetes Kategorie-Menü blieb offen stehen, bis irgendwo anders
|
||||||
|
hingeklickt wurde — verlässt man es einfach nur mit der Maus (ohne
|
||||||
|
zu klicken), sollte es sich von selbst schließen, genau wie das
|
||||||
|
reine Hover-Aufklappen (siehe main.css @media (hover:hover)) es
|
||||||
|
nahelegt. Nur auf echten Maus-Geräten (nicht Touch/Tablet, dort
|
||||||
|
gibt es kein "Verlassen mit der Maus", nur echte Klicks). */
|
||||||
|
const hatEchteMaus = window.matchMedia("(hover: hover) and (pointer: fine)").matches;
|
||||||
|
if (hatEchteMaus) {
|
||||||
|
el.querySelectorAll(".nav-group").forEach((group) => {
|
||||||
|
group.addEventListener("mouseleave", () => {
|
||||||
|
if (!group.classList.contains("open")) return;
|
||||||
|
group.classList.remove("open");
|
||||||
|
group.querySelector("button")?.setAttribute("aria-expanded", "false");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
el.querySelectorAll(".sprach-schalter [data-sprache]").forEach((link) => {
|
el.querySelectorAll(".sprach-schalter [data-sprache]").forEach((link) => {
|
||||||
link.addEventListener("click", (e) => {
|
link.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -554,12 +625,12 @@
|
|||||||
<p class="small" style="margin-top:.8rem;max-width:32ch;">
|
<p class="small" style="margin-top:.8rem;max-width:32ch;">
|
||||||
${t("footer_tagline")}
|
${t("footer_tagline")}
|
||||||
</p>
|
</p>
|
||||||
|
<button type="button" id="pwa-install-btn" class="btn btn-outline" style="display:none;margin-top:1rem;">📲 ${t("footer_app_installieren")}</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h4>DogFather</h4>
|
<h4>${t("nav_dogfather")}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="dogfather.html">${t("nav_dogfather_person")}</a></li>
|
<li><a href="dogfather.html">${t("nav_dogfather_person")}</a></li>
|
||||||
<li><a href="streamer.html">${t("nav_dogfather_streamer")}</a></li>
|
|
||||||
<li><a href="casper.html">${t("nav_dogfather_casper")}</a></li>
|
<li><a href="casper.html">${t("nav_dogfather_casper")}</a></li>
|
||||||
<li><a href="vanvan.html">${t("nav_dogfather_vanvan")}</a></li>
|
<li><a href="vanvan.html">${t("nav_dogfather_vanvan")}</a></li>
|
||||||
<li><a href="tt-muddi.html">${t("nav_dogfather_ttmuddi")}</a></li>
|
<li><a href="tt-muddi.html">${t("nav_dogfather_ttmuddi")}</a></li>
|
||||||
@@ -569,6 +640,10 @@
|
|||||||
<div>
|
<div>
|
||||||
<h4>${t("footer_h4_hasidog_manager")}</h4>
|
<h4>${t("footer_h4_hasidog_manager")}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
|
<!-- "DogFather"/streamer.html steht seit 20.08.2026 auch in der
|
||||||
|
Fußzeile in dieser Spalte (und ganz oben), damit sie dieselbe
|
||||||
|
Struktur zeigt wie die Navigation darüber. -->
|
||||||
|
<li><a href="streamer.html">${t("nav_dogfather_streamer")}</a></li>
|
||||||
<li><a href="hasidog.html">${t("nav_hasidog_welt")}</a></li>
|
<li><a href="hasidog.html">${t("nav_hasidog_welt")}</a></li>
|
||||||
<li><a href="team-modis.html">${t("nav_hasidog_modi")}</a></li>
|
<li><a href="team-modis.html">${t("nav_hasidog_modi")}</a></li>
|
||||||
<li><a href="community.html">${t("nav_hasidog_community")}</a></li>
|
<li><a href="community.html">${t("nav_hasidog_community")}</a></li>
|
||||||
@@ -874,7 +949,22 @@
|
|||||||
// erneut laufen, sonst bleibt window.I18N_PAGE nach einem
|
// erneut laufen, sonst bleibt window.I18N_PAGE nach einem
|
||||||
// Zwischenstopp auf einer anderen Seite (die es zurückgesetzt hat)
|
// Zwischenstopp auf einer anderen Seite (die es zurückgesetzt hat)
|
||||||
// fälschlich leer, wenn man z.B. zur Startseite zurückkehrt.
|
// fälschlich leer, wenn man z.B. zur Startseite zurückkehrt.
|
||||||
const istSprachDatei = /\/i18n-[^/]+\.js$/.test(src);
|
//
|
||||||
|
// BUGFIX 21.08.2026 (Nutzer-Report: rohe Übersetzungs-Schlüssel wie
|
||||||
|
// "ts_profil_ansehen" sichtbar, aber NUR nachdem man auf einen Link
|
||||||
|
// geklickt und dann "Zurück" gedrückt hat): das Muster endete bisher
|
||||||
|
// auf ".js$" (striktes String-Ende). Seit die Cache-Busting-Version
|
||||||
|
// ("?v=20260821...") an JEDE Skript-URL angehängt wird, endet der
|
||||||
|
// echte src-Wert aber nie mehr auf ".js", sondern auf ".js?v=...".
|
||||||
|
// Der Test schlug dadurch für JEDE i18n-Datei fehl -> istSprachDatei
|
||||||
|
// war immer false -> die i18n-Datei wurde wie eine normale, schon
|
||||||
|
// geladene Datei übersprungen und NIE erneut ausgeführt, sobald man
|
||||||
|
// per Klick auf eine andere Seite und wieder zurück navigiert ist.
|
||||||
|
// window.I18N_PAGE blieb dadurch dauerhaft auf null (von der
|
||||||
|
// Zwischenseite zurückgesetzt) stehen -- jede Übersetzung fiel auf
|
||||||
|
// den rohen Schlüsselnamen zurück. Fix: ".js" darf jetzt auch von
|
||||||
|
// einem "?..." gefolgt werden, nicht nur vom exakten String-Ende.
|
||||||
|
const istSprachDatei = /\/i18n-[^/]+\.js(\?.*)?$/.test(src);
|
||||||
|
|
||||||
// Schon vorhanden (z.B. main.js oder eine bereits geladene Datendatei):
|
// Schon vorhanden (z.B. main.js oder eine bereits geladene Datendatei):
|
||||||
// nicht erneut ausführen — sonst gäbe es doppelte Deklarationen.
|
// nicht erneut ausführen — sonst gäbe es doppelte Deklarationen.
|
||||||
@@ -986,6 +1076,7 @@
|
|||||||
|
|
||||||
await skripteUebernehmen(dok);
|
await skripteUebernehmen(dok);
|
||||||
applyTranslations(); // seiteneigene data-i18n-Elemente der neuen Seite übersetzen
|
applyTranslations(); // seiteneigene data-i18n-Elemente der neuen Seite übersetzen
|
||||||
|
if (window.dogiSiteTexteLaden) window.dogiSiteTexteLaden(); // Kategorie-/Seitentitel-Overrides der neuen Seite
|
||||||
}
|
}
|
||||||
|
|
||||||
function nahtloseNavigationAktivieren() {
|
function nahtloseNavigationAktivieren() {
|
||||||
@@ -1039,10 +1130,25 @@
|
|||||||
renderFooter();
|
renderFooter();
|
||||||
renderFog();
|
renderFog();
|
||||||
renderMusicWidget();
|
renderMusicWidget();
|
||||||
renderVorschauSchalter();
|
// Bugfix/Nutzer-Wunsch 20.08.2026, vor dem morgigen öffentlichen Start:
|
||||||
|
// "die leute sollen das ja nicht selbst auswählen können". Der Schalter
|
||||||
|
// erkennt Dogi/VanVan bisher nur über das rein client-seitig lesbare
|
||||||
|
// Cookie dogi_role (bewusst NICHT das echte, HttpOnly-geschützte
|
||||||
|
// Sicherheits-Cookie, siehe Kommentar bei getVorschauRolle() oben) —
|
||||||
|
// technisch könnte sich also jede Besucherin/jeder Besucher dieses
|
||||||
|
// Cookie selbst per Browser-Konsole setzen und den Schalter auslösen.
|
||||||
|
// Echte Daten wären dadurch nie einsehbar (der Schalter zeigt nur
|
||||||
|
// Beispieldaten für den eigenen Browser), aber "soll weg für die
|
||||||
|
// Öffentlichkeit" heißt hier bewusst: ganz weg, nicht nur "sicherer".
|
||||||
|
// renderVorschauSchalter() bleibt unangetastet erhalten (funktioniert
|
||||||
|
// exakt wie vorher), nur DIESER Aufruf beim Seitenaufbau ist deaktiviert
|
||||||
|
// — für ein künftiges internes Testen kann diese Zeile jederzeit wieder
|
||||||
|
// aktiviert werden.
|
||||||
|
// renderVorschauSchalter();
|
||||||
musikKnopfFarbenAnpassen();
|
musikKnopfFarbenAnpassen();
|
||||||
nahtloseNavigationAktivieren();
|
nahtloseNavigationAktivieren();
|
||||||
applyTranslations(); // seiteneigene data-i18n-Elemente (z.B. Startseite) übersetzen
|
applyTranslations(); // seiteneigene data-i18n-Elemente (z.B. Startseite) übersetzen
|
||||||
|
if (window.dogiSiteTexteLaden) window.dogiSiteTexteLaden(); // Kategorie-/Seitentitel-Overrides (siehe Verwaltung)
|
||||||
});
|
});
|
||||||
|
|
||||||
/* Kleine öffentliche Schnittstelle für seiteneigene Inline-Skripte
|
/* Kleine öffentliche Schnittstelle für seiteneigene Inline-Skripte
|
||||||
@@ -1074,6 +1180,142 @@
|
|||||||
return feld[sprache] || feld.de || "";
|
return feld[sprache] || feld.de || "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
dogiTeilen() — 21.08.2026, Nutzer-Wunsch: "die events des jahres und
|
||||||
|
speziellen live events soll man auch teilen können." Wiederverwendbare
|
||||||
|
Teilen-Funktion für einzelne Inhalte (aktuell: Event des Jahres auf
|
||||||
|
index.html, spezielles Live-Event auf streamplan.html — künftig bei
|
||||||
|
Bedarf für weitere Stellen nutzbar, ohne den Mechanismus neu zu bauen).
|
||||||
|
|
||||||
|
Nutzt zuerst die native Teilen-Funktion des Geräts (Web-Share-API) —
|
||||||
|
auf dem Handy öffnet das den echten Teilen-Dialog (WhatsApp, TikTok,
|
||||||
|
Mail, ...). Die meisten Desktop-Browser kennen diese API nicht/zeigen
|
||||||
|
dort ohnehin kein sinnvolles Menü -- dort stattdessen Fallback: Link in
|
||||||
|
die Zwischenablage kopieren, der klickende Knopf bekommt kurz ein
|
||||||
|
"✓ Link kopiert"-Feedback (eigener Text + eigene Klasse, gleiches
|
||||||
|
Timing wie .vw-copy-btn in der Verwaltung). */
|
||||||
|
window.dogiTeilen = async function (daten, knopf) {
|
||||||
|
const payload = { title: daten.title || "", text: daten.text || "", url: daten.url || location.href };
|
||||||
|
if (navigator.share) {
|
||||||
|
try {
|
||||||
|
await navigator.share(payload);
|
||||||
|
return;
|
||||||
|
} catch (err) {
|
||||||
|
if (err && err.name === "AbortError") return; // Dialog selbst abgebrochen -- kein Fallback nötig
|
||||||
|
// sonst: weiter zum Zwischenablage-Fallback unten
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!navigator.clipboard) return;
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(payload.url);
|
||||||
|
if (knopf) {
|
||||||
|
const alterText = knopf.textContent;
|
||||||
|
knopf.textContent = "✓ Link kopiert";
|
||||||
|
knopf.classList.add("dogi-teilen-kopiert");
|
||||||
|
setTimeout(() => {
|
||||||
|
knopf.textContent = alterText;
|
||||||
|
knopf.classList.remove("dogi-teilen-kopiert");
|
||||||
|
}, 1600);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* Zwischenablage nicht erreichbar (z.B. kein HTTPS-Kontext) -- kein
|
||||||
|
Blocker, der Link bleibt über die normale Adressleiste erreichbar. */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* =====================================================================
|
||||||
|
dogiTeamLaden() — 21.08.2026, Nutzer-Wunsch: "überall wo Modis sind
|
||||||
|
oder Scouts oder Manager, oder Creator, will ich dass ich die easy
|
||||||
|
über meine Verwaltungsseite hinzufüge und die automatisch in der
|
||||||
|
Website hinzugefügt werden ... auch mit Fotos und TikTok Link."
|
||||||
|
|
||||||
|
Holt die über die Verwaltung angelegten Team-Mitglieder einer
|
||||||
|
Kategorie ("modi" | "scout" | "creator" | "manager") vom Server und
|
||||||
|
bringt sie in GENAU dieselbe Objektform wie die von Hand gepflegten
|
||||||
|
Einträge in data-modis.js/data-scouts.js/data-creator.js/
|
||||||
|
data-manager.js (name/slug/role/photo/bio/quote/social/...) — die
|
||||||
|
aufrufende Seite hängt das Ergebnis einfach hinten an ihr bestehendes
|
||||||
|
window.MODIS/SCOUTS/CREATORS/MANAGERS-Array an und ruft ihre eigene,
|
||||||
|
unveränderte Render-Funktion erneut auf. Kein kategorie-Parameter
|
||||||
|
liefert ALLE Kategorien zusammen (genutzt von profil.html, das vorab
|
||||||
|
nicht weiß, zu welcher Kategorie ein slug gehört).
|
||||||
|
|
||||||
|
Schlägt die Anfrage fehl (Server nicht erreichbar, Netz weg): leeres
|
||||||
|
Array statt Fehler -- die von Hand gepflegten Einträge bleiben davon
|
||||||
|
komplett unberührt sichtbar, nur admin-gepflegte kommen dann halt
|
||||||
|
(vorübergehend) nicht hinzu. */
|
||||||
|
const TEAM_API_BASIS = "https://postfach.dogfather-universe.com";
|
||||||
|
window.dogiTeamLaden = async function (kategorie) {
|
||||||
|
try {
|
||||||
|
const url = kategorie ? `${TEAM_API_BASIS}/team?kategorie=${encodeURIComponent(kategorie)}` : `${TEAM_API_BASIS}/team`;
|
||||||
|
const res = await fetch(url, { cache: "no-store" });
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data || !data.ok || !Array.isArray(data.mitglieder)) return [];
|
||||||
|
return data.mitglieder.map((m) => ({
|
||||||
|
name: m.name,
|
||||||
|
slug: m.slug,
|
||||||
|
kategorie: m.kategorie,
|
||||||
|
role: m.rolle || "",
|
||||||
|
photo: m.foto || "",
|
||||||
|
avatar: m.foto || "",
|
||||||
|
// 21.08.2026 (Runde 2, "wenn ich eine änderung mache ist es auch
|
||||||
|
// für jeden"): volle Feld-Parität mit den von Hand gepflegten
|
||||||
|
// Profilen, damit ein importiertes/bearbeitetes Profil genau so
|
||||||
|
// reichhaltig sein kann wie die schon bestehenden.
|
||||||
|
intro: m.intro || "",
|
||||||
|
bio: m.bio || "",
|
||||||
|
bioHtml: m.bioHtml || "",
|
||||||
|
quote: m.zitat || "",
|
||||||
|
social: m.tiktok ? { tiktok: m.tiktok } : {},
|
||||||
|
status: "aktiv", // /team liefert ohnehin nur aktive Mitglieder
|
||||||
|
rank: m.rang || undefined,
|
||||||
|
rankLabel: m.rangLabel || undefined,
|
||||||
|
extraCta: m.extraCta || undefined,
|
||||||
|
// Markiert admin-gepflegte Einträge (z.B. für profil.html, das
|
||||||
|
// sonst nicht zwischen fest einprogrammierten und dynamischen
|
||||||
|
// Personen unterscheiden könnte).
|
||||||
|
istAdminGepflegt: true,
|
||||||
|
}));
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 21.08.2026 (Runde 2, Nutzer-Wunsch: "wenn ich eine änderung mache ist
|
||||||
|
es auch für jeden für die die schon da sind und die neuen"): fügt eine
|
||||||
|
per dogiTeamLaden() geladene Liste NICHT einfach hinten an die von Hand
|
||||||
|
gepflegte Liste an, sondern ÜBERSCHREIBT darin jeden Eintrag mit
|
||||||
|
demselben slug. So zeigt die Seite nach einem Import + einer
|
||||||
|
Bearbeitung in der Verwaltung automatisch den aktuellen (DB-)Stand
|
||||||
|
statt der alten, einprogrammierten Version -- ist das Backend mal
|
||||||
|
nicht erreichbar, bleibt wenigstens die statische Version sichtbar
|
||||||
|
(nichts verschwindet einfach). */
|
||||||
|
window.dogiTeamZusammenfuehren = function (statische, neue) {
|
||||||
|
if (!neue || !neue.length) return statische;
|
||||||
|
const neueSlugs = new Set(neue.map((n) => n.slug));
|
||||||
|
return [...statische.filter((s) => !neueSlugs.has(s.slug)), ...neue];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 21.08.2026 (Nutzer-Wunsch: "ich will ich titel und namen von
|
||||||
|
kategorien und seiten ändern können in der verwaltung"): lädt einmalig
|
||||||
|
alle in der Verwaltung gesetzten Text-Overrides (siehe
|
||||||
|
server-internal/routes/site-texts.js) und ruft applyTranslations()
|
||||||
|
danach erneut auf, damit gesetzte Overrides die eingebauten
|
||||||
|
Standardtexte sofort ersetzen. Ohne Overrides (Normalfall) ändert sich
|
||||||
|
einfach nichts -- die Seite zeigt weiterhin ihren gewohnten Text. */
|
||||||
|
window.dogiSiteTexteLaden = async function () {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${TEAM_API_BASIS}/site-texts`, { cache: "no-store" });
|
||||||
|
const data = await res.json();
|
||||||
|
if (data && data.ok) {
|
||||||
|
window.__siteTexteCache = data.texte || {};
|
||||||
|
applyTranslations();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
/* kein Override verfügbar -> eingebaute Standardtexte bleiben stehen */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* ===================================================================
|
/* ===================================================================
|
||||||
PWA: Service-Worker-Registrierung
|
PWA: Service-Worker-Registrierung
|
||||||
Voraussetzung dafür, dass Chrome/Edge auf dem Desktop "App
|
Voraussetzung dafür, dass Chrome/Edge auf dem Desktop "App
|
||||||
@@ -1115,4 +1357,47 @@
|
|||||||
appleTitel.content = "DogFather";
|
appleTitel.content = "DogFather";
|
||||||
document.head.appendChild(appleTitel);
|
document.head.appendChild(appleTitel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
PWA: sichtbarer "App installieren"-Knopf (20.08.2026)
|
||||||
|
Nutzer-Wunsch: "ich kann sie immer noch nicht runter laden" — reine
|
||||||
|
Installierbarkeit über Manifest+Service-Worker reicht nicht, wenn
|
||||||
|
niemand weiß, WO im Browser man das anklickt (verstecktes
|
||||||
|
Adressleisten-Symbol/3-Punkte-Menü). Jede Seite mit einem Element
|
||||||
|
#pwa-install-btn (siehe renderFooter() oben + verwaltung.html) bekommt
|
||||||
|
jetzt einen echten, sichtbaren Knopf, der den nativen Install-Dialog
|
||||||
|
direkt auslöst — funktioniert pro Seite mit GENAU dem Manifest, das
|
||||||
|
diese Seite selbst verlinkt (index.html -> manifest.json,
|
||||||
|
verwaltung.html -> manifest-verwaltung.json), ganz ohne Sonderfall-Code
|
||||||
|
hier. Zeigt sich NUR, wenn der Browser das überhaupt unterstützt UND
|
||||||
|
die Seite nicht schon als installierte App läuft (Safari/iOS feuert
|
||||||
|
"beforeinstallprompt" nie — dort bleibt "Zum Home-Bildschirm" über das
|
||||||
|
Teilen-Menü weiterhin der einzige Weg, das ist eine Apple-Plattform-
|
||||||
|
Grenze, kein Bug dieser Seite). */
|
||||||
|
let installEvent = null;
|
||||||
|
const laeuftSchonAlsApp = () =>
|
||||||
|
window.matchMedia("(display-mode: standalone)").matches || window.navigator.standalone === true;
|
||||||
|
|
||||||
|
window.addEventListener("beforeinstallprompt", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
installEvent = e;
|
||||||
|
const btn = document.getElementById("pwa-install-btn");
|
||||||
|
if (btn && !laeuftSchonAlsApp()) btn.style.display = "";
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("click", (e) => {
|
||||||
|
if (e.target.closest && e.target.closest("#pwa-install-btn") && installEvent) {
|
||||||
|
installEvent.prompt();
|
||||||
|
installEvent.userChoice.finally(() => {
|
||||||
|
installEvent = null;
|
||||||
|
const btn = document.getElementById("pwa-install-btn");
|
||||||
|
if (btn) btn.style.display = "none";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("appinstalled", () => {
|
||||||
|
const btn = document.getElementById("pwa-install-btn");
|
||||||
|
if (btn) btn.style.display = "none";
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
stimmen.js — NUR für stimmen.html (20.08.2026, Nutzer-Wunsch: "eine
|
||||||
|
richtig geile kachel ... wo die leute mit namen und tiktok namen mir
|
||||||
|
eine nachricht schreiben können die ich in die verwaltung kriege").
|
||||||
|
Formular schickt eine Einreichung ab (landet als Entwurf, siehe
|
||||||
|
server-internal/routes/testimonials.js), zweiter Teil lädt die bereits
|
||||||
|
freigegebenen Stimmen und zeigt sie an — bleibt komplett unsichtbar,
|
||||||
|
solange keine einzige freigegeben wurde. ===================================================================== */
|
||||||
|
(function () {
|
||||||
|
const API_BASE_URL = "https://postfach.dogfather-universe.com";
|
||||||
|
|
||||||
|
const formular = document.getElementById("stimmen-formular");
|
||||||
|
if (formular) {
|
||||||
|
const erfolgEl = document.getElementById("stimmen-erfolg");
|
||||||
|
const fehlerEl = document.getElementById("stimmen-fehler");
|
||||||
|
formular.addEventListener("submit", async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
erfolgEl.classList.remove("zeige");
|
||||||
|
fehlerEl.classList.remove("zeige");
|
||||||
|
|
||||||
|
const name = document.getElementById("st-name").value.trim();
|
||||||
|
const tiktokUsername = document.getElementById("st-tiktok").value.trim();
|
||||||
|
const nachricht = document.getElementById("st-nachricht").value.trim();
|
||||||
|
|
||||||
|
const absendenBtn = formular.querySelector("button[type=submit]");
|
||||||
|
absendenBtn.disabled = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_BASE_URL}/testimonials/submit`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ name, tiktokUsername, nachricht }),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.ok) {
|
||||||
|
fehlerEl.textContent = data.error || "Einreichen hat leider nicht geklappt. Bitte versuch es nochmal.";
|
||||||
|
fehlerEl.classList.add("zeige");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formular.hidden = true;
|
||||||
|
erfolgEl.classList.add("zeige");
|
||||||
|
} catch {
|
||||||
|
fehlerEl.textContent = "Verbindung zum Server fehlgeschlagen. Bitte versuch es nochmal.";
|
||||||
|
fehlerEl.classList.add("zeige");
|
||||||
|
} finally {
|
||||||
|
absendenBtn.disabled = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeHtml(text) {
|
||||||
|
return String(text ?? "").replace(/[&<>"']/g, (z) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[z]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Erstes sichtbares Zeichen des Namens fürs Avatar-Medaillon. [...name]
|
||||||
|
statt name[0], damit auch Namen, die mit einem Emoji beginnen, nicht
|
||||||
|
mitten in einem Zeichen abgeschnitten werden (ein Emoji besteht in
|
||||||
|
JavaScript aus mehreren "Zeichen" — name[0] lieferte sonst nur ein
|
||||||
|
kaputtes Halb-Zeichen). */
|
||||||
|
function erstesZeichen(name) {
|
||||||
|
const zeichen = [...String(name || "").trim()];
|
||||||
|
return zeichen.length ? zeichen[0].toUpperCase() : "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
function ladeEchteStimmen() {
|
||||||
|
fetch(`${API_BASE_URL}/testimonials`, { cache: "no-store" })
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
const liste = data && data.ok ? data.testimonials : [];
|
||||||
|
if (!liste.length) return; // Abschnitt bleibt hidden, kein Platzhalter mehr
|
||||||
|
const root = document.getElementById("stimmen-echte-root");
|
||||||
|
const grid = document.getElementById("stimmen-echte-grid");
|
||||||
|
grid.innerHTML = liste
|
||||||
|
.map(
|
||||||
|
(t) => `<div class="stimmen-karte">
|
||||||
|
<p class="stimmen-karte-text">${escapeHtml(t.nachricht)}</p>
|
||||||
|
<div class="stimmen-karte-fuss">
|
||||||
|
<span class="stimmen-karte-avatar" aria-hidden="true">${escapeHtml(erstesZeichen(t.name))}</span>
|
||||||
|
<span class="stimmen-karte-namen">
|
||||||
|
<span class="stimmen-karte-name">${escapeHtml(t.name)}</span>
|
||||||
|
${t.tiktokUsername ? `<span class="stimmen-karte-tiktok">@${escapeHtml(t.tiktokUsername)}</span>` : ""}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
root.hidden = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* Abschnitt bleibt einfach unsichtbar -- kein Fehler nötig. */
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ladeEchteStimmen();
|
||||||
|
})();
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
/* =====================================================================
|
||||||
|
streamplan.js — "Live-Radar" NUR für streamplan.html (20.08.2026,
|
||||||
|
Nutzer-Wunsch: "soll viel geiler und spezieller sein, überrasch mich",
|
||||||
|
ergänzt um eine zweite Uhr für ein "spezielles Live-Event": "sehr
|
||||||
|
wichtig ist dass ich auch spezielle live events da auch eintragen kann
|
||||||
|
in der verwaltungsseite so dass da eine zweite uhr erscheint wo die
|
||||||
|
zeit dann für dieses spezielle event läuft").
|
||||||
|
|
||||||
|
Zwei 24-Stunden-Ziffernblätter (reines SVG, keine Bild-Assets nötig):
|
||||||
|
- Uhr 1 (immer sichtbar): der feste 20-Uhr-Termin leuchtet dauerhaft als
|
||||||
|
Fixpunkt, ein "Jetzt"-Zeiger bewegt sich mit der echten Uhrzeit der
|
||||||
|
besuchenden Person mit. Live laut /live-status -> Rot/Puls +
|
||||||
|
"Jetzt anschauen".
|
||||||
|
- Uhr 2 (nur wenn in der Verwaltung aktiviert): zählt zu einem exakten
|
||||||
|
Zieldatum+Uhrzeit runter (Tage möglich, nicht nur Stunden), Magenta/
|
||||||
|
Violett statt Cyan/Gold, damit beide Uhren auf einen Blick
|
||||||
|
unterscheidbar sind ("bissl Farbe"). ===================================================================== */
|
||||||
|
(function () {
|
||||||
|
const API_BASE_URL = "https://postfach.dogfather-universe.com";
|
||||||
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
||||||
|
const CX = 150, CY = 150, R_OUT = 130, R_TICK_IN = 116, R_TICK_MAJOR_IN = 106, R_HAND = 100;
|
||||||
|
|
||||||
|
function polar(r, stundenAnteil) {
|
||||||
|
const winkel = (stundenAnteil / 24) * 360 - 90; // 0 Uhr oben, im Uhrzeigersinn
|
||||||
|
const rad = (winkel * Math.PI) / 180;
|
||||||
|
return { x: CX + r * Math.cos(rad), y: CY + r * Math.sin(rad) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Zeichnet die 24 Stundenstriche + einen Fixpunkt-Leuchtpunkt bei
|
||||||
|
"fixStunde" (kann eine Dezimalzahl sein, z.B. 19.5 = 19:30) in die
|
||||||
|
übergebenen <g>-Container. Läuft einmalig pro Uhr. */
|
||||||
|
function zeichneZiffernblatt(ticksHost, fixHost, fixStunde) {
|
||||||
|
ticksHost.innerHTML = "";
|
||||||
|
for (let h = 0; h < 24; h++) {
|
||||||
|
const major = h % 6 === 0;
|
||||||
|
const p1 = polar(R_OUT, h);
|
||||||
|
const p2 = polar(major ? R_TICK_MAJOR_IN : R_TICK_IN, h);
|
||||||
|
const line = document.createElementNS(SVG_NS, "line");
|
||||||
|
line.setAttribute("x1", p1.x); line.setAttribute("y1", p1.y);
|
||||||
|
line.setAttribute("x2", p2.x); line.setAttribute("y2", p2.y);
|
||||||
|
line.setAttribute("class", major ? "radar-tick radar-tick-major" : "radar-tick");
|
||||||
|
ticksHost.appendChild(line);
|
||||||
|
}
|
||||||
|
fixHost.innerHTML = "";
|
||||||
|
if (fixStunde != null) {
|
||||||
|
const fixPos = polar(R_OUT + 11, fixStunde);
|
||||||
|
const fixDot = document.createElementNS(SVG_NS, "circle");
|
||||||
|
fixDot.setAttribute("cx", fixPos.x); fixDot.setAttribute("cy", fixPos.y);
|
||||||
|
fixDot.setAttribute("r", 6.5);
|
||||||
|
fixDot.setAttribute("class", "radar-fixmarker-dot");
|
||||||
|
fixHost.appendChild(fixDot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function zeigerAktualisieren(handEl, dotEl, stundenAnteil) {
|
||||||
|
const spitze = polar(R_HAND, stundenAnteil);
|
||||||
|
handEl.setAttribute("x2", spitze.x); handEl.setAttribute("y2", spitze.y);
|
||||||
|
dotEl.setAttribute("cx", spitze.x); dotEl.setAttribute("cy", spitze.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function zweistellig(n) { return String(n).padStart(2, "0"); }
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
Uhr 1: täglicher 20-Uhr-Fixtermin (bereits bestehend, unverändert im
|
||||||
|
Verhalten) ================================================================= */
|
||||||
|
const radar = document.getElementById("stream-radar");
|
||||||
|
if (radar) {
|
||||||
|
const FIX_STUNDE = 20;
|
||||||
|
zeichneZiffernblatt(document.getElementById("radar-ticks"), document.getElementById("radar-fixmarker"), FIX_STUNDE);
|
||||||
|
|
||||||
|
const nowHand = document.getElementById("radar-now-hand");
|
||||||
|
const nowDot = document.getElementById("radar-now-dot");
|
||||||
|
function zeigerTick() {
|
||||||
|
const jetzt = new Date();
|
||||||
|
zeigerAktualisieren(nowHand, nowDot, jetzt.getHours() + jetzt.getMinutes() / 60 + jetzt.getSeconds() / 3600);
|
||||||
|
}
|
||||||
|
zeigerTick();
|
||||||
|
setInterval(zeigerTick, 15000);
|
||||||
|
|
||||||
|
function naechsterFixTermin() {
|
||||||
|
const jetzt = new Date();
|
||||||
|
const ziel = new Date(jetzt);
|
||||||
|
ziel.setHours(FIX_STUNDE, 0, 0, 0);
|
||||||
|
if (ziel <= jetzt) ziel.setDate(ziel.getDate() + 1);
|
||||||
|
return ziel;
|
||||||
|
}
|
||||||
|
|
||||||
|
const countdownEl = document.getElementById("radar-countdown");
|
||||||
|
const statusText = document.getElementById("radar-status-text");
|
||||||
|
const statusSub = document.getElementById("radar-status-sub");
|
||||||
|
const watchBtn = document.getElementById("radar-watch-btn");
|
||||||
|
|
||||||
|
let istLive = false;
|
||||||
|
let letzterTitel = null;
|
||||||
|
|
||||||
|
function countdownTick() {
|
||||||
|
if (istLive) return;
|
||||||
|
const diffMs = naechsterFixTermin() - new Date();
|
||||||
|
const gesamtSek = Math.max(0, Math.floor(diffMs / 1000));
|
||||||
|
const std = Math.floor(gesamtSek / 3600);
|
||||||
|
const min = Math.floor((gesamtSek % 3600) / 60);
|
||||||
|
const sek = gesamtSek % 60;
|
||||||
|
countdownEl.textContent = `${zweistellig(std)}:${zweistellig(min)}:${zweistellig(sek)}`;
|
||||||
|
}
|
||||||
|
setInterval(countdownTick, 1000);
|
||||||
|
|
||||||
|
function texteNeuSetzen() {
|
||||||
|
const t = window.dogiUebersetzen || ((k) => k);
|
||||||
|
if (istLive) {
|
||||||
|
statusText.textContent = t("sp_radar_live");
|
||||||
|
countdownEl.textContent = letzterTitel || t("sp_radar_live_generic");
|
||||||
|
statusSub.textContent = "";
|
||||||
|
watchBtn.hidden = false;
|
||||||
|
} else {
|
||||||
|
statusText.textContent = t("sp_radar_offline");
|
||||||
|
statusSub.textContent = t("sp_radar_countdown_label");
|
||||||
|
watchBtn.hidden = true;
|
||||||
|
countdownTick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function liveStatusAnwenden(data) {
|
||||||
|
istLive = !!(data && data.live);
|
||||||
|
letzterTitel = data && data.title ? data.title : null;
|
||||||
|
radar.classList.toggle("ist-live", istLive);
|
||||||
|
texteNeuSetzen();
|
||||||
|
}
|
||||||
|
|
||||||
|
function liveStatusLaden() {
|
||||||
|
return fetch(`${API_BASE_URL}/live-status`, { cache: "no-store" })
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(liveStatusAnwenden)
|
||||||
|
.catch(() => {
|
||||||
|
istLive = false;
|
||||||
|
radar.classList.remove("ist-live");
|
||||||
|
texteNeuSetzen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
liveStatusLaden();
|
||||||
|
setInterval(liveStatusLaden, 30000);
|
||||||
|
document.addEventListener("visibilitychange", () => { if (!document.hidden) liveStatusLaden(); });
|
||||||
|
document.addEventListener("dogi-sprache-geaendert", texteNeuSetzen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
Uhr 2: "spezielles Live-Event" aus der Verwaltung (20.08.2026, neu).
|
||||||
|
Nur sichtbar, wenn /special-event ein aktives, noch bevorstehendes
|
||||||
|
Event liefert. Zieluhrzeit kann Tage entfernt sein -> Countdown zeigt
|
||||||
|
bei Bedarf "Xd HH:MM:SS" statt nur HH:MM:SS. ================================================================= */
|
||||||
|
const seWrap = document.getElementById("se-radar-wrap");
|
||||||
|
if (seWrap) {
|
||||||
|
const seRadar = document.getElementById("se-radar");
|
||||||
|
const seNowHand = document.getElementById("se-radar-now-hand");
|
||||||
|
const seNowDot = document.getElementById("se-radar-now-dot");
|
||||||
|
const seCountdown = document.getElementById("se-radar-countdown");
|
||||||
|
const seTitel = document.getElementById("se-radar-titel");
|
||||||
|
const seLink = document.getElementById("se-radar-link");
|
||||||
|
const seTeilenBtn = document.getElementById("se-radar-teilen");
|
||||||
|
let zielDatum = null;
|
||||||
|
let aktuellesEvent = null; // für dogiTeilen() beim Klick (21.08.2026)
|
||||||
|
let countdownIntervalId = null;
|
||||||
|
let zeigerIntervalId = null;
|
||||||
|
|
||||||
|
function seCountdownTick() {
|
||||||
|
if (!zielDatum) return;
|
||||||
|
const diffMs = zielDatum - new Date();
|
||||||
|
if (diffMs <= 0) {
|
||||||
|
seRadar.classList.add("ist-live");
|
||||||
|
seCountdown.textContent = window.dogiUebersetzen ? window.dogiUebersetzen("sp_radar_live_generic") : "Live!";
|
||||||
|
clearInterval(countdownIntervalId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const gesamtSek = Math.floor(diffMs / 1000);
|
||||||
|
const tage = Math.floor(gesamtSek / 86400);
|
||||||
|
const std = Math.floor((gesamtSek % 86400) / 3600);
|
||||||
|
const min = Math.floor((gesamtSek % 3600) / 60);
|
||||||
|
const sek = gesamtSek % 60;
|
||||||
|
seCountdown.textContent = tage > 0
|
||||||
|
? `${tage}T ${zweistellig(std)}:${zweistellig(min)}:${zweistellig(sek)}`
|
||||||
|
: `${zweistellig(std)}:${zweistellig(min)}:${zweistellig(sek)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Der "Jetzt"-Zeiger zeigt wie bei Uhr 1 die ECHTE aktuelle Uhrzeit --
|
||||||
|
// der magentafarbene Fixpunkt markiert dagegen die Tageszeit, zu der
|
||||||
|
// das spezielle Event stattfindet. So bleibt die Bildsprache zwischen
|
||||||
|
// beiden Uhren konsistent (nur der Fixpunkt unterscheidet sich).
|
||||||
|
function seZeigerTick() {
|
||||||
|
const jetzt = new Date();
|
||||||
|
zeigerAktualisieren(seNowHand, seNowDot, jetzt.getHours() + jetzt.getMinutes() / 60 + jetzt.getSeconds() / 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
function specialEventLaden() {
|
||||||
|
return fetch(`${API_BASE_URL}/special-event`, { cache: "no-store" })
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
const event = data && data.ok ? data.event : null;
|
||||||
|
if (countdownIntervalId) clearInterval(countdownIntervalId);
|
||||||
|
if (zeigerIntervalId) clearInterval(zeigerIntervalId);
|
||||||
|
if (!event) {
|
||||||
|
seWrap.hidden = true;
|
||||||
|
zielDatum = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
zielDatum = new Date(event.zieldatum);
|
||||||
|
aktuellesEvent = event;
|
||||||
|
seWrap.hidden = false;
|
||||||
|
seRadar.classList.remove("ist-live");
|
||||||
|
seTitel.textContent = window.dogiFeld ? window.dogiFeld(event.titel) : event.titel?.de || "";
|
||||||
|
zeichneZiffernblatt(document.getElementById("se-radar-ticks"), document.getElementById("se-radar-fixmarker"), zielDatum.getHours() + zielDatum.getMinutes() / 60);
|
||||||
|
seZeigerTick();
|
||||||
|
seCountdownTick();
|
||||||
|
zeigerIntervalId = setInterval(seZeigerTick, 15000);
|
||||||
|
countdownIntervalId = setInterval(seCountdownTick, 1000);
|
||||||
|
if (event.link) {
|
||||||
|
seLink.href = event.link;
|
||||||
|
seLink.hidden = false;
|
||||||
|
} else {
|
||||||
|
seLink.hidden = true;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
seWrap.hidden = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
specialEventLaden();
|
||||||
|
setInterval(specialEventLaden, 60000); // reicht -- Aenderungen in der Verwaltung muessen nicht sekundengenau ankommen
|
||||||
|
document.addEventListener("dogi-sprache-geaendert", () => {
|
||||||
|
if (zielDatum) specialEventLaden();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Teilen (21.08.2026, Nutzer-Wunsch: "... speziellen live events soll man
|
||||||
|
// auch teilen können") -- teilt die Streamplan-Seite selbst (dort steht
|
||||||
|
// ja bereits automatisch das aktuell aktive Event), zusammen mit Titel
|
||||||
|
// im Text, damit klar ist, worum es geht. Nur EIN spezielles Event kann
|
||||||
|
// je aktiv sein, deshalb reicht ein einziger Knopf ohne Slot-Bezug.
|
||||||
|
seTeilenBtn?.addEventListener("click", () => {
|
||||||
|
if (!aktuellesEvent) return;
|
||||||
|
const titel = window.dogiFeld ? window.dogiFeld(aktuellesEvent.titel) : aktuellesEvent.titel?.de || "";
|
||||||
|
window.dogiTeilen?.(
|
||||||
|
{
|
||||||
|
title: titel,
|
||||||
|
text: `🔴 ${titel} — spezielles Live-Event bei DogFather!`,
|
||||||
|
url: `${location.origin}/streamplan.html`,
|
||||||
|
},
|
||||||
|
seTeilenBtn
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Du bist Streamer und suchst einen Manager? Bewirb dich als Creator bei Spicy Media." />
|
<meta name="twitter:description" content="Du bist Streamer und suchst einen Manager? Bewirb dich als Creator bei Spicy Media." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-creator.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-creator-mobile.jpg');">
|
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-creator.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-creator-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -70,8 +70,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js"></script>
|
<script src="assets/js/forms.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-bewerben-creator.js"></script>
|
<script src="assets/js/i18n-bewerben-creator.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de" data-theme="spicymedia">
|
<html lang="de" data-theme="dogfather">
|
||||||
<head>
|
<head>
|
||||||
|
<!-- Nutzer-Wunsch 20.08.2026: neues Hero-Bild (Hasi Dog/DogFather/Husky, babyblau statt
|
||||||
|
rot) + Theme von "spicymedia" (Rot/Orange) auf "dogfather" (Babyblau/Lila) umgestellt,
|
||||||
|
damit Knopf-/Link-/Eyebrow-Farben zum neuen, blauen Bild passen. -->
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Kooperation anfragen — DOGFATHER UNIVERSE</title>
|
<title>Kooperation anfragen — DOGFATHER UNIVERSE</title>
|
||||||
@@ -19,10 +22,10 @@
|
|||||||
<meta name="twitter:description" content="Kooperationsanfrage an Manager DogFather / Spicy Media." />
|
<meta name="twitter:description" content="Kooperationsanfrage an Manager DogFather / Spicy Media." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-mobile.jpg');">
|
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -65,8 +68,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js"></script>
|
<script src="assets/js/forms.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-bewerben-kooperation.js"></script>
|
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,17 +19,17 @@
|
|||||||
<meta name="twitter:description" content="Werde Modi bei Team Dogi — DogFathers persönliches Community-Team." />
|
<meta name="twitter:description" content="Werde Modi bei Team Dogi — DogFathers persönliches Community-Team." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
|
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-modi.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-modi-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="hero page-bg">
|
<section class="hero page-bg">
|
||||||
<div class="hero-banner"><img src="/assets/img/bg-dogfather.jpg" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-bewerben-modi.jpg" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="bwm_eyebrow">🩵 Team Dogi — Community</span>
|
<span class="eyebrow" data-i18n="bwm_eyebrow">🩵 Team Dogi — Community</span>
|
||||||
<h1 data-i18n="bwm_h1">Werde Modi bei Team Dogi</h1>
|
<h1 data-i18n="bwm_h1">Werde Modi bei Team Dogi</h1>
|
||||||
@@ -79,8 +79,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js"></script>
|
<script src="assets/js/forms.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-bewerben-modi.js"></script>
|
<script src="assets/js/i18n-bewerben-modi.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,17 +19,17 @@
|
|||||||
<meta name="twitter:description" content="Werde Scout für DogFather bei der Spicy Media Agentur — finde neue Talente." />
|
<meta name="twitter:description" content="Werde Scout für DogFather bei der Spicy Media Agentur — finde neue Talente." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-scouts.jpg');--page-bg-mobile:url('/assets/img/bg-scouts-mobile.jpg');">
|
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-scout.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-scout-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="hero page-bg">
|
<section class="hero page-bg">
|
||||||
<div class="hero-banner"><img src="/assets/img/bg-scouts.jpg" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-bewerben-scout.jpg" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="bws_eyebrow">Manager DogFather — Agentur</span>
|
<span class="eyebrow" data-i18n="bws_eyebrow">Manager DogFather — Agentur</span>
|
||||||
<h1 data-i18n="bws_h1">Werde Scout für DogFather</h1>
|
<h1 data-i18n="bws_h1">Werde Scout für DogFather</h1>
|
||||||
@@ -71,8 +71,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/forms.js"></script>
|
<script src="assets/js/forms.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-bewerben-scout.js"></script>
|
<script src="assets/js/i18n-bewerben-scout.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Werde Teil vom Team: Modi bei Team Dogi, Scout oder Creator bei Spicy Media, oder Kooperation anfragen." />
|
<meta name="twitter:description" content="Werde Teil vom Team: Modi bei Team Dogi, Scout oder Creator bei Spicy Media, oder Kooperation anfragen." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-split.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-split-mobile.jpg');">
|
<body class="mit-hintergrund bewerben-seite" style="--page-bg:url('/assets/img/bg-bewerben-split.jpg');--page-bg-mobile:url('/assets/img/bg-bewerben-split-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<div class="hero-banner"><img src="/assets/img/bg-bewerben-split.jpg" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-bewerben-split.jpg" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="bw_eyebrow">Werde Teil vom Team</span>
|
<span class="eyebrow" data-i18n="bw_eyebrow">Werde Teil vom Team</span>
|
||||||
<h1 data-i18n="bw_h1">Community oder Agentur — such dir deinen Weg</h1>
|
<h1 data-i18n="bw_h1">Community oder Agentur — such dir deinen Weg</h1>
|
||||||
<p class="lead worlds" data-i18n="bw_lead">Vier Bewerbungen, drei ganz unterschiedliche Rollen. Wähl, was zu dir passt.</p>
|
<p class="lead worlds" data-i18n="bw_lead">Vier Bewerbungen, drei ganz unterschiedliche Rollen. Wähl, was zu dir passt.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-bewerben.js"></script>
|
<script src="assets/js/i18n-bewerben.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Der Husky an DogFathers Seite — das tierische Gesicht der HasiDog-Welt." />
|
<meta name="twitter:description" content="Der Husky an DogFathers Seite — das tierische Gesicht der HasiDog-Welt." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-casper.jpg');--page-bg-mobile:url('/assets/img/bg-casper-mobile.jpg');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-casper.jpg');--page-bg-mobile:url('/assets/img/bg-casper-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-casper.js"></script>
|
<script src="assets/js/i18n-casper.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Supporter-Abstimmungen (20.08.2026) — "nächste Ausbaustufe" aus
|
||||||
|
-- Dogfather_VanVan_Supporter_Abo.odt Abschnitt 20 (Bonus-Features, dort
|
||||||
|
-- bewusst nicht in den verbindlichen Abnahmekriterien Abschnitt 34, siehe
|
||||||
|
-- Supporter-Abo-System.md "Bewusst nicht Teil dieser ersten Ausbaustufe").
|
||||||
|
-- Dogi erstellt in der Verwaltung eine Frage + Antwortoptionen auf Deutsch,
|
||||||
|
-- die anderen Sprachen werden wie bei "Event des Jahres" automatisch beim
|
||||||
|
-- Speichern übersetzt (lib/translate.js). Jeder DogiCrew-Supporter darf pro
|
||||||
|
-- Abstimmung genau einmal abstimmen (UNIQUE-Constraint statt Anwendungslogik
|
||||||
|
-- — robust auch bei gleichzeitigen Anfragen).
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS supporter_polls (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
question TEXT NOT NULL, -- JSON {de, "de-CH", en, fr, pt}
|
||||||
|
options TEXT NOT NULL, -- JSON-Array von {de, "de-CH", en, fr, pt}
|
||||||
|
status TEXT NOT NULL DEFAULT 'active', -- active | closed
|
||||||
|
created_at TEXT NOT NULL,
|
||||||
|
created_by TEXT NOT NULL,
|
||||||
|
closed_at TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS supporter_poll_votes (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
poll_id TEXT NOT NULL REFERENCES supporter_polls(id),
|
||||||
|
supporter_id TEXT NOT NULL REFERENCES supporter_users(id),
|
||||||
|
option_index INTEGER NOT NULL,
|
||||||
|
voted_at TEXT NOT NULL,
|
||||||
|
UNIQUE(poll_id, supporter_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_supporter_poll_votes_poll ON supporter_poll_votes (poll_id);
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- "Stimmen" / Testimonials (20.08.2026) — Fans können auf stimmen.html
|
||||||
|
-- selbst eine Nachricht mit Name + TikTok-Namen einreichen. Landet erst
|
||||||
|
-- als Entwurf in der Verwaltung ("eingereicht") -- öffentlich sichtbar
|
||||||
|
-- wird ein Eintrag NUR, wenn Dogi/VanVan ihn dort ausdrücklich freigeben
|
||||||
|
-- (Nutzer-Wunsch: "die ausgewählten über die verwaltungsseite auf die
|
||||||
|
-- website hinzufügen"). Eigene Tabelle statt app_settings, weil dies eine
|
||||||
|
-- WACHSENDE Liste ist (beliebig viele Einreichungen), keine feste Anzahl
|
||||||
|
-- Slots wie "Event des Jahres".
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS testimonials (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
tiktok_username TEXT,
|
||||||
|
nachricht TEXT NOT NULL,
|
||||||
|
status TEXT NOT NULL DEFAULT 'eingereicht', -- eingereicht | freigegeben | abgelehnt
|
||||||
|
created_at TEXT NOT NULL,
|
||||||
|
entschieden_am TEXT,
|
||||||
|
entschieden_von TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_testimonials_status ON testimonials (status, created_at);
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Team-Mitglieder (21.08.2026) — Nutzer-Wunsch: "überall wo Modis sind
|
||||||
|
-- oder Scouts oder Manager, oder Creator, will ich dass ich die easy über
|
||||||
|
-- meine Verwaltungsseite hinzufüge und die automatisch in der Website
|
||||||
|
-- hinzugefügt werden ... auch mit Fotos und TikTok Link."
|
||||||
|
--
|
||||||
|
-- Eine gemeinsame Tabelle für alle vier Kategorien (statt vier eigenen),
|
||||||
|
-- weil sie strukturell identisch sind (Name/Rolle/Foto/TikTok/Bio/Zitat) --
|
||||||
|
-- "kategorie" unterscheidet, wo jemand öffentlich erscheint. rang/
|
||||||
|
-- rang_label sind nur für Modis relevant (Pyramiden-Hierarchie auf
|
||||||
|
-- team-modis.html), bei den anderen Kategorien bleiben sie einfach leer.
|
||||||
|
--
|
||||||
|
-- Ergänzt die weiterhin bestehenden, von Hand gepflegten Einträge in
|
||||||
|
-- assets/js/data-modis.js/data-scouts.js/data-creator.js/data-manager.js
|
||||||
|
-- (z.B. VanVan, Patrick, Bananenstift) -- die Website mischt beide Quellen
|
||||||
|
-- zu einer gemeinsamen Liste (siehe window.dogiTeamLaden() in main.js).
|
||||||
|
-- Ein Fehler bei einer Kategorie kann so nie die schon bestehenden,
|
||||||
|
-- fest einprogrammierten Profile verschwinden lassen.
|
||||||
|
--
|
||||||
|
-- slug ist GLOBAL eindeutig (nicht nur pro Kategorie), weil profil.html
|
||||||
|
-- über alle Kategorien hinweg nach genau einem slug sucht.
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS team_members (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
kategorie TEXT NOT NULL, -- modi | scout | creator | manager
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
slug TEXT NOT NULL UNIQUE,
|
||||||
|
rolle_json TEXT NOT NULL, -- {de, "de-CH", en, fr, pt}
|
||||||
|
foto_url TEXT,
|
||||||
|
tiktok_url TEXT,
|
||||||
|
bio_json TEXT, -- {de, "de-CH", en, fr, pt} oder NULL
|
||||||
|
zitat_json TEXT, -- {de, "de-CH", en, fr, pt} oder NULL
|
||||||
|
rang INTEGER, -- nur Modi-Pyramide, sonst NULL
|
||||||
|
rang_label_json TEXT, -- nur Modi-Pyramide, sonst NULL
|
||||||
|
reihenfolge INTEGER NOT NULL DEFAULT 0,
|
||||||
|
status TEXT NOT NULL DEFAULT 'aktiv', -- aktiv | pausiert
|
||||||
|
erstellt_am TEXT NOT NULL,
|
||||||
|
geaendert_am TEXT NOT NULL,
|
||||||
|
erstellt_von TEXT,
|
||||||
|
geaendert_von TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_team_members_kategorie ON team_members (kategorie, status, reihenfolge);
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- Team-Mitglieder: volle Feld-Parität mit den von Hand gepflegten
|
||||||
|
-- Profilen (21.08.2026, Nutzer-Wunsch: "wenn ich eine änderung mache
|
||||||
|
-- ist es auch für jeden für die die schon da sind und die neuen").
|
||||||
|
--
|
||||||
|
-- Damit ein bestehendes Profil (Patrick, Bananenstift, Diene, ...) 1:1
|
||||||
|
-- in die Verwaltung importiert werden kann, OHNE Inhalt zu verlieren,
|
||||||
|
-- braucht team_members dieselben optionalen Felder wie
|
||||||
|
-- data-modis.js/data-scouts.js: intro (kurzer Text neben dem Foto),
|
||||||
|
-- bio_html (ausführliche, mehrabsatzige Vorstellung mit Überschriften),
|
||||||
|
-- extra_cta (zweiter Button auf der Profilkarte, aktuell nur bei Marina
|
||||||
|
-- -> manager.html genutzt).
|
||||||
|
-- =====================================================================
|
||||||
|
|
||||||
|
ALTER TABLE team_members ADD COLUMN intro_json TEXT;
|
||||||
|
ALTER TABLE team_members ADD COLUMN bio_html_json TEXT;
|
||||||
|
ALTER TABLE team_members ADD COLUMN extra_cta_href TEXT;
|
||||||
|
ALTER TABLE team_members ADD COLUMN extra_cta_label_json TEXT;
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- 0009_supporter_access_code.sql — Eigener, fester Zugangscode je Supporter
|
||||||
|
--
|
||||||
|
-- Nutzer-Wunsch 21.08.2026: "wieso sollte ich wieder ein code bekommen,
|
||||||
|
-- mach die anmeldung einfach, die leute sollen nach der registrierung einen
|
||||||
|
-- festen code erstellen können für sich selbst und fertig. mit dem sie rein
|
||||||
|
-- kommen."
|
||||||
|
--
|
||||||
|
-- Bis hierher lief JEDER Login über einen 6-stelligen Einmalcode per E-Mail
|
||||||
|
-- (supporter_auth_codes) — auch für längst registrierte Leute. Ab jetzt legt
|
||||||
|
-- man bei der Registrierung einen eigenen, dauerhaften Zugangscode fest und
|
||||||
|
-- kommt damit direkt rein.
|
||||||
|
--
|
||||||
|
-- Gespeichert wird NIE der Code selbst, sondern ein scrypt-Hash inkl.
|
||||||
|
-- zufälligem Salt (Format "scrypt$<salt-hex>$<hash-hex>", siehe
|
||||||
|
-- lib/crypto.js). Damit ist der Code selbst aus der Datenbank heraus nicht
|
||||||
|
-- rekonstruierbar — auch nicht von uns.
|
||||||
|
--
|
||||||
|
-- Der E-Mail-Einmalcode-Weg bleibt vollständig erhalten: er ist jetzt der
|
||||||
|
-- "Code vergessen?"-Weg, über den man sich einen neuen Zugangscode setzen
|
||||||
|
-- kann. Deshalb wird an supporter_auth_codes nichts geändert.
|
||||||
|
--
|
||||||
|
-- NULL = Konto aus der Zeit vor dieser Änderung, hat noch keinen eigenen
|
||||||
|
-- Code. Solche Konten kommen über "Code vergessen?" an einen.
|
||||||
|
-- =====================================================================
|
||||||
|
ALTER TABLE supporter_users ADD COLUMN access_code_hash TEXT;
|
||||||
|
ALTER TABLE supporter_users ADD COLUMN access_code_set_at TEXT;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
-- =====================================================================
|
||||||
|
-- 0010_testimonials_avatar.sql — Profilbild für die "Stimmen"
|
||||||
|
--
|
||||||
|
-- Nutzer-Wunsch 21.08.2026: "ich soll auch paar geile bilder zum aussuchen
|
||||||
|
-- haben als profilbild falls die leute kein bild mit schicken, das soll
|
||||||
|
-- denen auch als möglichkeit gegeben sein."
|
||||||
|
--
|
||||||
|
-- Bis hierher zeigte jede Stimme nur den Anfangsbuchstaben des Namens in
|
||||||
|
-- einem Kreis. Jetzt kann ein Bild hinterlegt werden.
|
||||||
|
--
|
||||||
|
-- Inhalt der Spalte:
|
||||||
|
-- * eine Auswahlbild-Id ("husky-hoodie", "casper", …) — siehe
|
||||||
|
-- ERLAUBTE_AVATARE in server-internal/routes/testimonials.js und
|
||||||
|
-- assets/js/data-stimmen-avatare.js
|
||||||
|
-- * ODER eine vollständige Adresse eines von der Verwaltung
|
||||||
|
-- hochgeladenen eigenen Fotos
|
||||||
|
-- * ODER NULL = kein Bild, dann bleibt es beim Anfangsbuchstaben
|
||||||
|
--
|
||||||
|
-- Bewusst nur EINE Spalte statt zweier (Auswahlbild/eigenes Foto): es kann
|
||||||
|
-- ohnehin immer nur eines von beiden gleichzeitig gelten, zwei Spalten
|
||||||
|
-- hätten nur die Frage aufgeworfen, welche gewinnt.
|
||||||
|
-- =====================================================================
|
||||||
|
ALTER TABLE testimonials ADD COLUMN avatar TEXT;
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Team Dogi ist Familie — die Community rund um DogFather und HasiDog." />
|
<meta name="twitter:description" content="Team Dogi ist Familie — die Community rund um DogFather und HasiDog." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-community.jpg');--page-bg-mobile:url('/assets/img/bg-community-mobile.jpg');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-community.jpg');--page-bg-mobile:url('/assets/img/bg-community-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -45,8 +45,8 @@
|
|||||||
<p data-i18n="com_mehr_2">Discord ist das Community-Zentrum — hier trifft man sich, tauscht sich aus und ist Teil des Alltags von Team Dogi.</p>
|
<p data-i18n="com_mehr_2">Discord ist das Community-Zentrum — hier trifft man sich, tauscht sich aus und ist Teil des Alltags von Team Dogi.</p>
|
||||||
<a class="btn btn-primary" href="links.html" data-i18n="com_discord_btn">Discord beitreten</a>
|
<a class="btn btn-primary" href="links.html" data-i18n="com_discord_btn">Discord beitreten</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="img-placeholder" style="aspect-ratio:4/3;">
|
<div class="collage-photo" style="aspect-ratio:3/4;border-radius:var(--radius);">
|
||||||
<span data-i18n="com_bild_platzhalter">Community-Bild</span><br><span class="small" data-i18n="com_foto_folgt">Foto folgt</span>
|
<img src="assets/img/community-team-dogi.jpg" alt="Diene, Ghost, VanVan und Dogi bei einem gemeinsamen Treffen von Team Dogi" style="width:100%;height:100%;object-fit:cover;object-position:center 25%;" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-community.js"></script>
|
<script src="assets/js/i18n-community.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Content Creator, die Teil von DogFather / Spicy Media sind." />
|
<meta name="twitter:description" content="Content Creator, die Teil von DogFather / Spicy Media sind." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-creator.jpg');--page-bg-mobile:url('/assets/img/bg-creator-mobile.jpg');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-creator.jpg');--page-bg-mobile:url('/assets/img/bg-creator-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<div class="hero-banner"><img src="/assets/img/bg-creator.jpg" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-creator.jpg" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="crt_eyebrow">Manager DogFather — Team</span>
|
<span class="eyebrow" data-i18n="crt_eyebrow">Manager DogFather — Team</span>
|
||||||
<h1 data-i18n="crt_h1">Creator im Team</h1>
|
<h1 data-i18n="crt_h1" data-site-text-key="seitentitel_creator">Creator im Team</h1>
|
||||||
<p class="lead worlds" data-i18n="crt_lead">Content Creator, die bei DogFather / Spicy Media mitmachen.</p>
|
<p class="lead worlds" data-i18n="crt_lead">Content Creator, die bei DogFather / Spicy Media mitmachen.</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
<span data-i18n="crt_empty">Noch keine Creator-Profile veröffentlicht — bald verfügbar. 🩵</span>
|
<span data-i18n="crt_empty">Noch keine Creator-Profile veröffentlicht — bald verfügbar. 🩵</span>
|
||||||
<span data-i18n="crt_filter_alle">alle</span>
|
<span data-i18n="crt_filter_alle">alle</span>
|
||||||
<span data-i18n="crt_profil_btn">Profil ansehen</span>
|
<span data-i18n="crt_profil_btn">Profil ansehen</span>
|
||||||
|
<span data-i18n="crt_kategorie_sonstige">Sonstige</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO: Noch keine Creator-Profile final geklärt. Sobald Namen/Infos feststehen, in assets/js/data-creator.js ergänzen — diese Seite rendert automatisch. -->
|
<!-- TODO: Noch keine Creator-Profile final geklärt. Sobald Namen/Infos feststehen, in assets/js/data-creator.js ergänzen — diese Seite rendert automatisch. -->
|
||||||
<div class="btn-row" style="justify-content:center;">
|
<div class="btn-row" style="justify-content:center;">
|
||||||
@@ -59,14 +60,29 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-creator.js"></script>
|
<script src="assets/js/data-creator.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-creator.js"></script>
|
<script src="assets/js/i18n-creator.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
const grid = document.getElementById("creator-grid");
|
const grid = document.getElementById("creator-grid");
|
||||||
const filterBar = document.getElementById("creator-filters");
|
const filterBar = document.getElementById("creator-filters");
|
||||||
const list = window.CREATORS || [];
|
let list = window.CREATORS || [];
|
||||||
|
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Creator ergänzen, BEVOR
|
||||||
|
// entschieden wird, ob die Seite "leer" ist (Nutzer-Wunsch "überall wo
|
||||||
|
// ... Creator ... sind, will ich dass ich die easy über meine
|
||||||
|
// Verwaltungsseite hinzufüge"). Vorher gab es direkt darunter ein
|
||||||
|
// "return", sobald window.CREATORS leer war (bisher der Normalfall, da
|
||||||
|
// noch keine echten Creator-Profile feststehen) -- dadurch wären
|
||||||
|
// admin-gepflegte Creator NIE geladen worden.
|
||||||
|
if (window.dogiTeamLaden) {
|
||||||
|
const neue = await window.dogiTeamLaden("creator");
|
||||||
|
// Runde 2 (21.08.2026, "wenn ich eine änderung mache ist es auch für
|
||||||
|
// jeden"): überschreibt bereits importierte Creator (gleicher slug)
|
||||||
|
// statt sie zu duplizieren.
|
||||||
|
list = window.dogiTeamZusammenfuehren(list, neue);
|
||||||
|
}
|
||||||
|
|
||||||
// Texte fürs JS-generierte Grid/Filter kommen aus den versteckten
|
// Texte fürs JS-generierte Grid/Filter kommen aus den versteckten
|
||||||
// data-i18n-Elementen oben — main.js übersetzt sie ganz normal (auch bei
|
// data-i18n-Elementen oben — main.js übersetzt sie ganz normal (auch bei
|
||||||
@@ -88,6 +104,11 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Creator haben (noch) kein
|
||||||
|
// eigenes Genre-Feld (category) -- ohne diesen Fallback würde die
|
||||||
|
// Filterleiste einen kaputten "undefined"-Knopf zeigen.
|
||||||
|
list = list.map((c) => (c.category ? c : { ...c, category: uebersetzen("crt_kategorie_sonstige") }));
|
||||||
|
|
||||||
const categories = ["alle", ...new Set(list.map((c) => c.category))];
|
const categories = ["alle", ...new Set(list.map((c) => c.category))];
|
||||||
let aktiverFilter = "alle";
|
let aktiverFilter = "alle";
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Wer ist DogFather wirklich? Persönlichkeit, Werte und die Geschichte hinter dem Content." />
|
<meta name="twitter:description" content="Wer ist DogFather wirklich? Persönlichkeit, Werte und die Geschichte hinter dem Content." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-dogfather-person.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-person-mobile.jpg');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-dogfather-person.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-person-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-dogfather.js"></script>
|
<script src="assets/js/i18n-dogfather.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="DogFather" />
|
<meta name="apple-mobile-web-app-title" content="DogFather" />
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<style>
|
<style>
|
||||||
/* =====================================================================
|
/* =====================================================================
|
||||||
Zugangsseite — bewusst kein braves Standard-Login-Formular, sondern
|
Zugangsseite — bewusst kein braves Standard-Login-Formular, sondern
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="HasiDog: Maskottchen, Musikprojekt und die verspielte, familiäre Seite von Team Dogi." />
|
<meta name="twitter:description" content="HasiDog: Maskottchen, Musikprojekt und die verspielte, familiäre Seite von Team Dogi." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-hasidog-mobile.jpg');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-hasidog-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -56,6 +56,10 @@
|
|||||||
<div class="container grid grid-2" style="align-items:center;">
|
<div class="container grid grid-2" style="align-items:center;">
|
||||||
<div>
|
<div>
|
||||||
<h2 data-i18n="hd_welt_h2">🐰 HasiDog – Eine eigene Welt voller Chaos, Herz und Fantasie</h2>
|
<h2 data-i18n="hd_welt_h2">🐰 HasiDog – Eine eigene Welt voller Chaos, Herz und Fantasie</h2>
|
||||||
|
<!-- Eigener TikTok-Account von HasiDog (Nutzer-Wunsch 20.08.2026) -->
|
||||||
|
<p style="margin:.2rem 0 1.2rem;">
|
||||||
|
<a class="btn-tiktok" href="https://www.tiktok.com/@hasidog0804" target="_blank" rel="noopener"><span class="btn-tiktok-note" aria-hidden="true">♪</span><span class="btn-tiktok-text" data-i18n="hd_btn_tiktok">Zur HasiDog TikTok-Seite</span></a>
|
||||||
|
</p>
|
||||||
<p data-i18n="hd_welt_1">HasiDog ist mehr als nur eine Figur. Hinter HasiDog steckt eine eigene Welt aus Humor, Musik, Geschichten, Überraschungen und liebenswertem Chaos. 🩵</p>
|
<p data-i18n="hd_welt_1">HasiDog ist mehr als nur eine Figur. Hinter HasiDog steckt eine eigene Welt aus Humor, Musik, Geschichten, Überraschungen und liebenswertem Chaos. 🩵</p>
|
||||||
<p data-i18n="hd_welt_2">Hier treffen freche Ideen auf große Gefühle, verrückte Momente auf echte Freundschaft und Fantasie auf Abenteuer. HasiDog steht vollkommen für sich – mit einem eigenen Charakter, einer eigenen Geschichte und einer eigenen Familie.</p>
|
<p data-i18n="hd_welt_2">Hier treffen freche Ideen auf große Gefühle, verrückte Momente auf echte Freundschaft und Fantasie auf Abenteuer. HasiDog steht vollkommen für sich – mit einem eigenen Charakter, einer eigenen Geschichte und einer eigenen Familie.</p>
|
||||||
<p class="quote" data-i18n="hd_welt_quote" style="margin-top:1.4rem;">Frech im Kopf. Chaotisch im Herzen. Einzigartig in jeder Geschichte.</p>
|
<p class="quote" data-i18n="hd_welt_quote" style="margin-top:1.4rem;">Frech im Kopf. Chaotisch im Herzen. Einzigartig in jeder Geschichte.</p>
|
||||||
@@ -182,7 +186,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-hasidog.js"></script>
|
<script src="assets/js/i18n-hasidog.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="DOGFATHER UNIVERSE: die Heimat von DogFather als Streamer, der HasiDog-Community und Manager DogFather bei Spicy Media." />
|
<meta name="twitter:description" content="DOGFATHER UNIVERSE: die Heimat von DogFather als Streamer, der HasiDog-Community und Manager DogFather bei Spicy Media." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="start-seite">
|
<body class="start-seite">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -81,9 +81,57 @@
|
|||||||
<p data-i18n="idx_hero_card3_desc">Als Manager bei Spicy Media: Scouts, Creator und faire Zusammenarbeit auf Augenhöhe.</p>
|
<p data-i18n="idx_hero_card3_desc">Als Manager bei Spicy Media: Scouts, Creator und faire Zusammenarbeit auf Augenhöhe.</p>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- "Event des Jahres": volle Breite wie die 3 Welt-Kacheln zusammen. Seit
|
||||||
|
20.08.2026 (Nutzer-Wunsch) NUR sichtbar, wenn Dogi/VanVan mindestens ein
|
||||||
|
Event in der Verwaltung ausgefüllt UND aktiviert haben -- kein
|
||||||
|
Standard-/Platzhalterinhalt mehr, kompletter Abschnitt bleibt bis dahin
|
||||||
|
unsichtbar (siehe zeigeJahresEvents() weiter unten). Klick öffnet ein
|
||||||
|
großes Detail-Fenster (Bild/Titel/Text größer, optionaler direkter Link)
|
||||||
|
statt sofort wegzunavigieren. -->
|
||||||
|
<div class="jahres-events" id="jahres-events" hidden>
|
||||||
|
<div class="jahres-events-head">
|
||||||
|
<span class="tag" data-i18n="idx_jahres_events_tag">🏆 Event des Jahres</span>
|
||||||
|
</div>
|
||||||
|
<div class="jahres-events-grid" id="jahres-events-grid">
|
||||||
|
<button type="button" class="jahres-event-card" id="jahres-event-1" data-slot="1" hidden>
|
||||||
|
<span class="jahres-event-shine" aria-hidden="true"></span>
|
||||||
|
<img class="jahres-event-bild" alt="" hidden />
|
||||||
|
<span class="jahres-event-datum"></span>
|
||||||
|
<h3></h3>
|
||||||
|
<p></p>
|
||||||
|
<span class="jahres-event-mehr" data-i18n="idx_jahres_events_mehr">Mehr erfahren →</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="jahres-event-card" id="jahres-event-2" data-slot="2" hidden>
|
||||||
|
<span class="jahres-event-shine" aria-hidden="true"></span>
|
||||||
|
<img class="jahres-event-bild" alt="" hidden />
|
||||||
|
<span class="jahres-event-datum"></span>
|
||||||
|
<h3></h3>
|
||||||
|
<p></p>
|
||||||
|
<span class="jahres-event-mehr" data-i18n="idx_jahres_events_mehr">Mehr erfahren →</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Detail-Fenster für "Event des Jahres" -- bewusst außerhalb der Hero-Sektion
|
||||||
|
(die hat eigene transform-/overflow-Effekte für die Polarlicht-/Nebel-
|
||||||
|
Animation, die ein position:fixed-Fenster aus der Zentrierung bringen
|
||||||
|
könnten). -->
|
||||||
|
<div class="jahres-event-modal-overlay" id="jahres-event-modal-overlay" hidden>
|
||||||
|
<div class="jahres-event-modal" role="dialog" aria-modal="true" aria-labelledby="jahres-event-modal-titel">
|
||||||
|
<button type="button" class="jahres-event-modal-close" id="jahres-event-modal-close" aria-label="Schließen">✕</button>
|
||||||
|
<img class="jahres-event-modal-bild" id="jahres-event-modal-bild" alt="" hidden />
|
||||||
|
<div class="jahres-event-modal-body">
|
||||||
|
<span class="jahres-event-modal-datum" id="jahres-event-modal-datum"></span>
|
||||||
|
<h2 id="jahres-event-modal-titel"></h2>
|
||||||
|
<p id="jahres-event-modal-text"></p>
|
||||||
|
<a class="btn btn-primary jahres-event-modal-link" id="jahres-event-modal-link" href="#" target="_blank" rel="noopener" hidden></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ================= CHARAKTERE: DOGFATHER-HUSKY & HASIDOG-HASE ================= -->
|
<!-- ================= CHARAKTERE: DOGFATHER-HUSKY & HASIDOG-HASE ================= -->
|
||||||
<section class="section-tight">
|
<section class="section-tight">
|
||||||
<div class="container text-center">
|
<div class="container text-center">
|
||||||
@@ -180,23 +228,20 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="text-center" data-i18n="idx_team_h2">Team Dogi 🩵</h2>
|
<h2 class="text-center" data-i18n="idx_team_h2">Team Dogi 🩵</h2>
|
||||||
<p class="lead text-center" data-i18n="idx_team_lead" style="max-width:60ch;margin:0 auto;">VanVan & Modis — die Menschen, die DOGFATHER UNIVERSE tragen.</p>
|
<p class="lead text-center" data-i18n="idx_team_lead" style="max-width:60ch;margin:0 auto;">VanVan & Modis — die Menschen, die DOGFATHER UNIVERSE tragen.</p>
|
||||||
<div id="team-preview" class="grid grid-4"></div>
|
<!-- Nutzer-Wunsch 21.08.2026: die vier Personen-Kacheln (VanVan/Diene/
|
||||||
|
Funny/Miss) hier auf der Startseite entfernt -- Überschrift, Text
|
||||||
|
und der Knopf zum Modi-Team bleiben. Die Kacheln selbst gibt es
|
||||||
|
weiterhin vollständig auf team-modis.html. -->
|
||||||
<div class="btn-row" style="justify-content:center;">
|
<div class="btn-row" style="justify-content:center;">
|
||||||
<a class="btn btn-outline" href="team-modis.html" data-i18n="idx_team_btn_modi">Modi-Team ansehen</a>
|
<a class="btn btn-outline" href="team-modis.html" data-i18n="idx_team_btn_modi">Modi-Team ansehen</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ================= AKTIONEN TEASER ================= -->
|
<!-- Nutzer-Wunsch 21.08.2026: der "Was gerade läuft"-Aktionen-Teaser
|
||||||
<section>
|
(Überschrift + Button "Alle Aktionen & Projekte", direkt hier davor)
|
||||||
<div class="container">
|
wurde entfernt. data-aktionen.js/aktion-detail.html bleiben unangetastet,
|
||||||
<h2 class="text-center" data-i18n="idx_aktionen_h2">Was gerade läuft</h2>
|
nur die Startseiten-Vorschau ist weg. -->
|
||||||
<div id="aktionen-teaser" class="grid grid-3"></div>
|
|
||||||
<div class="btn-row" style="justify-content:center;">
|
|
||||||
<a class="btn btn-outline" href="medien-dogfather.html#events" data-i18n="idx_aktionen_btn">Alle Aktionen & Projekte</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- ================= GALERIE TEASER ================= -->
|
<!-- ================= GALERIE TEASER ================= -->
|
||||||
<section class="section-tight">
|
<section class="section-tight">
|
||||||
@@ -245,11 +290,10 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-modis.js"></script>
|
<script src="assets/js/data-modis.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-scouts.js"></script>
|
<script src="assets/js/data-scouts.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-aktionen.js"></script>
|
<script src="assets/js/i18n-index.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-index.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
// Gleiche URL wie in assets/js/forms.js / postfach.html — läuft seit
|
// Gleiche URL wie in assets/js/forms.js / postfach.html — läuft seit
|
||||||
// 05.08.2026 auf dem eigenen Server statt auf Cloudflare Workers.
|
// 05.08.2026 auf dem eigenen Server statt auf Cloudflare Workers.
|
||||||
@@ -331,46 +375,129 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* "Event des Jahres": seit 20.08.2026 in der Verwaltung pflegbar (Bild,
|
||||||
|
Text, Link, Aktiv-Schalter je Slot, siehe verwaltung.html +
|
||||||
|
server-internal/routes/events.js). Nutzer-Wunsch 20.08.2026: "die
|
||||||
|
kisten sollen leer sein solang wie ich nichts rein setze, am besten
|
||||||
|
sollen die sogar immer nur erscheinen wenn ich was rein setze und sie
|
||||||
|
aktiviere" — der Server liefert über /events-of-year ohnehin NUR
|
||||||
|
aktivierte Slots (inaktive/leere kommen als null), hier also einfach:
|
||||||
|
kein Eintrag -> Kachel bleibt versteckt, kompletter Abschnitt bleibt
|
||||||
|
versteckt, wenn beide Slots leer sind. Klick öffnet ein großes
|
||||||
|
Detail-Fenster statt sofort zu navigieren. */
|
||||||
|
const feldJahresEvent = (x) => (window.dogiFeld ? window.dogiFeld(x) : x || "");
|
||||||
|
let jahresEventsDaten = [null, null];
|
||||||
|
|
||||||
|
function zeigeJahresEventKachel(slot, daten) {
|
||||||
|
const karte = document.getElementById(`jahres-event-${slot}`);
|
||||||
|
if (!karte) return;
|
||||||
|
if (!daten) {
|
||||||
|
karte.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bild = karte.querySelector(".jahres-event-bild");
|
||||||
|
if (daten.bild) {
|
||||||
|
bild.src = daten.bild;
|
||||||
|
bild.alt = feldJahresEvent(daten.titel);
|
||||||
|
bild.hidden = false;
|
||||||
|
} else {
|
||||||
|
bild.hidden = true;
|
||||||
|
}
|
||||||
|
karte.querySelector(".jahres-event-datum").textContent = daten.datum || "";
|
||||||
|
karte.querySelector("h3").textContent = feldJahresEvent(daten.titel);
|
||||||
|
karte.querySelector("p").textContent = feldJahresEvent(daten.text);
|
||||||
|
karte.hidden = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function aktualisiereJahresEventsSichtbarkeit() {
|
||||||
|
const aktiveSlots = jahresEventsDaten.filter(Boolean).length;
|
||||||
|
const abschnitt = document.getElementById("jahres-events");
|
||||||
|
const grid = document.getElementById("jahres-events-grid");
|
||||||
|
abschnitt.hidden = aktiveSlots === 0;
|
||||||
|
// Genau 1 aktives Event: Kachel zentriert und etwas größer statt halb
|
||||||
|
// leerem Raster (Nutzer-Wunsch: "kacheln sollen bissl spezieller sein").
|
||||||
|
grid.classList.toggle("jahres-events-grid-einzeln", aktiveSlots === 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function jahresEventModalOeffnen(slot) {
|
||||||
|
const daten = jahresEventsDaten[slot - 1];
|
||||||
|
if (!daten) return;
|
||||||
|
const bild = document.getElementById("jahres-event-modal-bild");
|
||||||
|
if (daten.bild) {
|
||||||
|
bild.src = daten.bild;
|
||||||
|
bild.alt = feldJahresEvent(daten.titel);
|
||||||
|
bild.hidden = false;
|
||||||
|
} else {
|
||||||
|
bild.hidden = true;
|
||||||
|
}
|
||||||
|
document.getElementById("jahres-event-modal-datum").textContent = daten.datum || "";
|
||||||
|
document.getElementById("jahres-event-modal-titel").textContent = feldJahresEvent(daten.titel);
|
||||||
|
document.getElementById("jahres-event-modal-text").textContent = feldJahresEvent(daten.text);
|
||||||
|
const linkEl = document.getElementById("jahres-event-modal-link");
|
||||||
|
if (daten.link) {
|
||||||
|
linkEl.href = daten.link;
|
||||||
|
linkEl.textContent = feldJahresEvent(window.I18N_PAGE?.idx_jahres_event_modal_link) || "Direkt ansehen →";
|
||||||
|
linkEl.hidden = false;
|
||||||
|
} else {
|
||||||
|
linkEl.hidden = true;
|
||||||
|
}
|
||||||
|
const overlay = document.getElementById("jahres-event-modal-overlay");
|
||||||
|
overlay.hidden = false;
|
||||||
|
requestAnimationFrame(() => overlay.classList.add("offen"));
|
||||||
|
document.body.classList.add("modal-offen");
|
||||||
|
}
|
||||||
|
|
||||||
|
function jahresEventModalSchliessen() {
|
||||||
|
const overlay = document.getElementById("jahres-event-modal-overlay");
|
||||||
|
overlay.classList.remove("offen");
|
||||||
|
document.body.classList.remove("modal-offen");
|
||||||
|
setTimeout(() => { overlay.hidden = true; }, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("jahres-event-1")?.addEventListener("click", () => jahresEventModalOeffnen(1));
|
||||||
|
document.getElementById("jahres-event-2")?.addEventListener("click", () => jahresEventModalOeffnen(2));
|
||||||
|
document.getElementById("jahres-event-modal-close")?.addEventListener("click", jahresEventModalSchliessen);
|
||||||
|
document.getElementById("jahres-event-modal-overlay")?.addEventListener("click", (e) => {
|
||||||
|
if (e.target.id === "jahres-event-modal-overlay") jahresEventModalSchliessen();
|
||||||
|
});
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Escape" && !document.getElementById("jahres-event-modal-overlay").hidden) jahresEventModalSchliessen();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (API_BASE_URL) {
|
||||||
|
const ladeJahresEvents = () =>
|
||||||
|
fetch(`${API_BASE_URL}/events-of-year`, { cache: "no-store" })
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => {
|
||||||
|
if (!data?.ok) return;
|
||||||
|
jahresEventsDaten = data.events;
|
||||||
|
zeigeJahresEventKachel(1, data.events[0]);
|
||||||
|
zeigeJahresEventKachel(2, data.events[1]);
|
||||||
|
aktualisiereJahresEventsSichtbarkeit();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
/* Kein Fehler nötig -- der Abschnitt bleibt einfach unsichtbar. */
|
||||||
|
});
|
||||||
|
ladeJahresEvents();
|
||||||
|
// Bei Sprachwechsel mit den zuletzt geladenen Daten neu rendern (kein
|
||||||
|
// erneuter Server-Aufruf nötig) — Titel/Text stehen ja bereits in allen
|
||||||
|
// Sprachen im geladenen Objekt.
|
||||||
|
document.addEventListener("dogi-sprache-geaendert", () => {
|
||||||
|
zeigeJahresEventKachel(1, jahresEventsDaten[0]);
|
||||||
|
zeigeJahresEventKachel(2, jahresEventsDaten[1]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Stats: Team-Größe aus den Datenobjekten berechnen
|
// Stats: Team-Größe aus den Datenobjekten berechnen
|
||||||
const teamCount = (window.MODIS || []).length + (window.SCOUTS || []).length;
|
const teamCount = (window.MODIS || []).length + (window.SCOUTS || []).length;
|
||||||
const statTeam = document.getElementById("stat-team");
|
const statTeam = document.getElementById("stat-team");
|
||||||
if (statTeam) statTeam.textContent = teamCount > 0 ? `${teamCount}+` : "–";
|
if (statTeam) statTeam.textContent = teamCount > 0 ? `${teamCount}+` : "–";
|
||||||
|
|
||||||
// Team-Vorschau: VanVan zuerst, dann die nächsten nach Rang, plus 1-2 Scouts
|
// Nutzer-Wunsch 21.08.2026: die Team-Vorschau (vier Personen-Kacheln unter
|
||||||
const teamPreview = document.getElementById("team-preview");
|
// "Team Dogi 🩵") wurde entfernt -- der zugehörige Render-Code stand hier.
|
||||||
if (teamPreview && window.MODIS) {
|
// data-modis.js/data-scouts.js bleiben eingebunden, weil die Statistik-Zeile
|
||||||
const feld = (x) => (window.dogiFeld ? window.dogiFeld(x) : x || "");
|
// weiter oben ("Menschen im Team Dogi") sie weiterhin auszählt.
|
||||||
const people = [...window.MODIS].sort((a, b) => a.rank - b.rank).slice(0, 4);
|
|
||||||
const renderTeamPreview = () => {
|
|
||||||
teamPreview.innerHTML = people
|
|
||||||
.map(
|
|
||||||
(p) => `
|
|
||||||
<a class="profile-card" href="${p.page || `profil.html?p=${p.slug}`}">
|
|
||||||
<div class="avatar">${(p.avatar || p.photo) ? `<img src="${p.avatar || p.photo}" alt="${p.name}" loading="lazy">` : `<span class="avatar-initial">${p.name.charAt(0).toUpperCase()}</span>`}</div>
|
|
||||||
<h3>${p.name}</h3>
|
|
||||||
<div class="role">${feld(p.role)}</div>
|
|
||||||
</a>`
|
|
||||||
)
|
|
||||||
.join("");
|
|
||||||
};
|
|
||||||
renderTeamPreview();
|
|
||||||
document.addEventListener("dogi-sprache-geaendert", renderTeamPreview);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Aktionen-Teaser: die ersten 3 Einträge aus data-aktionen.js
|
|
||||||
const aktionenTeaser = document.getElementById("aktionen-teaser");
|
|
||||||
if (aktionenTeaser && window.AKTIONEN) {
|
|
||||||
aktionenTeaser.innerHTML = window.AKTIONEN.slice(0, 3)
|
|
||||||
.map(
|
|
||||||
(a) => `
|
|
||||||
<a class="card" href="aktion-detail.html?id=${a.id}">
|
|
||||||
<span class="tag">${a.tag} · ${a.status}</span>
|
|
||||||
<h3>${a.title}</h3>
|
|
||||||
<p>${a.summary}</p>
|
|
||||||
</a>`
|
|
||||||
)
|
|
||||||
.join("");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Kontakt, Impressum und Datenschutz von DOGFATHER UNIVERSE." />
|
<meta name="twitter:description" content="Kontakt, Impressum und Datenschutz von DOGFATHER UNIVERSE." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-kontakt.js"></script>
|
<script src="assets/js/i18n-kontakt.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Alle Social-Media- und Musik-Links von DogFather an einem Ort — plus QR-Codes." />
|
<meta name="twitter:description" content="Alle Social-Media- und Musik-Links von DogFather an einem Ort — plus QR-Codes." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
<style>
|
<style>
|
||||||
/* Rabattcodes: an das Supporter-Abo gekoppelt (Nutzer-Feedback
|
/* Rabattcodes: an das Supporter-Abo gekoppelt (Nutzer-Feedback
|
||||||
03.08.2026: "verbinde es mit dem Abo... wenn die Leute drauf drücken
|
03.08.2026: "verbinde es mit dem Abo... wenn die Leute drauf drücken
|
||||||
@@ -46,12 +46,12 @@
|
|||||||
.rabatt-gesperrt-hinweis p { color: #ffe9a8; margin: 0; flex: 1 1 260px; font-size: .95rem; }
|
.rabatt-gesperrt-hinweis p { color: #ffe9a8; margin: 0; flex: 1 1 260px; font-size: .95rem; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-links.jpg');--page-bg-mobile:url('/assets/img/bg-links-mobile.jpg');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-links-seite.jpg');--page-bg-mobile:url('/assets/img/bg-links-seite-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="hero page-bg">
|
<section class="hero page-bg">
|
||||||
<div class="hero-banner"><img src="/assets/img/bg-links.jpg" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-links-seite.jpg" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="lk_eyebrow">🩵 Alle Links</span>
|
<span class="eyebrow" data-i18n="lk_eyebrow">🩵 Alle Links</span>
|
||||||
<h1 data-i18n="lk_h1">Alles an einem Ort</h1>
|
<h1 data-i18n="lk_h1">Alles an einem Ort</h1>
|
||||||
@@ -140,8 +140,8 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-links.js"></script>
|
<script src="assets/js/i18n-links.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="DogFather als Manager bei der Spicy Media Agency: Scouts, Creator und faire Zusammenarbeit." />
|
<meta name="twitter:description" content="DogFather als Manager bei der Spicy Media Agency: Scouts, Creator und faire Zusammenarbeit." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-manager.jpg');--page-bg-mobile:url('/assets/img/bg-manager-mobile.jpg');">
|
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-manager.jpg');--page-bg-mobile:url('/assets/img/bg-manager-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -96,6 +96,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- "Weitere Manager" (21.08.2026, Nutzer-Wunsch: "überall wo ... Manager
|
||||||
|
... sind, will ich dass ich die easy über meine Verwaltungsseite
|
||||||
|
hinzufüge"). Aktuell gibt es nur DogFather selbst als Manager -- diese
|
||||||
|
Sektion bleibt deshalb komplett unsichtbar (hidden), bis über die
|
||||||
|
Verwaltung mindestens ein weiterer Manager angelegt wurde. Gleiches
|
||||||
|
flaches Karten-Raster wie team-scouts.html. -->
|
||||||
|
<section class="section-tight" id="weitere-manager-root" hidden>
|
||||||
|
<div class="container">
|
||||||
|
<h2 class="text-center" data-i18n="mgr_weitere_h2" data-site-text-key="seitentitel_manager">Weitere Manager</h2>
|
||||||
|
<div id="weitere-manager-grid" class="grid grid-4"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2 class="text-center" data-i18n="mgr_team_h2">Willst du dazugehören?</h2>
|
<h2 class="text-center" data-i18n="mgr_team_h2">Willst du dazugehören?</h2>
|
||||||
@@ -126,7 +139,54 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/i18n-manager.js"></script>
|
<script src="assets/js/data-manager.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/i18n-manager.js?v=20260821o"></script>
|
||||||
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
|
<script>
|
||||||
|
// "Weitere Manager" (21.08.2026) -- siehe Kommentar bei #weitere-manager-root
|
||||||
|
// oben. Aktuell IMMER window.MANAGERS === [] (siehe data-manager.js), Inhalt
|
||||||
|
// kommt also ausschließlich über die Verwaltung (dogiTeamLaden). Bleibt
|
||||||
|
// komplett unsichtbar, solange niemand eingetragen ist.
|
||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const root = document.getElementById("weitere-manager-root");
|
||||||
|
const grid = document.getElementById("weitere-manager-grid");
|
||||||
|
if (!root || !grid) return;
|
||||||
|
|
||||||
|
const feld = (x) => (window.dogiFeld ? window.dogiFeld(x) : x || "");
|
||||||
|
const t = (schluessel) => (window.dogiUebersetzen ? window.dogiUebersetzen(schluessel) : schluessel);
|
||||||
|
|
||||||
|
const renderWeitereManager = () => {
|
||||||
|
const liste = window.MANAGERS || [];
|
||||||
|
if (!liste.length) {
|
||||||
|
root.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
grid.innerHTML = liste.map((m) => {
|
||||||
|
const quoteText = feld(m.quote);
|
||||||
|
return `
|
||||||
|
<a class="profile-card" href="profil.html?p=${m.slug}">
|
||||||
|
<div class="avatar">${(m.avatar || m.photo) ? `<img src="${m.avatar || m.photo}" alt="${m.name}" loading="lazy">` : `<span class="avatar-initial">${m.name.charAt(0).toUpperCase()}</span>`}</div>
|
||||||
|
<h3>${m.name}</h3>
|
||||||
|
<div class="role">${feld(m.role)}</div>
|
||||||
|
${quoteText ? `<p class="quote">„${quoteText}"</p>` : ""}
|
||||||
|
<span class="btn btn-outline" style="margin-top:.7rem;pointer-events:none;">${t("mgr_weitere_profil_btn")}</span>
|
||||||
|
</a>`;
|
||||||
|
}).join("");
|
||||||
|
root.hidden = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("dogi-sprache-geaendert", renderWeitereManager);
|
||||||
|
|
||||||
|
if (window.dogiTeamLaden) {
|
||||||
|
window.dogiTeamLaden("manager").then((neue) => {
|
||||||
|
if (!neue.length) return;
|
||||||
|
// Runde 2 (21.08.2026, "wenn ich eine änderung mache ist es auch für
|
||||||
|
// jeden"): überschreibt bereits importierte Manager statt zu duplizieren.
|
||||||
|
window.MANAGERS = window.dogiTeamZusammenfuehren(window.MANAGERS || [], neue);
|
||||||
|
renderWeitereManager();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "DogiCrew-Verwaltung",
|
||||||
|
"short_name": "Verwaltung",
|
||||||
|
"description": "Interner Admin-Bereich: DogiCrew-Supporter, Prämien, Event des Jahres, Abstimmungen. Nur mit Zugangscode.",
|
||||||
|
"start_url": "/verwaltung.html",
|
||||||
|
"id": "/verwaltung.html",
|
||||||
|
"scope": "/verwaltung.html",
|
||||||
|
"display": "standalone",
|
||||||
|
"orientation": "any",
|
||||||
|
"background_color": "#0e0a16",
|
||||||
|
"theme_color": "#0e0a16",
|
||||||
|
"lang": "de",
|
||||||
|
"dir": "ltr",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/assets/img/icon-192-admin.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/img/icon-512-admin.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/img/icon-192-maskable-admin.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/assets/img/icon-512-maskable-admin.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -19,17 +19,17 @@
|
|||||||
<meta name="twitter:description" content="Fotos und Videos rund um Husky Casper an einem Ort." />
|
<meta name="twitter:description" content="Fotos und Videos rund um Husky Casper an einem Ort." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" data-welt="casper" style="--page-bg:url('/assets/img/bg-casper.jpg');--page-bg-mobile:url('/assets/img/bg-casper-mobile.jpg');">
|
<body class="mit-hintergrund" data-welt="casper" style="--page-bg:url('/assets/img/bg-medien-casper.jpg');--page-bg-mobile:url('/assets/img/bg-medien-casper-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="hero page-bg">
|
<section class="hero page-bg">
|
||||||
<div class="hero-banner"><img src="/assets/img/bg-casper.jpg" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-medien-casper.jpg" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="mc_eyebrow">Welt 2 — HasiDog</span>
|
<span class="eyebrow" data-i18n="mc_eyebrow">Welt 2 — HasiDog</span>
|
||||||
<h1 data-i18n="mc_h1">Medien — Casper</h1>
|
<h1 data-i18n="mc_h1">Medien — Casper</h1>
|
||||||
@@ -59,10 +59,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-media.js"></script>
|
<script src="assets/js/data-media.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-videos.js"></script>
|
<script src="assets/js/data-videos.js?v=20260821o"></script>
|
||||||
<script src="assets/js/medien-welt.js"></script>
|
<script src="assets/js/medien-welt.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-medien-casper.js"></script>
|
<script src="assets/js/i18n-medien-casper.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,10 +19,10 @@
|
|||||||
<meta name="twitter:description" content="Fotos, Videos und Events aus DogFathers Welt an einem Ort." />
|
<meta name="twitter:description" content="Fotos, Videos und Events aus DogFathers Welt an einem Ort." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" data-welt="dogfather" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
|
<body class="mit-hintergrund" data-welt="dogfather" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
@@ -65,11 +65,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-media.js"></script>
|
<script src="assets/js/data-media.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-videos.js"></script>
|
<script src="assets/js/data-videos.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-aktionen.js"></script>
|
<script src="assets/js/data-aktionen.js?v=20260821o"></script>
|
||||||
<script src="assets/js/medien-welt.js"></script>
|
<script src="assets/js/medien-welt.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-medien-dogfather.js"></script>
|
<script src="assets/js/i18n-medien-dogfather.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -19,17 +19,17 @@
|
|||||||
<meta name="twitter:description" content="Fotos, Videos und Events aus der HasiDog-Welt an einem Ort." />
|
<meta name="twitter:description" content="Fotos, Videos und Events aus der HasiDog-Welt an einem Ort." />
|
||||||
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
</head>
|
</head>
|
||||||
<body class="mit-hintergrund" data-welt="hasidog" style="--page-bg:url('/assets/img/bg-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-hasidog-mobile.jpg');">
|
<body class="mit-hintergrund" data-welt="hasidog" style="--page-bg:url('/assets/img/bg-medien-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-medien-hasidog-mobile.jpg');">
|
||||||
<div id="site-header"></div>
|
<div id="site-header"></div>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<section class="hero page-bg">
|
<section class="hero page-bg">
|
||||||
<div class="hero-banner"><img src="/assets/img/bg-hasidog.jpg" alt="" loading="eager" /></div>
|
<div class="hero-banner"><img src="/assets/img/bg-medien-hasidog.jpg" alt="" loading="eager" /></div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="eyebrow" data-i18n="mh_eyebrow">Welt 2 — HasiDog</span>
|
<span class="eyebrow" data-i18n="mh_eyebrow">Welt 2 — HasiDog</span>
|
||||||
<h1 data-i18n="mh_h1">Medien — HasiDog</h1>
|
<h1 data-i18n="mh_h1">Medien — HasiDog</h1>
|
||||||
@@ -65,11 +65,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-media.js"></script>
|
<script src="assets/js/data-media.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-videos.js"></script>
|
<script src="assets/js/data-videos.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-aktionen.js"></script>
|
<script src="assets/js/data-aktionen.js?v=20260821o"></script>
|
||||||
<script src="assets/js/medien-welt.js"></script>
|
<script src="assets/js/medien-welt.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-medien-hasidog.js"></script>
|
<script src="assets/js/i18n-medien-hasidog.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -16,10 +16,10 @@
|
|||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
<meta name="robots" content="noindex" /> <!-- generische URL, echte Team-Seiten verlinken hierher -->
|
<meta name="robots" content="noindex" /> <!-- generische URL, echte Team-Seiten verlinken hierher -->
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/css/main.css" />
|
<link rel="stylesheet" href="assets/css/main.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-hasidog.css" />
|
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821o" />
|
||||||
<link rel="stylesheet" href="assets/css/theme-spicymedia.css" />
|
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821o" />
|
||||||
<style>
|
<style>
|
||||||
.profil-photo { aspect-ratio: 4/5; max-width: 420px; margin: 0 auto; }
|
.profil-photo { aspect-ratio: 4/5; max-width: 420px; margin: 0 auto; }
|
||||||
.profil-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
.profil-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||||
@@ -130,18 +130,25 @@
|
|||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div id="site-footer"></div>
|
<div id="site-footer"></div>
|
||||||
<script src="assets/js/data-modis.js"></script>
|
<script src="assets/js/data-modis.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-scouts.js"></script>
|
<script src="assets/js/data-scouts.js?v=20260821o"></script>
|
||||||
<script src="assets/js/data-creator.js"></script>
|
<script src="assets/js/data-creator.js?v=20260821o"></script>
|
||||||
<script src="assets/js/i18n-profil.js"></script>
|
<script src="assets/js/data-manager.js?v=20260821o"></script>
|
||||||
<script src="assets/js/main.js"></script>
|
<script src="assets/js/i18n-profil.js?v=20260821o"></script>
|
||||||
|
<script src="assets/js/main.js?v=20260821o"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", async () => {
|
||||||
const slug = new URLSearchParams(location.search).get("p") || "";
|
const slug = new URLSearchParams(location.search).get("p") || "";
|
||||||
const modis = window.MODIS || [];
|
const modis = window.MODIS || [];
|
||||||
const scouts = window.SCOUTS || [];
|
const scouts = window.SCOUTS || [];
|
||||||
const creators = window.CREATORS || [];
|
const creators = window.CREATORS || [];
|
||||||
const alle = [...modis, ...scouts, ...creators];
|
const managers = window.MANAGERS || [];
|
||||||
|
// 21.08.2026: über die Verwaltung angelegte Mitglieder ALLER Kategorien
|
||||||
|
// dazuholen -- profil.html weiß vorab nicht, zu welcher Kategorie ein
|
||||||
|
// slug gehört, deshalb kommt hier (ohne Kategorie-Filter) einfach alles
|
||||||
|
// zusammen zurück und wird unten normal mitgesucht.
|
||||||
|
const admins = window.dogiTeamLaden ? await window.dogiTeamLaden() : [];
|
||||||
|
const alle = [...modis, ...scouts, ...creators, ...managers, ...admins];
|
||||||
const person = alle.find((p) => p.slug === slug);
|
const person = alle.find((p) => p.slug === slug);
|
||||||
|
|
||||||
// Für alle Rahmentexte, die rein per JS gesetzt werden (Name/Rolle/Bio
|
// Für alle Rahmentexte, die rein per JS gesetzt werden (Name/Rolle/Bio
|
||||||
@@ -170,16 +177,22 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const istScout = scouts.includes(person);
|
// 21.08.2026: admin-gepflegte Personen (aus dogiTeamLaden) stecken NICHT
|
||||||
const istCreator = creators.includes(person);
|
// in den statischen modis/scouts/creators/managers-Arrays selbst (die
|
||||||
const welt = istScout || istCreator ? "spicymedia" : "dogfather";
|
// reference-Prüfung .includes(person) würde für sie also fälschlich
|
||||||
|
// "false" liefern) -- deshalb zusätzlich über das von dogiTeamLaden
|
||||||
|
// gesetzte person.kategorie-Feld prüfen.
|
||||||
|
const istScout = scouts.includes(person) || person.kategorie === "scout";
|
||||||
|
const istCreator = creators.includes(person) || person.kategorie === "creator";
|
||||||
|
const istManager = managers.includes(person) || person.kategorie === "manager";
|
||||||
|
const welt = istScout || istCreator || istManager ? "spicymedia" : "dogfather";
|
||||||
document.documentElement.setAttribute("data-theme", welt);
|
document.documentElement.setAttribute("data-theme", welt);
|
||||||
|
|
||||||
// Passendes Weltmotiv nur noch als weichgezeichnete Stimmung ganz im
|
// Passendes Weltmotiv nur noch als weichgezeichnete Stimmung ganz im
|
||||||
// Hintergrund (body.mit-hintergrund) — bewusst KEIN scharfes Banner-Bild
|
// Hintergrund (body.mit-hintergrund) — bewusst KEIN scharfes Banner-Bild
|
||||||
// mehr oben im Kopfbereich, damit das Profil der Person (Foto/Name/Bio)
|
// mehr oben im Kopfbereich, damit das Profil der Person (Foto/Name/Bio)
|
||||||
// sofort beim Öffnen der Seite im Fokus steht, ohne Banner davor.
|
// sofort beim Öffnen der Seite im Fokus steht, ohne Banner davor.
|
||||||
const bgBasis = istScout || istCreator ? "bg-scouts" : "bg-dogfather";
|
const bgBasis = istScout || istCreator || istManager ? "bg-scouts" : "bg-dogfather";
|
||||||
document.body.style.setProperty("--page-bg", `url('/assets/img/${bgBasis}.jpg')`);
|
document.body.style.setProperty("--page-bg", `url('/assets/img/${bgBasis}.jpg')`);
|
||||||
document.body.style.setProperty("--page-bg-mobile", `url('/assets/img/${bgBasis}-mobile.jpg')`);
|
document.body.style.setProperty("--page-bg-mobile", `url('/assets/img/${bgBasis}-mobile.jpg')`);
|
||||||
|
|
||||||
@@ -241,6 +254,8 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
? t("pr_eyebrow_scout")
|
? t("pr_eyebrow_scout")
|
||||||
: istCreator
|
: istCreator
|
||||||
? t("pr_eyebrow_creator")
|
? t("pr_eyebrow_creator")
|
||||||
|
: istManager
|
||||||
|
? t("pr_eyebrow_manager")
|
||||||
: t("pr_eyebrow_default");
|
: t("pr_eyebrow_default");
|
||||||
|
|
||||||
document.getElementById("profil-hero-name").textContent = person.name;
|
document.getElementById("profil-hero-name").textContent = person.name;
|
||||||
@@ -286,6 +301,9 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
} else if (istCreator) {
|
} else if (istCreator) {
|
||||||
zurueck.href = "creator.html";
|
zurueck.href = "creator.html";
|
||||||
zurueck.textContent = t("pr_back_creator");
|
zurueck.textContent = t("pr_back_creator");
|
||||||
|
} else if (istManager) {
|
||||||
|
zurueck.href = "manager.html";
|
||||||
|
zurueck.textContent = t("pr_back_manager");
|
||||||
} else {
|
} else {
|
||||||
zurueck.href = "team-modis.html";
|
zurueck.href = "team-modis.html";
|
||||||
zurueck.textContent = t("pr_back_modis");
|
zurueck.textContent = t("pr_back_modis");
|
||||||
|
|||||||