Damit die Abteilungen das Wappen selbst holen können, statt jedes Mal zu fragen:
drei Varianten (farbig, einfarbig schwarz, einfarbig weiß) in SVG, PDF, PNG und JPG,
dazu ein ZIP über alles und die Vereinsfarben. Verlinkt nur in der Fußzeile.
Bisher lag das Logo genau einmal im Projekt, als eine einzige SVG. Es gab kein PNG,
kein JPG, kein PDF und keine einfarbige Fassung.
bin/logo-build.php erzeugt alle Dateien aus einer Master-SVG unter storage/logo-quellen/.
Gerastert wird mit headless Chrome, weil GD kein SVG lesen kann und weder Inkscape noch
rsvg-convert noch ImageMagick verfügbar sind; das PDF ist dabei echter Vektor. Das Skript
läuft einmalig lokal, nichts davon im Request-Pfad. bin/preflight.php meldet als Fehler,
wenn eine Download-Datei fehlt: die Komponente ließe den Knopf sonst still weg.
Die beiden einfarbigen Fassungen sind exakte Negative voneinander, nur Linien, alle
Flächen frei. EPS gibt es bewusst nicht, dafür fehlt jedes Werkzeug; SVG und PDF decken
die Druckereien ab. Frühere Vereinslogos sind vorbereitet, aber im Projekt liegt bisher
kein einziges historisches Wappen vor.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J9mubC8gtpjtJntbhSJBRS
Neue Live-Seiten, geschaltet vom chat-gepflegten Feld `veranstaltung` (Slug aus
data/veranstaltungen.json) — Programm und Turnierpläne kommen damit aus derselben
Quelle wie die Website, nicht als Kopie ins Heft:
- Programm-Seite (programm[], rendert zugleich auf /veranstaltungen/sportfest-2026)
- vier Turnierplan-Seiten (turniere[]) mit leerer Ergebnisspalte und leerer
Abschlusstabelle zum Eintragen am Platz
- Gegnerseite je Mannschaft mit Heimspiel am Fest statt nur der tragenden:
stadionzeitung_gegner_fixtures() ist die einzige Stelle, die das entscheidet,
Seitenplanung und Snapshot lesen beide dort. `gegner` ist deshalb eine Map je
Team-Key; die Vorschau überspringt jedes Spiel mit eigener Gegnerseite
- Danke-Seite mit Partner-Logo-Wand aus data/partners.json und QR auf
/partner-werden, dazu das Anzeigen-Feld `platzierung` für Motive mit festem
Platz auf einer Live-Seite (Emons über der Logo-Wand). Vorher klebte der Dank
als leere Fläche unten an einem Anzeigen-Bild
- Cover einer Sonderausgabe: das Fest als Titelgeschichte statt einer der drei
Begegnungen, dazu das Hauptplakat schräg gelegt an Stelle des Spielerfotos
Ausgabe 2026-09-04-ats-wartenfels als ENTWURF angelegt, 48 Seiten (12 Bögen),
Tabellen auf gerader Seite, längster Anzeigenblock zwei. Veröffentlicht wird auf
dem Server, dort liegt der frische Matchcenter-Stand.
Nebenbei behoben: bin/anzeige-add.php räumte beim Anlegen alle Motive weg, deren
Slug mit demselben Text beginnt (`emons` löschte `emons-partnerdank` und
`emons-stuebinger`). Muster jetzt auf die Breiten-Endung eingeschränkt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KubsajzuV6N6QsSd3xKNjC
Der Snapshot fror die Daten ein, verwies aber weiter auf Ordner, die die
Sync-Skripte verwalten: matchcenter-sync räumt Wappen weg, die der BFV nicht mehr
führt (eine Saison), instagram-sync ältere Posts (Wochen). Am 31.07.2026
nachgewiesen — ein Lauf löschte 18 Wappen und 3 Bilder. Eine archivierte Ausgabe
wäre also mit der Zeit von selbst zerfallen, und "gedruckt = fixiert" war damit
nur die halbe Wahrheit.
bin/stadionzeitung-publish.php kopiert jetzt alles, worauf der Snapshot zeigt
(img/crests, img/instagram, img/news), als fix-* in den Ausgaben-Ordner und
schreibt die Pfade um. Varianten-Sets {base, widths} werden über alle Breiten
kopiert und nur umgeschrieben, wenn alle da sind — ein halbes srcset wäre
schlimmer als der alte Pfad; genau daran fiel img/news/neue-website beim ersten
Versuch auf. --entwurf löscht die fix-*-Dateien wieder, sie gehören zum Snapshot.
Flache Dateinamen statt Unterordner, weil die Aufräum-Logik in
stadionzeitung-add.php nur Dateien entfernt und ein Unterordner als Waise bliebe.
bin/preflight.php warnt, wenn eine veröffentlichte Ausgabe noch auf die
flüchtigen Ordner zeigt — sonst verfiele sie still.
Geprüft am Hutschdorf-Heft: 39 Dateien eingefroren, Snapshot verweist nur noch
auf den Ausgaben-Ordner, 88 Bilder in Viewer und Druckfassung erreichbar, keins
fehlt, preflight-Warnung verschwindet. Der Testlauf wurde danach zurückgenommen:
der lokale Matchcenter-Stand ist einen Tag alt, eingefroren wird auf dem Server
nach dem letzten Sync vor dem Druck.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Beim ersten Deploy blieben die Slides des Banner-Sliders schwarz. Ursache war
kein fehlendes Bild, sondern die CSP: public/.htaccess setzt
default-src 'self' ohne style-src, der Browser verwirft damit jedes
style-Attribut — und die Komponente lieferte Bild und Akzentfarbe genau so
(style="--banner-bg: url(…); --banner-accent: #6cbbbc"). Lokal war das
unsichtbar, weil php -S keine .htaccess liest.
Der Hintergrund ist jetzt ein <img class="ad-banner__bg"> über die bestehende
img-Komponente (CSP erlaubt img-src) und bringt srcset mit: das Handy lädt die
640er statt der 1600er, die erste Slide lädt eager mit fetchpriority. Der
dunkle Leseverlauf liegt auf ::before statt im background-image.
Die Akzentfarbe kann als beliebiger Hex-Wert nicht ohne Inline-Style in die
Seite. Sie steht deshalb als Token in tokens.css (wo Farben laut CLAUDE.md
hingehören), components.css bildet den Namen darauf ab, und banners.json nennt
nur noch "accent": "breadcrumb".
bin/preflight.php bricht ab, wenn unter app/ wieder ein style-Attribut
auftaucht — der Fehler wäre sonst erneut erst in Produktion sichtbar. Beim
Bauen dieser Wache zweimal selbst hineingetappt: ein Muster mit [^>]* bricht an
jedem schließenden PHP-Tag ab, und die Zeichenfolge dieses Tags beendet den
PHP-Modus auch in einem //-Kommentar (preflight gab ab dort seinen eigenen
Quelltext aus, php -l meldete nichts).
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