Die Turnabteilung (Maxi) beantwortet laufend Anfragen nach einem Wartelistenplatz für „Tiger" und „Hühner", den es nicht gibt. Auf der alten Seite stand der Hinweis direkt über dem Anfrageformular, hier fehlte er. Schlimmer: an der Stelle stand das GEGENTEIL. Der bisherige Hinweis lautete "Aktuell sind alle Kinderturngruppen belegt. Bei Interesse setzen wir sie gerne auf die Warteliste." — für alle Kindergruppen, also auch für die beiden, die keine Liste führen. Der Satz hat einen Teil der Anfragen vermutlich erst erzeugt. Er ist nicht gelöscht, sondern auf die übrigen Gruppen eingegrenzt und aufs Du umgestellt (Haus-Stil; vorher stand dort klein "sie"). Der Text steht genau EINMAL, in data/turnen.json unter disciplines.eltern-kind-turnen.notice + notice_title. Zwei Stellen lesen ihn: die rote Kachel auf /turnen/eltern-kind-turnen (gab es schon) und neu ein Block direkt über dem Formular auf /turnen. Ändert die Abteilung die Lage, ist das eine Zeile in einer Datei. Umsetzung: - notice_title ist neu, weil eine rote Fläche ohne Überschrift wie eine Fehlermeldung liest. Abel statt Coolvetica, der Schnitt ist bei ~19px in Versalien unleserlich (CLAUDE.md, Design). - Der Block steht AUSSERHALB des <form> und damit außerhalb der role="status"-Region: ein dauerhafter Hinweis, keine Rückmeldung zum Absenden. In der Live-Region läse ihn ein Screenreader bei jeder Formularmeldung erneut vor. Kein role="alert", er steht schon beim Laden da. - Überschrift als h3 unter der h2 des Intro-Blocks, damit die Folge nicht springt. - .contact__notice übernimmt bewusst die Optik von .turnen-dash__notice, damit beide Stellen als derselbe Hinweis erkennbar sind. Weiß auf --clr-accent erreicht 4,92:1 und trägt damit Fließtext (WCAG 1.4.3). Kommt ein dritter Ort dazu, gehören die gemeinsamen Eigenschaften in eine geteilte .notice-Klasse; für zwei wäre das verfrüht. Gemessen: Hinweisbreite gleich Formularbreite (720px bei 1280, 347px bei 390), kein Überlauf. preflight ohne neue Meldungen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NzeVVgMCrzCDJAKeLEdKr7
8404 lines
233 KiB
CSS
8404 lines
233 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);
|
||
}
|
||
|
||
/* Hinweis direkt über dem Formular (Wartelisten-Status). Bewusst dieselbe Optik wie
|
||
.turnen-dash__notice, damit es auf /turnen und der Kinderturn-Unterseite als ein und
|
||
derselbe Hinweis erkennbar ist. Kommt ein dritter Ort dazu, gehören die gemeinsamen
|
||
Eigenschaften in eine geteilte .notice-Klasse; für zwei wäre das verfrüht.
|
||
Weiß auf --clr-accent erreicht 4,92:1 und trägt damit Fließtext (WCAG 1.4.3);
|
||
die Breite folgt dem Formular (.form max-width: 720px), sonst stünde der Kasten
|
||
sichtbar über die Feldkante hinaus. */
|
||
.contact__notice {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: var(--space-xs);
|
||
width: 100%;
|
||
max-width: 720px;
|
||
padding: var(--pad-card);
|
||
border-radius: var(--radius-card);
|
||
background: var(--clr-accent);
|
||
color: #fff;
|
||
}
|
||
|
||
.contact__notice-icon {
|
||
flex: none;
|
||
margin-top: 0.15em;
|
||
font-size: var(--icon-inline-lg);
|
||
}
|
||
|
||
.contact__notice-title {
|
||
margin: 0 0 var(--space-3xs);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-600);
|
||
letter-spacing: var(--ls-caps);
|
||
text-transform: uppercase;
|
||
color: #fff;
|
||
}
|
||
|
||
.contact__notice p {
|
||
margin: 0;
|
||
max-width: none;
|
||
font-size: var(--fs-600);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.form {
|
||
display: grid;
|
||
gap: var(--space-m);
|
||
width: 100%;
|
||
max-width: 720px;
|
||
/* --pad-card statt fester 32px: auf einem 309px-Handy blieben dem Eingabefeld
|
||
sonst nur 71% der Bildschirmbreite, weil Container- und Kartenabstand sich
|
||
stapeln. Obergrenze ist derselbe Wert wie vorher, am Desktop ändert sich
|
||
nichts. */
|
||
padding: var(--pad-card);
|
||
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;
|
||
}
|
||
|
||
/* Abel statt Coolvetica: die Zeile ist mit ~19px weit unter den 28px, ab denen der
|
||
Condensed-Schnitt in Versalien lesbar bleibt (siehe CLAUDE.md, Design). */
|
||
.turnen-dash__notice-title {
|
||
margin: 0 0 var(--space-3xs);
|
||
font-family: var(--ff-body);
|
||
font-size: var(--fs-600);
|
||
letter-spacing: var(--ls-caps);
|
||
text-transform: uppercase;
|
||
color: #fff;
|
||
}
|
||
|
||
.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;
|
||
/* minmax(0, …) statt nackter fr-Werte: eine fr-Spur hat als automatisches
|
||
Minimum min-content und kann deshalb nicht schmaler werden als das längste
|
||
Wort darin. Bei deutschen Komposita sprengte das auf schmalen Bildschirmen
|
||
die Karte: bei 300px rechnete sich die einzelne Spur auf 267,7px aus,
|
||
obwohl die Karte nur 193px Inhaltsfläche hat — das Dokument lief um 28px
|
||
über und der Text stand über den Rand hinaus (31.07.2026).
|
||
min-width: 0 am Grid-KIND allein reicht dafür nicht, die SPUR muss es
|
||
erlauben. Dieselbe Korrektur steht bei .group-list--stacked, wo dasselbe
|
||
Muster latent drinsteckte. */
|
||
grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
|
||
gap: var(--space-4xl);
|
||
align-items: start;
|
||
padding: var(--pad-card-lg) var(--pad-card-lg);
|
||
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: minmax(0, 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(--pad-card) var(--pad-card);
|
||
background: var(--clr-glass-bg);
|
||
border: 1px solid var(--clr-glass-border);
|
||
border-radius: var(--radius-card);
|
||
}
|
||
|
||
/* Gestapelt: Beschreibung links, Trainingszeiten-Panel rechts.
|
||
minmax(0, …) statt nackter fr-Werte: eine fr-Spur hat als automatisches Minimum
|
||
min-content, kann also nicht schmaler werden als das längste Wort darin. Bei
|
||
deutschen Komposita („sportartenübergreifende", „Grundlagenausbildung") sprengte
|
||
das auf schmalen Bildschirmen die Karte — bei 300px war der Karteninhalt 268px
|
||
breit in 193px Fläche und das Dokument lief um 28px über (31.07.2026).
|
||
min-width: 0 am Grid-KIND allein reicht dafür nicht, die Spur muss es erlauben. */
|
||
.group-list--stacked .group-card {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.6fr) minmax(0, 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: minmax(0, 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;
|
||
}
|
||
|
||
/* Grid-Kind der Karte: OHNE min-width: 0 darf es nicht unter seine min-content-
|
||
Breite schrumpfen, und die bestimmt bei deutschen Komposita das längste Wort
|
||
(„sportartenübergreifende", „Grundlagenausbildung"). Bei 300px Bildschirm war der
|
||
Body dadurch 268px breit in einer Karte mit 193px Inhaltsfläche, das Dokument lief
|
||
um 28px über und der Text stand über den Rand hinaus (31.07.2026).
|
||
Dieselbe Falle wie bei .stadionzeitung-page .league-table.
|
||
hyphens: auto trennt die Komposita sauber statt sie hart umzubrechen — die Seite
|
||
ist als lang="de" ausgezeichnet, der Browser kennt also die Trennregeln.
|
||
overflow-wrap bleibt als Notausgang für Wörter ohne mögliche Trennstelle. */
|
||
.group-card__body {
|
||
min-width: 0;
|
||
hyphens: auto;
|
||
overflow-wrap: break-word;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
}
|
||
|