Zwei Korrekturen an Stellen, die beim Anlegen der Cronjobs in die Irre geführt
hätten: (1) der Abschnitt behauptete noch, der Tarif habe keine Cronjobs —
seit 31.07.2026 ist Weg A unser Weg, cron.key ist leer und public/cron.php
stillgelegt. (2) Die Cron-Zeilen leiteten die Fehlerausgabe in die Kanal-Logs,
deren Format `[Zeit] LEVEL Meldung` eine rohe PHP-Warnung bricht (bin/logs.php
wertet Kanäle nach Level aus). Sie geht jetzt nach php-errors.log, das
bin/logs.php bereits als "eigenes Format" behandelt. Dazu die echten Pfade
dieses Webspace statt Platzhalter.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Auf dem Server scheiterte die Anmeldung mit "Could not authenticate", lief
Minuten später ohne jede Änderung fehlerfrei und wurde parallel vom
Entwicklungsrechner durchgehend angenommen. Es war also vorübergehend, nicht
falsch konfiguriert — vermutlich der Brute-Force-Schutz des Mailservers.
Notiert, damit das nicht als Konfigurationsfehler behandelt wird, plus der
nachgewiesene Rückfallweg (localhost:25 ohne Anmeldung, der lokale Relay).
Bewusst nicht der Standard: mit localhost liefen Dev und Produktion auseinander.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News,
Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover,
Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen
kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei
hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das
Skript selbst, statt sie von Hand zu prüfen.
Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus
über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit
Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie
doppelt gepflegt wird.
News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head.
Behobene Fehler:
- Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das
Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen
(Ergebnis-Rückblick, Vorschau, Cover).
- --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px
Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links
unter dem Logo.
- base_url in der Produktions-Config ohne www, während .htaccess auf www
umleitet; preflight prüft das jetzt.
- .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen.
Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php
(Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit
Warnung, wenn base_url nicht auf die echte Domain zeigt).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Zweiter Teil der Konsolidierung. Vorher standen 31 font-size-Werte außerhalb
der Skala — beim Durchgehen stellte sich heraus, dass 20 davon gar keine
Textgrößen sind: der icon()-Helper rendert 1em, also steuerte font-size dort
die Icon-Größe. Dafür gibt es jetzt eine eigene, kleine Skala (--icon-s/m/l/xl
in rem, --icon-inline-sm/--icon-inline/--icon-inline-lg in em für Icons, die im
Textfluss mitwachsen sollen). Elf Ad-hoc-Werte fallen damit auf sieben Stufen.
h4–h6 laufen nicht mehr über Coolvetica. Sie lagen mit 26px und 21px unter der
--fs-650-Grenze aus CLAUDE.md, ab der der Condensed-Schnitt in Uppercase
unleserlich wird — die Regel war also nur dokumentiert, nicht durchsetzbar.
Jetzt tragen h1–h3 Coolvetica (alle über der Grenze) und h4–h6 Abel mit
Sperrung. Bezeichnend: das einzige <h4> im Markup hatte sich genau dieses
Muster bereits von Hand gebaut. Die Basisregel liefert es jetzt, die Komponente
gibt ihre drei Duplikat-Deklarationen ab — sichtbar ändert sich dadurch nichts.
Neue Stufen schließen die Lücken: --fs-550 (fluider Lead), --fs-690 (Karten-
Überschrift) und --fs-1000 (Display-Hero). Vier fast deckungsgleiche clamp()-
Rampen für Karten-Überschriften (bento__heading, bento__stat-value,
group-card__name, team-card__name) laufen jetzt über --fs-690; team-card__name
wächst dabei am Desktop um ~5px, die anderen drei bleiben im 1px-Bereich.
Die Wappen-Initialen im Matchcenter hingen an zwei Mini-Schriftgrößen (10,4 und
11,2px) unterhalb der Skala. Sie sind in Wahrheit ~0,4 × Kreisdurchmesser,
folgen jetzt also --crest-size statt einer eigenen font-size. Nebenbei fallen
drei redundante Doppel-Selektoren weg: .crest--initials trägt immer auch .crest.
Ebenfalls tokenisiert: 27 letter-spacing-Werte mit elf verschiedenen Zahlen auf
vier Stufen (--ls-tight/caps/wide/wider), maximale Abweichung 0,02em.
Danach steht kein font-size- und kein letter-spacing-Wert mehr außerhalb von
tokens.css.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
Umbenennung durchgängig: „Damenmannschaft" → „Frauenmannschaft" und
„Damenfußball" → „Frauenfußball". Der Abteilungsname ist ein Zuordnungs-
schlüssel und musste auf allen Seiten gleichzeitig wandern — teams.json
(department + contact_department), ansprechpartner.json, die Abteilungslisten
auf Start- und Fußballseite, die Auswahl im Kontaktformular UND die Whitelist
in contact-submit.php. Geprüft: Ansprechpartner erscheinen weiter auf allen
vier Seiten, Formular-Optionen und serverseitige Whitelist sind deckungsgleich.
URL /fussball/damen und der Datenschlüssel „damen" bleiben absichtlich — eine
Slug-Änderung würde bestehende Links und die Suchmaschinen-Historie brechen,
sichtbar ist der Slug nirgends.
Die zweite Mannschaft ist in matchcenter.teams auskommentiert (nicht gelöscht):
Die BFV-Mannschaft hinter der ID lief 25/26 als „(SG1) TSV Melkendorf I/
TSV 08 Kulmbach II" und liefert in 26/27 nur noch „TSV Melkendorf" — also
Spiele ohne den TSV 08, samt Tabelle, die Melkendorf als eigenen Verein
markiert. Begründung steht bei der Zeile; nach Klärung im Verein reicht ein
Entfernen des Kommentars, ggf. mit neuer team_id. Die Seite
/fussball/zweite-mannschaft und ihr Menüpunkt bleiben unberührt, sie hängen
nicht am Matchcenter. Die Matchcenter-Description nannte die Zweite noch und
ist entsprechend neu gefasst.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CLAUDE.md und die Header von instagram-sync.php wie
matchcenter-sync.php verweisen für die Cron-Einrichtung auf
config.example.php — dort standen sie nie. Nachgetragen inkl.
Pfad-Platzhaltern und Hinweis auf den PHP-Binary-Pfad bei All-Inkl.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Versand läuft jetzt über das KAS-Postfach noreply@tsv08kulmbach.de
(w01ca75d.kasserver.com:587, STARTTLS) — ein Drittanbieter weniger,
SPF der Domain deckt den Server bereits ab. bin/smtp-test.php prüft
Verbindung, TLS und Login ohne eine Mail zu versenden (Debug bewusst
aus, damit keine Credentials im Terminal landen).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neue Unterseite /matchcenter ersetzt die alten BFV-Widget-Embeds (verstoßen
gegen CSP) durch eine self-hostete Lösung: Cron-Scraper holt die Daten aus der
öffentlichen BFV-Widget-JSON-API, schreibt data/matchcenter.json und lädt die
Vereinswappen lokal — Besucher kontaktieren nur unsere Domain.
- bin/matchcenter-sync.php: CLI/Cron-Scraper (Muster wie instagram-sync), JSON-API
team/{id}/matches + competition/{compoundId}/table, Wappen via getLogo lokal,
atomarer Write, Fail-safe. Auto-Saison: Tabellen-compoundId wird aus der
Matches-API abgeleitet (team_id ist saison-stabil). Per-Team-Cache-Fallback bei
Fetch-Fehlern, damit Sektionen nie leeren.
- Seite app/pages/matchcenter.php + Route + Nav-Eintrag (unter Fußball).
- Komponenten: match-slider (nächste Spiele), match-card, match-row, league-table
(mit Auf-/Abstiegszonen), matchcenter-section (kicker-Layout: Tabelle 2/3 +
Spiele 1/3), matchcenter-empty, crest (weißes Chip + Initialen-Fallback).
- carousel.js: banner.js zum generischen [data-carousel]-Antrieb verallgemeinert
(Banner- und Match-Slider teilen ihn); crest.js: Logo-Fallback im Browser.
- helpers.php: sportsevent_nodes() für SportsEvent-JSON-LD.
- Icons calendar-event; config.example + .gitignore + CLAUDE.md ergänzt.
- Responsive geprüft (kein Seiten-Overflow 360–1280, Tabelle scrollt in der Karte).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0192vTfGmGpoWeyUse4TuQFj
- Ordnerstruktur mit public/-Docroot, Deny-.htaccess für app/bin/config/data/storage
- CLAUDE.md mit Leitplanken (self-hosted only, Single Source of Truth, Component-first)
- Design-Tokens aus alter Seite extrahiert (Akzent #e20612, Coolvetica/Abel als woff2)
- Front Controller mit Routen-Register, Sitemap, Canonical, JSON-LD (SportsClub)
- Startseite: Hero, Instagram-Feed (lokaler Cache), Historie/Sportheim, Stats, Partner, Kontakt
- Kontaktformular via PHPMailer/Brevo mit Honeypot + HMAC-Time-Trap (kein reCAPTCHA)
- bin/instagram-sync.php: Scraper mit Strategie-Kette, flock, atomarem Cache, lokalen Bildern
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>