Files
tsv08kulmbach-website/public/assets/css/layout.css

754 lines
21 KiB
CSS
Raw Normal View History

/* ============================================================
LAYOUT Container, Header, Navigation, Footer, Sektionen
============================================================ */
.container {
max-width: var(--container-max);
margin-inline: auto;
padding-inline: var(--container-pad);
}
.section {
padding-block: var(--space-section);
}
.section--center {
text-align: center;
}
.section--center p {
margin-inline: auto;
}
/* --- Header --- */
.site-header {
position: absolute;
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News, Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover, Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das Skript selbst, statt sie von Hand zu prüfen. Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie doppelt gepflegt wird. News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head. Behobene Fehler: - Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen (Ergebnis-Rückblick, Vorschau, Cover). - --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links unter dem Logo. - base_url in der Produktions-Config ohne www, während .htaccess auf www umleitet; preflight prüft das jetzt. - .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen. Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php (Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit Warnung, wenn base_url nicht auf die echte Domain zeigt). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
inset: var(--header-top) 0 auto 0;
z-index: 10;
}
/* Desktop: Logo + Schriftzug links | Nav-Punkte und CTA rechtsbündig zusammen */
.site-header__inner {
display: flex;
align-items: center;
gap: var(--space-l);
min-height: var(--header-h);
}
.site-header__brand {
display: flex;
align-items: center;
gap: var(--space-xs);
text-decoration: none;
color: var(--clr-text);
}
.site-header__logo {
height: var(--logo-h);
width: auto;
filter: drop-shadow(var(--shadow));
}
.site-header__name {
font-family: var(--ff-heading);
font-size: var(--fs-675);
line-height: 0.85;
text-transform: uppercase;
white-space: nowrap;
text-shadow: var(--shadow);
}
.site-header__actions {
display: flex;
align-items: center;
gap: var(--space-2xs);
}
/* Header-CTAs haben eine eigene, kompakte Größe (kein generisches sm/lg-Preset):
etwas dezentere Schrift als Standard-Buttons, engeres L/R-Padding. */
.site-header__actions .btn {
font-size: var(--fs-400);
padding-inline: 0.9em;
}
/* --- Navigation --- */
/* margin-left:auto schiebt Nav + nachfolgende CTAs als Block nach rechts */
.site-nav {
margin-left: auto;
}
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News, Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover, Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das Skript selbst, statt sie von Hand zu prüfen. Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie doppelt gepflegt wird. News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head. Behobene Fehler: - Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen (Ergebnis-Rückblick, Vorschau, Cover). - --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links unter dem Logo. - base_url in der Produktions-Config ohne www, während .htaccess auf www umleitet; preflight prüft das jetzt. - .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen. Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php (Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit Warnung, wenn base_url nicht auf die echte Domain zeigt). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
/* CTAs im Menü-Overlay: nur im Burger-Modus sichtbar (dort ersetzen sie die
Header-Buttons Best Practice: mobil kein Dauer-Button neben dem Burger,
die Handlungsaufrufe stehen prominent am Ende des Menüs). */
.site-nav__ctas {
display: none;
}
.site-nav__toggle {
display: none;
}
.site-nav__list {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: var(--space-l);
font-size: var(--fs-500);
}
.site-nav__list a:not(.btn) {
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
display: inline-flex;
align-items: center;
/* 44px Trefferfläche (WCAG 2.5.5). Die Leiste ist --header-h (60px) hoch und
zentriert ihre Punkte, die Höhe ist deshalb nicht sichtbar. */
min-height: var(--tap-min);
color: var(--clr-text);
text-decoration: none;
padding-block: 0.25em;
border-bottom: 2px solid transparent;
transition: border-color var(--transition);
}
.site-nav__list a:not(.btn):hover,
.site-nav__list a:not(.btn)[aria-current="page"] {
border-bottom-color: var(--clr-accent);
}
/* --- Dropdown-Untermenü --- */
.site-nav__item--has-submenu {
position: relative;
}
/* Unsichtbare Brücke über die Lücke zwischen Trigger und Menü: hält den :hover
durchgehend, damit das Dropdown beim Rüberfahren nicht sofort schließt. */
.site-nav__item--has-submenu:hover::after {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
width: 15rem;
max-width: 90vw;
height: 0.85rem;
}
/* Dropdown-Trigger sieht aus wie die übrigen Nav-Links (ist aber ein <button>). */
.site-nav__sublink {
display: inline-flex;
align-items: center;
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
min-height: var(--tap-min);
gap: 0.3em;
margin: 0;
padding: 0.25em 0;
font: inherit;
color: var(--clr-text);
background: none;
border: 0;
border-bottom: 2px solid transparent;
cursor: pointer;
transition: border-color var(--transition);
}
.site-nav__sublink:hover,
.site-nav__sublink[data-current],
.site-nav__item--has-submenu:focus-within > .site-nav__sublink {
border-bottom-color: var(--clr-accent);
}
.site-nav__chevron {
Design-System: Typo-Skala schließen, h4–h6 auf Abel, Icon-Größen tokenisieren Zweiter Teil der Konsolidierung. Vorher standen 31 font-size-Werte außerhalb der Skala — beim Durchgehen stellte sich heraus, dass 20 davon gar keine Textgrößen sind: der icon()-Helper rendert 1em, also steuerte font-size dort die Icon-Größe. Dafür gibt es jetzt eine eigene, kleine Skala (--icon-s/m/l/xl in rem, --icon-inline-sm/--icon-inline/--icon-inline-lg in em für Icons, die im Textfluss mitwachsen sollen). Elf Ad-hoc-Werte fallen damit auf sieben Stufen. h4–h6 laufen nicht mehr über Coolvetica. Sie lagen mit 26px und 21px unter der --fs-650-Grenze aus CLAUDE.md, ab der der Condensed-Schnitt in Uppercase unleserlich wird — die Regel war also nur dokumentiert, nicht durchsetzbar. Jetzt tragen h1–h3 Coolvetica (alle über der Grenze) und h4–h6 Abel mit Sperrung. Bezeichnend: das einzige <h4> im Markup hatte sich genau dieses Muster bereits von Hand gebaut. Die Basisregel liefert es jetzt, die Komponente gibt ihre drei Duplikat-Deklarationen ab — sichtbar ändert sich dadurch nichts. Neue Stufen schließen die Lücken: --fs-550 (fluider Lead), --fs-690 (Karten- Überschrift) und --fs-1000 (Display-Hero). Vier fast deckungsgleiche clamp()- Rampen für Karten-Überschriften (bento__heading, bento__stat-value, group-card__name, team-card__name) laufen jetzt über --fs-690; team-card__name wächst dabei am Desktop um ~5px, die anderen drei bleiben im 1px-Bereich. Die Wappen-Initialen im Matchcenter hingen an zwei Mini-Schriftgrößen (10,4 und 11,2px) unterhalb der Skala. Sie sind in Wahrheit ~0,4 × Kreisdurchmesser, folgen jetzt also --crest-size statt einer eigenen font-size. Nebenbei fallen drei redundante Doppel-Selektoren weg: .crest--initials trägt immer auch .crest. Ebenfalls tokenisiert: 27 letter-spacing-Werte mit elf verschiedenen Zahlen auf vier Stufen (--ls-tight/caps/wide/wider), maximale Abweichung 0,02em. Danach steht kein font-size- und kein letter-spacing-Wert mehr außerhalb von tokens.css. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
2026-07-27 10:17:13 +02:00
font-size: var(--icon-inline-sm);
transition: transform var(--transition);
}
.site-nav__item--has-submenu:hover .site-nav__chevron,
.site-nav__item--has-submenu:focus-within .site-nav__chevron,
.site-nav__sublink[aria-expanded="true"] .site-nav__chevron {
transform: rotate(180deg);
}
/* Höhere Spezifität (0,2,0) nötig, damit das Panel-Padding den globalen
reset.css-Selektor `ul[class] { padding: 0 }` (0,1,1) schlägt. */
.site-nav__list .site-nav__submenu {
position: absolute;
top: calc(100% + 0.4rem);
left: 50%;
transform: translateX(-50%) scale(0.97);
transform-origin: top center;
z-index: 25;
display: grid;
gap: var(--space-3xs);
Nav-Labels an die Seitentitel angeglichen, Dropdown-Breite nachgerechnet Im Menü stand „Eltern-Kind-Turnen", die Seite heißt „Eltern-Kind- & Kinderturnen" — das Kinderturnen fehlte also im Menü, obwohl Eltern genau danach suchen. Beim Prüfen fielen zwei weitere Abweichungen auf, alle drei sind jetzt gleich: Eltern-Kind-Turnen → Eltern-Kind- & Kinderturnen Fitness und Pilates → Fitness & Pilates Trampolin → Trampolinturnen Punkte („…") braucht es dafür nicht: das Dropdown hat min-width und white-space: nowrap, das Panel wächst also mit dem längsten Punkt statt zu beschneiden. Ein abgeschnittener Menüpunkt wäre auch die schlechtere Lösung, weil man dann nicht mehr liest, wohin der Link führt. Es war aber knapper als es aussah. Gemessen mit dem längsten Punkt (187,6px): ruhend 190,0px Platz, im Hover UND auf der aktiven Seite nur 186,0px, weil padding-left dort von --space-s (16px) auf --space-m (20px) wächst. Das Panel wäre beim Rübergehen um zwei Pixel gewachsen und auf dieser Seite dauerhaft breiter gewesen — sichtbar als Flackern, weil es mittig sitzt und sich nach beiden Seiten schiebt. Behoben mit einem Wert: min-width 15rem → 15,5rem. Alle drei Panels bleiben jetzt in jedem Zustand bei 248px, mit 8,4px Luft statt 1,6px Fehlbetrag. Auch bei 1250px Viewport (der schmalsten Breite mit Desktop-Menü) liegt kein Panel am Rand. Die Rechnung steht als Kommentar über der Regel, damit der nächste längere Menüpunkt nachgemessen wird. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 22:31:58 +02:00
/* 15,5rem statt 15rem: der längste Punkt Eltern-Kind- & Kinderturnen" ist 187,6px
breit, und im Hover bzw. auf der aktiven Seite wächst padding-left von --space-s
auf --space-m. Bei 15rem blieben dafür nur 186px, das Panel wuchs beim Hover um
zwei Pixel und war auf dieser Seite dauerhaft breiter als Flackern sichtbar,
weil das Panel mittig sitzt und sich also nach beiden Seiten schiebt. Wer hier
einen längeren Menüpunkt einträgt, misst die Textbreite im Hover-Zustand nach. */
min-width: 15.5rem;
margin: 0;
/* Gleichmäßiges Padding rundum — Pillen stehen oben/unten/seitlich gleich dezent frei. */
padding: var(--space-2xs);
list-style: none;
/* Glasiges, leicht transluzentes Panel mit Tiefe über der Hintergrund-Textur. */
background: rgb(20 20 20 / 0.96);
border: 1px solid var(--clr-glass-border);
border-radius: var(--radius-card);
box-shadow: var(--shadow-card);
/* Sanftes Einblenden + leichtes Hochgleiten/Scale statt hartem display-Wechsel. */
opacity: 0;
visibility: hidden;
transition: opacity var(--transition), top var(--transition),
transform var(--transition), visibility var(--transition);
}
@supports (backdrop-filter: blur(1px)) {
.site-nav__list .site-nav__submenu {
background: rgb(20 20 20 / 0.82);
backdrop-filter: blur(14px) saturate(1.4);
}
}
.site-nav__item--has-submenu:hover > .site-nav__submenu,
.site-nav__item--has-submenu:focus-within > .site-nav__submenu,
.site-nav__sublink[aria-expanded="true"] + .site-nav__submenu {
opacity: 1;
visibility: visible;
top: calc(100% + 0.7rem);
transform: translateX(-50%) scale(1);
}
.site-nav__list .site-nav__submenu a:not(.btn) {
position: relative;
display: block;
padding: var(--space-xs) var(--space-s) var(--space-xs) var(--space-s);
border-bottom: 0;
border-radius: var(--radius-btn);
font-size: var(--fs-500);
line-height: 1.25;
white-space: nowrap;
transition: background var(--transition), color var(--transition),
padding-left var(--transition);
}
/* Rote Akzent-Leiste links, die beim Hover/aktiver Seite reinwächst. */
.site-nav__list .site-nav__submenu a:not(.btn)::before {
content: "";
position: absolute;
left: 0.4rem;
top: 50%;
transform: translateY(-50%);
width: 3px;
height: 0;
border-radius: 3px;
background: var(--clr-accent);
transition: height var(--transition);
}
.site-nav__list .site-nav__submenu a:not(.btn):hover,
.site-nav__list .site-nav__submenu a:not(.btn):focus-visible {
border-bottom: 0;
padding-left: var(--space-m);
background: rgb(var(--clr-accent-rgb) / 0.14);
}
.site-nav__list .site-nav__submenu a:not(.btn):hover::before,
.site-nav__list .site-nav__submenu a:not(.btn):focus-visible::before,
.site-nav__list .site-nav__submenu a:not(.btn)[aria-current="page"]::before {
height: 1.1em;
}
.site-nav__list .site-nav__submenu a:not(.btn)[aria-current="page"] {
border-bottom: 0;
padding-left: var(--space-m);
background: rgb(var(--clr-accent-rgb) / 0.2);
}
/* Letzter Dropdown-Punkt mit optischer Trennung (hairline oben + etwas Abstand) */
.site-nav__submenu-sep {
border-top: 1px solid var(--clr-glass-border);
margin-top: var(--space-3xs);
padding-top: var(--space-3xs);
}
@media (max-width: 1249px) {
/* Mobile: Logo links, CTAs + Burger rechts */
.site-header__inner {
display: flex;
justify-content: space-between;
}
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News, Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover, Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das Skript selbst, statt sie von Hand zu prüfen. Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie doppelt gepflegt wird. News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head. Behobene Fehler: - Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen (Ergebnis-Rückblick, Vorschau, Cover). - --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links unter dem Logo. - base_url in der Produktions-Config ohne www, während .htaccess auf www umleitet; preflight prüft das jetzt. - .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen. Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php (Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit Warnung, wenn base_url nicht auf die echte Domain zeigt). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
/* Header-CTAs weichen in den Burger (site-nav__ctas) kein Dauer-Button
neben dem Burger, der Header bleibt ruhig: Logo links, Burger rechts. */
.site-header__actions {
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News, Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover, Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das Skript selbst, statt sie von Hand zu prüfen. Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie doppelt gepflegt wird. News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head. Behobene Fehler: - Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen (Ergebnis-Rückblick, Vorschau, Cover). - --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links unter dem Logo. - base_url in der Produktions-Config ohne www, während .htaccess auf www umleitet; preflight prüft das jetzt. - .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen. Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php (Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit Warnung, wenn base_url nicht auf die echte Domain zeigt). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
display: none;
}
.site-nav__ctas {
display: flex;
flex-direction: column;
gap: var(--space-s);
margin-top: var(--space-2xl);
}
.site-nav__ctas .btn {
justify-content: center;
font-size: var(--btn-font-lg);
}
/* Logo/Brand ebenfalls über dem Overlay halten — Orientierung bei offenem Menü. */
.site-header__brand {
position: relative;
z-index: 30;
}
.site-nav {
order: 3;
margin-left: 0;
}
.site-nav__toggle {
display: grid;
place-items: center;
width: 48px;
height: 48px;
background: transparent;
border: 0;
cursor: pointer;
}
/* Burger: Mittelbalken + Pseudo-Balken, animiert zum X */
.site-nav__toggle-bar,
.site-nav__toggle-bar::before,
.site-nav__toggle-bar::after {
display: block;
width: 28px;
height: 3px;
background: var(--clr-text);
border-radius: 2px;
transition: transform var(--transition), opacity var(--transition);
}
.site-nav__toggle-bar {
position: relative;
}
.site-nav__toggle-bar::before,
.site-nav__toggle-bar::after {
content: '';
position: absolute;
left: 0;
}
.site-nav__toggle-bar::before { top: -9px; }
.site-nav__toggle-bar::after { top: 9px; }
.site-nav__toggle[aria-expanded="true"] .site-nav__toggle-bar {
background: transparent;
}
.site-nav__toggle[aria-expanded="true"] .site-nav__toggle-bar::before {
transform: translateY(9px) rotate(45deg);
}
.site-nav__toggle[aria-expanded="true"] .site-nav__toggle-bar::after {
transform: translateY(-9px) rotate(-45deg);
}
/* Overlay: oben verankert + scrollbar beim Aufklappen eines Dropdowns bleibt
der angetippte Punkt stehen (kein Re-Zentrier-Sprung), lange Menüs laufen
nicht mehr aus dem Screen. */
.site-nav__list {
position: fixed;
inset: 0;
z-index: 20;
flex-direction: column;
Stadionzeitung, Veranstaltungen, News-Artikel, Lightbox und Anzeigen-Verteilung Stadionzeitung: Live-Seiten (Tabellen, Ergebnisse, Vorschau, Termine, News, Kontakt, Historie, Sportheim, Partner, Momente, Impressum), automatisches Cover, Swipe-Viewer mit Vollbildmodus und die Druckfassung als Falt-Heft. Anzeigen kommen jetzt aus dem Bestand und werden gleichmäßig verteilt, nie mehr als zwei hintereinander (vorher vier Blöcke à fünf). Die Doppelseiten-Regel rechnet das Skript selbst, statt sie von Hand zu prüfen. Veranstaltungen: Bereich /veranstaltungen mit Detailseite pro Fest, Lebenszyklus über das Datum (Ankündigung vor dem Fest, Rückblick danach), Galerie mit Lightbox. veranstaltungen.json ist dritte Termin-Quelle, damit ein Fest nie doppelt gepflegt wird. News-Artikel als dritte dynamische Prefix-Route, gemeinsamer detail-head. Behobene Fehler: - Wappen-Kontexte setzten Zeilen-Layout auf .crest statt auf die Zeile; das Wappen wurde zum Grid, Vereinsname und Tore rutschten darunter zusammen (Ergebnis-Rückblick, Vorschau, Cover). - --header-h (60px) unterschätzt die Kopfleiste um 32px (Logo 72px + 20px Versatz): neues abgeleitetes --header-space, sonst klebten Zurück-Links unter dem Logo. - base_url in der Produktions-Config ohne www, während .htaccess auf www umleitet; preflight prüft das jetzt. - .lightbox brauchte display:none mit (0,2,0), sonst lag das Overlay offen. Neue Werkzeuge: bin/anzeige-add.php (Anzeigen-Bestand), bin/veranstaltung-bilder.php (Plakate und Galerien, Alt-Texte folgen der Quelldatei), bin/qr.php (Druck-QR mit Warnung, wenn base_url nicht auf die echte Domain zeigt). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
2026-07-31 14:55:27 +02:00
/* Desktop-Leiste wrappt (horizontale Nav) im Overlay MUSS das aus,
sonst bricht die Spalte bei Überlänge (offenes Untermenü + CTAs)
in eine zweite Spalte um, statt zu scrollen. */
flex-wrap: nowrap;
justify-content: flex-start;
align-items: stretch;
gap: 0;
overflow-y: auto;
overscroll-behavior: contain;
background: rgb(8 8 8 / 0.96);
font-size: var(--fs-650);
text-align: left;
visibility: hidden;
opacity: 0;
transition: opacity var(--transition), visibility var(--transition);
}
/* Padding separat mit (0,2,0): der reset-Selektor ul[class] (0,1,1) würde
es auf der Ein-Klassen-Regel überschreiben. Links auf --container-pad,
damit die Menüpunkte mit dem Logo fluchten. */
.site-nav .site-nav__list {
/* Oben: Header + Logo-Überhang (Logo ragt unter den Header) + Luft. */
padding: calc(var(--header-h) + var(--space-5xl)) var(--container-pad) var(--space-4xl);
}
@supports (backdrop-filter: blur(1px)) {
.site-nav__list {
background: rgb(8 8 8 / 0.85);
backdrop-filter: blur(18px) saturate(1.3);
}
}
.site-nav__list a:not(.btn) {
font-family: var(--ff-heading);
text-transform: uppercase;
}
/* Drawer-Zeilen: volle Tap-Breite mit Hairline-Trennern statt frei schwebender
Punkte Chevron sitzt rechtsbündig, aktive Seite bekommt den roten Balken
links (die Desktop-Unterstreichung wird hier neutralisiert). Untermenü-Höhe
animiert über die 0fr/1fr-Zeile des li-Grids (weicher Accordion-Effekt). */
.site-nav__list > .site-nav__item {
border-bottom: 1px solid var(--clr-glass-border);
}
.site-nav__list > .site-nav__item > a:not(.btn),
.site-nav__sublink {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: var(--space-s) var(--space-3xs);
border-bottom: 0;
font-family: var(--ff-heading);
font-size: var(--fs-650);
text-transform: uppercase;
}
.site-nav__list > .site-nav__item > a[aria-current="page"],
.site-nav__sublink[data-current] {
border-bottom: 0;
box-shadow: inset 3px 0 0 var(--clr-accent);
padding-left: var(--space-s);
}
/* Volle Breite + zentrierter Text: Items und Untermenüs zentrieren sich am
Screen (nicht am shrink-gewrappten li), Unterstreichungen bleiben textbreit. */
.site-nav__item--has-submenu {
display: grid;
grid-template-rows: auto 0fr;
transition: grid-template-rows var(--transition);
}
.site-nav__item--has-submenu:has(> .site-nav__sublink[aria-expanded="true"]) {
grid-template-rows: auto 1fr;
}
/* Ohne JS gibt es kein aria-expanded-Toggling dann (und nur dann) öffnet
:focus-within. Mit JS wäre :focus-within ein Bug: der Button behält nach
dem Tipp den Fokus, das Menü ließe sich nie wieder zuklappen. */
.site-nav:not(.site-nav--js) .site-nav__item--has-submenu:focus-within {
grid-template-rows: auto 1fr;
}
/* Desktop-Panel-Optik komplett zurücksetzen (inkl. backdrop-filter/radius
sonst schimmert das Glas-Panel im Overlay durch). Spezifität (0,2,0) gleich
der Desktop-Regel; gewinnt hier per Quell-Reihenfolge. */
.site-nav__list .site-nav__submenu {
position: static;
transform: none;
display: grid;
min-height: 0;
overflow: hidden;
opacity: 0;
visibility: hidden;
gap: var(--space-3xs);
min-width: 0;
justify-self: stretch;
margin: 0;
/* Kein Padding am ul: bei border-box könnte die 0fr-Zeile sonst nicht auf 0
kollabieren (ungleiche Abstände). Luft macht das letzte Kind-li. Keine
Einrückung Hierarchie kommt aus Schriftart/-größe und Farbe. */
padding: 0;
background: none;
backdrop-filter: none;
border: 0;
border-radius: 0;
box-shadow: none;
text-align: left;
transition: opacity var(--transition), visibility var(--transition);
}
.site-nav__sublink[aria-expanded="true"] + .site-nav__submenu,
.site-nav:not(.site-nav--js) .site-nav__item--has-submenu:focus-within > .site-nav__submenu {
opacity: 1;
visibility: visible;
/* Desktop-Open-Regel (0,3,0) setzt translateX(-50%) hier neutralisieren,
sonst hängt das Untermenü halb aus dem Screen. */
transform: none;
}
.site-nav__list .site-nav__submenu > li:last-child {
padding-bottom: var(--space-xs);
}
/* Unterpunkte in Abel, Groß-/Kleinschreibung wie im Desktop-Dropdown
(Coolvetica-Condensed-Uppercase wird unter 28px unleserlich). Bündig mit
den Hauptpunkten; die aktive Unterseite rückt per Desktop-Regel mit
Akzentbalken + Tint leicht ein. */
.site-nav__list .site-nav__submenu a:not(.btn) {
padding: var(--space-2xs) var(--space-3xs);
font-family: var(--ff-body);
font-size: var(--fs-500);
text-transform: none;
color: var(--clr-text-muted);
white-space: normal;
}
body.nav-open .site-nav__list {
visibility: visible;
opacity: 1;
}
body.nav-open {
overflow: hidden;
}
/* Toggle über dem Overlay halten */
.site-nav__toggle {
position: relative;
z-index: 30;
}
}
@media (max-width: 600px) {
/* Schmale Screens: Schriftzug ausblenden (Logo + aria-label bleiben). */
.site-header__name {
display: none;
}
}
/* --- Back-to-top --- */
.back-to-top {
position: fixed;
right: 1.25rem;
bottom: 1.25rem;
z-index: 15;
display: grid;
place-items: center;
width: 48px;
height: 48px;
color: var(--clr-text);
Design-System: Typo-Skala schließen, h4–h6 auf Abel, Icon-Größen tokenisieren Zweiter Teil der Konsolidierung. Vorher standen 31 font-size-Werte außerhalb der Skala — beim Durchgehen stellte sich heraus, dass 20 davon gar keine Textgrößen sind: der icon()-Helper rendert 1em, also steuerte font-size dort die Icon-Größe. Dafür gibt es jetzt eine eigene, kleine Skala (--icon-s/m/l/xl in rem, --icon-inline-sm/--icon-inline/--icon-inline-lg in em für Icons, die im Textfluss mitwachsen sollen). Elf Ad-hoc-Werte fallen damit auf sieben Stufen. h4–h6 laufen nicht mehr über Coolvetica. Sie lagen mit 26px und 21px unter der --fs-650-Grenze aus CLAUDE.md, ab der der Condensed-Schnitt in Uppercase unleserlich wird — die Regel war also nur dokumentiert, nicht durchsetzbar. Jetzt tragen h1–h3 Coolvetica (alle über der Grenze) und h4–h6 Abel mit Sperrung. Bezeichnend: das einzige <h4> im Markup hatte sich genau dieses Muster bereits von Hand gebaut. Die Basisregel liefert es jetzt, die Komponente gibt ihre drei Duplikat-Deklarationen ab — sichtbar ändert sich dadurch nichts. Neue Stufen schließen die Lücken: --fs-550 (fluider Lead), --fs-690 (Karten- Überschrift) und --fs-1000 (Display-Hero). Vier fast deckungsgleiche clamp()- Rampen für Karten-Überschriften (bento__heading, bento__stat-value, group-card__name, team-card__name) laufen jetzt über --fs-690; team-card__name wächst dabei am Desktop um ~5px, die anderen drei bleiben im 1px-Bereich. Die Wappen-Initialen im Matchcenter hingen an zwei Mini-Schriftgrößen (10,4 und 11,2px) unterhalb der Skala. Sie sind in Wahrheit ~0,4 × Kreisdurchmesser, folgen jetzt also --crest-size statt einer eigenen font-size. Nebenbei fallen drei redundante Doppel-Selektoren weg: .crest--initials trägt immer auch .crest. Ebenfalls tokenisiert: 27 letter-spacing-Werte mit elf verschiedenen Zahlen auf vier Stufen (--ls-tight/caps/wide/wider), maximale Abweichung 0,02em. Danach steht kein font-size- und kein letter-spacing-Wert mehr außerhalb von tokens.css. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
2026-07-27 10:17:13 +02:00
font-size: var(--icon-m);
background: rgb(10 10 10 / 0.82);
border: 1px solid var(--clr-glass-border-strong);
border-radius: 50%;
backdrop-filter: blur(6px);
/* Unsichtbar, bis genug gescrollt wurde (is-visible via JS) */
opacity: 0;
visibility: hidden;
translate: 0 10px;
transition: opacity var(--transition), visibility var(--transition), translate var(--transition), background var(--transition);
}
.back-to-top.is-visible {
opacity: 1;
visibility: visible;
translate: 0 0;
}
.back-to-top:hover {
background: var(--clr-accent);
border-color: var(--clr-accent);
}
@media print {
.back-to-top {
display: none;
}
}
/* --- Footer --- */
.site-footer {
margin-top: var(--space-section);
background: var(--clr-bg-footer);
font-size: var(--fs-500);
}
.site-footer__grid {
display: grid;
grid-template-columns: 1.5fr repeat(4, minmax(0, 1fr));
gap: var(--space-3xl) var(--space-4xl);
padding-block: var(--space-6xl) var(--space-5xl);
}
/* Brand-Block bleibt links stehen, Nav-Spalten rücken rechts daneben */
@media (max-width: 1024px) {
.site-footer__grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.site-footer__brand {
grid-column: 1 / -1;
}
}
@media (max-width: 768px) {
.site-footer__grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 500px) {
.site-footer__grid {
grid-template-columns: 1fr;
gap: var(--space-3xl);
}
}
/* Brand-Block */
.site-footer__brand {
display: grid;
justify-items: start;
gap: var(--space-m);
}
.site-footer__claim {
color: var(--clr-text-muted);
max-width: 36ch;
}
.site-footer__social {
display: grid;
gap: var(--space-2xs);
}
.site-footer__social a {
display: inline-flex;
align-items: center;
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
min-height: var(--tap-min);
gap: var(--space-2xs);
padding-block: var(--space-3xs);
color: var(--clr-text-muted);
text-decoration: none;
transition: color var(--transition);
}
.site-footer__social .icon {
Design-System: Typo-Skala schließen, h4–h6 auf Abel, Icon-Größen tokenisieren Zweiter Teil der Konsolidierung. Vorher standen 31 font-size-Werte außerhalb der Skala — beim Durchgehen stellte sich heraus, dass 20 davon gar keine Textgrößen sind: der icon()-Helper rendert 1em, also steuerte font-size dort die Icon-Größe. Dafür gibt es jetzt eine eigene, kleine Skala (--icon-s/m/l/xl in rem, --icon-inline-sm/--icon-inline/--icon-inline-lg in em für Icons, die im Textfluss mitwachsen sollen). Elf Ad-hoc-Werte fallen damit auf sieben Stufen. h4–h6 laufen nicht mehr über Coolvetica. Sie lagen mit 26px und 21px unter der --fs-650-Grenze aus CLAUDE.md, ab der der Condensed-Schnitt in Uppercase unleserlich wird — die Regel war also nur dokumentiert, nicht durchsetzbar. Jetzt tragen h1–h3 Coolvetica (alle über der Grenze) und h4–h6 Abel mit Sperrung. Bezeichnend: das einzige <h4> im Markup hatte sich genau dieses Muster bereits von Hand gebaut. Die Basisregel liefert es jetzt, die Komponente gibt ihre drei Duplikat-Deklarationen ab — sichtbar ändert sich dadurch nichts. Neue Stufen schließen die Lücken: --fs-550 (fluider Lead), --fs-690 (Karten- Überschrift) und --fs-1000 (Display-Hero). Vier fast deckungsgleiche clamp()- Rampen für Karten-Überschriften (bento__heading, bento__stat-value, group-card__name, team-card__name) laufen jetzt über --fs-690; team-card__name wächst dabei am Desktop um ~5px, die anderen drei bleiben im 1px-Bereich. Die Wappen-Initialen im Matchcenter hingen an zwei Mini-Schriftgrößen (10,4 und 11,2px) unterhalb der Skala. Sie sind in Wahrheit ~0,4 × Kreisdurchmesser, folgen jetzt also --crest-size statt einer eigenen font-size. Nebenbei fallen drei redundante Doppel-Selektoren weg: .crest--initials trägt immer auch .crest. Ebenfalls tokenisiert: 27 letter-spacing-Werte mit elf verschiedenen Zahlen auf vier Stufen (--ls-tight/caps/wide/wider), maximale Abweichung 0,02em. Danach steht kein font-size- und kein letter-spacing-Wert mehr außerhalb von tokens.css. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
2026-07-27 10:17:13 +02:00
font-size: var(--icon-inline-lg);
color: var(--clr-text);
}
/* Spalten-Headings mit rotem Akzentstrich Coolvetica ist schmal,
braucht hier mehr Größe + Laufweite für Lesbarkeit */
.site-footer__heading {
font-size: var(--fs-650);
Design-System: Typo-Skala schließen, h4–h6 auf Abel, Icon-Größen tokenisieren Zweiter Teil der Konsolidierung. Vorher standen 31 font-size-Werte außerhalb der Skala — beim Durchgehen stellte sich heraus, dass 20 davon gar keine Textgrößen sind: der icon()-Helper rendert 1em, also steuerte font-size dort die Icon-Größe. Dafür gibt es jetzt eine eigene, kleine Skala (--icon-s/m/l/xl in rem, --icon-inline-sm/--icon-inline/--icon-inline-lg in em für Icons, die im Textfluss mitwachsen sollen). Elf Ad-hoc-Werte fallen damit auf sieben Stufen. h4–h6 laufen nicht mehr über Coolvetica. Sie lagen mit 26px und 21px unter der --fs-650-Grenze aus CLAUDE.md, ab der der Condensed-Schnitt in Uppercase unleserlich wird — die Regel war also nur dokumentiert, nicht durchsetzbar. Jetzt tragen h1–h3 Coolvetica (alle über der Grenze) und h4–h6 Abel mit Sperrung. Bezeichnend: das einzige <h4> im Markup hatte sich genau dieses Muster bereits von Hand gebaut. Die Basisregel liefert es jetzt, die Komponente gibt ihre drei Duplikat-Deklarationen ab — sichtbar ändert sich dadurch nichts. Neue Stufen schließen die Lücken: --fs-550 (fluider Lead), --fs-690 (Karten- Überschrift) und --fs-1000 (Display-Hero). Vier fast deckungsgleiche clamp()- Rampen für Karten-Überschriften (bento__heading, bento__stat-value, group-card__name, team-card__name) laufen jetzt über --fs-690; team-card__name wächst dabei am Desktop um ~5px, die anderen drei bleiben im 1px-Bereich. Die Wappen-Initialen im Matchcenter hingen an zwei Mini-Schriftgrößen (10,4 und 11,2px) unterhalb der Skala. Sie sind in Wahrheit ~0,4 × Kreisdurchmesser, folgen jetzt also --crest-size statt einer eigenen font-size. Nebenbei fallen drei redundante Doppel-Selektoren weg: .crest--initials trägt immer auch .crest. Ebenfalls tokenisiert: 27 letter-spacing-Werte mit elf verschiedenen Zahlen auf vier Stufen (--ls-tight/caps/wide/wider), maximale Abweichung 0,02em. Danach steht kein font-size- und kein letter-spacing-Wert mehr außerhalb von tokens.css. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MSsVdooFLgPA8gPFp7HwZU
2026-07-27 10:17:13 +02:00
letter-spacing: var(--ls-caps);
margin-bottom: var(--space-s);
padding-bottom: var(--space-2xs);
position: relative;
}
.site-footer__heading a {
color: inherit;
text-decoration: none;
transition: color var(--transition);
}
.site-footer__heading a:hover {
color: var(--clr-accent);
}
.site-footer__heading::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 2.5rem;
height: 3px;
background: var(--clr-accent);
border-radius: 2px;
}
.site-footer__address {
font-style: normal;
color: var(--clr-text-muted);
display: grid;
gap: var(--space-s);
}
.site-footer__address p {
display: flex;
gap: var(--space-2xs);
margin: 0;
}
.site-footer__address .icon {
flex: none;
margin-top: 0.3em;
color: var(--clr-text);
}
.site-footer__list {
display: grid;
gap: var(--space-3xs);
}
.site-footer__list a,
.site-footer__address a {
display: inline-block;
padding-block: var(--space-3xs);
color: var(--clr-text-muted);
text-decoration: none;
transition: color var(--transition);
}
.site-footer__list a:hover,
.site-footer__address a:hover,
.site-footer__social a:hover,
.site-footer__legal a:hover {
color: var(--clr-text);
text-decoration: underline;
text-underline-offset: 0.25em;
}
/* Zusatzlinks (Startseite, Mitglied werden) unter dem Kontakt-Block */
.site-footer__list--extra {
margin-top: var(--space-m);
padding-top: var(--space-m);
border-top: 1px solid var(--clr-glass-border);
}
/* Bottom-Bar */
.site-footer__bottom {
border-top: 1px solid var(--clr-glass-border);
}
.site-footer__bottom-inner {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: var(--space-2xs) var(--space-2xl);
padding-block: var(--space-l);
font-size: var(--fs-400);
color: var(--clr-text-muted);
}
.site-footer__bottom-inner p {
margin: 0;
}
.site-footer__legal {
display: flex;
flex-wrap: wrap;
gap: var(--space-2xs) var(--space-l);
margin: 0;
padding: 0;
list-style: none;
}
.site-footer__legal a {
color: var(--clr-text-muted);
text-decoration: none;
transition: color var(--transition);
}
/* Verbände-Zeile bleibt ab Tablet-Breite einzeilig (wrappt als Ganzes in die nächste Reihe) */
@media (min-width: 768px) {
.site-footer__memberships {
white-space: nowrap;
}
}