Files
dogfather-universe/abonnieren.html
T
DogFatherGitandClaude Opus 5 41faf22614 Handy: vollständige Prüfung von Inhalten, Knöpfen und allen Systemen
Nachtrag auf Nachfrage ("hast du alles geprüft oder nur die menü
leisten?") -- vorher hatte ich nur Navigation, Seitenbreite, Tippflächen
und Hoch/Querformat geprüft. Jetzt zusätzlich Bilder, Texte, Knöpfe und
die kompletten Abläufe.

Geprüft über alle 35 Seiten bei 393px:
- Bilder: kein einziges kaputtes Bild, keine fehlenden alt-Texte
- Dateien: keine 404er
- Knöpfe/Links: alle beschriftet (keine namenlosen Bedienelemente)
- Text: nichts wird abgeschnitten. Die zunächst gemeldeten "Überläufe"
  bei .btn/.card waren Fehlalarme -- sie stammen von den dekorativen
  Leucht-Ebenen (::before mit negativem inset), echter Text ragt
  nirgends heraus (einzeln gegengeprüft).

Zwei echte Funde behoben:
1. Marken-Unterzeile war mit 8,96px zu klein zum Lesen (hatte ich beim
   Handy-Fix selbst so verkleinert). Jetzt 11px -- Platz ist da, seit der
   Menü-Knopf eigenständig rechts sitzt. Über acht Breiten gegengeprüft,
   Kopfleiste bleibt überall stabil.
2. "Aktiv"-Ankreuzfelder in der Verwaltung waren 22px. Jetzt 26px, die
   ganze Beschriftungszeile ist 44px hoch und schaltet mit um.

Abläufe am Handy durchgespielt (echte Fingertipps, Fake-Backend):
- Registrierung über alle drei Schritte inkl. falschem Code
- Login mit falschem und richtigem Zugangscode
- Abmelden auf supporter.html
- Stimme einreichen inkl. Profilbild-Auswahl
- Verwaltung: Team-Mitglied anlegen, Stimme freigeben, alle Bereiche
  sichtbar, kein Überlauf

Cache-Busting-Version auf 20260821r erhöht.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-21 18:33:52 +02:00

930 lines
51 KiB
HTML

<!DOCTYPE html>
<html lang="de" data-theme="dogfather">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DogiCrew 🩵 — DOGFATHER UNIVERSE</title>
<meta name="description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
<link rel="icon" type="image/png" href="assets/img/favicon.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#0b0d10" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="DOGFATHER UNIVERSE" />
<meta property="og:title" content="DogiCrew 🩵 — DOGFATHER UNIVERSE" />
<meta property="og:description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
<meta property="og:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<meta property="og:url" content="https://dogfather-universe.com/abonnieren.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="DogiCrew 🩵 — DOGFATHER UNIVERSE" />
<meta name="twitter:description" content="Unterstütze Dogfather dauerhaft mit der DogiCrew 🩵 und sammle exklusive Treueprämien — die Teddy-Kollektion entsteht in Kooperation mit VanVan." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260821r" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260821r" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260821r" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260821r" />
<style>
/* =====================================================================
Preis-/Anmelde-Kiste — komplett neu gestaltet (Nutzer-Feedback
03.08.2026: "das sieht ja richtig kake aus"). Ziel: dasselbe edle
Gold-Motiv wie beim Nav-Abo-Knopf/.btn-silver, klare visuelle
Hierarchie (Preis dominiert, Bedingungen dezent, Formular großzügig),
spürbar mehr Luft zwischen den Elementen statt der bisherigen dichten
Textwand. Bewusst KEIN max-width auf der Box selbst — soll exakt so
breit sein wie der umgebende .container (1180px), siehe Kommentar
unten bei .abo-preis-box.
===================================================================== */
.abo-preis-box {
width: 100%;
text-align: center;
position: relative;
isolation: isolate;
overflow: hidden;
padding: 2.6rem clamp(1.4rem, 5vw, 3.2rem) 3rem;
border: 1.5px solid rgba(212,175,55,.45);
background:
radial-gradient(circle at 22% 0%, rgba(212,175,55,.16), transparent 55%),
radial-gradient(circle at 82% 100%, rgba(139,92,246,.12), transparent 55%),
linear-gradient(180deg, rgba(23,27,33,.96), rgba(15,17,21,.98));
box-shadow: 0 0 0 1px rgba(212,175,55,.06) inset, 0 30px 60px -30px rgba(0,0,0,.6);
}
/* Feiner, ruhig schimmernder Goldrand oben — reines Deko-Element, kein
rotierendes Objekt (siehe dokumentierte Lektion: rotierende Ringe auf
nicht-runden Formen verzerren sich, deshalb hier nur ein sanftes
Auf-und-Ab-Glühen statt einer Drehung). */
.abo-preis-box::before {
content: "";
position: absolute; top: 0; left: 10%; right: 10%; height: 1px; z-index: 2;
background: linear-gradient(90deg, transparent, rgba(255,214,102,.9), transparent);
animation: abo-goldglanz 4.5s ease-in-out infinite;
}
@keyframes abo-goldglanz { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
/* Logo als leuchtendes Wasserzeichen im Kartenhintergrund (Nutzer-Wunsch
03.08.2026: "mein Logo integriert, als Hintergrund, richtig geil,
soll so geil dass man nicht davon wegkommt"). Freigestelltes Logo
(logo-dogfather-transparent.png, siehe gate.html-Lektion: Original
hatte einen weißen Hintergrund eingebrannt, hier bereits freigestellt)
— groß, mittig, mit pulsierender Gold/Lila-Glut dahinter. Sitzt per
z-index HINTER dem echten Karteninhalt (Preis/Formular), damit nichts
an Lesbarkeit verliert; .abo-preis-box hat dafür isolation:isolate +
overflow:hidden bekommen, damit die Glut nicht über den Kartenrand
hinausblutet (dieselbe dokumentierte Lektion wie beim TikTok-Button-
Glow). Bewusst KEINE Rotation des Logos selbst (nur Skalieren/
Verblassen) — ein Markenlogo, das sich dreht, würde zeitweise auf dem
Kopf stehen. */
.abo-preis-box .abo-logo-glut {
position: absolute; inset: -10%; z-index: -1; pointer-events: none;
background:
radial-gradient(circle at 50% 28%, rgba(255,233,168,.4) 0%, rgba(212,175,55,.16) 32%, transparent 62%),
radial-gradient(circle at 22% 75%, rgba(139,92,246,.22), transparent 55%),
radial-gradient(circle at 80% 80%, rgba(143,217,234,.16), transparent 55%);
animation: abo-logo-glut-puls 5s ease-in-out infinite;
}
@keyframes abo-logo-glut-puls { 0%, 100% { opacity: .75; } 50% { opacity: 1; } }
.abo-preis-box .abo-logo-wz {
/* Bewusst NICHT inset:0 über die ganze (sehr hohe) Karte — dadurch
wären background-position/mask-Prozentwerte relativ zur kompletten
Kartenhöhe (inkl. Formular) berechnet und kaum kontrollierbar. Eine
feste Zone oben (deckt Badge/Preis/Bedingungen ab) macht Position
und Ausblenden vorhersagbar und garantiert, dass die Eingabefelder
darunter sauber lesbar bleiben (Nutzer-Feedback: Logo lag vorher
über dem Namensfeld). */
position: absolute; top: 0; left: 0; right: 0; height: 420px; z-index: -1; pointer-events: none;
background-image: url('assets/img/logo-dogfather-transparent.png');
background-repeat: no-repeat;
background-position: center 6%;
background-size: min(62%, 320px) auto;
opacity: .4;
filter: drop-shadow(0 0 28px rgba(255,233,168,.65)) drop-shadow(0 0 56px rgba(139,92,246,.4));
-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 92%);
mask-image: linear-gradient(to bottom, #000 0%, #000 45%, transparent 92%);
animation: abo-logo-wz-atem 6s ease-in-out infinite;
}
@keyframes abo-logo-wz-atem {
0%, 100% { opacity: .32; transform: scale(.99); }
50% { opacity: .48; transform: scale(1.03); }
}
@media (prefers-reduced-motion: reduce) {
.abo-preis-box .abo-logo-glut, .abo-preis-box .abo-logo-wz, .abo-preis-box::before { animation: none; }
}
.abo-preis-box .eyebrow {
display: inline-flex; align-items: center; gap: .5rem;
color: #ffe9a8; border-color: rgba(255,214,102,.5);
font-size: 1.3rem; letter-spacing: .1em;
}
.abo-preis-zahl {
font-size: clamp(2.6rem, 5vw, 3.4rem); font-weight: 800; color: #ffe9a8;
margin: .6rem 0 .2rem; letter-spacing: -.01em;
text-shadow: 0 0 28px rgba(255,214,102,.35);
}
.abo-preis-zahl small { font-size: 1.05rem; font-weight: 600; color: var(--text-muted); letter-spacing: 0; text-shadow: none; }
.abo-bedingungen {
list-style: none; padding: 0; text-align: left; max-width: 460px;
margin: 1.6rem auto 0; display: flex; flex-direction: column; gap: .55rem;
}
.abo-bedingungen li {
display: flex; align-items: flex-start; gap: .6rem;
color: var(--text-muted); font-size: .96rem; line-height: 1.5;
}
.abo-bedingungen li::before {
content: "✓"; flex-shrink: 0; width: 1.35rem; height: 1.35rem; margin-top: .05rem;
display: inline-flex; align-items: center; justify-content: center;
border-radius: 50%; font-size: .78rem; font-weight: 800;
color: #0b0d10; background: linear-gradient(135deg, #ffe9a8, #d4af37);
}
/* Widerrufsrecht-Checkbox (siehe agb.html Ziffer 6): NICHT vorausgewählt,
sperrt die PayPal-Zahlungsoptionen optisch + funktional (pointer-events),
bis sie aktiv angehakt wurde. */
.abo-widerruf-check {
display: flex; align-items: flex-start; gap: .65rem; text-align: left;
max-width: 460px; margin: 0 auto 1.1rem; padding: .85rem .95rem; border-radius: 12px;
background: rgba(255,255,255,.03); border: 1.5px solid rgba(139,92,246,.35);
font-size: .85rem; line-height: 1.5; color: var(--text-muted); cursor: pointer;
}
.abo-widerruf-check input[type="checkbox"] {
flex-shrink: 0; width: 1.15rem; height: 1.15rem; margin-top: .15rem; cursor: pointer; accent-color: #8b5cf6;
}
.abo-widerruf-check a { color: var(--accent); }
#paypal-button-container.paypal-gesperrt { opacity: .35; filter: grayscale(1); pointer-events: none; min-height: 45px; }
#paypal-gesperrt-hinweis { text-align: center; margin-top: .5rem; }
.abo-panel { max-width: 440px; margin: 2.2rem auto 0; }
.abo-panel[hidden] { display: none; }
.abo-field { margin-bottom: 1.2rem; text-align: left; }
.abo-field label {
display: block; font-size: .82rem; font-weight: 600; letter-spacing: .02em;
color: var(--text-muted); margin-bottom: .45rem;
}
.abo-field input {
display: block; width: 100%; box-sizing: border-box;
padding: .9rem 1.05rem; border-radius: 12px;
background: rgba(255,255,255,.035); border: 1.5px solid var(--border); color: var(--text);
font-size: 1.02rem; transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.abo-field input::placeholder { color: rgba(154,164,178,.55); }
.abo-field input:focus {
outline: none; border-color: rgba(212,175,55,.7); background: rgba(255,255,255,.055);
box-shadow: 0 0 0 4px rgba(212,175,55,.12);
}
/* Registrieren/Login-Knöpfe: eigener Gold-Foil-Stil statt des generischen
.btn-primary (Nutzer-Feedback 03.08.2026: "viel spezieller" + Text
zentriert). Gleiche sichere Technik wie überall im Projekt — die
Fläche schimmert per background-position, NICHT per rotierendem Ring
(der würde auf dieser Pillenform verzerren, siehe dokumentierte
TikTok-Button-Lektion). */
.abo-panel .btn {
position: relative; isolation: isolate; overflow: hidden;
display: flex; align-items: center; justify-content: center;
padding: 1rem 1.4rem; border-radius: 14px; font-weight: 800; font-size: 1.04rem;
color: #1a1408; text-decoration: none; cursor: pointer;
border: 1.5px solid rgba(255,233,168,.65);
background: linear-gradient(120deg, #ffe9a8 0%, #d4af37 30%, #ffe9a8 55%, #fff2c9 78%, #d4af37 100%);
background-size: 280% 100%;
animation: abo-btn-shine 6s ease-in-out infinite;
box-shadow: 0 10px 26px -12px rgba(212,175,55,.55);
transition: transform .2s ease, box-shadow .2s ease;
}
.abo-panel .btn::before { content: none; }
.abo-panel .btn::after {
content: ""; position: absolute; inset: 0; z-index: 1;
background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.85) 48%, transparent 66%);
background-size: 240% 100%;
mix-blend-mode: overlay;
animation: abo-btn-glare 3.2s ease-in-out infinite;
pointer-events: none;
}
.abo-panel .btn:hover {
transform: translateY(-3px) scale(1.015);
box-shadow: 0 16px 34px -10px rgba(212,175,55,.65), 0 0 26px rgba(212,175,55,.4);
}
@keyframes abo-btn-shine { 0%, 100% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } }
@keyframes abo-btn-glare { 0% { background-position: -80% 0; } 100% { background-position: 180% 0; } }
@media (prefers-reduced-motion: reduce) { .abo-panel .btn { animation: none; } .abo-panel .btn::after { animation: none; } }
.abo-fehler { color: #ff9a9a; font-size: .88rem; margin: .8rem 0 0; display: none; }
.abo-fehler.zeige { display: block; }
.abo-erfolg { color: #baf7d6; font-size: .88rem; margin: .8rem 0 0; display: none; }
.abo-erfolg.zeige { display: block; }
.abo-switch { text-align: center; margin-top: 1.4rem; font-size: .92rem; color: var(--text-muted); }
.abo-switch a { color: var(--accent); cursor: pointer; font-weight: 600; }
.abo-switch a:hover { text-decoration: underline; }
/* Registrieren/Einloggen-Umschalter (21.08.2026, Nutzer-Wunsch: "die
sollen dann sofort immer den anmelde knopf auswählen können") — zwei
Segmente in einer Pille, aktiver Tab in Gold hervorgehoben, damit die
Wahl sofort ins Auge fällt, noch bevor überhaupt ein Formularfeld zu
sehen ist. Augenschonend: kein grelles Weiß, ruhiger Farbwechsel. */
/* =====================================================================
Aktionsleiste (21.08.2026) — EIN Element für beide Wege, statt vorher
doppeltem "Registrieren" (Umschalter oben + Knopf unten). Links die
eigentliche Aktion in Gold (unverändert im bestehenden Marken-Look),
rechts der Wechsel in Babyblau — der zweiten Hausfarbe der Seite
(theme-dogfather.css). Beide Hälften nutzen dieselbe Technik wie die
bestehenden Gold-Knöpfe (wandernder Verlauf per background-position +
ein durchlaufender Glanzstreifen als ::after) — dadurch wirken sie wie
ein zusammengehöriges Paar statt wie zwei fremde Knöpfe.
Augenschonend (Projektregel): kein Blinken/Flackern, nur ruhig
wandernde Verläufe, und bei prefers-reduced-motion steht alles still.
===================================================================== */
.abo-aktionsleiste {
display: flex; gap: .6rem; margin-top: .4rem;
}
/* flex-basis "auto" statt "0": jede Hälfte startet bei ihrer eigenen
Textbreite und wächst dann anteilig mit. Dadurch bekommt automatisch
die Seite mit dem längeren Text mehr Platz — egal ob das gerade links
("Login-Code anfordern") oder rechts ("Schon registriert? Einloggen")
ist. Mit fester 0-Basis wären beide exakt gleich breit gewesen und der
jeweils längere Text wäre auf zwei Zeilen umgebrochen. */
.abo-aktionsleiste > button {
position: relative; isolation: isolate; overflow: hidden;
flex: 1 1 auto; min-width: 0;
display: flex; align-items: center; justify-content: center; text-align: center;
padding: 1rem .95rem; border-radius: 14px;
font-weight: 800; font-size: .92rem; line-height: 1.25;
white-space: nowrap;
cursor: pointer;
transition: transform .2s ease, box-shadow .2s ease;
}
.abo-aktionsleiste > button::after {
content: ""; position: absolute; inset: 0; z-index: 1;
background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.85) 48%, transparent 66%);
background-size: 240% 100%;
mix-blend-mode: overlay;
animation: abo-btn-glare 3.2s ease-in-out infinite;
pointer-events: none;
}
.abo-aktionsleiste > button:hover { transform: translateY(-3px) scale(1.015); }
/* Links: Gold — exakt die bestehende Knopf-Optik der Seite. */
.abo-aktion-haupt {
color: #1a1408;
border: 1.5px solid rgba(255,233,168,.65);
background: linear-gradient(120deg, #ffe9a8 0%, #d4af37 30%, #ffe9a8 55%, #fff2c9 78%, #d4af37 100%);
background-size: 280% 100%;
animation: abo-btn-shine 6s ease-in-out infinite;
box-shadow: 0 10px 26px -12px rgba(212,175,55,.55);
}
.abo-aktion-haupt:hover {
box-shadow: 0 16px 34px -10px rgba(212,175,55,.65), 0 0 26px rgba(212,175,55,.4);
}
/* Babyblau — gleiche Machart, kräftigeres Sky-Blau statt des sehr
blassen Standard-Babyblaus, damit es neben dem Gold nicht untergeht
(dieselbe Lektion wie auf der Verwaltungsseite). Dunkler Tiefsee-Text
darauf für klaren Kontrast; zusätzlich ein sanft atmender Ring, der die
Kachel leicht leuchten lässt, ohne zu blenden.
Zwei Namen, eine Optik: ".abo-aktion-neben" ist der Wechsel-Knopf neben
einer Gold-Aktion (Registrierungs-Seite), ".abo-aktion-blau" ist ein
Knopf, der selbst DIE Hauptaktion ist und trotzdem blau sein soll
(Einloggen-Seite, Nutzer-Wunsch 21.08.2026). Bewusst getrennte Namen,
damit später klar bleibt, warum ein Knopf blau ist. */
.abo-aktion-neben,
.abo-aktion-blau {
color: #05202b;
border: 1.5px solid rgba(224,247,254,.7);
background: linear-gradient(120deg, #e0f7fe 0%, #38bdf8 30%, #7dd3fc 55%, #f0fbff 78%, #38bdf8 100%);
background-size: 280% 100%;
animation: abo-btn-shine 6s ease-in-out infinite, abo-blau-atem 4.5s ease-in-out infinite;
box-shadow: 0 10px 26px -12px rgba(56,189,248,.6), 0 0 0 0 rgba(125,211,252,.5);
}
.abo-aktion-neben:hover,
.abo-aktion-blau:hover {
box-shadow: 0 16px 34px -10px rgba(56,189,248,.75), 0 0 30px rgba(125,211,252,.55);
}
@keyframes abo-blau-atem {
0%, 100% { box-shadow: 0 10px 26px -12px rgba(56,189,248,.55), 0 0 14px rgba(125,211,252,.25); }
50% { box-shadow: 0 12px 30px -12px rgba(56,189,248,.75), 0 0 26px rgba(125,211,252,.5); }
}
@media (prefers-reduced-motion: reduce) {
.abo-aktionsleiste > button,
.abo-aktionsleiste > button::after { animation: none; }
}
/* Auf schmalen Handys untereinander statt gequetscht nebeneinander. */
@media (max-width: 430px) {
.abo-aktionsleiste { flex-direction: column; gap: .55rem; }
.abo-aktionsleiste > button { font-size: .92rem; }
}
.abo-oder {
display: flex; align-items: center; gap: .8rem;
color: var(--text-muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
margin: .3rem 0 1.4rem;
}
.abo-oder::before, .abo-oder::after { content: ""; flex: 1; height: 1px; background: var(--border); }
/* Google liefert seinen Knopf als eigenes iframe aus — die Innenfarben
(Schwarz/Weiß/Blau) lassen sich aus Sicherheitsgründen NICHT per CSS
umfärben. "Größer und passend zur Seite" wird deshalb über einen
eigenen Goldrahmen drumherum gelöst (derselbe Schimmer-Trick wie beim
Preis-Rahmen) plus einer Vergrößerung des Google-Knopfs selbst per
transform:scale — der Knopf bleibt dabei voll funktionsfähig, es wird
nur optisch skaliert. */
.abo-google-frame {
display: flex; justify-content: center; margin: 0 auto 1.3rem; padding: .55rem 0;
border-radius: 999px; border: 1.5px solid transparent;
background: linear-gradient(rgba(20,23,29,.5), rgba(20,23,29,.5)) padding-box,
linear-gradient(120deg, #d4af37, #ffe9a8, #d4af37, #fff2c9) border-box;
background-size: 100% 100%, 260% 100%;
animation: abo-btn-shine 6s ease-in-out infinite;
box-shadow: 0 10px 26px -12px rgba(212,175,55,.5);
}
.abo-google-frame > div { transform: scale(1.18); transform-origin: center; }
.abo-vorteile { max-width: 520px; margin: 1.6rem auto 0; display: flex; flex-direction: column; gap: .6rem; }
.abo-vorteile li { display: flex; gap: .65rem; align-items: baseline; color: var(--text-muted); font-size: .97rem; line-height: 1.5; }
.abo-vorteile li::before { content: "👑"; flex-shrink: 0; }
</style>
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-abonnieren.jpg');--page-bg-mobile:url('/assets/img/bg-abonnieren-mobile.jpg');">
<div id="site-header"></div>
<main>
<section class="hero page-bg">
<div class="hero-banner"><img src="/assets/img/bg-abonnieren.jpg" alt="Dogfather Supporter-Abo" loading="eager" /></div>
<div class="container">
<span class="eyebrow" data-i18n="ab_eyebrow">👑 DogiCrew 🩵</span>
<h1 data-i18n="ab_h1">Mehr als ein Abo — deine Unterstützung wird Teil unserer Geschichte</h1>
<p class="lead worlds" data-i18n="ab_lead">Kein gewöhnlicher Shop. Ein exklusives Dankeschön- und Treueprogramm für alle, die Dogfather dauerhaft unterstützen.</p>
</div>
</section>
<section class="section-tight">
<div class="container">
<div class="card abo-preis-box">
<div class="abo-logo-glut" aria-hidden="true"></div>
<div class="abo-logo-wz" aria-hidden="true"></div>
<span class="eyebrow" data-i18n="ab_preis_eyebrow">DogiCrew 🩵</span>
<!-- Bugfix/Nutzer-Wunsch 20.08.2026: Preis noch NICHT zeigen, solange die
Registrierung gesperrt ist (siehe .btn-gold-locked weiter unten) — sonst
wäre der Preis schon öffentlich, bevor überhaupt jemand abschließen kann.
Gleicher Chrome-Gold-Schimmer-Text wie beim gesperrten Knopf
(.btn-gold-locked-text), hier freistehend statt in einem Knopf. -->
<div class="abo-preis-zahl"><span class="btn-gold-locked-text" style="font-size:1em;" data-i18n="ab_kollektion_coming_soon">Coming soon…</span></div>
<p class="small muted" style="margin-top:-.6rem;margin-bottom:1rem;">Keine gesondert ausgewiesene MwSt. gemäß Kleinunternehmerregelung (Art. 57 des luxemburgischen TVA-Gesetzes) — keine weiteren Kosten.</p>
<ul class="abo-bedingungen">
<li data-i18n="ab_bed1">Monatliche automatische Zahlung über PayPal</li>
<li data-i18n="ab_bed2">Keine Mindestlaufzeit, jederzeit kündbar</li>
<li data-i18n="ab_bed3">Kündigung zum Ende des bereits bezahlten Zeitraums</li>
<li data-i18n="ab_bed4">Keine versteckten Zusatzkosten</li>
</ul>
<!-- Zustand 1: lädt (kurz, bis localStorage/Session geprüft ist) -->
<div class="abo-panel" id="panel-lade">
<p class="small" data-i18n="ab_laedt">Einen Moment …</p>
</div>
<!-- Professioneller Login: Google, sitzt über Registrierung/Login und
wird zusammen mit ihnen ein-/ausgeblendet (siehe Skript unten).
Ohne konfigurierte GOOGLE_CLIENT_ID bleibt der Bereich leer/
unsichtbar — der normale E-Mail-Code-Weg funktioniert immer. -->
<div class="abo-panel" id="panel-social-login" hidden>
<!-- Datenschutz: Googles Skript (und damit eine Verbindung/IP-Übertragung
zu Google) wird bewusst NICHT automatisch beim Seitenaufruf geladen,
sondern erst nach explizitem Klick hier — echtes Opt-in statt
versteckter Drittanbieter-Verbindung vor jeder Zustimmung. -->
<div id="google-consent-wrap" style="text-align:center;margin-bottom:1.1rem;" hidden>
<button type="button" id="google-consent-btn" class="btn btn-primary" style="width:100%;display:inline-flex;align-items:center;justify-content:center;gap:.6rem;">
<svg width="20" height="20" viewBox="0 0 48 48" aria-hidden="true" style="flex-shrink:0;">
<path fill="#FFC107" d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12s5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24s8.955,20,20,20s20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"/>
<path fill="#FF3D00" d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"/>
<path fill="#4CAF50" d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"/>
<path fill="#1976D2" d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"/>
</svg>
<span data-i18n="ab_google_consent_btn">Mit Google anmelden</span>
</button>
<p class="small muted" style="margin-top:.5rem;" data-i18n="ab_google_consent_hint">Beim Klick wird eine Verbindung zu Google hergestellt (deine IP-Adresse wird dabei übertragen).</p>
</div>
<div class="abo-google-frame" id="google-frame-wrap" hidden>
<div id="google-signin-button"></div>
</div>
<div class="abo-oder" data-i18n="ab_oder_email">oder mit E-Mail</div>
</div>
<!-- Zustand 2: Registrierung -->
<div class="abo-panel" id="panel-register" hidden>
<!-- Bugfix/Nutzer-Wunsch 20.08.2026 (Nachpruefung): Nur der Knopf war
gesperrt, die drei Felder ließen sich trotzdem antippen und
beschreiben ("ich kann immer nur was rein tippen, soll garnicht
möglich sein"). Jetzt wie das E-Mail-Feld im Login-Panel: disabled
statt nur readonly, damit auch Tastatur-/Screenreader-Nutzung
korrekt "nicht bedienbar" meldet. -->
<div class="abo-field">
<label for="reg-name" data-i18n="ab_feld_name">Name oder Anzeigename</label>
<input type="text" id="reg-name" maxlength="60" />
</div>
<div class="abo-field">
<label for="reg-tiktok" data-i18n="ab_feld_tiktok">TikTok-Benutzername</label>
<input type="text" id="reg-tiktok" placeholder="@benutzername" maxlength="40" />
</div>
<div class="abo-field">
<label for="reg-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
<input type="email" id="reg-email" maxlength="120" />
</div>
<!-- Das Zugangscode-Feld stand bis 21.08.2026 hier, direkt neben den
Stammdaten. Es ist bewusst in einen eigenen, SPÄTEREN Schritt
gewandert (Nutzer-Wunsch: "bei der ersten registrierung sollen
die eine email bekommen mit einem einmaligen code damit wir auch
wissen dass email stimmt und danach wenn sie den code eingegeben
haben sollen die erst ihren zugangscode auswählen/eintippen
können"). Ablauf jetzt: hier Stammdaten -> Einmalcode per Mail
(#panel-code) -> Zugangscode festlegen (#panel-neuer-code). -->
<p class="small muted" style="margin:-.2rem 0 .2rem;" data-i18n="ab_reg_hinweis_bestaetigung">Wir schicken dir gleich einen 6-stelligen Code per E-Mail. Danach legst du deinen eigenen Zugangscode fest.</p>
<!-- Nutzer-Wunsch 21.08.2026: "die leute sollen sich auch schon
registrieren können, aber der abo soll noch zu sein, da soll
dann coming soon stehen." Registrierung/Login sind ab jetzt
offen -- Konten können angelegt werden. Gesperrt bleibt NUR
noch der eigentliche Zahlungsschritt danach (siehe
panel-subscribe weiter unten), weil ohne Dogis echte PayPal-
Business-Zugangsdaten niemand wirklich zahlen kann. Wieder
normaler .btn-primary-Stil statt .btn-gold-locked. -->
<!-- Aktionsleiste (21.08.2026, Nutzer-Wunsch: "bringt nichts 2 mal
registrieren da zu haben"). EIN Element statt vorher Umschalter
oben + Knopf unten: links die eigentliche Aktion in Gold,
rechts der Wechsel zum Einloggen in Babyblau. Beide Hälften
sind immer aktiv (kein Tab-Zustand) — links macht etwas,
rechts wechselt die Ansicht. -->
<div class="abo-aktionsleiste">
<button type="button" class="abo-aktion-haupt" id="btn-register" data-i18n="ab_btn_register">Registrieren</button>
<button type="button" class="abo-aktion-neben" id="tab-login" data-i18n="ab_tab_login">Schon registriert? Einloggen</button>
</div>
<p class="abo-fehler" id="register-fehler"></p>
</div>
<!-- Zustand 3: Einloggen mit E-Mail + eigenem Zugangscode.
Umgestellt 21.08.2026 (Nutzer-Wunsch: "wieso sollte ich wieder
ein code bekommen, mach die anmeldung einfach"): vorher wurde
auch hier bei JEDEM Login erst ein Einmalcode per E-Mail
verschickt — für längst registrierte Leute ein völlig
unnötiger Umweg. Der E-Mail-Weg existiert weiterhin, aber nur
noch als "Code vergessen?" (siehe Link unten). -->
<div class="abo-panel" id="panel-login" hidden>
<div class="abo-field">
<label for="login-email" data-i18n="ab_feld_email">E-Mail-Adresse</label>
<input type="email" id="login-email" maxlength="120" autocomplete="email" />
</div>
<div class="abo-field">
<label for="login-code" data-i18n="ab_feld_code_eigen">Dein Zugangscode</label>
<input type="password" id="login-code" maxlength="200" autocomplete="current-password" />
</div>
<!-- Nutzer-Wunsch 21.08.2026: "da soll nur noch einloggen sein und
der knopf soll auch blau sein, der neu registriert soll weg."
Hier steht deshalb nur EIN Knopf, in Babyblau statt Gold —
wer auf dieser Ansicht gelandet ist, will einloggen; der Weg
zurück zur Registrierung führt über den Zurück-Knopf des
Browsers bzw. den blauen Knopf auf der Registrierungs-Seite. -->
<div class="abo-aktionsleiste">
<button type="button" class="abo-aktion-blau" id="btn-login" data-i18n="ab_btn_login">Einloggen</button>
</div>
<p class="abo-fehler" id="login-fehler"></p>
<p class="abo-switch"><a id="code-vergessen" data-i18n="ab_code_vergessen">Code vergessen?</a></p>
</div>
<!-- Zustand 4: Einmalcode aus der E-Mail eingeben. Kommt seit
21.08.2026 NUR noch im "Code vergessen?"-Weg vor — die normale
Registrierung/Anmeldung braucht ihn nicht mehr. -->
<div class="abo-panel" id="panel-code" hidden>
<p class="small" id="code-hinweis" data-i18n="ab_code_hinweis">Wir haben dir einen 6-stelligen Code per E-Mail geschickt.</p>
<div class="abo-field">
<label for="code-input" data-i18n="ab_feld_code">Code</label>
<input type="text" id="code-input" inputmode="numeric" maxlength="6" style="letter-spacing:4px;font-size:1.3rem;text-align:center;" />
</div>
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-code-verify" data-i18n="ab_btn_code_verify">Bestätigen</button>
<p class="abo-fehler" id="code-fehler"></p>
</div>
<!-- Zustand 4b: neuen dauerhaften Zugangscode festlegen. Letzter
Schritt des "Code vergessen?"-Weges — an dieser Stelle ist man
durch den Einmalcode bereits eingeloggt, es fehlt nur noch der
neue Code für künftige Anmeldungen. -->
<div class="abo-panel" id="panel-neuer-code" hidden>
<p class="small" data-i18n="ab_neuer_code_hinweis">Fast fertig — leg jetzt deinen neuen Zugangscode fest.</p>
<div class="abo-field">
<label for="neuer-code" data-i18n="ab_feld_code_neu">Dein Zugangscode (mindestens 6 Zeichen)</label>
<input type="password" id="neuer-code" maxlength="200" autocomplete="new-password" />
</div>
<button type="button" class="btn btn-primary" style="width:100%;" id="btn-neuer-code" data-i18n="ab_btn_code_speichern">Zugangscode speichern</button>
<p class="abo-fehler" id="neuer-code-fehler"></p>
</div>
<!-- Zustand 5: eingeloggt, noch kein Abo -> PayPal Smart Buttons.
Zeigt automatisch alle für die Käuferin/den Käufer verfügbaren
Zahlarten (PayPal-Konto, Kreditkarte als Gast, je nach Land) —
das Geld landet dabei direkt auf Dogis eigenem PayPal-Konto. -->
<div class="abo-panel" id="panel-subscribe" hidden>
<p class="abo-erfolg zeige" data-i18n="ab_eingeloggt_als">Eingeloggt.</p>
<!-- Nutzer-Wunsch 21.08.2026: "die leute sollen sich schon registrieren
können, aber der abo soll noch zu sein, da soll dann coming soon
stehen." Konto ist angelegt/Login hat geklappt -- aber die eigent-
liche Zahlung (PayPal) bleibt gesperrt, bis Dogis echte PayPal-
Business-Zugangsdaten stehen. Die echten Zahlungs-Elemente (Preis-
Hinweis, Widerrufs-Checkbox, PayPal-Container) bleiben unten UNVER-
ÄNDERT im DOM (nur per hidden versteckt), damit das Skript weiter
unten (Checkbox-Listener, ladePaypalButtons() usw.) unangetastet
funktioniert -- einfach das hidden am Wrapper unten entfernen,
sobald PayPal bereitsteht. -->
<div class="btn-gold-locked" style="width:100%;margin-top:.4rem;cursor:default;">
<span class="btn-gold-locked-lock" aria-hidden="true">🔒</span>
<span class="btn-gold-locked-text" data-i18n="ab_kollektion_coming_soon">Coming soon…</span>
</div>
<p class="small muted" style="text-align:center;margin-top:.9rem;" data-i18n="ab_subscribe_locked_hinweis">Dein Konto ist bereit — die Bezahlung startet, sobald das Abo offiziell live geht. Schau einfach bald wieder vorbei.</p>
<!-- Nutzer-Wunsch 21.08.2026: "ich will dass jetzt alles verbunden
ist so dass man mit den klicks pro klick weiter kommt" — vorher
war hier eine Sackgasse (kein Link/Knopf mehr nach dem Login).
Jetzt zwei klare Wege weiter: zurück zur Startseite, oder
direkt zum Streamplan (naheliegendster nächster Klick für
jemanden, der sich gerade für Dogi interessiert). -->
<div class="btn-row" style="justify-content:center;margin-top:1.4rem;">
<a class="btn btn-outline" href="index.html" data-i18n="ab_weiter_startseite">Zur Startseite →</a>
<a class="btn btn-outline" href="streamplan.html" data-i18n="ab_weiter_streamplan">Zum Streamplan →</a>
</div>
<div hidden>
<p class="small" style="text-align:center;font-weight:700;margin-bottom:.8rem;">4,99 € / Monat · zahlungspflichtiges Abo · monatlich kündbar</p>
<label class="abo-widerruf-check" for="widerruf-consent">
<input type="checkbox" id="widerruf-consent" />
<span>Ich wünsche ausdrücklich, dass der Zugang zum Supporter-Bereich sofort mit Vertragsschluss beginnt, und weiß, dass mein 14-tägiges Widerrufsrecht dadurch erlischt, sobald der Zugang vollständig bereitgestellt ist (Details in den <a href="agb.html" target="_blank" rel="noopener">AGB</a>, Ziffer 6).</span>
</label>
<div id="paypal-button-container" class="paypal-gesperrt"></div>
<p class="small muted" id="paypal-gesperrt-hinweis">Bitte bestätige oben die Checkbox, um die Zahlungsoptionen freizuschalten.</p>
<p class="small" id="paypal-laedt" data-i18n="ab_paypal_laedt" hidden>Zahlungsoptionen werden geladen …</p>
<p class="abo-fehler" id="subscribe-fehler"></p>
</div>
</div>
<!-- Zustand 6: bereits Supporter -->
<div class="abo-panel" id="panel-already" hidden>
<p class="abo-erfolg zeige" data-i18n="ab_bereits_supporter">Du bist bereits Supporter! 🩵</p>
<a class="btn btn-primary" style="width:100%;" href="supporter.html" data-i18n="ab_btn_zum_bereich">Zum Supporter-Bereich</a>
</div>
</div>
</div>
</section>
<section class="section-tight">
<div class="container grid grid-2">
<div class="card">
<p data-i18n="ab_text1">Dieses Abonnement ist kein gewöhnlicher Shop und kein einfacher Kauf. Es ist für die Menschen gedacht, die Dogfather dauerhaft unterstützen und ein besonderer Teil dieser gemeinsamen Reise sein möchten.</p>
<!-- Bugfix/Nutzer-Wunsch 20.08.2026: "4,99 €" hier entfernt (nur die Zahl, Rest
des Satzes unverändert) — sonst hätte der eigentlich gesperrte Preis
weiter oben in der Preis-Box hier im Fließtext trotzdem gestanden. -->
<p data-i18n="ab_text2">Mit deinem monatlichen Beitrag erhältst du Zugang zu einem exklusiven Supporter-Bereich, besonderen Inhalten und einer persönlichen Treuekollektion. Mit jedem erfolgreich bezahlten Meilenstein wächst deine Sammlung: vom kleinen Hasen-Teddy über besondere Jubiläumsprämien bis zum exklusiven Hoodie nach 30 Monaten. Danach beginnt für dich eine neue Kollektion — mit neuen Designs, neuen Erinnerungen und derselben Bedeutung.</p>
<ul class="abo-vorteile">
<li data-i18n="ab_vorteil1">Exklusiver Supporter-Bereich</li>
<li data-i18n="ab_vorteil2">Kleiner Hasen-Teddy nach 6 bezahlten Monaten</li>
<li data-i18n="ab_vorteil3">Mittelgroßer Hasen-Teddy nach 12 bezahlten Monaten</li>
<li data-i18n="ab_vorteil4">Großer Hasen-Teddy nach 18 bezahlten Monaten</li>
<li data-i18n="ab_vorteil5">Tasse &amp; Autogrammkarte nach 24 bezahlten Monaten</li>
<li data-i18n="ab_vorteil6">Exklusiver Hoodie nach 30 bezahlten Monaten</li>
<li data-i18n="ab_vorteil7">Neue Supporter-Kollektion nach jedem abgeschlossenen Zyklus</li>
</ul>
</div>
<div class="card">
<h2 data-i18n="ab_vanvan_h2">Die Teddy-Kollektion — in Kooperation mit VanVan</h2>
<p data-i18n="ab_vanvan_text">Die DogiCrew 🩵 gehört Dogfather. Die Hasen-Teddys und die dazugehörigen Sammlerkollektionen entstehen jedoch in Zusammenarbeit mit VanVan — der rechten Hand von Dogfather, seiner wichtigsten Vertrauensperson und einem festen Bestandteil der Team-Dogi-Familie. Sie stehen für ihre besondere Rolle, den Zusammenhalt innerhalb der Community und die Verbindung zwischen Dogfather, VanVan und allen Menschen, die diesen gemeinsamen Weg dauerhaft unterstützen.</p>
<div style="display:flex;justify-content:flex-end;margin-top:1.4rem;">
<a class="btn-silver" href="#" aria-disabled="true" onclick="return false;"><span class="btn-silver-spark" aria-hidden="true">✨</span><span class="btn-silver-text" data-i18n="ab_kollektion_coming_soon">Coming soon…</span><span class="btn-silver-spark" aria-hidden="true">✨</span></a>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-abonnieren.js?v=20260821r"></script>
<script src="assets/js/supporter.js?v=20260821r"></script>
<script src="assets/js/main.js?v=20260821r"></script>
<script>
(function () {
const S = window.DogiSupporter;
const panels = ["panel-lade", "panel-register", "panel-login", "panel-code", "panel-neuer-code", "panel-subscribe", "panel-already"];
// panel-social-login (Google-Button) hängt sich an register/login dran,
// ist aber kein "richtiger" exklusiver Zustand — separat gesteuert.
const SOCIAL_LOGIN_PANELS = ["panel-register", "panel-login"];
let publicConfig = {
googleClientId: null,
paypalClientId: null, paypalPlanId: null,
};
function irgendeinSocialLoginAktiv() {
return !!publicConfig.googleClientId;
}
function zeige(id) {
panels.forEach((p) => { document.getElementById(p).hidden = p !== id; });
const socialArea = document.getElementById("panel-social-login");
socialArea.hidden = !(irgendeinSocialLoginAktiv() && SOCIAL_LOGIN_PANELS.includes(id));
// Bugfix/Rechts-Update 04.08.2026: PayPal-Zahlungsoptionen werden NICHT
// mehr automatisch beim Anzeigen dieser Kiste geladen — erst nach
// aktivem Ankreuzen der Widerrufsrecht-Checkbox (siehe
// widerrufCheckbox-Listener unten). Bei jedem erneuten Aufruf dieser
// Kiste wird die Checkbox zurückgesetzt, die Zustimmung gilt immer nur
// für den jeweils aktuellen Besuch.
if (id === "panel-subscribe") {
const check = document.getElementById("widerruf-consent");
if (check) check.checked = false;
document.getElementById("paypal-button-container").classList.add("paypal-gesperrt");
document.getElementById("paypal-gesperrt-hinweis").hidden = false;
}
}
function fehlerZeigen(el, msg) {
el.textContent = msg;
el.classList.add("zeige");
}
function fehlerVerstecken(el) {
el.classList.remove("zeige");
}
// Merkt sich die E-Mail-Adresse zwischen "Code vergessen?" und der
// Eingabe des Einmalcodes (der frühere codePurpose-Schalter entfiel mit
// der Umstellung 21.08.2026 — es gibt nur noch diesen einen Code-Weg).
let codeEmail = "";
// Merkt sich, WARUM gerade ein Einmalcode eingegeben wird (21.08.2026):
// "register" = Erstbestätigung der E-Mail, "login" = "Code vergessen?".
// Steuert in btn-code-verify, welcher Endpunkt aufgerufen wird.
let codeZweck = "login";
async function pruefeStatus() {
if (!S.getToken()) {
zeige("panel-register");
return;
}
try {
const data = await S.apiFetch("/supporter/me", {}, true);
const status = data.subscription?.status || "none";
S.cacheSubStatus(status);
S.cacheDisplayName(data.profile?.displayName || "");
/* Nutzer-Wunsch 21.08.2026: "anstatt das was du jetzt auf dem screen
siehst, sollen die leute nicht mehr sehen wenn sie angemeldet sind.
sie sollen die richtige seite sehen die wir schon mal vorbereitet
hatten, wo sie ihre persönliche daten sehen und so."
Wer eingeloggt ist, gehört nicht mehr auf diese Anmeldeseite —
egal ob mit oder ohne bezahltes Abo. supporter.html zeigt in BEIDEN
Fällen das Richtige: persönliche Daten, Fortschritt mit allen
Prämien-Meilensteinen, Sammlungsarchiv, Profil bearbeiten,
Ausloggen — bei "kein Abo" eben mit entsprechendem Hinweis statt
echter Laufzeit. Vorher landete man hier stattdessen in einer
Sackgasse ("Coming soon…"), ohne je die eigentliche Seite zu sehen.
replace() statt href: die Anmeldeseite verschwindet damit aus dem
Verlauf — ein "Zurück" führt sonst sofort wieder hierher und von
hier wieder weiter, was sich wie eine Schleife anfühlt. */
location.replace("supporter.html");
return;
} catch (err) {
// Token ungültig/abgelaufen -> zurück zur Registrierung/Login
S.clearToken();
zeige("panel-register");
}
}
/* ---------- "Mit Google anmelden" (professioneller Login, Abschnitt
"screen1" Feedback vom 03.08.2026) — komplett optional, taucht nur
auf, wenn der Websitebetreiber eine GOOGLE_CLIENT_ID hinterlegt hat.
Datenschutz (Nachtrag 04.08.2026): Googles Skript wird NICHT mehr
automatisch beim Seitenaufruf nachgeladen — das würde eine Verbindung
zu Google (inkl. IP-Übertragung) herstellen, bevor der/die Besucher:in
überhaupt zugestimmt hat. Stattdessen zeigt initGoogleConsent() nur
einen eigenen, klar beschrifteten Button; erst der echte Klick darauf
lädt Googles Skript und rendert deren echten Button (initGoogleSignIn). */
function initGoogleConsent() {
const wrap = document.getElementById("google-consent-wrap");
const btn = document.getElementById("google-consent-btn");
if (!publicConfig.googleClientId || !wrap || !btn) return;
wrap.hidden = false;
btn.addEventListener("click", () => {
wrap.hidden = true;
document.getElementById("google-frame-wrap").hidden = false;
initGoogleSignIn();
}, { once: true });
}
function initGoogleSignIn() {
if (!publicConfig.googleClientId) return;
const script = document.createElement("script");
script.src = "https://accounts.google.com/gsi/client";
script.async = true;
script.defer = true;
script.onload = () => {
window.google.accounts.id.initialize({
client_id: publicConfig.googleClientId,
callback: handleGoogleCredential,
});
window.google.accounts.id.renderButton(document.getElementById("google-signin-button"), {
type: "standard", theme: "filled_black", size: "large", shape: "pill", text: "continue_with", width: 340,
});
};
document.head.appendChild(script);
}
async function handleGoogleCredential(response) {
const el = document.getElementById("register-fehler");
fehlerVerstecken(el);
try {
const data = await S.apiFetch("/supporter/google-login", { credential: response.credential });
S.setToken(data.token);
await pruefeStatus();
} catch (err) {
fehlerZeigen(el, err.message);
}
}
/* ---------- PayPal Smart Payment Buttons (Abschnitt "screen2" Feedback
vom 03.08.2026: alle in Deutschland üblichen Zahlarten anzeigen — das
übernimmt PayPal automatisch, sobald die echten Buttons statt einer
reinen Weiterleitung genutzt werden). ---------- */
let paypalSdkGeladen = false;
// Zeitpunkt der Widerrufsrecht-Zustimmung (siehe Checkbox-Listener weiter
// unten) — wird beim Abschluss an den Server mitgegeben, damit ein
// Zeitstempel-Nachweis existiert (siehe Datenschutzerklärung Ziffer 7).
let widerrufConsentAt = null;
function ladePaypalButtons() {
const fehlerEl = document.getElementById("subscribe-fehler");
const ladeHinweis = document.getElementById("paypal-laedt");
fehlerVerstecken(fehlerEl);
ladeHinweis.hidden = false;
ladeHinweis.textContent = "Zahlungsoptionen werden geladen …";
if (!publicConfig.paypalClientId || !publicConfig.paypalPlanId) {
ladeHinweis.textContent = "Das Bezahlsystem wird gerade eingerichtet — bitte später erneut versuchen.";
return;
}
if (paypalSdkGeladen) {
renderPaypalButtons();
return;
}
const script = document.createElement("script");
script.src = `https://www.paypal.com/sdk/js?client-id=${encodeURIComponent(publicConfig.paypalClientId)}&vault=true&intent=subscription&currency=EUR&locale=de_DE`;
script.onload = () => { paypalSdkGeladen = true; renderPaypalButtons(); };
script.onerror = () => { ladeHinweis.textContent = "PayPal konnte nicht geladen werden. Bitte Seite neu laden."; ladeHinweis.hidden = false; };
document.body.appendChild(script);
}
function renderPaypalButtons() {
const container = document.getElementById("paypal-button-container");
const ladeHinweis = document.getElementById("paypal-laedt");
container.innerHTML = "";
window.paypal
.Buttons({
style: { shape: "pill", color: "gold", layout: "vertical", label: "subscribe" },
createSubscription: (data, actions) => actions.subscription.create({ plan_id: publicConfig.paypalPlanId }),
onApprove: async (data) => {
const fehlerEl = document.getElementById("subscribe-fehler");
try {
await S.apiFetch("/supporter/paypal/confirm", { subscriptionId: data.subscriptionID, widerrufConsentAt }, true);
await pruefeStatus();
} catch (err) {
fehlerZeigen(fehlerEl, err.message);
}
},
onError: (err) => {
fehlerZeigen(document.getElementById("subscribe-fehler"), "PayPal-Fehler: " + (err?.message || "Unbekannter Fehler."));
},
})
.render("#paypal-button-container");
ladeHinweis.hidden = true;
}
// Der frühere Gegenstück-Knopf "Neu registrieren" auf der Einloggen-Seite
// wurde am 21.08.2026 auf Wunsch entfernt — deshalb hier nur noch der eine
// Wechsel von der Registrierung zum Einloggen.
document.getElementById("tab-login").addEventListener("click", () => zeige("panel-login"));
// Widerrufsrecht-Checkbox: erst nach aktivem Ankreuzen werden die
// PayPal-Zahlungsoptionen überhaupt geladen/klickbar (siehe agb.html
// Ziffer 6 + Datenschutzerklärung Ziffer 7). Beim Abwählen wird alles
// wieder gesperrt und der Zeitstempel verworfen.
document.getElementById("widerruf-consent").addEventListener("change", (e) => {
const container = document.getElementById("paypal-button-container");
const gesperrtHinweis = document.getElementById("paypal-gesperrt-hinweis");
if (e.target.checked) {
widerrufConsentAt = new Date().toISOString();
container.classList.remove("paypal-gesperrt");
gesperrtHinweis.hidden = true;
ladePaypalButtons();
} else {
widerrufConsentAt = null;
container.classList.add("paypal-gesperrt");
gesperrtHinweis.hidden = false;
}
});
document.getElementById("btn-register").addEventListener("click", async () => {
const el = document.getElementById("register-fehler");
fehlerVerstecken(el);
const displayName = document.getElementById("reg-name").value.trim();
const tiktokUsername = document.getElementById("reg-tiktok").value.trim();
const email = document.getElementById("reg-email").value.trim();
if (!displayName || !email) return fehlerZeigen(el, "Bitte Name und E-Mail-Adresse angeben.");
try {
// Schritt 1 von 3 (21.08.2026): /supporter/register legt nur das noch
// unbestätigte Konto an und verschickt einen Einmalcode -- es kommt
// bewusst KEIN Token zurück, eingeloggt ist man erst nach Schritt 2.
await S.apiFetch("/supporter/register", { displayName, tiktokUsername, email });
codeEmail = email;
codeZweck = "register";
zeige("panel-code");
} catch (err) {
fehlerZeigen(el, err.message);
}
});
// Normale Anmeldung: E-Mail + eigener Zugangscode, ohne Umweg über E-Mail.
document.getElementById("btn-login").addEventListener("click", async () => {
const el = document.getElementById("login-fehler");
fehlerVerstecken(el);
const email = document.getElementById("login-email").value.trim();
const accessCode = document.getElementById("login-code").value;
if (!email || !accessCode) return fehlerZeigen(el, "Bitte E-Mail-Adresse und Zugangscode eingeben.");
try {
const data = await S.apiFetch("/supporter/login-code", { email, accessCode });
S.setToken(data.token);
await pruefeStatus();
} catch (err) {
fehlerZeigen(el, err.message);
}
});
// "Code vergessen?" — hier lebt der alte E-Mail-Einmalcode-Weg weiter.
document.getElementById("code-vergessen").addEventListener("click", async () => {
const el = document.getElementById("login-fehler");
fehlerVerstecken(el);
const email = document.getElementById("login-email").value.trim();
if (!email) return fehlerZeigen(el, "Bitte zuerst deine E-Mail-Adresse eingeben.");
try {
await S.apiFetch("/supporter/login-request", { email });
codeEmail = email;
codeZweck = "login";
zeige("panel-code");
} catch (err) {
fehlerZeigen(el, err.message);
}
});
document.getElementById("btn-code-verify").addEventListener("click", async () => {
const el = document.getElementById("code-fehler");
fehlerVerstecken(el);
const code = document.getElementById("code-input").value.trim();
if (!code) return fehlerZeigen(el, "Bitte Code eingeben.");
try {
// Dieselbe Code-Eingabe bedient zwei Wege (21.08.2026):
// "register" -> Erstbestätigung der E-Mail bei der Registrierung
// "login" -> "Code vergessen?"-Weg für bestehende Konten
// Beide enden gleich: eingeloggt, danach Zugangscode festlegen.
const endpunkt = codeZweck === "register" ? "/supporter/verify-email" : "/supporter/verify-login";
const data = await S.apiFetch(endpunkt, { email: codeEmail, code });
S.setToken(data.token);
zeige("panel-neuer-code");
} catch (err) {
fehlerZeigen(el, err.message);
}
});
document.getElementById("btn-neuer-code").addEventListener("click", async () => {
const el = document.getElementById("neuer-code-fehler");
fehlerVerstecken(el);
const accessCode = document.getElementById("neuer-code").value;
if (!accessCode) return fehlerZeigen(el, "Bitte leg einen Zugangscode fest.");
try {
await S.apiFetch("/supporter/set-access-code", { accessCode }, true);
await pruefeStatus();
} catch (err) {
fehlerZeigen(el, err.message);
}
});
async function init() {
try {
const res = await fetch(`${S.API_BASE}/supporter/public-config`);
const data = await res.json();
if (data.ok) publicConfig = data;
} catch {
/* Konfiguration konnte nicht geladen werden — Google-Button/PayPal-Buttons bleiben dann aus. */
}
/* Bugfix 20.08.2026, wieder aktiviert 21.08.2026: "Mit Google anmelden" war ein
dritter, bisher übersehener Weg, trotz gesperrtem Registrieren-/Login-Knopf
doch ein echtes Konto anzulegen bzw. sich einzuloggen. War deshalb deaktiviert,
solange die Registrierung selbst gesperrt war. Registrierung ist jetzt (Nutzer-
Wunsch 21.08.2026) bewusst offen -- Google-Login ist nur ein weiterer,
gleichwertiger Weg zum selben jetzt erlaubten Ziel, kein Schlupfloch mehr. */
initGoogleConsent();
await pruefeStatus();
}
init();
})();
</script>
</body>
</html>