/* ============================================================ 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):
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 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

(level-Prop, siehe person-card.php) statt als

wie im Ansprechpartner-Grid — base.css gibt h4 damit Abel statt Coolvetica, bei gleicher Schriftgröße (--fs-650) liest sich das als fette Blocktype statt kompakter Headline. In der schmalen 16rem-Sidebar wirkt das riesig UND (mangels color-Override) heller als der gedämpfte Name darunter. Gleiche kleine Kicker-Optik wie .position-card__area direkt nebenan — der Name bleibt die eigentliche Aussage. */ .position-card__contact .person-card__department { font-size: var(--fs-300); font-weight: var(--fw-bold); letter-spacing: var(--ls-wide); color: var(--clr-text-muted); } @media (max-width: 768px) { .position-card__body { grid-template-columns: 1fr; gap: var(--space-l); padding: 0 var(--space-l) var(--space-l); } .position-card__contact { max-width: 18rem; } } /* === contact form === */ .contact__inner { display: grid; gap: var(--space-3xl); justify-items: center; } .contact__intro { max-width: 60ch; text-align: center; } .contact__intro p { margin-top: var(--space-s); } .form { display: grid; gap: var(--space-m); width: 100%; max-width: 720px; padding: var(--space-2xl); background: var(--clr-glass-bg); border: 1px solid var(--clr-glass-border); border-radius: var(--radius-card); } .form__row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-m); } @media (max-width: 600px) { .form__row { grid-template-columns: 1fr; } } /* Floating Label: Label liegt im leeren Feld wie ein Platzhalter und wandert bei Fokus oder Inhalt nach oben. Rein visuell — das echte