Files
tsv08kulmbach-website/app/components/group-schedule.php

322 lines
16 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
/**
* Turn-Disziplin im einheitlichen Bento-Look (wie die Mannschaftsseiten). Props:
* $location array{venue, street?, zip?, city?} (optional) Trainingsort + „Route planen"
„Trainerteam" statt „Übungsleitung", Eva Wünsch mit Sternchen Eva Wünsch aus der Turnabteilung am 31.07.2026: „Der Sternchentext müsste eigentlich sein, dass die Person mit dem Sternchen Übungsleiter ist. Von daher müsste vor den Namen 'Trainerteam' stehen. Einige sind auch nicht ÜL." Ich hatte davon nur die Fußnote erledigt. Das Label blieb „Übungsleitung" und damit stand die falsche Aussage weiter auf der Seite: neun namentlich genannte Personen liefen unter „Übungsleitung", vier davon sind es. Das ist keine Formulierungsfrage, die Seite behauptete etwas über echte Menschen. Label jetzt „Trainerteam:" an beiden Stellen (Trainingszeit und Gruppe), der Stern bleibt die Auszeichnung und die Fußnote erklärt ihn. So stand es auch auf der alten Seite: „TRAINERTEAM (* = ÜBUNGSLEITER)". Ein Kommentar an der Stelle sagt, warum es nicht zurückbenannt werden darf. Dabei fiel ein zweiter Datenfehler auf: die alte Seite führt „Annelie Frisque*, Eva Wünsch*", bei uns stand Eva Wünsch ohne Stern. Sie ist Übungsleiterin. Geprüft: „Übungsleitung" kommt auf keiner der sechs Turn-Seiten mehr vor, die Grashüpfer-Zeile lautet jetzt Zeichen für Zeichen wie in Evas Screenshot. Die Fußnote erscheint nur in Sektionen mit mehreren Gruppen — nachgezählt, dass beide Disziplinen mit Sternchen-Namen mehrere Gruppen haben, die Einzelgruppen-Disziplinen tragen keine Sterne. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:57:31 +02:00
* $lead string (optional) Ansprechperson der Disziplin (Akzent-Kachel)
* $contactEmail string (optional) persönliche E-Mail der Ansprechperson für die
* Kontakt-Kachel. Überschreibt die Abteilungs-Mail (club.email_turnen), falls
* gesetzt genutzt, wenn eine Disziplin einen eigenen direkten Draht angibt.
* $contactPhone string (optional) Telefonnummer der Ansprechperson (Anzeige-Form). Ergibt
* einen zusätzlichen tel:-Link in der Kontakt-Kachel. tel:-Href wie in person-card.php.
* $notice string (optional) hervorgehobener Hinweis (z. B. Wartelisten-Status)
Trampolin: Schnupper-Einladung, Hinweise bekommen zwei Tonlagen 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
2026-07-31 22:04:41 +02:00
* $noticeTitle string (optional) Überschrift dazu
* $noticeTone 'info' (Default) | 'stop' Tonlage, siehe components/notice.php
* $noticeIcon string (optional) Icon-Name, siehe components/notice.php
* Gerendert wird über component('notice') dieselbe Komponente, die
* contact-form.php über dem Formular benutzt, damit derselbe Hinweis an
* beiden Orten gleich aussieht und nur EINMAL in data/turnen.json steht.
* $groups array von Gruppen:
Flamingos zum Wettkampfturnen, Inhalte gegen die alte Seite abgeglichen Die Flamingos sind organisatorisch vom Kinderturnen zum Wettkampfturnen gewechselt. Felix hat dazu die Fassung geschickt, die die Turnabteilung selbst auf der alten Seite gepflegt hatte — dabei kamen sieben Abweichungen heraus, alle korrigiert: - Die zweite Gruppe heißt "Leistungsturnen", nicht "Wettkampfturnen" (so hieß bei uns die Gruppe wie die Disziplin). - Reihenfolge: Flamingos zuerst, Leistungsturnen danach. - Den Flamingos fehlte eine Trainingszeit: Montag gibt es ZWEI Blöcke (18:00–19:00 und 19:00–20:00), wir hatten nur den ersten. - Ansprechpersonen sind pro Gruppe verschieden (Flamingos: Theresa Diener, Leistungsturnen: Franziska Trotzke). Dafür ist `contact` neu an einer Gruppe. Die Kontakt-Kachel oben kann nur EINE Person nennen und wäre für die andere Gruppe falsch — deshalb ist `lead` bei dieser Disziplin jetzt leer, die Kachel trägt nur noch die Abteilungs-Mail. So war es auf der alten Seite auch: dort gab es keine Ansprechperson für die Disziplin, nur je Gruppe. - Theresa Diener steht im Trainerteam vorn und ohne Stern (bei uns hinten). - Intro, Trainingsinhalte, Wettkämpfe und der Hinweis waren gekürzt; jetzt im Wortlaut der Abteilung. Den Gedankenstrich in "(Kür modifiziert – Leistungsklasse 3)" ersetzt ein Komma (Sprache & Ton). - Die Fußnote sagte "* So im Trainingsplan der Abteilung gekennzeichnet" — eine Verlegenheitsformulierung, weil wir die Bedeutung nicht kannten. Die alte Seite schreibt "* = ÜBUNGSLEITER", also jetzt "* Übungsleiterinnen und Übungsleiter" (Paarform, Haus-Stil). `level` bleibt "Turnen · fortgeschritten" ohne "Alter nach Absprache" — das hatte Felix am 31.07.2026 ausdrücklich aus dem Chip genommen. Zwei Folgen, die sonst still kaputtgegangen wären: die Seite hatte eine einzige Gruppe ohne Beschreibung und lief damit im Einzelgruppen-Layout. Mit zwei Gruppen, davon eine mit Beschreibung, rendert group-schedule eine Tab-Leiste — die braucht group-tabs.js, das die Seite nicht lud (tote Tab-Knöpfe). Und drei Flamingos-Trainerinnen tragen ein Sternchen, die erklärende Fußnote wurde auf dieser Seite nicht übergeben (drei unerklärte Sterne). Geprüft: Tabs "Flamingos" und "Leistungsturnen", Skript geladen, Fußnote da, Flamingos auf /turnen/eltern-kind-turnen nicht mehr erwähnt, kein Überlauf bei 390px, alle sechs Turn-Seiten 200. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:40:25 +02:00
* { name, level?, contact?, intro?, details?:[{heading,text}], recommendation?,
* note?, trainers?:[], sessions:[{ label?, day, time, note?, trainers?:[] }] }
* contact = Ansprechperson NUR dieser Gruppe. Braucht es, sobald eine Disziplin
* mehrere Gruppen mit verschiedenen Ansprechpersonen hat (Wettkampfturnen:
* Flamingos Theresa Diener, Leistungsturnen Franziska Trotzke) die
* Kontakt-Kachel oben kann nur EINE nennen und wäre für die andere falsch.
* Beschriftet als „Kontakt" wie die Kachel, nicht als „Ansprechpartner":
* das Label kennt das Geschlecht der Person nicht (Haus-Stil Paarformen).
* $footnote string (optional) Erläuterung (z. B. zur Sternchen-Kennzeichnung)
* $groupsTitle string (optional, Default 'Unsere Gruppen')
Wettkampfturnen: Karten offen untereinander statt hinter Tabs Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die Flamingos-Karte allein ist am Handy 2142px hoch. .group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte. Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in data/turnen.json las ebenfalls niemand, ist raus. Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide in einem halben Jahr auseinanderlaufen. Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte. Zwei Nachbesserungen, beide gemessen: - Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen), in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding weg, jetzt 80px. - Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop unverändert 40px, am Handy 28px. Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77 VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
* $groupsTitleHidden bool (optional) H2 bleibt im DOM (Dokumentgliederung für
* Screenreader, Sektion behält ihren Namen), ist aber visuell versteckt.
* Dasselbe Muster wie $title_hidden in team-cards. NICHT die Überschrift
* weglassen: dann hätte die Sektion keinen zugänglichen Namen mehr.
* $layout 'tabs' (Default) | 'stacked' greift nur bei beschreibenden Gruppen.
* 'stacked' zeigt alle Karten offen untereinander (wenige Gruppen),
* 'tabs' ordnet viele textreiche Gruppen (Kinderturnen).
* $groupsInDash bool (optional) nur für SCHLICHTE Gruppen (ohne intro/details): zieht die
* Gruppen-Karten als volle Bodenreihe MIT INS Bento-Dashboard, ohne eigene Sektion
* und ohne „Unsere Gruppen"-Überschrift. Für Disziplinen mit wenigen kurzen Gruppen
* (Trampolin), damit das Dashboard ein zusammenhängender Block bleibt statt einer
* losgelösten Kartenreihe darunter. Bei beschreibenden Gruppen (Wettkampfturnen,
* Kinderturnen) wirkungslos die brauchen Platz und ihre eigene Sektion.
* $anchor string (optional, Default 'training')
*
* Eine Gruppe Trainingszeiten landen als Bento-Kachel im Dashboard.
* Mehrere Dashboard zeigt nur Ort/Verantwortlich, darunter eine Gruppen-Sektion.
* Gruppen mit Beschreibung werden gestapelt, schlichte Gruppen als Raster.
*/
$anchor = $anchor ?? 'training';
$groupsTitle = $groupsTitle ?? 'Unsere Gruppen';
Wettkampfturnen: Karten offen untereinander statt hinter Tabs Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die Flamingos-Karte allein ist am Handy 2142px hoch. .group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte. Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in data/turnen.json las ebenfalls niemand, ist raus. Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide in einem halben Jahr auseinanderlaufen. Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte. Zwei Nachbesserungen, beide gemessen: - Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen), in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding weg, jetzt 80px. - Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop unverändert 40px, am Handy 28px. Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77 VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
$groupsTitleHidden = $groupsTitleHidden ?? false;
$layout = ($layout ?? 'tabs') === 'stacked' ? 'stacked' : 'tabs';
$lead = $lead ?? '';
$contactEmail = $contactEmail ?? '';
$contactPhone = $contactPhone ?? '';
$notice = $notice ?? '';
Wartelisten-Hinweis für Tiger und Hühner, über dem Anfrageformular 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
2026-07-31 21:54:10 +02:00
$noticeTitle = $noticeTitle ?? '';
Trampolin: Schnupper-Einladung, Hinweise bekommen zwei Tonlagen 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
2026-07-31 22:04:41 +02:00
$noticeTone = $noticeTone ?? 'info';
$noticeIcon = $noticeIcon ?? 'info-circle';
$footnote = $footnote ?? '';
$groupsInDash = !empty($groupsInDash);
$groups = $groups ?? [];
$location = $location ?? [];
if ($groups === []) {
return;
}
$club = json_load('club');
$email = $contactEmail !== '' ? $contactEmail : ($club['email_turnen'] ?? ($club['email'] ?? ''));
$telHref = $contactPhone !== '' ? 'tel:' . preg_replace('/[^+\d]/', '', $contactPhone) : '';
$single = count($groups) === 1;
$rich = false;
foreach ($groups as $g) {
if (!empty($g['intro']) || !empty($g['details'])) {
$rich = true;
break;
}
}
// Schlichte Gruppen direkt ins Dashboard ziehen (nur wenn mehrere und keine „reich").
$groupsInline = $groupsInDash && !$single && !$rich;
$hasVenue = !empty($location['venue']);
$mapsQuery = $hasVenue ? rawurlencode(trim(
($location['venue'] ?? '') . ', '
. ($location['street'] ?? '') . ', '
. ($location['zip'] ?? '') . ' ' . ($location['city'] ?? ''),
', '
)) : '';
„Trainerteam" statt „Übungsleitung", Eva Wünsch mit Sternchen Eva Wünsch aus der Turnabteilung am 31.07.2026: „Der Sternchentext müsste eigentlich sein, dass die Person mit dem Sternchen Übungsleiter ist. Von daher müsste vor den Namen 'Trainerteam' stehen. Einige sind auch nicht ÜL." Ich hatte davon nur die Fußnote erledigt. Das Label blieb „Übungsleitung" und damit stand die falsche Aussage weiter auf der Seite: neun namentlich genannte Personen liefen unter „Übungsleitung", vier davon sind es. Das ist keine Formulierungsfrage, die Seite behauptete etwas über echte Menschen. Label jetzt „Trainerteam:" an beiden Stellen (Trainingszeit und Gruppe), der Stern bleibt die Auszeichnung und die Fußnote erklärt ihn. So stand es auch auf der alten Seite: „TRAINERTEAM (* = ÜBUNGSLEITER)". Ein Kommentar an der Stelle sagt, warum es nicht zurückbenannt werden darf. Dabei fiel ein zweiter Datenfehler auf: die alte Seite führt „Annelie Frisque*, Eva Wünsch*", bei uns stand Eva Wünsch ohne Stern. Sie ist Übungsleiterin. Geprüft: „Übungsleitung" kommt auf keiner der sechs Turn-Seiten mehr vor, die Grashüpfer-Zeile lautet jetzt Zeichen für Zeichen wie in Evas Screenshot. Die Fußnote erscheint nur in Sektionen mit mehreren Gruppen — nachgezählt, dass beide Disziplinen mit Sternchen-Namen mehrere Gruppen haben, die Einzelgruppen-Disziplinen tragen keine Sterne. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:57:31 +02:00
/**
* Eine Trainingszeit (Tag/Zeit + optional Label, Notiz, Trainerteam) ausgeben.
*
* Das Label heißt „Trainerteam", nicht „Übungsleitung": in der Liste stehen auch
* Personen, die KEINE Übungsleitung sind (Eva Wünsch aus der Abteilung, 31.07.2026).
* Wer eine ist, trägt ein Sternchen, und das erklärt die Fußnote der Sektion. Die alte
* Seite schrieb dafür „TRAINERTEAM (* = ÜBUNGSLEITER)". Nicht zurückbenennen.
*/
$renderSession = static function (array $s): void {
?>
<li class="schedule-row">
<?php if (!empty($s['label'])): ?>
<span class="schedule-row__label"><?= e($s['label']) ?></span>
<?php endif; ?>
<span class="schedule-row__when">
<span class="schedule-row__day"><?= e($s['day']) ?></span>
<span class="schedule-row__time"><?= e($s['time']) ?></span>
</span>
<?php if (!empty($s['note'])): ?>
<span class="schedule-row__note text-muted"><?= e($s['note']) ?></span>
<?php endif; ?>
<?php if (!empty($s['trainers'])): ?>
„Trainerteam" statt „Übungsleitung", Eva Wünsch mit Sternchen Eva Wünsch aus der Turnabteilung am 31.07.2026: „Der Sternchentext müsste eigentlich sein, dass die Person mit dem Sternchen Übungsleiter ist. Von daher müsste vor den Namen 'Trainerteam' stehen. Einige sind auch nicht ÜL." Ich hatte davon nur die Fußnote erledigt. Das Label blieb „Übungsleitung" und damit stand die falsche Aussage weiter auf der Seite: neun namentlich genannte Personen liefen unter „Übungsleitung", vier davon sind es. Das ist keine Formulierungsfrage, die Seite behauptete etwas über echte Menschen. Label jetzt „Trainerteam:" an beiden Stellen (Trainingszeit und Gruppe), der Stern bleibt die Auszeichnung und die Fußnote erklärt ihn. So stand es auch auf der alten Seite: „TRAINERTEAM (* = ÜBUNGSLEITER)". Ein Kommentar an der Stelle sagt, warum es nicht zurückbenannt werden darf. Dabei fiel ein zweiter Datenfehler auf: die alte Seite führt „Annelie Frisque*, Eva Wünsch*", bei uns stand Eva Wünsch ohne Stern. Sie ist Übungsleiterin. Geprüft: „Übungsleitung" kommt auf keiner der sechs Turn-Seiten mehr vor, die Grashüpfer-Zeile lautet jetzt Zeichen für Zeichen wie in Evas Screenshot. Die Fußnote erscheint nur in Sektionen mit mehreren Gruppen — nachgezählt, dass beide Disziplinen mit Sternchen-Namen mehrere Gruppen haben, die Einzelgruppen-Disziplinen tragen keine Sterne. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:57:31 +02:00
<span class="schedule-row__trainers"><span class="schedule-row__trainers-label">Trainerteam:</span> <?= e(implode(', ', $s['trainers'])) ?></span>
<?php endif; ?>
</li>
<?php
};
Wettkampfturnen: Karten offen untereinander statt hinter Tabs Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die Flamingos-Karte allein ist am Handy 2142px hoch. .group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte. Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in data/turnen.json las ebenfalls niemand, ist raus. Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide in einem halben Jahr auseinanderlaufen. Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte. Zwei Nachbesserungen, beide gemessen: - Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen), in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding weg, jetzt 80px. - Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop unverändert 40px, am Handy 28px. Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77 VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
/**
* Inhalt einer beschreibenden Gruppen-Karte (ohne das <article> drumherum). Als Closure,
* weil es diese Karte in zwei Fassungen gibt: als Tab-Panel (mit role/aria) und gestapelt
* (ohne). Vorher stand das Markup nur in der Tab-Fassung; eine zweite Kopie daneben wäre
* die Stelle, an der beide auseinanderlaufen.
*/
$renderCardBody = static function (array $g) use ($renderSession): void {
?>
<div class="group-card__body">
<div class="group-card__header">
<h3 class="group-card__name"><?= e($g['name']) ?></h3>
<?php if (!empty($g['level'])): ?>
<p class="group-card__level"><?= e($g['level']) ?></p>
<?php endif; ?>
</div>
<?php if (!empty($g['intro'])): ?>
<p class="group-card__intro"><?= e($g['intro']) ?></p>
<?php endif; ?>
<?php foreach ($g['details'] ?? [] as $det): ?>
<?php if (!empty($det['heading'])): ?>
<h4 class="group-card__detail-heading"><?= e($det['heading']) ?></h4>
<?php endif; ?>
<?php if (!empty($det['text'])): ?>
<p class="group-card__detail-text"><?= e($det['text']) ?></p>
<?php endif; ?>
<?php endforeach; ?>
<?php if (!empty($g['recommendation'])): ?>
<p class="group-card__recommendation text-muted"><?= e($g['recommendation']) ?></p>
<?php endif; ?>
</div>
<?php if (!empty($g['sessions'])): ?>
<div class="group-card__schedule">
<p class="group-card__schedule-title"><?= icon('clock', 'group-card__schedule-icon') ?>Trainingszeiten</p>
<ul class="schedule-list">
<?php foreach ($g['sessions'] as $s): $renderSession($s); endforeach; ?>
</ul>
<?php if (!empty($g['contact'])): ?>
<p class="group-card__trainers"><span class="schedule-row__trainers-label">Kontakt:</span> <?= e($g['contact']) ?></p>
<?php endif; ?>
<?php if (!empty($g['trainers'])): ?>
„Trainerteam" statt „Übungsleitung", Eva Wünsch mit Sternchen Eva Wünsch aus der Turnabteilung am 31.07.2026: „Der Sternchentext müsste eigentlich sein, dass die Person mit dem Sternchen Übungsleiter ist. Von daher müsste vor den Namen 'Trainerteam' stehen. Einige sind auch nicht ÜL." Ich hatte davon nur die Fußnote erledigt. Das Label blieb „Übungsleitung" und damit stand die falsche Aussage weiter auf der Seite: neun namentlich genannte Personen liefen unter „Übungsleitung", vier davon sind es. Das ist keine Formulierungsfrage, die Seite behauptete etwas über echte Menschen. Label jetzt „Trainerteam:" an beiden Stellen (Trainingszeit und Gruppe), der Stern bleibt die Auszeichnung und die Fußnote erklärt ihn. So stand es auch auf der alten Seite: „TRAINERTEAM (* = ÜBUNGSLEITER)". Ein Kommentar an der Stelle sagt, warum es nicht zurückbenannt werden darf. Dabei fiel ein zweiter Datenfehler auf: die alte Seite führt „Annelie Frisque*, Eva Wünsch*", bei uns stand Eva Wünsch ohne Stern. Sie ist Übungsleiterin. Geprüft: „Übungsleitung" kommt auf keiner der sechs Turn-Seiten mehr vor, die Grashüpfer-Zeile lautet jetzt Zeichen für Zeichen wie in Evas Screenshot. Die Fußnote erscheint nur in Sektionen mit mehreren Gruppen — nachgezählt, dass beide Disziplinen mit Sternchen-Namen mehrere Gruppen haben, die Einzelgruppen-Disziplinen tragen keine Sterne. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:57:31 +02:00
<p class="group-card__trainers"><span class="schedule-row__trainers-label">Trainerteam:</span> <?= e(implode(', ', $g['trainers'])) ?></p>
Wettkampfturnen: Karten offen untereinander statt hinter Tabs Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die Flamingos-Karte allein ist am Handy 2142px hoch. .group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte. Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in data/turnen.json las ebenfalls niemand, ist raus. Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide in einem halben Jahr auseinanderlaufen. Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte. Zwei Nachbesserungen, beide gemessen: - Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen), in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding weg, jetzt 80px. - Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop unverändert 40px, am Handy 28px. Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77 VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
<?php endif; ?>
<?php if (!empty($g['note'])): ?>
<p class="group-card__note text-muted"><?= e($g['note']) ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
<?php
};
/**
* Schlichte Gruppen-Karte (Name + Trainingszeiten). Als Closure, weil sie an zwei Stellen
* gebraucht wird: im Raster der eigenen Gruppen-Sektion und bei $groupsInline als Kachel
* direkt im Bento-Dashboard.
*/
$renderSimpleCard = static function (array $g) use ($renderSession): void {
?>
<article class="group-card">
<div class="group-card__body">
<div class="group-card__header">
<h3 class="group-card__name"><?= e($g['name']) ?></h3>
<?php if (!empty($g['level'])): ?>
<p class="group-card__level"><?= e($g['level']) ?></p>
<?php endif; ?>
</div>
</div>
<?php if (!empty($g['sessions'])): ?>
<div class="group-card__schedule">
<ul class="schedule-list">
<?php foreach ($g['sessions'] as $s): $renderSession($s); endforeach; ?>
</ul>
</div>
<?php endif; ?>
</article>
<?php
};
$soleSessions = $single ? ($groups[0]['sessions'] ?? []) : [];
$soleNote = $single ? ($groups[0]['note'] ?? '') : '';
?>
<section class="section turnen-dash-section" id="<?= e($anchor) ?>">
<div class="container">
<div class="turnen-dash<?= $single ? ' turnen-dash--single' : '' ?><?= $notice !== '' ? ' turnen-dash--notice' : '' ?><?= $groupsInline ? ' turnen-dash--groups' : '' ?>">
<?php if ($notice !== ''): ?>
Trampolin: Schnupper-Einladung, Hinweise bekommen zwei Tonlagen 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
2026-07-31 22:04:41 +02:00
<?php component('notice', [
'title' => $noticeTitle,
'text' => $notice,
'tone' => $noticeTone,
'icon' => $noticeIcon,
'class' => 'turnen-dash__notice',
]); ?>
<?php endif; ?>
<?php if ($single): ?>
<article class="bento__tile turnen-dash__training">
<h2 class="bento__heading"><?= icon('clock', 'bento__hicon') ?>Trainingszeiten</h2>
<ul class="bento__sessions">
<?php foreach ($soleSessions as $s): ?>
<li>
<span class="bento__day"><?= e($s['day']) ?></span>
<span class="bento__time"><?= e($s['time']) ?></span>
</li>
<?php endforeach; ?>
</ul>
<?php if ($soleNote !== ''): ?>
<p class="bento__note text-muted"><?= e($soleNote) ?></p>
<?php endif; ?>
<span class="bento__deco" aria-hidden="true"><?= icon('clock') ?></span>
</article>
<?php endif; ?>
<?php if ($lead !== '' || $email !== '' || $telHref !== ''): ?>
<article class="bento__tile bento__tile--accent turnen-dash__contact">
<span class="bento__stat-label">Kontakt</span>
<?php if ($lead !== ''): ?>
<span class="bento__stat-value"><?= e($lead) ?></span>
<?php endif; ?>
<?php if ($email !== ''): ?>
<a class="turnen-dash__mail" href="mailto:<?= e($email) ?>"><?= icon('envelope', 'turnen-dash__mail-icon') ?>E-Mail schreiben</a>
<?php endif; ?>
<?php if ($telHref !== ''): ?>
<a class="turnen-dash__mail" href="<?= e($telHref) ?>"><?= icon('telephone', 'turnen-dash__mail-icon') ?><?= e($contactPhone) ?></a>
<?php endif; ?>
</article>
<?php endif; ?>
<?php if ($hasVenue): ?>
<article class="bento__tile turnen-dash__venue">
<div class="turnen-dash__venue-main">
<h2 class="bento__heading"><?= icon('geo-alt', 'bento__hicon') ?>Trainingsort</h2>
<p class="bento__address">
<strong><?= e($location['venue']) ?></strong>
<?php if (!empty($location['street'])): ?> · <?= e($location['street']) ?><?php endif; ?>
<?php if (!empty($location['zip']) || !empty($location['city'])): ?>, <?= e(trim(($location['zip'] ?? '') . ' ' . ($location['city'] ?? ''))) ?><?php endif; ?>
</p>
</div>
<a class="btn btn--outline turnen-dash__route" href="https://www.google.com/maps/search/?api=1&amp;query=<?= e($mapsQuery) ?>" target="_blank" rel="noopener">Route planen<span class="visually-hidden"> (öffnet Google Maps in neuem Tab)</span></a>
<span class="bento__deco" aria-hidden="true"><?= icon('geo-alt') ?></span>
</article>
<?php endif; ?>
<?php if ($groupsInline): ?>
<div class="turnen-dash__groups group-list--grid">
<?php foreach ($groups as $g): $renderSimpleCard($g); endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php if ($groupsInline && $footnote !== ''): ?>
<p class="group-list__footnote text-muted"><?= e($footnote) ?></p>
<?php endif; ?>
</div>
</section>
<?php if (!$single && !$groupsInline): ?>
Wettkampfturnen: Karten offen untereinander statt hinter Tabs Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die Flamingos-Karte allein ist am Handy 2142px hoch. .group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte. Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in data/turnen.json las ebenfalls niemand, ist raus. Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide in einem halben Jahr auseinanderlaufen. Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte. Zwei Nachbesserungen, beide gemessen: - Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen), in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding weg, jetzt 80px. - Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop unverändert 40px, am Handy 28px. Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77 VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
<section class="section group-list-section<?= $groupsTitleHidden ? ' group-list-section--tight' : '' ?>" id="gruppen">
<div class="container">
Wettkampfturnen: Karten offen untereinander statt hinter Tabs Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die Flamingos-Karte allein ist am Handy 2142px hoch. .group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte. Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in data/turnen.json las ebenfalls niemand, ist raus. Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide in einem halben Jahr auseinanderlaufen. Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte. Zwei Nachbesserungen, beide gemessen: - Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen), in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding weg, jetzt 80px. - Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop unverändert 40px, am Handy 28px. Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77 VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
<h2 class="group-list__title<?= $groupsTitleHidden ? ' visually-hidden' : '' ?>"><?= e($groupsTitle) ?></h2>
<?php if ($rich && $layout === 'stacked'): ?>
<?php /* Karten direkt untereinander, alle sichtbar. Für Disziplinen mit wenigen Gruppen:
Tabs verstecken dort mehr Inhalt als sie ordnen, und die offene Kartenreihe hält
den Bento-Eindruck der Seite. Bei vielen textreichen Gruppen (Kinderturnen) sind
Tabs weiter richtig, sonst wird die Seite endlos. */ ?>
<div class="group-list group-list--stacked">
<?php foreach ($groups as $g): ?>
<article class="group-card">
<?php $renderCardBody($g); ?>
</article>
<?php endforeach; ?>
</div>
<?php elseif ($rich): ?>
<div class="group-tabs" data-group-tabs>
<div class="group-tabs__bar" role="tablist" aria-label="<?= e($groupsTitle) ?>">
<?php foreach ($groups as $i => $g): ?>
Design-System: Button-Varianten konsolidieren, 44px-Trefferflächen Dritter Teil. .btn hatte nur primary + outline + sm/lg, daneben lebten mehrere Controls als Eigenbau — teils zeichengleich dupliziert. Tabs: .group-tabs__tab und .filter-bar__tab waren bis auf flex-shrink und 4px Innenabstand identisch. Beide laufen jetzt über eine gemeinsame .tab-Basis, die aria-pressed (Filter) und aria-selected (Tabs) gleich behandelt; von den BEM-Klassen bleibt nur das jeweilige Delta. Innenabstand vereinheitlicht auf --space-m, die Gruppen-Tabs verlieren dadurch 4px pro Seite. Icon-Buttons: .ad-banner__nav-btn und .ad-banner__toggle waren vollständige Duplikate voneinander. Sie tragen jetzt .btn .btn--icon und behalten nur die Glas-Optik als Delta. Der Selektor schreibt .btn mit — utilities.css lädt nach components.css, bei gleicher Spezifität gewänne sonst die rote .btn-Fläche. Trefferflächen auf 44px (WCAG 2.5.5): .btn bekommt min-height und wird inline-flex, damit Icon und Label ohne Zusatz-Markup zentriert liegen. Bezeichnend auch hier — .department-choice__btn hatte inline-flex, align-items und exakt denselben gap bereits selbst deklariert und gibt sie jetzt ab. Controls, die optisch klein bleiben sollen, strecken die Fläche stattdessen unsichtbar per Pseudo-Element (.tap-target, automatisch auch für .btn--sm) — gewertet wird die Trefferfläche, nicht die gezeichnete. Der Banner-Indikator brauchte einen anderen Weg: er trug overflow: hidden, um den Fortschrittsbalken zu beschneiden, was jede Erweiterung wieder abgeschnitten hätte. Der sichtbare 6px-Balken liegt jetzt im ::before, der Fortschritt ist selbst rund — overflow entfällt, der Button ist 48x44px. Haupt-Nav-Links, Dropdown-Trigger und Footer-Social-Links bekommen min-height statt einer Pseudo-Erweiterung: sie stehen in Reihen, wo überlappende Trefferflächen den Nachbarn verdecken würden. In der 60px hohen Kopfleiste ist die Höhe ohnehin nicht sichtbar. Bewusst unverändert: die Formular-Checkbox bleibt bei 24px (WCAG 2.5.8). <input> ist ein Replaced Element, dort greift ::after nicht zuverlässig — und das zugehörige <label> ist klickbar und liefert die große Fläche. .btn--ghost ist angelegt, hat aber aktuell keinen Abnehmer: die vermuteten nackten Links sind bei näherem Hinsehen Bildkarten (.instagram__link), Wrapper (.split__cta) oder eigenständige Karten-Affordanzen (.team-card__cta), keine tertiären Buttons. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
2026-07-27 10:28:02 +02:00
<button class="tab group-tabs__tab" role="tab" type="button"
id="gtab-<?= $i ?>"
aria-controls="gpanel-<?= $i ?>"
aria-selected="<?= $i === 0 ? 'true' : 'false' ?>"
><?= e($g['name']) ?></button>
<?php endforeach; ?>
</div>
<?php foreach ($groups as $i => $g): ?>
<article class="group-card group-tabs__panel" role="tabpanel"
id="gpanel-<?= $i ?>"
aria-labelledby="gtab-<?= $i ?>"
tabindex="0"
>
Wettkampfturnen: Karten offen untereinander statt hinter Tabs Bei zwei Gruppen verstecken Tabs mehr Inhalt als sie ordnen, und die offene Kartenreihe hält den Bento-Eindruck der Seite (Entscheidung Felix). Das Kinderturnen bleibt bei Tabs, dort sind es drei textreiche Gruppen — die Flamingos-Karte allein ist am Handy 2142px hoch. .group-list--stacked lag vollständig im CSS, wurde aber von KEINEM PHP ausgegeben: totes CSS aus der Zeit vor den Tabs, das heute morgen sogar noch meine minmax(0, …)-Korrektur mitbekommen hat, ohne dass es jemand sehen konnte. Jetzt ist es lebendig. Dritter toter Rest heute: "layout": "blocks" in data/turnen.json las ebenfalls niemand, ist raus. Der Karteninhalt steht dafür jetzt einmal statt zweimal. Er wird in zwei Fassungen gebraucht (Tab-Panel mit role/aria und gestapelt ohne) und ist deshalb eine Closure geworden — eine zweite Kopie wäre die Stelle, an der beide in einem halben Jahr auseinanderlaufen. Die Überschrift ist visually-hidden, nicht entfernt: sonst hätte die Sektion keinen zugänglichen Namen und die Überschriften-Gliederung eine Lücke. Dasselbe Muster wie $title_hidden in team-cards. Damit braucht die Seite auch kein group-tabs.js mehr, das ich zwei Commits vorher erst dazugeschrieben hatte. Zwei Nachbesserungen, beide gemessen: - Die versteckte Überschrift hinterließ ein Loch. Zwischen Dashboard und erster Karte standen 160px (80 unten von der einen Sektion, 80 oben von der anderen), in denen nur nackte Hintergrund-Textur zu sehen war — vorher füllte die Überschrift diesen Raum. .group-list-section--tight nimmt das obere Padding weg, jetzt 80px. - Die gestapelten Karten hatten feste 40px Innenabstand; auf --pad-card-lg umgestellt, dieselbe Korrektur wie heute morgen beim Tab-Panel. Am Desktop unverändert 40px, am Handy 28px. Nicht behoben, weil kein Fehler: die Hintergrund-Textur wirkt in leeren Flächen weich. Sie ist 1920x2545 groß und wird bei 1478px Fensterbreite auf Faktor 0,77 VERKLEINERT, ist also nicht unscharf gerechnet. Sichtbar wurde sie durch den Leerraum plus background-attachment: fixed, das sie beim Scrollen stehen lässt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:56:21 +02:00
<?php $renderCardBody($g); ?>
</article>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="group-list group-list--grid">
<?php foreach ($groups as $g): $renderSimpleCard($g); endforeach; ?>
</div>
<?php endif; ?>
<?php if ($footnote !== ''): ?>
<p class="group-list__footnote text-muted"><?= e($footnote) ?></p>
<?php endif; ?>
</div>
</section>
<?php endif; ?>