Info von Eva aus der Trampolin-Gruppe: neue Kinder können ab September
(Schulbeginn) gerne schnuppern kommen. Die Info stand nirgends.
Rot wäre dafür falsch gewesen. Die Farbe bekommt deshalb eine Bedeutung:
--stop (gefüllt rot) heißt „geht nicht", --info (Glas-Fläche, Rot nur im Icon)
heißt „geht". Eine Einladung in Alarmrot liest sich wie eine Absage, und wenn
alles rot ist, stumpft Rot ab und der echte Wartelisten-Stopp bei „Tiger" und
„Hühner" verliert seine Wirkung. --info ist ab jetzt der Default.
Damit steht der Hinweis an drei Stellen, also greift die Bedingung aus dem
letzten Commit („kommt ein dritter Ort dazu, gehören die gemeinsamen
Eigenschaften in eine geteilte Klasse"): neu app/components/notice.php als
einzige Render-Stelle statt dreimal dasselbe Markup. Die beiden bestehenden
Klassen schrumpfen auf das, was sie wirklich beitragen —
.turnen-dash__notice nur noch grid-area, .contact__notice nur noch die
Formularbreite. Die tote .turnen-dash__notice-icon-Regel ist raus, der
CSS-Abschnitt neu sortiert (notice als eigener Block hinter turnen-dash statt
mitten hinein).
Text, Tonlage und Icon stehen wie gehabt in data/turnen.json bei der
Disziplin, nicht im Template. Kinderturnen trägt jetzt explizit
notice_tone: stop, Trampolin nichts und ist damit neutral.
Das Grid-Template für --notice + --single gab es schon, an beiden
Breakpoints — die Trampolin-Seite hat eine Gruppe und wäre sonst
auseinandergefallen.
Gemessen bei 390px: kein Überlauf auf allen drei Seiten, Trampolin-Fläche
rgba(0,0,0,0.22) (Glas), die beiden anderen #e20612. Zwölf Seiten geprüft,
alle 200. preflight ohne neue Meldungen.
OFFEN, mit Eva oder Tina zu klären: der zweite Satz („Schreib uns davor
einfach kurz eine Nachricht") ist von mir, Eva hat ihn nicht gesagt. Wenn die
Kinder einfach zum Training kommen dürfen, gehört er weg. Und die Einladung
steht bisher nur auf /turnen/trampolin, nicht auf der Übersicht /turnen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Die Turnabteilung (Maxi) beantwortet laufend Anfragen nach einem Wartelistenplatz
für „Tiger" und „Hühner", den es nicht gibt. Auf der alten Seite stand der Hinweis
direkt über dem Anfrageformular, hier fehlte er.
Schlimmer: an der Stelle stand das GEGENTEIL. Der bisherige Hinweis lautete
"Aktuell sind alle Kinderturngruppen belegt. Bei Interesse setzen wir sie gerne
auf die Warteliste." — für alle Kindergruppen, also auch für die beiden, die
keine Liste führen. Der Satz hat einen Teil der Anfragen vermutlich erst erzeugt.
Er ist nicht gelöscht, sondern auf die übrigen Gruppen eingegrenzt und aufs Du
umgestellt (Haus-Stil; vorher stand dort klein "sie").
Der Text steht genau EINMAL, in data/turnen.json unter
disciplines.eltern-kind-turnen.notice + notice_title. Zwei Stellen lesen ihn:
die rote Kachel auf /turnen/eltern-kind-turnen (gab es schon) und neu ein Block
direkt über dem Formular auf /turnen. Ändert die Abteilung die Lage, ist das eine
Zeile in einer Datei.
Umsetzung:
- notice_title ist neu, weil eine rote Fläche ohne Überschrift wie eine
Fehlermeldung liest. Abel statt Coolvetica, der Schnitt ist bei ~19px in
Versalien unleserlich (CLAUDE.md, Design).
- Der Block steht AUSSERHALB des <form> und damit außerhalb der
role="status"-Region: ein dauerhafter Hinweis, keine Rückmeldung zum Absenden.
In der Live-Region läse ihn ein Screenreader bei jeder Formularmeldung erneut
vor. Kein role="alert", er steht schon beim Laden da.
- Überschrift als h3 unter der h2 des Intro-Blocks, damit die Folge nicht springt.
- .contact__notice übernimmt bewusst die Optik von .turnen-dash__notice, damit
beide Stellen als derselbe Hinweis erkennbar sind. Weiß auf --clr-accent
erreicht 4,92:1 und trägt damit Fließtext (WCAG 1.4.3). Kommt ein dritter Ort
dazu, gehören die gemeinsamen Eigenschaften in eine geteilte .notice-Klasse;
für zwei wäre das verfrüht.
Gemessen: Hinweisbreite gleich Formularbreite (720px bei 1280, 347px bei 390),
kein Überlauf. preflight ohne neue Meldungen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Rückmeldung der Turnabteilung mit Screenshot: auf dem iPhone war im
Flamingos-Panel der Fließtext rechts abgeschnitten.
Gemessen bei 390px mit aktivem Flamingos-Tab (die anderen Panels sind
ausgeblendet, ihre Chips haben Breite 0 — deshalb fiel es beim Prüfen der Seite
nicht auf): Dokument 442px bei 379px Bildschirm, also 63px Überlauf; der Chip
393px breit in einer 345px-Karte.
Ursache war nicht die Textlänge allein, sondern .group-card__level mit
`white-space: nowrap` UND `flex-shrink: 0`: der Chip konnte seinen Text nicht
brechen und auch nicht schrumpfen, seine Mindestbreite spannte die ganze Karte
auf. Von vier level-Chips war nur dieser betroffen (47 Zeichen gegen 17 bis 25).
Zwei Änderungen:
- Inhalt wie gewünscht gekürzt: "Turnen · fortgeschritten · Alter nach Absprache"
→ "Turnen · fortgeschritten".
- Sicherheitsnetz im CSS: nowrap raus, max-width: 100% rein. Ein Chip trägt
Information, deshalb umbrechen statt mit … kürzen. flex-shrink: 0 bleibt, es
hält den Chip neben einem kurzen Gruppennamen in Form.
Gegenprobe, damit der Fix nicht nur am kürzeren Text hängt: mit dem LANGEN Text
und dem CSS-Fix bleibt das Dokument bei 379px, der Chip bricht auf zwei Zeilen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Drei Dinge, zwei aus deinem Feedback und eines von mir.
1) Der Einleitungstext klebte ohne Abstand an der Überschrift und lief über die
volle Breite, über 120 Zeichen je Zeile ("schaut weird aus"). Ursache: ich
hatte <p class="lead"> geschrieben — eine Klasse, die es im Projekt gar nicht
gibt, der Absatz war also völlig unformatiert. Jetzt das etablierte Muster
(.membership-signup__head, instagram, partners): zentrierter Kopf, 60ch breit,
Luft nach unten. Anmerkung im Code: dasselbe Kopf-Muster steht damit zum
vierten Mal fast gleich in components.css — ein gemeinsames .section__head
wäre die Aufräumung, aber ein eigener Umbau.
2) Preise von 28px auf 34px. Sie tragen die Hervorhebung allein, seit die Farbe
weg ist, und sind die Information, auf die man in einer Beitragstabelle sieht.
3) Selbst aufgefallen: die Überschrift "Austritt" lag in .prose und rendert dort
mit 21px Abel — exakt Fließtextgröße, also gar keine Auszeichnung. In
Impressum und Artikeln ist das richtig, deshalb .prose nicht angetastet,
sondern eigene Klassen für den Hinweis: 28px Coolvetica (die Untergrenze für
Versalien) mit derselben dezenten Sperrung wie die Kartentitel.
Außerdem: data/turnen.json ist wieder in seiner ursprünglichen Formatierung.
Mein JSON-Schreiber hatte im vorigen Commit die kompakten Arrays aufgeklappt
("widths": [640, 1000, 1600] wurde vierzeilig) — inhaltlich identisch, aber 371
Diff-Zeilen für eine Umbenennung. Jetzt textuell ersetzt statt neu geschrieben:
17 Diff-Zeilen, Inhalt per Vergleich identisch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Rückmeldung der Turnabteilung: Step Aerobic findet nicht mehr statt.
Wichtig war der Befund vor der Änderung: die Seite bündelt DREI Angebote —
Pilates, Fitness & Stretching und Step-Aerobic. Ein pauschales "die Card raus"
hätte Pilates und Fitness mitgenommen. Entfernt ist deshalb nur die eine Gruppe
(Donnerstag 19:30, Heike Wahler); Pilates und Fitness & Stretching bei Karola
Lorz laufen unverändert weiter. Heike Wahler stand nur in dieser Gruppe und
erscheint damit nicht mehr auf der Website — das ist die einzige Nebenwirkung,
die jemandem auffallen könnte.
Umbenannt in "Fitness & Pilates" an allen zehn Fundstellen: navigation.json,
turnen.json (Disziplin-Schlüssel, Hero-Titel, drei Übersichtstexte),
ansprechpartner.json, news.json, turnen.php (Karte, Alt-Text, Meta) und die
Seiten-Datei selbst.
URL mitgezogen wie besprochen: turnen/step-aerobic-fitness-pilates →
turnen/fitness-pilates, samt Seiten-Datei und den drei Bilddateien (git mv,
Historie bleibt). Der alte Slug war in der Sitemap und ist indexiert, deshalb
eine 301 in public/.htaccess — VOR dem Front Controller, sonst würde index.php
den Pfad als unbekannten Slug abfangen und 404 rendern.
Geprüft: /turnen/fitness-pilates liefert 200 mit nur noch zwei Gruppen,
Sitemap führt ausschließlich den neuen Slug, Navigation, Übersichtskarte und der
News-Artikel verweisen darauf, keine Referenz auf die alten Bildnamen mehr.
Der alte Slug gibt lokal 404, weil php -S keine .htaccess liest; die 301 greift
auf Apache.
Nicht geändert: das Wort "Step-Aerobic" in der CLAUDE.md-Sprachregel. Dort ist
es ein Beispiel für orthografisch nötige Bindestriche und bleibt als solches
richtig, auch ohne den Kurs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Das Cover blieb am iPhone leer, während Chrome am Desktop es zeigte. Ursache
war kein fehlendes Bild: das freigestellte Spielerfoto hatte 2584x5093 Pixel und
12,3 MB. iOS Safari verwirft Bilder, deren dekodierte Größe sein Budget
übersteigt — hier gut 53 MB, und auf einer Ausgabe liegen 86 Bilder.
Dargestellt wird das Foto mit 98cqi Höhe, bei 480px Seitenbreite also etwa
238x470 CSS-Pixel. Es gibt jetzt 320/640/1000 als PNG mit erhaltener
Transparenz (0,2 / 0,8 / 1,9 MB); sizes nennt die echte Breite, sonst zöge ein
Handy mit DPR 3 die größte Variante. Das Original ist gelöscht.
Dafür kann resize_image_variants() nun Alpha erhalten ($alpha = true schreibt
PNG statt JPG) und die img-Komponente eine Endung ($image['ext']). Der Default
bleibt JPG, weil bestehende Aufrufer PNG-Quellen absichtlich zu JPG verkleinern.
bin/stadionzeitung-add.php erzeugt die Varianten künftig selbst. Dort stand
"unverändert kopieren, Felix liefert bereits ein web-taugliches Foto" — genau
diese Annahme ist gerissen.
Außerdem: smtp.password stand in der Produktiv-Config noch auf dem Platzhalter
POSTFACH_PASSWORT (von preflight auf dem Server gefunden, einziger harter
Fehler). Übernommen wurde das Passwort aus der Dev-Config, mit dem der Versand
am 27.07.2026 verifiziert wurde; Host, Postfach und Empfänger sind identisch.
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
QR-Codes und Adresszeilen trugen `http://localhost:8000` ins Druckmaterial, weil
sie über abs_url() liefen. Nach dem Produktivgang hätten alle zehn Codes neu
erzeugt werden müssen — und ein gedruckter Code mit localhost darin ist Altpapier.
Die öffentliche Domain ist Vereinsidentität, keine Umgebungseinstellung: neues
Feld `club.website` in data/club.json plus club_url()/club_domain(). base_url
bleibt umgebungsabhängig, weil Canonical, OG, Sitemap und JSON-LD zur
ausliefernden Adresse passen müssen.
Umgestellt: Cover-QR und die acht geteilten CTA-QRs, die Internet-Zeile im
Heft-Impressum, die Cover-Zeile neben dem QR, die acht Linkzeilen unter den
CTA-QRs der Live-Seiten (die hatte ich beim ersten Durchgang übersehen), das
Web-Feld im Website-Impressum und bin/qr.php.
Nebenbei: club_domain() ohne Pfad hängte einen Wurzel-Slash an
(„tsv08kulmbach.de/"), und der Kopf-Kommentar von stadionzeitung-add.php
beschrieb noch die alten vier Anzeigen-Gruppen.
Nicht verifizierbar und deshalb dokumentiert: die vendorte QR-Bibliothek
arbeitet nicht reproduzierbar (gleiche URL, andere Pixel — anderes Maskenmuster,
beides gültig). Ein PNG-Vergleich beweist nichts, nachweisbar ist nur der
Code-Pfad. Jeden Druck-QR einmal scannen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
Neue Datenquellen:
- data/veranstaltungen.json Feste mit eigener Detailseite (dritte Termin-Quelle)
- data/anzeigen.json Sponsoren-Anzeigen als dauerhafter Bestand statt
pro Ausgabe abgeliefert
- data/stadionzeitung.json Ausgaben-Archiv (maschinenverwaltet)
- data/news.json redaktionelle Meldungen mit Artikelseiten
Bilder einmal statt pro Ausgabe: public/assets/img/anzeigen/ hält die 19
Sponsorenmotive plus drei Eigenanzeigen; vorher lag jede Anzeige als Kopie
unter jeder Stadionzeitungs-Ausgabe.
Zuerst die Inhalte, dann der Code: so ist der Baum in jedem Schritt gültig.
Daten ohne die zugehörigen Seiten sind harmlos, Seiten ohne Daten nicht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
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>
Audit aller 23 indexierbaren Routen (lokaler Crawl) und Fixes:
- Descriptions auf ~130-160 Zeichen; Turnen-Disziplinseiten hatten den
kurzen Hero-Teaser als Description, jetzt eigene SERP-Texte
- Titles: team-erste gekürzt, sportheimbuchung ohne doppelten Brand
- h1->h3-Sprünge behoben: visually-hidden h2 aus JSON-Section-Titeln
(sportheimbuchung, partner-werden), sichtbarer choice.title auf
mitglied-werden; historie bekommt Breadcrumb-Schema
- Neuer Helper img_intrinsic_attrs() liefert width/height aus der
Bilddatei (PNG via getimagesize, SVG via viewBox) für Partner-Grid-
und Banner-Slider-Logos — kein Layout-Shift mehr
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Neue /kontakt-Seite (Hero + wiederverwendete contact-form-Komponente, ohne
Intro-Block). Behebt die bisher ins 404 laufenden "Kontakt aufnehmen"-CTAs.
- contact-form: optionales $intro + Rücksprung-Slug $return (Hidden-Feld)
- contact-submit: PRG-Redirect zur absendenden Seite, whitelist-validiert
gegen routes.php (kein Open-Redirect)
- Verlinkt im Footer-Rechtsbereich und im Menü "Der Verein"
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
Prev/Next-Navigationsbuttons für den Banner-Slider (carousel.js, generisch über
[data-carousel-prev/next]), zugehörige ad-banner-Steuerungs-Styles und Chevron-
Icons. Ohne JS bleiben die Controls ausgeblendet (progressive enhancement).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
Überarbeitete Seite /partner-werden mit Argument-Karten (sponsor-arguments)
und einer Übersicht der Werbekanäle (sponsor-channels) aus
data/partner-werden.json, inkl. zugehöriger Icons.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
Neue Seite /mitmachen, die offene Ehrenamtsstellen aus data/mitmachen.json als
position-card-Komponente listet, mit clientseitigem Bereichsfilter
(position-filter.js, progressive enhancement) und JobPosting-Schema
(job_posting_schema(), employmentType VOLUNTEER). Route ergänzt.
Enthält zudem kleine person-card__contact-Stilanpassungen (gleiche CSS-Sektion).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
Neue Seite /sportheimbuchung mit Bento-Layout (Fotos, Lage, Ausstattung) und
einem Buchungsanfrage-Formular nach Projektkonvention (form-field-Komponente,
Status-Region, Honeypot + ft-Token, Whitelist-Validierung, Rate-Limiting, PRG).
Versand via Brevo SMTP über app/actions/sportheimbuchung-senden.php, POST-Route
in index.php. form-field um min/max-Attribute erweitert (Personenzahl-Feld).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
Alle Unterseiten-Heros auf ein einheitliches Muster gebracht — nur title +
text, dazu compact und align bottom-left. kicker und tagline entfernt
(team-erste/zwei/damen, jugendfussball, matchcenter, ansprechpartner,
mitglied-werden), historie von zentriert auf bottom-left. Der große
Startseiten-Hero (display) bleibt unverändert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
Mitglied werden: Hub-Seite mit Online-Anmeldung (membership-form +
membership-submit-Action, Honeypot + Time-Trap), Abteilungswahl
(department-choice), Beitragstabelle (fee-table) und Turn-Aufnahmeantrag
als PDF-Download. Daten in data/mitglied-werden.json.
Partner werden & Sportheimbuchung: neue Seiten samt Routing und Navigation.
Ansprechpartner: echter Hero (Vereinsgelände), Bereiche aus dem group-Feld
der Single Source (Fußball / Turnen) zu zwei Sektionen gebündelt, Disziplin
als Karten-Überschrift. Fehlende Turn-Disziplin-Leads (Frisque, Lorz,
Kornatz, Trotzke) aus den Turnen-Unterseiten extrahiert und in
data/ansprechpartner.json ergänzt (allgemeine Turnabteilungs-Mail als
Kontakt). WhatsApp für alle Personen mit Telefonnummer. person-grid:
kompaktere Karten, Hairline unter der Bereichsüberschrift, 5 Spalten am
Desktop. Startseite auf die Haupt-Ansprechpartner je Bereich begrenzt.
Dazu: neue Icons (check-circle, download, file-earmark-text, whatsapp),
Anpassungen an Header/Footer/CTA/age-groups sowie tokens/layout/utilities.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZsKTL5kRSG4KkK8w2LKkE
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
- Neue Übersichtsseite (Hub: Hero + Disziplin-Kacheln) sowie Unterseiten für
Erwachsenenturnen, Eltern-Kind-/Kinderturnen, Step Aerobic/Fitness/Pilates,
Trampolin und Wettkampfturnen. Inhalte 1:1 von der alten Seite, gepflegt in
der neuen Single-Source data/turnen.json.
- Neue Komponente group-schedule im einheitlichen Bento-Look wie die
Mannschaftsseiten: Dashboard mit Trainingszeiten, Kontakt-Kachel (mailto an die
Turnabteilung) und Trainingsort; gestapelte/gerasterte Gruppen-Karten für
Mehr-Gruppen-Disziplinen, rote Hinweis-Kachel (z. B. Warteliste).
- Routing (app/routes.php) und Navigations-Dropdown (data/navigation.json) für
Turnen; zugehörige Styles in components.css.
- Themenbezogene, self-hostete Unsplash-Platzhalterbilder für die Kacheln.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
Logo führt die Slide oben links an (statt großer Logo-Platte rechts); Inhalt
einspaltig, „Anzeige"/Status-Label als Pille unten rechts in der Fußzeile.
Damit gibt die rechte Hälfte das Hintergrundfoto frei — Motivwahl wird viel
einfacher. Logo nur rendern, wenn vorhanden; Wappen (Eigenwerbung) etwas größer.
Breadcrumb-Slide auf einen CTA reduziert: „Neue Website entdecken" → Startseite.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
Folienwechsel als sauberes Crossfade (best practice): Slides im selben
Grid-Feld gestapelt → konstante Höhe ohne Sprung, ein-/ausgehende Folie
blendet über (Opacity + leichtes translateY). JS steuert die Sichtbarkeit
über .is-active statt hidden; ohne JS bleibt der hidden-Fallback (erste Folie).
Reduced-Motion über den globalen Kill-Switch instant.
Neue Slide-Prop "accent" (CSS-Farbe) überschreibt das Vereins-Rot der Buttons
pro Slide; Breadcrumb-Slide nutzt #6cbbbc. Button-Text wird aus der Akzentfarbe
dunkel abgeleitet (color-mix), da helle Akzente mit Weiß zu wenig Kontrast haben.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
Zweite Slide als echte Sponsoren-Anzeige (Hero-Foto + Wortmarken-Logo,
self-hosted). Komponente erweitert um cta_secondary (Outline-Button) und
logo_light (weiße Logo-Platte für dunkle Firmenlogos). CTAs verlinken extern
(target=_blank, rel=sponsored) auf Lösungen + Über uns. Mit zwei Slides
aktiviert der Slider Auto-Rotation und Indikatoren.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
House-Ad mit TSV-08-Wappen und echtem Foto (fussball-hero) statt der
Platzhalter-Slides: wirbt selbst für die Werbefläche, CTA führt intern aufs
Kontaktformular (#kontakt). Komponente um generische Props erweitert: variant
(CSS-Modifier), tag (Badge-Text) und cta.internal (interner Link ohne
target=_blank/rel=sponsored). Neue Styles: rote Glas-/Foto-Variante mit
gestricheltem "Werbeplatz frei"-Frame, rot-dunkler Verlauf für Lesbarkeit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
navigation.json gruppiert Fußball (Übersicht, Mannschaften, Jugend) und
Verein (Übersicht, Historie) als children. nav.php rendert Disclosure-Buttons
mit aria-expanded/-controls; ohne JS per CSS :hover/:focus-within bedienbar.
nav.js ergänzt Toggle, Escape (Untermenü zuerst, dann Overlay) und
Klick-außerhalb-schließt. Kontakt-CTA aus der Nav entfernt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
Neue Seiten + Routen: fussball, fussball/{erste-mannschaft,zweite-mannschaft,
damen}, jugendfussball, verein, impressum, datenschutz. Dazu die Komponenten
team, team-cards/-roster/-photo/-bento, faq/faq-contact, key-facts, age-groups,
cta-band, training-location und der contact-slider.
Daten in teams/fussball/jugend/verein.json; club.json um Vorstand, Bankdaten,
Datenschutz-Kontakt erweitert. person-grid bekommt $names-Filter, partner-grid
einen Intro-Stil. Seiten setzen $meta['schema'] via team_/page_schema.
Styles für alle neuen Komponenten in components.css.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
- 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>