Zweite Live-Uhr fuer "spezielles Live-Event" + mehr Farbe im Radar

Nutzer-Wunsch 20.08.2026: "wuensch mir nur noch bissl farbe und 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 fuer dieses spezielle event laeuft."

- Neue Verwaltungs-Sektion "Spezielles Live-Event": Titel (Deutsch, wird
  automatisch uebersetzt), echtes Datum+Uhrzeit, optionaler Link, Aktiv-
  Schalter -- nur mit Titel + Zukunftsdatum aktivierbar.
- Zweite Uhr auf streamplan.html (Magenta/Violett statt Cyan/Gold, direkt
  neben der bestehenden), nur sichtbar wenn ein Event aktiviert ist und
  das Zieldatum noch nicht vorbei ist. Echter Countdown inkl. Tage (z.B.
  "2T 03:14:59"), "Jetzt"-Zeiger zeigt wie bei Uhr 1 die tatsaechliche
  Uhrzeit, der magentafarbene Fixpunkt markiert die Tageszeit des Events.
  Zieldatum wird als UTC gespeichert -- jede besuchende Person sieht den
  exakt richtigen Countdown in ihrer eigenen Zeitzone.
- "Bissl Farbe": bunter Farbverlauf (Cyan/Violett/Gold) auf dem aeusseren
  Ring statt reinem Grauton, kraeftigerer Sweep-Farbschein.
- Backend: neue routes/special-event.js (getSpecialEventPublic nur bei
  aktiv+zukuenftig, getSpecialEventAdmin fuer Entwuerfe, saveSpecialEvent
  mit Validierung), 13 automatisierte Tests gegen eine Fake-DB bestanden.
- Bugfix unterwegs gefunden (Playwright-Screenshot, wiederholtes Muster
  auf dieser Seite): .stream-radar-wrap blieb trotz [hidden]-Attribut
  sichtbar (display:block ueberschreibt die eingebaute [hidden]-Regel bei
  gleicher Spezifitaet) -- explizite Regel ergaenzt, per Playwright erneut
  verifiziert (display:none bestaetigt).

Backend-Teil (server-internal/) noch ohne Deploy-Zugriff -- Dogi muss ihn
manuell auf dogiintern ausrollen, sonst bleibt die zweite Uhr unsichtbar.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-20 23:24:17 +02:00
co-authored by Claude Opus 5
parent 7fdceadb9e
commit 519e209bce
40 changed files with 763 additions and 369 deletions
+6 -6
View File
@@ -18,10 +18,10 @@
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<meta name="robots" content="noindex" />
<title>Seite nicht gefunden — DOGFATHER UNIVERSE</title>
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body>
<div id="site-header"></div>
@@ -41,7 +41,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-404.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-404.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+7 -7
View File
@@ -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:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
/* =====================================================================
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
@@ -454,9 +454,9 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-abonnieren.js?v=20260820i"></script>
<script src="assets/js/supporter.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-abonnieren.js?v=20260820j"></script>
<script src="assets/js/supporter.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
(function () {
const S = window.DogiSupporter;
+5 -5
View File
@@ -20,10 +20,10 @@
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<meta name="robots" content="noindex" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body>
<div id="site-header"></div>
@@ -134,6 +134,6 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+7 -7
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body>
<div id="site-header"></div>
@@ -51,9 +51,9 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/data-aktionen.js?v=20260820i"></script>
<script src="assets/js/i18n-aktion-detail.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/i18n-aktion-detail.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const params = new URLSearchParams(location.search);
+44 -4
View File
@@ -3591,7 +3591,23 @@ body.start-seite .music-toggle:focus-visible {
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. ================================================================= */
.stream-radar-wrap { display: flex; flex-direction: column; align-items: center; margin: 1.4rem auto 0; }
/* 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);
@@ -3603,15 +3619,28 @@ body.start-seite .music-toggle:focus-visible {
inset: -6%;
border-radius: 50%;
background: conic-gradient(from 0deg,
transparent 0deg, rgba(143, 217, 234, .16) 40deg, transparent 90deg,
transparent 260deg, rgba(139, 92, 246, .14) 300deg, transparent 340deg, transparent 360deg);
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; }
.radar-ring-outer { fill: none; stroke: rgba(255, 255, 255, .12); stroke-width: 1.5; }
/* 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; }
@@ -3670,6 +3699,17 @@ body.start-seite .music-toggle:focus-visible {
}
.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. */
+8
View File
@@ -50,6 +50,14 @@ window.I18N_PAGE = {
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_card1_tag: {
de: "Fixer Termin", "de-CH": "Fixe Termin",
en: "Fixed Time", fr: "Rendez-vous fixe", pt: "Horário Fixo",
+209 -110
View File
@@ -1,25 +1,24 @@
/* =====================================================================
streamplan.js — "Live-Radar" NUR für streamplan.html (20.08.2026,
Nutzer-Wunsch: "soll viel geiler und spezieller sein, überrasch mich").
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").
Ein 24-Stunden-Ziffernblatt (reines SVG, keine Bild-Assets nötig): der
feste 20-Uhr-Termin leuchtet dauerhaft als Fixpunkt, ein "Jetzt"-Zeiger
bewegt sich mit der echten Uhrzeit der besuchenden Person mit. Nutzt
denselben /live-status-Endpunkt wie die Startseite (main.js lädt
window.dogiUebersetzen/dogiSprache VOR dieser Datei, siehe <script>-
Reihenfolge in streamplan.html) — läuft DogFather laut TikTok gerade
wirklich, schaltet das ganze Radar auf Rot/Puls um und zeigt einen
"Jetzt anschauen"-Knopf, sonst zählt ein echter Sekunden-Countdown zum
nächsten lokalen 20-Uhr-Termin runter. ===================================================================== */
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 FIX_STUNDE = 20; // täglicher Fix-Termin, in der Ortszeit der besuchenden Person
const radar = document.getElementById("stream-radar");
if (!radar) return; // Datei ist nur auf streamplan.html eingebunden
const CX = 150, CY = 150, R_OUT = 130, R_TICK_IN = 116, R_TICK_MAJOR_IN = 106, R_HAND = 100;
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
@@ -27,105 +26,205 @@
return { x: CX + r * Math.cos(rad), y: CY + r * Math.sin(rad) };
}
// ---------- 24 Stundenstriche (alle 6 Std. etwas länger/heller) ----------
const ticksHost = document.getElementById("radar-ticks");
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);
}
// ---------- Fixmarkierung beim täglichen 20-Uhr-Termin ----------
const fixHost = document.getElementById("radar-fixmarker");
const fixPos = polar(R_OUT + 11, FIX_STUNDE);
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);
// ---------- "Jetzt"-Zeiger: bewegt sich mit der echten Uhrzeit mit ----------
const nowHand = document.getElementById("radar-now-hand");
const nowDot = document.getElementById("radar-now-dot");
function zeigerAktualisieren() {
const jetzt = new Date();
const stundenAnteil = jetzt.getHours() + jetzt.getMinutes() / 60 + jetzt.getSeconds() / 3600;
const spitze = polar(R_HAND, stundenAnteil);
nowHand.setAttribute("x2", spitze.x); nowHand.setAttribute("y2", spitze.y);
nowDot.setAttribute("cx", spitze.x); nowDot.setAttribute("cy", spitze.y);
}
zeigerAktualisieren();
setInterval(zeigerAktualisieren, 15000); // alle 15s reicht für eine sichtbare, ruhige Bewegung
// ---------- Countdown zum nächsten lokalen 20-Uhr-Termin ----------
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;
}
function zweistellig(n) { return String(n).padStart(2, "0"); }
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; // im Live-Zustand zeigt der Countdown-Platz die Uhrzeit/den Titel, siehe unten
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();
/** 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 liveStatusAnwenden(data) {
istLive = !!(data && data.live);
letzterTitel = data && data.title ? data.title : null;
radar.classList.toggle("ist-live", istLive);
texteNeuSetzen();
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 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();
});
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);
}
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");
let zielDatum = null;
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);
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();
});
}
})();
+7 -7
View File
@@ -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:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -70,8 +70,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/forms.js?v=20260820i"></script>
<script src="assets/js/i18n-bewerben-creator.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-creator.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+7 -7
View File
@@ -22,10 +22,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -68,8 +68,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/forms.js?v=20260820i"></script>
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+7 -7
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -79,8 +79,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/forms.js?v=20260820i"></script>
<script src="assets/js/i18n-bewerben-modi.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-modi.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+7 -7
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -71,8 +71,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/forms.js?v=20260820i"></script>
<script src="assets/js/i18n-bewerben-scout.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-scout.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+6 -6
View File
@@ -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:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -85,7 +85,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-bewerben.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-bewerben.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+6 -6
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<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>
@@ -85,7 +85,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-casper.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-casper.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+6 -6
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<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>
@@ -84,7 +84,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-community.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-community.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+7 -7
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<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>
@@ -59,9 +59,9 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/data-creator.js?v=20260820i"></script>
<script src="assets/js/i18n-creator.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-creator.js?v=20260820j"></script>
<script src="assets/js/i18n-creator.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const grid = document.getElementById("creator-grid");
+6 -6
View File
@@ -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:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -158,7 +158,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-dogfather.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-dogfather.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+2 -2
View File
@@ -26,8 +26,8 @@
<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-title" content="DogFather" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<style>
/* =====================================================================
Zugangsseite — bewusst kein braves Standard-Login-Formular, sondern
+6 -6
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<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>
@@ -182,7 +182,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-hasidog.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-hasidog.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+9 -9
View File
@@ -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:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="start-seite">
<div id="site-header"></div>
@@ -293,11 +293,11 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/data-modis.js?v=20260820i"></script>
<script src="assets/js/data-scouts.js?v=20260820i"></script>
<script src="assets/js/data-aktionen.js?v=20260820i"></script>
<script src="assets/js/i18n-index.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-modis.js?v=20260820j"></script>
<script src="assets/js/data-scouts.js?v=20260820j"></script>
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/i18n-index.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
// Gleiche URL wie in assets/js/forms.js / postfach.html — läuft seit
// 05.08.2026 auf dem eigenen Server statt auf Cloudflare Workers.
+6 -6
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body>
<div id="site-header"></div>
@@ -126,7 +126,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-kontakt.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-kontakt.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+6 -6
View File
@@ -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:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
/* Rabattcodes: an das Supporter-Abo gekoppelt (Nutzer-Feedback
03.08.2026: "verbinde es mit dem Abo... wenn die Leute drauf drücken
@@ -140,8 +140,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-links.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-links.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
(function () {
document.addEventListener("DOMContentLoaded", function () {
+6 -6
View File
@@ -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:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<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>
@@ -126,7 +126,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-manager.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-manager.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+9 -9
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -59,10 +59,10 @@
</div>
<div id="site-footer"></div>
<script src="assets/js/data-media.js?v=20260820i"></script>
<script src="assets/js/data-videos.js?v=20260820i"></script>
<script src="assets/js/medien-welt.js?v=20260820i"></script>
<script src="assets/js/i18n-medien-casper.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-media.js?v=20260820j"></script>
<script src="assets/js/data-videos.js?v=20260820j"></script>
<script src="assets/js/medien-welt.js?v=20260820j"></script>
<script src="assets/js/i18n-medien-casper.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+10 -10
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -65,11 +65,11 @@
</div>
<div id="site-footer"></div>
<script src="assets/js/data-media.js?v=20260820i"></script>
<script src="assets/js/data-videos.js?v=20260820i"></script>
<script src="assets/js/data-aktionen.js?v=20260820i"></script>
<script src="assets/js/medien-welt.js?v=20260820i"></script>
<script src="assets/js/i18n-medien-dogfather.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-media.js?v=20260820j"></script>
<script src="assets/js/data-videos.js?v=20260820j"></script>
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/medien-welt.js?v=20260820j"></script>
<script src="assets/js/i18n-medien-dogfather.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+10 -10
View File
@@ -19,10 +19,10 @@
<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" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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');">
<div id="site-header"></div>
@@ -65,11 +65,11 @@
</div>
<div id="site-footer"></div>
<script src="assets/js/data-media.js?v=20260820i"></script>
<script src="assets/js/data-videos.js?v=20260820i"></script>
<script src="assets/js/data-aktionen.js?v=20260820i"></script>
<script src="assets/js/medien-welt.js?v=20260820i"></script>
<script src="assets/js/i18n-medien-hasidog.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-media.js?v=20260820j"></script>
<script src="assets/js/data-videos.js?v=20260820j"></script>
<script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/medien-welt.js?v=20260820j"></script>
<script src="assets/js/i18n-medien-hasidog.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+9 -9
View File
@@ -16,10 +16,10 @@
<meta name="twitter:card" content="summary_large_image" />
<meta name="robots" content="noindex" /> <!-- generische URL, echte Team-Seiten verlinken hierher -->
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
.profil-photo { aspect-ratio: 4/5; max-width: 420px; margin: 0 auto; }
.profil-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
@@ -130,11 +130,11 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/data-modis.js?v=20260820i"></script>
<script src="assets/js/data-scouts.js?v=20260820i"></script>
<script src="assets/js/data-creator.js?v=20260820i"></script>
<script src="assets/js/i18n-profil.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-modis.js?v=20260820j"></script>
<script src="assets/js/data-scouts.js?v=20260820j"></script>
<script src="assets/js/data-creator.js?v=20260820j"></script>
<script src="assets/js/i18n-profil.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const slug = new URLSearchParams(location.search).get("p") || "";
+6 -6
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="Rabattcodes von Dogis Partnern für Team Dogi — spar bares Geld bei ausgewählten Marken." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
/* Leere-Zustand-Kachel im "Gutschein"-Look: gestrichelter Rand wie ein
ausgeschnittener Coupon, damit auch OHNE echte Codes schon klar ist,
@@ -131,8 +131,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-rabattcodes.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-rabattcodes.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
(function () {
document.addEventListener("DOMContentLoaded", function () {
+6
View File
@@ -64,6 +64,7 @@ import {
listSupporters, getSupporterDetail, updatePremium, listCollections, upsertCollection, getAdminOverview,
} from "./routes/supporter-admin.js";
import { getEventsPublic, getEventsAdmin, saveEvent, uploadEventImage } from "./routes/events.js";
import { getSpecialEventPublic, getSpecialEventAdmin, saveSpecialEvent } from "./routes/special-event.js";
import {
createPoll, listPolls, closePoll, reopenPoll, deletePoll, getActivePollPublic, voteOnPoll,
} from "./routes/polls.js";
@@ -157,6 +158,7 @@ app.post("/submit", wrap(submitApplication));
app.get("/live-status", wrap(handleLiveStatusGet));
app.post("/auth/login", wrap(login));
app.get("/events-of-year", wrap(getEventsPublic));
app.get("/special-event", wrap(getSpecialEventPublic));
app.post("/supporter/register", wrap(registerSupporter));
app.post("/supporter/verify-email", wrap(verifySupporterEmail));
@@ -194,6 +196,10 @@ app.post("/admin/events/list", requireTeamSession, withSession(getEventsAdmin));
app.post("/admin/events/save", requireTeamSession, withSession(saveEvent));
app.post("/admin/events/upload-image", requireTeamSession, bildUpload.single("image"), withSession(uploadEventImage));
// Spezielles Live-Event mit Countdown auf streamplan.html (20.08.2026)
app.post("/admin/special-event", requireTeamSession, withSession(getSpecialEventAdmin));
app.post("/admin/special-event/save", requireTeamSession, withSession(saveSpecialEvent));
app.post("/admin/roles/list", requireTeamSession, withSession(listRoles));
app.post("/admin/roles/create", requireTeamSession, withSession(createRole));
app.post("/admin/roles/update", requireTeamSession, withSession(updateRole));
+97
View File
@@ -0,0 +1,97 @@
/* =====================================================================
routes/special-event.js — "Spezielles Live-Event" für streamplan.html
(20.08.2026, Nutzer-Wunsch: "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"). Anders als "Event des Jahres" (2 feste Slots) gibt es hier
GENAU EIN Event mit einem exakten Zieldatum+Uhrzeit statt nur einem
freien Datumstext — daraus lässt sich ein echter Countdown berechnen.
Läuft auf derselben app_settings-Tabelle (key/value), kein Schema-Umbau
nötig. Gleiche Rechte-/Freigabe-Logik wie "Event des Jahres"
(EVENTS_MANAGE, Owner immer erlaubt, öffentlich lesbar, aber nur wenn
aktiviert UND das Zieldatum noch in der Zukunft liegt). ===================================================================== */
import { db } from "../db.js";
import { hasPermission } from "../lib/permissions.js";
import { json } from "../lib/http.js";
import { logAction } from "../lib/audit.js";
import { uebersetzeAlleSprachen } from "../lib/translate.js";
const SETTINGS_KEY = "special_live_event";
function can(session, key) {
if (!session) return false;
if (session.isOwner) return true;
return hasPermission(session.permissions, session.overrides, key);
}
function forbidden(res) {
return json(res, { ok: false, error: "Keine Berechtigung." }, 403);
}
function laden() {
const row = db.prepare(`SELECT value FROM app_settings WHERE key = ?`).get(SETTINGS_KEY);
if (!row) return null;
try {
return JSON.parse(row.value);
} catch {
return null;
}
}
/** Öffentlich, keine Session: nur wenn aktiviert UND das Zieldatum noch
nicht vorbei ist (ein abgelaufenes Event soll nicht ewig als "Countdown
bei 00:00:00" hängen bleiben, sondern einfach verschwinden). */
export async function getSpecialEventPublic(req, res) {
const eintrag = laden();
const istGueltig = eintrag && eintrag.aktiv && new Date(eintrag.zieldatum) > new Date();
return json(res, { ok: true, event: istGueltig ? eintrag : null });
}
/** Für die Verwaltung: liefert den rohen Stand (auch inaktiv/abgelaufen),
damit das Formular beim Öffnen alles vorausfüllen kann. */
export async function getSpecialEventAdmin(req, res, session) {
if (!can(session, "EVENTS_MANAGE")) return forbidden(res);
return json(res, { ok: true, event: laden() });
}
export async function saveSpecialEvent(req, res, session) {
if (!can(session, "EVENTS_MANAGE")) return forbidden(res);
const body = req.body || {};
const titelDe = String(body.titel || "").trim().slice(0, 120);
const zieldatumRoh = String(body.zieldatum || "").trim();
const link = String(body.link || "").trim().slice(0, 300) || null;
const aktiv = !!body.aktiv;
if (!titelDe) return json(res, { ok: false, error: "Titel ist Pflicht." }, 400);
const zieldatum = new Date(zieldatumRoh);
if (!zieldatumRoh || Number.isNaN(zieldatum.getTime())) {
return json(res, { ok: false, error: "Bitte ein gültiges Datum + Uhrzeit angeben." }, 400);
}
if (link && !/^https?:\/\//i.test(link)) {
return json(res, { ok: false, error: "Link muss mit http:// oder https:// beginnen." }, 400);
}
if (aktiv && zieldatum <= new Date()) {
return json(res, { ok: false, error: "Das Zieldatum liegt in der Vergangenheit — zum Aktivieren muss es in der Zukunft liegen." }, 400);
}
const titel = await uebersetzeAlleSprachen(titelDe);
const eintrag = {
titel,
zieldatum: zieldatum.toISOString(),
link,
aktiv,
aktualisiertVon: session.actor,
aktualisiertAm: new Date().toISOString(),
};
db.prepare(
`INSERT INTO app_settings (key, value, updated_at) VALUES (?, ?, ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`
).run(SETTINGS_KEY, JSON.stringify(eintrag), eintrag.aktualisiertAm);
logAction(session.actor, "special_event.saved", null, { titel: titelDe, zieldatum: eintrag.zieldatum, aktiv });
return json(res, { ok: true, event: eintrag });
}
+6 -6
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="Was die Community und das Team über DogFather sagen." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-stimmen.jpg');--page-bg-mobile:url('/assets/img/bg-stimmen-mobile.jpg');">
<div id="site-header"></div>
@@ -57,7 +57,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-stimmen.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-stimmen.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+6 -6
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="Die persönliche Geschichte hinter DogFather — Filipe, Streamer, Manager bei Spicy Media und Hundepapa von Husky Casper." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamer.jpg');--page-bg-mobile:url('/assets/img/bg-streamer-mobile.jpg');">
<div id="site-header"></div>
@@ -189,7 +189,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-streamer.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-streamer.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+68 -29
View File
@@ -19,17 +19,17 @@
<meta name="twitter:description" content="Wann geht DogFather live? Streamplan und Livebereich." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamplan.jpg?v=20260820i');--page-bg-mobile:url('/assets/img/bg-streamplan-mobile.jpg?v=20260820i');">
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamplan.jpg?v=20260820j');--page-bg-mobile:url('/assets/img/bg-streamplan-mobile.jpg?v=20260820j');">
<div id="site-header"></div>
<main>
<section class="hero page-bg">
<div class="hero-banner"><img src="/assets/img/bg-streamplan.jpg?v=20260820i" alt="" loading="eager" /></div>
<div class="hero-banner"><img src="/assets/img/bg-streamplan.jpg?v=20260820j" alt="" loading="eager" /></div>
<div class="container">
<span class="eyebrow" data-i18n="sp_eyebrow">Welt 1 — DogFather</span>
<h1 data-i18n="sp_h1">Streamplan &amp; Livebereich</h1>
@@ -37,28 +37,67 @@
<!-- Live-Radar (20.08.2026, Nutzer-Wunsch "geiler und spezieller"): 24h-Ziffernblatt
mit festem 20-Uhr-Leuchtpunkt + echtem "Jetzt"-Zeiger, schaltet live auf Rot/Puls
um, sonst zählt es zum nächsten 20-Uhr-Termin runter. Siehe assets/js/streamplan.js. -->
<div class="stream-radar-wrap">
<div class="stream-radar" id="stream-radar">
<div class="stream-radar-sweep" aria-hidden="true"></div>
<svg viewBox="0 0 300 300" class="stream-radar-dial" aria-hidden="true">
<circle cx="150" cy="150" r="130" class="radar-ring-outer"></circle>
<circle cx="150" cy="150" r="94" class="radar-ring-inner"></circle>
<g id="radar-ticks"></g>
<g id="radar-fixmarker"></g>
<line id="radar-now-hand" class="radar-now-hand" x1="150" y1="150" x2="150" y2="50"></line>
<circle id="radar-now-dot" class="radar-now-dot" cx="150" cy="50" r="5"></circle>
<circle cx="150" cy="150" r="4" class="radar-center-dot"></circle>
</svg>
<div class="stream-radar-center">
<span class="stream-radar-dot" id="radar-status-dot"></span>
<span class="stream-radar-status" id="radar-status-text" data-i18n="sp_radar_loading">Lädt …</span>
<span class="stream-radar-countdown" id="radar-countdown"></span>
<span class="stream-radar-sub" id="radar-status-sub"></span>
um, sonst zählt es zum nächsten 20-Uhr-Termin runter. Zweite Uhr (rechts, Magenta/
Violett) für ein optionales "spezielles Live-Event" aus der Verwaltung -- nur
sichtbar, wenn dort eins aktiviert ist. Siehe assets/js/streamplan.js. -->
<div class="stream-radar-reihe">
<div class="stream-radar-wrap">
<div class="stream-radar" id="stream-radar">
<div class="stream-radar-sweep" aria-hidden="true"></div>
<svg viewBox="0 0 300 300" class="stream-radar-dial" aria-hidden="true">
<defs>
<linearGradient id="radarGradTaeglich" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#8fd9ea"></stop>
<stop offset="50%" stop-color="#8b5cf6"></stop>
<stop offset="100%" stop-color="#e8c465"></stop>
</linearGradient>
</defs>
<circle cx="150" cy="150" r="130" class="radar-ring-outer" stroke="url(#radarGradTaeglich)"></circle>
<circle cx="150" cy="150" r="94" class="radar-ring-inner"></circle>
<g id="radar-ticks"></g>
<g id="radar-fixmarker"></g>
<line id="radar-now-hand" class="radar-now-hand" x1="150" y1="150" x2="150" y2="50"></line>
<circle id="radar-now-dot" class="radar-now-dot" cx="150" cy="50" r="5"></circle>
<circle cx="150" cy="150" r="4" class="radar-center-dot"></circle>
</svg>
<div class="stream-radar-center">
<span class="stream-radar-dot" id="radar-status-dot"></span>
<span class="stream-radar-status" id="radar-status-text" data-i18n="sp_radar_loading">Lädt …</span>
<span class="stream-radar-countdown" id="radar-countdown"></span>
<span class="stream-radar-sub" id="radar-status-sub"></span>
</div>
</div>
<a class="btn btn-primary stream-radar-watch-btn" id="radar-watch-btn" href="https://www.tiktok.com/@dogfather0804" target="_blank" rel="noopener" hidden data-i18n="sp_radar_watch_btn">🔴 Jetzt anschauen</a>
<p class="small muted stream-radar-hinweis" data-i18n="sp_radar_hinweis">Alle Zeiten in deiner Ortszeit.</p>
</div>
<div class="stream-radar-wrap" id="se-radar-wrap" hidden>
<div class="stream-radar event-spezial" id="se-radar">
<div class="stream-radar-sweep" aria-hidden="true"></div>
<svg viewBox="0 0 300 300" class="stream-radar-dial" aria-hidden="true">
<defs>
<linearGradient id="radarGradSpezial" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ff5cb8"></stop>
<stop offset="50%" stop-color="#c9a6ff"></stop>
<stop offset="100%" stop-color="#e8c465"></stop>
</linearGradient>
</defs>
<circle cx="150" cy="150" r="130" class="radar-ring-outer" stroke="url(#radarGradSpezial)"></circle>
<circle cx="150" cy="150" r="94" class="radar-ring-inner"></circle>
<g id="se-radar-ticks"></g>
<g id="se-radar-fixmarker"></g>
<line id="se-radar-now-hand" class="radar-now-hand" x1="150" y1="150" x2="150" y2="50"></line>
<circle id="se-radar-now-dot" class="radar-now-dot" cx="150" cy="50" r="5"></circle>
<circle cx="150" cy="150" r="4" class="radar-center-dot"></circle>
</svg>
<div class="stream-radar-center">
<span class="stream-radar-titel" id="se-radar-titel"></span>
<span class="stream-radar-countdown" id="se-radar-countdown"></span>
<span class="stream-radar-sub" data-i18n="sp_radar_hinweis_spezial">Spezielles Live-Event</span>
</div>
</div>
<a class="btn btn-primary stream-radar-watch-btn" id="se-radar-link" href="#" target="_blank" rel="noopener" hidden data-i18n="sp_radar_mehr_erfahren">Mehr erfahren →</a>
</div>
<a class="btn btn-primary stream-radar-watch-btn" id="radar-watch-btn" href="https://www.tiktok.com/@dogfather0804" target="_blank" rel="noopener" hidden data-i18n="sp_radar_watch_btn">🔴 Jetzt anschauen</a>
<p class="small muted stream-radar-hinweis" data-i18n="sp_radar_hinweis">Alle Zeiten in deiner Ortszeit.</p>
</div>
<div class="btn-row" style="justify-content:center;">
@@ -87,8 +126,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-streamplan.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/streamplan.js?v=20260820i"></script>
<script src="assets/js/i18n-streamplan.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script src="assets/js/streamplan.js?v=20260820j"></script>
</body>
</html>
+7 -7
View File
@@ -9,10 +9,10 @@
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#0b0d10" />
<meta name="robots" content="noindex" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
/* =====================================================================
Supporter-Bereich — komplett neu gestaltet (Nutzer-Feedback
@@ -416,9 +416,9 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-supporter.js?v=20260820i"></script>
<script src="assets/js/supporter.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-supporter.js?v=20260820j"></script>
<script src="assets/js/supporter.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
(function () {
const S = window.DogiSupporter;
+7 -7
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="VanVan, Diene, Funny, Miss und Marina — das Modi-/Core-Team von Team Dogi." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
<div id="site-header"></div>
@@ -66,9 +66,9 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/data-modis.js?v=20260820i"></script>
<script src="assets/js/i18n-team-modis.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-modis.js?v=20260820j"></script>
<script src="assets/js/i18n-team-modis.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const grid = document.getElementById("modis-grid");
+7 -7
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="Die Scouts von Team Dogi bei Spicy Media." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-scouts.jpg');--page-bg-mobile:url('/assets/img/bg-scouts-mobile.jpg');">
<div id="site-header"></div>
@@ -49,9 +49,9 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/data-scouts.js?v=20260820i"></script>
<script src="assets/js/i18n-team-scouts.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/data-scouts.js?v=20260820j"></script>
<script src="assets/js/i18n-team-scouts.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const grid = document.getElementById("scouts-grid");
+6 -6
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="Eine persönliche Widmung von Dogi an Tili — seine TikTok-Muddi, die erste und bis heute einzige Streamerin, die ihm all die Zeit treu geblieben ist." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
/* Gleiche Anpassung wie vanvan.html: der site-weit etwas größere
Fließtext wirkt bei einem so langen persönlichen Brief zu wuchtig —
@@ -153,7 +153,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-tt-muddi.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-tt-muddi.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+6 -6
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="VanVan erzählt ihre Geschichte: wie sie zu Team Dogi kam, wer sie ist und was DogFather und Team Dogi ihr bedeuten." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
/* Auf Wunsch: der site-weit vergrößerte Fließtext wirkte auf VanVans
eigener Seite zu groß (viel Text am Stück) — hier gezielt wieder
@@ -175,7 +175,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-vanvan.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-vanvan.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+111 -6
View File
@@ -19,10 +19,10 @@
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon-admin.png" />
<meta name="apple-mobile-web-app-title" content="DogiCrew-Verwaltung" />
<meta name="theme-color" content="#0e0a16" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style>
/* =====================================================================
verwaltung.html — Dogis privater Command-Center-Bereich.
@@ -803,6 +803,47 @@
</div>
</section>
<!-- ===== "Spezielles Live-Event" (Streamplan-Seite) — 20.08.2026,
Nutzer-Wunsch: "spezielle live events da auch eintragen ... so dass
da eine zweite uhr erscheint wo die zeit dann für dieses spezielle
event läuft." Anders als "Event des Jahres" hier ein echtes
Datum+Uhrzeit statt eines freien Textes -- daraus baut
streamplan.html einen echten Countdown. Nur sichtbar mit derselben
EVENTS_MANAGE-Berechtigung. -->
<section class="section-tight" id="special-event-root" style="display:none;padding-top:0;">
<div class="container">
<h2 class="text-center" style="margin-bottom:.3rem;">⚡ Spezielles Live-Event</h2>
<p class="small muted text-center" style="max-width:60ch;margin:0 auto 1.4rem;">
Erscheint als zweite Uhr auf der Streamplan-Seite, sobald aktiviert. Titel bitte auf
Deutsch eintippen, wird beim Speichern automatisch übersetzt.
</p>
<div class="card vw-card vw-shine-card vw-noise" style="max-width:520px;margin:0 auto;padding:1.5rem;">
<div class="field">
<label for="se-titel">Titel (Deutsch)</label>
<input type="text" id="se-titel" maxlength="120" placeholder="z. B. Großer Q&amp;A-Abend" />
</div>
<div class="field">
<label for="se-datum">Datum &amp; Uhrzeit</label>
<input type="datetime-local" id="se-datum" />
</div>
<div class="field">
<label for="se-link">Link (optional, muss mit https:// beginnen)</label>
<input type="url" id="se-link" placeholder="https://…" />
</div>
<label style="display:flex;align-items:center;gap:.6rem;margin:1rem 0;cursor:pointer;">
<input type="checkbox" id="se-aktiv" style="width:1.2rem;height:1.2rem;" />
<span>Aktiv — zweite Uhr auf der Streamplan-Seite sichtbar</span>
</label>
<p class="small muted" style="margin:-.4rem 0 1rem;">
Nur mit Titel + Datum in der Zukunft aktivierbar. Ist das Datum vorbei, verschwindet
die zweite Uhr automatisch wieder.
</p>
<button type="button" class="btn btn-primary" id="se-speichern" style="width:100%;">Speichern</button>
<p class="small" id="se-status" style="margin-top:.6rem;"></p>
</div>
</div>
</section>
<!-- ===== Supporter-Liste ===== -->
<section class="section-tight" id="supp-root" style="display:none;padding-top:0;">
<div class="container" id="liste-ansicht">
@@ -977,8 +1018,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/admin-auth.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script src="assets/js/admin-auth.js?v=20260820j"></script>
<script>
(function () {
const API_BASE_URL = "https://postfach.dogfather-universe.com";
@@ -1051,6 +1092,8 @@
if (window.AdminAuth.sessionHasPermission(session, "EVENTS_MANAGE")) {
document.getElementById("events-root").style.display = "";
await ladeUndRenderEvents(session);
document.getElementById("special-event-root").style.display = "";
await ladeUndRenderSpecialEvent();
}
const pollsBox = document.getElementById("box-abstimmungen");
if (window.AdminAuth.sessionHasPermission(session, "POLLS_MANAGE")) {
@@ -1069,6 +1112,7 @@
document.getElementById("vw-stats-root").style.display = "none";
document.getElementById("supp-root").style.display = "none";
document.getElementById("events-root").style.display = "none";
document.getElementById("special-event-root").style.display = "none";
document.getElementById("box-abstimmungen").style.display = "none";
}
@@ -1372,6 +1416,67 @@
});
}
/* ===== "Spezielles Live-Event" — Verwaltung (20.08.2026) =====
Datum+Uhrzeit kommt vom Server als UTC-ISO-String, das <input
type="datetime-local"> braucht aber "YYYY-MM-DDTHH:mm" in der
LOKALEN Zeitzone der Person, die gerade in der Verwaltung sitzt —
deshalb hier von Hand umgerechnet statt toISOString() (das würde UTC
zurückgeben und beim erneuten Öffnen eine falsche Uhrzeit anzeigen). */
function isoZuLokalemDatetimeLocal(iso) {
const d = new Date(iso);
const pad = (n) => String(n).padStart(2, "0");
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
async function ladeUndRenderSpecialEvent() {
const statusEl = document.getElementById("se-status");
let eintrag = null;
try {
const res = await window.AdminAuth.apiCall(API_BASE_URL, "/admin/special-event", {});
if (res.ok) eintrag = res.event;
} catch {
/* Formular bleibt leer, Speichern funktioniert trotzdem. */
}
if (eintrag) {
document.getElementById("se-titel").value = eintrag.titel?.de || "";
document.getElementById("se-datum").value = isoZuLokalemDatetimeLocal(eintrag.zieldatum);
document.getElementById("se-link").value = eintrag.link || "";
document.getElementById("se-aktiv").checked = !!eintrag.aktiv;
}
document.getElementById("se-speichern").addEventListener("click", async () => {
statusEl.textContent = "";
const titel = document.getElementById("se-titel").value.trim();
const datumRoh = document.getElementById("se-datum").value; // "YYYY-MM-DDTHH:mm", lokale Zeit
if (!titel || !datumRoh) {
statusEl.textContent = "Titel und Datum sind Pflicht.";
statusEl.style.color = "#ff6b6b";
return;
}
statusEl.textContent = "Wird gespeichert und übersetzt …";
statusEl.style.color = "";
// new Date("YYYY-MM-DDTHH:mm") interpretiert das als lokale Zeit dieses
// Browsers -- .toISOString() macht daraus den korrekten UTC-Zeitpunkt,
// den jede besuchende Person später in IHRER eigenen Ortszeit sieht.
const zieldatum = new Date(datumRoh).toISOString();
const res = await window.AdminAuth.apiCall(API_BASE_URL, "/admin/special-event/save", {
titel,
zieldatum,
link: document.getElementById("se-link").value.trim(),
aktiv: document.getElementById("se-aktiv").checked,
});
if (!res.ok) {
statusEl.textContent = res.error || "Speichern fehlgeschlagen.";
statusEl.style.color = "#ff6b6b";
return;
}
statusEl.textContent = res.event.aktiv
? "✅ Gespeichert — zweite Uhr ist live auf der Streamplan-Seite."
: "✅ Als Entwurf gespeichert (noch nicht sichtbar).";
statusEl.style.color = "#baf7d6";
});
}
// Nutzer-Wunsch 04.08.2026 (6. Runde): zwei getrennte Kisten/Formulare
// (Dogi + VanVan) statt einem gemeinsamen Feld — beide rufen denselben
// Login-Endpunkt auf, die Fehlermeldung erscheint jeweils nur in der
+6 -6
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="Die Werte, nach denen DogFather als Manager bei Spicy Media arbeitet." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-werte.jpg');--page-bg-mobile:url('/assets/img/bg-werte-mobile.jpg');">
<div id="site-header"></div>
@@ -113,7 +113,7 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-werte.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-werte.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
</body>
</html>
+6 -6
View File
@@ -19,10 +19,10 @@
<meta name="twitter:description" content="Die Geschichte von DogFather in einer interaktiven Zeitleiste." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-zeitreise.jpg');--page-bg-mobile:url('/assets/img/bg-zeitreise-mobile.jpg');">
<div id="site-header"></div>
@@ -219,8 +219,8 @@
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-zeitreise.js?v=20260820i"></script>
<script src="assets/js/main.js?v=20260820i"></script>
<script src="assets/js/i18n-zeitreise.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820j"></script>
<script>
/* Wachstums-Reveal: Äste blenden sich beim Scrollen sanft ein — passt
zum "wachsenden Baum"-Konzept dieser Seite. Rein page-lokal, betrifft