/* Farb-Tokens aus dem dataviz-Skill (references/palette.md) - dieselbe Palette
   wie im Scriptable-Widget, damit App und Widget wie ein Produkt wirken. */
:root {
  color-scheme: light;
  --page-bg: #f9f9f7;
  --card-bg: #fcfcfb;
  --ink-primary: #0b0b0b;
  --ink-secondary: #52514e;
  --ink-muted: #898781;
  --border: rgba(11, 11, 11, 0.1);
  --sequential-blue: #375bff;
  --good: #0ca30c;
  --warning: #fab219;
  --serious: #ec835a;
  --critical: #d03b3b;
  --track: #e1e0d9;
  /* Glas-Tokens fuer die schwebende Navigationsleiste. Bewusst als eigene Tokens
     und nicht aus --card-bg abgeleitet: die Leiste braucht Transparenz, damit der
     Karteninhalt beim Scrollen sichtbar dahinter durchlaeuft - genau das macht
     den Glaseindruck aus. Auf hellem Grund traegt eine hoehere Deckkraft, weil
     dunkle Karten sonst zu stark durchschlagen und die Labels schluckt. */
  --glass-bg: rgba(252, 252, 251, 0.72);
  --glass-border: rgba(11, 11, 11, 0.08);
  --glass-highlight: rgba(255, 255, 255, 0.65);
  --glass-shadow: 0 6px 24px rgba(11, 11, 11, 0.12);
  --glass-active: rgba(55, 91, 255, 0.1);
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --page-bg: #0d0d0d;
    --card-bg: #1a1a19;
    --ink-primary: #ffffff;
    --ink-secondary: #c3c2b7;
    --ink-muted: #898781;
    --border: rgba(255, 255, 255, 0.1);
    --sequential-blue: #3b60ff;
    --track: #2c2c2a;
    --glass-bg: rgba(26, 26, 25, 0.6);
    --glass-border: rgba(255, 255, 255, 0.12);
    --glass-highlight: rgba(255, 255, 255, 0.14);
    --glass-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
    --glass-active: rgba(59, 96, 255, 0.18);
  }
}

* {
  box-sizing: border-box;
}

/* Ohne diese Regel gewinnt z.B. ".token-gate{display:flex}" gegen das
   [hidden]-Attribut (Autor-CSS schlaegt UA-Stylesheet, auch bei gleicher
   Spezifitaet) - dann sind Token-Gate und Hauptansicht gleichzeitig sichtbar. */
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  height: 100%;
  background: var(--page-bg);
  color: var(--ink-primary);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
}

#app {
  display: flex;
  flex-direction: column;
  /* calc(var(--vh)*100) statt 100dvh: mobile Safari berechnet 100dvh teils anhand des
     GROESSEREN "Toolbar eingeklappt"-Viewports und haengt an diesem Wert fest, auch wenn
     die Toolbar gerade sichtbar (= Viewport kleiner) ist - Ergebnis: #app wird hoeher als
     der tatsaechlich sichtbare Bereich, man kann in echten, bleibenden Leerraum darunter
     scrollen (User-Feedback 2026-07-20; overscroll-behavior half nicht, also kein Bounce,
     sondern echter Ueberschuss). --vh wird in app.js aus window.innerHeight gesetzt (folgt
     der Toolbar zuverlaessig, respektiert per resize-Listener) - Standard-Fix fuer dieses
     bekannte dvh-Verhalten. 100dvh bleibt als Fallback, falls JS/--vh noch nicht gesetzt ist. */
  min-height: 100dvh;
  min-height: calc(var(--vh, 1vh) * 100);
}

/* --- Header --- */
.app-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* 16/8 -> 12/6 (User-Wunsch 2026-07-29: Displayflaeche sparen, Kacheln in
     Zeile 2 der Uebersicht wurden unten abgeschnitten). */
  padding: 12px 16px 6px;
  /* iOS-Statusleiste/Dynamic Island: ohne das ueberlappt der Header die Uhrzeit/
     Signalanzeige, weil "12px" allein nicht bis unter die Statusleiste reicht. */
  padding-top: calc(12px + env(safe-area-inset-top));
}

.header-title {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.app-header .wordmark {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--ink-secondary);
  text-transform: uppercase;
}

.last-sync {
  font-size: 11px;
  color: var(--ink-muted);
}

.header-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

.edit-toggle,
.sync-toggle {
  border: none;
  background: none;
  color: var(--ink-secondary);
  font-size: 26px;
  padding: 6px 9px;
  cursor: pointer;
}
.edit-toggle.active {
  color: var(--sequential-blue);
}
.sync-toggle:disabled {
  cursor: default;
  opacity: 0.6;
}
/* Dreht sich waehrend eine Sync-Anfrage laeuft (Klick -> Watcher-Bestaetigung
   abwarten, siehe requestSync() in app.js) - reduced-motion respektiert, damit
   das nicht ungewollt fuer Nutzer mit Bewegungsempfindlichkeit rotiert. */
.sync-toggle.spinning {
  animation: sync-spin 1s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .sync-toggle.spinning {
    animation: none;
  }
}
@keyframes sync-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* --- Tab content --- */
/* Alle Panels bleiben nebeneinander im DOM (kein display:none/block-Switch mehr) -
   die Sichtbarkeit ergibt sich rein aus der horizontalen transform-Position von
   .tab-track. Das ist Voraussetzung fuer den Swipe: beim Ziehen muss das
   Nachbar-Panel schon gerendert und sichtbar danebenliegen, statt erst beim
   Loslassen eingeblendet zu werden. */
.tab-viewport {
  /* Hoehe wird per JS gesetzt (setTabViewportHeight() in app.js), NICHT ueber flex:1 -
     flex-grow hat sich hier nachweislich nicht zuverlaessig auf den verbleibenden Platz
     geklemmt, sondern liess sich vom eigenen Karteninhalt content-basiert aufblaehen (User-
     Beobachtung 2026-07-20: leere Tabs hatten trotzdem riesigen Scroll-Bereich, weil
     .tab-viewport/.tab-track insgesamt so hoch wurden wie der VOLLSTE Tab). Die JS-Messung
     (window.innerHeight - tabViewport.getBoundingClientRect().top) ist unabhaengig von
     dieser Flex-Unschaerfe und liefert einen harten Pixelwert. */
  overflow: hidden;
  position: relative;
  /* pan-y: vertikales Scrollen bleibt nativ fluessig, horizontal uebernimmt die
     JS-Drag-Logik komplett (kein Wettlauf mit dem Browser um die Achsen-Deutung) */
  touch-action: pan-y;
}
.tab-track {
  display: flex;
  height: 100%;
  /* Transform/Transition werden per JS gesetzt (Drag-Position bzw. Snap-Animation) */
}
.tab-panel {
  flex: 0 0 100%;
  width: 100%;
  min-width: 0;
  /* Hart (nicht min-height) auf die Track-/Viewport-Hoehe geklemmt: nur so wird das eigene
     overflow-y:auto unten ueberhaupt zum echten, unabhaengigen Scroll-Container - mit
     min-height allein wuchs ein langer Tab einfach ueber die Bildschirmhoehe hinaus (statt
     intern zu scrollen) und riss GLEICHZEITIG .tab-viewport/.tab-track wieder mit hoch,
     was fuer JEDEN anderen (kurzen) Tab unnoetigen Scroll-Raum erzeugte (User-Feedback
     2026-07-20: "in einem leeren Tab ist der Scrollbereich unnoetig gross"). */
  height: 100%;
  overflow-y: auto;
  padding: 8px 16px 104px;  /* unten Platz fuer die schwebende Glasleiste inkl. ihres Abstands */
  box-sizing: border-box;
  /* Gleicher Grund wie bei html/body: sonst federt auch das einzelne Panel am eigenen
     Anfang/Ende nach - "scrollen ins Leere" trotz Ende des Karteninhalts. */
  overscroll-behavior-y: none;
}
/* Waehrend eine horizontale Swipe-Achse gelockt ist (siehe app.js): Panels per
   overflow:hidden hart einfrieren, damit ein durch touch-action:pan-y bereits
   nativ gestartetes Vertikal-Scrollen sofort stoppt statt weiterzulaufen. */
.tab-track.locking-vertical .tab-panel {
  overflow-y: hidden;
}

.card-grid {
  display: grid;
  /* minmax(0, 1fr) statt nacktem 1fr: ein bloßes "1fr" impliziert "minmax(auto, 1fr)" -
     eine Spalte mit inhaltlich breiterem Min-Content (z.B. eine Textzeile) kann dann
     breiter werden als ihr rechnerischer Anteil, die andere Spalte wird entsprechend
     schmaler gequetscht (unterschiedlich grosse Gauges links/rechts trotz identischem
     Code - User-Feedback 2026-07-21, in Chrome nicht reproduzierbar, vermutlich ein
     WebKit-spezifisches Grid-Sizing-Detail). minmax(0, 1fr) erzwingt strikt gleiche
     Spaltenbreiten unabhaengig vom Inhalt, browserunabhaengig. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  /* 12px -> 10px (User-Wunsch 2026-07-29: Displayflaeche sparen, siehe .card-Padding
     unten fuer denselben Anlass). Vorheriger 10px->12px-Rueckschritt 2026-07-21 war
     zusammen mit dem seinerzeitigen Gauge-viewBox-Crop zu viel auf einmal - press-
     iert diesmal nicht, da hier gezielt nur der Grid-Gap allein reduziert wird. */
  gap: 10px;
}

.card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 16px;
  /* 14px -> 12px, gap 6px -> 5px (User-Wunsch 2026-07-29: Displayflaeche sparen -
     Trainings-Readiness/Schritte-Kacheln wurden unten abgeschnitten). */
  padding: 12px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  position: relative;
}

.card.wide {
  grid-column: 1 / -1;
}

.card-linkable {
  cursor: pointer;
}

.card-linkable:active {
  background: var(--border);
}

/* Titelzeile ist immer eine volle Breite: links der Titel, rechts titleExtra und
   der Info-Button. Frueher nur bei Karten mit titleExtra per Inline-Style
   gestreckt - jetzt einheitlich, weil das i auf jeder Karte rechts sitzt. */
.card-label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-muted);
  align-self: flex-start;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  width: 100%;
  gap: 6px;
}

/* Zeile direkt unter dem Kartentitel (Wiegedatum der Koerper-Karten, 2026-09-15).
   Bewusst KEIN .card-status: das steht in 13px/ink-secondary und wuerde neben dem
   Titel als zweite Aussage konkurrieren. Hier geht es nur um die Herkunft des
   Werts, also dieselbe gedaempfte Farbe wie der Titel, eine Stufe kleiner und ohne
   Versalien. margin-top hebt den Karten-Gap (5px) teilweise auf, damit die Zeile am
   Titel klebt statt in der Mitte zwischen Titel und Wert zu schweben. */
.card-sublabel {
  align-self: flex-start;
  margin-top: -4px;
  font-size: 11px;
  color: var(--ink-muted);
}

.card-label-right {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Kein Rahmen, keine Flaeche - das i soll die Kachel nicht mit einem zweiten
   Bedienelement neben dem Wert belasten. 24px Trefferflaeche bei 15px sichtbarem
   Kreis: unter ~24px wird das Tippen auf dem Geraet unzuverlaessig, ein
   entsprechend grosser sichtbarer Kreis waere aber zu dominant. */
.card-info {
  position: relative;
  width: 24px;
  height: 24px;
  margin: -4px -4px -4px 0;
  padding: 0;
  border: none;
  background: none;
  color: var(--ink-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
  /* Serifen-Kursiv: das klassische Info-i. Die Kachel-Labels stehen in
     Versalien mit letter-spacing, beides muss hier zurueckgesetzt werden. */
  font-family: Georgia, "Times New Roman", serif;
  font-size: 11px;
  font-style: italic;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0;
  text-transform: none;
}

/* Kreis als ::before statt als border auf dem Button selbst: so bleibt die
   24px-Trefferflaeche unabhaengig vom 15px grossen sichtbaren Ring. */
.card-info::before {
  content: "";
  position: absolute;
  width: 15px;
  height: 15px;
  border: 1px solid currentColor;
  border-radius: 50%;
  opacity: 0.55;
}

.card-info:active {
  color: var(--ink-primary);
}

/* --- Info-Sheet: Erklaerung zur Kachel, faehrt von unten ein --- */
.info-sheet {
  position: fixed;
  inset: 0;
  z-index: 50;
}

/* Explizit, statt sich auf das display:none des UA-Stylesheets fuer [hidden] zu
   verlassen - eine Klassenregel mit display waere staerker und wuerde es
   aushebeln. */
.info-sheet[hidden] {
  display: none;
}

.info-sheet-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  transition: opacity 220ms ease;
}

.info-sheet.open .info-sheet-backdrop {
  opacity: 1;
}

.info-sheet-panel {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 80vh;
  overflow-y: auto;
  /* env() faengt die Home-Indicator-Zone ab, damit der letzte Absatz nicht unter
     der Wischleiste klebt (gleiche Ueberlegung wie bei der Tab-Bar). */
  padding: 10px 20px calc(24px + env(safe-area-inset-bottom));
  background: var(--card-bg);
  border-top: 1px solid var(--border);
  border-radius: 20px 20px 0 0;
  transform: translateY(100%);
  transition: transform 220ms cubic-bezier(0.32, 0.72, 0, 1);
}

.info-sheet.open .info-sheet-panel {
  transform: translateY(0);
}

.info-sheet-grip {
  width: 36px;
  height: 4px;
  margin: 0 auto 12px;
  border-radius: 2px;
  background: var(--border);
}

.info-sheet-close {
  position: absolute;
  top: 12px;
  right: 16px;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: var(--border);
  color: var(--ink-secondary);
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
}

.info-sheet-title {
  margin: 0 40px 10px 0;
  font-size: 17px;
  font-weight: 600;
  color: var(--ink-primary);
}

.info-sheet-body p {
  margin: 0 0 12px;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-secondary);
}

.info-sheet-body p:last-child {
  margin-bottom: 0;
}

/* Gauge-Karten reservieren zwei Titelzeilen, auch wenn der Titel nur eine
   braucht. Sonst startet der Bogen in einer Kachel mit langem Titel ("Acute:
   Chronic Workload Ratio", zweizeilig) 15px tiefer als beim einzeiligen Nachbarn
   ("Schritte") - Gauge und Statuswort standen dadurch nebeneinander auf
   unterschiedlicher Hoehe (User-Feedback 2026-08-29, Screenshot vom Geraet).
   Bewusst ueber :has() auf Gauge-Karten begrenzt statt global: nur dort ist der
   Bogen die dominante, zeilenweise vergleichbare Form. In einer Zeile, in der
   ohnehin beide Titel einzeilig sind, kostet das 15px Hoehe - der Preis fuer ein
   Alignment, das auch nach dem Umsortieren der Kacheln haelt. */
.card:has(svg.gauge) > .card-label {
  min-height: 30px;
}

.card-value {
  font-size: 30px;
  font-weight: 600;
  color: var(--ink-primary);
  font-variant-numeric: tabular-nums;
}
.card-value .unit {
  font-size: 15px;
  font-weight: 400;
  color: var(--ink-secondary);
  margin-left: 3px;
}

.card-status {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
  color: var(--ink-secondary);
}

.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* Kommende Trainings (Uebersicht, ganz oben). Wie die frueher hier stehende
   KI-Karte eine Liste statt einer Zahl - .card zentriert sonst (align-items:center),
   was fuer zeilenweisen Inhalt nicht passt. */
.card-training-plan {
  align-items: stretch;
  gap: 0;
}

/* "Planen ↗" rechts oben auf der Karte (titleExtra-Slot). Das Padding vergroessert
   die Trefferflaeche auf Apples 44px-Richtwert fuer Fingerziele, das gleich grosse
   negative Margin nimmt den Platzbedarf im Layout wieder heraus - der Link sieht
   also unveraendert klein aus, laesst sich aber zuverlaessig treffen. Ohne den
   Ausgleich schoebe das Padding die Titelzeile und damit die ganze Karte auf. */
.card-action {
  /* 15px statt der urspruenglichen 12px (User-Wunsch 2026-08-30): der Button ist die
     einzige Aktion auf der Karte und der Einstieg in den Sonntagsdialog - er darf
     groesser sein als das 13px-Kartenlabel daneben.
     line-height:1 ist dabei kein Schoenheitsdetail: ohne die Begrenzung bestimmte der
     groessere Text die Hoehe der Titelzeile und schoebe die ganze Karte auf. Der
     Uebersichts-Tab ist auf "ohne Scrollen sichtbar" getrimmt (siehe README), da
     zaehlen auch 3px. Mit line-height:1 bleibt die Zeilenhoehe beim Label. */
  font-size: 15px;
  line-height: 1;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: none;
  color: var(--sequential-blue);
  text-decoration: none;
  padding: 12px 4px 12px 16px;
  margin: -12px -4px -12px 0;
  white-space: nowrap;
}
.card-action:active {
  opacity: 0.6;
}
/* Fernzugriff geschlossen (ausserhalb des Sonntagsfensters): sichtbar, aber
   erkennbar nicht anklickbar. Gedaempfte Sekundaerfarbe statt Blau - Blau
   signalisiert in dieser App durchgaengig "hier passiert etwas beim Tippen". */
.card-action-inaktiv {
  color: var(--ink-muted);
  opacity: 0.65;
}

.plan-phase {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 8px;
}

.plan-list {
  display: flex;
  flex-direction: column;
}

.plan-row {
  display: grid;
  /* Feste Spalte fuer den Tag, damit Titel und Zeiten ueber alle Zeilen hinweg
     buendig stehen - mit auto wandert die Kante je nach "Heute"/"Mo, 31. Aug." */
  /* minmax(0, 1fr) statt 1fr fuer die Textspalte: ein blosses "1fr" bedeutet
     "minmax(auto, 1fr)" - der laengste Beschreibungstext setzt damit eine
     Mindestbreite und schiebt die Zeile ueber den Kartenrand hinaus (User-Bug
     2026-08-26, Text lief rechts aus dem Display). Gleicher Fallstrick wie bei
     .card-grid weiter oben. */
  grid-template-columns: 62px 22px minmax(0, 1fr) auto;
  align-items: center;
  gap: 8px;
  padding: 7px 0;
}

.plan-row + .plan-row {
  border-top: 1px solid var(--border);
}

.plan-day {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-secondary);
  /* Nie umbrechen - ein zweizeiliger Tag zieht die ganze Zeile auseinander und
     macht die Liste unruhig. Das Format oben ist so kurz gewaehlt, dass es passt. */
  white-space: nowrap;
}

.plan-row-today .plan-day {
  color: var(--sequential-blue);
}

.plan-icon {
  font-size: 15px;
  line-height: 1;
  text-align: center;
}

.plan-title {
  font-size: 14px;
  color: var(--ink-primary);
  text-align: left;
  /* Lange Titel ebenfalls umbrechen lassen statt die Spalte zu sprengen. */
  overflow-wrap: anywhere;
}

.plan-sub {
  font-size: 11px;
  line-height: 1.35;
  color: var(--ink-muted);
  text-align: left;
  /* Umbruch auf maximal zwei Zeilen (User-Wunsch 2026-08-26). Vorher eine Zeile
     mit Ellipse - die griff wegen der 1fr-Spalte oben aber gar nicht, der Text
     lief stattdessen aus dem Display. Zwei Zeilen als Deckel, damit eine lange
     Begruendung die Uebersicht nicht nach unten schiebt (siehe README
     "Displayflaeche gespart"). */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.plan-meta {
  text-align: right;
  white-space: nowrap;
}

.plan-time {
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-primary);
}

.plan-duration {
  font-size: 11px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}

.hypnogram {
  display: block;
  margin-top: 6px;
}
.calorie-week-svg {
  display: block;
  /* Wie .gauge: SVG-Default clippt an der viewBox, das "Konsumiert"-%-Label
     ueber sehr hohen Balken darf davon nicht abgeschnitten werden. */
  overflow: visible;
}
.hypnogram-legend {
  display: flex;
  gap: 12px;
  margin-top: 6px;
  flex-wrap: wrap;
}
/* Nur fuer die ACWR-Gauge-Legende (User-Wunsch 2026-07-29) - eigene Modifier-
   Klasse statt .hypnogram-legend selbst umzustellen, damit Schlafphasen-/
   Stress-Legende (breite Karten, andere Ausrichtung passt dort) unveraendert
   bleiben. */
.legend-center {
  justify-content: center;
}
.legend-item {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--ink-secondary);
}

.card.hidden-in-edit {
  outline: 2px dashed var(--border);
}
.card-toggle {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--ink-secondary);
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  display: none;
  align-items: center;
  justify-content: center;
}
.edit-mode .card-toggle {
  display: flex;
}
.edit-mode .card.card-hidden {
  opacity: 0.4;
}

/* --- Wobble-Mode: Karten per Drag umsortieren (User-Wunsch 2026-07-20) --- */
/* touch-action:none opfert bewusst das Touch-Scrollen direkt auf einer Karte im
   Edit-Modus (noetig, damit der Drag zuverlaessig startet statt mit dem nativen
   Scroll zu konkurrieren) - das Auto-Scroll in app.js faengt lange Listen ab. */
.edit-mode .card {
  touch-action: none;
  user-select: none;
}
.card.dragging {
  z-index: 5;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  cursor: grabbing;
}
@keyframes wobble {
  0%,
  100% {
    transform: rotate(-1deg);
  }
  50% {
    transform: rotate(1deg);
  }
}
/* Unterschiedliche Dauer je nth-child, damit nicht alle Karten synchron wackeln
   (wie bei den iOS-Homescreen-Icons) - nicht durch echten Zufall, sondern durch
   zwei versetzte Perioden (2er/3er-Raster) simuliert, reicht optisch voellig. */
.edit-mode .card:not(.dragging) {
  animation: wobble 0.26s ease-in-out infinite;
}
.edit-mode .card:nth-child(2n):not(.dragging) {
  animation-duration: 0.22s;
  animation-direction: reverse;
}
.edit-mode .card:nth-child(3n):not(.dragging) {
  animation-duration: 0.3s;
}
@media (prefers-reduced-motion: reduce) {
  .edit-mode .card {
    animation: none;
  }
}

/* --- Gauge (SVG ring) --- */
.gauge {
  /* Skaliert mit der Kartenbreite statt fixer 96px - auf einer ~170-200px
     breiten Karte liess das vorher viel Platz links/rechts neben dem Ring leer. */
  width: 100%;
  max-width: 172px;
  aspect-ratio: 1;
  height: auto;
  /* Ohne das schneidet die SVG-eigene Bounding-Box den drop-shadow-Glow hart ab -
     sichtbare rechteckige Kante statt weichem Ausklingen (User-Feedback 2026-07-19).
     Der eigentliche Glow-Fix ist inzwischen das viewBox-Padding in ringSVG() (siehe
     app.js), das skaliert proportional mit - eine groessere max-width bringt hier
     also kein Clipping-Risiko zurueck. */
  overflow: visible;
}
.gauge-value {
  font-size: 25px;
  font-weight: 700;
  fill: var(--ink-primary);
}
.gauge-label {
  font-size: 11px;
  fill: var(--ink-muted);
}
.gauge-band-label {
  font-size: 16px;
  font-weight: 700;
  text-align: center;
}

/* --- Trend line --- */
.trend-svg {
  width: 100%;
  height: 40px;
  overflow: visible;
}

/* Body-Battery-Sparkline: waechst auf die volle Resthoehe der Karte statt der
   festen 40px oben - schiebt die Achse bündig an den unteren Kartenrand, damit
   sie auf gleicher Hoehe wie die letzte Zeile ("Wach") der Schlafqualitaet-
   Karte endet (Grid streckt beide Karten ohnehin schon auf gleiche Hoehe,
   User-Feedback 2026-07-19). Nur "day-trend-svg" (Body Battery), nicht die
   generische ".trend-svg" (Ruhepuls/HRV-Karten behalten ihre feste Hoehe). */
.day-trend-wrap {
  width: 100%;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.day-trend-wrap .trend-svg {
  flex: 1;
  min-height: 0;
  height: auto;
}

/* --- Fill bar (hydration) --- */
.fill-bar-track {
  width: 100%;
  height: 10px;
  border-radius: 5px;
  background: var(--track);
  overflow: hidden;
}
.fill-bar-fill {
  height: 100%;
  background: var(--sequential-blue);
  border-radius: 5px;
}

/* --- Recovery-Countdown-Leiste (Uebersicht) --- */
/* Gleiche Track-Optik wie .fill-bar-track (10px/5px-Radius) fuer Konsistenz, aber
   overflow:visible statt hidden - der Marker-Punkt am Fuellstands-Ende soll leicht
   ueber den Track hinausragen (Scrubber-Optik), das clippt overflow:hidden sonst ab. */
.recovery-bar-track {
  position: relative;
  width: 100%;
  height: 10px;
  border-radius: 5px;
  background: var(--track);
  overflow: visible;
  margin-top: 8px;
}
.recovery-bar-fill {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  border-radius: 5px;
  /* Countdown zaehlt runter statt hoch (Gegenteil von Hydration) - Breitenaenderung
     weich animiert statt hart zu springen, falls sich der Wert waehrend offener App
     durch einen Sync aendert. */
  transition: width 400ms ease, background-color 400ms ease;
}
.recovery-bar-marker {
  position: absolute;
  top: 50%;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  /* Ring in Karten-Hintergrundfarbe statt Border - trennt den Punkt optisch vom
     Fuellbalken darunter (dataviz-Skill: 2px Oberflaechen-Ring bei ueberlappenden Marks). */
  box-shadow: 0 0 0 3px var(--card-bg);
  transition: left 400ms ease, background-color 400ms ease;
}

/* --- Zielbereich-Balken (Belastungsfokus / Akute Belastung) --- */
.range-bar-track {
  position: relative;
  width: 100%;
  height: 10px;
  border-radius: 5px;
  background: var(--track);
  overflow: visible;
}
.range-bar-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  border-radius: 5px;
}
/* Gestrichelte Zone = neutrale Referenz-Annotation (Garmins "optimaler Bereich"),
   ragt bewusst 3px oben/unten ueber den Fuellbalken hinaus - deutlich als eigene
   Ebene erkennbar statt mit der Fuellung zu verschmelzen (kein Farbklecks, nur
   Kontur, damit sie nicht als vierte Datenfarbe konkurriert). */
.range-bar-zone {
  position: absolute;
  top: -3px;
  bottom: -3px;
  border: 1.5px dashed var(--ink-muted);
  border-radius: 6px;
  box-sizing: border-box;
}

/* --- Badge (training status) --- */
/* 40px->56px (User-Wunsch 2026-07-26, deutlich praesenter als Statussymbol) */
.status-icon-circle {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
  flex-shrink: 0;
}

/* --- Bottom tab bar (Glas-Optik, 2026-08-29) --- */
.tab-bar {
  display: flex;
  /* Fixed statt im normalen Fluss - sonst haengt die Leiste bei kurzen Tabs
     (z.B. Uebersicht mit nur 2 Karten) mitten auf der Seite statt am unteren Rand,
     mit einer grossen Leerflaeche dazwischen. */
  position: fixed;
  /* Schwebende Pill mit schmaler Luecke ringsum (User-Wunsch 2026-08-29). Der
     Abstand nach unten nutzt bewusst NICHT die volle Safe Area: iOS meldet dort
     34pt als Puffer fuer die Home-Geste, der Indikator-Balken selbst endet bei
     rund 13pt. 16pt lassen die Pill knapp darueber schweben - sichtbar eine
     Luecke, ohne den Balken zu beruehren. max() faengt Geraete ohne Safe Area ab
     (dort meldet env() 0). */
  left: 8px;
  right: 8px;
  bottom: max(8px, calc(env(safe-area-inset-bottom) - 18px));
  z-index: 10;
  border-radius: 26px;
  /* Seitliches Padding knapp gehalten: bei sechs Tabs auf einem 393pt-Geraet
     entscheiden wenige Pixel darueber, ob "Gesundheit" noch in seinen Tab passt.
     Am Geraet nachgerechnet. */
  padding: 9px 2px 9px;
  background: var(--glass-bg);
  /* saturate() zusaetzlich zum Blur: ohne die Saettigungsanhebung wirkt
     durchscheinender Inhalt hinter der Milchglasflaeche ausgewaschen und grau -
     das Anheben ist genau der Effekt, der die Flaeche wie getoentes Glas statt
     wie ein Nebelschleier aussehen laesst. -webkit- ist auf iOS weiterhin
     noetig, die App laeuft dort als PWA. */
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  border: 1px solid var(--glass-border);
  /* Schlagschatten hebt die Pill vom Inhalt ab, die inset-Lichtkante oben
     imitiert die Brechung an der Glasoberkante. */
  box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-highlight);
}
.tab-bar button {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  padding: 8px 1px 7px;
  /* Labels 10px, Symbole bleiben gross (siehe .tab-icon): als schwebende Pill
     ist die Leiste 16px schmaler als der Viewport, wodurch je Tab rund 61px
     bleiben - "Gesundheit" braucht bei 11px aber 60.5px und liefe ueber. Bei
     10px sind es 55px, also gut 5px Luft. Es ist die Groesse, die iOS in seiner
     eigenen Tab-Bar verwendet. Am Geraet nachgerechnet, nicht geschaetzt. */
  font-size: 10px;
  color: var(--ink-muted);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  border-radius: 18px;
  /* Labels duerfen bei sechs Tabs auf schmalen Geraeten nicht umbrechen - ein
     zweizeiliger Eintrag wuerde die ganze Leiste hoeher machen. */
  white-space: nowrap;
  transition: background 0.18s ease, color 0.18s ease;
}
.tab-bar button.active {
  color: var(--sequential-blue);
  font-weight: 600;
  /* Eigene Glasflaeche hinter dem aktiven Tab statt nur farbigem Text: auf der
     ohnehin transparenten Leiste traegt Farbe allein zu wenig. */
  background: var(--glass-active);
}
.tab-icon {
  /* 18px -> 23px (User-Wunsch 2026-08-29): die Navigationssymbole wirkten neben
     den Gauge-Kacheln zu klein. */
  font-size: 23px;
  line-height: 1;
}

/* --- Token gate --- */
.token-gate {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 24px;
  text-align: center;
}
.token-gate input {
  width: 100%;
  max-width: 320px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--ink-primary);
  font-size: 14px;
}
.token-gate button {
  padding: 10px 20px;
  border-radius: 10px;
  border: none;
  background: var(--sequential-blue);
  color: white;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.token-gate p {
  color: var(--ink-secondary);
  font-size: 13px;
  max-width: 320px;
}

.error-banner {
  color: var(--critical);
  font-size: 13px;
  padding: 12px 16px;
}
