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="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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/i18n-404.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style> <style>
/* ===================================================================== /* =====================================================================
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
@@ -454,9 +454,9 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-abonnieren.js?v=20260820i"></script> <script src="assets/js/i18n-abonnieren.js?v=20260820j"></script>
<script src="assets/js/supporter.js?v=20260820i"></script> <script src="assets/js/supporter.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
(function () { (function () {
const S = window.DogiSupporter; 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="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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/i18n-aktion-detail.js?v=20260820i"></script> <script src="assets/js/i18n-aktion-detail.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const params = new URLSearchParams(location.search); 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 sonst zählt ein echter Sekunden-Countdown zum nächsten 20-Uhr-Termin
runter. Bewusst augenschonend: gedeckte Farben, langsame, sanfte runter. Bewusst augenschonend: gedeckte Farben, langsame, sanfte
Animation, kompletter Stillstand bei prefers-reduced-motion. ================================================================= */ 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 { .stream-radar {
position: relative; position: relative;
width: min(300px, 70vw); width: min(300px, 70vw);
@@ -3603,15 +3619,28 @@ body.start-seite .music-toggle:focus-visible {
inset: -6%; inset: -6%;
border-radius: 50%; border-radius: 50%;
background: conic-gradient(from 0deg, background: conic-gradient(from 0deg,
transparent 0deg, rgba(143, 217, 234, .16) 40deg, transparent 90deg, transparent 0deg, rgba(143, 217, 234, .28) 40deg, transparent 90deg,
transparent 260deg, rgba(139, 92, 246, .14) 300deg, transparent 340deg, transparent 360deg); 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; animation: stream-radar-rotate 16s linear infinite;
pointer-events: none; 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); } } @keyframes stream-radar-rotate { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .stream-radar-sweep { animation: none; } } @media (prefers-reduced-motion: reduce) { .stream-radar-sweep { animation: none; } }
.stream-radar-dial { position: relative; width: 100%; height: 100%; display: block; } .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-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 { 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-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.ist-live .stream-radar-countdown { color: #ff5c72; }
.stream-radar-sub { font-size: .74rem; color: var(--text-muted); } .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 /* .btn setzt selbst "display", das schlägt bei gleicher Spezifität die
eingebaute [hidden]-Regel des Browsers (identisches Muster wie bei eingebaute [hidden]-Regel des Browsers (identisches Muster wie bei
.jahres-event-card weiter oben) — deshalb hier explizit. */ .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.", 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.", 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: { 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",
+209 -110
View File
@@ -1,25 +1,24 @@
/* ===================================================================== /* =====================================================================
streamplan.js — "Live-Radar" NUR für streamplan.html (20.08.2026, 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 Zwei 24-Stunden-Ziffernblätter (reines SVG, keine Bild-Assets nötig):
feste 20-Uhr-Termin leuchtet dauerhaft als Fixpunkt, ein "Jetzt"-Zeiger - Uhr 1 (immer sichtbar): der feste 20-Uhr-Termin leuchtet dauerhaft als
bewegt sich mit der echten Uhrzeit der besuchenden Person mit. Nutzt Fixpunkt, ein "Jetzt"-Zeiger bewegt sich mit der echten Uhrzeit der
denselben /live-status-Endpunkt wie die Startseite (main.js lädt besuchenden Person mit. Live laut /live-status -> Rot/Puls +
window.dogiUebersetzen/dogiSprache VOR dieser Datei, siehe <script>- "Jetzt anschauen".
Reihenfolge in streamplan.html) — läuft DogFather laut TikTok gerade - Uhr 2 (nur wenn in der Verwaltung aktiviert): zählt zu einem exakten
wirklich, schaltet das ganze Radar auf Rot/Puls um und zeigt einen Zieldatum+Uhrzeit runter (Tage möglich, nicht nur Stunden), Magenta/
"Jetzt anschauen"-Knopf, sonst zählt ein echter Sekunden-Countdown zum Violett statt Cyan/Gold, damit beide Uhren auf einen Blick
nächsten lokalen 20-Uhr-Termin runter. ===================================================================== */ unterscheidbar sind ("bissl Farbe"). ===================================================================== */
(function () { (function () {
const API_BASE_URL = "https://postfach.dogfather-universe.com"; 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 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) { function polar(r, stundenAnteil) {
const winkel = (stundenAnteil / 24) * 360 - 90; // 0 Uhr oben, im Uhrzeigersinn 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) }; return { x: CX + r * Math.cos(rad), y: CY + r * Math.sin(rad) };
} }
// ---------- 24 Stundenstriche (alle 6 Std. etwas länger/heller) ---------- /** Zeichnet die 24 Stundenstriche + einen Fixpunkt-Leuchtpunkt bei
const ticksHost = document.getElementById("radar-ticks"); "fixStunde" (kann eine Dezimalzahl sein, z.B. 19.5 = 19:30) in die
for (let h = 0; h < 24; h++) { übergebenen <g>-Container. Läuft einmalig pro Uhr. */
const major = h % 6 === 0; function zeichneZiffernblatt(ticksHost, fixHost, fixStunde) {
const p1 = polar(R_OUT, h); ticksHost.innerHTML = "";
const p2 = polar(major ? R_TICK_MAJOR_IN : R_TICK_IN, h); for (let h = 0; h < 24; h++) {
const line = document.createElementNS(SVG_NS, "line"); const major = h % 6 === 0;
line.setAttribute("x1", p1.x); line.setAttribute("y1", p1.y); const p1 = polar(R_OUT, h);
line.setAttribute("x2", p2.x); line.setAttribute("y2", p2.y); const p2 = polar(major ? R_TICK_MAJOR_IN : R_TICK_IN, h);
line.setAttribute("class", major ? "radar-tick radar-tick-major" : "radar-tick"); const line = document.createElementNS(SVG_NS, "line");
ticksHost.appendChild(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");
// ---------- Fixmarkierung beim täglichen 20-Uhr-Termin ---------- ticksHost.appendChild(line);
const fixHost = document.getElementById("radar-fixmarker"); }
const fixPos = polar(R_OUT + 11, FIX_STUNDE); fixHost.innerHTML = "";
const fixDot = document.createElementNS(SVG_NS, "circle"); if (fixStunde != null) {
fixDot.setAttribute("cx", fixPos.x); fixDot.setAttribute("cy", fixPos.y); const fixPos = polar(R_OUT + 11, fixStunde);
fixDot.setAttribute("r", 6.5); const fixDot = document.createElementNS(SVG_NS, "circle");
fixDot.setAttribute("class", "radar-fixmarker-dot"); fixDot.setAttribute("cx", fixPos.x); fixDot.setAttribute("cy", fixPos.y);
fixHost.appendChild(fixDot); fixDot.setAttribute("r", 6.5);
fixDot.setAttribute("class", "radar-fixmarker-dot");
// ---------- "Jetzt"-Zeiger: bewegt sich mit der echten Uhrzeit mit ---------- fixHost.appendChild(fixDot);
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();
} }
} }
function liveStatusAnwenden(data) { function zeigerAktualisieren(handEl, dotEl, stundenAnteil) {
istLive = !!(data && data.live); const spitze = polar(R_HAND, stundenAnteil);
letzterTitel = data && data.title ? data.title : null; handEl.setAttribute("x2", spitze.x); handEl.setAttribute("y2", spitze.y);
radar.classList.toggle("ist-live", istLive); dotEl.setAttribute("cx", spitze.x); dotEl.setAttribute("cy", spitze.y);
texteNeuSetzen();
} }
function liveStatusLaden() { function zweistellig(n) { return String(n).padStart(2, "0"); }
return fetch(`${API_BASE_URL}/live-status`, { cache: "no-store" })
.then((res) => res.json()) /* ===================================================================
.then(liveStatusAnwenden) Uhr 1: täglicher 20-Uhr-Fixtermin (bereits bestehend, unverändert im
.catch(() => { Verhalten) ================================================================= */
istLive = false; const radar = document.getElementById("stream-radar");
radar.classList.remove("ist-live"); if (radar) {
texteNeuSetzen(); 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); Uhr 2: "spezielles Live-Event" aus der Verwaltung (20.08.2026, neu).
document.addEventListener("visibilitychange", () => { if (!document.hidden) liveStatusLaden(); }); Nur sichtbar, wenn /special-event ein aktives, noch bevorstehendes
document.addEventListener("dogi-sprache-geaendert", texteNeuSetzen); 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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-creator.js?v=20260820i"></script> <script src="assets/js/i18n-bewerben-creator.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </html>
+7 -7
View File
@@ -22,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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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>
@@ -68,8 +68,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/forms.js?v=20260820i"></script> <script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-kooperation.js?v=20260820i"></script> <script src="assets/js/i18n-bewerben-kooperation.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');">
<div id="site-header"></div> <div id="site-header"></div>
@@ -79,8 +79,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/forms.js?v=20260820i"></script> <script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-modi.js?v=20260820i"></script> <script src="assets/js/i18n-bewerben-modi.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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-scouts.jpg');--page-bg-mobile:url('/assets/img/bg-scouts-mobile.jpg');">
<div id="site-header"></div> <div id="site-header"></div>
@@ -71,8 +71,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/forms.js?v=20260820i"></script> <script src="assets/js/forms.js?v=20260820j"></script>
<script src="assets/js/i18n-bewerben-scout.js?v=20260820i"></script> <script src="assets/js/i18n-bewerben-scout.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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>
@@ -85,7 +85,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-bewerben.js?v=20260820i"></script> <script src="assets/js/i18n-bewerben.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/i18n-casper.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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>
@@ -84,7 +84,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-community.js?v=20260820i"></script> <script src="assets/js/i18n-community.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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>
@@ -59,9 +59,9 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/data-creator.js?v=20260820i"></script> <script src="assets/js/data-creator.js?v=20260820j"></script>
<script src="assets/js/i18n-creator.js?v=20260820i"></script> <script src="assets/js/i18n-creator.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const grid = document.getElementById("creator-grid"); 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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/i18n-dogfather.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </html>
+2 -2
View File
@@ -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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<style> <style>
/* ===================================================================== /* =====================================================================
Zugangsseite — bewusst kein braves Standard-Login-Formular, sondern 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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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>
@@ -182,7 +182,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-hasidog.js?v=20260820i"></script> <script src="assets/js/i18n-hasidog.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </head>
<body class="start-seite"> <body class="start-seite">
<div id="site-header"></div> <div id="site-header"></div>
@@ -293,11 +293,11 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/data-modis.js?v=20260820i"></script> <script src="assets/js/data-modis.js?v=20260820j"></script>
<script src="assets/js/data-scouts.js?v=20260820i"></script> <script src="assets/js/data-scouts.js?v=20260820j"></script>
<script src="assets/js/data-aktionen.js?v=20260820i"></script> <script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/i18n-index.js?v=20260820i"></script> <script src="assets/js/i18n-index.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></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.
+6 -6
View File
@@ -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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/i18n-kontakt.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<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
@@ -140,8 +140,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-links.js?v=20260820i"></script> <script src="assets/js/i18n-links.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
(function () { (function () {
document.addEventListener("DOMContentLoaded", 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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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>
@@ -126,7 +126,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-manager.js?v=20260820i"></script> <script src="assets/js/i18n-manager.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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-casper.jpg');--page-bg-mobile:url('/assets/img/bg-casper-mobile.jpg');">
<div id="site-header"></div> <div id="site-header"></div>
@@ -59,10 +59,10 @@
</div> </div>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/data-media.js?v=20260820i"></script> <script src="assets/js/data-media.js?v=20260820j"></script>
<script src="assets/js/data-videos.js?v=20260820i"></script> <script src="assets/js/data-videos.js?v=20260820j"></script>
<script src="assets/js/medien-welt.js?v=20260820i"></script> <script src="assets/js/medien-welt.js?v=20260820j"></script>
<script src="assets/js/i18n-medien-casper.js?v=20260820i"></script> <script src="assets/js/i18n-medien-casper.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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?v=20260820i"></script> <script src="assets/js/data-media.js?v=20260820j"></script>
<script src="assets/js/data-videos.js?v=20260820i"></script> <script src="assets/js/data-videos.js?v=20260820j"></script>
<script src="assets/js/data-aktionen.js?v=20260820i"></script> <script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/medien-welt.js?v=20260820i"></script> <script src="assets/js/medien-welt.js?v=20260820j"></script>
<script src="assets/js/i18n-medien-dogfather.js?v=20260820i"></script> <script src="assets/js/i18n-medien-dogfather.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</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-hasidog.jpg');--page-bg-mobile:url('/assets/img/bg-hasidog-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?v=20260820i"></script> <script src="assets/js/data-media.js?v=20260820j"></script>
<script src="assets/js/data-videos.js?v=20260820i"></script> <script src="assets/js/data-videos.js?v=20260820j"></script>
<script src="assets/js/data-aktionen.js?v=20260820i"></script> <script src="assets/js/data-aktionen.js?v=20260820j"></script>
<script src="assets/js/medien-welt.js?v=20260820i"></script> <script src="assets/js/medien-welt.js?v=20260820j"></script>
<script src="assets/js/i18n-medien-hasidog.js?v=20260820i"></script> <script src="assets/js/i18n-medien-hasidog.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </html>
+9 -9
View File
@@ -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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<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,11 +130,11 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/data-modis.js?v=20260820i"></script> <script src="assets/js/data-modis.js?v=20260820j"></script>
<script src="assets/js/data-scouts.js?v=20260820i"></script> <script src="assets/js/data-scouts.js?v=20260820j"></script>
<script src="assets/js/data-creator.js?v=20260820i"></script> <script src="assets/js/data-creator.js?v=20260820j"></script>
<script src="assets/js/i18n-profil.js?v=20260820i"></script> <script src="assets/js/i18n-profil.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const slug = new URLSearchParams(location.search).get("p") || ""; 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: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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style> <style>
/* Leere-Zustand-Kachel im "Gutschein"-Look: gestrichelter Rand wie ein /* Leere-Zustand-Kachel im "Gutschein"-Look: gestrichelter Rand wie ein
ausgeschnittener Coupon, damit auch OHNE echte Codes schon klar ist, ausgeschnittener Coupon, damit auch OHNE echte Codes schon klar ist,
@@ -131,8 +131,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-rabattcodes.js?v=20260820i"></script> <script src="assets/js/i18n-rabattcodes.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
(function () { (function () {
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
+6
View File
@@ -64,6 +64,7 @@ import {
listSupporters, getSupporterDetail, updatePremium, listCollections, upsertCollection, getAdminOverview, listSupporters, getSupporterDetail, updatePremium, listCollections, upsertCollection, getAdminOverview,
} from "./routes/supporter-admin.js"; } from "./routes/supporter-admin.js";
import { getEventsPublic, getEventsAdmin, saveEvent, uploadEventImage } from "./routes/events.js"; import { getEventsPublic, getEventsAdmin, saveEvent, uploadEventImage } from "./routes/events.js";
import { getSpecialEventPublic, getSpecialEventAdmin, saveSpecialEvent } from "./routes/special-event.js";
import { import {
createPoll, listPolls, closePoll, reopenPoll, deletePoll, getActivePollPublic, voteOnPoll, createPoll, listPolls, closePoll, reopenPoll, deletePoll, getActivePollPublic, voteOnPoll,
} from "./routes/polls.js"; } from "./routes/polls.js";
@@ -157,6 +158,7 @@ app.post("/submit", wrap(submitApplication));
app.get("/live-status", wrap(handleLiveStatusGet)); app.get("/live-status", wrap(handleLiveStatusGet));
app.post("/auth/login", wrap(login)); app.post("/auth/login", wrap(login));
app.get("/events-of-year", wrap(getEventsPublic)); app.get("/events-of-year", wrap(getEventsPublic));
app.get("/special-event", wrap(getSpecialEventPublic));
app.post("/supporter/register", wrap(registerSupporter)); app.post("/supporter/register", wrap(registerSupporter));
app.post("/supporter/verify-email", wrap(verifySupporterEmail)); 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/save", requireTeamSession, withSession(saveEvent));
app.post("/admin/events/upload-image", requireTeamSession, bildUpload.single("image"), withSession(uploadEventImage)); 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/list", requireTeamSession, withSession(listRoles));
app.post("/admin/roles/create", requireTeamSession, withSession(createRole)); app.post("/admin/roles/create", requireTeamSession, withSession(createRole));
app.post("/admin/roles/update", requireTeamSession, withSession(updateRole)); 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: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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-stimmen.jpg');--page-bg-mobile:url('/assets/img/bg-stimmen-mobile.jpg');"> <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> <div id="site-header"></div>
@@ -57,7 +57,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-stimmen.js?v=20260820i"></script> <script src="assets/js/i18n-stimmen.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-streamer.jpg');--page-bg-mobile:url('/assets/img/bg-streamer-mobile.jpg');"> <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> <div id="site-header"></div>
@@ -189,7 +189,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-streamer.js?v=20260820i"></script> <script src="assets/js/i18n-streamer.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </html>
+68 -29
View File
@@ -19,17 +19,17 @@
<meta name="twitter:description" content="Wann geht DogFather live? Streamplan und Livebereich." /> <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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </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> <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-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"> <div class="container">
<span class="eyebrow" data-i18n="sp_eyebrow">Welt 1 — DogFather</span> <span class="eyebrow" data-i18n="sp_eyebrow">Welt 1 — DogFather</span>
<h1 data-i18n="sp_h1">Streamplan &amp; Livebereich</h1> <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 <!-- 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 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. --> um, sonst zählt es zum nächsten 20-Uhr-Termin runter. Zweite Uhr (rechts, Magenta/
<div class="stream-radar-wrap"> Violett) für ein optionales "spezielles Live-Event" aus der Verwaltung -- nur
<div class="stream-radar" id="stream-radar"> sichtbar, wenn dort eins aktiviert ist. Siehe assets/js/streamplan.js. -->
<div class="stream-radar-sweep" aria-hidden="true"></div> <div class="stream-radar-reihe">
<svg viewBox="0 0 300 300" class="stream-radar-dial" aria-hidden="true"> <div class="stream-radar-wrap">
<circle cx="150" cy="150" r="130" class="radar-ring-outer"></circle> <div class="stream-radar" id="stream-radar">
<circle cx="150" cy="150" r="94" class="radar-ring-inner"></circle> <div class="stream-radar-sweep" aria-hidden="true"></div>
<g id="radar-ticks"></g> <svg viewBox="0 0 300 300" class="stream-radar-dial" aria-hidden="true">
<g id="radar-fixmarker"></g> <defs>
<line id="radar-now-hand" class="radar-now-hand" x1="150" y1="150" x2="150" y2="50"></line> <linearGradient id="radarGradTaeglich" x1="0%" y1="0%" x2="100%" y2="100%">
<circle id="radar-now-dot" class="radar-now-dot" cx="150" cy="50" r="5"></circle> <stop offset="0%" stop-color="#8fd9ea"></stop>
<circle cx="150" cy="150" r="4" class="radar-center-dot"></circle> <stop offset="50%" stop-color="#8b5cf6"></stop>
</svg> <stop offset="100%" stop-color="#e8c465"></stop>
<div class="stream-radar-center"> </linearGradient>
<span class="stream-radar-dot" id="radar-status-dot"></span> </defs>
<span class="stream-radar-status" id="radar-status-text" data-i18n="sp_radar_loading">Lädt …</span> <circle cx="150" cy="150" r="130" class="radar-ring-outer" stroke="url(#radarGradTaeglich)"></circle>
<span class="stream-radar-countdown" id="radar-countdown"></span> <circle cx="150" cy="150" r="94" class="radar-ring-inner"></circle>
<span class="stream-radar-sub" id="radar-status-sub"></span> <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> </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> </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>
<div class="btn-row" style="justify-content:center;"> <div class="btn-row" style="justify-content:center;">
@@ -87,8 +126,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-streamplan.js?v=20260820i"></script> <script src="assets/js/i18n-streamplan.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script src="assets/js/streamplan.js?v=20260820i"></script> <script src="assets/js/streamplan.js?v=20260820j"></script>
</body> </body>
</html> </html>
+7 -7
View File
@@ -9,10 +9,10 @@
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#0b0d10" /> <meta name="theme-color" content="#0b0d10" />
<meta name="robots" content="noindex" /> <meta name="robots" content="noindex" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style> <style>
/* ===================================================================== /* =====================================================================
Supporter-Bereich — komplett neu gestaltet (Nutzer-Feedback Supporter-Bereich — komplett neu gestaltet (Nutzer-Feedback
@@ -416,9 +416,9 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-supporter.js?v=20260820i"></script> <script src="assets/js/i18n-supporter.js?v=20260820j"></script>
<script src="assets/js/supporter.js?v=20260820i"></script> <script src="assets/js/supporter.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
(function () { (function () {
const S = window.DogiSupporter; 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: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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-dogfather.jpg');--page-bg-mobile:url('/assets/img/bg-dogfather-mobile.jpg');"> <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> <div id="site-header"></div>
@@ -66,9 +66,9 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/data-modis.js?v=20260820i"></script> <script src="assets/js/data-modis.js?v=20260820j"></script>
<script src="assets/js/i18n-team-modis.js?v=20260820i"></script> <script src="assets/js/i18n-team-modis.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const grid = document.getElementById("modis-grid"); 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:description" content="Die Scouts von Team Dogi 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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-scouts.jpg');--page-bg-mobile:url('/assets/img/bg-scouts-mobile.jpg');"> <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> <div id="site-header"></div>
@@ -49,9 +49,9 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/data-scouts.js?v=20260820i"></script> <script src="assets/js/data-scouts.js?v=20260820j"></script>
<script src="assets/js/i18n-team-scouts.js?v=20260820i"></script> <script src="assets/js/i18n-team-scouts.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
const grid = document.getElementById("scouts-grid"); 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: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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style> <style>
/* Gleiche Anpassung wie vanvan.html: der site-weit etwas größere /* Gleiche Anpassung wie vanvan.html: der site-weit etwas größere
Fließtext wirkt bei einem so langen persönlichen Brief zu wuchtig — Fließtext wirkt bei einem so langen persönlichen Brief zu wuchtig —
@@ -153,7 +153,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-tt-muddi.js?v=20260820i"></script> <script src="assets/js/i18n-tt-muddi.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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: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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style> <style>
/* Auf Wunsch: der site-weit vergrößerte Fließtext wirkte auf VanVans /* Auf Wunsch: der site-weit vergrößerte Fließtext wirkte auf VanVans
eigener Seite zu groß (viel Text am Stück) — hier gezielt wieder eigener Seite zu groß (viel Text am Stück) — hier gezielt wieder
@@ -175,7 +175,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-vanvan.js?v=20260820i"></script> <script src="assets/js/i18n-vanvan.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </html>
+111 -6
View File
@@ -19,10 +19,10 @@
<link rel="apple-touch-icon" href="assets/img/apple-touch-icon-admin.png" /> <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="apple-mobile-web-app-title" content="DogiCrew-Verwaltung" />
<meta name="theme-color" content="#0e0a16" /> <meta name="theme-color" content="#0e0a16" />
<link rel="stylesheet" href="assets/css/main.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
<style> <style>
/* ===================================================================== /* =====================================================================
verwaltung.html — Dogis privater Command-Center-Bereich. verwaltung.html — Dogis privater Command-Center-Bereich.
@@ -803,6 +803,47 @@
</div> </div>
</section> </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 ===== --> <!-- ===== Supporter-Liste ===== -->
<section class="section-tight" id="supp-root" style="display:none;padding-top:0;"> <section class="section-tight" id="supp-root" style="display:none;padding-top:0;">
<div class="container" id="liste-ansicht"> <div class="container" id="liste-ansicht">
@@ -977,8 +1018,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script src="assets/js/admin-auth.js?v=20260820i"></script> <script src="assets/js/admin-auth.js?v=20260820j"></script>
<script> <script>
(function () { (function () {
const API_BASE_URL = "https://postfach.dogfather-universe.com"; const API_BASE_URL = "https://postfach.dogfather-universe.com";
@@ -1051,6 +1092,8 @@
if (window.AdminAuth.sessionHasPermission(session, "EVENTS_MANAGE")) { if (window.AdminAuth.sessionHasPermission(session, "EVENTS_MANAGE")) {
document.getElementById("events-root").style.display = ""; document.getElementById("events-root").style.display = "";
await ladeUndRenderEvents(session); await ladeUndRenderEvents(session);
document.getElementById("special-event-root").style.display = "";
await ladeUndRenderSpecialEvent();
} }
const pollsBox = document.getElementById("box-abstimmungen"); const pollsBox = document.getElementById("box-abstimmungen");
if (window.AdminAuth.sessionHasPermission(session, "POLLS_MANAGE")) { if (window.AdminAuth.sessionHasPermission(session, "POLLS_MANAGE")) {
@@ -1069,6 +1112,7 @@
document.getElementById("vw-stats-root").style.display = "none"; document.getElementById("vw-stats-root").style.display = "none";
document.getElementById("supp-root").style.display = "none"; document.getElementById("supp-root").style.display = "none";
document.getElementById("events-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"; 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 // Nutzer-Wunsch 04.08.2026 (6. Runde): zwei getrennte Kisten/Formulare
// (Dogi + VanVan) statt einem gemeinsamen Feld — beide rufen denselben // (Dogi + VanVan) statt einem gemeinsamen Feld — beide rufen denselben
// Login-Endpunkt auf, die Fehlermeldung erscheint jeweils nur in der // 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: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" /> <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/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-werte.jpg');--page-bg-mobile:url('/assets/img/bg-werte-mobile.jpg');"> <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> <div id="site-header"></div>
@@ -113,7 +113,7 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-werte.js?v=20260820i"></script> <script src="assets/js/i18n-werte.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
</body> </body>
</html> </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:description" content="Die Geschichte von DogFather in einer interaktiven Zeitleiste." />
<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?v=20260820i" /> <link rel="stylesheet" href="assets/css/main.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260820j" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820i" /> <link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260820j" />
</head> </head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-zeitreise.jpg');--page-bg-mobile:url('/assets/img/bg-zeitreise-mobile.jpg');"> <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> <div id="site-header"></div>
@@ -219,8 +219,8 @@
</main> </main>
<div id="site-footer"></div> <div id="site-footer"></div>
<script src="assets/js/i18n-zeitreise.js?v=20260820i"></script> <script src="assets/js/i18n-zeitreise.js?v=20260820j"></script>
<script src="assets/js/main.js?v=20260820i"></script> <script src="assets/js/main.js?v=20260820j"></script>
<script> <script>
/* Wachstums-Reveal: Äste blenden sich beim Scrollen sanft ein — passt /* Wachstums-Reveal: Äste blenden sich beim Scrollen sanft ein — passt
zum "wachsenden Baum"-Konzept dieser Seite. Rein page-lokal, betrifft zum "wachsenden Baum"-Konzept dieser Seite. Rein page-lokal, betrifft