2026-06-19 10:33:03 +02:00
|
|
|
<?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"
|
|
|
|
|
* $lead string (optional) — verantwortliche Übungsleitung (Akzent-Kachel)
|
|
|
|
|
* $notice string (optional) — hervorgehobener Hinweis (z. B. Wartelisten-Status)
|
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 string (optional) — Überschrift dazu. Ohne sie liest sich die rote Kachel
|
|
|
|
|
* wie eine Fehlermeldung; mit ihr ist auf einen Blick klar, dass es eine
|
|
|
|
|
* Information ist. Dieselben zwei Felder rendert contact-form.php über dem
|
|
|
|
|
* Formular, damit der Hinweis nur EINMAL in data/turnen.json steht.
|
2026-06-19 10:33:03 +02:00
|
|
|
* $groups array von Gruppen:
|
|
|
|
|
* { name, level?, intro?, details?:[{heading,text}], recommendation?, note?,
|
|
|
|
|
* trainers?:[…], sessions:[{ label?, day, time, note?, trainers?:[…] }] }
|
|
|
|
|
* $footnote string (optional) — Erläuterung (z. B. zur Sternchen-Kennzeichnung)
|
|
|
|
|
* $groupsTitle string (optional, Default 'Unsere Gruppen')
|
|
|
|
|
* $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';
|
|
|
|
|
$lead = $lead ?? '';
|
|
|
|
|
$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 ?? '';
|
2026-06-19 10:33:03 +02:00
|
|
|
$footnote = $footnote ?? '';
|
|
|
|
|
$groups = $groups ?? [];
|
|
|
|
|
$location = $location ?? [];
|
|
|
|
|
|
|
|
|
|
if ($groups === []) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$club = json_load('club');
|
|
|
|
|
$email = $club['email_turnen'] ?? ($club['email'] ?? '');
|
|
|
|
|
|
|
|
|
|
$single = count($groups) === 1;
|
|
|
|
|
$rich = false;
|
|
|
|
|
foreach ($groups as $g) {
|
|
|
|
|
if (!empty($g['intro']) || !empty($g['details'])) {
|
|
|
|
|
$rich = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$hasVenue = !empty($location['venue']);
|
|
|
|
|
$mapsQuery = $hasVenue ? rawurlencode(trim(
|
|
|
|
|
($location['venue'] ?? '') . ', '
|
|
|
|
|
. ($location['street'] ?? '') . ', '
|
|
|
|
|
. ($location['zip'] ?? '') . ' ' . ($location['city'] ?? ''),
|
|
|
|
|
', '
|
|
|
|
|
)) : '';
|
|
|
|
|
|
|
|
|
|
/** Eine Trainingszeit (Tag/Zeit + optional Label, Notiz, Übungsleitung) ausgeben. */
|
|
|
|
|
$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'])): ?>
|
|
|
|
|
<span class="schedule-row__trainers"><span class="schedule-row__trainers-label">Übungsleitung:</span> <?= e(implode(', ', $s['trainers'])) ?></span>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</li>
|
|
|
|
|
<?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' : '' ?>">
|
|
|
|
|
<?php if ($notice !== ''): ?>
|
|
|
|
|
<article class="bento__tile turnen-dash__notice">
|
|
|
|
|
<?= icon('info-circle', 'turnen-dash__notice-icon') ?>
|
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
|
|
|
<div>
|
|
|
|
|
<?php if ($noticeTitle !== ''): ?>
|
|
|
|
|
<h2 class="turnen-dash__notice-title"><?= e($noticeTitle) ?></h2>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<p><?= e($notice) ?></p>
|
|
|
|
|
</div>
|
2026-06-19 10:33:03 +02:00
|
|
|
</article>
|
|
|
|
|
<?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 !== ''): ?>
|
|
|
|
|
<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; ?>
|
|
|
|
|
</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&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; ?>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<?php if (!$single): ?>
|
|
|
|
|
<section class="section group-list-section" id="gruppen">
|
|
|
|
|
<div class="container">
|
|
|
|
|
<h2 class="group-list__title"><?= e($groupsTitle) ?></h2>
|
2026-06-20 20:48:39 +02:00
|
|
|
<?php if ($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"
|
2026-06-20 20:48:39 +02:00
|
|
|
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"
|
|
|
|
|
>
|
2026-06-19 10:33:03 +02:00
|
|
|
<div class="group-card__body">
|
2026-06-20 20:48:39 +02:00
|
|
|
<div class="group-card__header">
|
|
|
|
|
<h3 class="group-card__name"><?= e($g['name']) ?></h3>
|
2026-06-19 10:33:03 +02:00
|
|
|
<?php if (!empty($g['level'])): ?>
|
2026-06-20 20:48:39 +02:00
|
|
|
<p class="group-card__level"><?= e($g['level']) ?></p>
|
2026-06-19 10:33:03 +02:00
|
|
|
<?php endif; ?>
|
2026-06-20 20:48:39 +02:00
|
|
|
</div>
|
2026-06-19 10:33:03 +02:00
|
|
|
<?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['trainers'])): ?>
|
|
|
|
|
<p class="group-card__trainers"><span class="schedule-row__trainers-label">Übungsleitung:</span> <?= e(implode(', ', $g['trainers'])) ?></p>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<?php if (!empty($g['note'])): ?>
|
|
|
|
|
<p class="group-card__note text-muted"><?= e($g['note']) ?></p>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</article>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</div>
|
2026-06-20 20:48:39 +02:00
|
|
|
<?php else: ?>
|
|
|
|
|
<div class="group-list group-list--grid">
|
|
|
|
|
<?php foreach ($groups as $g): ?>
|
|
|
|
|
<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 endforeach; ?>
|
|
|
|
|
</div>
|
|
|
|
|
<?php endif; ?>
|
2026-06-19 10:33:03 +02:00
|
|
|
<?php if ($footnote !== ''): ?>
|
|
|
|
|
<p class="group-list__footnote text-muted"><?= e($footnote) ?></p>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
<?php endif; ?>
|