Rückmeldung der Turnabteilung mit Screenshot: auf dem iPhone war im Flamingos-Panel der Fließtext rechts abgeschnitten. Gemessen bei 390px mit aktivem Flamingos-Tab (die anderen Panels sind ausgeblendet, ihre Chips haben Breite 0 — deshalb fiel es beim Prüfen der Seite nicht auf): Dokument 442px bei 379px Bildschirm, also 63px Überlauf; der Chip 393px breit in einer 345px-Karte. Ursache war nicht die Textlänge allein, sondern .group-card__level mit `white-space: nowrap` UND `flex-shrink: 0`: der Chip konnte seinen Text nicht brechen und auch nicht schrumpfen, seine Mindestbreite spannte die ganze Karte auf. Von vier level-Chips war nur dieser betroffen (47 Zeichen gegen 17 bis 25). Zwei Änderungen: - Inhalt wie gewünscht gekürzt: "Turnen · fortgeschritten · Alter nach Absprache" → "Turnen · fortgeschritten". - Sicherheitsnetz im CSS: nowrap raus, max-width: 100% rein. Ein Chip trägt Information, deshalb umbrechen statt mit … kürzen. flex-shrink: 0 bleibt, es hält den Chip neben einem kurzen Gruppennamen in Form. Gegenprobe, damit der Fix nicht nur am kürzeren Text hängt: mit dem LANGEN Text und dem CSS-Fix bleibt das Dokument bei 379px, der Chip bricht auf zwei Zeilen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
8318 lines
229 KiB
CSS
8318 lines
229 KiB
CSS
/* ============================================================
|
||
COMPONENTS — eine Sektion pro Komponente, Banner-Kommentare
|
||
============================================================ */
|
||
|
||
/* === hero === */
|
||
.hero {
|
||
position: relative;
|
||
display: grid;
|
||
align-items: end;
|
||
/* svh statt vh: stabil gegenüber ein-/ausfahrender Browser-UI auf Phones. */
|
||
min-height: min(92vh, 900px);
|
||
min-height: min(92svh, 900px);
|
||
padding-block: calc(var(--header-h) + 80px) var(--space-section);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Kompakte Variante: schlanker Unterseiten-Header statt vollflächig. */
|
||
.hero--compact {
|
||
min-height: min(60vh, 560px);
|
||
min-height: min(60svh, 560px);
|
||
padding-block: calc(var(--header-h) + 64px) var(--space-5xl);
|
||
}
|
||
|
||
/* Mobile: Höhe am Inhalt statt am Viewport orientieren — weniger Leer-Scroll.
|
||
Startseiten-Hero lässt bewusst einen Streifen Folge-Inhalt anblitzen (Scroll-Affordance),
|
||
Unterseiten-Heros kollabieren auf Überschrift + Teaser. */
|
||
@media (max-width: 768px) {
|
||
.hero {
|
||
min-height: min(82vh, 700px);
|
||
min-height: min(82svh, 700px);
|
||
padding-block: calc(var(--header-h) + var(--space-5xl)) var(--space-4xl);
|
||
}
|
||
|
||
.hero--compact {
|
||
min-height: 0;
|
||
padding-block: calc(var(--header-h) + var(--space-5xl)) var(--space-3xl);
|
||
}
|
||
}
|
||
|
||
.hero__media {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: -1;
|
||
/* Unten ausblenden statt mit grauem BG zudecken: der echte, dunkle
|
||
texturierte Seiten-Hintergrund scheint durch → transparent & matcht
|
||
den Grund exakt (kein graues, opakes Band mehr). */
|
||
-webkit-mask-image: linear-gradient(180deg, #000 0%, #000 58%, transparent 92%);
|
||
mask-image: linear-gradient(180deg, #000 0%, #000 58%, transparent 92%);
|
||
}
|
||
|
||
.hero__img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.hero__video {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.hero__video {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* Reine Schwarz-Abdunklung (kein grauer Fill) — der Auslauf zum Grund kommt
|
||
aus der Mask auf .hero__media, hier wird nur schwärzer getönt. */
|
||
.hero__media::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background: linear-gradient(180deg, rgb(0 0 0 / 0.45) 0%, rgb(0 0 0 / 0.15) 45%, rgb(0 0 0 / 0.5) 100%);
|
||
}
|
||
|
||
.hero__content {
|
||
width: 100%;
|
||
}
|
||
|
||
.hero__title {
|
||
text-shadow: 0 2px 24px rgb(0 0 0 / 0.5);
|
||
}
|
||
|
||
/* Keyword-Subline innerhalb der h1 (SEO) — visuell kleinere zweite Zeile */
|
||
.hero__tagline {
|
||
display: block;
|
||
margin-top: var(--space-xs);
|
||
font-size: var(--fs-600);
|
||
letter-spacing: var(--ls-wide);
|
||
}
|
||
|
||
.hero__text {
|
||
margin-top: var(--space-s);
|
||
max-width: 60ch;
|
||
text-shadow: 0 1px 12px rgb(0 0 0 / 0.6);
|
||
text-wrap: pretty;
|
||
}
|
||
|
||
/* Display-Hero: schmaleres Maß fürs Zentrieren — ausgewogene Zeilen statt
|
||
vier Zeilen mit unruhigem Flatterrand. */
|
||
.hero--display .hero__text {
|
||
max-width: 52ch;
|
||
}
|
||
|
||
.hero__actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--space-xs);
|
||
margin-top: var(--space-xl);
|
||
}
|
||
|
||
/* Zweispaltige Variante (siehe hero.php „hero--duo"): links Titel/Text/CTAs
|
||
linksbündig, rechts die Termin-Karte als Live-Aufhänger. Ersetzt für
|
||
diese Variante das zentrierte Layout komplett — kein hero--center. */
|
||
.hero__content--duo {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
|
||
align-items: center;
|
||
gap: clamp(var(--space-l), var(--space-m) + 2vw, var(--space-4xl));
|
||
text-align: left;
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.hero__content--duo {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
.hero--duo .hero__title {
|
||
/* --fs-1000 sprengt die schmalere Spalte — eine Stufe kleiner. */
|
||
font-size: var(--fs-900);
|
||
}
|
||
|
||
/* Glas-Karte für die nächsten Termine — dieselbe Optik wie andere Leaf-Karten
|
||
(--clr-glass-bg + Blur, siehe @supports weiter unten), nur mit dem
|
||
stärkeren Rand, weil sie auf bewegtem Video statt festem Seitengrund sitzt.
|
||
Enthält bis zu drei Folien (Spiele + Vereins-Events, s. hero-upcoming.php),
|
||
die assets/js/carousel.js überblendet. */
|
||
.hero__upcoming {
|
||
--hero-upcoming-interval: 5s;
|
||
|
||
padding: clamp(var(--space-m), var(--space-s) + 1vw, var(--space-l));
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border-strong);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.hero__upcoming-track {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
/* Ohne JS liegt nur die erste Folie da (die weiteren tragen hidden). Mit JS
|
||
werden alle Folien gestapelt und übergeblendet — dadurch nimmt die Karte die
|
||
Höhe der höchsten Folie an und der Hero springt beim Wechsel nicht.
|
||
Gleiches Muster wie beim Banner-Slider (.ad-banner.is-enhanced). */
|
||
.hero__upcoming.is-enhanced .hero__upcoming-track {
|
||
display: grid;
|
||
}
|
||
|
||
.hero__upcoming.is-enhanced .hero__upcoming-slide {
|
||
grid-area: 1 / 1;
|
||
/* Grid-Items haben min-width:auto — ohne explizites Minimum weitet eine
|
||
lange nowrap-Vereinszeile (SG-Namen) die Folie über die Kartenkante,
|
||
statt in der Ellipsis-Kette (.match-feature__side/__club) zu enden. */
|
||
min-width: 0;
|
||
opacity: 0;
|
||
transform: translateY(8px);
|
||
pointer-events: none;
|
||
transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s linear 0.5s;
|
||
visibility: hidden;
|
||
}
|
||
|
||
.hero__upcoming.is-enhanced .hero__upcoming-slide.is-active {
|
||
opacity: 1;
|
||
transform: none;
|
||
pointer-events: auto;
|
||
visibility: visible;
|
||
transition: opacity 0.5s ease, transform 0.5s ease, visibility 0s;
|
||
}
|
||
|
||
.hero__upcoming-link {
|
||
display: block;
|
||
color: inherit;
|
||
text-decoration: none;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.hero__upcoming-link:hover {
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.hero__upcoming-label {
|
||
margin-bottom: var(--space-xs);
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wide);
|
||
color: rgb(255 255 255 / 0.65);
|
||
}
|
||
|
||
/* Eingedampft gegenüber dem next-matches-Band auf /matchcenter — die Karte ist
|
||
hier ein Live-Aufhänger neben dem Titel, kein eigener Abschnitt. Die
|
||
Meta-Zeile (Mannschaft · Liga · Heim/Auswärts) fällt komplett weg, der
|
||
hero__upcoming-label übernimmt ihre Rolle („was ist das hier"). */
|
||
.hero__upcoming .match-feature__meta {
|
||
display: none;
|
||
}
|
||
|
||
.hero__upcoming .match-feature {
|
||
gap: var(--space-xs);
|
||
}
|
||
|
||
.hero__upcoming .match-feature .crest {
|
||
--crest-size: clamp(1.75rem, 1.5rem + 0.6vw, 2.1rem);
|
||
}
|
||
|
||
/* Die größere Team-Schrift (--fs-690) braucht mehr Luft zwischen Heim- und
|
||
Gast-Zeile als das Default-Fixture-Gap (--space-2xs) — sonst wirken beide
|
||
Zeilen zusammengequetscht. */
|
||
.hero__upcoming .match-feature__fixture {
|
||
gap: var(--space-s);
|
||
}
|
||
|
||
.hero__upcoming .match-feature__club {
|
||
font-size: var(--fs-690);
|
||
}
|
||
|
||
.hero__upcoming .match-feature__when {
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
/* Event-Folien werden genauso eingedampft wie die Spiel-Folien, damit beide
|
||
Varianten in der Karte gleich groß wirken (und dieselbe Höhe brauchen). */
|
||
.hero__upcoming .event-feature {
|
||
gap: var(--space-xs);
|
||
}
|
||
|
||
.hero__upcoming .event-feature__title {
|
||
font-size: var(--fs-690);
|
||
}
|
||
|
||
.hero__upcoming .event-feature__when {
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
/* Steuerleiste an der unteren Kartenkante: Indikatoren links, Pause rechts.
|
||
Bewusst ohne Pfeile — im Hero zählt jeder Quadratzentimeter, und die
|
||
Indikatoren springen ohnehin direkt zu jedem Termin. */
|
||
.hero__upcoming-controls {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
margin-top: var(--space-s);
|
||
padding-top: var(--space-2xs);
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.hero__upcoming-controls[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
.hero__upcoming-indicators {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--space-2xs);
|
||
}
|
||
|
||
/* Wie die Banner-Indikatoren: 44px Trefferfläche (WCAG 2.5.5), sichtbar ist
|
||
nur die 6px-Leiste in der Mitte. */
|
||
.hero__upcoming-dot {
|
||
position: relative;
|
||
width: 40px;
|
||
height: var(--tap-min);
|
||
padding: 0;
|
||
background: none;
|
||
border: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.hero__upcoming-dot::before,
|
||
.hero__upcoming-progress {
|
||
position: absolute;
|
||
inset: calc(50% - 3px) 0 auto;
|
||
height: 6px;
|
||
border-radius: 999px;
|
||
}
|
||
|
||
.hero__upcoming-dot::before {
|
||
content: "";
|
||
background: var(--clr-glass-border-strong);
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.hero__upcoming-dot:hover::before {
|
||
background: rgb(255 255 255 / 0.45);
|
||
}
|
||
|
||
.hero__upcoming-progress {
|
||
background: var(--clr-accent);
|
||
transform: scaleX(0);
|
||
transform-origin: left;
|
||
}
|
||
|
||
/* Der durchlaufende Fortschritt treibt den Folienwechsel (animationend in
|
||
carousel.js) — dieselbe Keyframe wie beim Banner-Slider, nur schnellerer Takt. */
|
||
.hero__upcoming-dot.is-active .hero__upcoming-progress {
|
||
animation: ad-progress var(--hero-upcoming-interval) linear forwards;
|
||
}
|
||
|
||
.hero__upcoming.is-paused .hero__upcoming-dot.is-active .hero__upcoming-progress {
|
||
animation-play-state: paused;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.hero__upcoming-dot.is-active .hero__upcoming-progress {
|
||
animation: none;
|
||
transform: scaleX(1);
|
||
}
|
||
}
|
||
|
||
/* Glas-Optik als Delta zu .btn--icon (Größe/Rundung/Trefferfläche kommen von
|
||
dort). .btn muss im Selektor mitstehen: utilities.css lädt nach
|
||
components.css, bei gleicher Spezifität gewänne sonst die rote .btn-Fläche. */
|
||
.btn.hero__upcoming-toggle {
|
||
flex: 0 0 auto;
|
||
font-size: var(--icon-s);
|
||
color: var(--clr-text);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.btn.hero__upcoming-toggle:hover {
|
||
background: rgb(255 255 255 / 0.2);
|
||
}
|
||
|
||
.hero__upcoming-toggle[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
/* Pause-Icon im Abspiel-Zustand, Play-Icon wenn pausiert. */
|
||
.hero__upcoming-toggle .hero__upcoming-toggle-icon--play {
|
||
display: none;
|
||
}
|
||
|
||
.hero__upcoming-toggle[aria-pressed="true"] .hero__upcoming-toggle-icon--pause {
|
||
display: none;
|
||
}
|
||
|
||
.hero__upcoming-toggle[aria-pressed="true"] .hero__upcoming-toggle-icon--play {
|
||
display: inline-block;
|
||
}
|
||
|
||
/* Variante: Text mittig zentriert (z. B. Startseite) */
|
||
.hero--center {
|
||
align-items: center;
|
||
text-align: center;
|
||
}
|
||
|
||
.hero--center .hero__text {
|
||
margin-inline: auto;
|
||
}
|
||
|
||
/* letter-spacing schiebt zentrierten Text optisch nach rechts (Spacing nach letztem Char).
|
||
text-indent gleich dem letter-spacing-Wert kompensiert das. */
|
||
.hero--center .hero__tagline {
|
||
text-indent: 0.14em;
|
||
}
|
||
|
||
.hero--center .hero__actions {
|
||
justify-content: center;
|
||
}
|
||
|
||
/* Schmale Viewports: lange Teaser lesen sich zentriert schlecht (beidseitiger
|
||
Flatterrand, 6–7 Zeilen). Unterseiten-Heros laufen mobil deshalb linksbündig
|
||
mit ruhiger Kante und eine Schriftstufe kleiner; das Bild dimmt stärker, weil
|
||
der Text dort einen größeren Teil der Fläche überdeckt. Der Display-Hero der
|
||
Startseite (kurzer Titel + Tagline, kein Langtext) bleibt zentriert und
|
||
behält seine eigenen, fein abgestimmten Verläufe (höhere Spezifität). */
|
||
@media (max-width: 600px) {
|
||
.hero--center:not(.hero--display) {
|
||
text-align: left;
|
||
}
|
||
|
||
.hero--center:not(.hero--display) .hero__text {
|
||
margin-inline: 0;
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
.hero--center:not(.hero--display) .hero__tagline {
|
||
text-indent: 0;
|
||
}
|
||
|
||
.hero--center:not(.hero--display) .hero__actions {
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.hero__media::after {
|
||
background: linear-gradient(180deg, rgb(0 0 0 / 0.55) 0%, rgb(0 0 0 / 0.3) 45%, rgb(0 0 0 / 0.6) 100%);
|
||
}
|
||
}
|
||
|
||
/* Variante: Editorial-Display (Startseite) — linksbündig unten, große Typo */
|
||
.hero--display .hero__media::after {
|
||
/* zusätzlicher Verlauf von links für Textlesbarkeit über dem Video */
|
||
background:
|
||
linear-gradient(90deg, rgb(10 10 10 / 0.62) 0%, rgb(10 10 10 / 0.25) 45%, transparent 70%),
|
||
linear-gradient(180deg, rgb(0 0 0 / 0.42) 0%, rgb(0 0 0 / 0.15) 45%, rgb(0 0 0 / 0.5) 100%);
|
||
}
|
||
|
||
/* Center+Display: kein Seitenverlauf, nur zentrales Abdunkeln für Lesbarkeit */
|
||
.hero--center.hero--display .hero__media::after {
|
||
background:
|
||
radial-gradient(ellipse 80% 60% at 50% 40%, rgb(10 10 10 / 0.5) 0%, transparent 100%),
|
||
linear-gradient(180deg, rgb(0 0 0 / 0.4) 0%, rgb(0 0 0 / 0.14) 45%, rgb(0 0 0 / 0.5) 100%);
|
||
}
|
||
|
||
/* Kicker: einziges gesperrtes Versalien-Element im Hero — bewusst ohne
|
||
Schmuck-Linien, damit die Typo-Hierarchie ruhig bleibt (Eyebrow → Titel →
|
||
Subline → Text). */
|
||
.hero__kicker {
|
||
margin-bottom: var(--space-m);
|
||
font-size: var(--fs-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wider);
|
||
/* Sperrung optisch kompensieren (Spacing nach letztem Zeichen). */
|
||
text-indent: 0.18em;
|
||
color: rgb(255 255 255 / 0.85);
|
||
}
|
||
|
||
.hero--display .hero__title {
|
||
font-size: var(--fs-1000);
|
||
}
|
||
|
||
/* Einzugs-Tempo statt Video-Energie: Kicker → Titel → Text → CTAs blenden
|
||
gestaffelt ein. prefers-reduced-motion kappt das global (reset.css). */
|
||
.hero--display:not(.hero--duo) .hero__content > * {
|
||
animation: hero-content-in 0.6s both ease-out;
|
||
}
|
||
|
||
.hero--display:not(.hero--duo) .hero__content > *:nth-child(1) { animation-delay: 0s; }
|
||
.hero--display:not(.hero--duo) .hero__content > *:nth-child(2) { animation-delay: 0.1s; }
|
||
.hero--display:not(.hero--duo) .hero__content > *:nth-child(3) { animation-delay: 0.2s; }
|
||
.hero--display:not(.hero--duo) .hero__content > *:nth-child(4) { animation-delay: 0.3s; }
|
||
|
||
/* hero--duo: links (Kicker/Titel/CTAs) staffelt sich selbst, die Termin-Karte
|
||
kommt als eigener Block danach. */
|
||
.hero--duo .hero__primary > * {
|
||
animation: hero-content-in 0.6s both ease-out;
|
||
}
|
||
|
||
.hero--duo .hero__primary > *:nth-child(1) { animation-delay: 0s; }
|
||
.hero--duo .hero__primary > *:nth-child(2) { animation-delay: 0.1s; }
|
||
.hero--duo .hero__primary > *:nth-child(3) { animation-delay: 0.2s; }
|
||
|
||
.hero--duo .hero__upcoming {
|
||
animation: hero-content-in 0.6s both ease-out;
|
||
animation-delay: 0.3s;
|
||
}
|
||
|
||
@keyframes hero-content-in {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(14px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
/* Subline ruhig in Abel, Groß-/Kleinschreibung — konkurriert nicht mehr mit
|
||
Kicker (gesperrte Versalien) und Coolvetica-Titel. */
|
||
.hero--display .hero__tagline {
|
||
margin-top: var(--space-xs);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-550);
|
||
font-weight: 400;
|
||
letter-spacing: 0;
|
||
text-indent: 0;
|
||
text-transform: none;
|
||
line-height: 1.35;
|
||
color: rgb(255 255 255 / 0.92);
|
||
}
|
||
|
||
.hero__scroll-cue {
|
||
position: absolute;
|
||
left: 50%;
|
||
bottom: 1.25rem;
|
||
translate: -50% 0;
|
||
display: grid;
|
||
place-items: center;
|
||
width: 44px;
|
||
height: 44px;
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--icon-l);
|
||
transition: color var(--transition);
|
||
animation: scroll-cue-bounce 2.4s ease-in-out infinite;
|
||
}
|
||
|
||
.hero__scroll-cue:hover {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
@keyframes scroll-cue-bounce {
|
||
0%, 100% { transform: translateY(0); }
|
||
50% { transform: translateY(8px); }
|
||
}
|
||
|
||
/* === instagram === */
|
||
/* Sektions-Kopf zentriert (einheitlich mit partners/persons/…) */
|
||
.instagram > .container > h2,
|
||
.instagram > .container > .text-muted {
|
||
text-align: center;
|
||
}
|
||
|
||
.instagram > .container > .text-muted {
|
||
max-width: 60ch;
|
||
margin-inline: auto;
|
||
}
|
||
|
||
.instagram__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: var(--space-s);
|
||
margin-top: var(--space-2xl);
|
||
}
|
||
|
||
/* Mobil: Ein-Reihen-Swiper statt 2er-Grid — volle Bildgröße, natürliche
|
||
Instagram-Geste. Fluchtet mit dem Text-Container; die angeschnittene
|
||
Folgekarte ist die Wisch-Affordance, Scrollbar bleibt darum versteckt. */
|
||
@media (max-width: 600px) {
|
||
.instagram__grid {
|
||
grid-template-columns: none;
|
||
grid-auto-flow: column;
|
||
grid-auto-columns: min(72vw, 320px);
|
||
gap: var(--space-xs);
|
||
overflow-x: auto;
|
||
scroll-snap-type: x mandatory;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.instagram__grid::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.instagram__link {
|
||
scroll-snap-align: start;
|
||
}
|
||
}
|
||
|
||
.instagram__link {
|
||
position: relative;
|
||
display: block;
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.instagram__link img {
|
||
width: 100%;
|
||
height: auto;
|
||
/* Instagram-Portrait 1080x1350 */
|
||
aspect-ratio: 4 / 5;
|
||
object-fit: cover;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.instagram__link:hover img,
|
||
.instagram__link:focus-visible img {
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.instagram__badge {
|
||
position: absolute;
|
||
top: 0.75rem;
|
||
right: 0.75rem;
|
||
display: grid;
|
||
place-items: center;
|
||
width: 2rem;
|
||
height: 2rem;
|
||
background: rgb(0 0 0 / 0.6);
|
||
border-radius: 50%;
|
||
font-size: var(--fs-300);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.instagram__fallback {
|
||
margin-top: var(--space-l);
|
||
padding: var(--space-3xl);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
text-align: center;
|
||
}
|
||
|
||
.instagram__fallback p {
|
||
margin-inline: auto;
|
||
}
|
||
|
||
.instagram__fallback .btn {
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.instagram__more {
|
||
margin-top: var(--space-l);
|
||
text-align: center;
|
||
}
|
||
|
||
/* === split (Bild/Text-Sektion) === */
|
||
.split__inner {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||
align-items: center;
|
||
gap: var(--space-4xl);
|
||
}
|
||
|
||
/* Sanft aufgehelltes, durchgehendes Band über die volle Breite — generisch für
|
||
jede Sektion (Split via tinted: true im JSON, person-grid u.a. per Klasse).
|
||
Direkt aufeinanderfolgende tinted-Sektionen verschmelzen nahtlos. */
|
||
.section--tinted {
|
||
background: linear-gradient(180deg, rgb(255 255 255 / 0.06), rgb(255 255 255 / 0.035));
|
||
border-block: 1px solid rgb(255 255 255 / 0.1);
|
||
}
|
||
|
||
/* Abgesetztes, dunkleres Band über die volle Breite — das Gegenstück zu
|
||
--tinted, wenn eine Sektion sich nach unten statt nach oben abheben soll. */
|
||
.section--sunken {
|
||
background: var(--clr-bg-deep);
|
||
border-block: 1px solid rgb(255 255 255 / 0.08);
|
||
}
|
||
|
||
.section--tinted + .section--tinted {
|
||
border-top: 0;
|
||
}
|
||
|
||
.section--tinted:has(+ .section--tinted) {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
/* Bild/Text-Sektionen im Band: mehr Luft nach oben und unten */
|
||
.split.section--tinted {
|
||
padding-block: clamp(var(--space-6xl), var(--space-5xl) + 3vw, var(--space-7xl));
|
||
}
|
||
|
||
/* Textabschnitt ohne Bild (section.php ohne image/gallery): einspaltig, Text auf
|
||
Lesebreite statt über die halbe Seite gezogen. */
|
||
.split--plain .split__inner {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
}
|
||
|
||
.split--plain .split__text {
|
||
max-width: 60ch;
|
||
}
|
||
|
||
/* Die extra Luft von .split.section--tinted rechnet mit einem Bild daneben.
|
||
Ohne Bild wirkt sie leer, deshalb zurück auf das normale Sektions-Maß. */
|
||
.split--plain.section--tinted {
|
||
padding-block: var(--space-section);
|
||
}
|
||
|
||
.split--flip .split__text {
|
||
order: 2;
|
||
}
|
||
|
||
.split--flip .split__media {
|
||
order: 1;
|
||
}
|
||
|
||
.split__text p {
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.split__cta {
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
.split__img {
|
||
width: 100%;
|
||
border-radius: var(--radius-card);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
/* Polaroid-Variante (polaroid: true im Section-JSON): <figure class="polaroid">
|
||
um das Bild — dicker heller Fotorahmen, unten klassisch breiter, leicht gekippt
|
||
(flip-Sektionen gegenläufig, damit zwei Polaroids nicht parallel hängen),
|
||
Klebestreifen über den oberen Ecken (::before/::after — deshalb der Wrapper,
|
||
auf einem <img> greifen Pseudo-Elemente nicht). Hover richtet das Foto sanft
|
||
auf; der globale reduced-motion-Kill-Switch in reset.css fängt die Transition. */
|
||
.polaroid {
|
||
--polaroid-tilt: -2deg;
|
||
position: relative;
|
||
margin: 0;
|
||
padding: var(--space-s) var(--space-s) var(--space-3xl);
|
||
background: var(--clr-photo-frame);
|
||
border-radius: var(--radius-photo);
|
||
box-shadow: var(--shadow-card);
|
||
transform: rotate(var(--polaroid-tilt));
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.split--flip .polaroid {
|
||
--polaroid-tilt: 2deg;
|
||
}
|
||
|
||
.polaroid:hover {
|
||
transform: rotate(0deg) scale(1.02);
|
||
}
|
||
|
||
.polaroid::before,
|
||
.polaroid::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: calc(-1 * var(--space-2xs));
|
||
width: var(--space-6xl);
|
||
height: var(--space-xl);
|
||
background: var(--clr-tape);
|
||
box-shadow: var(--shadow);
|
||
}
|
||
|
||
.polaroid::before {
|
||
left: calc(-1 * var(--space-xs));
|
||
transform: rotate(-45deg);
|
||
}
|
||
|
||
.polaroid::after {
|
||
right: calc(-1 * var(--space-xs));
|
||
transform: rotate(45deg);
|
||
}
|
||
|
||
/* Rundung/Schatten trägt im Polaroid der Rahmen, nicht das Foto */
|
||
.polaroid .split__img {
|
||
display: block;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* Bildunterschrift auf dem weißen Unterrand (optionales caption-Feld am
|
||
Bild) — bewusst nüchtern in Abel, wie die Timeline-Captions auf /historie
|
||
(Handschrift-Variante war Felix zu verspielt, 30.07.2026). Die Unterschrift
|
||
füllt das „Kinn" des Polaroids, deshalb schrumpft der Rahmen-Unterrand,
|
||
wenn sie da ist. */
|
||
.polaroid__caption {
|
||
margin-top: var(--space-xs);
|
||
font-size: var(--fs-400);
|
||
text-align: center;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-sepia-text);
|
||
}
|
||
|
||
.polaroid:has(.polaroid__caption) {
|
||
padding-bottom: var(--space-s);
|
||
}
|
||
|
||
/* Schmale Viewports: Klebestreifen proportional zum kleineren Rahmen verkleinern,
|
||
sonst dominieren sie das Foto (am Desktop ~14 % der Rahmenbreite, mobil sonst ~24 %) */
|
||
@media (max-width: 600px) {
|
||
.polaroid::before,
|
||
.polaroid::after {
|
||
width: var(--space-5xl);
|
||
height: var(--space-l);
|
||
}
|
||
}
|
||
|
||
/* Galerie-Variante der Bild/Text-Sektion (gallery: [] im JSON): erstes Bild
|
||
volle Breite, weitere halbbreit darunter — kleine Collage. Polaroids kippen
|
||
darin abwechselnd und dezenter als das Einzel-Polaroid, Rahmen und
|
||
Klebestreifen schrumpfen mit den kleineren Fotos mit. */
|
||
.split__gallery {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: var(--space-l);
|
||
align-items: start;
|
||
}
|
||
|
||
.split__gallery > :first-child {
|
||
grid-column: 1 / -1;
|
||
}
|
||
|
||
.split__gallery .polaroid {
|
||
--polaroid-tilt: -1.5deg;
|
||
padding: var(--space-xs) var(--space-xs) var(--space-l);
|
||
}
|
||
|
||
.split__gallery .polaroid:nth-child(even) {
|
||
--polaroid-tilt: 1.5deg;
|
||
}
|
||
|
||
.split__gallery .polaroid::before,
|
||
.split__gallery .polaroid::after {
|
||
width: var(--space-5xl);
|
||
height: var(--space-l);
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.split__inner {
|
||
grid-template-columns: 1fr;
|
||
gap: var(--space-2xl);
|
||
}
|
||
|
||
.split--flip .split__text {
|
||
order: 0;
|
||
}
|
||
|
||
.split--flip .split__media {
|
||
order: 0;
|
||
}
|
||
}
|
||
|
||
/* === timeline (Vereinshistorie, Museums-Look) === */
|
||
.timeline-intro {
|
||
max-width: 60ch;
|
||
margin-inline: auto;
|
||
padding-block: var(--space-3xl) 0;
|
||
text-align: center;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Sprung-Navigation: Jahres-Pills, klebt beim Scrollen oben */
|
||
.timeline-nav {
|
||
position: sticky;
|
||
top: 1rem;
|
||
z-index: 5;
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-bottom: var(--space-4xl);
|
||
}
|
||
|
||
.timeline-nav__list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: var(--space-2xs);
|
||
padding: var(--space-2xs);
|
||
background: rgb(10 10 10 / 0.82);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: 999px;
|
||
backdrop-filter: blur(6px);
|
||
}
|
||
|
||
.timeline-nav a {
|
||
display: inline-block;
|
||
padding: 0.25em 0.85em;
|
||
font-family: var(--ff-heading);
|
||
/* Coolvetica unter ~28px wird unleserlich (Projekt-Faustregel) */
|
||
font-size: var(--fs-650);
|
||
letter-spacing: var(--ls-caps);
|
||
text-transform: uppercase;
|
||
text-decoration: none;
|
||
color: var(--clr-text-muted);
|
||
border-radius: 999px;
|
||
transition: color var(--transition), background var(--transition);
|
||
}
|
||
|
||
.timeline-nav a:hover {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.timeline-nav a.is-active {
|
||
color: var(--clr-text);
|
||
background: var(--clr-accent);
|
||
}
|
||
|
||
/* Mittellinie + Stationen */
|
||
.timeline {
|
||
position: relative;
|
||
display: grid;
|
||
gap: var(--space-6xl);
|
||
padding-block: var(--space-s) var(--space-2xl);
|
||
}
|
||
|
||
.timeline::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
bottom: 0;
|
||
left: 50%;
|
||
width: 2px;
|
||
transform: translateX(-50%);
|
||
background: linear-gradient(180deg,
|
||
transparent,
|
||
rgb(var(--clr-accent-rgb) / 0.55) 6%,
|
||
rgb(var(--clr-accent-rgb) / 0.55) 94%,
|
||
transparent);
|
||
}
|
||
|
||
.timeline__event {
|
||
position: relative;
|
||
width: calc(50% - 3.5rem);
|
||
}
|
||
|
||
.timeline__event:nth-child(even) {
|
||
margin-left: auto;
|
||
}
|
||
|
||
.timeline__marker {
|
||
position: absolute;
|
||
top: 0.6rem;
|
||
left: calc(100% + 3.5rem);
|
||
width: 16px;
|
||
height: 16px;
|
||
transform: translateX(-50%);
|
||
background: var(--clr-accent);
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 0 4px rgb(var(--clr-accent-rgb) / 0.25), 0 0 18px rgb(var(--clr-accent-rgb) / 0.5);
|
||
}
|
||
|
||
.timeline__event:nth-child(even) .timeline__marker {
|
||
left: -3.5rem;
|
||
}
|
||
|
||
.timeline__year {
|
||
font-size: var(--fs-900);
|
||
color: var(--clr-text);
|
||
text-shadow: 0 2px 18px rgb(0 0 0 / 0.5);
|
||
margin-bottom: var(--space-s);
|
||
}
|
||
|
||
.timeline__event:nth-child(odd) .timeline__year {
|
||
text-align: right;
|
||
}
|
||
|
||
/* Museums-Rahmen: Foto wie ein aufgezogener Print, leicht gedreht */
|
||
.timeline__figure {
|
||
margin: 0;
|
||
padding: var(--space-s) var(--space-s) var(--space-s);
|
||
background: var(--clr-sepia-bg);
|
||
border-radius: 3px;
|
||
box-shadow: 0 12px 35px rgb(0 0 0 / 0.5);
|
||
transform: rotate(-1.2deg);
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.timeline__event:nth-child(even) .timeline__figure {
|
||
transform: rotate(1.2deg);
|
||
}
|
||
|
||
.timeline__event:hover .timeline__figure {
|
||
transform: rotate(0deg) scale(1.02);
|
||
}
|
||
|
||
.timeline__figure img {
|
||
width: 100%;
|
||
height: auto;
|
||
display: block;
|
||
}
|
||
|
||
.timeline__text {
|
||
padding-top: var(--space-s);
|
||
font-size: var(--fs-500);
|
||
line-height: 1.45;
|
||
color: var(--clr-sepia-text);
|
||
}
|
||
|
||
/* Station ohne Foto: dieselbe Sepia-Fläche, nur ohne den Abstand zum Bild darüber */
|
||
.timeline__figure--note .timeline__text {
|
||
padding-top: 0;
|
||
}
|
||
|
||
/* Scroll-Reveal nur mit JS (js-reveal), sonst alles sichtbar */
|
||
.js-reveal .timeline__event {
|
||
opacity: 0;
|
||
translate: 0 28px;
|
||
transition: opacity 600ms ease, translate 600ms ease;
|
||
}
|
||
|
||
.js-reveal .timeline__event.is-visible {
|
||
opacity: 1;
|
||
translate: 0 0;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.timeline::before {
|
||
left: 8px;
|
||
}
|
||
|
||
.timeline__event,
|
||
.timeline__event:nth-child(even) {
|
||
width: auto;
|
||
margin-left: 0;
|
||
padding-left: var(--space-3xl);
|
||
}
|
||
|
||
.timeline__marker,
|
||
.timeline__event:nth-child(even) .timeline__marker {
|
||
left: 8px;
|
||
}
|
||
|
||
.timeline__event:nth-child(odd) .timeline__year {
|
||
text-align: left;
|
||
}
|
||
}
|
||
|
||
/* === partners === */
|
||
.partners > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.partners > .container > p {
|
||
max-width: 60ch;
|
||
margin-inline: auto;
|
||
}
|
||
|
||
/* Intro-Abstand zur Überschrift einheitlich mit den anderen Sektionen */
|
||
.partners__intro {
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.partners__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(180px, 220px));
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: var(--space-2xl);
|
||
margin-block: var(--space-3xl);
|
||
}
|
||
|
||
.partners__item {
|
||
display: grid;
|
||
place-items: center;
|
||
padding: var(--space-l);
|
||
/* Weiße Karte — die Logos sind die farbigen Originale der Partner,
|
||
ein neutraler weißer Grund trägt jede Markenfarbe */
|
||
background: var(--clr-logo-card);
|
||
border-radius: var(--radius-card);
|
||
min-height: 120px;
|
||
}
|
||
|
||
.partners__item img {
|
||
max-height: 80px;
|
||
width: auto;
|
||
max-width: 100%;
|
||
object-fit: contain;
|
||
}
|
||
|
||
/* === sponsor-arguments === */
|
||
.sponsor-arguments__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: var(--space-3xl) var(--space-4xl);
|
||
list-style: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.sponsor-arguments__item {
|
||
display: flex;
|
||
gap: var(--space-m);
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.sponsor-arguments__icon {
|
||
flex-shrink: 0;
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
background: var(--clr-accent);
|
||
border-radius: 8px;
|
||
display: grid;
|
||
place-items: center;
|
||
font-size: var(--icon-s);
|
||
color: #fff;
|
||
margin-top: var(--space-3xs);
|
||
}
|
||
|
||
.sponsor-arguments__title {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-675);
|
||
text-transform: uppercase;
|
||
line-height: var(--lh-heading);
|
||
margin: 0 0 var(--space-2xs);
|
||
}
|
||
|
||
.sponsor-arguments__text {
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-500);
|
||
line-height: var(--lh-body);
|
||
margin: 0;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.sponsor-arguments__grid {
|
||
grid-template-columns: 1fr;
|
||
gap: var(--space-2xl);
|
||
}
|
||
}
|
||
|
||
/* === sponsor-channels === */
|
||
.sponsor-channels > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.sponsor-channels__intro {
|
||
max-width: 65ch;
|
||
margin-inline: auto;
|
||
margin-top: var(--space-s);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.sponsor-channels__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: var(--space-l);
|
||
margin-top: var(--space-3xl);
|
||
list-style: none;
|
||
padding: 0;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.sponsor-channels__card {
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-top: 3px solid var(--clr-accent);
|
||
border-radius: var(--radius-card);
|
||
padding: var(--space-2xl);
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0;
|
||
text-align: left;
|
||
transition: background var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
.sponsor-channels__card:hover {
|
||
background: rgb(255 255 255 / 0.07);
|
||
box-shadow: 0 8px 32px rgb(0 0 0 / 0.35);
|
||
}
|
||
|
||
.sponsor-channels__head {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 0;
|
||
}
|
||
|
||
.sponsor-channels__kicker {
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-300);
|
||
font-weight: var(--fw-bold);
|
||
letter-spacing: var(--ls-wide);
|
||
text-transform: uppercase;
|
||
color: var(--clr-text-muted);
|
||
margin: 0 0 var(--space-s);
|
||
}
|
||
|
||
.sponsor-channels__icon {
|
||
width: 2.75rem;
|
||
height: 2.75rem;
|
||
background: var(--clr-accent);
|
||
border-radius: 8px;
|
||
display: grid;
|
||
place-items: center;
|
||
font-size: var(--icon-m);
|
||
color: #fff;
|
||
margin-bottom: var(--space-m);
|
||
}
|
||
|
||
.sponsor-channels__title {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-700);
|
||
text-transform: uppercase;
|
||
line-height: var(--lh-heading);
|
||
margin: 0 0 var(--space-xs);
|
||
}
|
||
|
||
.sponsor-channels__desc {
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-500);
|
||
line-height: var(--lh-body);
|
||
margin: 0;
|
||
}
|
||
|
||
.sponsor-channels__divider {
|
||
border: none;
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
margin: var(--space-l) 0;
|
||
}
|
||
|
||
.sponsor-channels__features {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-xs);
|
||
flex: 1;
|
||
}
|
||
|
||
.sponsor-channels__features li {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-xs);
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.sponsor-channels__check {
|
||
flex-shrink: 0;
|
||
font-size: var(--icon-s);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.sponsor-channels__grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.sponsor-channels__title {
|
||
font-size: var(--fs-700);
|
||
}
|
||
}
|
||
|
||
@media (min-width: 769px) and (max-width: 992px) {
|
||
.sponsor-channels__grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
|
||
.sponsor-channels__card:last-child:nth-child(odd) {
|
||
grid-column: 1 / -1;
|
||
max-width: 50%;
|
||
margin-inline: auto;
|
||
}
|
||
}
|
||
|
||
/* === persons (Ansprechpartner-Visitenkarten) === */
|
||
.persons > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.persons__intro {
|
||
margin-inline: auto;
|
||
margin-top: var(--space-s);
|
||
max-width: 60ch;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Kurze Hairline unter der Bereichs-Überschrift */
|
||
.persons h2::after {
|
||
content: "";
|
||
display: block;
|
||
width: 2.5rem;
|
||
height: 2px;
|
||
margin: var(--space-2xs) auto 0;
|
||
background: var(--clr-accent);
|
||
}
|
||
|
||
/* Flexbox statt Grid — bewusst: Grid zentriert mit auto-fit + justify-content
|
||
nur den GESAMTEN Spaltensatz als Block. Bei mehreren vollen Reihen plus einem
|
||
Rest (z. B. 5 Karten = 4 + 1) landet der letzte Einzeleintrag trotzdem unter
|
||
Spalte 1 des (zwar zentrierten) Rasters — optisch wieder linksbündig. Flexbox
|
||
mit flex-wrap zentriert dagegen JEDE Zeile für sich, auch die letzte
|
||
unvollständige. flex-grow:0 verhindert, dass eine fast leere letzte Zeile die
|
||
Karte(n) darin künstlich breitzieht. */
|
||
.persons__grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: var(--space-m);
|
||
margin-top: var(--space-3xl);
|
||
text-align: left;
|
||
}
|
||
|
||
/* Höchstens drei Karten je Reihe (vorher fünf à 200px): bei fünf Ansprech-
|
||
partnern stehen jetzt 3 + 2 statt einer gequetschten Fünferreihe. Erst ab
|
||
dieser Breite passen Text und Kontakt-Knöpfe nebeneinander in die Karte
|
||
(siehe .person-card__body) — in 200px-Karten rutschten die Knöpfe wieder
|
||
unter den Text und die gesparte Zeile war zurück.
|
||
flex-basis über calc() statt fester px-Breite: reagiert auf die Container-
|
||
breite statt auf eine angenommene Kartengröße. */
|
||
.persons__grid > li {
|
||
flex: 0 1 calc((100% - 2 * var(--space-m)) / 3);
|
||
min-width: 0;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.persons__grid > li {
|
||
flex-basis: calc((100% - var(--space-m)) / 2);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 500px) {
|
||
.persons__grid > li {
|
||
flex-basis: 100%;
|
||
}
|
||
}
|
||
|
||
/* Untergruppierte Ansicht (group_by_department): Zwischenüberschrift je
|
||
Abteilung, Karten darunter linksbündig — eine einzelne Karte wirkt so
|
||
verortet statt verwaist zentriert. */
|
||
.persons__group-title {
|
||
margin-top: var(--space-2xl);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-500);
|
||
text-align: left;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.persons__grid--grouped {
|
||
justify-content: flex-start;
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.person-card {
|
||
display: grid;
|
||
height: 100%;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
transition: transform var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
.person-card:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.person-card__media img {
|
||
width: 100%;
|
||
height: auto;
|
||
aspect-ratio: 4 / 5;
|
||
object-fit: cover;
|
||
/* Ausschnitt nach oben → Gesicht bleibt im Bild, nicht der Bauch */
|
||
object-position: center top;
|
||
}
|
||
|
||
/* Ohne Foto: Kreis-Avatar mit Initialen statt grauer Silhouette — liest sich
|
||
sofort als „Profilbild folgt". Gleiche Seitenverhältnisse wie das Foto,
|
||
damit die Kartenreihe bündig bleibt. */
|
||
.person-card__media--initials {
|
||
display: grid;
|
||
place-items: center;
|
||
aspect-ratio: 4 / 5;
|
||
background: var(--clr-bg-deep);
|
||
}
|
||
|
||
.person-card__initials {
|
||
display: grid;
|
||
place-items: center;
|
||
width: var(--space-6xl);
|
||
height: var(--space-6xl);
|
||
border-radius: 50%;
|
||
border: 1px solid var(--clr-glass-border-strong);
|
||
background: var(--clr-glass-bg);
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
line-height: 1;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Links der Text, rechts die Kontakt-Knöpfe auf einer Höhe damit (statt
|
||
darunter) — spart pro Karte eine ganze Button-Zeile Scrollstrecke.
|
||
Flex mit wrap statt zwei Grid-Spalten: bleibt für die Textspalte weniger als
|
||
ihre flex-basis übrig (schmale 200px-Karten im Ansprechpartner-Grid), rutschen
|
||
die Knöpfe von selbst wieder unter den Text, statt Name und Nummer auf je
|
||
zwei Zeilen zu quetschen. Kein Breakpoint nötig, die Kartenbreite entscheidet. */
|
||
.person-card__body {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-2xs) var(--space-s);
|
||
padding: var(--space-s) var(--space-m) var(--space-m);
|
||
/* Verlauf knüpft an die dunklen Studiofotos an */
|
||
background: linear-gradient(180deg, rgb(20 20 20 / 0) 0%, rgb(20 20 20 / 0.35) 100%);
|
||
}
|
||
|
||
.person-card__text {
|
||
display: grid;
|
||
gap: var(--space-3xs);
|
||
/* 9rem = Umbruchschwelle: darunter passen Text und Knöpfe nicht mehr
|
||
nebeneinander. min-width: 0, damit lange Mailadressen umbrechen statt
|
||
die Knöpfe aus der Karte zu drücken. */
|
||
flex: 1 1 9rem;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Kompaktere Kacheln im Ansprechpartner-Grid (faq-contact bleibt unberührt):
|
||
flacherer Foto-Beschnitt = deutlich weniger Scrollstrecke, object-position
|
||
top hält die Gesichter im Bild. */
|
||
.persons .person-card__media img,
|
||
.persons .person-card__media--initials {
|
||
aspect-ratio: 4 / 3;
|
||
}
|
||
|
||
.persons .person-card__body {
|
||
padding: var(--space-xs) var(--space-s) var(--space-s);
|
||
}
|
||
|
||
/* Hierarchie: der NAME ist die Karten-Überschrift, die Abteilung nur eine
|
||
Kicker-Zeile darüber (kleine Versalien mit rotem Akzent-Balken — Rot als
|
||
Textfarbe wäre auf dunklem Grund zu kontrastarm, siehe Design-Regeln). */
|
||
/* Dezente einzeilige Kicker-Zeile — fs-300 ist die kleinste Token-Stufe,
|
||
„kleiner" kommt aus engerer Sperrung. Lange Abteilungsnamen („Step Aerobic,
|
||
Fitness & Pilates") werden mit … gekürzt statt umzubrechen. */
|
||
.person-card__department {
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-faint);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
/* Klassenbasiert statt über das Element: der Name rendert je Kontext als
|
||
h3 oder h4, soll aber immer gleich aussehen (h4–h6 laufen in base.css
|
||
sonst über Abel). --fs-650 = Coolvetica-Minimum, nie kleiner. */
|
||
.person-card__name {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
line-height: var(--lh-heading);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-tight);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.person-card__detail {
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
overflow-wrap: anywhere;
|
||
}
|
||
|
||
/* Kontaktwege als echte Buttons statt Textlinks. Größe, Rundung und die 44px
|
||
kommen komplett aus der globalen .btn--icon-Regel (utilities.css) — hier
|
||
bewusst KEIN Größen-Delta mehr: die frühere lokale Verkleinerung
|
||
(min-width: 0 + em-Padding + btn--sm) ließ die Knöpfe als flache Pillen
|
||
erscheinen und wich als einzige Stelle im Projekt vom Icon-Button-Standard ab. */
|
||
.person-card__actions {
|
||
display: flex;
|
||
flex: 0 0 auto;
|
||
gap: var(--space-2xs);
|
||
}
|
||
|
||
.person-card__contact span {
|
||
overflow-wrap: anywhere;
|
||
}
|
||
|
||
.person-card__contact:hover {
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.25em;
|
||
}
|
||
|
||
/* === mitmachen / positions (Ehrenamt-Stellen) === */
|
||
/* Filter-Stripe (Alle/Fußball/Turnen) — Unterstrich-Tabbar wie group-tabs */
|
||
.filter-bar {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
margin-bottom: var(--space-2xl);
|
||
}
|
||
|
||
/* Bereichs-Tabs auf /ansprechpartner: zentriert in der Sektion, direkt über
|
||
den Karten (die Leiste startet hidden, persons-tabs.js blendet sie ein).
|
||
Schmales Tab-Padding: die Unterstreichung (inset-Shadow über die volle
|
||
Tab-Breite) soll am Text kleben, nicht 20px davor beginnen — bei der
|
||
zentrierten Zweier-Leiste wirkt das Standard-Padding wie ein Versatz. */
|
||
.persons-tabs__bar {
|
||
justify-content: center;
|
||
gap: var(--space-l);
|
||
}
|
||
|
||
.persons-tabs__bar .tab {
|
||
padding-inline: var(--space-2xs);
|
||
}
|
||
|
||
/* Ohne JS stehen beide Panels untereinander — Luft zwischen ihnen. */
|
||
.persons--embedded + .persons--embedded {
|
||
margin-top: var(--space-4xl);
|
||
}
|
||
|
||
/* Gemeinsame Tab-Basis für Filter-Leiste und Gruppen-Tabs. Beide waren bis auf
|
||
flex-shrink und 4px Innenabstand zeichengleich implementiert. Aktiv-Zustand
|
||
kommt je nach Muster über aria-pressed (Filter) oder aria-selected (Tabs).
|
||
Fokus liegt inset, damit der Ring nicht in den Nachbar-Tab ragt. */
|
||
.tab {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
min-height: var(--tap-min);
|
||
padding: var(--space-xs) var(--space-m);
|
||
background: none;
|
||
border: none;
|
||
box-shadow: inset 0 -2px 0 var(--clr-glass-border);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text-muted);
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
transition: color var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
.tab:hover {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.tab[aria-pressed="true"],
|
||
.tab[aria-selected="true"] {
|
||
color: var(--clr-text);
|
||
box-shadow: inset 0 -2px 0 var(--clr-accent);
|
||
}
|
||
|
||
.tab:focus-visible {
|
||
outline: var(--focus-ring);
|
||
outline-offset: -2px;
|
||
}
|
||
|
||
/* Erster Filter bündig zur Textkante links. */
|
||
.filter-bar__tab:first-child {
|
||
padding-inline-start: 0;
|
||
}
|
||
|
||
.positions__list {
|
||
display: grid;
|
||
gap: var(--space-l);
|
||
}
|
||
|
||
.positions__empty {
|
||
margin: 0;
|
||
padding: var(--space-3xl) 0;
|
||
max-width: 60ch;
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-500);
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
.positions__empty[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
.positions__empty a {
|
||
color: var(--clr-text);
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.2em;
|
||
}
|
||
|
||
/* "Warum mithelfen?": geteiltes sponsor-arguments-Raster, hier mit Überschrift
|
||
(nur auf dieser Seite — Partner-/Sportheim-Variante bleibt ohne Heading unberührt) */
|
||
#warum-mithelfen > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
#warum-mithelfen .sponsor-arguments__intro {
|
||
max-width: 60ch;
|
||
margin: var(--space-s) auto 0;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
#warum-mithelfen .sponsor-arguments__grid {
|
||
margin-top: var(--space-3xl);
|
||
text-align: left;
|
||
}
|
||
|
||
/* Aufklappbare Stelle (details/summary, FAQ-Mechanik): eingeklappt nur Bereich + Titel */
|
||
.position-card {
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
transition: border-color var(--transition);
|
||
}
|
||
|
||
.position-card[open] {
|
||
border-color: var(--clr-glass-border-strong, var(--clr-glass-border));
|
||
}
|
||
|
||
/* display:grid o. Ä. würde sonst [hidden]{display:none} überschreiben — explizit halten */
|
||
.position-card[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
/* Klickzeile: Icon + Bereich/Titel + Chevron */
|
||
.position-card__head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-s);
|
||
padding: var(--space-m) var(--space-l);
|
||
cursor: pointer;
|
||
list-style: none;
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.position-card__head::-webkit-details-marker {
|
||
display: none;
|
||
}
|
||
|
||
.position-card__head:hover {
|
||
background: rgb(255 255 255 / 0.04);
|
||
}
|
||
|
||
.position-card__head:focus-visible {
|
||
outline: var(--focus-ring);
|
||
outline-offset: -2px;
|
||
}
|
||
|
||
.position-card__icon {
|
||
flex-shrink: 0;
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
background: var(--clr-accent);
|
||
border-radius: 8px;
|
||
display: grid;
|
||
place-items: center;
|
||
font-size: var(--icon-m);
|
||
color: #fff;
|
||
}
|
||
|
||
.position-card__heading {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.position-card__area {
|
||
display: block;
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-300);
|
||
font-weight: var(--fw-bold);
|
||
letter-spacing: var(--ls-wide);
|
||
text-transform: uppercase;
|
||
color: var(--clr-text-muted);
|
||
margin: 0 0 var(--space-3xs);
|
||
}
|
||
|
||
.position-card__title {
|
||
font-family: var(--ff-heading);
|
||
/* --fs-675 (34px) ist laut tokens.css für die Header-Wortmarke gedacht, nicht für
|
||
Karten-Titel — --fs-690 ("Karten-/Kachel-Überschrift") ist der richtige Token,
|
||
dasselbe Muster wie .team-card__name/.group-card__name/.bento__heading. */
|
||
font-size: var(--fs-690);
|
||
text-transform: uppercase;
|
||
line-height: var(--lh-heading);
|
||
margin: 0;
|
||
}
|
||
|
||
.position-card__chevron {
|
||
flex: none;
|
||
font-size: var(--icon-s);
|
||
color: var(--clr-text-muted);
|
||
transition: transform var(--transition), color var(--transition);
|
||
}
|
||
|
||
.position-card[open] .position-card__chevron {
|
||
transform: rotate(180deg);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* Aufgeklappter Inhalt: Detailspalte + Ansprechpartner */
|
||
.position-card__body {
|
||
display: grid;
|
||
grid-template-columns: 1fr minmax(0, 16rem);
|
||
gap: var(--space-3xl);
|
||
align-items: start;
|
||
padding: 0 var(--space-l) var(--space-l) var(--space-6xl);
|
||
animation: faq-reveal 260ms ease;
|
||
}
|
||
|
||
.position-card__main > :first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.position-card__summary {
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-500);
|
||
line-height: var(--lh-body);
|
||
margin: 0;
|
||
}
|
||
|
||
.position-card__tasks {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: var(--space-m) 0 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-xs);
|
||
}
|
||
|
||
.position-card__tasks li {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--space-xs);
|
||
font-size: var(--fs-500);
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
.position-card__check {
|
||
flex-shrink: 0;
|
||
margin-top: 0.2em;
|
||
font-size: var(--icon-s);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.position-card__meta {
|
||
margin: var(--space-m) 0 0;
|
||
display: grid;
|
||
gap: var(--space-xs);
|
||
}
|
||
|
||
.position-card__meta-row {
|
||
display: grid;
|
||
gap: var(--space-3xs);
|
||
}
|
||
|
||
.position-card__meta dt {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
font-size: var(--fs-300);
|
||
font-weight: var(--fw-bold);
|
||
letter-spacing: var(--ls-caps);
|
||
text-transform: uppercase;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.position-card__meta-icon {
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.position-card__meta dd {
|
||
margin: 0;
|
||
font-size: var(--fs-500);
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
/* Kompakte Kontakt-Karte: flacheres Foto als im Ansprechpartner-Grid */
|
||
.position-card__contact .person-card__media img {
|
||
aspect-ratio: 4 / 3.4;
|
||
}
|
||
|
||
/* .person-card__department läuft hier als <h4> (level-Prop, siehe person-card.php) statt
|
||
als <h3> wie im Ansprechpartner-Grid — base.css gibt h4 damit Abel statt Coolvetica,
|
||
bei gleicher Schriftgröße (--fs-650) liest sich das als fette Blocktype statt kompakter
|
||
Headline. In der schmalen 16rem-Sidebar wirkt das riesig UND (mangels color-Override)
|
||
heller als der gedämpfte Name darunter. Gleiche kleine Kicker-Optik wie .position-card__area
|
||
direkt nebenan — der Name bleibt die eigentliche Aussage. */
|
||
.position-card__contact .person-card__department {
|
||
font-size: var(--fs-300);
|
||
font-weight: var(--fw-bold);
|
||
letter-spacing: var(--ls-wide);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.position-card__body {
|
||
grid-template-columns: 1fr;
|
||
gap: var(--space-l);
|
||
padding: 0 var(--space-l) var(--space-l);
|
||
}
|
||
|
||
.position-card__contact {
|
||
max-width: 18rem;
|
||
}
|
||
}
|
||
|
||
/* === contact form === */
|
||
.contact__inner {
|
||
display: grid;
|
||
gap: var(--space-3xl);
|
||
justify-items: center;
|
||
}
|
||
|
||
.contact__intro {
|
||
max-width: 60ch;
|
||
text-align: center;
|
||
}
|
||
|
||
.contact__intro p {
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.form {
|
||
display: grid;
|
||
gap: var(--space-m);
|
||
width: 100%;
|
||
max-width: 720px;
|
||
padding: var(--space-2xl);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.form__row {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||
gap: var(--space-m);
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.form__row {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* Floating Label: Label liegt im leeren Feld wie ein Platzhalter und wandert bei
|
||
Fokus oder Inhalt nach oben. Rein visuell — das echte <label for> bleibt im DOM
|
||
(kein Placeholder-als-Label), darum auch für Screenreader voll nutzbar. */
|
||
.form__field {
|
||
position: relative;
|
||
}
|
||
|
||
.form__field input,
|
||
.form__field select,
|
||
.form__field textarea {
|
||
width: 100%;
|
||
padding: var(--space-l) var(--space-xs) var(--space-2xs);
|
||
background: var(--clr-field-bg);
|
||
border: 1px solid var(--clr-field-border);
|
||
border-radius: var(--radius-btn);
|
||
color: var(--clr-text);
|
||
transition: border-color var(--transition), background var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
/* Label im Ruhezustand — sitzt auf der Eingabezeile (deckt das " "-Placeholder) */
|
||
.form__field:not(.form__field--checkbox) > label {
|
||
position: absolute;
|
||
left: 0.9rem;
|
||
top: 1.4rem;
|
||
font-size: var(--fs-500);
|
||
line-height: 1;
|
||
color: var(--clr-text-muted);
|
||
pointer-events: none;
|
||
transform-origin: left top;
|
||
transition: transform var(--transition), color var(--transition);
|
||
}
|
||
|
||
/* Geschwebt: bei Fokus, bei vorhandenem Inhalt (placeholder=" "-Trick) sowie
|
||
dauerhaft für Select und Datumsfelder (haben keinen :placeholder-shown-Zustand). */
|
||
.form__field:not(.form__field--checkbox):has(input:focus) > label,
|
||
.form__field:not(.form__field--checkbox):has(textarea:focus) > label,
|
||
.form__field:not(.form__field--checkbox):has(select:focus) > label,
|
||
.form__field:not(.form__field--checkbox):has(input:not(:placeholder-shown)) > label,
|
||
.form__field:not(.form__field--checkbox):has(textarea:not(:placeholder-shown)) > label,
|
||
.form__field:not(.form__field--checkbox):has(select) > label,
|
||
.form__field:not(.form__field--checkbox):has(input[type="date"]) > label {
|
||
transform: translateY(-0.95rem) scale(0.78);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.form__field input:hover,
|
||
.form__field select:hover,
|
||
.form__field textarea:hover {
|
||
border-color: var(--clr-field-border-hover);
|
||
}
|
||
|
||
/* Roter Rahmen + Glow als Marken-Signal, dazu der weiße Standard-Ring.
|
||
Der rote Rahmen allein reichte nicht: Akzentrot kommt auf der Feldfläche nur
|
||
auf 2,8:1 und verfehlt damit WCAG 1.4.11 (>=3:1) für Fokus-Indikatoren.
|
||
outline-offset entspricht der Glow-Breite, beide Ringe liegen also bündig
|
||
nebeneinander statt übereinander. */
|
||
.form__field input:focus-visible,
|
||
.form__field select:focus-visible,
|
||
.form__field textarea:focus-visible {
|
||
outline: var(--focus-ring);
|
||
outline-offset: var(--focus-offset);
|
||
border-color: var(--clr-accent);
|
||
box-shadow: 0 0 0 3px rgb(var(--clr-accent-rgb) / 0.3);
|
||
}
|
||
|
||
/* Der " "-Platzhalter aus dem Floating-Trick bleibt unsichtbar */
|
||
.form__field ::placeholder {
|
||
color: transparent;
|
||
}
|
||
|
||
/* Autofill (Chrome/Safari/Edge füllen Telefon/E-Mail gern automatisch): der
|
||
Browser rendert das Feld sonst mit einer eigenen hellen Füllfläche + schwarzer
|
||
Schrift, die sich weder per background noch per color übersteuern lässt.
|
||
Einzig wirksamer Hebel ist ein deckender box-shadow inset (deshalb die OPAKE
|
||
--clr-field-bg-solid statt der sonst überall genutzten transparenten
|
||
--clr-field-bg) + -webkit-text-fill-color für die Schriftfarbe. Die absurd
|
||
lange transition verhindert, dass Chrome die native Füllung beim periodischen
|
||
Neuanwenden kurz aufblitzen lässt. :autofill (ohne Präfix) deckt Firefox ab. */
|
||
.form__field input:-webkit-autofill,
|
||
.form__field input:-webkit-autofill:hover,
|
||
.form__field input:-webkit-autofill:focus,
|
||
.form__field select:-webkit-autofill,
|
||
.form__field textarea:-webkit-autofill,
|
||
.form__field input:autofill,
|
||
.form__field select:autofill,
|
||
.form__field textarea:autofill {
|
||
-webkit-text-fill-color: var(--clr-text);
|
||
caret-color: var(--clr-text);
|
||
-webkit-box-shadow: 0 0 0 1000px var(--clr-field-bg-solid) inset;
|
||
box-shadow: 0 0 0 1000px var(--clr-field-bg-solid) inset;
|
||
transition: background-color 600000s ease-in-out 0s, box-shadow 600000s ease-in-out 0s;
|
||
}
|
||
|
||
/* Fehlerzustand: per JS (aria-invalid) und nativ nach Nutzereingabe (:user-invalid) */
|
||
.form__field input[aria-invalid="true"],
|
||
.form__field select[aria-invalid="true"],
|
||
.form__field textarea[aria-invalid="true"],
|
||
.form__field input:user-invalid,
|
||
.form__field select:user-invalid,
|
||
.form__field textarea:user-invalid {
|
||
border-color: var(--clr-error);
|
||
}
|
||
|
||
.form__field select {
|
||
appearance: none;
|
||
padding-right: var(--space-3xl);
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='9' fill='none'%3E%3Cpath d='M1 1l6 6 6-6' stroke='%23ffffff' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");
|
||
background-repeat: no-repeat;
|
||
background-position: right 0.9rem center;
|
||
}
|
||
|
||
.form__field select option {
|
||
background: var(--clr-bg);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.form__required {
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.form__hint {
|
||
margin-top: var(--space-2xs);
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.form__error-msg {
|
||
margin-top: var(--space-2xs);
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-error);
|
||
}
|
||
|
||
.form button[disabled] {
|
||
opacity: 0.65;
|
||
cursor: progress;
|
||
}
|
||
|
||
.form__field textarea {
|
||
resize: vertical;
|
||
min-height: 8rem;
|
||
}
|
||
|
||
.form__field--checkbox {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr;
|
||
align-items: start;
|
||
gap: var(--space-2xs);
|
||
}
|
||
|
||
.form__field--checkbox input {
|
||
/* 24px — Mindest-Zielgröße (WCAG 2.5.8) */
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
margin-top: var(--space-4xs);
|
||
accent-color: var(--clr-accent);
|
||
}
|
||
|
||
.form__field--checkbox label {
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
/* Leer: unsichtbar, aber im Accessibility-Tree — display:none würde die
|
||
aria-live-Region abmelden und Screenreader verpassen die erste Meldung. */
|
||
.form__status:empty {
|
||
position: absolute;
|
||
width: 1px;
|
||
height: 1px;
|
||
overflow: hidden;
|
||
clip-path: inset(50%);
|
||
}
|
||
|
||
.form__success {
|
||
padding: var(--space-xs) var(--space-s);
|
||
border: 1px solid var(--clr-success);
|
||
border-radius: var(--radius-btn);
|
||
color: var(--clr-success);
|
||
}
|
||
|
||
.form__error {
|
||
padding: var(--space-xs) var(--space-s);
|
||
border: 1px solid var(--clr-error);
|
||
border-radius: var(--radius-btn);
|
||
color: var(--clr-error);
|
||
}
|
||
|
||
.form__submit {
|
||
margin: 0;
|
||
}
|
||
|
||
/* Gruppierte Abschnitte (fieldset/legend) — jeder Block ein dezent umrandetes
|
||
Feld mit Titel auf der Rahmenlinie. Gliedert lange Formulare optisch klar. */
|
||
.form__group {
|
||
margin: 0;
|
||
padding: var(--space-m) var(--space-l) var(--space-l);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
min-inline-size: 0;
|
||
}
|
||
|
||
.form__legend {
|
||
padding: 0 var(--space-2xs);
|
||
font-size: var(--fs-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.form__group-body {
|
||
display: grid;
|
||
gap: var(--space-m);
|
||
}
|
||
|
||
/* === Werbe-/Sponsoren-Banner (Slider) === */
|
||
.ad-banner {
|
||
--banner-interval: 6s;
|
||
}
|
||
|
||
.ad-banner__track {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
/* Meta-Zeile unter dem Slider: Label links, Controls rechts */
|
||
.ad-banner__meta {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
margin-top: var(--space-s);
|
||
min-height: 2rem;
|
||
}
|
||
|
||
/* Unter 600px verlieren Label und die vier Controls (Pfeile, Dots, Pause) den
|
||
Platz nebeneinander — das Label bricht auf 4-5 schmale Zeilen, während die
|
||
Controls mittig danebenschweben. Gestapelt statt gequetscht: Label über volle
|
||
Breite, Controls darunter als eigene, zentrierte Zeile. */
|
||
@media (max-width: 600px) {
|
||
.ad-banner__meta {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
min-height: 0;
|
||
}
|
||
|
||
.ad-banner__controls {
|
||
align-self: stretch;
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
.ad-banner__meta-label {
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
letter-spacing: var(--ls-tight);
|
||
}
|
||
|
||
.ad-banner__slide[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
.ad-banner__slide {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
min-height: 320px;
|
||
padding: clamp(var(--space-xl), var(--space-s) + 2vw, var(--space-4xl));
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
/* Inhaltsbreite begrenzen, damit Titel/Text die linke Hälfte belegen und das
|
||
Foto rechts frei bleibt. */
|
||
.ad-banner__content {
|
||
max-width: 60ch;
|
||
}
|
||
|
||
/* Fußzeile: CTAs links, Anzeige-/Status-Label rechts — an den unteren Rand. */
|
||
.ad-banner__footer {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: var(--space-xs) var(--space-s);
|
||
width: 100%;
|
||
margin-top: auto;
|
||
padding-top: var(--space-l);
|
||
}
|
||
|
||
/* Crossfade-Übergang (nur mit JS): Slides im selben Grid-Feld stapeln, sodass
|
||
die Höhe konstant bleibt (kein Sprung) und ein- wie ausgehende Folie sauber
|
||
überblenden. Die ausgehende driftet leicht nach unten, die eingehende steigt
|
||
auf. Ohne JS greift weiter .ad-banner__slide[hidden] (nur erste Folie).
|
||
Reduced-Motion: globaler Kill-Switch in reset.css macht den Wechsel instant. */
|
||
.ad-banner.is-enhanced .ad-banner__track {
|
||
display: grid;
|
||
}
|
||
|
||
.ad-banner.is-enhanced .ad-banner__slide {
|
||
grid-area: 1 / 1;
|
||
opacity: 0;
|
||
transform: translateY(8px);
|
||
pointer-events: none;
|
||
transition: opacity 0.55s ease, transform 0.55s ease, visibility 0s linear 0.55s;
|
||
visibility: hidden;
|
||
}
|
||
|
||
.ad-banner.is-enhanced .ad-banner__slide.is-active {
|
||
opacity: 1;
|
||
transform: none;
|
||
pointer-events: auto;
|
||
visibility: visible;
|
||
transition: opacity 0.55s ease, transform 0.55s ease, visibility 0s;
|
||
}
|
||
|
||
/* Optionaler Hintergrund pro Banner (data: "background").
|
||
Das Bild ist ein <img class="ad-banner__bg">, KEIN CSS-Hintergrund: die CSP
|
||
(default-src 'self', kein style-src) verwirft style-Attribute, eine Inline-Variable
|
||
--banner-bg kam auf echtem Apache nie an (31.07.2026, Slides blieben schwarz).
|
||
Der dunkle Verlauf, der Text und CTA links lesbar hält, liegt deshalb auf ::before. */
|
||
.ad-banner__slide--bg {
|
||
position: relative;
|
||
/* Eigener Stapelkontext: das Bild darf hinter den Inhalt, aber nicht hinter
|
||
die Slide-Fläche selbst rutschen. */
|
||
isolation: isolate;
|
||
border-color: transparent;
|
||
}
|
||
|
||
.ad-banner__bg,
|
||
.ad-banner__slide--bg::before {
|
||
position: absolute;
|
||
/* inset bezieht sich auf die Padding-Box — das Bild endet an der Innenkante und
|
||
malt nicht unter den 1px-Rahmen (dort schimmerte es sonst durch). */
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.ad-banner__bg {
|
||
z-index: -2;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center;
|
||
}
|
||
|
||
.ad-banner__slide--bg::before {
|
||
content: '';
|
||
z-index: -1;
|
||
background-image: linear-gradient(to right, rgb(0 0 0 / 0.82), rgb(0 0 0 / 0.45) 60%, rgb(0 0 0 / 0.3));
|
||
}
|
||
|
||
/* Pro-Slide-Akzentfarbe (data: "accent") — z. B. Sponsor-CI statt Vereins-Rot.
|
||
Der Name kommt aus der JSON, die Farbe aus tokens.css: ein Hex-Wert aus der JSON
|
||
käme nur per Inline-Style in die Seite, und den verwirft die CSP (siehe oben). */
|
||
.ad-banner__slide--accent-breadcrumb {
|
||
--banner-accent: var(--clr-banner-accent-breadcrumb);
|
||
}
|
||
|
||
.ad-banner__slide--accent {
|
||
--clr-accent: var(--banner-accent);
|
||
--clr-accent-dark: color-mix(in srgb, var(--banner-accent) 82%, #000);
|
||
}
|
||
|
||
/* Solider Button: dunkler, aus der Akzentfarbe abgeleiteter Text — helle
|
||
Akzentfarben (z. B. Teal) hätten mit Weiß zu wenig Kontrast (WCAG 1.4.3). */
|
||
.ad-banner__slide--accent .ad-banner__cta.btn:not(.btn--outline) {
|
||
color: color-mix(in srgb, var(--banner-accent) 22%, #000);
|
||
}
|
||
|
||
.ad-banner__slide--accent .ad-banner__cta.btn--outline {
|
||
color: var(--banner-accent);
|
||
border-color: var(--banner-accent);
|
||
}
|
||
|
||
.ad-banner__slide--accent .ad-banner__cta.btn--outline:hover {
|
||
background: color-mix(in srgb, var(--banner-accent) 16%, transparent);
|
||
border-color: var(--banner-accent);
|
||
color: var(--banner-accent);
|
||
}
|
||
|
||
.ad-banner__slide--bg .ad-banner__text {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
/* Auf Mobil läuft der Verlauf nach unten, damit der gestapelte Text lesbar bleibt. */
|
||
.ad-banner__slide--bg::before {
|
||
background-image: linear-gradient(to bottom, rgb(0 0 0 / 0.82), rgb(0 0 0 / 0.5));
|
||
}
|
||
}
|
||
|
||
/* Eigenwerbung („Hier könnte Ihre Werbung stehen") — rote Glasfläche mit
|
||
gestricheltem Frame, das eine freie Werbefläche andeutet. */
|
||
.ad-banner__slide--house {
|
||
position: relative;
|
||
overflow: hidden;
|
||
background:
|
||
radial-gradient(135% 145% at 100% 0%, rgb(var(--clr-accent-rgb) / 0.24), transparent 55%),
|
||
var(--clr-glass-bg);
|
||
border-color: rgb(var(--clr-accent-rgb) / 0.5);
|
||
}
|
||
|
||
.ad-banner__slide--house .ad-banner__content,
|
||
.ad-banner__slide--house .ad-banner__logo {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.ad-banner__slide--house .ad-banner__tag {
|
||
color: #fff;
|
||
background: var(--clr-accent);
|
||
border-color: transparent;
|
||
}
|
||
|
||
.ad-banner__slide--house .ad-banner__text {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* Eigenwerbung MIT Foto-Hintergrund: rot-dunkler Verlauf von links hält den
|
||
Text lesbar, das Foto bleibt rechts sichtbar. */
|
||
.ad-banner__slide--house.ad-banner__slide--bg {
|
||
background-image:
|
||
linear-gradient(to right, rgb(12 10 11 / 0.9), rgb(20 16 17 / 0.55) 55%, rgb(20 16 17 / 0.12) 90%),
|
||
var(--banner-bg);
|
||
background-size: cover;
|
||
background-position: center;
|
||
/* wie bei --bg: nicht unter den Rahmen malen (--house-Shorthand setzt es zurück) */
|
||
background-clip: padding-box;
|
||
border-color: transparent;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
/* Auf Mobil läuft der Verlauf nach unten (Text steht gestapelt darüber). */
|
||
.ad-banner__slide--house.ad-banner__slide--bg {
|
||
background-image:
|
||
linear-gradient(to bottom, rgb(12 10 11 / 0.88), rgb(20 16 17 / 0.7)),
|
||
var(--banner-bg);
|
||
}
|
||
}
|
||
|
||
/* Anzeige-/Status-Label: sitzt rechts in der Fußzeile (margin-left:auto).
|
||
Dunkle Pille, damit es auch über dem Foto lesbar bleibt. */
|
||
.ad-banner__tag {
|
||
margin-left: auto;
|
||
align-self: center;
|
||
padding: 0.2em 0.7em;
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
background: rgb(0 0 0 / 0.45);
|
||
border: 1px solid rgb(255 255 255 / 0.18);
|
||
border-radius: 999px;
|
||
}
|
||
|
||
.ad-banner__title {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-700);
|
||
line-height: var(--lh-heading);
|
||
margin-bottom: var(--space-2xs);
|
||
}
|
||
|
||
.ad-banner__text {
|
||
max-width: 52ch;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* CTA-Reihe: Primär- + optionaler Outline-Sekundär-Button nebeneinander. */
|
||
.ad-banner__actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--space-xs);
|
||
}
|
||
|
||
/* Logo führt die Slide an: kompaktes Badge oben links. */
|
||
.ad-banner__logo {
|
||
margin-bottom: var(--space-l);
|
||
}
|
||
|
||
/* Helle Platte für dunkle Firmenlogos (Wortmarken). */
|
||
.ad-banner__logo--light {
|
||
padding: var(--space-2xs) var(--space-xs);
|
||
background: #fff;
|
||
border-radius: var(--radius-btn);
|
||
}
|
||
|
||
.ad-banner__logo img {
|
||
display: block;
|
||
width: auto;
|
||
height: auto;
|
||
max-width: 230px;
|
||
max-height: 56px;
|
||
object-fit: contain;
|
||
}
|
||
|
||
/* Vereinswappen (Eigenwerbung) darf etwas größer stehen. */
|
||
.ad-banner__slide--house .ad-banner__logo img {
|
||
max-height: 76px;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.ad-banner__content {
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
|
||
/* Steuerung: Pause/Play-Taste + Indikatorleiste — sitzt rechts in .ad-banner__meta. */
|
||
.ad-banner__controls {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-xs);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.ad-banner__controls[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
.ad-banner__toggle[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
/* Glas-Optik als Delta zu .btn--icon — Größe, Rundung, Zentrierung und die
|
||
44px-Trefferfläche kommen von dort. Beide Controls waren vorher zeichengleich
|
||
dupliziert. .btn muss im Selektor mitstehen: utilities.css lädt nach
|
||
components.css, bei gleicher Spezifität gewänne sonst die rote .btn-Fläche. */
|
||
.btn.ad-banner__toggle,
|
||
.btn.ad-banner__nav-btn {
|
||
font-size: var(--icon-s);
|
||
color: var(--clr-text);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.btn.ad-banner__toggle:hover,
|
||
.btn.ad-banner__nav-btn:hover {
|
||
background: rgb(255 255 255 / 0.2);
|
||
border-color: var(--clr-glass-border);
|
||
}
|
||
|
||
.ad-banner__toggle-icon {
|
||
font-size: var(--icon-s);
|
||
}
|
||
|
||
/* Pause-Icon im Abspiel-Zustand, Play-Icon wenn pausiert. */
|
||
.ad-banner__toggle .ad-banner__toggle-icon--play {
|
||
display: none;
|
||
}
|
||
|
||
.ad-banner__toggle[aria-pressed="true"] .ad-banner__toggle-icon--pause {
|
||
display: none;
|
||
}
|
||
|
||
.ad-banner__toggle[aria-pressed="true"] .ad-banner__toggle-icon--play {
|
||
display: inline-block;
|
||
}
|
||
|
||
/* Indikatorleiste — die aktive Leiste füllt sich über --banner-interval. */
|
||
.ad-banner__indicators {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: var(--space-2xs);
|
||
}
|
||
|
||
/* Trefferfläche 48x44px, sichtbar bleibt der 6px-Balken. Der Balken liegt im
|
||
::before statt auf dem Button selbst — so kann overflow:hidden entfallen, das
|
||
sonst jede Vergrößerung der Trefferfläche wieder abschneiden würde. */
|
||
.ad-banner__dot {
|
||
position: relative;
|
||
width: 48px;
|
||
height: var(--tap-min);
|
||
padding: 0;
|
||
background: none;
|
||
border: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.ad-banner__dot::before,
|
||
.ad-banner__progress {
|
||
position: absolute;
|
||
inset: calc(50% - 3px) 0 auto;
|
||
height: 6px;
|
||
border-radius: 999px;
|
||
}
|
||
|
||
.ad-banner__dot::before {
|
||
content: "";
|
||
background: var(--clr-glass-border);
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.ad-banner__dot:hover::before {
|
||
background: rgb(255 255 255 / 0.35);
|
||
}
|
||
|
||
.ad-banner__progress {
|
||
background: var(--clr-accent);
|
||
transform: scaleX(0);
|
||
transform-origin: left;
|
||
}
|
||
|
||
.ad-banner__dot.is-active .ad-banner__progress {
|
||
animation: ad-progress var(--banner-interval) linear forwards;
|
||
}
|
||
|
||
.ad-banner.is-paused .ad-banner__dot.is-active .ad-banner__progress {
|
||
animation-play-state: paused;
|
||
}
|
||
|
||
@keyframes ad-progress {
|
||
from { transform: scaleX(0); }
|
||
to { transform: scaleX(1); }
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.ad-banner__dot.is-active .ad-banner__progress {
|
||
animation: none;
|
||
transform: scaleX(1);
|
||
}
|
||
}
|
||
|
||
/* === key-facts (Faktenleiste) === */
|
||
.key-facts__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: var(--space-s);
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.key-facts__item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-xs);
|
||
padding: var(--space-s) var(--space-m);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.key-facts__icon {
|
||
display: inline-flex;
|
||
font-size: var(--icon-l);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.key-facts__text {
|
||
display: grid;
|
||
gap: var(--space-4xs);
|
||
}
|
||
|
||
.key-facts__label {
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.key-facts__value {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
line-height: var(--lh-heading);
|
||
}
|
||
|
||
/* === team-about === */
|
||
.team-about p {
|
||
max-width: 68ch;
|
||
}
|
||
|
||
/* === training-location (Bento-Grid) === */
|
||
.bento {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
grid-auto-rows: minmax(0, auto);
|
||
gap: var(--space-m);
|
||
}
|
||
|
||
/* Tile-Größen: Trainingszeiten = großer Anker, Spielort = breit, dazu zwei kleine. */
|
||
.bento__tile--times { grid-column: span 2; grid-row: span 2; }
|
||
.bento__tile--venue { grid-column: span 2; }
|
||
.bento__tile--accent { grid-column: span 1; }
|
||
.bento__tile--route { grid-column: span 1; }
|
||
|
||
.bento__tile {
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: var(--space-xl);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Akzent-Tile: gefüllt, weiße Schrift (gut lesbar), zentrierte Kennzahl. */
|
||
.bento__tile--accent {
|
||
justify-content: center;
|
||
gap: var(--space-3xs);
|
||
background: var(--clr-accent);
|
||
border-color: var(--clr-accent);
|
||
text-align: center;
|
||
}
|
||
|
||
.bento__tile--route {
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: var(--space-s);
|
||
text-align: center;
|
||
}
|
||
|
||
/* Inhalt über dem Deko-Icon halten. */
|
||
.bento__heading,
|
||
.bento__sessions,
|
||
.bento__address,
|
||
.bento__note {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.bento__heading {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-xs);
|
||
margin-bottom: var(--space-l);
|
||
font-size: var(--fs-690);
|
||
line-height: var(--lh-heading);
|
||
}
|
||
|
||
.bento__hicon {
|
||
flex: none;
|
||
font-size: var(--icon-l);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.bento__sessions {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.bento__sessions li {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
padding: var(--space-s) 0;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.bento__sessions li:first-child { padding-top: 0; }
|
||
.bento__sessions li:last-child { padding-bottom: 0; border-bottom: 0; }
|
||
|
||
.bento__day {
|
||
font-size: var(--fs-600);
|
||
}
|
||
|
||
.bento__time {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.bento__note {
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.bento__address {
|
||
margin: 0;
|
||
font-size: var(--fs-600);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* Akzent-Tile-Typo. */
|
||
.bento__stat-label {
|
||
font-size: var(--fs-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: rgb(255 255 255 / 0.85);
|
||
}
|
||
|
||
.bento__stat-value {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-690);
|
||
line-height: var(--lh-heading);
|
||
color: #fff;
|
||
}
|
||
|
||
.bento__route-label {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
}
|
||
|
||
/* Großes, angeschnittenes Deko-Icon unten rechts. */
|
||
.bento__deco {
|
||
position: absolute;
|
||
right: -1rem;
|
||
bottom: -1.75rem;
|
||
z-index: 0;
|
||
font-size: var(--fs-deco);
|
||
line-height: 1;
|
||
color: var(--clr-text);
|
||
opacity: 0.06;
|
||
pointer-events: none;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.bento {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
.bento__tile--times { grid-column: span 2; grid-row: auto; }
|
||
.bento__tile--venue { grid-column: span 2; }
|
||
}
|
||
|
||
@media (max-width: 560px) {
|
||
.bento {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
.bento__tile--times,
|
||
.bento__tile--venue,
|
||
.bento__tile--accent,
|
||
.bento__tile--route {
|
||
grid-column: span 1;
|
||
}
|
||
}
|
||
|
||
/* === turnen-dash (Turn-Disziplin-Dashboard im Bento-Look wie Mannschaftsseiten) === */
|
||
.turnen-dash {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: var(--space-m);
|
||
}
|
||
|
||
/* Eine Gruppe: Trainingszeiten (2/3) + Verantwortlich (1/3), Ort volle Breite darunter. */
|
||
.turnen-dash--single {
|
||
grid-template-areas:
|
||
"train train lead"
|
||
"venue venue venue";
|
||
}
|
||
.turnen-dash--single .turnen-dash__training { grid-area: train; }
|
||
.turnen-dash--single .turnen-dash__contact { grid-area: lead; }
|
||
.turnen-dash--single .turnen-dash__venue { grid-area: venue; }
|
||
|
||
/* Mehrere Gruppen: Ort (2/3) + Kontakt rechts (1/3) — Zeiten stehen je Gruppe.
|
||
Kontakt steht dadurch konsistent rechts wie bei den Einzel-Disziplinen. */
|
||
.turnen-dash:not(.turnen-dash--single) {
|
||
grid-template-areas: "venue venue lead";
|
||
}
|
||
.turnen-dash:not(.turnen-dash--single) .turnen-dash__contact { grid-area: lead; }
|
||
.turnen-dash:not(.turnen-dash--single) .turnen-dash__venue { grid-area: venue; }
|
||
|
||
/* Mit Hinweis-Kachel: rote Banner-Zeile ganz oben (volle Breite). */
|
||
.turnen-dash--notice.turnen-dash--single {
|
||
grid-template-areas:
|
||
"notice notice notice"
|
||
"train train lead"
|
||
"venue venue venue";
|
||
}
|
||
.turnen-dash--notice:not(.turnen-dash--single) {
|
||
grid-template-areas:
|
||
"notice notice notice"
|
||
"venue venue lead";
|
||
}
|
||
.turnen-dash__notice { grid-area: notice; }
|
||
|
||
/* Hinweis-Kachel: klar rot, weiße Schrift, Icon links (z. B. Wartelisten-Status). */
|
||
.turnen-dash__notice {
|
||
flex-direction: row;
|
||
align-items: flex-start;
|
||
gap: var(--space-xs);
|
||
background: var(--clr-accent);
|
||
border-color: var(--clr-accent);
|
||
color: #fff;
|
||
}
|
||
|
||
.turnen-dash__notice p {
|
||
margin: 0;
|
||
max-width: none;
|
||
font-size: var(--fs-600);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.turnen-dash__notice-icon {
|
||
flex: none;
|
||
margin-top: 0.1em;
|
||
font-size: var(--icon-l);
|
||
}
|
||
|
||
/* Trainingsort: Adresse links, Route-Button rechts (wie team-bento__venue). */
|
||
.turnen-dash__venue {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-m);
|
||
}
|
||
|
||
.turnen-dash__venue-main {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.turnen-dash__venue .bento__heading {
|
||
margin-bottom: var(--space-2xs);
|
||
}
|
||
|
||
.turnen-dash__route {
|
||
position: relative;
|
||
z-index: 1;
|
||
flex: none;
|
||
}
|
||
|
||
/* Kontakt-Kachel: schlichter weißer Mail-Link (zentriert) auf der Akzentfläche. */
|
||
.turnen-dash__mail {
|
||
display: inline-flex;
|
||
align-self: center;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
margin-top: var(--space-xs);
|
||
color: #fff;
|
||
font-size: var(--fs-500);
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.2em;
|
||
}
|
||
|
||
.turnen-dash__mail:hover,
|
||
.turnen-dash__mail:focus-visible {
|
||
color: rgb(255 255 255 / 0.82);
|
||
}
|
||
|
||
.turnen-dash__mail-icon {
|
||
flex: none;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.turnen-dash,
|
||
.turnen-dash--single {
|
||
grid-template-columns: 1fr;
|
||
grid-template-areas:
|
||
"train"
|
||
"lead"
|
||
"venue";
|
||
}
|
||
.turnen-dash:not(.turnen-dash--single) {
|
||
grid-template-areas:
|
||
"lead"
|
||
"venue";
|
||
}
|
||
.turnen-dash--notice,
|
||
.turnen-dash--notice.turnen-dash--single {
|
||
grid-template-areas:
|
||
"notice"
|
||
"train"
|
||
"lead"
|
||
"venue";
|
||
}
|
||
.turnen-dash--notice:not(.turnen-dash--single) {
|
||
grid-template-areas:
|
||
"notice"
|
||
"lead"
|
||
"venue";
|
||
}
|
||
}
|
||
|
||
/* === group-list (Turn-Gruppen einer Disziplin) === */
|
||
.group-list__title {
|
||
margin-bottom: var(--space-l);
|
||
text-align: center;
|
||
}
|
||
|
||
/* === group-tabs (Tab-Navigation für rich Gruppen) === */
|
||
.group-tabs__bar {
|
||
display: flex;
|
||
justify-content: center;
|
||
overflow-x: auto;
|
||
scrollbar-width: none;
|
||
margin-bottom: var(--space-2xl);
|
||
gap: 0;
|
||
}
|
||
|
||
.group-tabs__bar::-webkit-scrollbar { display: none; }
|
||
|
||
/* Tabs im Panel dürfen nicht schrumpfen — sie scrollen stattdessen horizontal. */
|
||
.group-tabs__tab {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Panel-Karte übernimmt das bestehende group-card--stacked-Layout */
|
||
.group-tabs__panel {
|
||
display: grid;
|
||
grid-template-columns: 1.6fr 1fr;
|
||
gap: var(--space-4xl);
|
||
align-items: start;
|
||
padding: var(--space-3xl) var(--space-3xl);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
/* display:grid überschreibt den UA-Stil [hidden]{display:none} — explizit korrigieren */
|
||
.group-tabs__panel[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.group-tabs__panel {
|
||
grid-template-columns: 1fr;
|
||
gap: var(--space-xl);
|
||
padding: var(--space-l) var(--space-xl);
|
||
}
|
||
}
|
||
|
||
/* Schlichte Gruppen (z. B. Kurse) als Raster, beschreibende Gruppen gestapelt. */
|
||
.group-list--grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||
gap: var(--space-m);
|
||
}
|
||
|
||
.group-list--stacked {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-4xl);
|
||
}
|
||
|
||
.group-card {
|
||
padding: var(--space-xl) var(--space-2xl) var(--space-2xl);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
/* Gestapelt: Beschreibung links, Trainingszeiten-Panel rechts. */
|
||
.group-list--stacked .group-card {
|
||
display: grid;
|
||
grid-template-columns: 1.6fr 1fr;
|
||
gap: var(--space-4xl);
|
||
align-items: start;
|
||
padding: var(--space-3xl) var(--space-3xl);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.group-list--stacked .group-card {
|
||
grid-template-columns: 1fr;
|
||
gap: var(--space-xl);
|
||
padding: var(--space-l) var(--space-xl) var(--space-xl);
|
||
}
|
||
}
|
||
|
||
/* Beschreibungsspalte: angenehme Zeilenlänge + Lesefluss. */
|
||
.group-card__body {
|
||
max-width: 64ch;
|
||
}
|
||
|
||
/* Name links, Level-Chip rechts vertikal zentriert */
|
||
.group-card__header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.group-card__name {
|
||
font-size: var(--fs-690);
|
||
line-height: var(--lh-heading);
|
||
}
|
||
|
||
.group-card__level {
|
||
/* flex-shrink: 0 hält den Chip neben einem kurzen Gruppennamen in Form; der
|
||
Elternteil hat flex-wrap, ein langer Chip rutscht also ohnehin auf eine
|
||
eigene Zeile.
|
||
|
||
max-width und der erlaubte Umbruch sind das Sicherheitsnetz: mit
|
||
`white-space: nowrap` UND `flex-shrink: 0` konnte der Chip seinen Text nicht
|
||
brechen und auch nicht schrumpfen — seine Mindestbreite spannte damit die
|
||
ganze Karte auf. Bei den „Flamingos" (47 Zeichen) war der Chip am Handy
|
||
393px breit in einer 345px-Karte, das Dokument lief um 63px über und der
|
||
Fließtext war rechts abgeschnitten (Rückmeldung der Turnabteilung,
|
||
31.07.2026). Ein Chip trägt Information, deshalb umbrechen statt mit … kürzen. */
|
||
flex-shrink: 0;
|
||
max-width: 100%;
|
||
padding: var(--space-4xs) var(--space-2xs);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
border: 1px solid var(--clr-glass-border-strong);
|
||
border-radius: 999px;
|
||
}
|
||
|
||
/* Intro = Lead-Paragraph, deutlich abgesetzt vom Level-Chip */
|
||
.group-card__intro {
|
||
margin: var(--space-m) 0 0;
|
||
font-size: var(--fs-600);
|
||
line-height: 1.65;
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* Detail-Abschnitte: Akzent-Randstrich links als visueller Anker */
|
||
/* Font-Familie, Uppercase und Sperrung kommen jetzt aus der h4-Basisregel —
|
||
hier bleibt nur, was diese Überschrift von der Basis unterscheidet. */
|
||
.group-card__detail-heading {
|
||
margin: var(--space-3xl) 0 0;
|
||
padding-left: var(--space-xs);
|
||
border-left: 2px solid var(--clr-accent);
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.group-card__detail-text {
|
||
margin: var(--space-2xs) 0 0;
|
||
padding-left: var(--space-xs);
|
||
font-size: var(--fs-600);
|
||
line-height: 1.72;
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.group-card__recommendation {
|
||
margin: var(--space-2xl) 0 0;
|
||
padding: var(--space-s) var(--space-s) 0;
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
font-size: var(--fs-500);
|
||
font-style: italic;
|
||
line-height: 1.6;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Trainingszeiten-Panel in der Karte. */
|
||
.group-card__schedule {
|
||
margin-top: var(--space-m);
|
||
}
|
||
|
||
.group-list--stacked .group-card__schedule,
|
||
.group-tabs .group-card__schedule {
|
||
margin-top: 0;
|
||
padding: var(--space-xl);
|
||
background: var(--clr-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
border-top: 2px solid var(--clr-accent);
|
||
}
|
||
|
||
.group-card__schedule-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
margin: 0 0 var(--space-s);
|
||
font-size: var(--fs-650);
|
||
letter-spacing: var(--ls-tight);
|
||
}
|
||
|
||
.group-card__schedule-icon {
|
||
flex: none;
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.group-card__trainers {
|
||
margin: var(--space-s) 0 0;
|
||
padding-top: var(--space-xs);
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
font-size: var(--fs-500);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.group-card__note {
|
||
margin: var(--space-2xs) 0 0;
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
/* Trainingszeiten-Liste (Tag/Zeit + optional Label, Notiz, Übungsleitung). */
|
||
.schedule-list {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.schedule-row {
|
||
padding: var(--space-xs) 0;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.schedule-row:first-child { padding-top: 0; }
|
||
.schedule-row:last-child { padding-bottom: 0; border-bottom: 0; }
|
||
|
||
.schedule-row__label {
|
||
display: block;
|
||
margin-bottom: var(--space-3xs);
|
||
font-size: var(--fs-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-accent);
|
||
font-family: var(--ff-body);
|
||
}
|
||
|
||
.schedule-row__when {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
}
|
||
|
||
.schedule-row__day {
|
||
font-size: var(--fs-600);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.schedule-row__time {
|
||
font-size: var(--fs-600);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.schedule-row__note {
|
||
display: block;
|
||
margin-top: var(--space-3xs);
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.schedule-row__trainers {
|
||
display: block;
|
||
margin-top: var(--space-3xs);
|
||
font-size: var(--fs-500);
|
||
line-height: 1.5;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.schedule-row__trainers-label {
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.group-list__footnote {
|
||
margin: var(--space-l) 0 0;
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.schedule-row__when {
|
||
flex-direction: column;
|
||
gap: var(--space-4xs);
|
||
}
|
||
.schedule-row__time {
|
||
text-align: left;
|
||
}
|
||
}
|
||
|
||
/* === cta-band === */
|
||
.cta-band__inner {
|
||
max-width: 60ch;
|
||
margin-inline: auto;
|
||
text-align: center;
|
||
}
|
||
|
||
.cta-band__text {
|
||
margin-top: var(--space-xs);
|
||
margin-inline: auto;
|
||
}
|
||
|
||
.cta-band__actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: var(--space-xs);
|
||
margin-top: var(--space-xl);
|
||
}
|
||
|
||
/* === faq (Accordion) === */
|
||
.faq__inner {
|
||
max-width: 760px;
|
||
margin-inline: auto;
|
||
}
|
||
|
||
.faq__inner > h2 {
|
||
text-align: center;
|
||
}
|
||
|
||
/* Schlichte Trennlinien-Liste statt Kästchen. */
|
||
.faq__list {
|
||
margin-top: var(--space-3xl);
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.faq__item {
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.faq__q {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
padding: var(--space-m) var(--space-3xs);
|
||
font-size: var(--fs-600);
|
||
line-height: 1.3;
|
||
cursor: pointer;
|
||
list-style: none;
|
||
transition: color var(--transition);
|
||
}
|
||
|
||
.faq__q::-webkit-details-marker {
|
||
display: none;
|
||
}
|
||
|
||
.faq__q:hover {
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.faq__chevron {
|
||
flex: none;
|
||
font-size: var(--icon-s);
|
||
color: var(--clr-text-muted);
|
||
transition: transform var(--transition), color var(--transition);
|
||
}
|
||
|
||
.faq__item[open] .faq__q {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.faq__item[open] .faq__chevron {
|
||
transform: rotate(180deg);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.faq__a {
|
||
padding: 0 var(--space-3xs) var(--space-l);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.faq__a p {
|
||
max-width: 64ch;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Antwort beim Öffnen sanft einblenden (reduced-motion via reset.css deaktiviert). */
|
||
.faq__item[open] .faq__a {
|
||
animation: faq-reveal 260ms ease;
|
||
}
|
||
|
||
@keyframes faq-reveal {
|
||
from { opacity: 0; transform: translateY(-4px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* === age-groups (Jugend-Altersklassen) === */
|
||
.age-groups > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.age-groups__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: var(--space-xl);
|
||
margin: var(--space-3xl) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
text-align: left;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.age-groups__grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
.age-group {
|
||
display: grid;
|
||
grid-template-rows: auto 1fr;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
transition: transform var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
.age-group:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.age-group__media {
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.age-group__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
aspect-ratio: 3 / 2;
|
||
object-fit: cover;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.age-group:hover .age-group__img {
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.age-group__body {
|
||
display: grid;
|
||
gap: var(--space-2xs);
|
||
align-content: start;
|
||
padding: var(--space-l) var(--space-l) var(--space-l);
|
||
}
|
||
|
||
.age-group__name {
|
||
font-size: var(--fs-700);
|
||
line-height: var(--lh-heading);
|
||
}
|
||
|
||
.age-group__training {
|
||
display: inline-flex;
|
||
align-items: flex-start;
|
||
gap: var(--space-2xs);
|
||
margin: 0;
|
||
font-size: var(--fs-600);
|
||
line-height: 1.45;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.age-group__icon {
|
||
flex: none;
|
||
margin-top: 0.2em;
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
/* Platzhalter-Karten (B-/A-Jugend „In Entstehung"): Bild gedämpft, Label oben links. */
|
||
.age-group--upcoming .age-group__img {
|
||
opacity: 0.5;
|
||
filter: grayscale(0.5);
|
||
}
|
||
|
||
.age-group--upcoming:hover .age-group__img {
|
||
transform: none;
|
||
}
|
||
|
||
.age-group__soon {
|
||
position: absolute;
|
||
top: 0.9rem;
|
||
left: 0.9rem;
|
||
padding: var(--space-3xs) var(--space-xs);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-400);
|
||
letter-spacing: var(--ls-wide);
|
||
text-transform: uppercase;
|
||
color: #fff;
|
||
background: var(--clr-accent);
|
||
border-radius: var(--radius-btn);
|
||
}
|
||
|
||
/* === team-cards (Mannschafts-Karten) === */
|
||
.team-cards > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.team-cards__intro {
|
||
max-width: 60ch;
|
||
margin: var(--space-s) auto 0;
|
||
}
|
||
|
||
/* Intro als „Klärtext" unter den Kacheln → mehr Luft nach oben zur Kartenreihe */
|
||
.team-cards__intro--below {
|
||
margin-top: var(--space-3xl);
|
||
}
|
||
|
||
.team-cards__grid {
|
||
display: grid;
|
||
/* Zwei Karten pro Reihe (50/50) — bei vier Mannschaften ergibt das ein 2×2-Raster */
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: var(--space-l);
|
||
margin: var(--space-3xl) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
text-align: left;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.team-cards__grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
.team-card {
|
||
display: grid;
|
||
grid-template-rows: auto 1fr;
|
||
height: 100%;
|
||
color: var(--clr-text);
|
||
text-decoration: none;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
transition: transform var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
.team-card:hover,
|
||
.team-card:focus-visible {
|
||
transform: translateY(-4px);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.team-card__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
aspect-ratio: 16 / 10;
|
||
object-fit: cover;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.team-card:hover .team-card__img,
|
||
.team-card:focus-visible .team-card__img {
|
||
transform: scale(1.04);
|
||
}
|
||
|
||
.team-card__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-3xs);
|
||
padding: var(--space-l) var(--space-l) var(--space-l);
|
||
}
|
||
|
||
.team-card__name {
|
||
/* Coolvetica/Heading (siehe CLAUDE.md) — bewusst groß und prägnant. */
|
||
font-size: var(--fs-690);
|
||
line-height: var(--lh-heading);
|
||
}
|
||
|
||
.team-card__meta {
|
||
font-size: var(--fs-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.team-card__cta {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
/* an den unteren Rand schieben → CTA aller Karten fluchtet, egal wie lang der Name */
|
||
margin-top: auto;
|
||
padding-top: var(--space-s);
|
||
/* Body-Schrift (Abel) — Coolvetica bleibt den Überschriften vorbehalten */
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.team-card__arrow {
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.team-card:hover .team-card__arrow {
|
||
transform: translateX(3px);
|
||
}
|
||
|
||
/* === stadionzeitung-cards (Ausgaben-Archiv) === */
|
||
/* Liste über die volle Breite, Cover links und die Angaben rechts daneben —
|
||
dasselbe Muster wie .veranstaltung-card, und aus demselben Grund: der Bestand
|
||
ist klein (eine Handvoll Ausgaben pro Saison).
|
||
|
||
Hier stand vorher ein horizontales Regal (grid-auto-flow: column, 280px-Spalten,
|
||
Scroll-Snap). Der Kommentar dazu nannte genau die richtige Gefahr — „bei 1-3
|
||
Karten wie eine leere Fläche mit einer verlorenen Karte" — löste sie aber nicht:
|
||
mit EINER veröffentlichten Ausgabe stand eine 280px-Karte allein links auf einer
|
||
1280px breiten Seite (31.07.2026). Eine Zeile über die volle Breite füllt die
|
||
Fläche bei jeder Anzahl, von einer Ausgabe bis zwanzig.
|
||
|
||
Unterschied zu .veranstaltung-card, der die Maße erklärt: dort ist das Medium ein
|
||
<img>, das per object-fit beschnitten wird. Hier ist es die live gerenderte
|
||
Cover-Seite mit festem A5-Verhältnis — die lässt sich nicht beschneiden, sie
|
||
bestimmt die Zeilenhöhe. Die Medienspalte ist deshalb schmaler als die 26% dort:
|
||
230px Cover ergeben 326px Zeilenhöhe. */
|
||
.stadionzeitung-cards__grid {
|
||
display: grid;
|
||
gap: var(--space-l);
|
||
margin: var(--space-l) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.stadionzeitung-card {
|
||
display: flex;
|
||
align-items: stretch;
|
||
color: var(--clr-text);
|
||
text-decoration: none;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
transition: transform var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
/* clamp statt fester Breite: unter ~700px Kartenbreite wären 230px Cover mehr als
|
||
ein Drittel der Zeile und der Text bekäme zu wenig. */
|
||
.stadionzeitung-card__media {
|
||
flex: 0 0 clamp(140px, 18%, 230px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stadionzeitung-card:hover,
|
||
.stadionzeitung-card:focus-visible {
|
||
transform: translateY(-4px);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.stadionzeitung-card__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
/* Magazinseite: Hochformat statt der 16/10-Fotokachel bei .team-card. */
|
||
aspect-ratio: 3 / 4;
|
||
object-fit: cover;
|
||
object-position: top;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.stadionzeitung-card:hover .stadionzeitung-card__img,
|
||
.stadionzeitung-card:focus-visible .stadionzeitung-card__img {
|
||
transform: scale(1.04);
|
||
}
|
||
|
||
/* Live-gerendertes Cover als Karten-Vorschau (siehe stadionzeitung-cards.php)
|
||
statt eines Bilds — dieselbe Zoom-Affordance beim Hover, aber ohne den
|
||
480px-Bildschirm-Deckel der Einzelansicht (soll die Kartenbreite ausfüllen). */
|
||
.stadionzeitung-card__media .stadionzeitung-cover {
|
||
max-width: none;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.stadionzeitung-card:hover .stadionzeitung-card__media .stadionzeitung-cover,
|
||
.stadionzeitung-card:focus-visible .stadionzeitung-card__media .stadionzeitung-cover {
|
||
transform: scale(1.04);
|
||
}
|
||
|
||
.stadionzeitung-card__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: var(--space-3xs);
|
||
padding: var(--space-2xl);
|
||
}
|
||
|
||
.stadionzeitung-card__name {
|
||
/* --fs-690 ist der Token für Karten-Überschriften (wie .team-card__name).
|
||
Hier stand vorher --fs-650 mit der Begründung, Gegner-Bezeichnungen könnten
|
||
lang sein und in die 280px-Karte passen — die Karte ist jetzt eine Zeile über
|
||
die volle Breite, der Grund ist weg (31.07.2026). Nach unten bleibt --fs-650
|
||
die harte Grenze für Coolvetica in Versalien (CLAUDE.md), nach oben ist hier
|
||
Platz. */
|
||
font-size: var(--fs-690);
|
||
line-height: var(--lh-heading);
|
||
}
|
||
|
||
.stadionzeitung-card__meta {
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Teaser wie .veranstaltung-card__text: Lesespalte begrenzt, damit die Zeile auf
|
||
breiten Bildschirmen nicht über die halbe Seite läuft. */
|
||
.stadionzeitung-card__text {
|
||
max-width: 60ch;
|
||
margin-top: var(--space-2xs);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-card__cta {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
padding-top: var(--space-s);
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-accent);
|
||
/* KEIN margin-top: auto. Das stammte aus der hochformatigen Karte, wo der
|
||
Link an den Kartenfuß gehörte. In der breiten Zeile riss es die drei
|
||
kurzen Zeilen auseinander: Titel oben, Link unten, dazwischen 150px
|
||
Leere. Jetzt stehen sie als Gruppe zusammen, mittig zur Cover-Höhe
|
||
(justify-content: center am __body). */
|
||
}
|
||
|
||
.stadionzeitung-card__arrow {
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.stadionzeitung-card:hover .stadionzeitung-card__arrow {
|
||
transform: translateX(3px);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
/* Am Handy steht das Cover über den Angaben — nebeneinander bliebe für keines
|
||
von beidem genug Breite. Gleiche Entscheidung wie bei .veranstaltung-card. */
|
||
.stadionzeitung-card {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.stadionzeitung-card__media {
|
||
flex: none;
|
||
}
|
||
|
||
.stadionzeitung-card__body {
|
||
padding: var(--space-l);
|
||
}
|
||
}
|
||
|
||
/* === team-roster (Kader + Trainerstab) === */
|
||
.team-roster > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.team-roster__intro {
|
||
max-width: 60ch;
|
||
margin: var(--space-s) auto 0;
|
||
}
|
||
|
||
/* Positions-Zwischenüberschrift (Trainer & Betreuer, Torwart, Abwehr, …) */
|
||
.team-roster__group {
|
||
margin-top: var(--space-4xl);
|
||
padding-bottom: var(--space-2xs);
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
text-align: left;
|
||
font-size: var(--fs-700);
|
||
line-height: var(--lh-heading);
|
||
}
|
||
|
||
.team-roster__grid {
|
||
display: grid;
|
||
/* Feste 5 pro Reihe → größere Cards/Bilder; runter gestuft auf schmaleren Screens */
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: var(--space-m);
|
||
margin: var(--space-l) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
text-align: left;
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.team-roster__grid {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.team-roster__grid {
|
||
grid-template-columns: repeat(3, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 500px) {
|
||
.team-roster__grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
}
|
||
|
||
.roster-card {
|
||
display: grid;
|
||
grid-template-rows: auto 1fr;
|
||
height: 100%;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
transition: transform var(--transition), box-shadow var(--transition);
|
||
}
|
||
|
||
.roster-card:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
/* Trainerstab dezent mit Akzentkante abgesetzt */
|
||
.roster-card--staff {
|
||
border-color: var(--clr-accent);
|
||
}
|
||
|
||
.roster-card__media img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
aspect-ratio: 4 / 5;
|
||
object-fit: cover;
|
||
object-position: center top;
|
||
}
|
||
|
||
.roster-card__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-4xs);
|
||
padding: var(--space-xs) var(--space-s) var(--space-s);
|
||
background: linear-gradient(180deg, rgb(20 20 20 / 0) 0%, rgb(20 20 20 / 0.35) 100%);
|
||
}
|
||
|
||
.roster-card__role {
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
/* Name in Abel (Body) — Coolvetica wäre unter 28px unzulässig (siehe CLAUDE.md) */
|
||
.roster-card__name {
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
/* --- Variante: Name als Overlay im Foto, unten links, dunkler Verlauf von unten --- */
|
||
.roster-card--overlay {
|
||
position: relative;
|
||
display: block;
|
||
}
|
||
|
||
.roster-card--overlay .roster-card__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
aspect-ratio: 4 / 5;
|
||
object-fit: cover;
|
||
object-position: center top;
|
||
}
|
||
|
||
.roster-card__caption {
|
||
position: absolute;
|
||
inset: auto 0 0 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-4xs);
|
||
padding: var(--space-3xl) var(--space-s) var(--space-xs);
|
||
/* dunkler Fade von unten → Name bleibt auf jedem Foto lesbar */
|
||
background: linear-gradient(180deg, rgb(15 15 15 / 0) 0%, rgb(15 15 15 / 0.55) 45%, rgb(15 15 15 / 0.9) 100%);
|
||
}
|
||
|
||
/* Name hier in Coolvetica (≥28px = fs-650, Mindestgröße eingehalten) */
|
||
.roster-card--overlay .roster-card__name {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
line-height: var(--lh-heading);
|
||
text-transform: uppercase;
|
||
color: #fff;
|
||
}
|
||
|
||
.roster-card--overlay .roster-card__role {
|
||
/* Auf dem dunklen Foto-Verlauf: klein und weiß statt rot → besser lesbar */
|
||
font-size: var(--fs-300);
|
||
color: #fff;
|
||
}
|
||
|
||
/* === faq-contact (FAQ 2/3 + Ansprechpartner 1/3) === */
|
||
.faq-contact__inner {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
|
||
gap: var(--space-4xl);
|
||
align-items: start;
|
||
}
|
||
|
||
/* Ohne Kontakt: FAQ allein, lesbar zentriert. */
|
||
.faq-contact--solo .faq-contact__inner {
|
||
grid-template-columns: minmax(0, 760px);
|
||
justify-content: center;
|
||
}
|
||
|
||
/* Sektions-Kopf zentriert über dem 2-Spalten-Inhalt (FAQ + Kontakt) */
|
||
.faq-contact__title {
|
||
text-align: center;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Gedämpfte Subline unter der Überschrift (einheitlich mit den anderen Sektionen) */
|
||
.faq-contact__intro {
|
||
max-width: 60ch;
|
||
margin-inline: auto;
|
||
margin-bottom: var(--space-3xl);
|
||
text-align: center;
|
||
}
|
||
|
||
/* Abstände je nachdem, ob eine Subline folgt */
|
||
.faq-contact__title + .faq-contact__intro {
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.faq-contact__title + .faq-contact__inner {
|
||
margin-top: var(--space-3xl);
|
||
}
|
||
|
||
.faq-contact__aside > h2 {
|
||
margin-bottom: var(--space-l);
|
||
}
|
||
|
||
/* --- Ansprechpartner-Mini-Slider (mehrere Kontakte) ---
|
||
Ohne JS: alle Karten gestapelt sichtbar. Mit JS (--ready): nur eine sichtbar,
|
||
wechselt automatisch; Punkte zur manuellen Auswahl. */
|
||
.contact-slider {
|
||
/* Auf Kartenbreite begrenzt und zentriert → Karte und Punkte teilen dieselbe Mittelachse */
|
||
max-width: 320px;
|
||
margin-inline: auto;
|
||
}
|
||
|
||
.contact-slider__track {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-l);
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.contact-slider__dots {
|
||
/* Ohne JS funktionslos → ausgeblendet; erst mit aktivem Slider sichtbar. */
|
||
display: none;
|
||
}
|
||
|
||
.contact-slider--ready .contact-slider__dots {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: var(--space-2xs);
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.contact-slider__dot {
|
||
/* 24px-Ziel (WCAG 2.5.8), sichtbarer Punkt 10px */
|
||
display: grid;
|
||
place-items: center;
|
||
width: 24px;
|
||
height: 24px;
|
||
padding: 0;
|
||
background: none;
|
||
border: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.contact-slider__dot::before {
|
||
content: '';
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
border: 1px solid var(--clr-glass-border-strong);
|
||
transition: background var(--transition), border-color var(--transition);
|
||
}
|
||
|
||
.contact-slider__dot[aria-current="true"]::before {
|
||
background: var(--clr-accent);
|
||
border-color: var(--clr-accent);
|
||
}
|
||
|
||
@keyframes contact-slide-fade {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
.contact-slider--ready .contact-slider__slide.is-active {
|
||
animation: contact-slide-fade 0.4s ease;
|
||
}
|
||
|
||
.faq-contact__main .faq__list {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.faq-contact__aside .person-card {
|
||
max-width: 320px;
|
||
margin-inline: auto;
|
||
}
|
||
|
||
/* Flacheres Foto im Kontakt-Block (Gesicht bleibt oben erhalten). */
|
||
.faq-contact__aside .person-card__media img {
|
||
aspect-ratio: 4 / 3;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.faq-contact__inner {
|
||
grid-template-columns: 1fr;
|
||
gap: var(--space-3xl);
|
||
}
|
||
}
|
||
|
||
/* === team-photo (Mannschaftsfoto in voller Breite) === */
|
||
.team-photo__figure {
|
||
margin: 0;
|
||
}
|
||
|
||
.team-photo__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
border-radius: var(--radius-card);
|
||
border: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.team-photo__caption {
|
||
margin-top: var(--space-xs);
|
||
text-align: center;
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
/* === team-bento (Bild + Einleitung + Training + Spielort als Dashboard) === */
|
||
.team-bento {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: var(--space-m);
|
||
grid-template-areas:
|
||
"about about about"
|
||
"train train klasse"
|
||
"venue venue venue";
|
||
}
|
||
|
||
.team-bento__about { grid-area: about; }
|
||
.team-bento__training { grid-area: train; }
|
||
.team-bento__klasse { grid-area: klasse; }
|
||
.team-bento__venue { grid-area: venue; }
|
||
|
||
.team-bento__about {
|
||
justify-content: center;
|
||
}
|
||
|
||
.team-bento__about > h2 {
|
||
margin-bottom: var(--space-xs);
|
||
}
|
||
|
||
.team-bento__about p {
|
||
color: var(--clr-text-muted);
|
||
max-width: 60ch;
|
||
}
|
||
|
||
/* Spielort: breite untere Kachel — Adresse links, Button rechts. */
|
||
.team-bento__venue {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-m);
|
||
}
|
||
|
||
.team-bento__venue-main {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.team-bento__venue .bento__heading {
|
||
margin-bottom: var(--space-2xs);
|
||
}
|
||
|
||
.team-bento__route {
|
||
position: relative;
|
||
z-index: 1;
|
||
flex: none;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.team-bento {
|
||
grid-template-columns: 1fr;
|
||
grid-template-areas:
|
||
"about"
|
||
"train"
|
||
"klasse"
|
||
"venue";
|
||
}
|
||
}
|
||
|
||
/* === intro-bento (Jugend-Einleitung: Bild + Text + Spielort + Stat) === */
|
||
.intro-bento {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: var(--space-m);
|
||
grid-template-areas:
|
||
"image about about"
|
||
"image venue stat";
|
||
}
|
||
|
||
.intro-bento__image {
|
||
grid-area: image;
|
||
position: relative;
|
||
min-height: 260px;
|
||
padding: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.intro-bento__img {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
object-position: center top;
|
||
}
|
||
|
||
.intro-bento__about {
|
||
grid-area: about;
|
||
justify-content: center;
|
||
}
|
||
|
||
.intro-bento__about > h2 {
|
||
margin-bottom: var(--space-xs);
|
||
}
|
||
|
||
.intro-bento__about p {
|
||
max-width: 60ch;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.intro-bento__venue {
|
||
grid-area: venue;
|
||
}
|
||
|
||
.intro-bento__venue .bento__address {
|
||
margin-bottom: var(--space-m);
|
||
}
|
||
|
||
.intro-bento__route {
|
||
position: relative;
|
||
z-index: 1;
|
||
align-self: start;
|
||
margin-top: auto;
|
||
}
|
||
|
||
.intro-bento__stat {
|
||
grid-area: stat;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.intro-bento {
|
||
grid-template-columns: 1fr;
|
||
grid-template-areas:
|
||
"image"
|
||
"about"
|
||
"venue"
|
||
"stat";
|
||
}
|
||
.intro-bento__image {
|
||
aspect-ratio: 16 / 10;
|
||
min-height: 0;
|
||
}
|
||
}
|
||
|
||
/* === prose (Lesespalte für langen Fließtext) ===
|
||
Eine Zeilenlänge und eine Typografie für alle Lesetexte der Seite:
|
||
Rechtstexte (.legal__body) und News-Artikel (.news-article__body) setzen die
|
||
Klasse zusätzlich, statt die gleichen Regeln zweimal zu führen.
|
||
72ch ist ein Maß, kein Spacing-Wert, und im Projekt etabliert (vgl. die 60ch
|
||
der einspaltigen split-Sektion). */
|
||
.prose {
|
||
max-width: 72ch;
|
||
}
|
||
|
||
.prose h2 {
|
||
font-size: var(--fs-700);
|
||
margin-bottom: var(--space-s);
|
||
}
|
||
|
||
/* Coolvetica nie unter --fs-650 (28px) — Unterüberschriften daher in Abel,
|
||
per Laufweite/Uppercase ausgezeichnet statt in der Display-Schrift. */
|
||
.prose h3 {
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-600);
|
||
font-weight: var(--fw-bold);
|
||
text-transform: none;
|
||
line-height: var(--lh-body);
|
||
margin-top: var(--space-xl);
|
||
margin-bottom: var(--space-2xs);
|
||
}
|
||
|
||
.prose p,
|
||
.prose ul {
|
||
margin-bottom: var(--space-s);
|
||
}
|
||
|
||
/* Interne Deeplinks im Fließtext (inline_links_html()) — gleiche Optik wie in
|
||
den FAQ-Antworten: unterstrichen, keine Akzentfarbe (Rot erreicht auf dem
|
||
dunklen Grund keinen ausreichenden Textkontrast). */
|
||
.prose a {
|
||
color: var(--clr-text);
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.2em;
|
||
}
|
||
|
||
.prose a:hover {
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.prose ul {
|
||
padding-left: var(--space-m);
|
||
max-width: 70ch;
|
||
}
|
||
|
||
.prose li {
|
||
margin-bottom: var(--space-3xs);
|
||
}
|
||
|
||
.prose li::marker {
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
/* === legal (Impressum, Datenschutz) ===
|
||
Schmale, gut lesbare Fließtext-Seite. Top-Padding klärt den absolut
|
||
positionierten Header (analog zur Hero-Logik). */
|
||
.legal {
|
||
padding-block: calc(var(--header-h) + 80px) var(--space-section);
|
||
}
|
||
|
||
.legal__head {
|
||
max-width: 60ch;
|
||
margin-bottom: clamp(var(--space-3xl), var(--space-l) + 3vw, var(--space-5xl));
|
||
}
|
||
|
||
.legal__kicker {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-xs);
|
||
margin-bottom: var(--space-m);
|
||
font-size: var(--fs-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wider);
|
||
}
|
||
|
||
.legal__kicker::before {
|
||
content: '';
|
||
width: 3rem;
|
||
height: 4px;
|
||
background: var(--clr-accent);
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.legal__lead {
|
||
margin-top: var(--space-m);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Die Lesespalte selbst steht in .prose (siehe eigenen Block) — .legal__body
|
||
trägt die Klasse zusätzlich. Hier bleibt nur, was den Rechtstexten eigen ist:
|
||
der große Abstand zwischen ihren Paragraphen-Sektionen. */
|
||
.legal__body > section + section {
|
||
margin-top: clamp(var(--space-3xl), var(--space-xl) + 2vw, var(--space-5xl));
|
||
}
|
||
|
||
/* Adress-/Kontaktblöcke (Verantwortlicher, Bankverbindung) als abgesetzte Karte */
|
||
.legal__card {
|
||
margin-bottom: var(--space-s);
|
||
padding: clamp(var(--space-m), var(--space-s) + 1.5vw, var(--space-xl));
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.legal__card :last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.legal__card address {
|
||
font-style: normal;
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
.legal__cards {
|
||
display: grid;
|
||
gap: var(--space-s);
|
||
margin-bottom: var(--space-s);
|
||
}
|
||
|
||
@media (min-width: 600px) {
|
||
.legal__cards--2 {
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
}
|
||
}
|
||
|
||
/* === department-choice (Abteilungs-Auswahl Fußball | Turnen) === */
|
||
.department-choice > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.department-choice__intro {
|
||
max-width: 60ch;
|
||
margin: var(--space-s) auto 0;
|
||
}
|
||
|
||
.department-choice__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: var(--space-l);
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
text-align: left;
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.department-choice__grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
.department-choice__card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Bildband ist optional (Prop $item['image']) — ohne Bild bleibt die Karte reiner Text. */
|
||
.department-choice__media {
|
||
overflow: hidden;
|
||
}
|
||
|
||
.department-choice__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
aspect-ratio: 16 / 9;
|
||
object-fit: cover;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.department-choice__card:hover .department-choice__img,
|
||
.department-choice__card:focus-within .department-choice__img {
|
||
transform: scale(1.04);
|
||
}
|
||
|
||
.department-choice__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex: 1;
|
||
gap: var(--space-3xs);
|
||
padding: clamp(var(--space-xl), var(--space-m) + 2vw, var(--space-4xl));
|
||
}
|
||
|
||
.department-choice__heading {
|
||
font-size: var(--fs-700);
|
||
line-height: var(--lh-heading);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.department-choice__subtitle {
|
||
font-size: var(--fs-500);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wide);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.department-choice__text {
|
||
margin-top: var(--space-xs);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.department-choice__btn {
|
||
/* display/align-items/gap kommen jetzt aus .btn */
|
||
align-self: start;
|
||
/* Fester Abstand statt margin-top:auto — bei den Bildkarten würde "auto" den
|
||
Button an den unteren Kartenrand drücken und eine große Lücke zum Text
|
||
aufreißen (die Kartenhöhe kommt jetzt vom Bild, nicht mehr vom Textinhalt). */
|
||
margin-top: var(--space-m);
|
||
}
|
||
|
||
.department-choice__arrow {
|
||
font-size: var(--icon-inline);
|
||
}
|
||
|
||
/* === fee-table (Beitragstabellen) === */
|
||
/* Sektions-Kopf der Beitragsseite. Dieselben Werte wie .membership-signup__head
|
||
(und damit wie instagram/partners): zentriert, auf 60 Zeichen begrenzt, mit Luft
|
||
nach unten. Hier stand zuerst ein linksbündiger h2 mit einem <p class="lead">
|
||
darunter — eine Klasse, die es im Projekt gar nicht gibt. Der Absatz hatte
|
||
dadurch keinen Abstand zur Überschrift und lief über die volle Breite, also über
|
||
120 Zeichen je Zeile (Felix: „schaut weird aus", 31.07.2026).
|
||
Anmerkung fürs nächste Mal: dieses Kopf-Muster steht jetzt zum vierten Mal
|
||
fast gleich in dieser Datei. Ein gemeinsames .section__head wäre die Aufräumung,
|
||
das ist aber ein eigener Umbau über mehrere Komponenten. */
|
||
.fees__head {
|
||
max-width: 60ch;
|
||
margin: 0 auto var(--space-3xl);
|
||
text-align: center;
|
||
}
|
||
|
||
.fees__intro {
|
||
margin-top: var(--space-s);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Austritts-Hinweis unter den Tabellen: Lesespalte, aber mit eigener Überschrift.
|
||
--fs-650 ist die Untergrenze für Coolvetica in Versalien (CLAUDE.md), plus die
|
||
dezente Sperrung aus demselben Grund wie bei den Kartentiteln. */
|
||
.fees__note {
|
||
max-width: 72ch;
|
||
margin-top: var(--space-3xl);
|
||
}
|
||
|
||
.fees__note-title {
|
||
font-size: var(--fs-650);
|
||
letter-spacing: var(--ls-heading);
|
||
line-height: var(--lh-heading);
|
||
margin-bottom: var(--space-s);
|
||
}
|
||
|
||
/* Mehrere Tabellen nebeneinander (Turnen hat drei: Zusatzbeitrag, Neumitglieder,
|
||
Förderstufen). auto-fit mit 22rem Mindestbreite statt einer festen Spaltenzahl:
|
||
bei drei Tabellen ergibt das auf Desktop eine Reihe, am Handy eine Spalte, und
|
||
es bleibt richtig, wenn eine vierte Tabelle dazukommt. /mitglied-werden gibt
|
||
nur EINE Tabelle aus und braucht den Wrapper nicht. */
|
||
.fee-tables {
|
||
display: grid;
|
||
/* min(22rem, 100%) statt nur 22rem: die reine Mindestbreite kann der Spur nicht
|
||
unterschritten werden, dadurch waren die Karten am Handy 352px breit in einem
|
||
347px-Raster und liefen über (gemessen bei 390px Viewport). */
|
||
grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
|
||
gap: var(--space-l);
|
||
margin-top: var(--space-2xl);
|
||
/* KEIN align-items: start. Mit `start` war jede Karte nur so hoch wie ihr Inhalt
|
||
(252 / 377 / 471px gemessen) und die Reihe sah zerrupft aus. Der Grid-Default
|
||
`stretch` macht sie gleich hoch; damit die gewonnene Höhe nicht als Loch
|
||
endet, schiebt .fee-table__note sich an den Kartenfuß (siehe unten). */
|
||
}
|
||
|
||
.fee-table {
|
||
/* Flex-Spalte, damit __note per margin-top: auto an den Kartenfuß rutscht.
|
||
Ohne Höhenüberschuss (Einzeltabelle auf /mitglied-werden) ändert das nichts. */
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: clamp(var(--space-m), var(--space-s) + 1.5vw, var(--space-xl));
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.fee-table__title {
|
||
/* --fs-675 statt --fs-650: bei 28px wirkte der Titel neben dem 21px-Fließtext
|
||
zu klein, weil Coolvetica ein schmaler Schnitt ist (Felix, 31.07.2026).
|
||
34px ist die nächste Stufe; --fs-690 (bis 44px) würde die langen Titel wie
|
||
„Zusatzbeitrag für bestehende TSV-Mitglieder" in drei Zeilen brechen. */
|
||
font-size: var(--fs-675);
|
||
/* Coolvetica ist ein Condensed-Schnitt: bei 60px (Abschnitts-h2) trägt er ohne
|
||
Sperrung, bei 34px in einer schmalen Kartenspalte klebten die Versalien und
|
||
„ZUSATZBEITRAG FÜR BESTEHENDE TSV-MITGLIEDER" war schwer zu lesen. Gemessen war
|
||
letter-spacing vorher `normal`, also gar keins — nicht zu eng, sondern nicht
|
||
gesetzt. --ls-heading (0,4px bei 34px), NICHT --ls-caps: mit 0.08em wirkte die
|
||
Überschrift auseinandergezogen (Felix, 31.07.2026). */
|
||
letter-spacing: var(--ls-heading);
|
||
line-height: var(--lh-heading);
|
||
margin-bottom: var(--space-s);
|
||
}
|
||
|
||
.fee-table__list {
|
||
margin: 0;
|
||
}
|
||
|
||
.fee-table__row {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
padding: var(--space-xs) 0;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.fee-table__row:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.fee-table__label {
|
||
margin: 0;
|
||
}
|
||
|
||
.fee-table__price {
|
||
margin: 0;
|
||
flex: none;
|
||
font-family: var(--ff-heading);
|
||
/* --fs-675 (34px) statt --fs-650: der Preis ist die Information, auf die man in
|
||
einer Beitragstabelle schaut, und trägt hier allein die Hervorhebung, seit die
|
||
Farbe weg ist. Gleiche Stufe wie der Kartentitel, aber an anderer Position —
|
||
sie konkurrieren nicht. */
|
||
font-size: var(--fs-675);
|
||
line-height: 1;
|
||
/* WEISS, nicht Akzentrot. Rot auf dem Kartengrund erreicht nur 3,50:1 und
|
||
verfehlt damit die 4,5:1 für Fließtext — und ein Beitrag ist Information, die
|
||
man lesen muss, kein Akzent. CLAUDE.md sagt es als Regel: „Rot nie für
|
||
Fließtext auf dunklem Grund, nur als Button-/Akzent-Fläche mit weißem Text."
|
||
Weiß liegt bei 17,22:1. Hervorgehoben bleibt der Preis durch Coolvetica und
|
||
28px gegen 21px Fließtext, das reicht ohne Farbe. */
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.fee-table__note {
|
||
margin-top: auto;
|
||
padding-top: var(--space-s);
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Mehrere Beitragstabellen + Austritts-Hinweis gestapelt (Turn-Übersicht). */
|
||
.fee-stack {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-l);
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
/* === membership (Mitglied werden) === */
|
||
.membership-signup__inner {
|
||
max-width: 720px;
|
||
margin-inline: auto;
|
||
}
|
||
|
||
/* Zentrierter, ruhiger Sektions-Kopf (einheitlich mit instagram/partners) */
|
||
.membership-signup__head {
|
||
max-width: 60ch;
|
||
margin: 0 auto var(--space-3xl);
|
||
text-align: center;
|
||
}
|
||
|
||
.membership-signup__intro {
|
||
margin-top: var(--space-s);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Eine einzige Karte: Beitragsinfo + Formular zusammen, durch Hairline getrennt. */
|
||
.membership-card {
|
||
padding: clamp(var(--space-l), var(--space-s) + 2.5vw, var(--space-3xl));
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
/* Verschachtelte Bausteine verlieren ihre eigene Karten-Optik (eine Karte, ein Rahmen).
|
||
box-shadow/backdrop-filter gehören mit rein: .form bekommt weiter unten global
|
||
--glass-shadow (Drop-Shadow + Top-Sheen-Highlight) + Blur — ohne Reset bleibt das
|
||
auf dem jetzt randlosen <form> hängen und wirft einen sichtbaren Schatten/Glow
|
||
genau an der Naht zur Beitragstabelle, obwohl gar kein eigener Rahmen mehr da ist. */
|
||
.membership-card .fee-table,
|
||
.membership-card .form {
|
||
max-width: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
background: none;
|
||
border: 0;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
backdrop-filter: none;
|
||
-webkit-backdrop-filter: none;
|
||
}
|
||
|
||
.membership-card .fee-table {
|
||
padding-bottom: var(--space-xl);
|
||
margin-bottom: var(--space-xl);
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
/* Turnen-Hinweise: ruhige Liste, Icon links, dezente Hairline als Trenner. */
|
||
.membership-notes {
|
||
margin: 0 0 var(--space-3xl);
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.membership-notes__item {
|
||
display: flex;
|
||
gap: var(--space-s);
|
||
padding: var(--space-m) 0;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.membership-notes__item:first-child {
|
||
padding-top: 0;
|
||
}
|
||
|
||
.membership-notes__item:last-child {
|
||
padding-bottom: 0;
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.membership-notes__icon {
|
||
flex: none;
|
||
margin-top: var(--space-4xs);
|
||
font-size: var(--icon-l);
|
||
line-height: 1;
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.membership-notes__title {
|
||
/* --fs-650 (28px, die Coolvetica-Untergrenze aus CLAUDE.md) wirkte hier neben
|
||
dem --icon-l-Icon zu klein. --fs-675 (34px) wie sponsor-arguments__title —
|
||
dasselbe Icon+Titel+Text-Listenmuster, dort bewährt. */
|
||
font-size: var(--fs-675);
|
||
line-height: var(--lh-heading);
|
||
margin-bottom: var(--space-3xs);
|
||
}
|
||
|
||
.membership-notes__body p {
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Download-Block: alles sauber mittig, Texte in lesbarer Breite. */
|
||
.membership-download {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: var(--space-xs);
|
||
text-align: center;
|
||
}
|
||
|
||
.membership-download__btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
}
|
||
|
||
.membership-download__icon {
|
||
font-size: var(--icon-inline);
|
||
}
|
||
|
||
.membership-download__hint,
|
||
.membership-download__contact {
|
||
margin: 0;
|
||
max-width: 48ch;
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.legal__muted {
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* === facility (Gelände / Drohnen-Luftbild auf der Vereinsseite) === */
|
||
.facility > .container {
|
||
text-align: center;
|
||
}
|
||
|
||
.facility__lead {
|
||
max-width: 70ch;
|
||
margin-top: var(--space-s);
|
||
margin-inline: auto;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.facility__figure {
|
||
margin: var(--space-3xl) 0 0;
|
||
}
|
||
|
||
.facility__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
border-radius: var(--radius-card);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.facility__caption {
|
||
margin-top: var(--space-xs);
|
||
color: var(--clr-text-faint);
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
.facility__actions {
|
||
margin-top: var(--space-xl);
|
||
}
|
||
|
||
/* === matchcenter === */
|
||
/* Vereinswappen — gemeinsam für Karten und Tabelle; fehlt das Bild, rendert die
|
||
Komponente einen Initialen-Kreis (.crest--initials) statt eines kaputten <img>. */
|
||
/* Weißes Chip hält auch helle/weiße Logos auf dunklem Grund sichtbar. */
|
||
/* Größe läuft über --crest-size: Kontexte setzen nur diese eine Variable, Breite,
|
||
Höhe und die Initialen-Schriftgröße folgen daraus. .crest--initials trägt immer
|
||
auch .crest, deshalb genügt überall der Basis-Selektor. */
|
||
.crest {
|
||
--crest-size: 2.25rem;
|
||
box-sizing: border-box;
|
||
flex: none;
|
||
width: var(--crest-size);
|
||
height: var(--crest-size);
|
||
padding: 3px;
|
||
object-fit: contain;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.crest--initials {
|
||
display: inline-grid;
|
||
place-items: center;
|
||
padding: 0;
|
||
font-family: var(--ff-body);
|
||
/* Initialen als Anteil des Durchmessers statt als eigene Stufe der Typo-Skala:
|
||
so bleibt der Kreis in jeder Größe gleich proportioniert, und es entstehen
|
||
keine Mini-Schriftgrößen unterhalb von --fs-300. */
|
||
font-size: calc(var(--crest-size) * 0.4);
|
||
font-weight: var(--fw-bold);
|
||
line-height: 1;
|
||
color: var(--clr-text-muted);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: 50%;
|
||
}
|
||
|
||
/* Aufmacher des „Nächste Spiele"-Bands: strukturgleich mit .match-row (Heim über
|
||
Gast, linksbündig), nur groß — ein Bauprinzip für das ganze Band. Bewusst ohne
|
||
eigene Fläche, ohne Badges und ohne Icons: die Wirkung kommt aus der Größe.
|
||
Drei Schriftrollen — Meta (Abel gesperrt) · Namen (Coolvetica) · Anstoß (Abel). */
|
||
.match-feature {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: clamp(var(--space-s), var(--space-xs) + 0.6vw, var(--space-l));
|
||
}
|
||
|
||
.match-feature__meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: 0.5em;
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.match-feature__sep {
|
||
color: var(--clr-text-faint);
|
||
}
|
||
|
||
.match-feature__fixture {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-2xs);
|
||
}
|
||
|
||
.match-feature__side {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: clamp(var(--space-2xs), var(--space-2xs) + 0.6vw, var(--space-s));
|
||
min-width: 0; /* lange Vereinsnamen dürfen umbrechen statt zu überlaufen */
|
||
}
|
||
|
||
.match-feature .crest {
|
||
--crest-size: clamp(2.25rem, 1.6rem + 2vw, 3rem);
|
||
}
|
||
|
||
/* Die Vereinsnamen sind das einzige Coolvetica-Element („wer"), alles Beschreibende
|
||
bleibt Abel („wann/was"). Coolvetica hat nur einen Schnitt — die eigene Mannschaft
|
||
wird deshalb über Helligkeit markiert, nicht über fett (kein Fake-Bold). */
|
||
/* Vereinszeilen sind IMMER einzeilig (Felix, 30.07.2026): lange (SG-)Namen
|
||
werden mit Auslassungspunkten gekürzt statt hässlich umzubrechen — der
|
||
volle Name steht im Matchcenter bzw. auf der verlinkten Seite. */
|
||
.match-feature__club {
|
||
min-width: 0;
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-700);
|
||
line-height: var(--lh-heading);
|
||
text-transform: uppercase;
|
||
color: var(--clr-text-muted);
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.match-feature__side.is-own .match-feature__club {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.match-feature__when {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: 0.5em;
|
||
font-size: var(--fs-600);
|
||
}
|
||
|
||
/* === event-feature (Termin-Aufmacher) === */
|
||
/* Gegenstück zu .match-feature für alles, was kein Spiel ist (Vereins-Events).
|
||
Gleiches Bauprinzip und dieselben drei Schriftrollen — statt des Fixtures mit
|
||
Wappen steht links das Datums-Chip (wie in der .termine-list, damit ein Termin
|
||
überall gleich aussieht). */
|
||
.event-feature {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: clamp(var(--space-s), var(--space-xs) + 0.6vw, var(--space-l));
|
||
}
|
||
|
||
.event-feature__head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: clamp(var(--space-2xs), var(--space-2xs) + 0.6vw, var(--space-s));
|
||
min-width: 0;
|
||
}
|
||
|
||
.event-feature__chip {
|
||
flex: 0 0 auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 56px;
|
||
padding: var(--space-3xs) var(--space-2xs);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-btn);
|
||
line-height: 1.15;
|
||
}
|
||
|
||
.event-feature__chip-day {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
}
|
||
|
||
.event-feature__chip-month {
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.event-feature__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-4xs);
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Der Titel ist das einzige Coolvetica-Element („was"), alles Beschreibende
|
||
bleibt Abel („wann/wo") — dieselbe Rollenverteilung wie bei .match-feature. */
|
||
.event-feature__title {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-700);
|
||
line-height: var(--lh-heading);
|
||
text-transform: uppercase;
|
||
overflow-wrap: anywhere; text-wrap: balance;
|
||
}
|
||
|
||
.event-feature__where {
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.event-feature__when {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: 0.5em;
|
||
font-size: var(--fs-600);
|
||
}
|
||
|
||
.event-feature__sep {
|
||
color: var(--clr-text-faint);
|
||
}
|
||
|
||
/* Band „Nächste Spiele" — abgesetzte Fläche (.section--sunken) mit roter Oberkante,
|
||
damit der Bereich nicht als Hero-Überhang gelesen wird. */
|
||
.next-matches {
|
||
border-top: 2px solid var(--clr-accent);
|
||
}
|
||
|
||
.next-matches__grid {
|
||
display: grid;
|
||
/* Der Aufmacher trägt mehr Gewicht als die Terminliste. */
|
||
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
|
||
align-items: start;
|
||
gap: clamp(var(--space-m), var(--space-s) + 1.5vw, var(--space-3xl));
|
||
margin-top: clamp(var(--space-l), var(--space-m) + 1vw, var(--space-3xl));
|
||
}
|
||
|
||
.next-matches__list-title {
|
||
margin-bottom: var(--space-xs);
|
||
/* Coolvetica wäre in dieser Größe unleserlich — Abel mit Sperrung (CLAUDE.md). */
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.next-matches__grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* Pro-Mannschaft-Sektion: Kopf mit Name/Liga + Link zur Mannschaftsseite. */
|
||
.matchcenter-team__head {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: var(--space-s);
|
||
margin-bottom: var(--space-l);
|
||
}
|
||
|
||
.matchcenter-team__league {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.4em;
|
||
margin-top: var(--space-3xs);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Saison-Umschalter: wiederverwendete Stripe-Tabbar (group-tabs__bar/__tab), aber
|
||
linksbündig und etwas kompakter als bei den Gruppen-Tabs. Ohne JS sind beide
|
||
Panels gestapelt sichtbar (group-tabs.js versteckt erst on init das zweite). */
|
||
.matchcenter-team__seasons-bar {
|
||
justify-content: flex-start;
|
||
margin-bottom: var(--space-m);
|
||
}
|
||
|
||
/* display-neutrales Panel (kein group-tabs__panel-Grid) — das innere
|
||
matchcenter-team__grid bringt sein eigenes Layout mit. */
|
||
.matchcenter-team__season-panel[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
.matchcenter-team__empty {
|
||
margin: 0;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* kicker-Layout: Tabelle 2/3 links, Spiele 1/3 rechts; gestapelt auf Mobil.
|
||
align-items:start + 0-Margins der Karten → beide Spalten starten oben bündig. */
|
||
.matchcenter-team__grid {
|
||
display: grid;
|
||
grid-template-columns: 2fr 1fr;
|
||
gap: clamp(var(--space-l), var(--space-s) + 2vw, var(--space-4xl));
|
||
align-items: start;
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.matchcenter-team__grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
/* min-width:0 erlaubt der Tabellenspalte, schmaler als die min-width:520 der
|
||
Tabelle zu werden — sonst schiebt die Tabelle die ganze Seite in den
|
||
horizontalen Scroll, statt selbst in ihrer Karte zu scrollen. */
|
||
.matchcenter-team__grid > * {
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Mehrere Listen (Nächste Spiele / Letzte Ergebnisse) gleichmäßig gestapelt.
|
||
max-width hält die kompakten Zeilen gestapelt (≤992px) lesbar schmal, statt sie
|
||
über die volle Breite zu ziehen; im 2-spaltigen Desktop greift es nicht (die
|
||
1fr-Spalte ist ohnehin schmaler). */
|
||
.matchcenter-team__aside {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-s);
|
||
max-width: 30rem;
|
||
}
|
||
|
||
/* Kompakte Spielliste (Seitenspalte) — gleiche Karten-Optik wie die Tabelle. */
|
||
.match-list {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
/* Im „Nächste Spiele"-Band trägt die Terminliste keine Kartenfläche — sonst stünde
|
||
ein gerahmter Block neben dem flächenlosen Aufmacher. Die Trennlinien der Zeilen
|
||
reichen; in den Mannschafts-Sektionen bleibt .match-list die Karte.
|
||
Zwei Klassen nötig: die Glas-Registry am Dateiende setzt .match-list den
|
||
box-shadow und würde einen Ein-Klassen-Selektor sonst überschreiben. */
|
||
.match-list.match-list--plain {
|
||
background: none;
|
||
border: 0;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.match-list--plain .match-row__link {
|
||
padding-inline: 0;
|
||
}
|
||
|
||
.match-row {
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.match-row:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
/* Raster und Innenabstand liegen im Wrapper: ist die Zeile ein Link, ist damit
|
||
die komplette Zeilenfläche klickbar — ohne Link sieht nichts anders aus. */
|
||
.match-row__link {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
align-items: center;
|
||
column-gap: var(--space-xs);
|
||
padding: var(--space-xs) var(--space-s);
|
||
color: inherit;
|
||
text-decoration: none;
|
||
}
|
||
|
||
a.match-row__link:hover {
|
||
background: rgb(255 255 255 / 0.06);
|
||
}
|
||
|
||
.match-row__head {
|
||
grid-column: 1 / -1;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: baseline;
|
||
gap: var(--space-2xs);
|
||
margin-bottom: var(--space-2xs);
|
||
}
|
||
|
||
.match-row__date {
|
||
font-size: var(--fs-300);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.match-row__meta {
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-faint);
|
||
}
|
||
|
||
.match-row__teams {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-3xs);
|
||
min-width: 0; /* erlaubt Ellipsis in den Namen */
|
||
}
|
||
|
||
.match-row__team {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
min-width: 0;
|
||
}
|
||
|
||
.match-row__team .crest {
|
||
--crest-size: 1.6rem;
|
||
}
|
||
|
||
.match-row__name {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
.match-row__team.is-own .match-row__name {
|
||
font-weight: var(--fw-bold);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.match-row__outcome {
|
||
align-self: center;
|
||
}
|
||
|
||
.match-row__score {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
line-height: 1;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.match-row__time {
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Liga-Tabelle: horizontal scrollbar (fokussierbare Region) auf schmalen Screens. */
|
||
.table-scroll {
|
||
overflow-x: auto;
|
||
-webkit-overflow-scrolling: touch;
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
/* Ruhender Rahmen um den Scroller: trägt den Wisch-Hinweis (Fade am rechten
|
||
Rand), der nicht mitscrollen darf. Nur auf Breiten sichtbar, wo die Tabelle
|
||
(min-width 520px) tatsächlich überläuft. */
|
||
.table-scroll-frame {
|
||
position: relative;
|
||
}
|
||
|
||
@media (max-width: 620px) {
|
||
.table-scroll-frame::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 1px 1px 1px auto;
|
||
width: 32px;
|
||
border-radius: 0 var(--radius-card) var(--radius-card) 0;
|
||
background: linear-gradient(90deg, transparent, rgb(0 0 0 / 0.55));
|
||
pointer-events: none;
|
||
}
|
||
}
|
||
|
||
.league-table {
|
||
width: 100%;
|
||
min-width: 520px;
|
||
border-collapse: collapse;
|
||
background: var(--clr-glass-bg);
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
.league-table th,
|
||
.league-table td {
|
||
padding: var(--space-2xs) var(--space-2xs);
|
||
text-align: center;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.league-table tbody tr:last-child th,
|
||
.league-table tbody tr:last-child td {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
/* Spaltenköpfe in Abel (Coolvetica wäre hier zu klein, < 28px). */
|
||
.league-table thead th {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-tight);
|
||
color: var(--clr-text-muted);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.league-table thead th abbr {
|
||
text-decoration: none;
|
||
}
|
||
|
||
.league-table__rank {
|
||
width: 2.75rem;
|
||
border-left: 3px solid transparent;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Vereinsspalte (Kopf + Zellen) linksbündig — höhere Spezifität als .league-table th. */
|
||
.league-table .league-table__club {
|
||
text-align: left;
|
||
}
|
||
|
||
.league-table tbody th.league-table__club {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
min-width: 0; /* erlaubt Ellipsis im Namen */
|
||
font-weight: var(--fw-regular);
|
||
}
|
||
|
||
.league-table tbody .crest {
|
||
--crest-size: 1.75rem;
|
||
}
|
||
|
||
/* Lange Vereinsnamen sauber kürzen (voller Name im title-Attribut). */
|
||
.league-table__name {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
line-height: 1.2;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.league-table__pts {
|
||
font-weight: var(--fw-bold);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* Eigene Vereinszeile hervorheben (rote Tönung hinter weißem Text). */
|
||
.league-table tbody tr.is-own {
|
||
background: rgb(var(--clr-accent-rgb) / 0.16);
|
||
}
|
||
|
||
.league-table tbody tr.is-own th,
|
||
.league-table tbody tr.is-own td {
|
||
font-weight: var(--fw-bold);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* Auf-/Abstiegszonen: farbiger linker Rand am Rang. */
|
||
.league-table tbody tr.zone-promotion .league-table__rank {
|
||
border-left-color: var(--clr-success);
|
||
}
|
||
|
||
.league-table tbody tr.zone-promotion-playoff .league-table__rank {
|
||
border-left-color: color-mix(in srgb, var(--clr-success) 60%, var(--clr-text-faint));
|
||
}
|
||
|
||
.league-table tbody tr.zone-relegation-playoff .league-table__rank {
|
||
border-left-color: color-mix(in srgb, var(--clr-error) 60%, var(--clr-text-faint));
|
||
}
|
||
|
||
.league-table tbody tr.zone-relegation .league-table__rank {
|
||
border-left-color: var(--clr-error);
|
||
}
|
||
|
||
.league-table__legend {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--space-2xs) var(--space-m);
|
||
margin: var(--space-xs) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
font-size: var(--fs-300);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.league-table__legend-item {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.45em;
|
||
}
|
||
|
||
.league-table__legend-item::before {
|
||
content: "";
|
||
width: 0.85rem;
|
||
height: 0.85rem;
|
||
border-radius: 3px;
|
||
background: var(--clr-text-faint);
|
||
}
|
||
|
||
.league-table__legend-item.zone-promotion::before {
|
||
background: var(--clr-success);
|
||
}
|
||
|
||
.league-table__legend-item.zone-promotion-playoff::before {
|
||
background: color-mix(in srgb, var(--clr-success) 60%, var(--clr-text-faint));
|
||
}
|
||
|
||
.league-table__legend-item.zone-relegation-playoff::before {
|
||
background: color-mix(in srgb, var(--clr-error) 60%, var(--clr-text-faint));
|
||
}
|
||
|
||
.league-table__legend-item.zone-relegation::before {
|
||
background: var(--clr-error);
|
||
}
|
||
|
||
/* Fallback ohne Cache. */
|
||
.matchcenter-empty {
|
||
max-width: 540px;
|
||
margin-inline: auto;
|
||
padding: clamp(var(--space-l), var(--space-s) + 2vw, var(--space-3xl));
|
||
text-align: center;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.matchcenter-empty__icon {
|
||
font-size: var(--icon-xl);
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.matchcenter-empty p {
|
||
margin-top: var(--space-xs);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.matchcenter-empty__actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
gap: var(--space-xs);
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
/* === sportheim === */
|
||
|
||
/* Bento: großes Foto links (2 Spalten × 2 Zeilen),
|
||
kleines Foto + roter CTA oben rechts, Karte + Adresse unten rechts */
|
||
.sportheim-bento {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
grid-auto-rows: 220px;
|
||
gap: var(--space-m);
|
||
grid-template-areas:
|
||
"p1 p1 p2 cta"
|
||
"p1 p1 map adr";
|
||
}
|
||
|
||
/* --- Foto-Kacheln --- */
|
||
.sportheim-bento__photo {
|
||
position: relative;
|
||
overflow: hidden;
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.sportheim-bento__photo img {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
transition: transform 600ms ease;
|
||
}
|
||
|
||
.sportheim-bento__photo:hover img {
|
||
transform: scale(1.04);
|
||
}
|
||
|
||
.sportheim-bento__photo--main { grid-area: p1; }
|
||
.sportheim-bento__photo--sec { grid-area: p2; }
|
||
|
||
/* --- Karten-Kachel --- */
|
||
.sportheim-bento__map {
|
||
grid-area: map;
|
||
position: relative;
|
||
overflow: hidden;
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.sportheim-bento__map img {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
display: block;
|
||
}
|
||
|
||
/* --- Adress-Kachel --- */
|
||
.sportheim-bento__addr {
|
||
grid-area: adr;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
padding: var(--space-l);
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
font-style: normal;
|
||
}
|
||
|
||
.sportheim-bento__addr-label {
|
||
font-size: var(--fs-300);
|
||
font-weight: var(--fw-bold);
|
||
letter-spacing: var(--ls-wide);
|
||
text-transform: uppercase;
|
||
color: var(--clr-text-muted);
|
||
margin: 0 0 var(--space-xs);
|
||
}
|
||
|
||
.sportheim-bento__addr-text {
|
||
font-size: var(--fs-500);
|
||
line-height: 1.6;
|
||
margin: 0;
|
||
}
|
||
|
||
/* --- CTA-Kachel (rot, volle Höhe) --- */
|
||
.sportheim-bento__cta {
|
||
grid-area: cta;
|
||
background: var(--clr-accent);
|
||
border-radius: var(--radius-card);
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: var(--space-l);
|
||
text-align: center;
|
||
padding: var(--space-2xl) var(--space-m);
|
||
}
|
||
|
||
.sportheim-bento__cta-title {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
text-transform: uppercase;
|
||
line-height: var(--lh-heading);
|
||
color: #fff;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Tablet: 2-spaltig, Foto groß oben, darunter Kleinzeug */
|
||
@media (max-width: 900px) {
|
||
.sportheim-bento {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
grid-auto-rows: 200px;
|
||
grid-template-areas:
|
||
"p1 p1"
|
||
"p1 p1"
|
||
"p2 cta"
|
||
"map adr";
|
||
}
|
||
}
|
||
|
||
/* Mobile: alles gestapelt */
|
||
@media (max-width: 500px) {
|
||
.sportheim-bento {
|
||
grid-template-columns: 1fr;
|
||
grid-auto-rows: 220px;
|
||
grid-template-areas:
|
||
"p1"
|
||
"p2"
|
||
"map"
|
||
"cta"
|
||
"adr";
|
||
}
|
||
|
||
.sportheim-bento__addr,
|
||
.sportheim-bento__cta {
|
||
grid-row: auto;
|
||
height: auto;
|
||
min-height: 140px;
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
FROSTED GLASS — globaler Look für alle Glas-Oberflächen.
|
||
EINZIGE Stelle für Blur + Ruhe-Glow (DRY statt 28× wiederholt).
|
||
Neue Glas-Karte? Hier zur passenden Liste ergänzen:
|
||
• Leaf = eigenständige Karte → bekommt Blur + Glow
|
||
• Container = umschließt selbst Glas-Karten → nur Glow
|
||
(kein Blur, sonst Doppel-Frost bei verschachtelten Karten)
|
||
Bewusst ausgenommen: Form-Inputs, Wappen-Initialen, Banner-/Slider-Controls.
|
||
Glas-Bg/-Border kommen weiterhin aus den Komponenten (var(--clr-glass-*)).
|
||
============================================================ */
|
||
|
||
/* Ruhe-Glow + Top-Sheen für ALLE Glas-Flächen (Leaf + Container).
|
||
Hover-Lift bleibt pro Komponente erhalten (:hover hat höhere Spezifität). */
|
||
.instagram__fallback,
|
||
.sponsor-channels__card,
|
||
.person-card,
|
||
.position-card,
|
||
.partners__item,
|
||
.key-facts__item,
|
||
.bento__tile,
|
||
.group-card,
|
||
.group-tabs__panel,
|
||
.age-group,
|
||
.match-list,
|
||
.matchcenter-empty,
|
||
.roster-card,
|
||
.team-card,
|
||
.stadionzeitung-card,
|
||
.department-choice__card,
|
||
.legal__card,
|
||
.fee-table,
|
||
.league-table,
|
||
.membership-card,
|
||
.sportheim-bento__addr,
|
||
.form {
|
||
box-shadow: var(--glass-shadow);
|
||
}
|
||
|
||
/* Echtes Frosted-Glass-Blur nur auf Leaf-Karten — Container (.match-list,
|
||
.group-tabs__panel, .age-group, Tabellen) bleiben blur-frei, damit darin
|
||
liegende Glas-Karten nicht doppelt verschwimmen. */
|
||
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
|
||
.instagram__fallback,
|
||
.sponsor-channels__card,
|
||
.person-card,
|
||
.position-card,
|
||
.partners__item,
|
||
.key-facts__item,
|
||
.bento__tile,
|
||
.group-card,
|
||
.matchcenter-empty,
|
||
.hero__upcoming,
|
||
.roster-card,
|
||
.team-card,
|
||
.stadionzeitung-card,
|
||
.department-choice__card,
|
||
.legal__card,
|
||
.membership-card,
|
||
.sportheim-bento__addr,
|
||
.form {
|
||
-webkit-backdrop-filter: var(--glass-blur);
|
||
backdrop-filter: var(--glass-blur);
|
||
}
|
||
}
|
||
|
||
/* === stadionzeitung-issue-head (schmaler Kopf statt hero, Ausgaben-Seite) === */
|
||
/* --header-space, nicht --header-h: die Leiste nimmt 92px weg, nicht 60px
|
||
(siehe tokens.css). Werte so gewählt, dass sich am Desktop nichts verschiebt. */
|
||
.stadionzeitung-issue-head {
|
||
/* Deutlich schlanker als ein voller Hero (dessen +80px/+64px), da der
|
||
Viewer darunter den Platz braucht. */
|
||
padding-block: calc(var(--header-space) + var(--space-s)) var(--space-l);
|
||
}
|
||
|
||
/* Die Druckfassung darf großzügiger sein: sie scrollt als Blatt-Vorschau und
|
||
braucht die Bildschirmhöhe nicht wie der Swipe-Viewer. */
|
||
.stadionzeitung-issue-head--druck {
|
||
padding-block: calc(var(--header-space) + var(--space-2xl)) var(--space-xl);
|
||
}
|
||
|
||
/* Drei feste Zonen und zwei Reihen: Rückweg links, Titel mittig über den Seiten
|
||
(wie der Viewer darunter), Datum bzw. Drucken-Knopf rechts — darunter über die
|
||
volle Breite die Statuszeile. */
|
||
.stadionzeitung-issue-head__inner {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto 1fr;
|
||
align-items: baseline;
|
||
gap: var(--space-2xs) var(--space-l);
|
||
}
|
||
|
||
.stadionzeitung-issue-head__back {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-3xs);
|
||
justify-self: start;
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-500);
|
||
text-decoration: none;
|
||
}
|
||
|
||
.stadionzeitung-issue-head__back:hover {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* Größer als --fs-650, weil Coolvetica condensed ist: 28px Versalien wirken hier
|
||
neben der Navigation kleiner als sie sind und der Titel verlor gegen die
|
||
Badges, die früher daneben standen. */
|
||
.stadionzeitung-issue-head__title {
|
||
grid-column: 2;
|
||
grid-row: 1;
|
||
justify-self: center;
|
||
text-align: center;
|
||
font-size: var(--fs-675);
|
||
font-family: var(--ff-heading);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
/* Badges neben dem Titel: „Druckfassung" (neutral) kennzeichnet die
|
||
?druck=1-Ansicht, „Entwurf" (rot umrandet) unveröffentlichte Ausgaben —
|
||
ein Entwurf darf nie mit einer fertigen Ausgabe verwechselt werden. Abel
|
||
statt Coolvetica: unter --fs-650 (siehe CLAUDE.md). */
|
||
.stadionzeitung-issue-head__badge {
|
||
display: inline-block;
|
||
padding: var(--space-4xs) var(--space-xs);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-300);
|
||
letter-spacing: var(--ls-caps);
|
||
vertical-align: middle;
|
||
color: var(--clr-text-muted);
|
||
border: 1px solid var(--clr-glass-border-strong);
|
||
border-radius: var(--radius-btn);
|
||
}
|
||
|
||
.stadionzeitung-issue-head__badge--entwurf {
|
||
color: var(--clr-text);
|
||
border-color: rgb(var(--clr-accent-rgb) / 0.6);
|
||
}
|
||
|
||
/* Rechte Zone der Druckfassung: nur die eine Aktion. Bewusst KEIN Aufklapp-Menü —
|
||
es gibt genau einen Knopf, ein Menü würde die Hauptaktion hinter einen Klick
|
||
legen und bräuchte Zustand, den die Seite sonst nirgends hat. */
|
||
.stadionzeitung-issue-head__actions {
|
||
grid-column: 3;
|
||
grid-row: 1;
|
||
justify-self: end;
|
||
font-family: var(--ff-body);
|
||
text-transform: none;
|
||
}
|
||
|
||
/* Statuszeile unter dem Titel, über die volle Breite zentriert: Zustand und
|
||
Bilanz sind Information, keine Überschrift — deshalb hier und nicht mehr als
|
||
Badges neben dem Titel. */
|
||
.stadionzeitung-issue-head__state {
|
||
grid-column: 1 / -1;
|
||
grid-row: 2;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: var(--space-2xs);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-400);
|
||
letter-spacing: var(--ls-caps);
|
||
text-transform: uppercase;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-issue-head__date {
|
||
grid-column: 3;
|
||
justify-self: end;
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-500);
|
||
text-transform: none;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Drei Zonen nebeneinander brauchen Platz — darunter gestapelt: Archiv oben
|
||
links, Titel + Datum darunter mittig. */
|
||
@media (max-width: 768px) {
|
||
.stadionzeitung-issue-head__inner {
|
||
grid-template-columns: 1fr;
|
||
justify-items: center;
|
||
text-align: center;
|
||
}
|
||
|
||
.stadionzeitung-issue-head__back {
|
||
justify-self: start;
|
||
}
|
||
|
||
.stadionzeitung-issue-head__title,
|
||
.stadionzeitung-issue-head__date,
|
||
.stadionzeitung-issue-head__actions,
|
||
.stadionzeitung-issue-head__state {
|
||
grid-column: 1;
|
||
grid-row: auto;
|
||
justify-self: center;
|
||
}
|
||
}
|
||
|
||
/* === stadionzeitung-viewer (Swipe-Ansicht einer Ausgabe) === */
|
||
/* Ohne JS: ganz normale gestapelte Liste, jede Seite ihr eigenes Bild. */
|
||
.stadionzeitung-viewer {
|
||
padding-block: var(--space-m) var(--space-4xl);
|
||
}
|
||
|
||
.stadionzeitung-viewer__track {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-m);
|
||
margin: 0;
|
||
padding: 0 var(--container-pad);
|
||
list-style: none;
|
||
}
|
||
|
||
.stadionzeitung-page__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
/* Ein bedrucktes Blatt hat keine runden Ecken. */
|
||
box-shadow: 0 6px 24px rgb(0 0 0 / 0.35);
|
||
}
|
||
|
||
.stadionzeitung-viewer__controls {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: var(--space-m);
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
.stadionzeitung-viewer__controls[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
.stadionzeitung-viewer__counter {
|
||
min-width: 9ch;
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text-muted);
|
||
text-align: center;
|
||
}
|
||
|
||
/* Glas-Optik wie .ad-banner__nav-btn — dieselbe Steuerungs-Sprache überall.
|
||
Der Vollbild-Knopf gehört dazu: als roter Primär-Knopf in einer Reihe aus
|
||
zwei Glas-Knöpfen zöge er die Aufmerksamkeit von der Seite weg. */
|
||
.btn.stadionzeitung-viewer__nav-btn,
|
||
.btn.stadionzeitung-viewer__full-btn {
|
||
font-size: var(--icon-s);
|
||
color: var(--clr-text);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
.btn.stadionzeitung-viewer__nav-btn:hover,
|
||
.btn.stadionzeitung-viewer__full-btn:hover {
|
||
background: rgb(255 255 255 / 0.2);
|
||
}
|
||
|
||
/* Im Vollbild ist der Knopf gedrückt — das darf man auch sehen. */
|
||
.btn.stadionzeitung-viewer__full-btn[aria-pressed="true"] {
|
||
background: var(--clr-accent);
|
||
border-color: var(--clr-accent);
|
||
}
|
||
|
||
/* Mit JS: Track wird zum horizontalen Scroll-Snap-Swiper. Kein eigener
|
||
Touch-Code — native Scroll-Snap-Mechanik übernimmt das Swipen. */
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-viewer__track {
|
||
flex-direction: row;
|
||
gap: 0;
|
||
overflow-x: auto;
|
||
scroll-snap-type: x mandatory;
|
||
-webkit-overflow-scrolling: touch;
|
||
scrollbar-width: none;
|
||
}
|
||
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-viewer__track::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-viewer__page {
|
||
display: flex;
|
||
/* align-items MUSS gesetzt sein: der Flex-Default `stretch` streckt die Seite auf
|
||
die Höhe der Flex-Zeile und übersteuert damit ihre aspect-ratio — bei 390px
|
||
Viewport war die Seite 559px statt 515px hoch, also gar kein A5 mehr. Sichtbar
|
||
wurde das nur auf der Vorwort-Seite, deren Rasenbild bis zur Blattkante reichen
|
||
muss: darunter blieben 44px weiß (31.07.2026). Die anderen Seiten sind oben
|
||
ausgerichtet und weiß, dort fällt eine zu hohe Seite nicht auf. */
|
||
align-items: center;
|
||
flex: 0 0 100%;
|
||
/* Ohne das schrumpft ein Flex-Item nie unter die Content-Breite seiner
|
||
Kinder (Default: min-width:auto) — eine breite Tabelle in der
|
||
Live-Seite hätte sonst den ganzen horizontalen Swipe-Track aufgerissen,
|
||
obwohl .table-scroll selbst schon eigenständig scrollbar ist. */
|
||
min-width: 0;
|
||
justify-content: center;
|
||
scroll-snap-align: start;
|
||
scroll-snap-stop: always;
|
||
padding-inline: var(--container-pad);
|
||
}
|
||
|
||
/* Ganze Seite im Viewport sichtbar halten (kein Vertikal-Scroll INNERHALB
|
||
des horizontalen Swipers — das würde sich mit der Swipe-Geste beißen). */
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-page__img {
|
||
width: auto;
|
||
max-width: 100%;
|
||
max-height: 72vh;
|
||
object-fit: contain;
|
||
}
|
||
|
||
/* Live-Seite (Tabelle/Termine/News/Ergebnisse/Vorschau/Kontakt) — jede
|
||
Stadionzeitungs-Seite ist ein bedrucktes A5-Blatt: HELL, unabhängig vom
|
||
dunklen Website-Theme (der Rest der Seite bleibt dunkel — das ist die
|
||
Ausnahme, nicht die Regel). Die Farb-Tokens werden hier lokal
|
||
überschrieben, damit league-table/termine-list/news-feed automatisch hell
|
||
werden statt jede Komponente einzeln zu überschreiben (Token-Disziplin,
|
||
siehe CLAUDE.md). Kontextneutral (läuft im Swipe-Viewer UND in der
|
||
Druckfassung, gerendert von stadionzeitung-seite.php) — alles
|
||
Viewer-Spezifische (max-width, Schatten, Swipe-Verhalten) liegt in den
|
||
.stadionzeitung-viewer-Selektoren darunter. Eigener Vertikal-Scroll
|
||
innerhalb der Seite ist unproblematisch (anders als beim Bild): die Seite
|
||
selbst nimmt nicht an der horizontalen Swipe-Geste teil, nur der Track. */
|
||
.stadionzeitung-page {
|
||
--clr-text: #171717;
|
||
--clr-text-muted: #4d4d4d;
|
||
--clr-text-faint: #6e6e6e;
|
||
--clr-glass-bg: #ffffff;
|
||
--clr-glass-border: #dddddd;
|
||
--clr-glass-border-strong: #999999;
|
||
/* Container fürs Selbst-Containment der Live-Seiten (dasselbe Prinzip wie
|
||
.stadionzeitung-cover, siehe CLAUDE.md): Innenmaße in cqi relativ zur
|
||
Seitenbreite, damit die Seite im Viewer (480px), in der Druck-
|
||
Blattvorschau (schmaler) und auf dem bedruckten Blatt (135mm)
|
||
proportional exakt gleich aussieht. Umgestellt sind bisher Vorwort +
|
||
Kopf-Muster; die übrigen Live-Seiten folgen im Feinschliff. Nebeneffekt
|
||
der inline-size-Containment: der Inhalt (z. B. eine breite Tabelle)
|
||
kann die Seitenbreite nicht mehr aufdrücken. */
|
||
container-type: inline-size;
|
||
width: 100%;
|
||
padding: var(--space-l);
|
||
background: #ffffff;
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.stadionzeitung-viewer .stadionzeitung-page {
|
||
max-width: 480px;
|
||
box-shadow: 0 6px 24px rgb(0 0 0 / 0.35);
|
||
}
|
||
|
||
/* Weiße Füllseite (nur Druckfassung): hält die Seitenzahl des Falt-Hefts auf
|
||
einem Vielfachen von 4 — bewusst komplett leer, wie eine unbedruckte
|
||
Umschlag-Innenseite einer Zeitschrift. */
|
||
.stadionzeitung-page--leer {
|
||
height: 100%;
|
||
}
|
||
|
||
/* Gemeinsames Kopf-Muster der Live-Seiten (Kicker + Überschrift + Trennlinie)
|
||
— dieselbe Anmutung wie die Canva-Vorlage („V O R W O R T" über der
|
||
Headline). Vorwort/Termine haben historisch eigene Kopf-Klassen mit
|
||
Foto-Hintergrund; neue Live-Seiten nutzen diese generischen. */
|
||
.stadionzeitung-page__head {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.8cqi;
|
||
margin-bottom: 4.2cqi;
|
||
text-align: left;
|
||
}
|
||
|
||
.stadionzeitung-page__kicker {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.7cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wider);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Bewusst nicht in Versalien — wie Vorwort/Termine: ruhiger redaktioneller
|
||
Ton statt einer schreienden Poster-Zeile. Größe einheitlich mit der
|
||
Vorwort-Überschrift (Felix, 29.07.2026: Coolvetica-Überschriften im Heft
|
||
durchgängig kräftiger). */
|
||
.stadionzeitung-page__heading {
|
||
font-size: 9.5cqi;
|
||
line-height: 1.05;
|
||
text-transform: none;
|
||
}
|
||
|
||
.stadionzeitung-page__sub {
|
||
font-size: 3.1cqi;
|
||
font-weight: var(--fw-bold);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-page__rule {
|
||
display: block;
|
||
margin-bottom: 4.2cqi;
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
/* Gemeinsamer CTA-Fuß der Live-Seiten (Kontakt: /mitmachen, News: /news,
|
||
Termine: /termine, Vorschau: /matchcenter) im TICKET-Stil (Interview
|
||
29.07.2026): links der Textteil mit rotem „Mehr online“-Chip und
|
||
Coolvetica-Titel, rechts der QR als „Abriss“ hinter einer perforierten
|
||
Linie mit Kerben oben/unten — wie eine Eintrittskarte. Die Kerben sind
|
||
weiße Kreise mit roter Kontur, die der overflow des Tickets halbiert. */
|
||
.stadionzeitung-page__cta {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: stretch;
|
||
margin-top: 4.2cqi;
|
||
border: 1px solid var(--clr-accent);
|
||
border-radius: var(--radius-btn);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stadionzeitung-page__cta-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
justify-content: center;
|
||
flex: 1;
|
||
min-width: 0;
|
||
padding: 2.2cqi 3cqi;
|
||
}
|
||
|
||
/* Rotes Label als Chip mit weißer Schrift — Rot als Fläche, nie als kleine
|
||
Textfarbe auf Weiß (4,5:1 würde sonst reißen, Kontrastregel CLAUDE.md). */
|
||
.stadionzeitung-page__cta-label {
|
||
display: inline-block;
|
||
margin-bottom: 1cqi;
|
||
padding: 0.5cqi 1.6cqi;
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.3cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: #fff;
|
||
background: var(--clr-accent);
|
||
border-radius: var(--radius-btn);
|
||
}
|
||
|
||
/* Titel in der Heading-Schrift — der CTA spricht dieselbe Sprache wie die
|
||
Seitenüberschriften. Proportional zur Seite (cqi), wie das ganze Blatt. */
|
||
.stadionzeitung-page__cta-title {
|
||
font-family: var(--ff-heading);
|
||
font-size: 5.8cqi;
|
||
line-height: 0.95;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.stadionzeitung-page__cta-link {
|
||
margin-top: 0.7cqi;
|
||
font-size: 2.9cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Der Abriss: perforierte Trennlinie + QR + „Scan mich“. */
|
||
.stadionzeitung-page__cta-abriss {
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 1cqi;
|
||
flex: none;
|
||
padding: 2cqi 2.8cqi;
|
||
border-left: 1px dashed var(--clr-accent);
|
||
}
|
||
|
||
/* Perforations-Kerben: halbe Kreise an Ober- und Unterkante der Trennlinie
|
||
(overflow: hidden des Tickets schneidet die äußere Hälfte ab). */
|
||
.stadionzeitung-page__cta-abriss::before,
|
||
.stadionzeitung-page__cta-abriss::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: -1.6cqi;
|
||
width: 3cqi;
|
||
height: 3cqi;
|
||
background: #fff;
|
||
border: 1px solid var(--clr-accent);
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.stadionzeitung-page__cta-abriss::before {
|
||
top: -1.9cqi;
|
||
}
|
||
|
||
.stadionzeitung-page__cta-abriss::after {
|
||
bottom: -1.9cqi;
|
||
}
|
||
|
||
.stadionzeitung-page__cta-qr {
|
||
width: 13cqi;
|
||
height: 13cqi;
|
||
}
|
||
|
||
.stadionzeitung-page__cta-scan {
|
||
font-size: 2.1cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Der CTA-Fuß ist auf JEDER Seite an der Blattunterkante verankert: Seiten
|
||
mit wenig Inhalt (z. B. die Vorschau in einer engen Spielwoche) ließen
|
||
das Ticket sonst mitten auf der Seite schweben — mit dem Anker haben alle
|
||
Verweis-Seiten denselben Rhythmus. Der Mindestabstand nach oben kommt aus
|
||
den margin-bottom der Inhalte darüber. */
|
||
.stadionzeitung-ergebnisse,
|
||
.stadionzeitung-news,
|
||
.stadionzeitung-kontakt,
|
||
.stadionzeitung-partner,
|
||
.stadionzeitung-momente,
|
||
.stadionzeitung-historie {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 100%;
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse .stadionzeitung-page__cta,
|
||
.stadionzeitung-news .stadionzeitung-page__cta,
|
||
.stadionzeitung-kontakt .stadionzeitung-page__cta,
|
||
.stadionzeitung-partner .stadionzeitung-page__cta,
|
||
.stadionzeitung-momente .stadionzeitung-page__cta,
|
||
.stadionzeitung-historie .stadionzeitung-page__cta {
|
||
margin-top: auto;
|
||
}
|
||
|
||
/* A5-Seitenverhältnis (148:210) wie ein echtes Blatt — im Swipe-Modus, damit
|
||
Bild- und Live-Seiten dieselbe Größe/denselben Rhythmus beim Durchblättern
|
||
haben. Reicht der Inhalt nicht in die Fläche, scrollt die Seite intern.
|
||
DEFINITE Breite aus der Zielhöhe statt width:auto + max-height: das
|
||
inline-size-Containment (s. o.) lässt eine auto-Breite sonst auf das
|
||
bloße Padding kollabieren (der Inhalt zählt beim Containment per
|
||
Definition nicht mit — reproduzierbar 48px schmale Seiten). min() hält
|
||
die Seite auf schmalen Viewports in der Track-Breite.
|
||
|
||
Das COVER steht bewusst im selben Selektor, obwohl es sonst eine eigene Klasse
|
||
hat: es ist genauso ein inline-size-Container und war genauso betroffen. Es hatte
|
||
hier `width: auto` und blieb am iPhone unsichtbar, während Chrome am Desktop es
|
||
zeigte (31.07.2026). Der Grund für den Unterschied: .stadionzeitung-viewer__page
|
||
ist `display: flex`, und auf einem Flex-Kind heißt `width: auto` „richte dich nach
|
||
dem Inhalt" — Chrome rettet sich über aspect-ratio plus gestreckte Höhe zu einem
|
||
Wert, Safari nicht. Zusammen in EINER Regel, damit die beiden nicht wieder
|
||
auseinanderlaufen; nur das interne Scrollen bleibt den Seiten vorbehalten (das
|
||
Cover ist `overflow: hidden`, sein Spielerfoto darf randabfallend beschnitten
|
||
werden). */
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-page,
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-cover,
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-page__img {
|
||
width: min(calc(72vh * 148 / 210), 100%);
|
||
max-width: none;
|
||
aspect-ratio: 148 / 210;
|
||
}
|
||
|
||
/* Die Bild-Seiten (Anzeigen) leiteten ihre Höhe vorher aus dem EIGENEN
|
||
Bildverhältnis ab (width:auto + max-height + object-fit: contain). Die meisten
|
||
Motive sind 1440x2043 und damit praktisch A5, drei aber 1440x2037 — die waren
|
||
dadurch 513px statt 515px hoch und fielen beim Wischen als Sprung auf
|
||
(31.07.2026). Jetzt gilt für sie dieselbe A5-Fläche wie für alle anderen, und
|
||
`cover` fängt die 0,4% Verhältnis-Abweichung ab, indem es minimal beschneidet
|
||
statt zu verzerren — genau wie die Druckfassung es ohnehin macht. */
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-page__img {
|
||
height: auto;
|
||
max-height: none;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-page {
|
||
overflow-y: auto;
|
||
}
|
||
|
||
/* --- Vollbild-Lesemodus ---------------------------------------------------
|
||
Am Handy ist die Seite durch ihre eigene Breite begrenzt, nicht durch die
|
||
Höhe: bei 390px Bildschirm ist eine A5-Seite 553px hoch, und Kopfleiste,
|
||
Navigation, Steuerung und Fußzeile beanspruchen zusammen mehr Platz als die
|
||
Seite selbst. Der Vollbildmodus räumt genau das weg und lässt die Seite
|
||
randlos die volle Breite nehmen. Das ist keine Zauberei: die Seite wächst um
|
||
etwa ein Zehntel — der eigentliche Gewinn ist die ruhige Fläche. Für dichte
|
||
Seiten (Tabellen) bleibt das Aufziehen mit zwei Fingern das Mittel der Wahl,
|
||
das der viewport-Meta-Tag bewusst erlaubt. */
|
||
body.is-sz-full {
|
||
overflow: hidden;
|
||
}
|
||
|
||
body.is-sz-full .site-header,
|
||
body.is-sz-full .site-footer,
|
||
body.is-sz-full .stadionzeitung-issue-head,
|
||
body.is-sz-full .back-to-top {
|
||
display: none;
|
||
}
|
||
|
||
/* z-index über dem Mobil-Menü (25/30) und der Lightbox (40), unter dem
|
||
Skip-Link (100) — die Kopfleiste ist hier ohnehin ausgeblendet. */
|
||
.stadionzeitung-viewer.is-full {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 45;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: var(--space-2xs);
|
||
padding-block: var(--space-2xs);
|
||
background: var(--clr-bg-deep);
|
||
}
|
||
|
||
.stadionzeitung-viewer.is-full .stadionzeitung-viewer__page {
|
||
padding-inline: 0;
|
||
}
|
||
|
||
/* dvh statt vh: am Handy klappt die Adressleiste beim Scrollen ein — mit vh
|
||
würde die Seitenhöhe dabei springen. */
|
||
.stadionzeitung-viewer.is-full .stadionzeitung-page,
|
||
.stadionzeitung-viewer.is-full .stadionzeitung-cover {
|
||
width: min(calc(92dvh * 148 / 210), 100%);
|
||
max-height: 92dvh;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.stadionzeitung-viewer.is-full .stadionzeitung-viewer__controls {
|
||
margin-top: 0;
|
||
}
|
||
|
||
/* Beide Icons liegen im Markup, sichtbar ist immer nur eines. */
|
||
.stadionzeitung-viewer__icon--close,
|
||
.stadionzeitung-viewer.is-full .stadionzeitung-viewer__icon--open {
|
||
display: none;
|
||
}
|
||
|
||
.stadionzeitung-viewer.is-full .stadionzeitung-viewer__icon--close {
|
||
display: inline-flex;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
/* Der Innenabstand kostet am Handy prozentual am meisten Seitenbreite. */
|
||
.stadionzeitung-viewer.is-enhanced .stadionzeitung-viewer__page {
|
||
padding-inline: var(--space-2xs);
|
||
}
|
||
|
||
/* Der Abstand nach OBEN bleibt wie am Desktop: die Leiste braucht ihre 92px,
|
||
und darunter noch Luft, sonst klebt der Zurück-Link am Logo (genau das war
|
||
hier der Fall). Gekürzt wird nur nach unten — die Höhe für die Seite kommt
|
||
aus der ausgeblendeten Datumszeile darunter. */
|
||
.stadionzeitung-issue-head {
|
||
padding-bottom: var(--space-2xs);
|
||
}
|
||
|
||
/* Das Datum steht schon auf dem Cover direkt darunter („Saison 2026/27 ·
|
||
1. August 2026") — am Handy ist die Zeile im Kopf reine Doppelung und
|
||
kostet Höhe, die die Seite besser braucht. */
|
||
.stadionzeitung-issue-head__date {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* Die frühere Cover-Sonderregel (max-height: 72vh; width: auto) stand hier und war
|
||
der Grund, warum das Cover am iPhone fehlte — siehe die gemeinsame Regel weiter
|
||
oben bei .stadionzeitung-page. Nicht wieder einführen: „nur eine Höhen-Deckelung"
|
||
klingt harmlos, erzeugt auf einem Flex-Kind mit inline-size-Containment aber eine
|
||
Breite von null. */
|
||
|
||
/* Tabellen-Seite: gedruckte Buchseite statt Website-Karte — scharfe Ecken
|
||
wie .stadionzeitung-page__img, kein .radius-card der geteilten
|
||
.table-scroll (die auf /fussball weiterhin abgerundet bleibt). */
|
||
/* Tabelle auf der A5-Seite: kein Rahmen (die Blattkante rahmt schon, und der
|
||
Kasten kostete unten Platz, bis die Legende abgeschnitten war — Felix,
|
||
29.07.2026), kein Wisch-Fade (das Blatt scrollt nicht), und die
|
||
Website-Tabelle erzwingt min-width 520px — hier stattdessen alles in cqi
|
||
(Selbst-Containment wie das Cover), damit Tabelle UND Legende in jeder
|
||
Darstellungsgröße vollständig in die Seite passen. */
|
||
.stadionzeitung-page .table-scroll {
|
||
border: 0;
|
||
border-radius: 0;
|
||
}
|
||
|
||
.stadionzeitung-page .table-scroll-frame::after {
|
||
content: none;
|
||
}
|
||
|
||
.stadionzeitung-page .league-table {
|
||
min-width: 0;
|
||
font-size: 2.7cqi;
|
||
}
|
||
|
||
.stadionzeitung-page .league-table thead th {
|
||
font-size: 2.3cqi;
|
||
}
|
||
|
||
.stadionzeitung-page .league-table th,
|
||
.stadionzeitung-page .league-table td {
|
||
padding: 1cqi 0.9cqi;
|
||
}
|
||
|
||
.stadionzeitung-page .league-table tbody .crest {
|
||
--crest-size: 4.5cqi;
|
||
}
|
||
|
||
.stadionzeitung-page .league-table__rank {
|
||
width: 6cqi;
|
||
}
|
||
|
||
.stadionzeitung-page .league-table__legend {
|
||
margin-top: 2.5cqi;
|
||
gap: 1.7cqi 4.2cqi;
|
||
font-size: 2.5cqi;
|
||
}
|
||
|
||
/* Swatch skaliert mit der Legenden-Schrift statt fest in rem. */
|
||
.stadionzeitung-page .league-table__legend-item::before {
|
||
width: 0.9em;
|
||
height: 0.9em;
|
||
}
|
||
|
||
/* Wappen auf dem weißen Blatt ohne Kreis-Chip: nicht jedes Vereinslogo ist
|
||
rund, der Kreis beschneidet eckige Wappen (Felix, 29.07.2026). Der weiße
|
||
Chip stammt vom dunklen Website-Grund und ist auf Papier unsichtbar bzw.
|
||
nur als Beschnitt sichtbar. Der Initialen-Fallback bleibt bewusst ein
|
||
Kreis, bekommt auf Weiß aber eine feine Kontur. */
|
||
.stadionzeitung-page img.crest {
|
||
padding: 0;
|
||
background: transparent;
|
||
border-radius: 0;
|
||
}
|
||
|
||
.stadionzeitung-page .crest--initials {
|
||
border: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
/* === stadionzeitung-cover (automatisch generiertes Titelblatt, Phase 3) === */
|
||
/* Einzige fotografische Stadionzeitungs-Seite — bewusst NICHT die helle
|
||
.stadionzeitung-page-Basis (die gilt für Tabelle/Termine/News,
|
||
siehe oben). Selbst-Containment: `container-type: inline-size` + alle
|
||
internen Maße in `cqi` (relativ zur eigenen Breite) statt der
|
||
Website-Schriftgrößen-Tokens — das Cover sieht dadurch in jedem Kontext
|
||
(Karten-Vorschau ~280px, Swipe-Viewer bis 480px, Druck ~90mm) proportional
|
||
exakt gleich aus, unabhängig von Root-Schriftgröße/Viewport. Feste
|
||
aspect-ratio unconditional (nicht erst im is_enhanced-Zustand wie bei den
|
||
Live-Seiten): das Layout braucht immer eine definierte Fläche, auch ohne JS.
|
||
Ausnahme von der cqi-Regel: `letter-spacing` bleibt über die `--ls-*`-Token
|
||
(dimensionslose `em`-Vielfache, skalieren ohnehin mit der eigenen
|
||
Schriftgröße — kein Konflikt mit dem Selbst-Containment). */
|
||
.stadionzeitung-cover {
|
||
position: relative;
|
||
overflow: hidden;
|
||
width: 100%;
|
||
max-width: 480px;
|
||
aspect-ratio: 148 / 210;
|
||
container-type: inline-size;
|
||
color: #fff;
|
||
/* Fallback-Fläche, falls kein Hero-Bild geladen werden kann — sonst deckt
|
||
.stadionzeitung-cover__bg das komplett ab. */
|
||
background: var(--clr-bg-deep);
|
||
box-shadow: 0 6px 24px rgb(0 0 0 / 0.35);
|
||
}
|
||
|
||
/* Passepartout-Rahmen — dünne Linie mit Luft zur Kante, kein volles
|
||
Randlos-Foto, gibt der Seite den "gedruckte Titelseite"-Charakter. Auf z2
|
||
wie .inner, damit sie nicht vom Spielerfoto verdeckt wird. */
|
||
.stadionzeitung-cover::before {
|
||
content: '';
|
||
position: absolute;
|
||
z-index: 2;
|
||
inset: 2.5cqi;
|
||
border: 1px solid rgb(255 255 255 / 0.4);
|
||
pointer-events: none;
|
||
}
|
||
|
||
|
||
/* Vereins-Hero-Bild als Hintergrund (dieselbe Quelle wie die Startseite). */
|
||
.stadionzeitung-cover__bg {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
/* Rechter Bildausschnitt des Vereinsfotos (Sportheim/Flutlicht) statt
|
||
Mitte — dort sitzt das interessantere Motiv (Felix, 29.07.2026). */
|
||
object-position: right;
|
||
}
|
||
|
||
/* Abdunklung für Lesbarkeit — unter dem Spielerfoto (das bleibt unverdunkelt,
|
||
knackig scharf), aber über dem Hintergrundbild. */
|
||
.stadionzeitung-cover::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
background: linear-gradient(180deg, rgb(0 0 0 / 0.68) 0%, rgb(0 0 0 / 0.15) 38%, rgb(0 0 0 / 0.75) 100%);
|
||
}
|
||
|
||
/* Freigestelltes Spielerfoto ist der Star: sehr großformatig, rechts
|
||
randabfallend („bleed"), reicht fast über die volle Höhe bis auf Ebene des
|
||
Masthead-Titels. Ohne Foto bleibt Hintergrundbild + Verlauf die Seite. Die
|
||
Schlagzeile (.stadionzeitung-cover__corner) sitzt dafür bewusst unten links
|
||
statt zentriert über die volle Breite — beide Elemente teilen sich die
|
||
Fläche wie ein echtes zweispaltiges Cover-Layout, statt sich zu überlappen. */
|
||
.stadionzeitung-cover__player {
|
||
position: absolute;
|
||
z-index: 1;
|
||
right: -8cqi;
|
||
bottom: 0;
|
||
height: 98cqi;
|
||
width: auto;
|
||
}
|
||
|
||
.stadionzeitung-cover__inner {
|
||
position: relative;
|
||
z-index: 2;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
padding: 5cqi 6cqi;
|
||
}
|
||
|
||
.stadionzeitung-cover__masthead {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 1.2cqi;
|
||
text-align: center;
|
||
}
|
||
|
||
/* Kicker-Zeile wie bei echten Magazinen: Saison + Ausgabedatum, klein,
|
||
gesperrt — gibt dem Cover die "Front-Matter"-Anmutung einer Zeitschrift.
|
||
Schlicht weiß statt rot+Schatten (Schatten auf Rot wirkte unruhig/schmutzig) —
|
||
der verstärkte obere Verlauf (siehe ::after) trägt den Kontrast allein. */
|
||
.stadionzeitung-cover__kicker {
|
||
font-family: var(--ff-body);
|
||
font-size: 2.7cqi;
|
||
font-weight: var(--fw-bold);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wider);
|
||
color: rgb(255 255 255 / 0.92);
|
||
}
|
||
|
||
/* Logo neben dem Titel, auf gleicher Höhe — wie ein Zeitschriften-Wortmarken-
|
||
Lockup, nicht gestapelt. */
|
||
.stadionzeitung-cover__masthead-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2.5cqi;
|
||
}
|
||
|
||
.stadionzeitung-cover__logo {
|
||
height: 15cqi;
|
||
width: auto;
|
||
}
|
||
|
||
/* Riesiger Zeitschriften-Masthead statt kleinem Website-Label — trägt die
|
||
Seite, nie kleiner als eine echte Magazin-Wortmarke. */
|
||
.stadionzeitung-cover__wordmark {
|
||
font-family: var(--ff-heading);
|
||
font-size: 21.5cqi;
|
||
line-height: 0.82;
|
||
text-transform: uppercase;
|
||
text-shadow: 0 2px 10px rgb(0 0 0 / 0.5);
|
||
}
|
||
|
||
/* Dezente Tagline unterm Masthead — bewusst kursiv/gemischte Groß-Kleinschreibung
|
||
als Kontrast zur Versal-Wortmarke, wie ein Zeitschriften-Untertitel. */
|
||
.stadionzeitung-cover__tagline {
|
||
font-family: var(--ff-body);
|
||
font-style: italic;
|
||
font-size: 3.2cqi;
|
||
letter-spacing: var(--ls-tight);
|
||
color: rgb(255 255 255 / 0.8);
|
||
}
|
||
|
||
/* Unten links gestapelt: Begegnung (Headline) über Slogan/QR (Footer) — sitzt
|
||
im dunkel abgedunkelten unteren Bildbereich, freie Typografie direkt auf
|
||
dem Hintergrund, kein Kasten/keine Karte. Kontrast kommt aus dem
|
||
Abdunklungs-Verlauf (::after), nicht aus text-shadow: Chrome rendert
|
||
text-shadow auf mehrzeiligem, umbrechendem Text (lange Gegner-Namen) beim
|
||
Druck-Export nachweislich als soliden Kasten statt eines weichen Schattens
|
||
(in einer echten Druckvorschau geprüft, reproduzierbar). margin-top: auto
|
||
schiebt die ganze Gruppe an den unteren Rand, unabhängig von der Höhe des
|
||
Masthead darüber. */
|
||
.stadionzeitung-cover__corner {
|
||
margin-top: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 3cqi;
|
||
/* Deutlich schmaler als der optisch verfügbare Platz, mit Sicherheitsabstand
|
||
zum randabfallenden Spielerfoto rechts — der darf nie angeschnitten
|
||
werden, egal wie lang der Gegner-Name ist. */
|
||
max-width: 56cqi;
|
||
}
|
||
|
||
.stadionzeitung-cover__headline {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 2.5cqi;
|
||
text-align: left;
|
||
}
|
||
|
||
/* Beide Wappen nebeneinander: Layout auf den Container, nicht aufs Wappen —
|
||
siehe Kommentar beim Ergebnis-Rückblick, hier lag derselbe Fehler. Er fiel
|
||
nur nicht auf, weil `display: flex` auf einem <img> wirkungslos ist; sobald
|
||
ein Verein kein Wappen hat und die Initialen-Variante (ein <span>) greift,
|
||
hätte es deren Zentrierung zerlegt. */
|
||
.stadionzeitung-cover__crests {
|
||
--crest-size: 11cqi;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2.5cqi;
|
||
}
|
||
|
||
/* Bewusst NICHT Coolvetica (die Riesen-Versalien vom Masthead) — bei langen,
|
||
zusammengesetzten Gegner-Namen wird der enge Condensed-Schnitt in Großbuchstaben
|
||
schnell unleserlich. Abel in gemischter Groß-/Kleinschreibung liest sich als
|
||
ruhige, editoriale "Cover-Zeile" und setzt bewusst einen Kontrast zum
|
||
schreienden Masthead darüber — genau dieses Zusammenspiel macht ein
|
||
Magazin-Cover aus (eine Display-Schrift, eine Lese-Schrift). */
|
||
.stadionzeitung-cover__matchup {
|
||
font-family: var(--ff-body);
|
||
font-size: 5.8cqi;
|
||
font-weight: var(--fw-bold);
|
||
line-height: 1.08;
|
||
letter-spacing: var(--ls-tight);
|
||
}
|
||
|
||
.stadionzeitung-cover__meta {
|
||
font-size: 3.1cqi;
|
||
color: rgb(255 255 255 / 0.85);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
/* Rote Trennlinie zwischen Begegnung und Fußzeile — klassisches
|
||
Magazin-Gestaltungsmittel, markiert den Wechsel von Schlagzeile zu
|
||
Fußzeilen-Information deutlicher als ein bloßer Abstand. */
|
||
.stadionzeitung-cover__footer {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 1.5cqi;
|
||
text-align: left;
|
||
}
|
||
|
||
/* Zweite Coverzeile (Teaser auf die Interview-Seite im Heft): Lese-Serif
|
||
kursiv wie ein Magazin-Anreißer, davor ein roter Balken — Rot als Fläche,
|
||
nie als Textfarbe auf dem dunklen Foto (Kontrastregel, CLAUDE.md). Die
|
||
Breite bleibt in der linken Spalte, damit die Zeile dem randabfallenden
|
||
Spielerfoto nicht in die Quere kommt. */
|
||
.stadionzeitung-cover__teaser {
|
||
max-width: 54cqi;
|
||
padding-left: 1.8cqi;
|
||
border-left: 0.9cqi solid var(--clr-accent);
|
||
font-family: var(--ff-serif);
|
||
font-style: italic;
|
||
font-size: 3.4cqi;
|
||
line-height: 1.35;
|
||
color: rgb(255 255 255 / 0.92);
|
||
}
|
||
|
||
.stadionzeitung-cover__teaser-label {
|
||
display: block;
|
||
font-family: var(--ff-body);
|
||
font-style: normal;
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.5cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: #fff;
|
||
}
|
||
|
||
/* QR bewusst minimal: kleiner Code + eine Zeile, keine Plakette (Felix,
|
||
29.07.2026). Der weiße Grund unterm Code bleibt — ohne ihn ist ein QR
|
||
auf Foto nicht zuverlässig scanbar. */
|
||
.stadionzeitung-cover__digital {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1.6cqi;
|
||
}
|
||
|
||
.stadionzeitung-cover__qr {
|
||
width: 11cqi;
|
||
height: 11cqi;
|
||
flex: none;
|
||
border-radius: 2px;
|
||
background: #fff;
|
||
padding: 0.9cqi;
|
||
}
|
||
|
||
.stadionzeitung-cover__link {
|
||
font-size: 2.7cqi;
|
||
color: rgb(255 255 255 / 0.8);
|
||
}
|
||
|
||
/* QR-Plakette statt frei stehendem Code — wirkt wie ein gedrucktes
|
||
Info-Kärtchen (Rahmen + rote Akzent-Kante), nicht wie ein Website-Widget. */
|
||
/* === stadionzeitung-vorwort (Live-Seite, Phase 3) === */
|
||
/* Schlichte weiße Content-Seite wie Tabelle/Termine/News (läuft in
|
||
.stadionzeitung-page, deren lokale Token-Overrides — siehe
|
||
dort — automatisch greifen), normale Website-Token-Skala statt cqi (anders
|
||
als das Cover: das Vorwort taucht nie in einer Karten-Vorschau auf).
|
||
Etwas mehr Luft als die Geschwister-Seiten: negativer Margin hebt das
|
||
geerbte Padding von .stadionzeitung-page auf, das eigene
|
||
Padding ist großzügiger — passend zu einem redaktionellen Brief statt
|
||
einer dichten Datenliste wie Tabelle/Termine. */
|
||
/* Eigene aspect-ratio statt sich auf die Höhe von .live-page zu verlassen —
|
||
dasselbe Prinzip wie .stadionzeitung-cover: liefert in jedem Modus
|
||
(Swipe/Enhanced, No-JS-Stapel, Druck) zuverlässig eine definierte Fläche,
|
||
damit das unten einblendende Hintergrundbild (siehe .stadionzeitung-vorwort__bg)
|
||
immer eine Fläche zum Einblenden hat. `.live-page` selbst setzt ihre
|
||
aspect-ratio nämlich nur über `max-height` + `aspect-ratio` im Enhanced-Modus
|
||
— kein echtes `height`, worauf sich ein Prozent- oder inset:0-Kind nicht
|
||
verlassen kann. Etwas mehr Luft als die Geschwister-Seiten: negativer
|
||
Margin hebt das geerbte Padding von .stadionzeitung-page auf,
|
||
das eigene Padding ist großzügiger. */
|
||
.stadionzeitung-vorwort {
|
||
position: relative;
|
||
/* z-index: 0 erzwingt einen eigenen Stacking-Context — sonst bricht der
|
||
z-index: -1 des Hintergrundbilds (siehe unten) aus dieser Box aus und
|
||
landet hinter der weißen Fläche von .stadionzeitung-page,
|
||
statt nur hinter dem eigenen Fließtext (reproduzierbar leer geblieben,
|
||
bis dieser eine Wert fehlte — position: relative allein reicht dafür
|
||
nicht, das erzeugt noch keinen Stacking-Context). */
|
||
z-index: 0;
|
||
aspect-ratio: 148 / 210;
|
||
overflow-y: auto;
|
||
margin: calc(var(--space-l) * -1);
|
||
/* cqi statt Space-Token: Selbst-Containment wie das Cover — die Seite
|
||
sieht in jedem Kontext (Viewer, Blattvorschau, Druck) proportional
|
||
gleich aus. Design-Referenz 480px Seitenbreite, 1cqi = 4,8px. */
|
||
padding: 6.7cqi 6.7cqi 5cqi;
|
||
text-align: left;
|
||
}
|
||
|
||
/* DREI Seiten bringen eine eigene aspect-ratio mit und heben per negativem Margin
|
||
das Padding von .stadionzeitung-page auf, damit ihr Foto bis an die Blattkante
|
||
läuft: Vorwort, Sportheim und Termine. Alle drei brauchen deshalb dieselbe
|
||
Absicherung, und sie steht bewusst in EINER Regel — beim Vorwort allein behoben
|
||
fehlte sie den anderen zwei, und die Sportheim-Seite war dadurch 625,7px hoch in
|
||
einer 636,4px hohen Seite: 10,7px weißer Streifen unter dem Foto (31.07.2026).
|
||
|
||
Wie es wirkt: die aspect-ratio gibt der Box eine Fläche, wenn die Seite keine
|
||
definite Höhe hat (No-JS-Stapel, Druck). Hat sie eine, füllt min-height die Seite
|
||
wirklich aus — 100% plus das doppelte Seiten-Padding, weil der negative Margin
|
||
unten genau um diesen Betrag über die Kante geht. Eine vierte Seite mit diesem
|
||
Konstrukt gehört in diesen Selektor, nicht in eine eigene Regel. */
|
||
.stadionzeitung-vorwort,
|
||
.stadionzeitung-sportheim,
|
||
.stadionzeitung-termine {
|
||
min-height: calc(100% + var(--space-l) * 2);
|
||
}
|
||
|
||
/* Vereins-Hero-Bild (dieselbe Quelle wie Startseite/Cover) blendet unten ein —
|
||
dasselbe Ausklingen wie bei .hero__media, hier aber am unteren Seitenrand:
|
||
der Fließtext braucht oben die volle Fläche, im leeren Rest darunter darf
|
||
ein Foto durchscheinen, wie im ursprünglichen Canva-Vorwort. z-index: -1
|
||
hält es hinter dem normal fließenden Text (unpositionierte Inhalte malen
|
||
sich sonst grundsätzlich VOR positionierten Geschwistern mit z-index 0). */
|
||
.stadionzeitung-vorwort__bg {
|
||
position: absolute;
|
||
z-index: -1;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* Weißer Verlauf ÜBER dem Foto statt mask-image auf dem Foto selbst — Chrome
|
||
respektiert mask-image beim PDF-Export nachweislich nicht (das Foto
|
||
erschien dort voll deckend statt ausgeblendet, reproduzierbar geprüft).
|
||
Ein simpler Gradient-Layer ist dieselbe Technik wie die Abdunklung beim
|
||
Cover (::after) und druckt zuverlässig — dadurch kann das Foto jetzt auch
|
||
im PDF mit ausgeblendet erscheinen, nicht nur auf dem Bildschirm. */
|
||
.stadionzeitung-vorwort__bg-fade {
|
||
position: absolute;
|
||
z-index: -1;
|
||
inset: 0;
|
||
background: linear-gradient(180deg, #fff 0%, #fff 72%, rgb(255 255 255 / 0) 100%);
|
||
}
|
||
|
||
.stadionzeitung-vorwort__head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 5cqi;
|
||
}
|
||
|
||
.stadionzeitung-vorwort__kicker {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.7cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wider);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-vorwort__logo {
|
||
height: 12.5cqi;
|
||
width: auto;
|
||
}
|
||
|
||
/* Bewusst NICHT in Versalien (anders als der globale h1–h6-Standard aus
|
||
base.css) — ein Begrüßungssatz in Großbuchstaben liest sich geschrien,
|
||
eine echte Zeitschriften-Headline schreit nicht. */
|
||
.stadionzeitung-vorwort__heading {
|
||
font-size: 9.5cqi;
|
||
line-height: 1.05;
|
||
text-transform: none;
|
||
margin-bottom: 4.2cqi;
|
||
}
|
||
|
||
/* Einzige Stelle im Projekt mit einer dritten Schriftart (--ff-serif,
|
||
PT Serif) — ein Vorwort ist ein redaktioneller Brief, keine UI-Fläche;
|
||
eine Lese-Serif passt hier besser als die Website-Grotesk --ff-body. */
|
||
.stadionzeitung-vorwort__text {
|
||
font-family: var(--ff-serif);
|
||
font-size: 3.75cqi;
|
||
line-height: var(--lh-body);
|
||
margin-bottom: 3.3cqi;
|
||
}
|
||
|
||
.stadionzeitung-vorwort__signature {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 3.3cqi;
|
||
margin-top: 4.2cqi;
|
||
padding-top: 3.3cqi;
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-vorwort__portrait {
|
||
flex: none;
|
||
width: 11.7cqi;
|
||
height: 11.7cqi;
|
||
object-fit: cover;
|
||
/* Wie bei den Kontakt-Porträts: Fokus oben, Köpfe nie anschneiden. */
|
||
object-position: 50% 15%;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.stadionzeitung-vorwort__signature-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.stadionzeitung-vorwort__closing {
|
||
font-family: var(--ff-serif);
|
||
font-style: italic;
|
||
font-size: 3.1cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Unterzeichner als handschriftliche Unterschrift (--ff-signature, Great
|
||
Vibes) — wie in der Canva-Vorlage. Script-Schriften bauen klein, deshalb
|
||
deutlich größer als der übrige Text; KEINE Versalien und kein
|
||
letter-spacing, das zerreißt die verbundenen Buchstaben. */
|
||
.stadionzeitung-vorwort__signee {
|
||
font-family: var(--ff-signature);
|
||
font-size: 7cqi;
|
||
line-height: 1.1;
|
||
text-transform: none;
|
||
}
|
||
|
||
.stadionzeitung-vorwort__role {
|
||
font-size: 3.1cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* === stadionzeitung-termine (Live-Seite, Phase 3) === */
|
||
/* Gleiches Bauprinzip wie .stadionzeitung-vorwort (Kicker + Überschrift über
|
||
einblendendem Vereinsfoto, eigene aspect-ratio statt sich auf .live-page zu
|
||
verlassen, negativer Margin für mehr Rand) — hier blendet das Foto aber
|
||
OBEN ein (der Kicker/die Überschrift sitzt direkt drauf, die Liste braucht
|
||
darunter die volle weiße Fläche), nicht unten wie beim Vorwort. */
|
||
.stadionzeitung-termine {
|
||
position: relative;
|
||
z-index: 0;
|
||
aspect-ratio: 148 / 210;
|
||
overflow-y: auto;
|
||
margin: calc(var(--space-l) * -1);
|
||
/* cqi wie Vorwort/Cover (Selbst-Containment), Referenz 480px Breite. */
|
||
padding: 6.7cqi 6.7cqi 5cqi;
|
||
text-align: left;
|
||
}
|
||
|
||
.stadionzeitung-termine__bg {
|
||
position: absolute;
|
||
z-index: -1;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* Verlauf UMGEKEHRT zum Vorwort: oben nur ein leichter weißer Schleier (genug
|
||
Kontrast für Kicker/Überschrift), unterhalb der Überschrift komplett weiß,
|
||
bevor die Liste beginnt. Gradient-Layer statt mask-image — druckt
|
||
zuverlässig (siehe ausführliche Begründung bei .stadionzeitung-vorwort__bg-fade). */
|
||
.stadionzeitung-termine__bg-fade {
|
||
position: absolute;
|
||
z-index: -1;
|
||
inset: 0;
|
||
background: linear-gradient(180deg, rgb(255 255 255 / 0.55) 0%, #fff 42%, #fff 100%);
|
||
}
|
||
|
||
.stadionzeitung-termine__head {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.8cqi;
|
||
margin-bottom: 4.2cqi;
|
||
}
|
||
|
||
.stadionzeitung-termine__kicker {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.7cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wider);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Bewusst nicht in Versalien, wie die Vorwort-Überschrift — passt zum
|
||
ruhigen redaktionellen Ton statt einer schreienden Poster-Zeile. */
|
||
.stadionzeitung-termine__heading {
|
||
font-size: 9.5cqi;
|
||
line-height: 1.05;
|
||
text-transform: none;
|
||
}
|
||
|
||
.stadionzeitung-termine__rule {
|
||
display: block;
|
||
margin-bottom: 4.2cqi;
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-termine__list {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.stadionzeitung-termine__item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 3.3cqi;
|
||
padding-block: 3.3cqi;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-termine__item:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
/* Rundes Icon-Badge statt Datums-Chip (anders als .termine-list auf /termine)
|
||
— Akzentfarbe als Tönung, wie die QR-Plakette am Cover ein wiederkehrendes
|
||
rotes Gestaltungsmittel. */
|
||
.stadionzeitung-termine__icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: none;
|
||
width: 9.2cqi;
|
||
height: 9.2cqi;
|
||
border-radius: 50%;
|
||
background: rgb(var(--clr-accent-rgb) / 0.12);
|
||
color: var(--clr-accent);
|
||
font-size: 4.2cqi;
|
||
}
|
||
|
||
.stadionzeitung-termine__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Titel prominenter — wie News-Titel/Kontakt-Namen (Abel-Fake-Bold allein
|
||
ist zu leise, deshalb zusätzlich größer). */
|
||
.stadionzeitung-termine__title {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 4.4cqi;
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.stadionzeitung-termine__meta {
|
||
font-size: 3.1cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* === termine-list (Termine/Events-Seite) === */
|
||
/* Zeilen-Liste wie .match-row statt Karten — hier geht's um schnelles
|
||
Überfliegen vieler Termine, keine Bild-Vorschau nötig. */
|
||
.termine-list {
|
||
margin: var(--space-l) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.termine-list__item {
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.termine-list__link {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
align-items: center;
|
||
gap: var(--space-s);
|
||
padding: var(--space-s) var(--space-xs);
|
||
color: inherit;
|
||
text-decoration: none;
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
a.termine-list__link:hover {
|
||
background: rgb(255 255 255 / 0.06);
|
||
}
|
||
|
||
/* Datums-Chip statt reinem Fließtext links — Tag groß/Monat klein, wie ein
|
||
Ticket-Abschnitt. Das vollständige Datum (inkl. Zeitraum) steht zusätzlich
|
||
ausgeschrieben in der Meta-Zeile, der Chip ist rein dekorativ (aria-hidden). */
|
||
.termine-list__date-chip {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 56px;
|
||
padding: var(--space-3xs) var(--space-2xs);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-btn);
|
||
line-height: 1.15;
|
||
}
|
||
|
||
.termine-list__date-day {
|
||
font-family: var(--ff-heading);
|
||
font-size: var(--fs-650);
|
||
}
|
||
|
||
.termine-list__date-month {
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.termine-list__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-4xs);
|
||
min-width: 0;
|
||
}
|
||
|
||
.termine-list__title {
|
||
font-weight: 700;
|
||
}
|
||
|
||
.termine-list__meta {
|
||
font-size: var(--fs-400);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.termine-list__badge {
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
padding: var(--space-4xs) var(--space-xs);
|
||
border-radius: var(--radius-btn);
|
||
border: 1px solid var(--clr-glass-border);
|
||
color: var(--clr-text-muted);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.termine-list__badge--fussball {
|
||
border-color: rgb(var(--clr-accent-rgb) / 0.5);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* === news-list/news-row (News-Seite) === */
|
||
/* Zeilen-Liste wie .termine-list — bewusst KEIN Karten-Grid: Einträge sind zu
|
||
unterschiedlich hoch (hohe Bild-Posts neben kurzen Text-Ergebnissen), ein
|
||
Grid reißt dabei Löcher, weil kurze Karten den Platz neben einer hohen
|
||
Karte nicht auffüllen können. Eine Spalte hat dieses Problem nicht. */
|
||
.news-list {
|
||
margin: var(--space-l) 0 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
border-top: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.news-row {
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.news-row__link {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--space-m);
|
||
padding: var(--space-s) var(--space-xs);
|
||
color: inherit;
|
||
text-decoration: none;
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
a.news-row__link:hover {
|
||
background: rgb(255 255 255 / 0.06);
|
||
}
|
||
|
||
/* Ein Medien-Slot für ALLE Zeilen: 4:5-Hochformat-Kachel mit Glasfläche.
|
||
Instagram lädt ohnehin 4:5-Hochformat hoch — Fotos füllen die Kachel also
|
||
unbeschnitten; Ergebnis-Zeilen zentrieren ihr Wappen auf derselben Fläche.
|
||
So haben Bild- und Wappen-Zeilen denselben visuellen Baustein statt
|
||
„hartes Quadrat neben schwebendem Kreis". */
|
||
.news-row__media {
|
||
flex-shrink: 0;
|
||
width: 96px;
|
||
aspect-ratio: 4 / 5;
|
||
display: grid;
|
||
place-items: center;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-btn);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.news-row__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* Variable muss auf .crest selbst gesetzt werden (die Komponente definiert
|
||
--crest-size lokal, ein Wert auf dem Eltern-Container käme nie an) —
|
||
gleiches Muster wie .hero__upcoming .match-feature .crest. */
|
||
.news-row__media--crest .crest {
|
||
--crest-size: 72px;
|
||
}
|
||
|
||
.news-row__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-4xs);
|
||
min-width: 0;
|
||
}
|
||
|
||
.news-row__meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: var(--space-xs);
|
||
font-size: var(--fs-300);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.news-row__badge {
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
padding: var(--space-4xs) var(--space-xs);
|
||
border-radius: var(--radius-btn);
|
||
border: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.news-row__badge--ergebnis {
|
||
border-color: rgb(var(--clr-accent-rgb) / 0.5);
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* Aufmacher (featured: true): der neueste Eintrag als große Glas-Karte über
|
||
der Liste — Meta/Badge teilen sich die news-row__-Klassen (identische
|
||
Bausteine, nur der Rahmen ist größer). Bild ab 769px links neben dem Text,
|
||
Hochformat-Posts (Instagram 4:5) werden auf 4:3 beschnitten, damit die
|
||
Karte kompakt bleibt. */
|
||
.news-feature {
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
.news-feature__link {
|
||
display: grid;
|
||
gap: var(--space-l);
|
||
padding: var(--space-l);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
color: inherit;
|
||
text-decoration: none;
|
||
transition: background var(--transition);
|
||
}
|
||
|
||
a.news-feature__link:hover {
|
||
background: rgb(255 255 255 / 0.06);
|
||
}
|
||
|
||
@media (min-width: 769px) {
|
||
.news-feature__link--media {
|
||
grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.news-feature__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
aspect-ratio: 4 / 3;
|
||
object-fit: cover;
|
||
border-radius: var(--radius-btn);
|
||
}
|
||
|
||
.news-feature__media--crest {
|
||
display: grid;
|
||
place-items: center;
|
||
}
|
||
|
||
/* Auf .crest selbst, nicht auf dem Container (siehe news-row__media--crest) */
|
||
.news-feature__media--crest .crest {
|
||
--crest-size: 96px;
|
||
}
|
||
|
||
.news-feature__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: var(--space-2xs);
|
||
min-width: 0;
|
||
}
|
||
|
||
.news-feature__title {
|
||
font-size: var(--fs-550);
|
||
font-weight: 700;
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.news-feature__text {
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text-muted);
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 3;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.news-row__title {
|
||
font-weight: 700;
|
||
}
|
||
|
||
.news-row__text {
|
||
font-size: var(--fs-500);
|
||
color: var(--clr-text-muted);
|
||
/* Lange Captions/Meldungen nicht endlos wachsen lassen. */
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* „Artikel lesen" — sichtbarer Hinweis, dass die Zeile weiterführt. Ohne ihn
|
||
unterscheidet sich eine Meldung mit Detailseite für Sehende nur durch den
|
||
Hover-Hintergrund von einer ohne. Coolvetica ist hier verboten (nie unter
|
||
--fs-650), also Abel mit Versalien und Laufweite. Liegt INNERHALB des
|
||
Zeilenlinks: kein zusätzlicher Tab-Stop, kein verschachteltes <a>. */
|
||
.news-row__more,
|
||
.news-feature__more {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-3xs);
|
||
margin-top: var(--space-3xs);
|
||
color: var(--clr-text-muted);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
a.news-row__link:hover .news-row__more,
|
||
a.news-feature__link:hover .news-feature__more {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* === news-article (Detailseite einer Meldung) === */
|
||
/* Blog-Optik: Aufmacher über der Lesespalte, Fließtext in .prose (gemeinsame
|
||
Zeilenlänge mit den Rechtstexten). Der Kopf der Seite kommt von
|
||
.detail-head, deshalb hier kein Titel-Styling. */
|
||
.news-article__lead {
|
||
max-width: 72ch;
|
||
margin-bottom: var(--space-xl);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.news-article__lead-img,
|
||
.news-article__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
/* h2 im Artikel: mehr Luft nach oben als im Rechtstext, weil hier Absätze und
|
||
nicht Sektionen davor stehen. */
|
||
.news-article__heading {
|
||
margin-top: var(--space-xl);
|
||
}
|
||
|
||
.news-article__figure {
|
||
margin-block: var(--space-l);
|
||
}
|
||
|
||
.news-article__figure-link {
|
||
display: block;
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
/* Die Bildfläche trägt hier keinen Textlink-Look aus .prose a. */
|
||
text-decoration: none;
|
||
}
|
||
|
||
.news-article__caption {
|
||
margin-top: var(--space-2xs);
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
/* Zitat: Akzentbalken links wie im Stadionzeitungs-Interview, Rot nur als
|
||
Fläche, nie als Textfarbe. */
|
||
.news-article__quote {
|
||
margin-block: var(--space-l);
|
||
padding-left: var(--space-m);
|
||
border-left: 4px solid var(--clr-accent);
|
||
}
|
||
|
||
.news-article__quote blockquote p {
|
||
margin-bottom: 0;
|
||
font-size: var(--fs-600);
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
.news-article__quote-source {
|
||
margin-top: var(--space-2xs);
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
/* === stadionzeitung-ergebnisse (Live-Seite) === */
|
||
/* Ergebnis-Rückblick: zwei Zeilen pro Spiel (Heim über Gast, wie das
|
||
„Nächste Spiele"-Band) — lange Vereinsnamen passen auf der schmalen
|
||
A5-Seite nie in eine Zeile. Eigene Mannschaft über Gewicht der Body-Schrift
|
||
markiert (Abel hat echte Schnitte — anders als Coolvetica erlaubt, siehe
|
||
CLAUDE.md „nie über font-weight" bei Coolvetica). */
|
||
.stadionzeitung-ergebnisse__team {
|
||
margin-bottom: 4.2cqi;
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse__team-name {
|
||
margin-bottom: 1.7cqi;
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.7cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse__match {
|
||
padding-bottom: 1.7cqi;
|
||
margin-bottom: 1.7cqi;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse__match:last-child {
|
||
margin-bottom: 0;
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse__meta {
|
||
font-size: 2.5cqi;
|
||
color: var(--clr-text-faint);
|
||
}
|
||
|
||
/* Das Zeilen-Layout gehört auf die ZEILE, nicht auf das Wappen darin (stand
|
||
hier mal als `.stadionzeitung-ergebnisse__row .crest` — dann wurde das Wappen
|
||
selbst zum Grid, lag als Block-Element auf eigener Zeile, und Vereinsname und
|
||
Tore rutschten als Inline-Spans darunter zusammen). `--crest-size` bleibt
|
||
trotzdem hier: Custom Properties erben, das Wappen bleibt eine dumme
|
||
Komponente und der Kontext gibt ihm seine Größe — so machen es alle anderen
|
||
Wappen-Kontexte im Projekt auch. */
|
||
.stadionzeitung-ergebnisse__row {
|
||
--crest-size: 5.8cqi;
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
align-items: center;
|
||
gap: 1.7cqi;
|
||
padding-block: 0.6cqi;
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse__club {
|
||
min-width: 0;
|
||
font-size: 3.75cqi;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse__row--own .stadionzeitung-ergebnisse__club {
|
||
font-weight: var(--fw-bold);
|
||
}
|
||
|
||
.stadionzeitung-ergebnisse__goals {
|
||
font-family: var(--ff-heading);
|
||
font-size: 5.8cqi;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* === stadionzeitung-vorschau (Live-Seite) === */
|
||
/* Nächstes Spiel je Mannschaft nach dem Spieltag — Meta-Zeile + Begegnung in
|
||
zwei Wappen-Zeilen (dasselbe Zeilen-Muster wie der Ergebnis-Rückblick),
|
||
darunter Datum/Anstoß/Ort. */
|
||
.stadionzeitung-vorschau__match {
|
||
margin-bottom: 4.2cqi;
|
||
padding-bottom: 3.3cqi;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-vorschau__match:last-child {
|
||
margin-bottom: 0;
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.stadionzeitung-vorschau__meta {
|
||
margin-bottom: 1.7cqi;
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.7cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Layout auf die Zeile, nicht aufs Wappen — siehe Kommentar beim
|
||
Ergebnis-Rückblick, hier lag derselbe Fehler. */
|
||
.stadionzeitung-vorschau__row {
|
||
--crest-size: 5.8cqi;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 1.7cqi;
|
||
padding-block: 0.6cqi;
|
||
}
|
||
|
||
/* Vereinsnamen prominenter — dieselbe Hierarchie-Stufe wie Termine-Titel. */
|
||
.stadionzeitung-vorschau__club {
|
||
min-width: 0;
|
||
font-size: 4.4cqi;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.stadionzeitung-vorschau__row--own .stadionzeitung-vorschau__club {
|
||
font-weight: var(--fw-bold);
|
||
}
|
||
|
||
.stadionzeitung-vorschau__when {
|
||
margin-top: 0.8cqi;
|
||
font-size: 3.1cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* === stadionzeitung-kontakt (Live-Seite) === */
|
||
/* Ansprechpartner wie die „KONTAKT"-Seite der Canva-Vorlage: Abteilung +
|
||
Name + Nummer links, rundes Porträt rechts. Ohne Foto ein getöntes
|
||
Icon-Badge in Akzentfarbe — dieselbe Sprache wie die Termine-Seite. */
|
||
.stadionzeitung-kontakt__list {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.stadionzeitung-kontakt__item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 3.3cqi;
|
||
padding-block: 3.3cqi;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-kontakt__item:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.stadionzeitung-kontakt__department {
|
||
font-size: 2.7cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Name deutlich als wichtigste Zeile: Abel hat nur einen echten Schnitt, das
|
||
Fake-Bold allein bleibt zu leise — deshalb zusätzlich eine Stufe größer
|
||
als der übrige Zeilentext. */
|
||
.stadionzeitung-kontakt__name {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 4.4cqi;
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.stadionzeitung-kontakt__contact {
|
||
font-size: 3.1cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-kontakt__portrait {
|
||
flex: none;
|
||
width: 11.7cqi;
|
||
height: 11.7cqi;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
/* Personenfotos sind Ganzkörper-Hochformate — der Fokus liegt oben beim
|
||
Kopf, sonst schneidet der Kreis-Beschnitt die Köpfe an. */
|
||
object-position: 50% 15%;
|
||
}
|
||
|
||
.stadionzeitung-kontakt__portrait--icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgb(var(--clr-accent-rgb) / 0.12);
|
||
color: var(--clr-accent);
|
||
font-size: 4.2cqi;
|
||
}
|
||
|
||
|
||
|
||
/* === stadionzeitung-interview (Live-Seite) === */
|
||
/* Das Gespräch zur Cover-Teaser-Zeile — Kopf-Muster wie die Geschwister-
|
||
Seiten, Fragen in Abel fett, Antworten in der Lese-Serif (PT Serif, wie
|
||
das Vorwort: redaktioneller Text, keine UI-Fläche). Inhalt ist wie das
|
||
Vorwort chat-gepflegt (interview-Feld der Ausgabe). */
|
||
.stadionzeitung-interview__head-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 3.3cqi;
|
||
}
|
||
|
||
.stadionzeitung-interview__portrait {
|
||
flex: none;
|
||
width: 14cqi;
|
||
height: 14cqi;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
/* Wie bei den Kontakt-Porträts: Fokus oben, Köpfe nie anschneiden. */
|
||
object-position: 50% 15%;
|
||
}
|
||
|
||
.stadionzeitung-interview__intro {
|
||
margin-bottom: 3.3cqi;
|
||
font-family: var(--ff-serif);
|
||
font-style: italic;
|
||
font-size: 3.4cqi;
|
||
line-height: var(--lh-body);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-interview__qa {
|
||
margin-bottom: 3.3cqi;
|
||
}
|
||
|
||
.stadionzeitung-interview__qa:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.stadionzeitung-interview__frage {
|
||
margin-bottom: 0.8cqi;
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 3.4cqi;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.stadionzeitung-interview__antwort {
|
||
font-family: var(--ff-serif);
|
||
font-size: 3.4cqi;
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
/* === stadionzeitung-news (Live-Seite) === */
|
||
/* Eigene Liste statt des geteilten news-feed von /news: feste Medienspalte
|
||
(Wappen/Foto/Icon-Badge in EINER Größe), damit alle Titel bündig stehen —
|
||
auf /news variieren die Spaltenbreiten, auf der schmalen A5-Seite sprangen
|
||
die Titel dadurch sichtbar. Maße in cqi (Selbst-Containment, siehe
|
||
.stadionzeitung-page). */
|
||
.stadionzeitung-news__list {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.stadionzeitung-news__item {
|
||
display: grid;
|
||
grid-template-columns: 8.5cqi 1fr;
|
||
gap: 2.5cqi;
|
||
align-items: center;
|
||
padding-block: 2.1cqi;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-news__item:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.stadionzeitung-news__media {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 8.5cqi;
|
||
height: 8.5cqi;
|
||
}
|
||
|
||
.stadionzeitung-news__media--crest .crest {
|
||
--crest-size: 8.5cqi;
|
||
}
|
||
|
||
.stadionzeitung-news__media--img {
|
||
object-fit: cover;
|
||
}
|
||
|
||
.stadionzeitung-news__media--icon {
|
||
border-radius: 50%;
|
||
background: rgb(var(--clr-accent-rgb) / 0.12);
|
||
color: var(--clr-accent);
|
||
font-size: 4.2cqi;
|
||
}
|
||
|
||
.stadionzeitung-news__meta {
|
||
font-size: 2.5cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Titel deutlich als wichtigste Zeile — wie die Namen der Kontakt-Seite:
|
||
Abel-Fake-Bold allein ist zu leise, deshalb zusätzlich größer. */
|
||
.stadionzeitung-news__title {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 4cqi;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.stadionzeitung-news__text {
|
||
font-size: 2.9cqi;
|
||
color: var(--clr-text-muted);
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* === stadionzeitung-mitglied (Live-Seite, Poster) === */
|
||
/* Mitglieder-Werbung als Poster: riesige zentrierte Coolvetica-Typo, letztes
|
||
Wort in Vereinsrot (auf Weiß erreicht das Rot für große Schrift die
|
||
nötigen 3:1), Claim + Abteilungszeile darunter, geteilter CTA-Fuß unten.
|
||
Der Poster-Block zentriert sich in der verfügbaren Höhe der A5-Seite. */
|
||
.stadionzeitung-mitglied {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
text-align: center;
|
||
/* Dünne Rahmenlinie wie das Passepartout des Covers — macht aus der
|
||
weißen Fläche eine gestaltete Plakatseite. Abgerundet über das
|
||
Karten-Token (moderner, Wunsch Felix 29.07.2026). */
|
||
border: 1px solid var(--clr-text);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.stadionzeitung-mitglied__poster {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: 1;
|
||
gap: 4.2cqi;
|
||
padding: 6cqi 4cqi;
|
||
}
|
||
|
||
.stadionzeitung-mitglied__logo {
|
||
width: 13cqi;
|
||
height: auto;
|
||
}
|
||
|
||
.stadionzeitung-mitglied__headline {
|
||
display: flex;
|
||
flex-direction: column;
|
||
text-transform: uppercase;
|
||
line-height: 0.92;
|
||
}
|
||
|
||
.stadionzeitung-mitglied__line {
|
||
font-size: 15cqi;
|
||
}
|
||
|
||
/* Das kurze „des" kleiner — klassischer Poster-Rhythmus groß/klein/groß. */
|
||
.stadionzeitung-mitglied__line:nth-child(2) {
|
||
font-size: 9cqi;
|
||
}
|
||
|
||
.stadionzeitung-mitglied__line--accent {
|
||
color: var(--clr-accent);
|
||
}
|
||
|
||
.stadionzeitung-mitglied__claim {
|
||
max-width: 68cqi;
|
||
font-size: 3.6cqi;
|
||
line-height: var(--lh-body);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-mitglied__departments {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 3.1cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wider);
|
||
}
|
||
|
||
/* Großer zentrierter QR statt des generischen CTA-Fußes der Listen-Seiten. */
|
||
.stadionzeitung-mitglied__qr-block {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 1.7cqi;
|
||
margin-top: 2cqi;
|
||
}
|
||
|
||
.stadionzeitung-mitglied__qr {
|
||
width: 20cqi;
|
||
height: 20cqi;
|
||
}
|
||
|
||
.stadionzeitung-mitglied__link {
|
||
font-size: 2.9cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* === stadionzeitung-historie (Live-Seite) === */
|
||
/* Teaser auf die Website-Historie + Material-Aufruf für die Chronik 2028:
|
||
Kopf-Muster wie die Geschwister-Seiten, zwei historische Fotos
|
||
nebeneinander mit Bildunterschriften, Aufruf-Absatz mit der
|
||
Historie-Mailadresse, geteilter CTA-Fuß mit QR auf /historie. */
|
||
.stadionzeitung-historie__text {
|
||
margin-bottom: 4.2cqi;
|
||
font-size: 3.4cqi;
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
/* Polaroid-Look: weißer Fotorahmen mit dicker Unterkante (dort sitzt die
|
||
Bildunterschrift), Schlagschatten und leichte Gegen-Rotation der beiden
|
||
Fotos — wie aus dem Schuhkarton auf die Seite gelegt. */
|
||
.stadionzeitung-historie__bilder {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 3.5cqi;
|
||
margin-block: 2cqi 5cqi;
|
||
padding-inline: 1cqi;
|
||
}
|
||
|
||
.stadionzeitung-historie__polaroid {
|
||
margin: 0;
|
||
padding: 1.6cqi 1.6cqi 0;
|
||
background: #fff;
|
||
border: 1px solid var(--clr-glass-border);
|
||
box-shadow: 0 3px 14px rgb(0 0 0 / 0.16);
|
||
}
|
||
|
||
.stadionzeitung-historie__polaroid--links {
|
||
transform: rotate(-1.6deg);
|
||
}
|
||
|
||
.stadionzeitung-historie__polaroid--rechts {
|
||
transform: rotate(1.4deg);
|
||
}
|
||
|
||
.stadionzeitung-historie__img {
|
||
display: block;
|
||
width: 100%;
|
||
aspect-ratio: 1 / 1;
|
||
object-fit: cover;
|
||
/* Leicht entsättigt Richtung Archivfoto — die beiden Bilder haben
|
||
unterschiedliche Alterungen, so wirken sie als Paar. */
|
||
filter: grayscale(0.25);
|
||
}
|
||
|
||
.stadionzeitung-historie__caption {
|
||
padding-block: 1.4cqi;
|
||
font-size: 2.3cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
text-align: center;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* Aufruf als Notizzettel: gestrichelter Rahmen wie ein eingelegter Zettel
|
||
im Album — hebt die Handlungsaufforderung von Teaser-Text und Ticket ab. */
|
||
.stadionzeitung-historie__aufruf {
|
||
margin-bottom: 4.2cqi;
|
||
padding: 2.5cqi 3cqi;
|
||
font-size: 3.2cqi;
|
||
line-height: var(--lh-body);
|
||
border: 1px dashed var(--clr-glass-border-strong);
|
||
border-radius: var(--radius-btn);
|
||
}
|
||
|
||
.stadionzeitung-historie__mail {
|
||
font-weight: var(--fw-bold);
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* === stadionzeitung-sportheim (Live-Seite) === */
|
||
/* Nachbau der Canva-Seite „Feiern im Katzbachtal": Kicker + Headline,
|
||
Serif-Text, unten blutet das Gastraum-Foto randabfallend aus der Seite
|
||
(weißer Verlauf darüber, dieselbe drucksichere Technik wie beim Vorwort),
|
||
darauf die weiße QR-Karte. Rote Pinselstrich-Ecken als CSS-Näherung. */
|
||
.stadionzeitung-sportheim {
|
||
position: relative;
|
||
z-index: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
aspect-ratio: 148 / 210;
|
||
overflow: hidden;
|
||
margin: calc(var(--space-l) * -1);
|
||
padding: 6.7cqi 6.7cqi 0;
|
||
}
|
||
|
||
.stadionzeitung-sportheim__text {
|
||
margin-bottom: 4.2cqi;
|
||
font-family: var(--ff-serif);
|
||
font-size: 3.75cqi;
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
.stadionzeitung-sportheim__foto-bereich {
|
||
position: relative;
|
||
flex: 1;
|
||
min-height: 0;
|
||
margin-inline: -6.7cqi;
|
||
}
|
||
|
||
.stadionzeitung-sportheim__foto {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.stadionzeitung-sportheim__foto-fade {
|
||
position: absolute;
|
||
inset: 0;
|
||
background: linear-gradient(180deg, #fff 0%, rgb(255 255 255 / 0) 45%);
|
||
}
|
||
|
||
.stadionzeitung-sportheim__qr-karte {
|
||
position: absolute;
|
||
right: 8cqi;
|
||
bottom: 8cqi;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 1.2cqi;
|
||
padding: 2cqi;
|
||
background: #fff;
|
||
border-radius: var(--radius-btn);
|
||
box-shadow: 0 2px 12px rgb(0 0 0 / 0.18);
|
||
}
|
||
|
||
.stadionzeitung-sportheim__qr {
|
||
width: 20cqi;
|
||
height: 20cqi;
|
||
}
|
||
|
||
.stadionzeitung-sportheim__link {
|
||
font-size: 2.5cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
/* === stadionzeitung-impressum (Live-Seite) === */
|
||
/* Zentriertes Impressum mit Logo und dünner Rahmenlinie (wie das
|
||
Mitglied-Poster) — ersetzt das frühere Canva-Bild, Daten aus club.json. */
|
||
.stadionzeitung-impressum {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 3.3cqi;
|
||
height: 100%;
|
||
padding: 6cqi 5cqi;
|
||
text-align: center;
|
||
border: 1px solid var(--clr-text);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
.stadionzeitung-impressum__logo {
|
||
width: 12cqi;
|
||
height: auto;
|
||
}
|
||
|
||
.stadionzeitung-impressum__name {
|
||
font-size: 8cqi;
|
||
line-height: 1.05;
|
||
text-transform: none;
|
||
margin-bottom: 1.7cqi;
|
||
}
|
||
|
||
.stadionzeitung-impressum__block p {
|
||
font-size: 3cqi;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.stadionzeitung-impressum__label {
|
||
margin-bottom: 0.8cqi;
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 2.7cqi;
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-impressum__note {
|
||
font-size: 2.5cqi;
|
||
color: var(--clr-text-faint);
|
||
}
|
||
|
||
/* === stadionzeitung-partner (Live-Seite) === */
|
||
/* Anzeigen-Akquise im eigenen Heft (Selbstbeweis-Seite, Sie-Form wie
|
||
/partner-werden): Intro, drei Paket-Zeilen mit Icon-Badges (dieselbe
|
||
Sprache wie die Termine-Icons), Ansprechpartner-Zeile, Ticket-CTA. */
|
||
.stadionzeitung-partner__intro {
|
||
margin-bottom: 4.2cqi;
|
||
font-size: 3.4cqi;
|
||
line-height: var(--lh-body);
|
||
}
|
||
|
||
.stadionzeitung-partner__pakete {
|
||
margin: 0 0 4.2cqi;
|
||
padding: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.stadionzeitung-partner__paket {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 3.3cqi;
|
||
padding-block: 2.7cqi;
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.stadionzeitung-partner__paket:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.stadionzeitung-partner__icon {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex: none;
|
||
width: 9.2cqi;
|
||
height: 9.2cqi;
|
||
border-radius: 50%;
|
||
background: rgb(var(--clr-accent-rgb) / 0.12);
|
||
color: var(--clr-accent);
|
||
font-size: 4.2cqi;
|
||
}
|
||
|
||
.stadionzeitung-partner__paket-titel {
|
||
font-family: var(--ff-body);
|
||
font-weight: var(--fw-bold);
|
||
font-size: 4cqi;
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.stadionzeitung-partner__paket-text {
|
||
margin-top: 0.6cqi;
|
||
font-size: 3cqi;
|
||
line-height: 1.45;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-partner__kontakt {
|
||
font-size: 3.2cqi;
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-partner__kontakt strong {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
/* === stadionzeitung-momente (Live-Seite, Heft-Abschluss) === */
|
||
/* 2×2-Galerie der neuesten Instagram-Fotos + Folge-uns-Ticket — quadratische
|
||
Kacheln wie im Instagram-Raster, bewusst ohne Bildunterschriften: die
|
||
Fotos sind der Abschluss des Hefts, kein Nachrichtenteil. */
|
||
/* Natives Instagram-Hochformat (4:5) statt quadratischem Beschnitt — die
|
||
Posts sind oft Grafiken mit Text bis an die Kanten, ein Quadrat-Crop
|
||
schneidet Inhalt ab. Damit vier Hochformate + Ticket auf die A5-Seite
|
||
passen, läuft die Galerie als schmalerer zentrierter Block. */
|
||
.stadionzeitung-momente__galerie {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 2cqi;
|
||
max-width: 73cqi;
|
||
margin: 0 auto 2.5cqi;
|
||
}
|
||
|
||
/* Verdichteter Kopf + Ticket ohne Standard-Abstand — die Fläche gehört auf
|
||
dieser Seite den Fotos. */
|
||
.stadionzeitung-momente .stadionzeitung-page__head {
|
||
margin-bottom: 2.5cqi;
|
||
}
|
||
|
||
.stadionzeitung-momente .stadionzeitung-page__cta {
|
||
margin-top: 1.5cqi;
|
||
}
|
||
|
||
.stadionzeitung-momente__foto {
|
||
display: block;
|
||
width: 100%;
|
||
aspect-ratio: 4 / 5;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* === stadionzeitung-druck (Druckfassung, Broschüren-Montage) === */
|
||
/* Bildschirm-Ansicht der Route ?druck=1: jedes Blatt als A4-quer-Vorschau
|
||
(297:210) mit zwei A5-Seiten, in Broschüren-Reihenfolge (die Montage
|
||
selbst rechnet stadionzeitung-druck.php — CSS kann Seiten nicht über
|
||
Blattgrenzen umsortieren). Die beiden Seiten stoßen in der Blattmitte
|
||
aneinander (dort wird gefaltet), Restbreite bleibt außen. */
|
||
.stadionzeitung-druck {
|
||
padding-block: var(--space-m) var(--space-4xl);
|
||
}
|
||
|
||
.stadionzeitung-druck__hilfe {
|
||
display: grid;
|
||
gap: var(--space-s);
|
||
justify-items: center;
|
||
max-width: 65ch;
|
||
margin: var(--space-xl) auto 0;
|
||
padding-inline: var(--container-pad);
|
||
text-align: center;
|
||
}
|
||
|
||
.stadionzeitung-druck__hilfe-title {
|
||
font-size: var(--fs-650);
|
||
}
|
||
|
||
.stadionzeitung-druck__blaetter {
|
||
display: grid;
|
||
gap: var(--space-l);
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
padding: 0 var(--container-pad);
|
||
list-style: none;
|
||
}
|
||
|
||
.stadionzeitung-druck__blatt-label {
|
||
margin-bottom: var(--space-2xs);
|
||
font-size: var(--fs-300);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.stadionzeitung-druck__flaeche {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
aspect-ratio: 297 / 210;
|
||
background: #fff;
|
||
box-shadow: 0 6px 24px rgb(0 0 0 / 0.35);
|
||
}
|
||
|
||
.stadionzeitung-druck__slot {
|
||
display: flex;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stadionzeitung-druck__slot:first-child {
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.stadionzeitung-druck__slot:last-child {
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
/* Hier lag ein Falz-Steg von 2% NUR auf den Innenbögen (`:nth-child(n+3)`), der
|
||
Umschlagbogen war bündig. Der Gedanke dahinter war richtig: randabfallende
|
||
Anzeigen stoßen sonst mit 0mm an die Blattmitte, was beim Falten im Knick
|
||
verschwindet. Die Umsetzung erzeugte aber unterschiedlich breite Seiten —
|
||
Umschlag 448,5px, Innenblätter 441px — und genau das fiel im Heft auf
|
||
(31.07.2026). Entfernt zugunsten von „alle Bögen gleich, alles randabfallend"
|
||
(Beschluss Felix, siehe @page margin: 0 weiter unten).
|
||
Falls der Steg je zurück soll: dann für ALLE Bögen, nie nur für einen Teil. */
|
||
|
||
/* Jede Seite füllt die Blatthöhe in A5-Proportion — Bilder bringen ihr
|
||
Seitenverhältnis selbst mit (Canva exportiert A5), Live-Seiten bekommen
|
||
es hier; Überstand wird beschnitten statt das Blatt zu sprengen. */
|
||
.stadionzeitung-druck .stadionzeitung-page {
|
||
height: 100%;
|
||
width: auto;
|
||
aspect-ratio: 148 / 210;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.stadionzeitung-druck .stadionzeitung-page__img {
|
||
height: 100%;
|
||
width: auto;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.stadionzeitung-druck .stadionzeitung-cover {
|
||
height: 100%;
|
||
width: auto;
|
||
max-width: none;
|
||
}
|
||
|
||
/* === detail-head (schmaler Kopf der Detailseiten: Veranstaltung, News-Artikel) === */
|
||
/* Kein Hero: die Fest-Seite hat kein eigenes Titelbild, und das Plakat im
|
||
Hochformat würde als cover-beschnittener Hintergrund zerstört; beim
|
||
News-Artikel trägt der Aufmacher im Artikelkörper das Bild (Blog-Optik).
|
||
Gleiche Grundhaltung wie bei der Stadionzeitungs-Ausgabe, die ihren Kopf aber
|
||
bewusst selbst mitbringt (Viewer-Chrome, siehe detail-head.php). */
|
||
.detail-head {
|
||
/* --header-h muss drin sein, sonst liegt der Zurück-Link hinter der festen
|
||
Kopfleiste (gleiche Rechnung wie .stadionzeitung-issue-head). */
|
||
/* --header-space, nicht --header-h: mit --header-h begann der Zurück-Link
|
||
exakt an der Unterkante des Logos, ohne einen Pixel Luft. */
|
||
padding-block: calc(var(--header-space) + var(--space-s)) var(--space-l);
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.detail-head__back {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-3xs);
|
||
min-height: var(--tap-min);
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
text-decoration: none;
|
||
}
|
||
|
||
.detail-head__back:hover {
|
||
color: var(--clr-text);
|
||
}
|
||
|
||
.detail-head__title {
|
||
margin-top: var(--space-2xs);
|
||
}
|
||
|
||
.detail-head__badge {
|
||
display: inline-block;
|
||
vertical-align: middle;
|
||
margin-left: var(--space-2xs);
|
||
padding: 0.2em 0.6em;
|
||
border-radius: var(--radius-btn);
|
||
background: var(--clr-accent);
|
||
color: var(--clr-text);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
.detail-head__meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: var(--space-2xs);
|
||
margin-top: var(--space-2xs);
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
.detail-head__ort {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-3xs);
|
||
}
|
||
|
||
/* === veranstaltungen-cards (Übersicht: was ansteht + Rückblick) === */
|
||
/* EINE Kartenform für beide Abschnitte: Plakat links, Text rechts, volle Breite,
|
||
untereinander gestapelt. Das Hochformat des Plakats bekommt so die Höhe, die
|
||
es braucht, ohne die Karte endlos lang zu machen. */
|
||
.veranstaltungen-cards__list {
|
||
display: grid;
|
||
gap: var(--space-l);
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
.veranstaltung-card {
|
||
display: flex;
|
||
align-items: stretch;
|
||
height: 100%;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
color: inherit;
|
||
text-decoration: none;
|
||
transition: transform var(--transition), border-color var(--transition);
|
||
}
|
||
|
||
.veranstaltung-card:hover {
|
||
transform: translateY(-4px);
|
||
border-color: var(--clr-glass-border-strong);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
.veranstaltung-card__media {
|
||
flex: 0 0 clamp(180px, 26%, 340px);
|
||
background: var(--clr-bg-deep);
|
||
}
|
||
|
||
.veranstaltung-card__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.veranstaltung-card__body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
gap: var(--space-2xs);
|
||
padding: var(--space-2xl);
|
||
}
|
||
|
||
.veranstaltung-card__meta {
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
.veranstaltung-card__text {
|
||
color: var(--clr-text-muted);
|
||
}
|
||
|
||
.veranstaltung-card__cta {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: var(--space-3xs);
|
||
margin-top: var(--space-2xs);
|
||
color: var(--clr-accent);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
.veranstaltung-card:hover .veranstaltung-card__arrow {
|
||
transform: translateX(3px);
|
||
}
|
||
|
||
.veranstaltung-card__arrow {
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
/* Am Handy steht das Plakat über dem Text, sonst bliebe für keines von
|
||
beiden genug Breite. */
|
||
.veranstaltung-card {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.veranstaltung-card__media {
|
||
flex: none;
|
||
}
|
||
|
||
.veranstaltung-card__body {
|
||
padding: var(--space-l);
|
||
}
|
||
}
|
||
|
||
/* === veranstaltung-programm (Tagesprogramm) === */
|
||
.veranstaltung-programm__tage {
|
||
display: grid;
|
||
gap: var(--space-3xl);
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
.veranstaltung-programm__tag-titel {
|
||
padding-bottom: var(--space-2xs);
|
||
border-bottom: 1px solid var(--clr-glass-border);
|
||
}
|
||
|
||
.veranstaltung-programm__liste {
|
||
display: grid;
|
||
gap: var(--space-s);
|
||
margin-top: var(--space-s);
|
||
}
|
||
|
||
.veranstaltung-programm__punkt {
|
||
display: grid;
|
||
grid-template-columns: 5rem minmax(0, 1fr);
|
||
gap: var(--space-s);
|
||
align-items: baseline;
|
||
}
|
||
|
||
/* Uhrzeit bewusst WEISS, nicht in Akzentrot: Rot erreicht auf dem dunklen Grund
|
||
nur ~3,4:1 und ist damit für Text zu schwach (siehe CLAUDE.md, Design). Die
|
||
Spalte trennt die Zeit schon deutlich genug vom Titel; das Rot sitzt
|
||
stattdessen als dünne Linie an der Zeile. */
|
||
.veranstaltung-programm__zeit {
|
||
color: var(--clr-text);
|
||
padding-left: var(--space-2xs);
|
||
border-left: 2px solid var(--clr-accent);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-500);
|
||
font-variant-numeric: tabular-nums;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
.veranstaltung-programm__body {
|
||
display: block;
|
||
}
|
||
|
||
.veranstaltung-programm__titel {
|
||
display: block;
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
.veranstaltung-programm__text {
|
||
display: block;
|
||
margin-top: var(--space-3xs);
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
/* Rückblick: das Programm dokumentiert nur noch, es informiert nicht mehr. */
|
||
.veranstaltung-programm--compact .veranstaltung-programm__tage {
|
||
gap: var(--space-l);
|
||
}
|
||
|
||
.veranstaltung-programm--compact .veranstaltung-programm__titel,
|
||
.veranstaltung-programm--compact .veranstaltung-programm__zeit {
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.veranstaltung-programm__punkt {
|
||
grid-template-columns: 4rem minmax(0, 1fr);
|
||
gap: var(--space-2xs);
|
||
}
|
||
}
|
||
|
||
/* === veranstaltung-info (Angaben + Karte) === */
|
||
/* Links ein Drittel Fakten, rechts zwei Drittel Karte. Das Kartenbild ist nur
|
||
512px breit (selbst gehostetes OSM-Rendering), deshalb bekommt die Karte eine
|
||
Obergrenze statt der halben Seitenbreite — sonst wird sie hochskaliert weich. */
|
||
/* Zwei gleiche Spalten, weil die Karte dann ungefähr ihre eigenen 512px breit
|
||
ist: eine breitere Spalte würde das Bild hochskalieren und die Straßennamen
|
||
weich zeichnen. */
|
||
.veranstaltung-info__panel {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||
gap: var(--space-2xl);
|
||
/* start, nicht center: zentriert schwebte der kürzere Textblock neben der
|
||
hohen Karte im Leeren. Oben auf gleicher Höhe beginnen beide Spalten. */
|
||
align-items: start;
|
||
padding: var(--space-2xl);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
|
||
/* Ein Textblock mit EINER linken Kante statt einer Label/Wert-Tabelle: die
|
||
winzigen Versal-Labels mit Haarlinien-Icons und die zweite Einrückung daneben
|
||
wirkten unruhig und billig (Felix, 30.07.2026). Die Hierarchie macht jetzt
|
||
allein die Schriftgröße. */
|
||
.veranstaltung-info__wann {
|
||
margin-top: var(--space-l);
|
||
font-size: var(--fs-550);
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.veranstaltung-info__beginn {
|
||
margin-top: var(--space-3xs);
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-500);
|
||
}
|
||
|
||
.veranstaltung-info__adresse {
|
||
margin-top: var(--space-l);
|
||
/* <address> läuft per Default kursiv — hier ist es eine Anschrift, kein Zitat. */
|
||
font-style: normal;
|
||
font-size: var(--fs-500);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.veranstaltung-info__hinweis {
|
||
max-width: 44ch;
|
||
margin-top: var(--space-l);
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
}
|
||
|
||
.veranstaltung-info__map {
|
||
margin: 0;
|
||
}
|
||
|
||
/* 4:3 statt des quadratischen Originals: quadratisch war die Karte höher als der
|
||
Textblock daneben und links blieb ein großes Loch. Der zentrierte Beschnitt
|
||
lässt unser Gelände (etwa auf halber Höhe) stehen. */
|
||
.veranstaltung-info__map-link {
|
||
position: relative;
|
||
display: block;
|
||
aspect-ratio: 4 / 3;
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.veranstaltung-info__map-img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.veranstaltung-info__map-cta {
|
||
position: absolute;
|
||
inset-inline: 0;
|
||
bottom: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: var(--space-3xs);
|
||
min-height: var(--tap-min);
|
||
padding: var(--space-2xs) var(--space-s);
|
||
background: var(--clr-accent);
|
||
color: var(--clr-text);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-caps);
|
||
}
|
||
|
||
.veranstaltung-info__map-link:hover .veranstaltung-info__map-cta {
|
||
background: var(--clr-accent-dark);
|
||
}
|
||
|
||
.veranstaltung-info__map-credit {
|
||
margin-top: var(--space-2xs);
|
||
color: var(--clr-text-faint);
|
||
font-size: var(--fs-300);
|
||
}
|
||
|
||
@media (max-width: 992px) {
|
||
.veranstaltung-info__panel {
|
||
grid-template-columns: minmax(0, 1fr);
|
||
gap: var(--space-2xl);
|
||
padding: var(--space-l);
|
||
}
|
||
}
|
||
|
||
/* === veranstaltung-plakate === */
|
||
/* Plakate im Hochformat: nie beschneiden (sie tragen Text), sondern in der
|
||
Höhe begrenzen und nebeneinander stellen. */
|
||
.veranstaltung-plakate__grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: var(--space-l);
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
.veranstaltung-plakate__item {
|
||
flex: 1 1 260px;
|
||
max-width: 420px;
|
||
}
|
||
|
||
.veranstaltung-plakate__link {
|
||
display: block;
|
||
border-radius: var(--radius-card);
|
||
overflow: hidden;
|
||
box-shadow: var(--shadow-card);
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.veranstaltung-plakate__link:hover {
|
||
transform: translateY(-4px);
|
||
}
|
||
|
||
.veranstaltung-plakate__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.veranstaltung-plakate--compact .veranstaltung-plakate__item {
|
||
flex: 0 1 200px;
|
||
max-width: 200px;
|
||
}
|
||
|
||
/* === veranstaltung-galerie + lightbox === */
|
||
.veranstaltung-galerie__grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
|
||
gap: var(--space-2xs);
|
||
margin-top: var(--space-l);
|
||
}
|
||
|
||
.veranstaltung-galerie__link {
|
||
display: block;
|
||
border-radius: var(--radius-photo);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.veranstaltung-galerie__img {
|
||
display: block;
|
||
width: 100%;
|
||
height: 100%;
|
||
aspect-ratio: 4 / 3;
|
||
object-fit: cover;
|
||
transition: transform var(--transition);
|
||
}
|
||
|
||
.veranstaltung-galerie__link:hover .veranstaltung-galerie__img {
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
/* Großansicht. Ohne JS bleibt sie `hidden` und die Vorschaubilder sind normale
|
||
Links auf die Bilddatei — deshalb steht hier nichts, was ohne JS sichtbar
|
||
wäre. z-index über dem Mobil-Menü (25/30), unter dem Skip-Link (100). */
|
||
.lightbox {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 40;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: var(--space-4xl) var(--space-l);
|
||
background: var(--clr-lightbox-bg);
|
||
}
|
||
|
||
/* MUSS (0,2,0) sein: `[hidden]` aus reset.css hat dieselbe Spezifität wie
|
||
`.lightbox` und verliert, weil components.css später lädt — ohne diese Regel
|
||
liegt das Overlay beim Laden offen über der ganzen Seite. */
|
||
.lightbox[hidden] {
|
||
display: none;
|
||
}
|
||
|
||
body.lightbox-open {
|
||
overflow: hidden;
|
||
}
|
||
|
||
.lightbox__img {
|
||
max-width: 100%;
|
||
/* Höhe der Fläche minus dem Platz, den Zähler und Knöpfe brauchen. */
|
||
max-height: calc(100vh - 2 * var(--space-4xl));
|
||
width: auto;
|
||
height: auto;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.lightbox__counter {
|
||
position: absolute;
|
||
bottom: var(--space-l);
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
margin: 0;
|
||
color: var(--clr-text-muted);
|
||
font-size: var(--fs-400);
|
||
text-transform: uppercase;
|
||
letter-spacing: var(--ls-wide);
|
||
}
|
||
|
||
/* (0,2,0) nötig: utilities.css lädt NACH components.css, bei gleicher
|
||
Spezifität gewinnt sonst die .btn-Basis (siehe CLAUDE.md). */
|
||
.btn.lightbox__btn {
|
||
position: absolute;
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border-strong);
|
||
}
|
||
|
||
.btn.lightbox__btn:hover:not(:disabled) {
|
||
background: var(--clr-accent);
|
||
border-color: var(--clr-accent);
|
||
}
|
||
|
||
.btn.lightbox__btn:disabled {
|
||
opacity: 0.35;
|
||
}
|
||
|
||
.btn.lightbox__close {
|
||
top: var(--space-l);
|
||
right: var(--space-l);
|
||
}
|
||
|
||
.btn.lightbox__prev {
|
||
top: 50%;
|
||
left: var(--space-s);
|
||
transform: translateY(-50%);
|
||
}
|
||
|
||
.btn.lightbox__next {
|
||
top: 50%;
|
||
right: var(--space-s);
|
||
transform: translateY(-50%);
|
||
}
|
||
|
||
@media (max-width: 600px) {
|
||
.lightbox {
|
||
padding: var(--space-4xl) var(--space-2xs);
|
||
}
|
||
|
||
/* Am Handy sitzen Vor/Zurück unten neben dem Zähler, wo der Daumen ist. */
|
||
.btn.lightbox__prev,
|
||
.btn.lightbox__next {
|
||
top: auto;
|
||
bottom: var(--space-s);
|
||
transform: none;
|
||
}
|
||
|
||
.btn.lightbox__prev {
|
||
left: var(--space-2xl);
|
||
}
|
||
|
||
.btn.lightbox__next {
|
||
right: var(--space-2xl);
|
||
}
|
||
}
|
||
|
||
/* === Druck (Stadionzeitung) === */
|
||
/* Nur wirksam, wenn eine Stadionzeitungs-Ansicht gedruckt wird — :has()-Scope
|
||
statt global, damit der Druck jeder anderen Seite unangetastet bleibt.
|
||
Farben sind bereits digital hell (.stadionzeitung-page) — hier geht es nur
|
||
um Layout: Chrome ausblenden, A4 quer, ein Broschüren-Blatt pro Bogen.
|
||
Der offizielle Druckweg ist die Druckfassung (?druck=1, Broschüren-
|
||
Reihenfolge fürs Falt-Heft); der normale Viewer druckt als einfacher
|
||
Seiten-Stapel (eine Seite pro Blatt, Lesereihenfolge) — als Fallback
|
||
brauchbar, fürs Heft die Druckfassung nehmen. Nach jeder Änderung mit
|
||
einer echten Druckvorschau prüfen. */
|
||
@media print {
|
||
body:has(.stadionzeitung-viewer),
|
||
body:has(.stadionzeitung-druck) {
|
||
background: #fff !important;
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
}
|
||
|
||
body:has(.stadionzeitung-viewer) .site-header,
|
||
body:has(.stadionzeitung-viewer) .site-footer,
|
||
body:has(.stadionzeitung-viewer) .skip-link,
|
||
body:has(.stadionzeitung-viewer) .back-to-top,
|
||
body:has(.stadionzeitung-viewer) .stadionzeitung-issue-head,
|
||
body:has(.stadionzeitung-viewer) .stadionzeitung-viewer__controls,
|
||
body:has(.stadionzeitung-druck) .site-header,
|
||
body:has(.stadionzeitung-druck) .site-footer,
|
||
body:has(.stadionzeitung-druck) .skip-link,
|
||
body:has(.stadionzeitung-druck) .back-to-top,
|
||
body:has(.stadionzeitung-druck) .stadionzeitung-issue-head,
|
||
body:has(.stadionzeitung-druck) .stadionzeitung-druck__hilfe,
|
||
body:has(.stadionzeitung-druck) .stadionzeitung-druck__blatt-label {
|
||
display: none !important;
|
||
}
|
||
|
||
/* Chrome rendert WEICHE Schatten im PDF-Export als harte graue Kästen
|
||
(dokumentierte Falle, siehe Cover-Kommentar zu text-shadow) — im
|
||
Druck fliegen deshalb pauschal alle Schatten der Stadionzeitungs-
|
||
Seiten raus. Konturen kommen aus Rahmenlinien (Polaroids, Tickets),
|
||
nicht aus Schatten; am Bildschirm bleiben die Schatten erhalten. */
|
||
body:has(.stadionzeitung-druck) .stadionzeitung-druck *,
|
||
body:has(.stadionzeitung-viewer) .stadionzeitung-viewer * {
|
||
box-shadow: none !important;
|
||
text-shadow: none !important;
|
||
}
|
||
|
||
/* ALLE Bögen laufen randabfallend (Beschluss 31.07.2026, Felix).
|
||
Vorher hatten nur die zwei Umschlagbögen `margin: 0`, die Innenblätter
|
||
einen 5mm-Rahmen — und genau daraus entstand die Ungleichmäßigkeit, die
|
||
im Druck störte. Der Rahmen war nämlich nicht das Problem: der
|
||
Blatt-Inhalt war über eine aspect-ratio bemaßt und nur 199,1mm hoch
|
||
statt 210mm. Die fehlenden 10,9mm sammelten sich IMMER unten, weil der
|
||
Inhalt oben ausgerichtet ist. Gemessen an Blatt 4:
|
||
margin 5mm → oben 4,8 / unten 5,8 mm (Rest 0,6 fiel kaum auf)
|
||
margin 0 → oben 0,0 / unten 10,9 mm (der ganze Rest unten)
|
||
Ein Rand hätte den Fehler also nur kaschiert, nicht behoben.
|
||
|
||
Randabfallend ist hier die geometrisch saubere Lösung, weil zwei A5
|
||
EXAKT ein A4 quer sind (2 × 148,5 = 297mm, Überschuss null). Es bleibt
|
||
nichts zu verteilen, die Asymmetrie kann nicht mehr entstehen.
|
||
|
||
Was der Bürodrucker daraus macht, ist bewusst in Kauf genommen: er
|
||
beschneidet auf sein Hardware-Minimum (~4 bis 5mm) und liefert damit
|
||
selbst einen Rand. Unser Fließtext verliert dabei nichts, die
|
||
Live-Seiten haben 6,7cqi = 9,9mm Innenabstand. Ein Copyshop mit
|
||
Randlos-Modus druckt es wirklich bis an die Kante.
|
||
KEINE @page-Randbreite wieder einführen, ohne die Blatt-Höhe
|
||
gegenzurechnen — sonst ist der Überschuss zurück. */
|
||
@page {
|
||
size: A4 landscape;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Diese vier Regeln galten vorher nur für die Umschlagbögen
|
||
(`:nth-child(-n+2)`) und sind jetzt der Normalfall — die Ausnahme wurde
|
||
aufgelöst, nicht erweitert. */
|
||
.stadionzeitung-druck__blatt .stadionzeitung-druck__flaeche {
|
||
height: 210mm;
|
||
}
|
||
|
||
.stadionzeitung-druck__blatt .stadionzeitung-druck__slot {
|
||
flex: 1;
|
||
}
|
||
|
||
.stadionzeitung-druck__blatt .stadionzeitung-page__img,
|
||
.stadionzeitung-druck__blatt .stadionzeitung-cover,
|
||
.stadionzeitung-druck__blatt .stadionzeitung-page {
|
||
width: 100%;
|
||
height: 100%;
|
||
aspect-ratio: auto;
|
||
object-fit: cover;
|
||
}
|
||
|
||
/* Vorwort/Termine bringen eine eigene aspect-ratio mit — auf dem
|
||
randlosen Bogen sollen sie die volle Slot-Höhe füllen (das
|
||
Wiesen-Foto blutet dann bis an die Blattkante). */
|
||
.stadionzeitung-druck__blatt .stadionzeitung-vorwort {
|
||
aspect-ratio: auto;
|
||
height: calc(100% + 2 * var(--space-l));
|
||
}
|
||
|
||
/* --- Druckfassung: ein Broschüren-Blatt pro Bogen --- */
|
||
.stadionzeitung-druck {
|
||
padding: 0;
|
||
}
|
||
|
||
.stadionzeitung-druck__blaetter {
|
||
display: block;
|
||
max-width: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.stadionzeitung-druck__blatt {
|
||
break-after: page;
|
||
break-inside: avoid;
|
||
}
|
||
|
||
.stadionzeitung-druck__blatt:last-child {
|
||
break-after: auto;
|
||
}
|
||
|
||
/* Feste Höhe in mm statt aspect-ratio: der bedruckbare Bereich ist
|
||
210mm − 2×5mm Rand — 199mm statt exakt 200mm als Rundungspuffer, die
|
||
Fläche darf den Bogen nie überragen, sonst schiebt der Überstand eine
|
||
Leerseite in den Ausdruck. Der kleine seitliche Rest (die A5-Seiten
|
||
behalten ihre exakte Proportion) bleibt als schmaler weißer Streifen
|
||
außen — innen an der Falz stoßen die Seiten bündig aneinander. */
|
||
.stadionzeitung-druck__flaeche {
|
||
aspect-ratio: auto;
|
||
height: 199mm;
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* --- Fallback: normaler Viewer gedruckt (Seiten-Stapel) --- */
|
||
.stadionzeitung-viewer {
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.stadionzeitung-viewer__track {
|
||
display: block !important;
|
||
overflow: visible !important;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.stadionzeitung-viewer__page {
|
||
display: block !important;
|
||
flex: none !important;
|
||
break-inside: avoid;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.stadionzeitung-viewer .stadionzeitung-page,
|
||
.stadionzeitung-viewer .stadionzeitung-cover {
|
||
max-width: none;
|
||
max-height: none !important;
|
||
overflow: visible !important;
|
||
width: 100%;
|
||
margin-inline: auto;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.stadionzeitung-viewer .stadionzeitung-page__img {
|
||
max-height: none !important;
|
||
width: 100% !important;
|
||
height: auto !important;
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* Vorwort/Termine behalten ihre aspect-ratio (dieselbe A5-Fläche, auf der
|
||
das Hintergrundbild einblendet) — nur der Scroll-Affordance fällt weg
|
||
(overflow-y: auto rendert im PDF-Export sichtbares Scrollbar-Chrome).
|
||
In der Druckfassung beschneidet der Seiten-Container Überstand
|
||
(overflow: hidden auf .stadionzeitung-page, s. o.). */
|
||
/* width/height müssen das Seiten-Padding MIT abdecken: die negativen
|
||
Ränder der beiden Komponenten heben es auf, ein reines 100% misst
|
||
aber nur den Innenbereich — das Foto endete sonst ein Padding
|
||
(~10mm) vor der rechten/unteren Blattkante. */
|
||
.stadionzeitung-vorwort,
|
||
.stadionzeitung-termine {
|
||
max-width: none;
|
||
width: calc(100% + 2 * var(--space-l));
|
||
overflow-y: visible;
|
||
}
|
||
|
||
/* Sportheim: gleiche Breiten-Korrektur, aber overflow bleibt hidden —
|
||
die rotierten Pinselstrich-Balken ragen konstruktionsbedingt über die
|
||
Seite hinaus und würden sonst auf die Nachbarseite drucken. */
|
||
.stadionzeitung-sportheim {
|
||
max-width: none;
|
||
width: calc(100% + 2 * var(--space-l));
|
||
overflow: hidden;
|
||
}
|
||
}
|
||
|