Files
dogfather-universe/rabattcodes.html
T
DogFatherGitandClaude Opus 5 5a67ef2948 Die Rabattcodes haengen am Konto, nicht mehr am Abo
Filipe: "die partner codes sollen auch schon fuer die leute sichtbar sein
die angemeldet sind." Umgestellt, und auf Nachfrage dauerhaft: Rabattcodes
sind ab jetzt ein Konto-Vorteil, kein Abo-Vorteil.

WARUM DAS MEHR IST ALS EINE BEQUEMLICHKEIT

Der alte Riegel verlangte einen Abo-Status. Nachgesehen, statt vermutet:
Die Bezahlung auf abonnieren.html steht auf "Coming soon", bis Dogis
PayPal-Business-Zugang da ist (der Kommentar dort nennt es beim Namen).
Registrieren geht, bezahlen nicht. Der erste echte Partnercode lag damit
seit gestern hinter einer Tuer, die sich gar nicht oeffnen laesst -- er
waere fuer NIEMANDEN sichtbar gewesen ausser fuer Dogi und VanVan ueber
die Rollenvorschau.

Entschieden wird jetzt an "supporterToken" (beim Login gesetzt, beim
Logout entfernt, supporter.js). Der Abo-Stand wird als Sicherheitsnetz
weiter mitgelesen: Niemand soll Zugang verlieren, den er gestern hatte.

BEIDE STELLEN, NICHT EINE

Die Bedingung steht doppelt im Haus -- an der Kachel auf links.html und
an der Seite selbst. Nur eine davon umzustellen erzeugt einen Fehler, den
keine der beiden fuer sich zeigt: Man kaeme mit Konto auf die Seite und
saehe dort die Sperre. Beide sind umgestellt, tragen den Hinweis
aufeinander, und die Pruefung vergleicht sie in jedem Anmeldezustand
gegeneinander.

TEXTE, DIE SONST GELOGEN HAETTEN

"Nur fuer Supporter" auf einer Seite, die ein kostenloses Konto oeffnet,
schickt Leute zum Bezahlen fuer etwas, das sie umsonst bekommen. Kopf,
Vorspann, Kachelband, Beschreibung und Sperrtext sagen jetzt "Konto", in
allen fuenf Sprachen. Die Sperre bietet auf Filipes Wunsch beide Wege an:
den kostenlosen zuerst, das Abo daneben -- mit einer Zeile darunter, dass
es erst startet, wenn es offiziell live geht. Ohne die waere der zweite
Knopf eine Falle.

EIN FEHLER, DER SEIT DEM 03.08.2026 DRINSTAND

Die Pruefung meldete auf der FREIGESCHALTETEN Kachel weiter "Nur mit
Konto" statt "Freigeschaltet". Ursache: Das Skript setzte den Text
(`badge.textContent = ...`), aber applyTranslations() schreibt aus dem
data-i18n-Attribut zurueck -- und es laeuft danach noch einmal, weil
dogiSiteTexteLaden() die Texte aus der Verwaltung holt und dann neu
uebersetzt.

NACHGEMESSEN STATT HERGELEITET, und die erste Erklaerung war zu schnell:
Der Text war schon nach 50 ms falsch, also nicht "irgendwann spaeter
ueberschrieben". Der Grund ist, dass TEAM_API_BASIS auf den ECHTEN Worker
zeigt -- der Abruf gelingt selbst aus einer lokalen Testseite. Kontroll-
versuch mit blockiertem Abruf: derselbe alte Code, und das Band bleibt
korrekt. Ursache weg, Fehler weg.

Behoben, indem der SCHLUESSEL getauscht wird statt des Textes. Damit
schreibt jeder weitere Uebersetzungslauf von selbst das Richtige hin --
auch bei Sprachwechsel, wo die alte Fassung ebenfalls zurueckfiel.
Aufgefallen ist es nie, weil bis gestern niemand in den freigeschalteten
Zustand kommen konnte.

NEBENBEFUND, NICHT ANGEFASST: index.html hat dieselbe Bauart beim
Live-Status (#live-text mit data-i18n, Text per Skript gesetzt).
Gemessen ist es dort ein Wettlauf zweier Abrufe -- in meinem Lauf gewann
der Status um Haaresbreite, und ein Sprachwechsel repariert es dort
ohnehin (dogi-sprache-geaendert). Kleiner, aber echt. Auf Ansage.

pruef-rabattcodes EXIT=0 (63 Pruefungen, vorher 42). Neu darunter: drei
Anmeldezustaende statt zweier -- ausgeloggt, angemeldet ohne Abo,
angemeldet mit Abo --, jeder auf BEIDEN Seiten, dazu der Klick auf die
Kachel (fuehrt sie wirklich weiter?), die Beschriftung des Bands und als
Gegenprobe ein erzwungener Uebersetzungslauf, der den alten Fehler
zuverlaessig ausloest.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-08 21:49:16 +02:00

577 lines
24 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>Rabattcodes — DOGFATHER UNIVERSE</title>
<meta name="description" content="Rabattcodes von Dogis Partnern für Team Dogi — spar bares Geld bei ausgewählten Marken." />
<link rel="icon" type="image/png" href="/assets/img/app-symbole/universe-32.png" />
<link rel="apple-touch-icon" href="/assets/img/app-symbole/universe-180.png" />
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#065f76" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="DOGFATHER UNIVERSE" />
<meta property="og:title" content="Rabattcodes — DOGFATHER UNIVERSE" />
<meta property="og:description" content="Rabattcodes von Dogis Partnern für Team Dogi — spar bares Geld bei ausgewählten Marken." />
<meta property="og:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<meta property="og:url" content="https://dogfather-universe.com/rabattcodes.html" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Rabattcodes — DOGFATHER UNIVERSE" />
<meta name="twitter:description" content="Rabattcodes von Dogis Partnern für Team Dogi — spar bares Geld bei ausgewählten Marken." />
<meta name="twitter:image" content="https://dogfather-universe.com/assets/img/og-cover.jpg" />
<link rel="stylesheet" href="assets/css/main.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-dogfather.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-hasidog.css?v=20260828e" />
<link rel="stylesheet" href="assets/css/theme-spicymedia.css?v=20260828e" />
<style>
.code-platzhalter {
max-width: 620px;
margin: 0 auto;
text-align: center;
}
/* Gesperrt-Zustand. Er hängt seit dem 08.09.2026 am KONTO, nicht mehr
am bezahlten Abo (Filipe: "die partner codes sollen auch schon für
die leute sichtbar sein die angemeldet sind") — die Begründung steht
ausführlich beim Skript am Seitenende. Unverändert gilt: dieselbe
Prüfung wie die Kachel auf links.html, damit der direkte Aufruf
(Lesezeichen, geteilter Link) genau dasselbe zeigt wie der Knopf
dort. Zwei Sperren, die auseinanderlaufen, sind schlimmer als eine. */
.code-gesperrt-siegel {
width: 96px; height: 96px;
display: flex; align-items: center; justify-content: center;
margin: 0 auto 1.4rem;
font-size: 2.6rem;
border-radius: 50%;
background: radial-gradient(circle at 32% 28%, rgba(212,175,55,.35), rgba(11,13,16,.9) 70%);
border: 1.5px solid rgba(212,175,55,.45);
box-shadow: 0 0 0 6px rgba(212,175,55,.06), 0 10px 30px rgba(0,0,0,.5);
}
/* =====================================================================
DER ERSTE ECHTE PARTNERCODE (08.09.2026)
Bis heute war das hier eine Platzhalterkachel ("Codes folgen in
Kürze") mit einem erfundenen Beispielcode. Jetzt steht der erste
echte drauf: DOGI10 für Van's DIY & Bastelbedarf.
⚠️ DIE KONDITIONEN SIND ABGELESEN, NICHT GERATEN.
Quelle der Wahrheit ist der Shop, nicht diese Seite:
/home/vandiy/vans-diy-bastelbedarf/src/content/partnercodes.json
{ "code": "DOGI10", "prozent": 10, "bis": "2026-09-30",
"aktiv": true, "giltAufSale": false }
Ausgewertet wird sie an zwei Stellen mit derselben Regel
(aktiv !== false UND jetzt <= bis 23:59:59): src/scripts/cart.ts für
den Warenkorb, server/lib/preis-berechnen.js für den Endpreis.
Der naheliegende Weg wäre gewesen, "–10 %" einfach aus dem Namen
"DOGI10" zu schließen. Das hätte hier zufällig gestimmt und wäre
trotzdem falsch gewesen: Dass der Code NICHT auf bereits reduzierte
Artikel gilt (giltAufSale: false) und dass er am 30.09.2026 ausläuft,
steht im Namen nicht drin. Eine falsche Zusage auf einer
Verkaufsseite kostet VanVan die Diskussion an der Kasse.
Abgeschrieben ist abgeschrieben: Wer die Konditionen ändert, ändert
sie im Shop UND hier — die Zeile unten rechnet nichts aus, sie sagt
nur an. Damit das nicht schleichend auseinanderläuft, kennt das
Skript am Seitenende das Ablaufdatum und schaltet die Karte von
selbst auf "abgelaufen", statt weiter etwas zu versprechen.
===================================================================== */
.rc-partner {
max-width: 620px;
margin: 0 auto;
text-align: center;
padding: 2.4rem 1.6rem 2rem;
border-color: rgba(94, 234, 167, .3);
}
/* Eckband wie bei den Feature-Kacheln auf links.html — hier aber ohne
Rotation und in Silber statt Bunt, damit es dem Siegel nicht die
Aufmerksamkeit wegnimmt. */
.rc-band {
position: absolute;
top: 1rem; right: 1rem;
font-size: .64rem;
font-weight: 800;
letter-spacing: .09em;
text-transform: uppercase;
padding: .36rem .72rem;
border-radius: 999px;
background: rgba(8, 10, 14, .6);
border: 1px solid rgba(255, 255, 255, .24);
color: #dfe6ef;
}
/* ---- Das Medaillon --------------------------------------------------
Warum für EIN Logo so viel Aufwand: Es ist das einzige Bild auf der
Seite und das Gesicht des Partners. Der Weg zu "besonders" führt hier
NICHT über mehr Farbe — das Siegel ist schwarz-weiß, jede Einfärbung
würde eine fremde Marke umlackieren. Er führt über mehr MATERIAL: aus
dem runden Siegel wird eine geprägte Münze, aus sechs Lagen:
1. Aura weicher Lichthof dahinter, atmet sehr langsam
2. Rändel die geriffelte Münzkante (repeating-conic-gradient),
72 Zähne. Sie steht STILL — eine sich drehende
Riffelung flimmert bei 148 px, und genau diese grelle
Optik soll hier nicht sein (Grundregel: augenschonend)
3. Glanz stattdessen wandert EIN Lichtpunkt in 22 s einmal um
die Kante. Das ist die Bewegung, die eine Münze im
Licht macht, und sie ist ruhig genug fürs Auge
4. Siegel das freigestellte Logo (partner-vandiy.webp)
5. Schliff Prägekante nach innen (oben Licht, unten Schatten) und
ein langsamer Lichtstreif über die Fläche
6. Ablage elliptischer Schatten darunter, damit die Münze auf der
Karte LIEGT statt zu schweben
Alles davon ist reines CSS: kein zweites Bild, kein Skript, keine
Abhängigkeit. Und alles davon steht still, sobald der Rechner
"weniger Bewegung" meldet (Block ganz unten). */
.rc-medaillon {
position: relative;
width: clamp(116px, 34vw, 148px);
aspect-ratio: 1 / 1;
margin: .2rem auto 1.5rem;
border-radius: 50%;
transition: transform .4s cubic-bezier(.22, .9, .3, 1);
}
.rc-medaillon > * {
position: absolute;
inset: 0;
border-radius: 50%;
}
/* 1 — Aura. Silber (die Farbe des Siegels) mit einem Hauch Mint (die
Farbe, in der diese Seite über Codes spricht). */
.rc-medaillon::before {
content: "";
position: absolute;
inset: -28%;
z-index: 0;
border-radius: 50%;
background: radial-gradient(circle at 50% 45%,
rgba(226, 234, 244, .30), rgba(94, 234, 167, .11) 45%, transparent 70%);
filter: blur(12px);
animation: rc-aura-atmen 9s ease-in-out infinite;
pointer-events: none;
}
/* 6 — Ablageschatten. Steht bewusst VOR der Aura im Quelltext, damit
die Reihenfolge im Kopf der Lage entspricht, nicht der Nummer. */
.rc-medaillon::after {
content: "";
position: absolute;
left: 14%; right: 14%;
bottom: -8%;
height: 13%;
z-index: 0;
border-radius: 50%;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, .6), transparent 72%);
filter: blur(4px);
pointer-events: none;
}
/* 2 — Die geriffelte Münzkante. Der Ring entsteht aus zwei Teilen:
border-radius beschneidet den Kegelverlauf auf den Kreis, die Maske
stanzt die Mitte heraus. Das Siegel (Lage 4) deckt anschließend
alles bis zu seinem eigenen Rand ab — sichtbar bleibt genau der
Streifen, der außen übersteht. */
.rc-raendel {
inset: -10%;
z-index: 1;
background: repeating-conic-gradient(from 0deg,
rgba(228, 234, 243, .92) 0deg 2.5deg,
rgba(70, 80, 94, .8) 2.5deg 5deg);
-webkit-mask: radial-gradient(closest-side, transparent 0 82%, #000 86%);
mask: radial-gradient(closest-side, transparent 0 82%, #000 86%);
opacity: .45;
}
/* 3 — Der wandernde Lichtpunkt auf derselben Kante. */
.rc-glanz {
inset: -10%;
z-index: 2;
background: conic-gradient(from 0deg,
rgba(255, 255, 255, 0) 0deg,
rgba(255, 255, 255, 0) 248deg,
rgba(255, 255, 255, .72) 322deg,
rgba(255, 255, 255, 0) 360deg);
-webkit-mask: radial-gradient(closest-side, transparent 0 82%, #000 86%);
mask: radial-gradient(closest-side, transparent 0 82%, #000 86%);
animation: rc-glanz-dreht 22s linear infinite;
pointer-events: none;
}
/* 4 — Das Siegel selbst. */
.rc-medaillon img {
z-index: 3;
width: 100%;
height: 100%;
object-fit: contain;
filter: drop-shadow(0 10px 20px rgba(0, 0, 0, .55));
}
/* 5 — Prägekante und Lichtstreif. Der Streifen benutzt dieselbe
Bewegung wie die Marken-Kacheln auf links.html (nav-cta-shimmer aus
main.css), nur deutlich langsamer. */
.rc-schliff {
z-index: 4;
pointer-events: none;
background: linear-gradient(118deg, transparent 36%, rgba(255, 255, 255, .28) 49%, transparent 62%);
background-size: 260% 100%;
box-shadow:
inset 0 2px 4px rgba(255, 255, 255, .3),
inset 0 -8px 16px rgba(0, 0, 0, .4),
inset 0 0 0 1px rgba(255, 255, 255, .1);
animation: nav-cta-shimmer 6.5s linear infinite;
}
/* Beim Zeigen kippt die Münze leicht ins Licht. Ausgelöst wird das an
der ganzen KARTE, nicht am Medaillon: Es ist kein Knopf, und ein
Effekt, der erst kommt, wenn man ein Bild genau trifft, findet
niemand. */
.rc-partner:hover .rc-medaillon {
transform: perspective(680px) rotateX(9deg) rotateY(-7deg) scale(1.045);
}
@keyframes rc-aura-atmen {
0%, 100% { opacity: .75; transform: scale(1); }
50% { opacity: 1; transform: scale(1.05); }
}
@keyframes rc-glanz-dreht { to { transform: rotate(360deg); } }
/* ---- Name, Beschreibung ------------------------------------------- */
.rc-marke {
font-size: clamp(1.35rem, 5vw, 1.7rem);
line-height: 1.2;
margin: 0;
}
.rc-marke small {
display: block;
font-size: .58em;
font-weight: 700;
letter-spacing: .05em;
color: var(--text-muted);
margin-top: .25rem;
}
.rc-beschreibung {
margin: .7rem auto 0;
max-width: 40ch;
font-size: .97rem;
}
/* ---- Der Code selbst ----------------------------------------------
Ein Knopf, kein Textfeld: Die einzige Handlung hier ist "kopieren",
und die gehört auf etwas Drückbares — mit Tastatur erreichbar, ohne
dass jemand erst markieren muss. Der gestrichelte Rand ist der
Gutschein-Look, den diese Seite vorher schon in der Vorschaukachel
hatte; er bleibt, das Erfundene daran verschwindet. */
.rc-code {
position: relative;
display: block;
width: 100%;
max-width: 340px;
margin: 1.7rem auto 0;
padding: .9rem 1.1rem 1rem;
border: 1.5px dashed rgba(94, 234, 167, .45);
border-radius: 14px;
background: rgba(52, 211, 153, .06);
color: inherit;
font-family: inherit;
cursor: pointer;
text-align: center;
transition: border-color .2s ease, background-color .2s ease, transform .12s ease;
}
.rc-code:hover {
border-color: rgba(94, 234, 167, .8);
background: rgba(52, 211, 153, .1);
}
.rc-code:active { transform: scale(.99); }
.rc-code.ist-kopiert {
border-style: solid;
border-color: rgba(94, 234, 167, .9);
background: rgba(52, 211, 153, .16);
}
.rc-code-kopf {
display: block;
font-size: .7rem;
font-weight: 800;
letter-spacing: .16em;
text-transform: uppercase;
color: var(--text-muted);
}
.rc-code-wert {
display: block;
margin: .32rem 0 .4rem;
font-size: clamp(1.7rem, 7.5vw, 2.15rem);
font-weight: 900;
letter-spacing: .16em;
text-indent: .16em; /* gleicht den Sperrsatz hinter dem letzten Zeichen aus */
background: linear-gradient(100deg,
#7fe3b4 0%, #ffffff 26%, #d9fff1 44%, #86e9bd 62%, #ffffff 80%, #7fe3b4 100%);
background-size: 260% 100%;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
animation: btn-silver-text-shine 5.5s linear infinite; /* aus main.css */
}
.rc-code-fuss {
display: block;
font-size: .8rem;
color: var(--text-muted);
}
/* Rückmeldung steht AUSSERHALB des Knopfes und wird nicht per Skript
beschriftet, sondern nur ein- und ausgeblendet. Sonst wäre der Text
nach einem Sprachwechsel deutsch geblieben — data-i18n greift nur an
Elementen, die im HTML stehen. Die feste Höhe verhindert, dass die
Karte beim Kopieren springt. */
.rc-code-echo {
min-height: 1.4em;
margin: .5rem 0 0;
font-size: .85rem;
font-weight: 700;
color: #8ff0c0;
}
.rc-code-echo .fehler { color: #ffc9a3; }
/* ---- Konditionen --------------------------------------------------- */
.rc-fakten {
list-style: none;
margin: 1.2rem 0 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: .4rem;
font-size: .9rem;
color: var(--text-muted);
}
.rc-fakten li { display: flex; gap: .5rem; align-items: baseline; }
.rc-fakten b { color: #8ff0c0; font-weight: 800; }
/* ---- Abgelaufen ---------------------------------------------------- */
.rc-abgelaufen {
max-width: 380px;
margin: 1.2rem auto 0;
padding: .7rem 1rem;
border-radius: 12px;
border: 1.5px solid rgba(212, 175, 55, .4);
background: rgba(212, 175, 55, .08);
color: #ffe9a8;
font-size: .88rem;
}
.rc-abgelaufen[hidden] { display: none; }
.rc-partner.ist-abgelaufen .rc-code-wert {
text-decoration: line-through;
opacity: .5;
animation: none;
}
.rc-partner.ist-abgelaufen .rc-code { cursor: not-allowed; opacity: .75; }
/* Steht unter den beiden Knöpfen der Sperrkachel: ohne sie wäre
"Supporter werden" eine Falle, weil das Abo noch nicht bezahlbar
ist. */
.rc-abo-hinweis {
max-width: 44ch;
margin: 1rem auto 0;
font-size: .84rem;
color: var(--text-muted);
}
.rc-mehr {
max-width: 620px;
margin: 1.4rem auto 0;
text-align: center;
font-size: .9rem;
color: var(--text-muted);
}
@media (prefers-reduced-motion: reduce) {
.rc-medaillon,
.rc-medaillon::before,
.rc-glanz,
.rc-schliff,
.rc-code-wert { animation: none; transition: none; }
.rc-partner:hover .rc-medaillon { transform: none; }
}
</style>
</head>
<body class="mit-hintergrund" style="--page-bg:url('/assets/img/bg-links.jpg');--page-bg-mobile:url('/assets/img/bg-links-mobile.jpg');">
<div id="site-header"></div>
<main>
<section class="hero page-bg">
<div class="hero-banner"><img src="/assets/img/bg-links.jpg" alt="" loading="eager" /></div>
<div class="container">
<span class="eyebrow" data-i18n="rc_eyebrow">🔒 Nur mit Konto</span>
<h1 data-i18n="rc_h1">Rabattcodes</h1>
<p class="lead worlds" data-i18n="rc_lead">Codes von Dogis Partnern — für alle mit einem Konto im DOGFATHER UNIVERSE.</p>
</div>
</section>
<section class="section-tight">
<div class="container">
<!-- Nur für Supporter sichtbar (siehe Skript unten) -->
<div id="rc-inhalt" hidden>
<article class="card rc-partner" id="rc-partner">
<span class="rc-band" data-i18n="rc_band">Partner Nr. 1</span>
<div class="rc-medaillon">
<span class="rc-raendel" aria-hidden="true"></span>
<span class="rc-glanz" aria-hidden="true"></span>
<img src="/assets/img/partner-vandiy.webp" width="384" height="384"
alt="Logo von Van’s DIY &amp; Bastelbedarf"
data-i18n-attr="alt:rc_logo_alt" loading="lazy" decoding="async" />
<span class="rc-schliff" aria-hidden="true"></span>
</div>
<h2 class="rc-marke">VAN’S DIY<small>&amp; Bastelbedarf</small></h2>
<p class="rc-beschreibung" data-i18n="rc_beschreibung">Häkelzubehör, Bastelbedarf, DIY-Plushies und Modeschmuck — VanVans eigener Shop.</p>
<button type="button" class="rc-code" id="rc-code" data-code="DOGI10">
<span class="rc-code-kopf" data-i18n="rc_code_kopf">Dein Code</span>
<span class="rc-code-wert">DOGI10</span>
<span class="rc-code-fuss" data-i18n="rc_code_fuss">Tippen zum Kopieren</span>
</button>
<p class="rc-code-echo" role="status" aria-live="polite">
<span id="rc-echo-ok" data-i18n="rc_code_kopiert" hidden>Code kopiert ✓</span>
<span class="fehler" id="rc-echo-fehler" data-i18n="rc_code_fehler" hidden>Kopieren ging nicht — bitte von Hand abtippen.</span>
</p>
<ul class="rc-fakten">
<li><span aria-hidden="true">💸</span><span data-i18n="rc_fakt_rabatt"><b>10 %</b> Rabatt auf deinen Einkauf</span></li>
<li><span aria-hidden="true">📅</span><span data-i18n="rc_fakt_bis">Gültig bis 30.09.2026</span></li>
<li><span aria-hidden="true">ℹ️</span><span data-i18n="rc_fakt_sale">Nicht auf bereits reduzierte Artikel</span></li>
</ul>
<p class="rc-abgelaufen" id="rc-abgelaufen" hidden data-i18n="rc_abgelaufen">Dieser Code ist ausgelaufen. Dogi klärt gerade die Verlängerung — schau bald wieder rein.</p>
<div class="btn-row" style="justify-content:center;margin-top:1.6rem;">
<a class="btn btn-primary" href="https://vans-diy-bastelbedarf.com/" target="_blank" rel="noopener" data-i18n="rc_btn_shop">Zum Shop von Van’s DIY ↗</a>
<a class="btn btn-outline" href="links.html" data-i18n="rc_btn_zurueck">← Zurück zu allen Links</a>
</div>
</article>
<p class="rc-mehr" data-i18n="rc_mehr">Weitere Codes folgen, sobald die nächsten Kooperationen stehen.</p>
</div>
<!-- Nur für Ausgeloggte sichtbar (siehe Skript unten) -->
<div class="card code-platzhalter" id="rc-gesperrt" hidden>
<div class="code-gesperrt-siegel" aria-hidden="true">🔒</div>
<h2 data-i18n="rc_gesperrt_titel">Melde dich an, dann sind sie da</h2>
<p data-i18n="rc_gesperrt_text">Die Rabattcodes gibt es für alle mit einem Konto bei Dogfather — Registrieren ist kostenlos und dauert eine Minute. Der erste Partnercode wartet schon.</p>
<div class="btn-row" style="justify-content:center;margin-top:1.8rem;">
<a class="btn btn-primary" href="abonnieren.html" data-i18n="rc_btn_anmelden">Anmelden oder kostenlos registrieren</a>
<a class="btn btn-outline" href="abonnieren.html" data-i18n="rc_btn_abo">Supporter werden 👑</a>
</div>
<p class="rc-abo-hinweis" data-i18n="rc_abo_hinweis">Fürs Abo brauchst du ebenfalls erst ein Konto — es startet, sobald es offiziell live geht.</p>
<div class="btn-row" style="justify-content:center;margin-top:.6rem;">
<a class="btn btn-outline" href="links.html" data-i18n="rc_btn_zurueck">← Zurück zu allen Links</a>
</div>
</div>
</div>
</section>
</main>
<div id="site-footer"></div>
<script src="assets/js/i18n-rabattcodes.js?v=20260908a"></script>
<script src="assets/js/main.js?v=20260828e"></script>
<script>
(function () {
/* Ablaufdatum von DOGI10 — dieselbe Angabe wie "bis" in
src/content/partnercodes.json im Shop, und dieselbe Auslegung:
einschließlich dieses Tages bis 23:59:59 (server/lib/preis-
berechnen.js, istRabattAktiv).
WARUM DAS HIER ÜBERHAUPT STEHT: Ein fest eingetipptes Datum in einem
Satz ist eine Zeitbombe — es stimmt, bis der Kalender es überholt,
und ab dann verspricht die Seite etwas, das der Shop schon ablehnt.
Deshalb steht das Datum genau EINMAL als Zahl da und nicht nur als
Text, damit die Seite selbst merken kann, wann sie stillhalten muss.
Gerechnet wird in der Zeitzone des Besuchers, der Shop rechnet in
seiner. In den wenigen Stunden Unterschied kann die Seite den Code
bereits als abgelaufen zeigen, während er noch gilt — das ist die
harmlose Richtung des Fehlers. Andersherum wäre sie es nicht. */
var ABLAUF = "2026-09-30";
document.addEventListener("DOMContentLoaded", function () {
/* WER DIE CODES SIEHT — seit 08.09.2026 das KONTO, nicht das Abo.
Anlass (Filipe): "die partner codes sollen auch schon für die leute
sichtbar sein die angemeldet sind." Der alte Riegel verlangte einen
Abo-Status. Der war zu diesem Zeitpunkt für NIEMANDEN erreichbar:
Die Bezahlung auf abonnieren.html steht auf "Coming soon", bis
Dogis PayPal-Business-Zugang da ist. Der erste echte Partnercode
wäre also hinter einer Tür gelegen, die sich gar nicht öffnen lässt.
"supporterToken" ist der Nachweis, dass jemand angemeldet ist — er
wird beim Login gesetzt und beim Logout wieder entfernt
(supporter.js, clearToken). "supporterSubStatus" sagt nur, OB und
wie bezahlt wird ("none", wenn ein Konto ohne Abo besteht).
Warum der Abo-Stand trotzdem noch mit abgefragt wird, obwohl er
ohne Token gar nicht vorkommen kann: Es ist ein Sicherheitsnetz.
Niemand, der die Codes gestern sehen konnte, soll sie heute
verlieren, nur weil ein Speicherstand anders aussieht als gedacht.
Die Prüfung ist bewusst rein clientseitig (wie vorher schon) — es
geht darum, wem etwas angeboten wird, nicht darum, ein Geheimnis zu
hüten. Der Code steht ohnehin in jedem geteilten Bildschirmfoto. */
var angemeldet = !!localStorage.getItem("supporterToken");
var status = localStorage.getItem("supporterSubStatus");
var hatAbo = ["active", "pending", "suspended", "cancelled"].indexOf(status) !== -1;
var darfSehen = angemeldet || hatAbo;
var inhalt = document.getElementById("rc-inhalt");
var gesperrt = document.getElementById("rc-gesperrt");
if (inhalt) inhalt.hidden = !darfSehen;
if (gesperrt) gesperrt.hidden = darfSehen;
var karte = document.getElementById("rc-partner");
var knopf = document.getElementById("rc-code");
var ok = document.getElementById("rc-echo-ok");
var fehler = document.getElementById("rc-echo-fehler");
var abgelaufen = new Date() > new Date(ABLAUF + "T23:59:59");
if (abgelaufen) {
if (karte) karte.classList.add("ist-abgelaufen");
var hinweis = document.getElementById("rc-abgelaufen");
if (hinweis) hinweis.hidden = false;
if (knopf) knopf.disabled = true;
}
if (!knopf) return;
var uhr = null;
knopf.addEventListener("click", function () {
if (abgelaufen) return;
var zeigen = function (welches) {
if (ok) ok.hidden = welches !== "ok";
if (fehler) fehler.hidden = welches !== "fehler";
knopf.classList.toggle("ist-kopiert", welches === "ok");
clearTimeout(uhr);
uhr = setTimeout(function () {
if (ok) ok.hidden = true;
if (fehler) fehler.hidden = true;
knopf.classList.remove("ist-kopiert");
}, 2600);
};
/* Ohne HTTPS gibt es navigator.clipboard nicht, und auch mit kann
das Versprechen abgelehnt werden. Beide Fälle bekommen dieselbe
ehrliche Antwort statt einer stillen Nicht-Reaktion — der Code
steht ja sichtbar da und lässt sich abtippen. */
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(knopf.getAttribute("data-code") || "").then(
function () { zeigen("ok"); },
function () { zeigen("fehler"); }
);
} else {
zeigen("fehler");
}
});
});
})();
</script>
</body>
</html>