/* Gemeinsames Designsystem für Trainer/Eltern-Shell.
   Redesign Runde 2 (13.09.2026, Carsten: "das reicht mir nicht, ich will eine
   echte Neugestaltung" -- frontend-design-Skill, ui-designer/ux-architect
   parallel konsultiert, vier Konfliktpunkte Carsten vorgelegt und entschieden):
   - Shell-Farbe: Navy bleibt Markenfläche (ux-architect) -- wird aber
     horizontal statt vertikal (Sidebar -> Kopfleiste). Siehe #sidebar-Regeln
     weiter unten: dieselbe von renderSidebarInner() erzeugte Liste wird per
     CSS für Desktop (>=1024px) zur Zeile umgebaut, im Mobil-Drawer bleibt
     sie unverändert eine vertikale Liste -- keine JS-/DOM-Änderung nötig.
   - Mobil-Stil (13.09.): zurückhaltend -- Dialoge bleiben zentrierte Modals
     (kein Bottom-Sheet), Filter-Chips sind nie sticky. **Überholt am
     16.09.2026** (Carsten-Auftrag, erneute Konsultation): Drawer/Hamburger
     sind einer Bottom-Tab-Bar gewichen -- der Widerspruch Desktop-Top-Nav
     vs. Mobil-Drawer war nicht mehr zu rechtfertigen. Der ursprüngliche
     Einwand ("unterer Rand gehört den Aktionen") gilt weiter, wird aber
     jetzt über eine Sichtbarkeitsregel gelöst statt durch Verzicht: die
     Tab-Bar verschwindet auf Detail-/Formular-Screens (T4, T5, Ergebnis-
     liste-Import) komplett, siehe `.tabbar`-Regeln weiter unten.
   - Status "erledigt": gefüllte Fläche (ux-architect) -- umrandet = wartet
     auf jemanden. Siehe .pill-Regeln weiter unten.
   - Zweite Schrift: ja (ui-designer) -- Barlow Condensed 600/700, nur für
     Zahlen/Titel >=20px (Zeiten, Zähler, Seitentitel), selbst gehostet wie
     Geist (Datenschutzgrund: keine Google-Fonts-CDN-Anfrage aus dem Browser
     der Nutzer -- die Datei liegt lokal, nur EINMAL beim Bauen dieser
     Änderung von Google Fonts bezogen, nicht zur Laufzeit).
   Farbsystem MUSS auf der echten Vereinsfarbe aufbauen: #0082D6 ist pixel-
   genau aus server/static/img/logo-tsg-wiesloch.png extrahiert (RGB
   0,130,214, Hue 203°) -- das war schon vor diesem Redesign so, bleibt die
   Wurzel aller Blautöne. */

@font-face {
  font-family: 'Geist'; font-style: normal; font-weight: 100 900;
  font-display: swap; src: url(/fonts/geist-latin-wght-normal.woff2) format('woff2');
}
@font-face {
  font-family: 'Barlow Condensed'; font-style: normal; font-weight: 600;
  font-display: swap; src: url(/fonts/barlow-condensed-600.woff2) format('woff2');
}
@font-face {
  font-family: 'Barlow Condensed'; font-style: normal; font-weight: 700;
  font-display: swap; src: url(/fonts/barlow-condensed-700.woff2) format('woff2');
}

:root{
  --brand:#0082D6;

  /* Ink-Skala statt Schwarz/Grau (ui-designer, Redesign Runde 2): Navy-
     Tinte statt reinem Schwarz, damit auch Fließtext erkennbar aus derselben
     Markenfarbe kommt. Kontrast unabhängig nachgerechnet (WCAG-Formel,
     Python), nicht nur optisch geschätzt:
     ink/Weiß 15,75:1 · ink/bg-2 14,42:1 · muted/Weiß 7,89:1 (AAA)
     Weiß/accent 7,52:1 · Weiß/accent-deep 10,92:1
     ok-fg/ok-bg 7,02:1 · warn-fg/warn-bg 8,32:1 · danger/Weiß 7,33:1. */
  --ink:#06253A; --muted:#3C5468;
  --bg:#FFFFFF; --bg-2:#EEF6FC; --bg-3:#D6EBFA;
  --card:#fff; --text:var(--ink); --surface:var(--card);
  /* rule-strong bewusst dunkler als ui-designers Vorschlag (#8FA6B8, nur
     2,53:1) -- #7E97AB erreicht 3,04:1 gegen Weiß, der WCAG-1.4.11-Boden
     für UI-Komponentenkontraste (Rahmen müssen sichtbar sein). Erste
     unabhängige Korrektur an diesem Konzept, per Python nachgerechnet. */
  --rule:#C9D8E4; --rule-strong:#7E97AB;
  --border:var(--rule); --border-strong:var(--rule-strong);
  --accent:#00588F; --accent-deep:#003F6B; --accent-light:#e6f3fc; --info-fg:var(--accent);
  --ok:#135C30; --ok-bg:#E3F3E8;
  --warn-fg:#5C4600; --warn-bg:#FFF6D6;
  --danger:#b00020; --danger-bg:#fbe7ea;
  --neutral-fg:var(--muted); --neutral-bg:var(--bg-2);
  --pill-declined-bg:var(--neutral-fg);
  --r-ctl:4px; --r-dialog:8px; --r-card:8px; --r-pill:999px;
  --lane:var(--brand); --lane-w:4px;

  /* Layout */
  --nav-w:240px; --topbar-h:56px; --tabbar-h:56px; --content-max:1200px; --form-max:640px;

  /* Abstandsskala — einzige erlaubte Werte für neue Regeln */
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px; --s-6:32px;

  /* Schriftskala. --fs-body (16px) bleibt bewusst unangetastet (Presbyopie-
     Beschluss 12.09.2026, s. readable-ui-Skill-Projektnotiz) -- Hierarchie
     entsteht über Gewicht/Kontrast/Abstand, nicht über weiteres Anheben.
     --fs-display ist neu: Seitentitel/Wettkampfname, in --font-display
     gesetzt (kondensiert wirkt erst ab 20px gut, s. Regel unten). */
  --fs-meta:15px; --fs-body:16px; --fs-body-lg:18px;
  --fs-title:22px; --fs-page:28px; --fs-display:32px;
  --font-display:'Barlow Condensed','Geist',-apple-system,sans-serif;

  /* Sidebar/Kopfleiste (Navy) — bewusst FEST, wechselt nicht mit
     prefers-color-scheme (Carstens Entscheidung: Marke bleibt auf jedem
     Gerät gleich). Auf Desktop (>=1024px) wird daraus eine horizontale
     Kopfleiste statt der bisherigen vertikalen Spalte -- Carstens
     Entscheidung Runde 2 (Konfliktpunkt "Shell-Farbe": Navy bleibt,
     wird aber horizontal). */
  --nav-bg:#0b202d; --nav-bg-raised:#102837; --nav-hover:#183649;
  --nav-active-bg:#1f445c; --nav-active-bar:#42adf0;
  --nav-text:#c9d6e3; --nav-text-strong:#f4f8fc; --nav-muted:#7c93ab;
  --nav-border:rgba(255,255,255,.09); --nav-focus:#7cc4ff;
}
@media (prefers-color-scheme: dark){
  :root{
    --ink:#E8F0F7; --muted:#A9BACB;
    --bg:#0B1620; --bg-2:#132231; --bg-3:#1B3348;
    --card:#18232f; --text:var(--ink);
    --rule:#2A3846; --rule-strong:#3D4E60;
    --accent:#0E6EAA; --accent-deep:#7CC4FF; --accent-light:rgba(0,130,214,.16); --info-fg:var(--accent);
    --ok:#4cc38a; --ok-bg:rgba(76,195,138,.14);
    --warn-fg:#e3b341; --warn-bg:rgba(227,179,65,.14);
    --danger:#f27b8a; --danger-bg:rgba(242,123,138,.14);
    --neutral-fg:var(--muted); --neutral-bg:var(--bg-2);
    --pill-declined-bg:#445563;
  }
}

*{box-sizing:border-box}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* [tabindex="-1"] wird in dieser App AUSSCHLIESSLICH für "nach dem Rendern
   programmatisch fokussieren, damit Screenreader die neue Seite/den neuen
   Abschnitt ansagen" verwendet -- nie für etwas, das ein Mensch per Tastatur
   ansteuert. Fokus bleibt (Screenreader-Ansage bleibt erhalten), nur der
   sichtbare Ring verschwindet. */
[tabindex="-1"]:focus{outline:none}
html,body{margin:0}
body{font-family:'Geist',-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,system-ui,sans-serif;
  background:var(--bg);color:var(--text)}

/* Zahlen/Zähler in kondensierter Anzeigetafel-Schrift (ui-designer,
   "Anzeigetafel"-Leitidee) -- NUR für Werte, die eine Entscheidung tragen
   (Zeiten, Zähler, Seitentitel), NIE für Namen oder Fließtext, und NUR
   ab 20px (kondensierte Schrift ist unter 20px presbyopie-feindlich enger
   als Geist, s. readable-ui). Immer mit tabular-nums, damit Ziffern nicht
   springen. */
.num{font-family:var(--font-display);font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:.01em}

.skip-link{position:absolute;left:-9999px;top:0;background:var(--accent);color:#fff;
  padding:10px 16px;border-radius:0 0 var(--r-ctl) 0;z-index:100;font-size:var(--fs-body);text-decoration:none}
.skip-link:focus{left:0}

/* Bereits als lokale Regel in trainer/index.html vorhanden -- hier geteilt
   verfügbar gemacht (19.09.2026, Pausenzeitleiste-Layout-Umbau), damit die
   Eltern-Kacheln (.tick.mine) denselben unsichtbaren "gewählt:"-Präfix
   nutzen können wie das Trainer-Pendant. */
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}

/* ===== App-Shell: Kopfleiste (Desktop) + Topbar/Drawer (Mobil) =====
   #sidebar/#sidebar-mobil teilen dieselbe von renderSidebarInner() erzeugte
   Liste. Die Basisregeln unten (mobil-tauglich: vertikale Liste mit
   sichtbaren Gruppentiteln) gelten für BEIDE Instanzen. Der Media-Query-
   Block weiter unten baut NUR #sidebar (die Desktop-Instanz) per CSS zur
   horizontalen Kopfleiste um -- #sidebar-mobil im Drawer bleibt unverändert
   die vertikale Liste, weil der Drawer ohnehin nur <1024px sichtbar ist. */
.app-shell{display:block;min-height:100vh}
.sidebar{width:var(--nav-w);flex:none;background:var(--nav-bg);color:var(--nav-text);
  display:flex;flex-direction:column;height:100%;overflow-y:auto}
.sidebar-brand{padding:var(--s-4) var(--s-4) var(--s-3);display:flex;align-items:center;
  gap:var(--s-3);text-decoration:none}
.sidebar-brand:focus-visible{outline:2px solid var(--nav-focus);outline-offset:2px;border-radius:var(--r-ctl)}
.sidebar-brand img{flex:none;display:block}
.sidebar-brand .verein{color:var(--nav-text-strong);font-size:var(--fs-body-lg);font-weight:700;display:block}
.sidebar-brand .produkt{color:var(--nav-muted);font-size:var(--fs-meta);font-weight:600}

.marken-zeile{display:flex;align-items:center;gap:var(--s-2);padding-top:var(--s-3)}
.marken-zeile img{flex:none;display:block}
.marken-zeile span{font-size:var(--fs-meta);font-weight:600;color:var(--text)}
@media (min-width:1024px){.marken-zeile{display:none}}
.sidebar nav{flex:1;padding:0 var(--s-2)}
.sidebar h2.nav-section{font-size:var(--fs-meta);font-weight:600;
  color:var(--nav-muted);margin:var(--s-4) var(--s-2) var(--s-1);padding-top:var(--s-3);
  border-top:1px solid var(--nav-border)}
.sidebar h2.nav-section:first-child{border-top:none;margin-top:0}
.sidebar ul{list-style:none;margin:0;padding:0}
.nav-item{display:block;padding:0 var(--s-3);margin:1px 0;min-height:44px;line-height:44px;
  border-radius:var(--r-ctl);color:var(--nav-text);text-decoration:none;font-size:var(--fs-body);
  font-weight:500;border-left:3px solid transparent;background:none;border-top:none;border-right:none;
  border-bottom:none;text-align:left;width:100%;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.nav-item:hover{background:var(--nav-hover);color:var(--nav-text-strong)}
.nav-item.aktuell{background:var(--nav-active-bg);color:var(--nav-text-strong);font-weight:700;
  border-left-color:var(--nav-active-bar)}
.nav-item:focus-visible{outline:2px solid var(--nav-focus);outline-offset:-2px}
.nav-item-sub{margin-left:12px;width:calc(100% - 12px)}
.nav-context{margin:0 0 var(--s-2) var(--s-3);padding-left:var(--s-3);border-left:2px solid var(--nav-border)}
.nav-context-name{display:block;font-size:var(--fs-meta);font-weight:600;color:var(--nav-text-strong);
  padding:var(--s-1) var(--s-2);line-height:1.3;overflow:hidden;display:-webkit-box;
  -webkit-line-clamp:2;-webkit-box-orient:vertical}
.nav-context .nav-item{font-size:var(--fs-meta);min-height:36px;line-height:36px}
.sidebar-foot{padding:var(--s-3) var(--s-4);border-top:1px solid var(--nav-border);
  font-size:var(--fs-meta);color:var(--nav-muted);font-weight:600}
/* Logout-Link (Carsten-Auftrag 20.09.2026) -- eigene Farbe statt der
   gedämpften .sidebar-foot-Schrift, damit er als Aktion statt als reine
   Statusanzeige erkennbar ist; Innenabstand vergrößert die Trefffläche,
   ohne die schmale Fußzeile optisch zu verbreitern. */
.nav-item-abmelden{color:var(--nav-text);font-weight:600;text-decoration:none;
  display:inline-block;padding:var(--s-2) 0;margin:calc(var(--s-2) * -1) 0}
.nav-item-abmelden:hover{color:var(--nav-text-strong);text-decoration:underline}

/* Eltern-Shell (Carsten-Auftrag 20.09.2026, eltern-shell.js) — mobile
   Kopfzeile + Tabs, zentral statt vier einzeln gepflegter, leicht
   abweichender Kopien (Ursache des "Menüführung springt beim Klicken"-
   Funds: historie/index.html hatte hier z.B. 12px statt 6px Abstand).
   Deaktivierter Tab (kein Kind/keine offene Kombination ermittelbar)
   bekommt sein Signal über aria-disabled + Kontrast, nicht über Farbe
   allein (ux-architect-Vorgabe). */
.eltern-nav{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:36px;margin-bottom:var(--s-2)}
.eltern-tabs{display:flex;gap:16px;flex-wrap:wrap}
.eltern-tab{font-size:var(--fs-body);color:var(--muted);font-weight:500;text-decoration:none;padding:4px 0;
  border-bottom:2px solid transparent;min-height:44px;display:inline-flex;align-items:center}
.eltern-tab.aktuell{color:var(--text);font-weight:700;border-bottom-color:var(--accent)}
.eltern-tab[aria-disabled="true"],.nav-item[aria-disabled="true"]{opacity:.45;pointer-events:none}
/* Unter 1024px ist die mobile Kopfzeile der Hauptfall (eigene .eltern-nav,
   keine Sidebar); ab 1024px übernimmt die App-Shell-Sidebar (#sidebar) die
   Navigation -- s. `@media (min-width:1024px){ #sidebar{...} }` oben. Genau
   diese Regel fehlte bisher in historie/index.html (GRÖSSTER Bug-Fund
   20.09.2026): dort verschwand die Sidebar auf Desktop unabhängig von der
   Breite komplett, sobald body.modus-eltern galt. */
@media (min-width:1024px){ .eltern-nav{display:none} }

/* Admin-Vorschau-Leiste (Carsten-Auftrag, 12.09.2026, zentralisiert
   20.09.2026 mit eltern-shell.js) — bewusst NICHT wie eine Meldestatus-
   Farbe (.standzeile), sonst liest man sie beim schnellen Blick als
   Statusmeldung statt als Rollen-Hinweis. */
.admin-vorschau{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:var(--fs-meta);color:var(--muted);
  border:1px dashed var(--border-strong);border-radius:var(--r-ctl);padding:6px 12px;margin:12px 18px 0}
.admin-vorschau a{color:var(--info-fg);text-decoration:none;font-weight:600}
.admin-vorschau .warn{color:var(--warn-fg)}

.app-content{flex:1;min-width:0;padding:var(--s-6) var(--s-5);max-width:var(--content-max);width:100%;margin:0 auto}
.app-content.schmal{max-width:calc(var(--form-max) + var(--s-5) * 2)}

.page-header{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--s-4);
  margin-bottom:var(--s-5);flex-wrap:wrap}
.page-header .zurueck{display:block;font-size:var(--fs-meta);color:var(--info-fg);text-decoration:none;
  background:none;border:none;cursor:pointer;padding:0;margin-bottom:var(--s-2)}
.page-header h1{font-size:var(--fs-page);font-weight:600;margin:0}
.page-header .meta{font-size:var(--fs-meta);color:var(--muted);margin-top:2px}

/* Desktop (>=1024px): #sidebar wird zur horizontalen Kopfleiste. Dieselbe
   Liste bleibt für Tastatur/Screenreader unverändert (nur Zeilenumbruch per
   Flex, keine DOM-Änderung) -- s. Kommentar oben am .app-shell-Block. */
@media (min-width:1024px){
  #sidebar{
    display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;
    position:sticky;top:0;z-index:30;width:100%;height:auto;overflow:visible;
    padding:0 var(--s-5);gap:0 var(--s-4);
    border-top:var(--lane-w) solid var(--brand);
  }
  #sidebar .sidebar-brand{padding:var(--s-2) 0;flex:none}
  #sidebar nav{display:flex;flex:1;flex-wrap:wrap;align-items:center;gap:0 var(--s-1);padding:0}
  #sidebar nav h2.nav-section{display:none}
  #sidebar nav>ul{display:flex;flex-wrap:wrap;gap:0 var(--s-1);margin:0}
  #sidebar .nav-item{display:inline-flex;align-items:center;min-height:44px;line-height:normal;
    padding:0 var(--s-3);width:auto;white-space:nowrap;border-left:none;border-bottom:3px solid transparent;
    border-radius:0}
  #sidebar .nav-item.aktuell{background:none;border-bottom-color:var(--nav-active-bar)}
  #sidebar .nav-item-sub{margin-left:0;width:auto}
  #sidebar .nav-context{
    flex-basis:100%;order:99;margin:0;padding:var(--s-2) 0 var(--s-2);border-left:none;
    border-top:1px solid var(--nav-border);display:flex;align-items:center;gap:0 var(--s-4);flex-wrap:wrap}
  #sidebar .nav-context-name{-webkit-line-clamp:1;padding:0;font-size:var(--fs-body);font-weight:600}
  #sidebar .nav-context ul{display:flex;gap:0 var(--s-1)}
  #sidebar .nav-context .nav-item{min-height:36px}
  #sidebar .sidebar-foot{margin-left:auto;padding:0;border-top:none;flex:none}
  .app-content{padding-top:var(--s-5)}
}

/* Topbar + Bottom-Tab-Bar (< 1024px) -- ersetzt den Hamburger+Drawer
   (Carsten-Auftrag 16.09.2026: "Top-Navigation Desktop, aufklappende
   Seitennavigation Mobil ergibt wenig Sinn" -- Rückkehr zur App-typischen
   Navigation unten, nachdem das Bottom-Nav-Muster am 12./13.09. zunächst
   verworfen worden war. ui-designer+ux-architect erneut konsultiert,
   diesmal MIT dem damaligen Konflikt im Bewusstsein: der untere Rand
   gehört den häufigsten Aktionen -- gelöst über eine native Regel statt
   durch Ignorieren: die Tab-Bar verschwindet auf Detail-/Formular-Screens
   (T4, T5, Ergebnisliste-Import) komplett, dort gehört der Rand wieder der
   sticky Aktionsleiste, wie schon immer. Zwei echte Konfliktpunkte
   Carsten vorgelegt: Verwaltung als eigener Tab+Seite statt "Mehr"-Overlay
   (ux-architect), Tab-Bar verschwindet auf allen Detail-Screens statt nur
   bei echter Platzkollision (ux-architect) -- beide entschieden.
   `dialog.drawer`/`#btn-menu` entfallen ersatzlos, kein Fallback (zwei
   Navigationsmuster wären der eigentliche Fehler gewesen). */
.topbar{display:none}
.tabbar{display:none}
.sub-nav{display:none}
@media (max-width:1023px){
  .app-shell > .sidebar{display:none}
  .topbar{display:flex;align-items:center;gap:var(--s-3);height:var(--topbar-h);
    padding:0 var(--s-4);background:var(--nav-bg);color:var(--nav-text-strong);position:sticky;top:0;z-index:20;
    border-bottom:var(--lane-w) solid var(--brand)}
  .topbar .titel{font-size:var(--fs-body-lg);font-weight:600;flex:1;min-width:0;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  .topbar .rolle{font-size:var(--fs-meta);color:var(--nav-muted);font-weight:600;flex:none}

  /* Wettkampf-Kontext (Meldeliste/Staffeln/Abschluss) -- dieselbe Rolle wie
     `#sidebar .nav-context` im Desktop-Header, nur direkt unter der Topbar
     statt im (jetzt entfallenen) Drawer. Teilt bewusst dieselbe id
     "nav-context" wie die Desktop-Fassung (siehe trainer/staffeln/
     index.html `aktualisiereSidebarKontext()`) -- ein einziger
     `querySelectorAll('#nav-context')`-Aufruf befüllt beide, keine
     JS-Änderung an bestehenden Seiten nötig. */
  .sub-nav{display:flex;overflow-x:auto;align-items:center;gap:0 var(--s-3);
    background:var(--nav-bg-raised);color:var(--nav-text-strong);
    padding:0 var(--s-3);border-bottom:1px solid var(--nav-border);position:sticky;top:var(--topbar-h);z-index:19}
  .sub-nav:empty{display:none}
  /* Name steht auf Mobile schon in der Topbar direkt darüber (Carsten-Fund
     19.09.2026: "warum steht hier zweimal Neptun Cup") -- die Sub-Nav zeigt
     hier absichtlich nur noch die Bereiche, kein zweites Mal den Namen.
     Bleibt als eigener CSS-Slot (nicht aus kontextInhalt() entfernt), weil
     dieselbe Funktion auch die Desktop-Sidebar füllt, wo der Name NUR hier
     steht (ui-designer-Konzept 19.09.2026). */
  .sub-nav .nav-context-name{display:none}
  .sub-nav ul{display:flex;list-style:none;margin:0;padding:0;gap:0 var(--s-1)}
  .sub-nav .nav-item{font-size:var(--fs-meta);min-height:40px;line-height:40px;white-space:nowrap;
    border-left:none;border-bottom:3px solid transparent;border-radius:0;padding:0 var(--s-2)}
  .sub-nav .nav-item.aktuell{background:none;border-bottom-color:var(--nav-active-bar)}

  .app-content{padding:var(--s-4) var(--s-4) calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--s-4))}
  .app-shell.tabbar-versteckt .app-content{padding-bottom:var(--s-4)}
  .app-shell.tabbar-versteckt .sub-nav{display:none}

  .tabbar{position:fixed;left:0;right:0;bottom:0;z-index:20;
    background:var(--nav-bg);border-top:1px solid var(--nav-border);
    padding-bottom:env(safe-area-inset-bottom);
    display:flex}
  /* Solange /me noch lädt (kurzes Fenster beim allerersten Aufbau) ist
     #tabbar leer -- ohne :empty stünde bis dahin ein leerer Navy-Streifen
     am unteren Rand (Senior-Diff-Review-Fund 16.09.2026). */
  .tabbar:empty{display:none}
  .app-shell.tabbar-versteckt .tabbar{display:none}
  .tabbar a{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
    min-height:var(--tabbar-h);color:var(--nav-text);text-decoration:none;font-size:var(--fs-meta);font-weight:600;
    border-top:3px solid transparent}
  .tabbar a svg{flex:none}
  .tabbar a.aktuell{color:var(--nav-text-strong);border-top-color:var(--nav-active-bar)}
  .tabbar a:focus-visible{outline:2px solid var(--nav-focus);outline-offset:-2px}
}

/* ===== Buttons, drei Stufen ===== */
.btn{border:0;border-radius:var(--r-ctl);font-size:var(--fs-body);font-weight:600;padding:10px 18px;
  cursor:pointer;min-height:44px}
.btn-primary{background:var(--accent);color:#fff}
.btn-primary:hover{background:var(--accent-deep)}
.btn-primary:disabled{opacity:.4;cursor:not-allowed}
.btn-outline{background:transparent;border:1.5px solid var(--rule-strong);color:var(--ink)}
.btn-outline:hover{background:var(--bg-2)}
.btn-outline:disabled{opacity:.5;cursor:not-allowed}
.btn-danger-outline{background:transparent;border:1.5px solid var(--danger);color:var(--danger)}
.btn-danger-outline:hover{background:var(--danger-bg)}
.btn-danger-outline:disabled{opacity:.5;cursor:not-allowed;background:transparent}
/* Bislang je einzeln inline dupliziert (z.B. "Wettkampf aus Ausschreibung
   anlegen") -- Senior-Diff-Review-Fund 16.09.2026: als generelle Regel
   ausgehoben, damit ein <a class="btn"> nicht jedes Mal von Neuem
   inline zentriert werden muss. */
a.btn{display:inline-flex;align-items:center;justify-content:center;text-decoration:none}

/* Wrapper für Vergleichs-/Review-Tabellen, bei denen gestapelte Karten die
   Nebeneinander-Lesbarkeit zerstören würden (Carsten-Fund 16.09.2026,
   ux-architect-Prinzip: pro Tabelle bewusst "Karte" ODER "scrollbar"
   einordnen, kein automatischer Auffang für alle). Kopfzeile bleibt
   sichtbar, `tabindex` macht die Scrollbarkeit per Tastatur erreichbar
   (Pfeiltasten scrollen ein fokussiertes Element mit overflow horizontal). */
.tabelle-scroll{overflow-x:auto}
.tabelle-scroll:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.link{font-size:var(--fs-meta);color:var(--info-fg);text-decoration:none;background:none;border:none;
  cursor:pointer;padding:10px 4px;min-height:44px;display:inline-flex;align-items:center}
/* Fehlender Hover-Zustand (Fund 21.09.2026, Konsistenz-Audit Zeilen-Klick):
   auf einer jetzt klickbaren Zeile (Kontakte) ist der Unterstrich beim
   Hover das einzige Signal "das hier ist ein EIGENES Ziel, nicht die
   Zeile" -- gilt für jeden .link in der App, nicht nur den neuen Fall. */
.link:hover{text-decoration:underline}
/* Kleinere Klickfläche für Links, die dicht neben anderem Text/Aktionen
   sitzen -- die 44px-Mindesthöhe von .link ist für freistehende Aktionen
   gedacht, hier (Meldeliste-Zugangshinweis + T4-Zugangsblock, Carsten-
   Auftrag 20.09.2026) würde sie die Zeile unnötig aufblähen. */
.link.kompakt{padding:4px 0;min-height:auto;display:inline-flex;align-items:center}

/* Status-Sprache (Carsten-Entscheidung Runde 2, Konfliktpunkt "Status-
   Form"): gefüllte Fläche = erledigt/abgeschlossen, Rahmen ohne Fläche =
   wartet noch auf jemanden. Wort steht immer dabei -- Farbe ist nie das
   einzige Signal. Zentralisiert aus trainer/index.html, trainer/import/
   index.html, historie/index.html (Mehrkind-Dashboard-Konzept 14.09.2026
   brauchte eine vierte Kopie in eltern/index.html -- das war der Anlass).
   `--pill-declined-bg` eigenes Token statt direkt --neutral-fg: im Dark-
   Mode ist --neutral-fg (=--muted, hell) mit weißer Schrift nur ~1,9:1
   (Accessibility-Fund beim Mehrkind-Konzept) -- fixer dunkler Fließwert
   statt dessen, gegengerechnet auf 7,7:1 gegen Weiß. */
/* Carsten-Fund 20.09.2026 (Trainer-Meldeliste, mobil): "Noch keine
   Antwort" wirkte optisch wie ein Button. ui-designer/ux-architect-Konzept
   (konvergiert auf Option c): Pille global entschärft -- rund (--r-pill)
   statt eckig (--r-ctl), schmaleres Padding, normales statt fettes
   Gewicht bei den beiden reinen Warte-Status. Bestehende App-Konvention
   bleibt: rund=Etikett/Status, eckig=Bedienelement (siehe .btn, .input). */
.pill{display:inline-block;font-size:var(--fs-meta);border-radius:var(--r-pill);padding:2px 10px;
  line-height:1.3;font-weight:600;border:1.5px solid transparent}
/* `a.pill` (Stepper-Pillen sind jetzt mehrheitlich Links, Carsten-Auftrag
   23.09.2026: "durch angelegt/eingeladen/staffeln/melden klicken können")
   -- ohne das würde der Browser-Standard-Unterstrich durch den Pillen-
   Rahmen "geklemmt" aussehen. */
a.pill{text-decoration:none;min-height:24px}
/* Hover als Unterstrich statt Hintergrundwechsel (ui-designer-Fund
   23.09.2026): `background:var(--accent-light)` schlug `.pill.confirmed`
   (weißer Text auf hellem Blau, ~1,3:1 Kontrast) -- betraf bisher nur die
   eine klickbare Staffeln-Pille, wird jetzt spürbar, weil fast jede Pille
   ein Link ist. */
a.pill:hover{text-decoration:underline}
.pill.open{background:transparent;border-color:var(--rule-strong);color:var(--muted);font-weight:500}
.pill.neutral{background:transparent;border-color:var(--rule-strong);color:var(--muted);font-weight:500}
/* .pill.admin (Mehrfachrollen 21.09.2026): Alias auf .pill.proposed --
   Admin ist kein Status ("gefüllt=erledigt" wäre falsch), der Akzent-Rand
   markiert stattdessen "andere Dimension als die Fachrollen-Pills daneben"
   (ui-designer-Konzept). */
.pill.proposed, .pill.admin{background:transparent;border-color:var(--accent);color:var(--accent)}
.pill.confirmed{background:var(--ok);color:#fff;border-color:var(--ok)}
.pill.declined{background:var(--pill-declined-bg);color:#fff;border-color:var(--pill-declined-bg)}
.pill.changed{background:transparent;border:2px solid var(--warn-fg);color:var(--warn-fg);font-weight:700}
/* .pill.fehler (Audit-Fund UI-2, 22.09.2026, ui-designer-Konzept): der
   Fehler-/Hinweis-Zähler im Ausschreibungs-Import ist ein Status-Signal
   ("N Fehler bleiben"), kein Filter-Umschalter -- bekommt deshalb die
   Danger-Rolle in derselben Kontur-Bauweise wie .pill.changed, statt der
   für Filter-Chips reservierten gefüllten Akzentfläche. */
.pill.fehler{background:transparent;border:2px solid var(--danger);color:var(--danger);font-weight:700}
/* .pill.ergebnisse (Ergebnisliste-Wettkampf, Carsten-Auftrag 22.09.2026,
   ui-designer-Konzept): gefüllt hell statt Kontur -- liest sich als
   Kategorie-Etikett ("Datenbestand"), nicht als Zustand eines laufenden
   Vorgangs wie die übrigen Pillen hier. */
.pill.ergebnisse{background:var(--neutral-bg);color:var(--muted);border-color:transparent;font-weight:500}

/* Wettkampf-Flow-Stepper (bis 22.09.2026 lokal in trainer/index.html --
   Carsten-Auftrag 23.09.2026: derselbe Stepper steht jetzt auch auf den
   eigenständigen Seiten staffeln/ und einladen/, deshalb hierher gezogen
   statt einer dritten Kopie, siehe shared.js::renderWettkampfStepperHtml()).
   Zwei unabhängige Bedeutungen pro Pille (ui-designer/ux-architect-Konzept
   22./23.09.2026): Status (Füllung/Kontur/Haken, exklusiv der Wettkampf-
   Status) und Ort ("wo bin ich gerade", `.stepper-hier`) -- können auf
   verschiedenen Pillen liegen (z.B. Status "Schwimmer" + Ort "Staffeln").
   Ort seit 24.09.2026 als Füllung statt Unterstrich-Balken (Carsten: "Unterstreichen
   ist schräg"); bewusst --ink statt --accent, damit die nicht klickbare Ort-Pille
   nicht wie ein Aktions-Knopf aussieht (ux-architect, Carsten-Entscheidung). */
.stepper{display:flex;align-items:center;flex-wrap:wrap;list-style:none;margin:var(--s-3) 0 var(--s-2);padding:0;gap:0;row-gap:var(--s-2)}
.stepper li{margin:0;position:relative}
.stepper-verbinder{display:inline-block;width:var(--s-5);height:2px;background:var(--rule-strong);margin:0 var(--s-2)}
.pill.step-current{background:transparent;border:2px solid var(--accent-deep);color:var(--accent-deep);font-weight:700}
.stepper li.stepper-hier{background:var(--ink);color:var(--bg);border-color:var(--ink);font-weight:700}

/* Pausenzeitleiste-Kacheln (Trainer-T4 + Eltern-Anmeldung, Carsten-Auftrag
   19.09.2026) -- geteilte Definition, damit beide Seiten dieselbe Optik
   zeigen und nicht auseinanderlaufen (ui-designer-Entscheidung: "nach
   app.css verschieben, nicht kopieren"). Echtes horizontales Scrollen
   (Carsten-Entscheidung 19.09.2026, Mockup-Vergleich Option A) --
   min-width:0/max-width:100% als Sicherung, falls .track in ein Grid/
   Fieldset mit abweichendem Default-min-width eingebaut wird. Fade-Kante
   nur auf der Seite, wo noch Inhalt versteckt ist. */
.track{display:flex;align-items:center;gap:3px;overflow-x:auto;padding:4px 0;min-width:0;max-width:100%}
.track.mehr-rechts{mask-image:linear-gradient(90deg,#000 92%,transparent 100%);-webkit-mask-image:linear-gradient(90deg,#000 92%,transparent 100%)}
.track.mehr-links{mask-image:linear-gradient(90deg,transparent 0%,#000 8%);-webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 8%)}
.track.mehr-links.mehr-rechts{mask-image:linear-gradient(90deg,transparent 0%,#000 8%,#000 92%,transparent 100%);-webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 8%,#000 92%,transparent 100%)}
.track:focus-visible{outline:2px solid var(--nav-focus,var(--accent));outline-offset:2px}
/* Nur zeigen, wenn tatsächlich was zu scrollen da ist. */
.scroll-hinweis{display:none;font-size:var(--fs-meta);color:var(--muted);font-style:italic;margin-top:4px}
.timeline-row:has(.track.mehr-links) .scroll-hinweis,
.timeline-row:has(.track.mehr-rechts) .scroll-hinweis{display:block}
/* min-width+text-align (19.09.2026, Layout-Umbau Pausenzeitleiste): seit
   eigene Kacheln nur noch die nackte Zahl statt "WK 4" zeigen, würde eine
   einstellige Kachel (19 px) neben einer zweistelligen (28 px) die Reihe
   beim Ankreuzen sichtbar springen lassen -- feste Mindestbreite hält alle
   Kacheln gleich breit, tabular-nums verhindert zusätzliches Wackeln durch
   proportionale Ziffernbreiten. */
/* border:1px solid transparent auf der Basis (statt nur bei .staffel) --
   reserviert den Rand auf JEDER Kachel, sonst würde die gestrichelte
   Staffel-Kachel 2px größer als ihre Nachbarn und die Reihe spränge beim
   Ankreuzen (20.09.2026, ui-designer-Konzept, gleiche Sorgfalt wie schon
   bei min-width/tabular-nums oben). */
.tick{flex:none;font-size:var(--fs-meta);color:var(--muted);background:var(--neutral-bg);border:1px solid transparent;border-radius:var(--r-ctl);padding:2px 4px;white-space:nowrap;min-width:28px;text-align:center;font-variant-numeric:tabular-nums}
.tick.mine{background:var(--accent);color:#fff;font-weight:700;padding:2px 5px}
.tick.mine.bestaetigt{background:var(--ok)}
.tick.mine.abgelehnt{background:var(--neutral-fg)}
/* Eigener Staffelwunsch: hohl statt gefüllt (Carsten-Auftrag 20.09.2026,
   ui-designer+ux-architect-Konsens) -- dieselbe Semantik wie im Trainer-
   Staffel-Editor (gestrichelt = Platz noch nicht fest, gefüllt = belegt).
   Nicht nur Farbe: Kontur+Strichmuster+der "Staffelwunsch: "-Präfix bleiben
   auch in Graustufen/Forced-Colors und für Screenreader unterscheidbar. */
.tick.mine.staffel{background:var(--card);color:var(--accent);border-color:var(--accent);border-style:dashed;font-weight:700}
@media (prefers-color-scheme: dark){
  /* Weiß auf dem hellen Dark-Mode-Grün (--ok:#4cc38a) lag bei ~2,2:1,
     unter WCAG AA (ui-designer-Fund 19.09.2026). */
  .tick.mine.bestaetigt{color:#06253A}
  /* --accent auf dem dunklen --card-Hintergrund läge bei ~2,9:1 -- wie beim
     Bestätigt-Grün oben der hellere --accent-deep (~8,5:1) statt der
     hellmodus-optimierten Grundfarbe. */
  .tick.mine.staffel{color:var(--accent-deep);border-color:var(--accent-deep)}
  /* Gleicher Kontrastfund, jetzt auch an .pill.proposed (ui-designer-Fund
     21.09.2026, sichtbar geworden durch den neuen Dauereinsatz "Nimmt
     teil" auf der Staffeln-Seite) -- dieselbe Korrektur wie oben. */
  .pill.proposed, .pill.admin{color:var(--accent-deep);border-color:var(--accent-deep)}
}
.gaptext{font-size:var(--fs-meta);color:var(--muted);margin-top:4px}
/* Eltern-Variante (19.09.2026): "keine Pause" (Staffel/Lauf direkt
   danach) wird hervorgehoben -- genau die Frage, die Eltern sich beim
   zweiten Haken stellen. */
.gaptext.warn{color:var(--warn-fg);font-weight:600}

.label{font-size:var(--fs-meta);font-weight:600;color:var(--ink);
  display:block;margin-bottom:4px}

/* ===== Filter-Chips + Achsen-Raster. Höhe zweistufig: 44px auf Touch-
   Geräten, 36px, wo eine Maus/ein Trackpad präzise genug ist. Bewusst NIE
   sticky (Carstens Entscheidung Runde 2, Konfliktpunkt "Mobil-Stil": der
   untere/obere Bildschirmrand bleibt für Aktionen reserviert, nicht für
   dauerhaft sichtbares Filter-Chrome -- hilft auch bei 200%-Zoom). */
.chipset{display:flex;gap:8px;flex-wrap:wrap}
.fchip{font-size:var(--fs-meta);border:1.5px solid var(--rule-strong);background:var(--card);color:var(--ink);
  border-radius:var(--r-ctl);padding:10px 14px;cursor:pointer;min-height:44px;white-space:nowrap}
.fchip.active{background:var(--bg-3);border-color:var(--brand);color:var(--ink);font-weight:700}
.fchip:disabled{opacity:.4;cursor:not-allowed}
/* Mehrfachauswahl-Toggle (Mehrfachrollen 21.09.2026, ui-designer-Konzept):
   Haken-Glyph unterscheidet Toggle-Chips (Rollen/Gruppen) von exklusiven
   Filter-Chips, ohne die Chipbreite beim An-/Abwählen springen zu lassen
   (visibility statt display, wie .tick border-transparent oben). */
.fchip .fchip-tick{visibility:hidden;margin-right:4px}
.fchip.active .fchip-tick{visibility:visible}
@media (pointer:fine){
  .fchip{min-height:36px;padding:6px 14px}
}
.filter{margin-top:var(--s-4);padding-top:var(--s-3);border-top:1px solid var(--rule)}
.filter-row{margin-top:0}
.filter-row + .filter-row{margin-top:var(--s-3)}
.filter-label{font-size:var(--fs-meta);font-weight:600;color:var(--muted);
  display:block;margin-bottom:6px}
@media (min-width:768px){
  .filter-row{display:grid;grid-template-columns:72px 1fr;align-items:center}
  .filter-label{margin-bottom:0}
}
.filter-status{font-size:var(--fs-meta);color:var(--muted);margin-top:var(--s-3)}
.filter-status .link{font-size:var(--fs-meta);color:var(--info-fg);background:none;border:none;cursor:pointer;padding:0;margin-left:6px}

/* ===== Leerer Zustand ===== */
.empty-state{padding:var(--s-6) var(--s-4);text-align:center;color:var(--muted)}
.empty-state .titel{font-size:var(--fs-title);font-weight:600;color:var(--ink);margin-bottom:4px}
.empty-state .satz{font-size:var(--fs-meta);margin-bottom:var(--s-4)}

/* ===== Dialog. Bewusst zentriertes Modal, kein Bottom-Sheet (Carstens
   Entscheidung Runde 2, Konfliktpunkt "Mobil-Stil"). ===== */
dialog.modal{border:none;border-radius:var(--r-dialog);padding:0;max-width:480px;width:92vw;
  max-height:calc(100vh - 32px);background:var(--surface);color:var(--text);box-shadow:0 24px 48px rgba(6,37,58,.28)}
/* .btn-icon (Audit-Fund UI-9, 22.09.2026, ui-designer-Konzept):
   Verallgemeinerung von .modal-close (unten) -- App-weiter Standard für
   "entfernen/schließen"-Icon-Buttons statt der bisherigen Mischung aus
   Unicode-"✕" (staffeln/import) und diesem SVG-Muster (nur die 3 Dialoge).
   44px Touch-Ziel, 36px bei Maus (gleiches Zweistufen-Muster wie .fchip),
   Farbe/Hover über currentColor -- ein SVG-Icon passt sich damit ohne
   eigene Farbwerte an. .danger-Variante für echte Löschaktionen
   (Kontrast siehe .btn-danger-outline, kein neuer Token). */
.btn-icon{background:none;border:none;color:var(--muted);width:44px;height:44px;flex:none;
  display:inline-flex;align-items:center;justify-content:center;border-radius:var(--r-ctl);cursor:pointer;padding:0}
.btn-icon:hover{background:var(--bg-2);color:var(--ink)}
.btn-icon.danger:hover{background:var(--danger-bg);color:var(--danger)}
.btn-icon:disabled{opacity:.3;cursor:not-allowed}
@media (pointer:fine){ .btn-icon{width:36px;height:36px} }

dialog.modal::backdrop{background:rgba(0,0,0,.4)}
dialog.modal .modal-head{display:flex;justify-content:space-between;align-items:center;
  padding:var(--s-4) var(--s-5);border-bottom:1px solid var(--rule)}
dialog.modal .modal-head h2{font-size:var(--fs-title);margin:0}
dialog.modal .modal-body{padding:var(--s-5);overflow:auto}
dialog.modal .modal-actions{padding:var(--s-4) var(--s-5);border-top:1px solid var(--rule);
  display:flex;justify-content:flex-end;gap:var(--s-3)}

/* Drag&Drop-Dropzone (Carsten-Auftrag 20.09.2026, "Wettkampf aus
   Ausschreibung anlegen" als Overlay statt Seitenwechsel -- ui-designer-
   Konzept). Ganze Fläche ist Drop-Ziel, der Button bleibt das einzige
   Tastatur-/Klick-Fokusziel (Dropzone selbst nicht fokussierbar) -- Drag&Drop
   ist auf dem Telefon totes Versprechen, deshalb @media(hover:none) unten. */
.dropzone{border:2px dashed var(--rule-strong);border-radius:var(--r-card);background:var(--bg-2);
  padding:var(--s-6) var(--s-4);text-align:center}
.dropzone .titel{font-size:var(--fs-body);font-weight:600;margin:0 0 4px}
.dropzone .sub{font-size:var(--fs-meta);color:var(--muted);margin:0 0 var(--s-4)}
.dropzone.is-over{border-color:var(--accent);border-style:solid;background:var(--accent-light)}
.dropzone.is-reject{border-color:var(--danger);border-style:solid;background:var(--danger-bg)}
@media (hover:none){ .dropzone .nur-maus{display:none} }

/* ===== Phasen-Fortschritt (Carsten-Auftrag 21.09.2026, ui-designer+
   ux-architect-Konzept): drei ehrliche Phasen statt eines Fake-Prozent-
   Balkens für "Wettkampf aus Ausschreibung anlegen" -- "Datei wird geprüft"
   (lokal, <2s), "KI liest die Ausschreibung" (30-90s), "KI prüft auf
   Widersprüche" (nur PDF, DSV7 zeigt stattdessen "übersprungen"). Verstrichene
   Zeit statt Restzeit-Schätzung: die Dauer eines KI-Aufrufs ist nicht
   vorhersagbar, eine erfundene Restzeit wäre unehrlich. Erledigt=gefüllter
   Punkt (--ok) + Häkchen, aktiv=Punkt in --brand + Text in --text, offen=Punkt
   --rule-strong + Text --muted -- dieselbe "gefüllt=erledigt"-Sprache wie
   .pill.confirmed weiter oben. ===== */
.phasen{list-style:none;margin:0 0 var(--s-2);padding:0;text-align:left}
/* `.phase-body` ist ein eigener Block (Label+Zeit-Zeile + optionaler
   Hinweistext darunter), NICHT Teil derselben Flex-Zeile wie `.phase-punkt`
   -- der Punkt bekommt so eine feste Spaltenbreite, der Rest stapelt sich
   sauber darunter statt mit dem Punkt um denselben Zeilenumbruch zu
   konkurrieren (Carsten-Fund 21.09.2026: Text/Zeit/Hinweis liefen bei
   schmaler Dialogbreite durcheinander). */
.phase{display:flex;align-items:flex-start;gap:var(--s-2);padding:3px 0}
.phase-punkt{flex:none;width:16px;height:16px;border-radius:50%;display:inline-flex;
  align-items:center;justify-content:center;font-size:11px;font-weight:700;color:#fff;
  border:2px solid var(--rule-strong);margin-top:2px}
.phase-offen .phase-punkt,.phase-uebersprungen .phase-punkt{background:transparent;border-color:var(--rule-strong)}
.phase-aktiv .phase-punkt{background:transparent;border-color:var(--brand)}
.phase-erledigt .phase-punkt{background:var(--ok);border-color:var(--ok)}
.phase-body{flex:1;min-width:0}
.phase-zeile{display:flex;align-items:baseline;flex-wrap:wrap;justify-content:space-between;gap:0 var(--s-2)}
.phase-text{font-size:var(--fs-body);color:var(--muted)}
.phase-aktiv .phase-text{color:var(--text);font-weight:600}
.phase-erledigt .phase-text{color:var(--text)}
.phase-uebersprungen .phase-text{font-style:italic}
.phase-zeit{flex:none;font-size:var(--fs-meta);color:var(--muted)}
.phase-sub{margin:2px 0 0;text-align:left}

/* Indeterminierter Balken -- kein aria-valuenow (echter Fortschritt ist
   unbekannt), Bewegung allein signalisiert "läuft noch", kein Prozentwert
   wird behauptet. */
.fortschritt{position:relative;height:4px;border-radius:var(--r-pill);background:var(--bg-3);
  overflow:hidden;margin-top:var(--s-2)}
.fortschritt-segment{position:absolute;top:0;left:0;height:100%;width:30%;border-radius:var(--r-pill);
  background:var(--brand);animation:fortschritt-lauf 1.4s ease-in-out infinite}
@keyframes fortschritt-lauf{
  0%{transform:translateX(-100%)}
  100%{transform:translateX(430%)}
}
@media (prefers-reduced-motion:reduce){
  .fortschritt-segment{animation:none;opacity:.5;width:100%}
}

.input{border:1.5px solid var(--rule-strong);border-radius:var(--r-ctl);padding:10px 12px;font-size:var(--fs-body);
  min-height:44px;background:var(--card);color:var(--text);width:100%}
select.input{min-height:44px}
.feld{margin-bottom:var(--s-4)}

/* Frist-Chip (zentralisiert 21.09.2026, Carsten-Auftrag "vereinsinterner
   Meldeschluss eine Woche vor dem offiziellen -- soll hervorgehoben werden"):
   war bislang VIERFACH dupliziert (trainer/index.html, trainer/import/
   index.html, trainer/ergebnisliste/index.html, historie/index.html), hier
   nach dem Konsistenz-Fix jetzt einmal zentral wie schon .eltern-nav. Auch
   .fristchip in trainer-gruppen.js (Jahrgangsspanne-Anzeige) nutzt weiterhin
   nur die Basisregel unverändert. Drei Zustände (ui-designer/ux-architect-
   Konzept): .aktiv (mehr als 3 Tage bis zur Frist) und .bald (≤3 Tage)
   heben eine bevorstehende Frist optisch ab, der Standard bleibt neutral
   ("vorbei"/kein Termin). Jetzt zusätzlich als <button> in der Meldeliste
   nutzbar (trainer-meldeliste.js) -- Rand/Cursor nur dort relevant, die
   Basisregel bleibt für die reine <span>-Nutzung (Jahrgangsspanne) optisch
   unverändert. */
.fristchip{font-size:var(--fs-meta);background:var(--neutral-bg);color:var(--neutral-fg);
  border-radius:var(--r-pill);padding:4px 12px;border:1.5px solid transparent;font-family:inherit}
button.fristchip{cursor:pointer}
.fristchip.aktiv{background:var(--accent-light);color:var(--accent-deep);font-weight:600}
.fristchip.bald{background:var(--warn-bg);color:var(--warn-fg);font-weight:700}
/* Zwei Chips übereinander statt einem (Carsten-Auftrag 21.09.2026, "beides
   anzeigen": Vereins- + Ausrichter-Termin) -- gestapelt statt nebeneinander,
   ui-designer-Konzept: nebeneinander würde bei den meisten Wettkampfnamen
   auf ~375px-Handys sofort umbrechen (383px für beide Chips zusammen). */
.fristgruppe{display:flex;flex-direction:column;align-items:flex-end;gap:var(--s-1)}
@media (max-width:767px){ .fristgruppe{align-items:flex-start} }
@media (pointer:fine){ button.fristchip:hover{border-color:currentColor} }

/* Nicht verfügbare Aktion als Text statt Button ("nur Admin", "Wird gesendet…") --
   aus trainer/index.html hierher verschoben, weil Historie sie ebenfalls nutzt
   (ui-designer-Konzept 24.09.2026). */
.disabled-note{font-size:var(--fs-meta);color:var(--muted);font-style:italic}
