/* ============================================================
   TCLE – Design Foundation
   base.css · Mobile-First · WCAG 2.2 AA · 2026
   ============================================================
   Inhalt:
   1. Design Tokens  (Farben, Typografie, Spacing, Layout)
   2. Modern CSS Reset
   3. Globale Basis-Stile (Body, Links, Bilder)
   4. Typografie (Überschriften, Utility-Klassen)
   5. Layout-Utilities (.inner, .sec, .two, .sec-hdr)
   6. Buttons
   7. Shared Components (News Cards, Divider, Footer)
   8. Accessibility (Focus, Reduced Motion)
   ============================================================ */


/* ════════════════════════════════════════════════════════════
   0. SCHRIFTARTEN (lokal gehostet, /fonts/)
   Urbanist          – Fließtext, Navigation, UI (400–800)
   Libre Baskerville – Überschriften h1–h3       (400 / 700)

   Beide Schriften liegen im Projekt und werden NICHT mehr von
   fonts.googleapis.com / fonts.gstatic.com geladen. Grund ist der
   Datenschutz: Beim Laden von Google-Servern geht bei jedem
   Seitenaufruf die IP-Adresse des Besuchers an Google. Lokal
   ausgeliefert verlässt kein Request die eigene Domain – deshalb
   sind die beiden Google-Hosts auch aus der CSP (_headers) raus.

   Je Familie gibt es zwei Dateien (latin / latin-ext) – dieselbe
   Aufteilung und dieselben Zeichenbereiche, die Google ausgeliefert
   hat. Es sind Variable Fonts, eine Datei deckt also alle Gewichte
   ab; die Schnitte unten zeigen deshalb mehrfach auf dieselbe Datei
   (geladen wird sie trotzdem nur einmal).

   Die Schnitte sind einzeln deklariert – 400/700 bzw.
   400/500/600/700/800 – und nicht als Spanne (400 700 / 100 900).
   Das ist Absicht: Genau diese Schnitte hat Google geliefert, und
   nur so bleibt die Darstellung unverändert. Beispiel .hero-h1: Sie
   verlangt Libre Baskerville in 600, das es als Schnitt nicht gibt,
   und wird deshalb auf 700 abgebildet. Mit einer Spanne 400–700
   würde der Variable Font stattdessen echte 600 rendern – die
   Startseiten-Überschrift käme dünner heraus als bisher.

   font-display: swap wie zuvor – Text ist sofort lesbar, die Schrift
   wird nachgezogen. Die @font-face-Regeln stehen hier am Anfang von
   base.css statt als eigener <link> im <head>: base.css wird ohnehin
   als Erstes geladen, damit bleibt die Anfragekette gleich kurz wie
   vorher und es kommt keine zusätzliche Datei dazu.
   ════════════════════════════════════════════════════════════ */

/* latin-ext */
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/libre-baskerville-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/libre-baskerville-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/libre-baskerville-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/libre-baskerville-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/urbanist-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/urbanist-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/urbanist-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/urbanist-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/urbanist-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/urbanist-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/urbanist-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/urbanist-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('../fonts/urbanist-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
  font-family: 'Urbanist';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('../fonts/urbanist-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ════════════════════════════════════════════════════════════
   1. DESIGN TOKENS
   ════════════════════════════════════════════════════════════ */
:root {

  /* ── FARBEN: Brand (Gelb) ───────────────────────────────── */
  --color-brand:    #F6E633;   /* Gelb – Primär-Akzent      */
  --color-brand-dk: #D1C009;   /* Gelb dunkel – Hover       */
  --color-brand-lt: #F9EE72;   /* Gelb hell – Labels        */
  --color-brand-bg: rgba(246,230,51,.12);

  /* ── FARBEN: Blau (Vereinsfarbe) ────────────────────────── */
  --color-navy:    #214F98;
  --color-navy-dk: #1B407A;
  --color-navy-lt: #2D6BCD;
  --color-navy-bg: rgba(33,79,152,.10);

  /* ── FARBEN: Hintergründe ───────────────────────────────── */
  --color-bg-base: #F0F0F1;    /* Grundton, nie überschrieben */
  --color-bg:      var(--color-bg-base); /* Flächen-Basis     */
  --color-bg-alt:  #FFFFFF;    /* Abschnittswechsel (weiß)   */
  --color-bg-card: #FFFFFF;    /* Karten-Hintergrund         */
  --color-tint:    #E5E5E6;    /* Hover-/UI-Tint             */

  /* ── FARBEN: Text ───────────────────────────────────────── */
  --color-text:      #000000;
  --color-text-soft: #5B5B5B;  /* aus Grau #8E8E8E abgeleitet – 6.8:1 auf Weiß (WCAG AA) */
  --color-text-mute: #686868;  /* aus Grau #8E8E8E abgeleitet – 5.6:1 auf Weiß (WCAG AA) */

  /* ── FARBEN: Rahmen ─────────────────────────────────────── */
  --color-border:   #E2E2E2;   /* helle Variante von Grau #8E8E8E */
  --color-border-h: rgba(0,0,0,.18);
  /* Kräftigerer Rahmen für Karten, die auf einer nur minimal anderen
     Fläche liegen. In weißen Abschnitten werden Karten mit #F0F0F1
     gefüllt – das sind 1,14:1 gegen den Abschnitt, und --color-border
     kommt gegen die Füllung ebenfalls nur auf 1,14:1, der Rahmen tut
     dort also nichts. #D2D2D4 kommt auf 1,33:1 gegen #F0F0F1 und
     1,49:1 gegen Weiß und trägt damit in beiden Richtungen. */
  --color-border-card: #D2D2D4;

  /* ── FARBEN: Grün (Natur / Tennisplatz) ─────────────────── */
  --color-green:    #318366;
  /* Dunklere Variante für Text auf hellem Grund. #318366 erreicht auf
     dem Grundton #F0F0F1 nur 4,04:1 und verfehlt damit die 4,5:1 von
     WCAG AA (auf Weiß sind es 4,60:1 – knapp bestanden). #2C7459 kommt
     auf 4,92:1 auf Grau und 5,60:1 auf Weiß und bleibt derselbe Ton.
     --color-green bleibt unverändert: er trägt auf den Unterseiten
     Kalender-Pills mit weißer Schrift, wo der hellere Ton richtig ist. */
  --color-green-dk: #2C7459;

  /* ── FARBEN: Formular-Status (Fehler/Warnung) ───────────── */
  --color-error:   #c0392b;
  --color-warning: #e67e22;

  /* ── BACKWARD-COMPAT: alte Token-Namen ─────────────────────
     Werden schrittweise durch --color-* ersetzt.
     Nicht mehr in neuem Code verwenden.
  ─────────────────────────────────────────────────────────── */
  --orange:    var(--color-brand);
  --orange-dk: var(--color-brand-dk);
  --orange-lt: var(--color-brand-lt);
  --navy:      var(--color-navy);
  --navy-dk:   var(--color-navy-dk);
  --navy-bg:   var(--color-navy-bg);
  --bg-2:      var(--color-tint);
  --bg-card:   var(--color-bg-card);
  --text:      var(--color-text);
  --text-soft: var(--color-text-soft);
  --text-mute: var(--color-text-mute);
  --border:    var(--color-border);
  --green:     var(--color-green);
  /* Sehr alte Namen aus initialem Refactoring */
  --terra-bg:  var(--color-brand-bg);
  --ink:       var(--color-text);
  --ink-soft:  var(--color-text-soft);
  --ink-muted: var(--color-text-mute);
  --cream:     var(--color-tint);
  --cream-bd:  var(--color-border);

  /* ── TYPOGRAFIE: Font Family ──────────────────────────────
     --font-body    → Fließtext, Navigation, UI-Elemente
     --font-heading → Überschriften h1–h3
     --ff bleibt als projektweiter Alias auf die Body-Schrift.
  ─────────────────────────────────────────────────────────── */
  --font-body:    'Urbanist', system-ui, sans-serif;
  --font-heading: 'Libre Baskerville', Georgia, serif;
  --ff: var(--font-body);

  /* ── TYPOGRAFIE: Fluid Font-Size-Skala ──────────────────────
     Formel: clamp(min, preferred, max)
     Viewport-Ankerpunkte: 320px (min) → 1440px (max)
     Methodologie: utopia.fyi – proportionale Stufung
  ─────────────────────────────────────────────────────────── */
  --fs-2xs:    clamp(.6rem,   .57rem + .15vw,  .7rem);
  /* 9.6px → 11.2px  │ Badges, Mikrolabels                  */
  --fs-xs:     clamp(.68rem,  .64rem + .2vw,   .78rem);
  /* 10.9px → 12.5px │ Eyebrows, Kategorien                 */
  --fs-sm:     clamp(.78rem,  .73rem + .25vw,  .875rem);
  /* 12.5px → 14px   │ UI-Text klein, Metadaten             */
  --fs-base:   clamp(1rem,    1rem + .1vw,     1.125rem);
  /* 16px → 18px     │ Fließtext                            */
  --fs-md:     clamp(1rem,    .95rem + .25vw,  1.05rem);
  /* 16px → 16.8px   │ Body-Variante, leicht größer         */
  --fs-lg:     clamp(.875rem, .8rem + .4vw,    1rem);
  /* 14px → 16px     │ h6, größere UI-Labels                */
  --fs-xl:     clamp(.9rem,   .82rem + .4vw,   1.1rem);
  /* 14.4px → 17.6px │ h5                                   */
  --fs-2xl:    clamp(1rem,    .88rem + .6vw,   1.35rem);
  /* 16px → 21.6px   │ h4                                   */
  --fs-3xl:    clamp(1.2rem,  1rem + 1vw,      1.75rem);
  /* 19.2px → 28px   │ h3                                   */
  --fs-4xl:    clamp(1.6rem,  1.2rem + 2vw,    3rem);
  /* 25.6px → 48px   │ h2                                   */
  --fs-5xl:    clamp(2rem,    1.5rem + 2.5vw,  4rem);
  /* 32px → 64px     │ h1                                   */
  --fs-display:clamp(2.2rem,  1.8rem + 2vw,    3.5rem);
  /* 35.2px → 56px   │ .h2-Utility (Display-Headline)       */

  /* ── TYPOGRAFIE: Zeilenhöhen ────────────────────────────── */
  --leading-none:    1;
  --leading-tight:   1.05;
  --leading-snug:    1.25;
  --leading-normal:  1.75;
  --leading-relaxed: 1.85;

  /* ── TYPOGRAFIE: Schriftgewichte ────────────────────────── */
  --weight-normal:    400;
  --weight-medium:    500;
  --weight-semibold:  600;
  --weight-bold:      700;
  --weight-extrabold: 800;

  /* ── TYPOGRAFIE: Letter Spacing ─────────────────────────── */
  --tracking-tight:   -.055em;
  --tracking-snug:    -.04em;
  --tracking-normal:  -.02em;
  --tracking-slight:  -.01em;
  --tracking-wide:    .08em;
  --tracking-wider:   .12em;
  --tracking-widest:  .16em;

  /* ── TYPOGRAFIE: Tracking der Überschriften (fluid) ─────────
     Ein fester em-Wert ist immer nur für eine Schriftgröße richtig.
     Die Überschriften hier ändern ihre Größe über den Viewport aber
     um das Zwei- bis Dreifache (h1: 32px → 64px, .hero-h1 in
     home.css: 38px → 87px), und Libre Baskerville braucht mit
     wachsender Größe anteilig mehr Verengung, mit fallender Größe
     weniger. Gemessen: bei 87px stießen in "Schlag groß" S und c
     fast zusammen, bei 14,7px lief .form-success h3 mit demselben
     -.04em wie eine 48px-Überschrift und wirkte gequetscht.

     Deshalb je Größenstufe ein eigener Verlauf statt der drei festen
     Stufen oben. Aufbau: clamp(engster Wert, Verlauf, weitester Wert).
     Die Grenzen stehen in em (skalieren also mit der Schriftgröße),
     der mittlere Term in rem + vw – dieselbe Methodik wie bei den
     --fs-*-Tokens und an denselben Ankerpunkten 320px → 1440px.
     Achtung bei negativen Werten: das erste clamp-Argument ist der
     mathematisch kleinere, also der engere Wert.

     Jedes Token gehört zu genau einer --fs-*-Stufe und ist auf deren
     Größenbereich gerechnet. Wird eine Überschriftengröße geändert,
     gehört das Tracking mit angepasst – sonst passen die Grenzen
     nicht mehr zur tatsächlich gerenderten Größe.

     Die festen negativen Werte oben bleiben unverändert stehen und sind
     für Text gedacht, dessen Größe sich nicht über den Viewport ändert:
     --tracking-slight (-.01em) für UI-Text in normaler Größe (.btn,
     .mob-sub-item, .f-brand-name, .form-success h3, .tk-deadline-date),
     --tracking-normal (-.02em) für Ziffern und mittlere Überschriften
     (.news-date-num, .tk-calc-total, .tr-feature-title, .faq-q),
     --tracking-snug (-.04em) für große, fett gesetzte Zahlen und
     Zierschrift (.ev-day, .cta-banner::before). --tracking-tight
     (-.055em) hat aktuell keine Verwendung, gehört aber als Stufe zur
     Skala.

     Einzelfälle bleiben bewusst lokal, weil sie je Element auf eine
     feste Schriftgröße gemessen sind und ein eigener Token für genau
     eine Verwendung nichts bindet: -.008em (.ev-title), -.011em
     (.tr-title), -.015em und -.05em (die Nächster-Termin-Variante von
     .ev-title und .ev-day, beide enger als die Grundregel) sowie
     -.025em (.mob-item/.mob-back, größer gesetzt als .mob-sub-item).

     Die Grenzen sind gegenüber der ersten Fassung zurückgenommen, weil
     dort Serifen benachbarter Buchstaben ineinanderliefen. Gemessen im
     Browser (Glyphen einzeln gerendert, überlagerte Deckflächen gezählt):
     bei -.04em verschmolzen in "Kleinsten" die obere K-Serife und die
     l-Serife über 4px², in "Schlag" h und l auf der Grundlinie über
     5,6px². Ohne negatives Tracking berührte sich nichts. Die Rücknahme
     wächst mit der Größenstufe, weil die Verläufe zum großen Ende hin
     enger werden und der Fehler dort am größten ist: 3xl +.008em,
     4xl +.015em, display +.018em, 5xl +.015em. Das eine Paar, das auch
     danach bleibt, ist "Kl" – dort überhängt die K-Serife in Libre
     Baskerville von Haus aus, unabhängig vom Tracking.
  ─────────────────────────────────────────────────────────── */
  --tracking-3xl:     clamp(-.014em, .001rem   - .028vw, -.004em);
  /* h3, .prose h2/h3, .news-title  │ 19,2px → 28px            */
  --tracking-4xl:     clamp(-.025em, -.0094rem - .073vw, -.015em);
  /* h2, .page-hero h1              │ 25,6px → 48px            */
  --tracking-5xl:     clamp(-.03em,  -.0197rem - .111vw, -.021em);
  /* h1                             │ 32px → 64px              */
  --tracking-display: clamp(-.014em, -.0086rem - .045vw, -.008em);
  /* .h2-Utility                    │ 35,2px → 56px            */
  /* Diese Stufe brauchte einen zweiten Schritt (+.012em): bei -.026em
     verschmolz in "Instagram" und "Förderstufen" das Ohr des g mit der
     Serife des r (3,1px²), in "Jugendbuchung" e und c (5,7px²). Kostet
     auf Desktop keinen einzigen zusätzlichen Umbruch, unter 400px drei. */

  /* ── SPACING-SKALA (fluid, clamp-basiert) ───────────────────
     Benannt nach T-Shirt-Größen / numerischer Skala.
     Basis: 1rem = 16px. Faktor ~1.5× je Stufe.
  ─────────────────────────────────────────────────────────── */
  --space-1:  clamp(.25rem,  .2rem  + .25vw,  .375rem);
  /* 4px → 6px                                              */
  --space-2:  clamp(.5rem,   .4rem  + .5vw,   .75rem);
  /* 8px → 12px                                             */
  --space-3:  clamp(.75rem,  .65rem + .5vw,   1rem);
  /* 12px → 16px                                            */
  --space-4:  clamp(1rem,    .85rem + .75vw,  1.25rem);
  /* 16px → 20px                                            */
  --space-5:  clamp(1.25rem, 1rem   + 1.25vw, 1.75rem);
  /* 20px → 28px                                            */
  --space-6:  clamp(1.5rem,  1.25rem + 1.25vw, 2rem);
  /* 24px → 32px                                            */
  --space-8:  clamp(2rem,    1.5rem + 2.5vw,  3.5rem);
  /* 32px → 56px                                            */
  --space-10: clamp(2.5rem,  2rem   + 2.5vw,  4rem);
  /* 40px → 64px                                            */
  --space-12: clamp(3rem,    2.25rem + 3.75vw, 5rem);
  /* 48px → 80px                                            */

  /* Semantische Spacing-Aliases (für Sektionen & Layout) */
  --space-inline:     clamp(1rem,  4vw, 2rem);     /* Seitl. Padding  */
  --space-section:    clamp(3rem,  8vw, 7rem);     /* Sektions-Padding */
  --space-section-sm: clamp(2rem,  5vw, 4rem);     /* Kleinere Sektion */
  --space-gap:        clamp(1.5rem,3vw, 3rem);     /* Grid/Flex-Gap    */

  /* ── LAYOUT ─────────────────────────────────────────────── */
  --max:       1320px;

  /* Border Radius */
  --radius-sm:   .5rem;      /* ~8px  */
  --radius-md:   .875rem;    /* ~14px */
  --radius-lg:   1.25rem;    /* ~20px */
  --radius-full: 9999px;     /* Pill / Vollkreis */

  /* Schatten */
  --shadow-sm: 0 2px 12px rgba(0,0,0,.06);
  --shadow-md: 0 6px 24px rgba(0,0,0,.08);
  --shadow-lg: 0 16px 48px rgba(0,0,0,.10);

  /* Touch-Ziel Minimum (WCAG 2.5.5 – 44×44px) */
  --touch: 2.75rem;

  /* Übergänge / Easing
     Kurven bewusst je eine Stufe weicher: --ease-out ist Ease-Out-Cubic statt
     -Quint. Das senkt die Anfangsgeschwindigkeit (weniger "schnippisch") und
     verkürzt zugleich den Ausklang – gerade der sehr lange Quint-Ausklang
     würde bei den angehobenen Dauern unten ins Schleppende kippen.
     --ease-in-out analog Ease-In-Out-Quad statt -Cubic. */
  --ease-out:    cubic-bezier(.33, 1, .68, 1);
  --ease-in-out: cubic-bezier(.45, 0, .55, 1);
  /* Dauern ggü. vorher (.15s/.22s) spürbar angehoben, aber bewusst unter der
     300ms-Marke gehalten – darüber liest sich vor allem das Zurückfedern von
     Hover-Zuständen als Verzögerung statt als Weichheit.
     --dur-slow ist seit der Umstellung des Lightbox-Bildwechsels auf
     --gl-fade-out/--gl-fade-in (siehe #gallery-lightbox) frei änderbar; die
     frühere feste Kopplung an ein setTimeout in js/gallery.js besteht nicht
     mehr. */
  --dur-fast:  .2s;
  --dur-base:  .28s;
  --dur-slow:  .35s;

  /* Ease-Out-Kurven (auch sanftere Varianten wie die vorige --ease-reveal-
     Sinus-Kurve) starten per Definition mit hoher Anfangsgeschwindigkeit –
     genau das liest sich bei einem Scroll-Reveal als "Aufploppen". Eine
     Spring, die aus der Ruhe (ohne Wurf/Flick) losläuft, beschleunigt dagegen
     sanft und bremst sanft ab – das entspricht eher einer Ease-In-Out-Kurve.
     --ease-reveal ist daher Ease-In-Out-Sine: symmetrisch weicher Start UND
     weiches Ende, ohne harten Einsatz. */
  --ease-reveal: cubic-bezier(.445, .05, .55, .95);

  /* Spiegelbild von --ease-out, für den Rückweg einer umkehrbaren Bewegung
     (aktuell das Schließen der Lightbox). Eine cubic-bezier(x1,y1,x2,y2)
     spiegelt man als (1-x2, 1-y2, 1-x1, 1-y1) – aus dem Ease-Out
     (.33, 1, .68, 1) wird damit dieses Ease-In. */
  --ease-in-mirror: cubic-bezier(.32, 0, .67, 0);

  /*
   Breakpoints (Referenz – nicht als var() in @media verwendbar):
   --bp-xs:  360px   (Kleines Smartphone)
   --bp-sm:  480px   (Smartphone)
   --bp-md:  768px   (Tablet)
   --bp-lg:  992px   (Tablet quer / kleines Laptop)
   --bp-xl:  1200px  (Desktop)
   --bp-2xl: 1920px  (Großes Display)
  */
}

/* Mobile: kompaktere Übersicht – engerer Zeilenabstand im Fließtext
   und weniger Leerraum zwischen Sektionen als auf Desktop. */
@media (max-width: 599px) {
  :root {
    --leading-normal:   1.55;
    --space-section:    2.25rem;
    --space-section-sm: 1.5rem;
  }
}


/* ════════════════════════════════════════════════════════════
   2. MODERN CSS RESET
   Inspiriert von Andy Bell (piccalil.li) & Josh Comeau
   ════════════════════════════════════════════════════════════ */

/* Box-Sizing auf alle Elemente */
*, *::before, *::after {
  box-sizing: border-box;
}

/* Kein Browser-Default-Blau auf Links; Tap-Highlight unterdrückt, auch auf
   den Aufklappern (<summary>), sonst blitzt beim Antippen ein grauer Balken */
a, button, summary {
  -webkit-tap-highlight-color: transparent;
}
a {
  color: inherit;
  text-decoration: none;
}

/* Margin & Padding zurücksetzen */
* {
  margin: 0;
  padding: 0;
}

/*
  Text-Size-Adjust: verhindert automatische Größenanpassung
  durch Mobile-Browser nach Orientierungswechsel.
*/
html {
  -moz-text-size-adjust: none;
  -webkit-text-size-adjust: none;
  text-size-adjust: none;
  /*
    overflow-x: clip statt hidden: verhindert horizontalen Overflow
    durch Glows, ohne fixed-position Elemente (Navigation) zu
    beeinträchtigen – hidden würde einen neuen Stacking Context erzeugen.
  */
  overflow-x: clip;
  /*
   * Platz für die Scrollleiste dauerhaft freihalten. Erst durch die
   * Scroll-Sperre unten verschwindet die Leiste beim Öffnen einer
   * Vollbild-Ebene überhaupt – ohne die Rinne würde der Seiteninhalt in
   * dem Moment um deren Breite breiter springen. Betrifft nur Systeme mit
   * klassischer Scrollleiste (Windows); wo sie über dem Inhalt schwebt
   * (macOS, iOS, Android), ist die Eigenschaft wirkungslos. Deckt auch die
   * Lightbox ab, für die es – anders als für Menü und Suche
   * (lockScroll() in js/layout.js) – keine Kompensation im Skript gibt.
   */
  scrollbar-gutter: stable;
}

/*
 * Scroll-Sperre für alle Vollbild-Ebenen (mobiles Menü, Suchoverlay,
 * Lightbox). Sie MUSS am <html> hängen, nicht am <body>:
 * Der overflow des Wurzelelements bestimmt den Viewport, und nur wenn er
 * dort "visible" ist, wird stattdessen der Wert des <body> herangezogen.
 * Durch das overflow-x: clip oben ist er hier nie "visible" – die vorhandenen
 * Regeln body.nav-open und body.gl-lock waren deshalb wirkungslos, gemessen
 * scrollte die Seite hinter Menü, Suche und Lightbox ungehindert weiter.
 *
 * Über :has() statt einer eigenen Klasse, damit die drei Stellen in
 * js/layout.js und js/gallery.js unverändert bleiben können; sie setzen
 * ihre Klassen weiterhin am <body>. Fehlt :has()-Unterstützung, bleibt es
 * beim bisherigen Verhalten – kein Rückschritt.
 *
 * Die Kurzform überschreibt für die Dauer der Sperre auch das overflow-x
 * oben; danach gilt wieder clip. Nachgemessen: Nav bleibt korrekt fixiert,
 * sticky positionierte Elemente bleiben sticky, kein horizontaler
 * Überlauf, Scrollposition bleibt erhalten.
 */
html:has(> body.nav-open),
html:has(> body.gl-lock) {
  overflow: hidden;
}

/* Medien-Elemente: kein Overflow, korrekte Höhe */
img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}

/* <picture> waehlt nur das Format (AVIF vor WebP) und darf im Layout nicht
   mitzaehlen: sonst wird der Wrapper zum Flex-/Grid-Kind statt des Bildes und
   Regeln wie .tc-photo oder .gallery-item img greifen ins Leere - das Bild
   faellt dann auf seine Intrinsic-Groesse zusammen.
   display:contents loest den Wrapper auf. Dabei wuerde allerdings <source>
   selbst zum Flex-/Grid-Kind (Chromium gibt ihm display:block) und erzeugte
   eine zusaetzliche gap-Spalte - deshalb muss es ausdruecklich raus.
   Beides zusammen im Browser gegen den Stand ohne Wrapper geprueft: pixelgleich. */
picture { display: contents; }
picture > source { display: none; }

/* Form-Elemente erben Schrift vom Elternelement */
input, button, textarea, select {
  font: inherit;
}

/* hidden-Attribut immer respektieren, auch wenn display durch CSS gesetzt */
[hidden] { display: none !important; }

/* Langer Text umbricht, kein ungewollter Overflow */
p, h1, h2, h3, h4, h5, h6, li, dt, dd, blockquote {
  overflow-wrap: break-word;
}

/* Lange URLs in Fließtext umbrechen */
p, li, td, th, label, span, a {
  overflow-wrap: anywhere;
}

/* Listen ohne semantische Rolle behalten ihren Marker */
ul[role='list'],
ol[role='list'] {
  list-style: none;
}

/* Dezente Standardisierung für HR */
hr {
  border: none;
  border-top: 1px solid var(--color-border);
}


/* ════════════════════════════════════════════════════════════
   3. GLOBALE BASIS-STILE
   ════════════════════════════════════════════════════════════ */

/*
 * Verhindert Flackern von Navigation und Menüpunkten beim Seitenaufruf.
 * layout.js injiziert Footer + Rest der Seite erst nach vollständigem
 * HTML-Parsing (DOMContentLoaded). Da base.css render-blocking ist, greift
 * visibility:hidden vor dem ersten Paint. layout.js setzt .layout-ready,
 * sobald alles fertig injiziert ist → sofortige Anzeige. Die Animation ist
 * ein Fallback falls JS nicht lädt (400ms Timeout).
 */
body:not(.layout-ready) {
  visibility: hidden;
  animation: _layout-unhide 0s 0.4s forwards;
}
@keyframes _layout-unhide { to { visibility: visible; } }

/*
 * #nav (inkl. Logo) wird als Erstes eingefügt, direkt beim Start von
 * layout.js – noch bevor der Rest der Seite bereit ist. Damit es dadurch
 * nicht bis zu 400ms unsichtbar bleibt, wird es unabhängig vom restlichen
 * body-Gate sofort sichtbar: Nav/Logo stehen so bei jedem Seitenaufruf/
 * -wechsel ohne spürbare Verzögerung fest.
 */
body:not(.layout-ready) #nav {
  visibility: visible;
}

body {
  font-family: var(--ff);
  font-size: var(--fs-base);
  font-weight: var(--weight-normal);
  line-height: var(--leading-normal);
  color: var(--color-text);
  background: var(--color-bg);
  /*
    Subpixel-Antialiasing: macht die Schrift auf hellen Hintergründen schärfer.
    Auf dunklen Hintergründen (navy) wäre auto besser – hier globales Default.
  */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Verhindert horizontalen Scroll auf Body-Ebene. clip statt hidden aus
     demselben Grund wie oben bei <html>: hidden macht den Body zu einem
     Scroll-Container, und darin greift position: sticky nicht mehr. Auf
     der Seite "Gastronomie" läuft die Reservierungskarte sticky mit
     (.gs-reservierung); mit hidden bliebe sie nicht stehen. */
  overflow-x: clip;
  /* Mindesthöhe = gesamter Viewport (wichtig für Footer-Sticky-Layouts) */
  min-height: 100dvh;
}

a {
  color: inherit;
  text-decoration: none;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}


/* ════════════════════════════════════════════════════════════
   4. TYPOGRAFIE
   Headings nutzen die fluid Font-Size-Tokens aus :root.
   Die konkreten Seitenklassen (.hero-h1, .h2) überschreiben
   diese Werte – hier ist das generische Fallback-System.
   ════════════════════════════════════════════════════════════ */

/* Gewicht 700 und nicht 800: Libre Baskerville gibt es nur in 400 und 700
   (siehe Abschnitt 0 oben). Bei 800 gab es keinen passenden Schnitt zu
   laden, der Browser hat ihn synthetisch verdickt – genau das "faux bold",
   das die Schriftauswahl oben vermeiden will. Betraf jede Überschrift der
   Website, weil h1/h2 in der Serif laufen. Gilt entsprechend für alle
   Regeln, die h1–h3 überschreiben (.h2 unten, .sec-h2/.page-hero h1/.prose
   in page.css, .tr-title/.ev-title in home.css, .cta-banner h2 unten,
   .form-success h3 in forms.css). h4–h6 laufen in Urbanist, das 800
   tatsächlich mitbringt – dort bleibt --weight-extrabold gültig. */
h1 {
  font-family: var(--font-heading);
  font-size: var(--fs-5xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-5xl);
  line-height: var(--leading-tight);
}

h2 {
  font-family: var(--font-heading);
  font-size: var(--fs-4xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-4xl);
  line-height: 1.1;
}

/* h3 bis h6 brauchen eine eigene Zeilenhöhe: ohne sie erben sie
   --leading-normal (1.75) vom Body und stehen damit so luftig wie ein
   Absatz – der Unterschied zwischen Überschrift und Fließtext verschwand.
   Am deutlichsten bei zweizeiligen Titeln (".teams-contact-h3",
   ".tr-title"). h1/h2 hatten diese Angabe schon. */
h3 {
  font-family: var(--font-heading);
  font-size: var(--fs-3xl);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-3xl);
  line-height: var(--leading-snug);
}

h4 {
  font-size: var(--fs-2xl);
  font-weight: var(--weight-bold);
  line-height: 1.3;
}

h5 {
  font-size: var(--fs-xl);
  font-weight: var(--weight-bold);
  line-height: 1.3;
}

h6 {
  font-size: var(--fs-lg);
  font-weight: var(--weight-bold);
  line-height: 1.3;
}

strong, b {
  font-weight: var(--weight-bold);
  color: var(--color-text);
}

/* ── EYEBROW / TAG ──────────────────────────────────────────
   Beschriftungen über Überschriften ("Über uns", "Training").
   .eyebrow = aktueller Standard  │  .tag = Legacy-Alias
──────────────────────────────────────────────────────────── */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-navy);
  margin-bottom: var(--space-4);
}

.eyebrow::before {
  content: '';
  display: block;
  width: 1.25rem;
  height: 2px;
  background: var(--color-navy);
  border-radius: 2px;
  flex-shrink: 0;
}

/* Legacy-Alias – .tag wird durch .eyebrow abgelöst */
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-navy);
  margin-bottom: var(--space-3);
}

.tag::before {
  content: '';
  display: block;
  width: .875rem;
  height: 2px;
  background: var(--color-navy);
  border-radius: 2px;
  flex-shrink: 0;
}

/* ── DISPLAY-HEADLINE (.h2) ─────────────────────────────────
   Große Display-Überschrift auf der Startseite.
   em-Kind: Akzent-Farbe ohne italic.
──────────────────────────────────────────────────────────── */
.h2 {
  font-size: var(--fs-display);
  /* 700 statt 800 – Begründung siehe h1/h2 oben (Libre Baskerville) */
  font-weight: var(--weight-bold);
  color: var(--color-text);
  letter-spacing: var(--tracking-display);
  line-height: var(--leading-none);
  margin-bottom: var(--space-5);
}

.h2 em {
  font-style: normal;
  color: var(--color-navy);
}

/* ── INTRO-TEXT ─────────────────────────────────────────────
   Einleitungstext unter Überschriften.
──────────────────────────────────────────────────────────── */
.intro {
  font-size: var(--fs-md);
  color: var(--color-text-soft);
  max-width: 520px;
  line-height: var(--leading-relaxed);
}


/* ════════════════════════════════════════════════════════════
   5. LAYOUT-UTILITIES
   ════════════════════════════════════════════════════════════ */

/* Zentrierung mit max-width. Kein eigenes horizontales Padding: die
   umgebende Sektion (.sec, .page-sec, .form-section o.ä.) liefert bereits
   var(--space-inline) als Seitenabstand – ein zusätzliches Padding hier
   würde den Inhalt doppelt einrücken und nicht mehr bündig zur Nav sein. */
.inner {
  max-width: var(--max);
  width: 100%;
  margin: 0 auto;
}

/* Sektions-Padding: vertikal fluid, horizontal direkt hier (siehe .inner oben) */
.sec {
  padding: var(--space-section) var(--space-inline);
}

/* Der Wechsel beginnt immer mit dem Grundton: .sec-cream ist der
   graue Abschnitt, .sec-white der weiße. Auf Unterseiten steht
   .sec-cream stets an erster Stelle, damit oben Grau anschließt.
   Seiten mit nur einem Abschnitt bleiben so durchgehend grau. */
.sec-cream { background: var(--color-bg-base); }

/* ── Weiße Abschnitte: Flächenrollen tauschen ───────────────
   Abschnitte mit weißem Hintergrund (--color-bg-alt) füllen
   Karten und Listenzeilen mit dem Grundton #F0F0F1, damit sie
   sich weiterhin absetzen. Eingabefelder werden umgekehrt weiß.
──────────────────────────────────────────────────────────── */
.sec-white,
.training-section,
.events-section,
.gallery-section,
.sponsors-section,
.faq-section,
.form-section {
  background:      var(--color-bg-alt);
  --color-bg:      var(--color-bg-alt);
  --color-bg-card: var(--color-bg-base);
  --bg-card:       var(--color-bg-base);
}

/* ── Naht zwischen zwei Abschnitten derselben Fläche ────────
   Teilen sich zwei benachbarte Abschnitte eine Fläche (siehe
   CLAUDE.md, Ausnahme vom Wechsel), fehlt die Farbkante, die den
   Abstand sonst in ein Ende und einen Anfang teilt. Die vollen
   2 × var(--space-section) lesen sich dann als Loch. Dieselbe
   Rechnung wie bei .page-hero-plain + .sec in css/page.css.
   Gemessen auf der Vorstandsseite: 224px → 168px auf Desktop,
   72px → 62px auf dem Handy. Nur oben gekürzt, nicht auch unten:
   bei halbierten 112px blieben auf dem Handy 44px über der
   Dachzeile gegenüber 32px unter der Überschrift, der
   Überschriftenblock gehörte optisch nicht mehr eindeutig zu den
   Karten darunter.
──────────────────────────────────────────────────────────── */
.sec-cream + .sec-cream,
.sec-white + .sec-white {
  padding-top: calc(var(--space-section) / 2);
}

/* Sektions-Header: Überschrift links, Aktion rechts */
.sec-hdr {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-section-sm);
  flex-wrap: wrap;
}

/* 2-Spalten-Layout: 1 Spalte Mobile → 2 Spalten ab 768px */
.two {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-8);
  align-items: center;
}

/* Grid-Items haben von Haus aus min-width:auto und werden deshalb von
   breiten Inhalten (z. B. .tcle-table mit min-width) über die Spalte
   hinaus aufgezogen – auf schmalen Displays läuft der Text dann rechts
   aus dem Bild. min-width:0 hält die Spalte auf Track-Breite; breite
   Tabellen scrollen wie vorgesehen innerhalb ihres .table-wrap. */
.two > * { min-width: 0; }

.two-start { align-items: start; }

@media (min-width: 768px) {
  .two {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-12);
  }
}


/* ════════════════════════════════════════════════════════════
   6. BUTTONS
   Mindest-Touch-Ziel 44×44px (WCAG 2.5.5).
   Hover-Effekte nur auf Pointer-Geräten (hover: hover).
   Beim Überfahren ändert sich nur die Farbe, der Button hebt sich nicht
   und bekommt keinen Schatten: Er bleibt liegen wie bei Apple, die
   Rückmeldung kommt beim Drücken (.btn:active weiter unten). Auch im
   Ruhezustand sind die gefüllten Buttons flach.
   ════════════════════════════════════════════════════════════ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: .75rem 1.5rem;
  min-height: var(--touch);
  min-width: var(--touch);
  font-family: var(--ff);
  font-size: var(--fs-sm);
  font-weight: var(--weight-bold);
  border-radius: var(--radius-full);
  cursor: pointer;
  border: none;
  text-decoration: none;
  transition: background-color var(--dur-base) var(--ease-out),
              color     var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
  letter-spacing: var(--tracking-slight);
  white-space: nowrap;
  /* Verhindert Textauswahl beim schnellen Klicken */
  user-select: none;
}

.btn-sm {
  padding: .55rem 1.25rem;
  font-size: var(--fs-xs);
  min-height: var(--touch);
}

/* ── Brand (Gelb) ── */
.btn-terra {
  background: var(--color-brand);
  /* Schwarz statt --color-navy-dk: Gelb traegt den staerksten Kontrast
     gegen Schwarz. Gilt fuer alle gelben Knoepfe der Seite. */
  color: var(--color-text);
}
@media (hover: hover) {
  .btn-terra:hover {
    background: var(--color-brand-dk);
  }
}

/* ── Getönte Buttons (zweite Stufe) ──────────────────────────
   Die Umriss-Buttons sind zu getönten Flächen geworden, wie die
   "tinted" Buttons bei Apple: kein Rahmen, die Farbe steckt in einer
   leichten Fläche und in der Schrift. Damit gibt es sichtbar nur noch
   drei Stufen: gefüllt (Gelb, Blau, Weiß), getönt und Textlink.
   Die Klassennamen (-outline…) sind geblieben, damit kein Markup
   angefasst werden musste. */

/* ── Getönt blau (auf hellem Grund) ──
   .btn-outline und .btn-outline-dk sehen gleich aus; früher waren es
   zwei Randfarben für denselben Zweck. */
.btn-outline,
.btn-outline-dk {
  background: rgba(33,79,152,.08);
  color: var(--color-navy);
}
@media (hover: hover) {
  .btn-outline:hover,
  .btn-outline-dk:hover {
    background: rgba(33,79,152,.14);
  }
}

/* ── Getönt grün (Zweitakzent) ──
   Für Akzentlinks, die nicht mit den blauen Verweisen der Seite
   konkurrieren sollen. Grün ist in CLAUDE.md genau dafür vorgesehen. */
.btn-outline-green {
  background: rgba(44,116,89,.10);
  color: var(--color-green-dk);
}
@media (hover: hover) {
  .btn-outline-green:hover {
    background: rgba(44,116,89,.16);
  }
}

/* ── Getönt weiß (auf dunklem Hintergrund) ──
   Gleiche Fläche wie .btn-glass (css/home.css). */
.btn-outline-white {
  background: rgba(255,255,255,.14);
  color: #fff;
}
@media (hover: hover) {
  .btn-outline-white:hover {
    background: rgba(255,255,255,.24);
  }
}

/* ── Weiß (auf Akzent-Hintergrund) ── */
.btn-white {
  background: #fff;
  color: var(--color-navy-dk);
}
@media (hover: hover) {
  .btn-white:hover {
    background: var(--color-bg-base);
  }
}

/* "Press"-Effekt beim Klick – gilt für alle Button-Varianten */
.btn:active {
  transform: scale(.97);
  transition-duration: .12s;
}


/* ════════════════════════════════════════════════════════════
   7. SHARED COMPONENTS
   ════════════════════════════════════════════════════════════ */

/* ── News-Meldungen ─────────────────────────────────────────
   Redaktionelle Liste statt Karten: links die Datumsspalte, rechts
   davon der Inhalt hinter einer senkrechten Linie. Die Liste selbst
   (Trennlinien, Spaltenmaße) steht in css/home.css – hier nur die
   Bausteine einer einzelnen Meldung.
──────────────────────────────────────────────────────────── */
.news-date {
  font-family: var(--font-heading);
  text-align: center;
  line-height: var(--leading-none);
  /* Optischer Ausgleich: Die Versalhöhe der Ziffern beginnt tiefer als
     die Oberkante der darüberliegenden Kategoriezeile. */
  padding-top: .15em;
}
.news-date-num {
  display: block;
  font-size: clamp(1.6rem, 1.25rem + 1.4vw, 2.4rem);
  font-weight: var(--weight-bold);
  color: var(--color-text);
  letter-spacing: var(--tracking-normal);
}
.news-date-unit {
  display: block;
  margin-top: .35em;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-soft);
}
/* Meldungen ohne Tagesdatum tragen dort einen Zeitraum ("2026 Saison").
   Vier Ziffern brauchen bei der großen Stufe mehr Platz als die Spalte
   hergibt – deshalb eine Stufe kleiner. */
.news-date--period .news-date-num {
  font-size: clamp(1.1rem, .95rem + .6vw, 1.5rem);
}

.news-link {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  margin-top: var(--space-5);
  font-size: var(--fs-base);
  font-weight: var(--weight-bold);
  color: var(--color-navy);
  text-decoration: underline;
  text-underline-offset: .45em;
  text-decoration-thickness: 1px;
  min-height: var(--touch);
}
.news-link::after {
  content: '→';
  transition: transform var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .news-link:hover::after { transform: translateX(.25rem); }
}

/* ════════════════════════════════════════════════════════════
   NEWS-KARTEN
   Gemeinsam für pages/news.html und die News-Vorschau der Startseite
   (index.html). Stand vorher im <style> der News-Seite; liegt jetzt
   hier, damit beide Seiten dieselbe Karte zeigen und nicht auseinander-
   laufen. Klassenpräfix "n-", weil .news-* schon belegt ist.
   ════════════════════════════════════════════════════════════ */
/* ── Kategorie-Farben ────────────────────────────────────
   Nur Töne aus der Projektpalette (siehe CLAUDE.md, Design):
   Blau, Grün, Gelb, Vereinsgrau. Die Fläche trägt die Farbe, das
   Etikett darauf bleibt weiß und nimmt den Ton als Schriftfarbe auf –
   Gelb als Schrift gäbe es auf hellem Grund nicht, als Fläche ist es
   richtig.
   Blau ist die Vereinsfarbe #214F98, nicht --color-navy-dk: Die
   dunkle Variante ist im Projekt durchgehend Schrift- und
   Hover-Farbe und steht nirgends als Fläche.
   Turnier und Verein teilen sich das Blau – für fünf Kategorien gibt
   die Palette nur vier Flächentöne her. Sichtbar wird das nicht,
   solange die Turnier-Meldung ein Foto trägt.
   --n-badge-ink ist die Schriftfarbe im weißen Etikett; sie ist je
   Kategorie eine Stufe dunkler gewählt, wo der Flächenton auf Weiß
   zu schwach käme. */
.n-cat-turnier     { --n-tile: var(--color-navy);  --n-badge-ink: var(--color-navy); }
.n-cat-verbandsrunde { --n-tile: #8E8E8E;          --n-badge-ink: var(--color-text); }
.n-cat-training    { --n-tile: var(--color-green); --n-badge-ink: var(--color-green-dk); }
.n-cat-feste       { --n-tile: var(--color-brand); --n-badge-ink: var(--color-text); }
.n-cat-verein      { --n-tile: var(--color-navy);  --n-badge-ink: var(--color-navy); }

.n-badge {
  position: absolute;
  top: var(--space-4);
  left: var(--space-4);
  display: inline-block;
  background: #fff;
  color: var(--n-badge-ink, var(--color-navy));
  font-size: var(--fs-2xs);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  padding: .3rem .7rem;
  border-radius: var(--radius-full);
  box-shadow: 0 2px 8px rgba(0,0,0,.12);
}

/* ── Meldungskarte ── */
.n-card {
  background: var(--color-bg-card);
  border: 1px solid var(--color-border-card);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out);
}
@media (hover: hover) {
  .n-card:hover { border-color: var(--color-navy-bg); box-shadow: var(--shadow-md); }
}
.n-media {
  position: relative;
  background: var(--n-tile, var(--color-navy));
  aspect-ratio: 16 / 9;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
/* Das Foto füllt die Fläche; der Glyph darunter ist davon
   ausgenommen – er soll ganz zu sehen sein, nicht beschnitten. */
.n-media img:not(.n-glyph) {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Unten ausgerichtet: Bei
     Motiven, die höher sind als die 16:9-Fläche, fällt der obere
     Rand weg statt der Bildunterschrift am Fuß. */
  object-position: center bottom;
  display: block;
}
/* Platzhalter, wo (noch) kein Foto vorliegt: immer das weiße
   Vereinslogo auf blauer Fläche, unabhängig von der Kategorie.
   Die Kategoriefarbe (--n-tile) gilt nur hinter Fotos. */
.n-media:has(> .n-glyph) { background: var(--color-navy); }
/* Über die Höhe begrenzt, nicht über die Breite: Der Platz steht
   hochkant (Seitenverhältnis 1:2), in der 16:9-Fläche liefe er sonst
   oben und unten heraus. max-width fängt die querformatigen Glyphen
   (Vereinslogo) ab. */
.n-glyph {
  width: auto;
  height: 62%;
  max-width: 46%;
  opacity: .45;
}
.n-body {
  padding: var(--space-5) var(--space-5) var(--space-6);
  display: flex;
  flex-direction: column;
  flex: 1;
}
.n-date {
  font-size: var(--fs-2xs);
  color: var(--color-text-mute);
  margin-bottom: .35rem;
}
.n-title {
  font-family: var(--font-heading);
  font-size: var(--fs-2xl);
  font-weight: var(--weight-bold);
  color: var(--color-text);
  letter-spacing: var(--tracking-3xl);
  line-height: var(--leading-snug);
  margin-bottom: var(--space-2);
}
.n-text {
  font-size: var(--fs-sm);
  color: var(--color-text-soft);
  line-height: var(--leading-relaxed);
}
/* Aufklapper bzw. Knopf sitzen am Kartenfuß, damit sie bei
   unterschiedlich langen Anrissen auf einer Linie stehen. */
.n-body > .btn { margin-top: auto; align-self: flex-start; }
.n-body > .btn { margin-top: var(--space-4); }

/* ── Footer ─────────────────────────────────────────────────
   Injiziert via js/layout.js in alle Seiten.
──────────────────────────────────────────────────────────── */
.footer-logo-img {
  display: block;
  height: clamp(3.5rem, 7vw, 5rem);   /* ~56px → 80px, fluid statt fix */
  width: auto;
  /* Reserviert die Breite anhand des echten Logo-Seitenverhältnisses,
     bevor das Bild geladen ist → kein Layout-Sprung (CLS). */
  aspect-ratio: 650 / 434;
  object-fit: contain;
  margin-bottom: var(--space-4);
}

footer {
  background: var(--color-navy);
  padding: var(--space-12) var(--space-inline) var(--space-8);
}

.f-inner {
  max-width: var(--max);
  margin: 0 auto;
}

/* Mobile: 1 col → 2 col tablet (brand full-width) → 5 col desktop */
.f-top {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  padding-bottom: var(--space-8);
  border-bottom: 1px solid rgba(255,255,255,.08);
  margin-bottom: var(--space-6);
}

@media (min-width: 600px) {
  .f-top { grid-template-columns: 1fr 1fr; }
  .f-brand { grid-column: 1 / -1; }
}

@media (min-width: 960px) {
  .f-top {
    grid-template-columns: 1.8fr repeat(3, 1fr);
    gap: var(--space-8);
  }
  .f-brand { grid-column: auto; }
}

/* Logo link */
.f-logo-link {
  display: inline-block;
  transition: opacity var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
  .f-logo-link:hover { opacity: .8; }
}

/* Address block */
.f-address {
  font-style: normal;
  font-size: var(--fs-sm);
  color: rgba(255,255,255,.72);
  line-height: var(--leading-relaxed);
  margin-bottom: var(--space-4);
  /* Zeilen als eigene Blöcke statt <br>, damit Anschrift und Kontaktzeilen
     jeweils einen gleichmäßigen Abstand haben. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
.f-address a {
  color: rgba(255,255,255,.82);
  transition: color var(--dur-fast) var(--ease-out);
  /* Touch-Ziel: min-height statt fester line-height */
  display: inline-flex;
  align-items: center;
  min-height: var(--touch);
}
/* Trennt die Anschrift von den Kontaktzeilen, ohne deren Rhythmus zu stören */
.f-address a:first-of-type { margin-top: var(--space-2); }
@media (hover: hover) {
  .f-address a:hover { color: rgba(255,255,255,.85); }
}

/* Social icons */
.f-social {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.f-social-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--touch);
  height: var(--touch);
  color: rgba(255,255,255,.72);
  border-radius: var(--radius-md);
  transition: color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
  touch-action: manipulation;
}
@media (hover: hover) {
  .f-social-link:hover {
    color: #fff;
    background: rgba(255,255,255,.08);
  }
}

/* Nav group lists */
.f-grp ul {
  list-style: none;
  padding: 0;
  margin: 0;
}
.f-grp li { margin: 0; }

.f-brand-name {
  font-size: var(--fs-md);
  font-weight: var(--weight-extrabold);
  color: #fff;
  margin-bottom: var(--space-1);
  letter-spacing: var(--tracking-slight);
}

.f-brand-tag {
  font-size: var(--fs-2xs);
  font-weight: var(--weight-bold);
  color: rgba(255,255,255,.70);
  margin-bottom: var(--space-3);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.f-brand p {
  font-size: var(--fs-sm);
  color: rgba(255,255,255,.72);
  line-height: var(--leading-normal);
}

.f-grp-title {
  font-size: var(--fs-2xs);
  font-weight: var(--weight-bold);
  /* Als <h2> ausgezeichnet (Überschriftenfolge), aussehen wie früher als <h3>:
     h2 bringt eine engere Zeilenhöhe mit, 1.25 ist der alte h3-Wert. */
  line-height: 1.25;
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  /* .65 lag bei 4.37:1 auf Navy (WCAG AA braucht 4.5:1) → auf .7 angehoben (4.8:1) */
  color: rgba(255,255,255,.7);
  margin-bottom: var(--space-3);
}

.f-grp a {
  font-size: var(--fs-sm);
  color: rgba(255,255,255,.72);
  margin-bottom: var(--space-2);
  transition: color var(--dur-fast);
  /* Touch-Ziel: min-height statt fester line-height */
  min-height: var(--touch);
  display: flex;
  align-items: center;
}
@media (hover: hover) {
  .f-grp a:hover { color: rgba(255,255,255,.85); }
}

.f-bot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.f-copy {
  font-size: var(--fs-xs);
  /* .62 lag bei 4.14:1 auf Navy (WCAG AA braucht 4.5:1) → auf .7 angehoben (4.8:1) */
  color: rgba(255,255,255,.7);
}
.f-copy a { color: rgba(255,255,255,.82); text-decoration: underline; }
.f-copy--right { text-align: right; }
@media (max-width: 599px) {
  .f-copy--right { text-align: left; }
}


/* ════════════════════════════════════════════════════════════
   8. ACCESSIBILITY
   ════════════════════════════════════════════════════════════ */

/* ── Reduced Motion ─────────────────────────────────────────
   Nutzer mit Gleichgewichts-/Bewegungsempfindlichkeit:
   alle Animationen und Übergänge deaktivieren.
   Ausnahme: opacity/Farbe (kein Bewegungs-Trigger) – zweiter Block
   darunter gibt ihnen eine sanfte Dauer zurück, ohne dass irgendein
   Bauteil einzeln angepasst werden muss.
   visibility steht mit in der Liste: Dropdown, mobiles Menü, Suche,
   Lightbox und Co. verstecken sich per verzögertem visibility-Wechsel.
   Fehlt es, greift hidden sofort und das Ausblenden ist unsichtbar –
   das Element wäre beim Schließen schlagartig weg.
──────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }

  *,
  *::before,
  *::after {
    transition-property: opacity, color, background-color, border-color, box-shadow, visibility !important;
    transition-duration: .2s !important;
  }
}

/* Druck-Feedback der Galerie-Kacheln – gleich wie bei den Link-Karten in
   css/page.css (scale .98, sofort). Die Übergänge stehen hier ausdrücklich,
   weil die Kacheln sonst verschiedene erben: auf der Startseite einen
   Sektions-Style ohne passende Transition, auf der Impressionen-Seite
   einen Seiten-Style ganz ohne transform. :root hebt die Spezifität über
   beide. Das Abdunkeln des Bildes beim Antippen (css/home.css) bleibt. */
:root .gallery-item {
  transition: border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
:root .gallery-item:active {
  transform: scale(.98);
  transition-duration: .1s;
}

/* ── Einblenden nach Umschalten ─────────────────────────────
   Für Inhalte, die per Knopf aus hidden geholt werden: Filterleisten
   (News, Termine, Ergebnisse), Tarifreiter und der zweite Teil des
   Aufnahmeantrags auf der Mitgliedschaftsseite. Das Skript setzt die
   Klasse erst beim Klick, beim Laden läuft also nichts.
   Die Klasse darf danach stehen bleiben: display:none startet jede
   Animation beim nächsten Einblenden von selbst neu, ein Element, das
   sichtbar bleibt, läuft dagegen nicht noch einmal an.
   Ausblenden bleibt bewusst hart – sonst ruckelt das Layout, während
   verschwindende Elemente noch Platz belegen.
   .swap-in-rise mit kleinem Versatz, unter 600px nur Deckkraft: Dort
   stehen die Inhalte untereinander, der Versatz verschiebt nur, was
   ohnehin direkt unter dem Knopf auftaucht.
   Reduced Motion: der Block oben kürzt die Dauer, beide Keyframes
   enden sichtbar – es bleibt nichts verborgen. */
@keyframes swap-in      { from { opacity: 0; } }
@keyframes swap-in-rise { from { opacity: 0; transform: translateY(6px); } }
.swap-in      { animation: swap-in var(--dur-fast) var(--ease-out) both; }
.swap-in-rise { animation: swap-in-rise var(--dur-base) var(--ease-out) both; }
@media (max-width: 599px) {
  .swap-in-rise { animation-name: swap-in; }
}

/* ── Screen Reader Only ─────────────────────────────────────
   Visuell versteckt, aber für Screenreader sichtbar.
──────────────────────────────────────────────────────────── */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Focus Visible ──────────────────────────────────────────
   Deutlicher Fokus-Ring für Tastaturnutzer (WCAG 2.4.11).
   :focus-visible verhindert den Ring beim Mausklick.
──────────────────────────────────────────────────────────── */
:focus-visible {
  outline: 2px solid var(--color-navy);
  outline-offset: 3px;
  border-radius: 4px;
}

/* Runde Elemente: border-radius ans Element anpassen */
.btn:focus-visible,
.nav-burger:focus-visible,
.faq-q:focus-visible,
.mob-cta:focus-visible,
.nav-cta:focus-visible {
  outline: 2px solid var(--color-navy);
  outline-offset: 3px;
  border-radius: var(--radius-full);
}

/* closeMenu() in js/layout.js gibt den Fokus an den Burger zurück – nach einem
   Schließen per Maus oder Touch bliebe der Ring dadurch als blauer Kreis stehen.
   Die Klasse setzt js/layout.js nur in diesem Fall; bei Tastaturbedienung
   (Escape, Enter/Leertaste) bleibt der Ring erhalten. */
.nav-burger.no-focus-ring:focus-visible {
  outline: none;
}

/* js/router.js setzt tabindex="-1" auf die neue Überschrift, um nach einem
   SPA-Seitenwechsel den Fokus für Screenreader dorthin zu lenken. Da dieses
   Element nicht per Tab erreichbar ist, bringt der sichtbare Ring hier
   keinen Nutzen für Tastaturnutzer – der Fokus selbst bleibt erhalten. */
h1[tabindex="-1"]:focus-visible {
  outline: none;
}

/* ── Bildergalerie-Lightbox ─────────────────────────────────
   Von js/gallery.js auf Startseite und Impressionen-Seite genutzt.
──────────────────────────────────────────────────────────── */
body.gl-lock { overflow: hidden; }
#gallery-lightbox {
  /* Dauern des Bildwechsels. js/gallery.js liest --gl-fade-out aus, um den
     Bildtausch genau ans Ende des Ausblendens zu legen – vorher stand dort
     ein fest eingetragenes setTimeout, das zur CSS-Dauer passen musste.
     Bewusst asymmetrisch: schnell raus, etwas ruhiger rein, das lässt den
     Wechsel direkter wirken. Zusammen 340ms statt vorher 700ms. */
  --gl-fade-out: 140ms;
  --gl-fade-in: 200ms;
  position: fixed;
  inset: 0;
  z-index: 500;
  /* Deckkraft als eigene Eigenschaft: js/gallery.js dünnt sie beim Ziehen
     zum Schließen aus, damit sichtbar wird, dass Loslassen jetzt schließt. */
  --gl-dim: .9;
  background: rgba(0,0,0,var(--gl-dim));
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  /* Wächst aus der angetippten Kachel: js/gallery.js legt transform-origin
     auf deren Mittelpunkt. Der Startmaßstab ist bewusst deutlich kleiner als
     die vorherigen .96 – bei einem Ursprung am Bildrand ist die Bewegung
     sonst zu klein, um den räumlichen Bezug zu zeigen. */
  transform: scale(.82);
  pointer-events: none;
  visibility: hidden;
  /* Diese Regel gilt beim SCHLIESSEN (Zielzustand nach Entfernen von .open),
     .open darunter beim ÖFFNEN. Deshalb hier die zu --ease-out gespiegelte
     Kurve: Der Rückweg läuft damit spiegelbildlich zum Hinweg statt beide
     Male mit einem Ease-Out. */
  transition: opacity .2s var(--ease-in-mirror), transform .2s var(--ease-in-mirror), visibility 0s linear .2s;
}
#gallery-lightbox.open {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
  visibility: visible;
  transition: opacity .2s var(--ease-out), transform .2s var(--ease-out), visibility 0s linear 0s;
}
/* Bühne und Spur für das Wischen: Die Spur trägt Vorgänger, aktuelles Bild
   und Nachfolger nebeneinander, jeweils bildschirmbreit, und steht in Ruhe
   um eine Breite nach links versetzt – das mittlere Slide ist im Bild. Beim
   Wischen verschiebt js/gallery.js die Spur 1:1 mit dem Finger.
   Die Bühne nimmt dafür die volle Fläche ein und schneidet ab; das Bild
   selbst behält seine bisherigen Maße und wird im Slide zentriert, damit
   Radius und Schatten weiterhin am Bild sitzen und nicht an einer Box. */
.gl-stage {
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.gl-track {
  display: flex;
  width: 300%;
  height: 100%;
  /* Ein Drittel der eigenen Breite = genau ein Slide. Prozentwerte in
     translateX beziehen sich auf das Element selbst, nicht auf den
     Elternbereich – deshalb hier 33.3333% und nicht 100%. */
  transform: translateX(-33.3333%);
}
/* will-change nur, solange die Lightbox offen ist – sonst hielte jede Seite
   mit Galerie dauerhaft eine eigene Compositing-Ebene vor. */
#gallery-lightbox.open .gl-track {
  will-change: transform;
}
.gl-slide {
  width: 33.3333%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
.gl-img {
  max-width: 90vw;
  max-height: 80vh;
  width: auto;
  height: auto;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  transition: opacity var(--gl-fade-in) var(--ease-out), transform var(--gl-fade-in) var(--ease-out), filter var(--dur-base) var(--ease-out);
}
.gl-img.is-swapping {
  opacity: 0;
  transition-duration: var(--gl-fade-out);
}
/* Platzhalter-Zustand: Die Großansicht ist noch unterwegs, im Bild steht
   solange das Vorschaubild. Das ist auf 600x450 beschnitten und passt bei
   einem Teil der Galerie weder in Größe noch Seitenverhältnis zur
   Großansicht – der Weichzeichner macht kenntlich, dass das noch nicht das
   fertige Bild ist, und nimmt dem anschließenden Wechsel die Härte.
   js/gallery.js setzt und entfernt die Klasse; sind die Nachbarbilder
   vorgeladen, tritt der Zustand gar nicht erst auf. */
.gl-img.gl-loading {
  filter: blur(8px);
}
.gl-close, .gl-nav {
  position: fixed;
  /* Über der Spur. Ohne das lag der linke Pfeil unter dem Bild und war dort,
     wo beide sich überlappen, gar nicht antippbar – der rechte dagegen schon:
     .gl-track trägt ein transform (die Ruhelage der Wisch-Spur) und bildet
     damit einen Stapelkontext, der wie z-index 0 gezeichnet wird. Bei
     gleichem z-index entscheidet die Reihenfolge im Markup, und .gl-prev wie
     .gl-close stehen dort VOR .gl-stage, .gl-next dahinter. Gemessen traf ein
     Tipp auf die Mitte des linken Pfeils das Bild statt des Knopfes. */
  z-index: 1;
  background: rgba(255,255,255,.1);
  border: none;
  color: #fff;
  cursor: pointer;
  border-radius: var(--radius-full);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
  touch-action: manipulation;
}
@media (hover: hover) {
  .gl-close:hover, .gl-nav:hover { background: rgba(255,255,255,.22); }
}
/* Trefferfläche auf 44px ausdehnen, ohne den sichtbaren Kreis zu
   vergrößern: Gemessen waren die Knöpfe auf 390px Breite 40x40 (Schließen)
   bzw. 38x38 (Pfeile) und lagen damit unter dem Richtwert von 44x44.
   Einfach größer zu zeichnen wäre der falsche Weg – die Pfeile liegen über
   dem Bild und wurden für schmale Viewports bewusst verkleinert. Deshalb
   dasselbe Prinzip wie bei den Slider-Punkten in css/home.css: Das
   Pseudo-Element ist unsichtbar, liegt mittig über dem Knopf und fängt die
   Tipps ab. Es steht absolut und wird dadurch nicht zum Flex-Kind, das das
   zentrierte Symbol verschieben würde. */
.gl-close::before,
.gl-nav::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
.gl-close {
  top: 1rem;
  right: 1rem;
  width: 2.5rem;
  height: 2.5rem;
  font-size: 1.1rem;
}
.gl-nav {
  top: 50%;
  transform: translateY(-50%);
  width: 3rem;
  height: 3rem;
  font-size: 1.8rem;
}
.gl-prev { left: 1rem; }
.gl-next { right: 1rem; }
/* "Press"-Effekt wie bei .btn:active. .gl-nav braucht die Zentrierung
   (translateY) mit im Wert, sonst springt der Pfeil beim Drücken nach unten. */
.gl-close:active, .gl-nav:active {
  background: rgba(255,255,255,.22);
  transition-duration: .1s;
}
.gl-close:active { transform: scale(.92); }
.gl-nav:active { transform: translateY(-50%) scale(.92); }
@media (max-width: 599px) {
  .gl-nav { width: 2.4rem; height: 2.4rem; font-size: 1.4rem; }
  .gl-prev { left: .4rem; }
  .gl-next { right: .4rem; }
  .gl-close { top: .6rem; right: .6rem; }
}

/* ════════════════════════════════════════════════════════════
   9. BALL-LOADER (SPA-Seitenwechsel)
   ════════════════════════════════════════════════════════════
   Ersetzt für die vom SPA-Router (js/router.js) abgefangenen Wechsel
   zwischen Unterseiten den harten, animationslosen Inhaltstausch durch
   einen kurzen Ball-Loader. Persistentes Element (siehe layout.js),
   Sichtbarkeit wird per .is-active von router.js gesteuert.

   Ein Weg, eine Richtung: Die Ebene fährt von unten herein, bleibt
   stehen, solange der Ball springt, und fährt oben wieder hinaus. Sie
   blendet nirgends ein oder aus. Was auf einem Weg verschwindet, soll auf
   demselben Weg wiederkommen – ein Hereinblenden und ein Abfahren wären
   zwei verschiedene Bewegungen am selben Element.
──────────────────────────────────────────────────────────── */
#page-loader {
  position: fixed;
  inset: 0;
  z-index: 9998;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
/* Die blaue Fläche liegt als Pseudo-Element im Loader und steht oben UND
   unten je 36 % über den Bildschirm hinaus. Diese beiden Überhänge sind
   die weichen Ränder: Sie verlaufen von durchsichtig auf volles Blau und
   erreichen das volle Blau schon knapp ausserhalb der Bildschirmkanten
   (19 % und 81 % von 172 % Gesamthöhe, also rund 3 % Reserve an jeder
   Seite). Im Ruhezustand steht im Bild damit ausschliesslich deckendes
   Blau, beide Verlaufszonen liegen darüber bzw. darunter.

   Beim Hereinfahren von unten führt die Oberkante: Ihr Verlauf wandert
   durchs Bild und legt sich ohne harte Kante über die alte Seite. Beim
   Abgang nach oben führt die Unterkante und gibt die neue Seite frei.
   Beide Ränder werden gebraucht, jeder einmal.

   Der Nullwert steht ausgeschrieben als rgba und nicht als "transparent",
   damit der Verlauf nicht über durchsichtiges Schwarz läuft und grau
   wird. */
#page-loader::before {
  content: '';
  position: absolute;
  top: -36%;
  bottom: -36%;
  left: 0;
  right: 0;
  background: linear-gradient(to bottom,
              rgba(33, 79, 152, 0) 0%,
              var(--color-navy) 19%,
              var(--color-navy) 81%,
              rgba(33, 79, 152, 0) 100%);
}
#page-loader.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  will-change: transform;
}

/* Hereinfahren von unten. 136 % ist die Bildschirmhöhe plus der untere
   Überhang: Erst dann steht auch der obere weiche Rand noch unterhalb des
   Bildes, die Ebene ist also wirklich ganz draussen. Dieselbe Kurve wie
   beim Abgang, beide Male startet die Bewegung aus dem Stand und läuft
   weich aus. Kürzer als der Abgang, weil direkt nach dem Klick eine
   Antwort kommen soll.

   Eigene Klasse und nicht an .is-active gehängt: .is-active steht die
   ganze Zeit, auch während des Abgangs. Die Einfahrt daran zu hängen
   hiesse, dass sie beim Abräumen von .geht als neue Animation von vorn
   beginnt – die Ebene spränge dann aus dem laufenden Abgang nach oben
   aus dem Bild und käme ein zweites Mal herunter. Gemessen, nicht
   vermutet. Mit eigener Klasse entscheidet js/router.js, ob überhaupt
   eingefahren wird. */
#page-loader.kommt {
  animation: page-loader-ein 620ms cubic-bezier(.42, 0, .2, 1) both;
}
@keyframes page-loader-ein {
  from { transform: translateY(136%); }
  to   { transform: translateY(0); }
}
#page-loader .page-loader-ball {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--color-brand);
  animation: page-loader-bounce 1s cubic-bezier(.22, 1, .36, 1) infinite;
}
@keyframes page-loader-bounce {
  0%, 100% { transform: translateY(0) scale(1, 1); }
  45%, 55% { transform: translateY(-30px) scale(1, 1); }
  90% { transform: translateY(0) scale(1.2, .8); }
}

/* ── Abgang ──
   Die Ebene fährt mitsamt Ball weiter nach oben aus dem Bild und gibt die
   neue Seite frei, die darunter längst fertig steht. Kein Ausblenden an
   Ort und Stelle: Dabei liegen für einen Moment beide Seiten halb
   durchsichtig übereinander, und genau das ist der harte Schnitt, den
   man sieht.

   Gleicher Weg wie beim Hereinfahren, 136 %, nur etwas langsamer: Hier
   wartet niemand auf eine Antwort, und die neue Seite kommt dabei zum
   Vorschein. Nur transform, damit die Bewegung auf dem Compositor läuft
   und auf dem Handy nicht ruckelt.

   Weil die letzten gut 25 % des Wegs schon ausserhalb des Bildes liegen,
   ist vom Auslaufen der Kurve nur der Anfang zu sehen – die Fläche
   verlässt das Bild in Bewegung und bleibt nirgends stehen.

   .geht bleibt nach dem Abgang am Element stehen (animation-fill-mode
   forwards hält die Verschiebung). Würde die Klasse direkt wieder
   entfernt, übernähme wieder page-loader-ein mit seinem Endwert und die
   Fläche stünde zurück an ihrem Platz. Entfernt wird sie erst beim
   nächsten Seitenwechsel, zusammen mit dem Setzen von .is-active im
   selben Durchlauf (siehe showLoader in js/router.js). */
#page-loader.geht {
  pointer-events: none;
  animation: page-loader-ab 760ms cubic-bezier(.42, 0, .2, 1) forwards;
}
@keyframes page-loader-ab {
  from { transform: translateY(0); }
  to   { transform: translateY(-136%); }
}

/* Reduzierte Bewegung: keine fahrende Fläche. Die Ebene blendet an Ort
   und Stelle ein und wieder aus, kurz und ohne Versatz. */
@media (prefers-reduced-motion: reduce) {
  #page-loader.kommt {
    animation: page-loader-da 200ms linear both;
  }
  #page-loader.geht {
    animation: page-loader-weg 200ms linear forwards;
  }
  @keyframes page-loader-da {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  @keyframes page-loader-weg {
    to { opacity: 0; }
  }
}

/* Seitenwechsel per View Transition (js/router.js): Die alte Seite
   bleibt als Standbild voll deckend stehen, die neue blendet darüber
   ein. Keine Kreuzblende, bei der beide Hälften gleichzeitig Deckkraft
   abgeben: Ist das neue Standbild im ersten Bild der Animation noch
   leer (gemessen in 2 von 6 Läufen zur Startseite), schiene dort der
   helle Seitenhintergrund durch. So liegt darunter immer die alte
   Seite. Die Nav sieht auf beiden Standbildern gleich aus und bleibt
   ruhig stehen. Kurve wie --ease-out, hier ausgeschrieben, weil die
   Pseudo-Elemente außerhalb des normalen Baums liegen.
   Die Animation wird vollständig ersetzt statt nur in Dauer und Kurve
   angepasst: Chrome hängt die Mischart plus-lighter der Voreinstellung
   als eigene Animation an, die sonst stehen bliebe und die neue Seite
   hell auf die alte addierte. */
::view-transition-image-pair(root) {
  isolation: auto;
}
::view-transition-old(root) {
  animation: none;
  mix-blend-mode: normal;
}
::view-transition-new(root) {
  animation: seite-einblenden .28s cubic-bezier(.33, 1, .68, 1) both;
  mix-blend-mode: normal;
}
@keyframes seite-einblenden {
  from { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-new(root) {
    animation-duration: .15s;
  }
}

/* ════════════════════════════════════════════════════════════
   CTA BANNER – gelber Abschluss auf Startseite, Training und Plätze
   Steht hier, weil base.css die einzige Datei ist, die Startseite
   (home.css) und Unterseiten (page.css) gemeinsam laden. Vorher lag
   je eine Kopie in beiden Dateien.
   Der Schriftzug liegt bewusst hinter der Überschrift, nicht mittig
   hinter dem ganzen Banner: auf Bannermitte lief er quer durch die
   Subline und drückte deren Kontrast von 4,83:1 auf 4,39:1 – unter
   die 4,5:1 von WCAG AA. Deckkraft 4,5 % statt 7 %, damit auch die
   Überlappung mit den Unterlängen der Überschrift ruhig bleibt.
   ════════════════════════════════════════════════════════════ */
.cta-banner {
  background: var(--color-brand);
  padding: var(--space-section) var(--space-inline);
  text-align: center;
  position: relative;
  overflow: hidden;
}
.cta-banner::before {
  content: 'GAME SET MATCH';
  position: absolute;
  top: 42%; left: 50%;
  transform: translate(-50%, -50%);
  font-size: 11.5vw;
  font-weight: 800;
  color: rgba(0,0,0,.045);
  letter-spacing: var(--tracking-snug);
  white-space: nowrap;
  pointer-events: none;
  line-height: 1;
  user-select: none;
}
/* Unter 582px wächst die Überschrift auf zwei bis drei Zeilen; der
   Schriftzug muss dann höher sitzen, um unter ihr herauszubleiben. */
@media (max-width: 581px) {
  .cta-banner::before { top: 34%; }
}
@media (max-width: 360px) {
  .cta-banner::before { display: none; }
}
.cta-banner-inner { position: relative; z-index: 1; }
.cta-banner h2 {
  font-size: clamp(2rem, 5.5vw, 5rem);
  font-weight: 700;
  color: var(--color-navy-dk);
  /* Eigene Größenstufe (32px → 79px); Aufbau siehe oben bei h1–h3.
     Um +.025em zurückgenommen: bei -.047em lief in "Bereit für dein
     erstes Match?" der f-Bogen mit 6,7px² in das ü hinein. */
  letter-spacing: clamp(-.022em, .0369rem - .162vw, -.011em);
  line-height: .95;
  margin-bottom: 1.1rem;
}
.cta-banner p { font-size: clamp(.9rem, 2vw, 1.05rem); color: var(--color-navy-dk); margin-bottom: 2.75rem; }
.cta-banner .btn-white       { background: #fff; color: var(--color-navy-dk); }
@media (hover: hover) {
  .cta-banner .btn-white:hover { background: var(--color-bg-base); }
}
/* Auf Gelb eine helle Fläche statt Blau-Tönung: Blau über Gelb kippt
   ins Oliv. Bleibt klar unter dem weißen Hauptbutton daneben. */
.cta-banner .btn-outline       { background: rgba(255,255,255,.45); color: var(--color-navy-dk); }
@media (hover: hover) {
  .cta-banner .btn-outline:hover { background: rgba(255,255,255,.65); }
}
.cta-banner-btns { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }
