/* ==========================================================================
   ÖY Designsystem — gemeinsame Token- und Basisschicht
   Richtung: „Ruhige Nordsee-Station" — Schiefer & Petrol, viel Weißraum,
   Farbe NUR für Status (Ampel) und Akzent. Soft-UI-Elevation, Bento-Raster,
   Glas ausschließlich auf Chrome-Ebenen (Topbar, Dialoge, Menüs, Toasts).

   Genutzt von dashboard/ und anmeldung/ — hier VOR dem seiteneigenen
   <style>-Block verlinken, damit Seiten-CSS Overrides behält.

   ACHTUNG — bei JEDER Änderung an dieser Datei das `?v=` an allen <link>-Stellen
   hochzählen (dashboard/, anmeldung/, anmeldung/schnell/, wieder/, faq/):
     grep -rln 'assets/css/oey.css?v=' --include=*.html .
   /assets/* geht mit max-age=86400 raus (vercel.json) UND liegt im Service-Worker-
   Cache (sw.js, stale-while-revalidate). Ohne Bump trifft frisches HTML bis zu einen
   Tag lang auf altes CSS. Genau so ging der Wiederkehrer-Hinweis (.wieder-hinweis)
   nach seinem ersten Deploy live: Markup neu, Stylesheet alt — bildschirmfüllendes
   Icon und blauer Standard-Link. Neue Klassen hier sind ohne Bump nicht ausgeliefert.

   Theming: alle Farbwerte einmalig via light-dark(); der manuelle Schalter
   (data-theme auf <html>, localStorage 'oey-theme') erzwingt color-scheme.

   Breakpoints (Media Queries können keine Variablen nutzen — Konvention):
     SM 560px · MD 760px · LG 900px
   ========================================================================== */

:root { color-scheme: light dark; }
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

:root {
  /* ---- ÖY-Palette: Schiefer, Petrol/Stahlblau, Nebelgrau, Rost-Rot als Detailfarbe ---- */
  --bg: light-dark(#f6f7f7, #1a2029); /* Logbuch: Papierton hell; dunkel bewusst unverändert */
  --card: light-dark(#ffffff, #232b36);
  --card-2: light-dark(#f3f6f7, #2a333f);
  --ink: light-dark(#262e39, #e6ebef);
  --ink-2: light-dark(#454f5c, #bcc7d0);
  --muted: light-dark(#5c6a75, #8d9aa6); /* hell: 5,5:1 auf --card, 5,2:1 auf --bg */
  --border: light-dark(#d8dee1, #333e4b);
  --border-strong: light-dark(#aeb9be, #4a5867); /* trägt jetzt Kontur-Buttons (≥3:1 auf --card) */
  /* Semantische Hairline-Aliase (Logbuch-Linienraster) — erben Sonnenmodus-Overrides via var() */
  --hair: var(--border);
  --hair-strong: var(--border-strong);
  --accent: light-dark(#35606f, #7fb0bf); /* Logbuch: Petrol als Ink — Weiß auf Accent hell 6,9:1 */
  --accent-strong: light-dark(#2d525f, #8fbcca); /* systematische Hover-Stufe ~12 % */
  --accent-ink: light-dark(#35606f, #a3cbd7);
  --accent-soft: light-dark(#e4edf0, #1d3941); /* dunkel 1 Stufe tiefer: accent-ink auf Soft jetzt 7,1:1 */
  --focus-ring: var(--accent); /* EIN Fokus-Token für alle Ringe */
  --on-accent: light-dark(#ffffff, #10242b);
  /* Mail-Gesprächsverlauf: eingegangen (links) vs. gesendet (rechts) — bewusst deutlicher
     unterscheidbar als --card-2/--accent-soft, in den Einstellungen (Branding) änderbar. */
  --mail-bubble-in: light-dark(#eef1f3, #28313d);
  --mail-bubble-out: light-dark(#dcf0e6, #1c3c30);
  --steel: #7c97a4;
  /* Eigener Akzent für die Tide-&-Pegel-Karte — deutlich dunkler/blauer als das Teal von
     --accent-ink (Wind-Karte), damit beide Karten sich auf einen Blick unterscheiden. */
  --tide-accent: light-dark(#0f4c5c, #7fc4d6);
  --rust: light-dark(#8b3e2f, #c4735e);
  --rust-soft: light-dark(#f5e9e5, #3d2721);
  --brand: light-dark(#2b333f, #e6ebef);
  --go: light-dark(#22765a, #63c49c);
  --go-bg: light-dark(#e2f0ea, #16352a);
  --warn: light-dark(#8f6c10, #dcb653);
  --warn-bg: light-dark(#f6efd8, #37300f);
  --warn-ink: light-dark(#7a5a0d, #dcb653); /* Text-Stufe: auf --warn-bg 7,1:1, auf --card 8,2:1 */
  --nogo: light-dark(#96412f, #dd9179);
  --nogo-bg: light-dark(#f5e5e0, #3e2620);
  --low: light-dark(#b06a1a, #e8a663);
  --low-bg: light-dark(#f8ecd9, #3c2c15);
  --low-ink: light-dark(#8a5210, #e8a663); /* Text-Stufe: auf --low-bg 5,5:1, auf --card 6,4:1 */
  /* Einstellungen-Kommunikations-Ton (vorher Literal in dashboard:3678) — Lizenzfarbe dlg-karte */
  --viola: light-dark(#6b5a9e, #ab9ad8);

  /* ---- Sportart-Farben (kategorial) — nur für die Auslastungs-Aufschlüsselung je Sportart
     (farbige Tagesbalken + Icons). Bewusst gedämpft-küstig und untereinander gut unterscheidbar;
     der Ampel-Status (Voll/Droht) bleibt Rot/Gelb und wird weiter über Rahmen & Zahlen getragen. ---- */
  --sport-kite: light-dark(#16788c, #52bccd);
  --sport-wind: light-dark(#4257b0, #8ca0ea);
  --sport-wing: light-dark(#237842, #62c487); /* Weiß auf Wing-Fläche 5,5:1 (A5) */
  --sport-welle: light-dark(#9c4f86, #d891c2);
  --sport-sup: light-dark(#5f7280, #9fb2bd); /* Weiß auf SUP-Fläche 5,0:1 (A5) */

  /* ---- Elevation-Rampe (Soft UI): 4 Stufen statt Ad-hoc-Schatten.
     Dunkelmodus bekommt ECHTE Tiefe (dunkler + weiter) plus feine
     Oberkanten-Aufhellung statt --shadow: none. ---- */
  --shadow-key: light-dark(rgba(43, 51, 63, .06), rgba(0, 0, 0, .32));
  --shadow-ambient: light-dark(rgba(43, 51, 63, .05), rgba(0, 0, 0, .28));
  --shadow-deep: light-dark(rgba(43, 51, 63, .08), rgba(0, 0, 0, .48));
  --edge-hl: light-dark(rgba(255, 255, 255, 0), rgba(255, 255, 255, .055));
  /* Logbuch ist flach: Karten/Buttons tragen Hairline statt Schatten; Elevation
     existiert praktisch nur noch auf Dialogen (--elev-4). */
  --elev-0: none;
  --elev-1: none;
  --elev-2: 0 2px 10px var(--shadow-ambient);
  --elev-3: 0 10px 32px var(--shadow-deep);
  --elev-4: 0 18px 60px var(--shadow-deep);
  --inset-soft: inset 0 1px 2px light-dark(rgba(43, 51, 63, .07), rgba(0, 0, 0, .38));
  /* Legacy-Alias — bestehende box-shadow: var(--shadow)-Stellen laufen weiter */
  --shadow: var(--elev-1);

  /* ---- Glas-Tokens: NUR Chrome-Ebenen (Topbar, Mobile-Nav, Dialog, Panel,
     Toast, Menüs, Chart-Tip). NIE auf Datenkarten/Tabellen.
     Deckkraft bewusst hoch (≥ .92): Menüs/Dialoge liegen direkt über Text —
     bei niedrigeren Werten scheint der Inhalt durch und wird unlesbar. ---- */
  --glass-bg: light-dark(rgba(255, 255, 255, .92), rgba(30, 38, 48, .90));
  --glass-bg-strong: light-dark(rgba(255, 255, 255, .97), rgba(35, 43, 54, .96));
  --glass-border: light-dark(rgba(255, 255, 255, .55), rgba(255, 255, 255, .09));
  --glass-blur: 14px;
  --glass-blur-strong: 20px;
  --backdrop: light-dark(rgba(8, 14, 20, .46), rgba(0, 0, 0, .58)); /* Scrim-Zielband 40–60 % */

  /* ---- Radien (themenunabhängig) — User-Nachtrag „Premium-Apple-Weichheit":
     weicher als der Logbuch-Bogen (8/6/6/4), aber knapper als der Alt-Bestand ---- */
  --r-lg: 14px;   /* Karten, Dialoge */
  --r: 10px;      /* innere Kacheln, Nav-Container */
  --r-sm: 10px;   /* Controls, Eingaben — weiche, angenehme Buttons */
  --r-xs: 6px;    /* Mini-Elemente (Logos in Listen, Menüeinträge) */
  --r-pill: 999px;
  --r-round: 50%;

  /* ---- Abstände auf 4px-Raster ---- */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 36px;

  /* ---- Feste Typo-Skala — alle Größen leiten sich hieraus ab ---- */
  --fs-micro: 11px;   /* Mini-Badges, Achsenbeschriftungen */
  --fs-label: 12px;   /* Oswald-Versal-Labels (Eyebrow, TH, Formular-Label) */
  --fs-meta: 13px;    /* Sekundärtext (Notizen, Gründe, Hinweise) */
  --fs-ui: 14px;      /* dichte UI-Zeilen (Listen, Toasts, Zahlungs-Zeilen) */
  --fs-body: 15px;    /* Fließtext, Tabellen, Buttons, Eingaben */
  --fs-emph: 16px;    /* hervorgehobene Werte in dichten Layouts */
  --fs-title: 18px;   /* Kartentitel */
  --fs-lg: 22px;      /* große Titel, Plan-Karten */
  --fs-xl: 24px;      /* Tide-Hauptwert, Wetter-Jetzt */
  --fs-h2: 26px;      /* View-Überschriften */
  --fs-kpi-sm: 22px;
  --fs-kpi-2: 28px;   /* Stat-Kacheln */
  --fs-kpi: 34px;
  --fs-instr: 42px;   /* Logbuch-Signatur „Instrumenten-Messwert" (Oswald 500, tabular) — Einsatz ab P3/P4 */

  /* ---- Schriftgewichte ---- */
  --fw-reg: 400; --fw-med: 500; --fw-semi: 600; --fw-bold: 700; --fw-black: 800;

  /* ---- Einheitliche Control-Höhen: bündige Buttons/Eingaben/Selects/Filter-Pills ---- */
  --control-h: 40px; --control-h-sm: 32px; --control-px: 14px; --control-fs: 14px;

  /* ---- Motion: dezente Mikro-Interaktionen, keine Choreografie ---- */
  --t-quick: 150ms; --t-med: 220ms; --t-slow: 300ms;
  /* P2-Dauer-Tokens je Interaktionstyp (Frequenz-Framework, docs/design-2026):
     Nav/View-Wechsel = 100+x/Tag = bewusst KEIN Token — dort wird nicht animiert. */
  --m-press: 60ms;    /* Button-Druckpunkt */
  --m-menu: 160ms;    /* Menüs/Dropdowns (Opacity läuft mit 130ms leicht voraus) */
  --m-toast: 220ms;   /* Toasts (= --t-med) */
  --m-dialog: 240ms;  /* Modals — nur Öffnen, Schließen bleibt sofort */
  --m-sheet: 320ms;   /* Bottom-Sheet (Mobile) */
  --ease-out: cubic-bezier(.2, .8, .2, 1);
  --ease-inout: cubic-bezier(.45, 0, .25, 1);
  /* iOS-Drawer-Kurve (Ionic): schneller Auftakt, langes weiches Ausrollen — nur Sheets */
  --ease-drawer: cubic-bezier(.32, .72, 0, 1);
  /* Feder-Easing, ausgerechnet aus der Physik hinter motion.dev (stiffness 260,
     damping 32, mass 1 → gedämpfter Schwinger auf 40 Stützstellen abgetastet).
     Schwingt am Ende minimal nach — dadurch „rastet" ein Dialog ein, statt
     abrupt zu stoppen. NUR für Weg-/Größen-Bewegungen; bei Farbe oder Opacity
     sieht Nachschwingen nach Fehler aus. */
  --ease-spring: linear(0,0.0222,0.0766,0.1494,0.2309,0.3145,0.3961,0.4731,0.5438,0.6078,0.6647,0.7148,0.7586,0.7964,0.8289,0.8567,0.8803,0.9003,0.9172,0.9313,0.9432,0.9531,0.9614,0.9682,0.9739,0.9786,0.9825,0.9857,0.9883,0.9905,0.9922,0.9937,0.9949,0.9958,0.9966,0.9973,0.9978,0.9982,0.9985,0.9988,0.9991);

  /* ---- Schriften: Inter fürs Interface (tabellarische Ziffern für Daten),
     Oswald-Versalien NUR für Labels/Nav/Titel, Marker als Brand-Akzent ---- */
  --font-display: "Oswald", "Segoe UI", sans-serif;
  --font-body: "Inter", -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  --font-mark: "Permanent Marker", "Bradley Hand", cursive;
}

/* ---- Touch-Grundmaß (PAKET-2 / A9-02) ----
   Die Control-Höhen oben stammen aus einer Maus-Oberfläche. Auf dem Tresen-iPad und am
   Handy am Strand sind 40 px knapp und 32 px zu klein. Weil die Höhenvariablen auf JEDE
   Ansicht der Plattform wirken, wird das Grundmaß hier einmal gehoben statt an hundert
   Einzelstellen — und weil es in der geteilten Datei steht, gilt es auch für die
   Anmeldeformulare und die FAQ, nicht nur fürs Dashboard.
   44 px ist die kleinste Zielgröße, die Apple und WCAG 2.5.5 übereinstimmend nennen.
   --control-h-sm zieht bewusst mit: die Konfliktauflösung W4 sagt „Zielgröße gewinnt,
   Dichte gibt nach" — kleine Knöpfe bleiben schmaler und leiser, aber nicht flacher.
   Was das NICHT beweist: ob 44 px am Wasser mit nassen Fingern reichen. Das braucht ein
   echtes Gerät am Tresen und bleibt ausdrücklich offen. */
@media (pointer: coarse) {
  :root { --control-h: 44px; --control-h-sm: 44px; }
}

/* ==========================================================================
   Seitenübergänge — natives Cross-Fade zwischen Seiten (MPA, kein Router).
   Progressive Enhancement: Browser ohne Unterstützung navigieren normal weiter.
   ========================================================================== */
@view-transition { navigation: auto; }

/* ==========================================================================
   Basis — Reset & Grundschrift
   ========================================================================== */
* { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body { font-family: var(--font-body); background: var(--bg); color: var(--ink); line-height: 1.55; font-size: var(--fs-body); }

/* Versal-Beschriftungen NUR auf Karten-Titel-Ebene (max. 1-2 pro Karte, z. B. "LIVE"/"REVIER") —
   Sub-Labels (Böen, Kursfenster, Als Nächstes, …) haben eine eigene, ruhigere Regel darunter,
   sonst sind alle Labels gleich laut und die Hierarchie geht verloren. */
.eyebrow, .strip-label, .stat-tile .label, label.f, th, .cal .dow {
  font-family: var(--font-display); font-size: var(--fs-label); font-weight: var(--fw-med);
  text-transform: uppercase; letter-spacing: .14em; color: var(--muted); /* Logbuch-Micro-Caps */
}
/* Sub-Labels innerhalb einer Karte: Sentence Case statt Caps, sonst identisch (Größe/Farbe/Gewicht) */
.kpi .label, .tide-row .label {
  font-family: var(--font-display); font-size: var(--fs-label); font-weight: var(--fw-med);
  letter-spacing: .02em; color: var(--muted);
}

/* Sichtbarer Tastatur-Fokus auf allen interaktiven Grundelementen */
nav button:focus-visible, nav a.navlink:focus-visible, .btn:focus-visible, .chips .pill:focus-visible, .burger:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ==========================================================================
   Karten
   ========================================================================== */
.card { background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); box-shadow: var(--elev-1); margin-bottom: var(--s5); overflow: hidden; }
.card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; padding: var(--s4) var(--s4) 0; }
.eyebrow { display: block; margin-bottom: 2px; }
.card-head h3 { font-family: var(--font-display); font-size: var(--fs-title); font-weight: var(--fw-semi); text-transform: uppercase; letter-spacing: .05em; }
.card-body { padding: var(--s4); }
.card-note { font-size: var(--fs-meta); color: var(--muted); padding: 0 var(--s4) var(--s4); }
.count-badge { font-size: var(--fs-label); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; background: var(--accent-soft); color: var(--accent-ink); border-radius: var(--r-pill); padding: 3px 10px; }

/* ==========================================================================
   Tabellen
   ========================================================================== */
table { width: 100%; border-collapse: collapse; font-size: var(--fs-body); font-variant-numeric: tabular-nums; }
th { text-align: left; padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border); white-space: nowrap; }
td { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: var(--card-2); }
td b { font-weight: var(--fw-bold); }
.table-scroll { overflow-x: auto; }
.num { font-variant-numeric: tabular-nums; }
/* Handy: Tabellen mit class="stack" werden zu gestapelten Karten (Label je Zelle aus data-label) */
@media (max-width: 560px) {
  table.stack, table.stack tbody { display: block; }
  table.stack thead { display: none; }
  table.stack tr { display: block; border: 1px solid var(--border); border-radius: var(--r); background: var(--card-2); padding: var(--s2) var(--s3); margin: var(--s3) var(--s4); }
  table.stack tbody tr:hover td { background: none; }
  table.stack td { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: var(--s1) var(--s3); padding: 5px 0; border: none; text-align: right; }
  /* Lange Werte (z. B. „außerhalb des Forecast-Zeitraums") brechen in die nächste Zeile,
     statt rechts aus der Karte zu laufen */
  table.stack td > * { min-width: 0; max-width: 100%; overflow-wrap: break-word; }
  table.stack td .reason { flex: 1 1 100%; }
  table.stack td::before { content: attr(data-label); font-family: var(--font-display); font-size: var(--fs-label); font-weight: var(--fw-med); text-transform: uppercase; letter-spacing: .08em; color: var(--muted); flex: none; text-align: left; }
  table.stack td .reason { text-align: right; }
  /* Erste Zelle = Karten-Überschrift: links, ohne Label, mit Trennlinie darunter */
  table.stack td:first-child { display: block; text-align: left; padding: 0 0 var(--s2); margin-bottom: var(--s1); border-bottom: 1px solid var(--border); }
  table.stack td:first-child::before { display: none; }
  table.stack td:first-child .reason { text-align: left; }
  table.stack td:empty, table.stack td[data-label='']:empty { display: none; }
  table.stack td.cell-actions { justify-content: flex-start; padding-top: var(--s2); }
  table.stack td.cell-actions::before { display: none; }
}

/* ==========================================================================
   Status-Pills
   ========================================================================== */
/* Logbuch-Kontur-Pille: Rahmen in currentColor statt Farbfläche; die -bg-Tokens tragen
   weiterhin FLÄCHIGE Statuszonen (Zeilen-Hintergründe, Chips). */
.pill { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-label); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; border: 1px solid currentColor; border-radius: var(--r-pill); padding: 3px 11px; white-space: nowrap; background: transparent; }
.pill::before { content: ""; width: 7px; height: 7px; border-radius: var(--r-round); background: currentColor; flex: none; } /* runder Marker — Apple-Weichheit (User-Nachtrag) */
.pill.go { color: var(--go); }
.pill.warn { color: var(--warn-ink); }
.pill.nogo { color: var(--nogo); }
.pill.low { color: var(--low-ink); }
.pill.neutral { color: var(--muted); border-color: var(--hair-strong); }
.pill.new { color: var(--accent-ink); }
.reason { font-size: var(--fs-meta); color: var(--muted); margin-top: 3px; max-width: 42ch; }

/* ==========================================================================
   Buttons & Formulare — alle Controls teilen dieselbe Höhe (--control-h)
   ========================================================================== */
/* Button-Varianten: .primary (eine Hauptaktion je Kontext) · .danger (destruktiv,
   Kontur) · .ghost (leise Nebenaktion) · .small (dichte Leisten) · .check (grüne
   Bestätigungs-Kontur, dashboard) · .an (aktiver Zustand/Toggle, dashboard) ·
   .oy-zusage/.oy-absage (Mail-Senden, dashboard). Neue Varianten nur mit Eintrag hier. */
/* Basis: weiche Karten-Fläche mit Hairline statt Schatten — Logbuch + Apple-Weichheit
   (User-Nachtrag): Fläche var(--card) statt reiner Kontur auf Transparenz */
.btn { font: inherit; font-size: var(--control-fs); font-weight: var(--fw-semi); letter-spacing: .01em; min-height: var(--control-h); display: inline-flex; align-items: center; justify-content: center; border-radius: var(--r-sm); border: 1px solid var(--hair-strong); background: var(--card); color: var(--ink); padding: 0 var(--control-px); cursor: pointer; white-space: nowrap; box-shadow: none; }
@media (hover: hover) {
  .btn:hover { border-color: var(--ink); color: var(--ink); background: color-mix(in srgb, var(--ink) 5%, var(--card)); box-shadow: none; transform: none; }
  .btn.primary:hover { background: var(--accent-strong); border-color: var(--accent-strong); color: var(--on-accent); filter: none; }
  .btn.ghost:hover { border-color: transparent; background: var(--card-2); color: var(--ink); box-shadow: none; transform: none; }
  .btn.danger:hover { background: var(--nogo-bg); border-color: var(--nogo); color: var(--nogo); }
}
.btn:active { background: color-mix(in srgb, var(--ink) 9%, var(--card)); box-shadow: none; transform: scale(.97); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn.primary:active { background: var(--accent-strong); }
.btn.danger { color: var(--nogo); border-color: var(--nogo); }
.btn.ghost { background: transparent; border-color: transparent; box-shadow: none; color: var(--ink-2); }
.btn.small { min-height: var(--control-h-sm); font-size: var(--fs-meta); padding: 0 var(--s3); }
.btn:disabled, .btn[disabled], .btn[aria-disabled="true"] { opacity: .45; cursor: default; pointer-events: none; }
.row { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; }
input:not([type=checkbox]), select, textarea { font: inherit; font-size: var(--fs-body); background: var(--card-2); color: var(--ink); border: 1px solid var(--border-strong); border-radius: var(--r-sm); padding: 0 12px; min-height: var(--control-h); box-shadow: var(--inset-soft); }
input:not([type=checkbox]):focus, select:focus, textarea:focus { background: var(--card); }
input[type=checkbox] { width: 19px; height: 19px; padding: 0; min-height: 0; accent-color: var(--accent); cursor: pointer; }
/* Touch-Geräte (und alle kleinen Screens): Checkboxen größer, sonst schwer zu treffen */
@media (pointer: coarse), (max-width: 760px) { input[type=checkbox] { width: 22px; height: 22px; } }
/* iOS-Zoom-Guard: unter 16px Eingabeschrift zoomt iOS beim Antippen automatisch hinein.
   !important mit Bedacht — der Guard muss jede spezifischere Schriftgröße (z. B. Such-
   feld 13px, Filter-Selects) auf dem Handy übersteuern. Gilt für alle Seiten. */
@media (max-width: 760px) {
  input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea { font-size: 16px !important; }
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--focus-ring); outline-offset: -1px; border-color: var(--focus-ring); }
textarea { width: 100%; resize: vertical; line-height: 1.55; padding: var(--s2) 12px; min-height: calc(var(--control-h) * 1.7); }
label.f { display: block; margin-bottom: var(--s1); }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: var(--s3) var(--s4); }
/* Grid-Zellen dürfen schmaler werden als die Eigenbreite von <select>/<input type=date> —
   sonst werden die Felder auf dem Handy rechts abgeschnitten. Label oben, Feld unten
   bündig: bricht ein Label um, bleiben die Eingabefelder der Zeile trotzdem auf einer Linie. */
.form-grid > div { min-width: 0; display: flex; flex-direction: column; justify-content: flex-end; }
.form-grid > div label.f { flex: 1; display: flex; align-items: flex-end; }
.form-grid input:not([type=checkbox]), .form-grid select { width: 100%; min-width: 0; max-width: 100%; }
/* .form-grid-lang: die 165px-Mindestbreite von .form-grid reicht für kurze Platzhalter,
   aber nicht für lange wie "Neues Passwort (mind. 10 Zeichen)" (~275px inkl. Padding) —
   dann wird der Platzhalter auf JEDER Breite abgeschnitten, nicht nur mobil (BLOCK1-006).
   280px lässt auto-fit bei schmalen Containern (z. B. den 640px der Konto-Karte) auf
   2 Spalten zurückfallen statt 3 zu quetschen; auf breiten Containern bleiben es 3. */
.form-grid.form-grid-lang { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.note { font-size: var(--fs-meta); color: var(--muted); margin-top: var(--s2); }
.inline-num { width: 74px; min-height: var(--control-h-sm); padding: 0 var(--s2); font-size: var(--fs-meta); }

/* ==========================================================================
   Dialoge
   ========================================================================== */
/* max-height in dvh: 100vh ignoriert die mobile Browser-Leiste — Dialoge würden sonst
   unten hinter der URL-Leiste abgeschnitten. overscroll-behavior verhindert, dass am
   Scroll-Ende des Dialogs die Seite dahinter mitscrollt. */
dialog { border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--card); color: var(--ink); padding: var(--s5); max-width: 660px; width: calc(100% - 32px); margin: auto; max-height: min(88vh, calc(100dvh - 24px)); overflow-y: auto; overscroll-behavior: contain; box-shadow: var(--elev-4); }
dialog::backdrop { background: var(--backdrop); }
/* P2: Öffnungsanimation für ALLE Dialoge — nur die Öffnen-Richtung (@starting-style),
   Schließen bleibt sofort: native close()-Wege und Verhaltens-Invarianten unangetastet.
   Modals bleiben zentriert (origin center), Start nie unter scale(.96) — nichts
   erscheint „aus dem Nichts". Backdrop blendet parallel von transparent ein. */
@media (prefers-reduced-motion: no-preference) {
  dialog[open] { transition: opacity var(--m-dialog) var(--ease-out), transform var(--m-dialog) var(--ease-out); }
  dialog[open]::backdrop { transition: background-color var(--m-dialog) var(--ease-out); }
  @starting-style {
    dialog[open] { opacity: 0; transform: scale(.96) translateY(8px); }
    dialog[open]::backdrop { background-color: transparent; }
  }
}
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  dialog { background: var(--glass-bg-strong); border-color: var(--glass-border); -webkit-backdrop-filter: blur(var(--glass-blur-strong)); backdrop-filter: blur(var(--glass-blur-strong)); }
  dialog::backdrop { -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
}
dialog h3 { font-family: var(--font-display); font-size: var(--fs-title); font-weight: var(--fw-semi); text-transform: uppercase; letter-spacing: .05em; margin-bottom: var(--s4); }

/* Auf/Zu-Animation: rein CSS. @starting-style deckt den Übergang aus dem
   display:none-Startzustand ab (sonst könnte die Öffnen-Transition nicht bei
   opacity 0 beginnen). transition-behavior:allow-discrete erlaubt erstmals
   eine Schließen-Transition, bevor display:none greift — vorher gab es dafür
   gar keine Animation. dialog.sheet hat eigene Slide-Keyframes (siehe
   dashboard/index.html); die laufende Keyframe-Animation gewinnt dort gegen
   diese Transition für transform, die Opacity-Blende kommt trotzdem oben drauf. */
@media (prefers-reduced-motion: no-preference) {
  /* Auf: 260 ms mit Feder — der Dialog kommt aus der Tiefe (scale .96) und rastet
     ein. Zu: 150 ms rein linear-schnell und OHNE Feder; ein nachschwingendes
     Schließen liest sich wie ein Zögern, und wer schließt, ist fertig.
     Deshalb zwei getrennte Übergänge statt eines symmetrischen. */
  dialog {
    opacity: 0; transform: translateY(8px) scale(.96);
    transition: opacity var(--t-quick) var(--ease-out), transform var(--t-quick) var(--ease-out),
      overlay var(--t-quick) allow-discrete, display var(--t-quick) allow-discrete;
  }
  dialog[open] {
    opacity: 1; transform: none;
    transition: opacity var(--t-med) var(--ease-out), transform 320ms var(--ease-spring),
      overlay var(--t-med) allow-discrete, display var(--t-med) allow-discrete;
  }
  @starting-style { dialog[open] { opacity: 0; transform: translateY(8px) scale(.96); } }
  dialog::backdrop {
    opacity: 0;
    transition: opacity var(--t-med), overlay var(--t-med) allow-discrete, display var(--t-med) allow-discrete;
  }
  dialog[open]::backdrop { opacity: 1; }
  @starting-style { dialog[open]::backdrop { opacity: 0; } }
}

/* Breite Variante für große Formular-Dialoge (Kurs / Schüler / Gruppe):
   nahezu bildschirmfüllend, luftigere Felder und mehr Sektionsabstand. */
dialog.wide { max-width: min(1560px, 97vw); width: min(1560px, 97vw); padding: var(--s6) var(--s5); }
dialog.wide .form-grid { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: var(--s4) var(--s5); }
dialog.wide h3 { font-size: var(--fs-lg); margin-bottom: var(--s5); }

/* Zwei zusätzliche Größenstufen neben der Basis (660px) und .wide (1560px) —
   ersetzen bislang verstreute Einzelfall-max-width-Werte an kleinen Dialogen. */
.dlg-xs { max-width: 480px; }
.dlg-md { max-width: 800px; }

/* Handy: Dialoge nutzen fast die volle Breite, Formulare in 2 flexiblen Spalten,
   Eingabefelder mit 16px Schrift (verhindert den Auto-Zoom von iOS beim Antippen) */
@media (max-width: 760px) {
  dialog.wide { width: calc(100% - 12px); max-width: calc(100% - 12px); padding: var(--s4) var(--s3); }
  dialog.wide .form-grid { grid-template-columns: 1fr 1fr; gap: var(--s3) var(--s3); }
}
@media (max-width: 560px) {
  dialog { padding: var(--s4) var(--s3); width: calc(100% - 12px); max-height: calc(100dvh - 20px); }
  .form-grid { grid-template-columns: 1fr 1fr; gap: var(--s3) var(--s2); }
  /* Zwei Spalten lassen auf 375 px nur ~127 px je Feld — genug für „Datum" und „Startzeit",
     zu wenig für alles mit langem Platzhalter oder langer Auswahl. Solche Raster tragen
     .form-grid-lang und stehen auf dem Handy einspaltig: drei Passwortfelder kürzten
     sonst alle drei auf „Aktuelles Passw…" / „Neues Passwort…" / „Neues Passwort…" und
     waren nicht mehr auseinanderzuhalten. */
  .form-grid.form-grid-lang { grid-template-columns: 1fr; }
  dialog input:not([type=checkbox]), dialog select, dialog textarea { font-size: 16px; /* iOS-Zoom-Guard — nicht tokenisieren */ }
}
/* Dialog-Fußzeile — ein Kanon für die ganze App (Betreiber-Entscheidung 08/26):
   destruktive NEBENaktion („Löschen", „Gruppe auflösen") links außen, dann die Gruppe
   [Abbrechen/Schließen · Hauptaktion] rechts, Hauptaktion ganz rechts. Die gefährlichste
   Aktion liegt damit so weit wie möglich von der meistgeklickten entfernt.
   Eine destruktive HAUPTaktion („Kurs absagen") bleibt rechts — sie ist der Zweck des
   Fensters, keine Nebenaktion.
   Auf dem Handy gestapelt in umgekehrter Reihenfolge: Hauptaktion oben, Abbrechen darunter,
   Destruktives ganz unten. */
.dlg-actions { display: flex; gap: var(--s2); margin-top: var(--s4); justify-content: space-between; flex-wrap: wrap; }
.dlg-actions .grp { display: flex; gap: var(--s2); flex-wrap: wrap; }
/* Steht links nichts (oder ist der Löschen-Knopf ausgeblendet, weil der Datensatz neu ist),
   würde space-between die Gruppe nach LINKS ziehen — sie gehört auch dann nach rechts. */
.dlg-actions > .grp:last-child { margin-left: auto; }
@media (max-width: 560px) {
  .dlg-actions { flex-direction: column-reverse; align-items: stretch; }
  .dlg-actions .grp { flex-direction: column-reverse; align-items: stretch; }
  /* Gestapelt zieht ein auto-Rand die Gruppe aus der vollen Breite — hier nicht gewollt. */
  .dlg-actions > .grp:last-child { margin-left: 0; }
  .dlg-actions .btn { width: 100%; justify-content: center; }
}
/* Fußzeile klebt am unteren Dialogrand, wo der Dialog als Ganzes scrollt (also überall
   außer bei Kopf-fix/Mitte-scroll/Fuß-fix wie in .dlg-screen). Ohne das lag die
   Aktionsleiste unter dem Falz: gemessen auf einem iPhone SE (667 px) stand „Senden"
   in dlg-nachricht bei y = 1003, in dlg-gruppe bei 903, in dlg-sammel bei 673 — kein
   Knopf im Bild. Verschärft dadurch, dass die Listen darin (.sa-liste, max-height 46vh)
   eigene Scrollbereiche sind: ein Wisch scrollt die Liste, nie den Dialog. Nur ≤760px;
   am Rechner ist genug Höhe da. */
@media (max-width: 760px) {
  .dlg-sticky-actions .dlg-actions {
    position: sticky; bottom: calc(-1 * var(--s4)); z-index: 2; background: var(--card);
    padding-block: var(--s3); margin-bottom: calc(-1 * var(--s4));
    box-shadow: 0 -10px 14px -10px rgba(0, 0, 0, .3);
  }
  /* Der Versatz oben nimmt an, dass der Dialog --s4 (16px) Innenabstand hat — das gilt
     für .wide ab ≤760px. .msg-dlg ist nicht .wide und behält zwischen 561 und 760 px die
     Grundregel mit --s5 (24px): darunter blieb ein 9-px-Streifen offen, durch den Inhalt
     hinter der Leiste durchscrollte. Unter 561 px greift die allgemeine dialog-Regel mit
     --s4, dort stimmt der Versatz wieder. */
  @media (min-width: 561px) {
    .msg-dlg.dlg-sticky-actions .dlg-actions { bottom: calc(-1 * var(--s5)); margin-bottom: calc(-1 * var(--s5)); }
  }
}

/* ==========================================================================
   Wiederkehrer-Hinweis — „Du warst schon mal bei uns?" → /wieder/
   Steht ganz oben in BEIDEN Anmeldeformularen (Wizard + Schnellanmeldung) und
   auf der Website-Variante derselben Seiten (?quelle=website). Hier in der
   geteilten Schicht statt zweimal im Seiten-CSS: ein Aussehen, eine Stelle.

   Bewusst nur Titel + Knopf, kein Untertext (der stand früher hier, s. Git-
   Historie „Dann füll nichts doppelt aus…") — der Kasten sitzt ganz oben auf
   der Startseite und soll in einem Blick erfasst sein, nicht gelesen werden.

   Der ganze Kasten ist EIN <a> — .wh-cta sieht nur aus wie ein Knopf. Ein
   echtes <button> im Link wäre verschachtelte Interaktivität und für
   Screenreader und Tastatur ein Stolperstein.
   ========================================================================== */
.wieder-hinweis { display: flex; align-items: center; gap: var(--s3); text-align: left;
  background: var(--accent-soft); border: 1.5px solid var(--accent); border-radius: var(--r);
  padding: 12px 14px; color: var(--ink); text-decoration: none; min-height: 44px;
  transition: background var(--t-quick), border-color var(--t-quick); }
.wieder-hinweis:hover { background: var(--card); }
.wieder-hinweis:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.wieder-hinweis .wh-ico { flex: none; width: 28px; height: 28px; color: var(--accent); }
.wieder-hinweis .wh-ico svg { width: 100%; height: 100%; display: block; }
.wieder-hinweis .wh-txt { flex: 1; min-width: 0; }
.wieder-hinweis .wh-titel { display: block; font-family: var(--font-display); font-size: var(--fs-ui);
  font-weight: var(--fw-semi); text-transform: uppercase; letter-spacing: .06em; color: var(--accent-ink); }
.wieder-hinweis .wh-cta { flex: none; font-family: var(--font-display); font-size: var(--fs-label);
  font-weight: var(--fw-semi); text-transform: uppercase; letter-spacing: .08em; white-space: nowrap;
  background: var(--accent); color: var(--on-accent); border-radius: var(--r-sm); padding: 10px 14px; }
/* Sehr schmales Handy: der Knopf rutscht unter den Titel, statt ihn zu quetschen */
@media (max-width: 360px) {
  .wieder-hinweis { flex-wrap: wrap; }
  .wieder-hinweis .wh-cta { width: 100%; text-align: center; }
}

/* ==========================================================================
   Atmosphäre — Holzfassade
   ========================================================================== */
/* Holzfassade im Hintergrund (echtes Foto der Container-Holzverkleidung) */
.wood-tex { position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background-image: url('../wood-facade-bg.webp');
  background-repeat: repeat-x; background-size: auto 100%; background-position: bottom left;
  opacity: .09; }
/* P4/W1: Dunkel-Dosis von .22 auf .12 — bei .22 wurde aus der Atmosphäre ein
   Holzbrett-Foto unter den Karten (Stilbruch zur Logbuch/Apple-Linie); .12 hält
   die Fassaden-Signatur auf der Wahrnehmungsstufe des Hellmodus. */
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .wood-tex { opacity: .12; } }
:root[data-theme="dark"] .wood-tex { opacity: .12; }

/* ==========================================================================
   Logo (hell/dunkel-Variante)
   ========================================================================== */
/* Doppelte Klasse = Spezifität (0,2,0): gewinnt gegen Container-Regeln wie
   ".brand-logo-big img { display: block }", ohne !important zu brauchen. */
.lg-d.lg-d { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .lg-l { display: none; }
  :root:not([data-theme="light"]) .lg-d { display: block; }
}
:root[data-theme="dark"] .lg-l { display: none; }
:root[data-theme="dark"] .lg-d { display: block; }
:root[data-theme="light"] .lg-l { display: block; }
:root[data-theme="light"] .lg-d { display: none; }

/* ==========================================================================
   Toasts
   ========================================================================== */
.toast-wrap { position: fixed; bottom: 92px; left: 50%; transform: translateX(-50%); z-index: 80; display: flex; flex-direction: column; gap: var(--s2); align-items: center; pointer-events: none; }
.toast { background: var(--ink); color: var(--bg); border-radius: var(--r-pill); padding: 10px 20px; font-size: var(--fs-ui); font-weight: var(--fw-semi); box-shadow: var(--elev-3); opacity: 0; transform: translateY(8px); max-width: min(92vw, 480px); }
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .toast { background: light-dark(rgba(43, 51, 63, .85), rgba(230, 235, 239, .92)); -webkit-backdrop-filter: blur(var(--glass-blur)); backdrop-filter: blur(var(--glass-blur)); }
  .toast.fehler { background: var(--nogo); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.toast.show { opacity: 1; transform: none; }
.toast.fehler { background: var(--nogo); color: #fff; }
@media (prefers-reduced-motion: no-preference) { .toast { transition: opacity var(--m-toast), transform var(--m-toast) var(--ease-out); } }

/* ==========================================================================
   Sonnenmodus (data-contrast="high") — für pralle Sonne am Strand
   Erzwingt helle Tokens (color-scheme: light lässt alle light-dark()-Werte hell
   werden) und ersetzt Glas/Transparenz durch solide, kontraststarke Flächen mit
   kräftigeren Rändern/Ink. Setzt bewusst NUR Token-Overrides — die Komponenten
   erben alles automatisch. Gewinnt gegen data-theme (gleiche Spezifität, später
   in der Datei), sodass der Sonnenmodus auch aus dem Dunkelmodus heraus greift.
   ========================================================================== */
:root[data-contrast="high"] { color-scheme: light; }
:root[data-contrast="high"] {
  --bg: #e7ebed;
  --card: #ffffff;
  --card-2: #eef2f4;
  --ink: #141c26;
  --ink-2: #2f3b47;
  --muted: #47535e;
  --border: #aeb8bf;
  --border-strong: #828d95;
  --glass-bg: #ffffff;
  --glass-bg-strong: #ffffff;
  --glass-border: #aeb8bf;
  --glass-blur: 0px;
  --glass-blur-strong: 0px;
}
:root[data-contrast="high"] * { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; }

/* ==========================================================================
   Aktions-Rückmeldung an Knöpfen
   Muster: KokonutUI (Klick-Quittung am Knopf) · shadcn/ui (Zustands-Varianten).
   Steuerung in assets/js/oey-motion.js — dort steht auch, warum die Quittung
   an toast() hängt und nicht an fünfzig einzelnen Aufrufstellen.

   Warum überhaupt: Beim Absenden liegt der Blick auf dem Knopf, den man gerade
   gedrückt hat — nicht auf dem Toast am unteren Bildschirmrand. Am Strand, mit
   nassen Fingern und in der Sonne ist „hat er den Knopf genommen?" die
   häufigste Unsicherheit. Die Antwort gehört dorthin, wo die Hand ist.
   ========================================================================== */

/* Damit Ripple und Haken innerhalb des Knopfes bleiben. overflow: hidden ist
   hier unkritisch — Knöpfe haben keine überstehenden Kinder. */
.btn { position: relative; overflow: hidden; }

@media (prefers-reduced-motion: no-preference) {

  /* ---- Ripple vom Berührungspunkt ----------------------------------------
     --rx/--ry setzt oey-motion.js aus dem pointerdown-Ereignis. Ohne die
     Variablen (kein JS) startet der Ripple mittig — harmlos.
     Auf ::before, weil ::after am Knopf schon anderweitig belegt sein kann. */
  .btn::before {
    content: ""; position: absolute; left: var(--rx, 50%); top: var(--ry, 50%);
    width: 0; height: 0; border-radius: var(--r-pill); pointer-events: none;
    transform: translate(-50%, -50%); opacity: 0;
    background: currentColor;
  }
  .btn.m-ripple::before { animation: m-ripple 480ms var(--ease-out); }
  /* 2.4× der Knopfbreite deckt auch einen Klick ganz am Rand bis zur
     gegenüberliegenden Ecke ab. Deckkraft bewusst niedrig: die Quittung soll
     spürbar sein, nicht die Beschriftung überdecken. */
  @keyframes m-ripple {
    from { width: 0; height: 0; opacity: .22; }
    to   { width: 240%; height: 240%; opacity: 0; }
  }
  /* Primärknöpfe tragen currentColor = Weiß auf Petrol — dort etwas kräftiger,
     sonst verschwindet der Ripple auf der satten Fläche. */
  .btn.primary.m-ripple::before { animation-name: m-ripple-hell; }
  @keyframes m-ripple-hell {
    from { width: 0; height: 0; opacity: .38; }
    to   { width: 240%; height: 240%; opacity: 0; }
  }

  /* ---- Druckpunkt --------------------------------------------------------
     Der bestehende :active-Zustand (Schatten nach innen) bleibt; hier kommt
     nur das kurze Eindrücken dazu. 60 ms — schneller als jede Absicht. */
  .btn:active { transform: scale(.97); transition: transform 60ms var(--ease-out); }

  /* ---- Erfolg / Fehler ---------------------------------------------------
     Nach der Aktion quittiert der Knopf kurz: Grün mit einem knappen Auffedern,
     Rot mit einem kurzen Kopfschütteln.

     BEWUSST OHNE Haken-/Kreuz-Symbol. Ein Symbol im Knopf muss irgendwo hin:
     als ::after wird es zum Flex-Element, verschiebt die Beschriftung und
     ändert die Knopfbreite — in einer Werkzeugleiste rutscht dann alles
     daneben mit. Zudem beschneidet das overflow: hidden (nötig für den Ripple)
     ein gedrehtes Häkchen an der Ecke. Farbe plus Bewegung tragen dieselbe
     Aussage, ohne einen einzigen Pixel Layout zu verschieben.

     Rot bekommt bewusst eine ANDERE Bewegungsform als Grün, nicht nur eine
     andere Farbe: Wer rot/grün nicht unterscheidet, liest den Unterschied
     trotzdem — Aufmerken gegen Verneinen. */
  .btn.m-ja   { border-color: var(--go);   color: var(--go);   animation: m-ja-pop 420ms var(--ease-spring); }
  .btn.m-nein { border-color: var(--nogo); color: var(--nogo); animation: m-nein-nein 380ms var(--ease-out); }
  .btn.m-ja, .btn.m-nein { transition: border-color var(--t-quick), color var(--t-quick), background var(--t-quick); }

  /* Primärknöpfe tragen Weiß auf Petrol — dort muss die FLÄCHE wechseln, sonst
     stünde grüner Text auf petrolfarbenem Grund und wäre schlechter lesbar als
     vorher. --card als Textfarbe kontrastiert in beiden Themes gegen --go. */
  .btn.primary.m-ja   { background: var(--go);   border-color: var(--go);   color: var(--card); }
  .btn.primary.m-nein { background: var(--nogo); border-color: var(--nogo); color: var(--card); }

  @keyframes m-ja-pop { 0% { transform: none; } 40% { transform: scale(1.045); } 100% { transform: none; } }
  /* Klein gehalten (3 px): ein Knopf, der weit ausschlägt, wirkt kaputt statt
     abweisend — und in einer engen Werkzeugleiste stößt er optisch an. */
  @keyframes m-nein-nein {
    0%, 100% { transform: none; }
    20% { transform: translateX(-3px); }
    50% { transform: translateX(3px); }
    80% { transform: translateX(-2px); }
  }
}

/* Abgeschaltete Bewegung: die Quittung bleibt als reine FARBE erhalten — sie
   trägt Information („hat geklappt" / „hat nicht geklappt") und darf deshalb
   nicht mit der Animation verschwinden. Nur Ripple und Haken entfallen. */
@media (prefers-reduced-motion: reduce) {
  .btn.m-ja   { border-color: var(--go);   color: var(--go); }
  .btn.m-nein { border-color: var(--nogo); color: var(--nogo); }
  .btn.primary.m-ja   { background: var(--go);   border-color: var(--go);   color: var(--card); }
  .btn.primary.m-nein { background: var(--nogo); border-color: var(--nogo); color: var(--card); }
}
