/* Triathlon-Coach Web-GUI - ohne Framework und ohne Build-Schritt.
 *
 * Aufbau dieser Datei:
 *   1. Farb- und Massmarken (Tokens)   - die einzige Stelle mit Farbliteralen
 *   2. Grundlagen                      - body, Knoepfe, Felder, Flaechen
 *   3. Kopfzeile, Menue, Unterleiste
 *   4. Dialoge (Saisonplan, Legende, Menue, Uebungen)
 *   5. Uebersicht: Chat
 *   6. Wochenplan
 *   7. Ereignisse, Termine, Profil, Coach-Einblick, Anleitung, Ueber
 *   8. Uebungskartei und Abspieler
 *
 * Zwei Regeln, die beim Erweitern einzuhalten sind:
 *
 *  - KEINE Schriftgroesse in px. Die A-/A+-Einstellung (ui.js) setzt die Grundgroesse am
 *    <html>-Element in Prozent; alles rechnet in rem/em darauf auf. Eine px-Angabe waere von
 *    dieser Einstellung ausgenommen und wuerde beim Vergroessern stehenbleiben. Fuer Rahmen,
 *    Radien und Schatten ist px dagegen richtig - die sollen nicht mitwachsen.
 *
 *  - KEINE Farbe direkt in einer Regel. Jede Farbe kommt aus einer Variablen des Tokenblocks,
 *    sonst gibt es sie im Dunkelmodus nicht. Jede Variable braucht einen Wert im blanken
 *    :root - die Systemeinstellung faellt sonst auf undefinierte Werte zurueck.
 */

/* ===================================================================
   1. Tokens
   Drei Zustaende: data-theme="hell", data-theme="dunkel", und nichts
   gesetzt = der Systemeinstellung folgen (ui.js schreibt das Attribut).
   =================================================================== */

:root {
  color-scheme: light;

  --grund: #f6f7f9;
  --flaeche: #ffffff;
  --flaeche-hoch: #eef1f5;
  --flaeche-still: #f7f9fb;

  --rand: #e3e8ee;
  --rand-stark: #cbd4de;

  --text: #16202b;
  --text-leise: #566675;
  --text-still: #8493a1;

  --akzent: #1668c4;
  --akzent-hell: #2f7fd8;
  --akzent-leise: #e7f0fb;
  --akzent-text: #ffffff;

  --gut: #1c7a37;
  --gut-leise: #e3f3e7;
  --warn: #94600a;
  --warn-leise: #fbf2e1;
  --schlecht: #b3261e;
  --schlecht-leise: #fdeeec;
  --regen: #1477d4;      /* Regentropfen im Wochenplan (2.3) */

  --sport-schwimmen: #0e7c95;
  --sport-rad: #b05f00;
  --sport-laufen: #6b3fb5;
  --sport-kraft: #2f6b4f;
  --sport-sonst: #6b7a88;

  /* Vorbereitungsphase im Saisonplan. Die uebrigen Phasen borgen sich Sport- und Zustandsfarben
     (saison-segment-*); fuer die Vorbereitung war keine mehr frei, die sich von der Grundlage
     daneben deutlich abhebt. */
  --phase-vorbereitung: #a3367a;

  /* --- Formkurve ---
     Fitness und Ermuedung sind zwei Reihen desselben Diagramms und muessen auch fuer
     Farbfehlsichtige klar auseinanderliegen; die Werte sind gegen beide Untergruende geprueft
     (Blau/Orange, Farbabstand deutlich ueber der Schwelle). Die Ermuedung steht bewusst eine
     Stufe blasser und duenner als die Fitness: Sie schwankt naturgemaess taeglich stark, und
     gleich stark gezeichnet uebertoent sie die Linie, um die es eigentlich geht.
     Die Baender sind Waschungen, keine gesaettigten Flaechen - sie liegen HINTER den Kurven. */
  --kurve-fitness: #2a78d6;
  --kurve-ermuedung: #ee7d50;
  --kurve-form: #16202b;
  --korridor: rgba(42, 120, 214, 0.18);
  --korridor-kante: rgba(42, 120, 214, 0.50);
  --band-abtrainiert: rgba(250, 178, 25, 0.13);
  --band-frisch: rgba(42, 120, 214, 0.10);
  --band-aufbau: rgba(12, 163, 12, 0.11);
  --band-risiko: rgba(208, 59, 59, 0.12);
  --ziel: #7a3ea3;                          /* Ziel-CTL zum A-Wettkampf: Linie und Beschriftung */
  --ziel-flaeche: rgba(122, 62, 163, 0.09); /* die Spanne zwischen unterem und oberem Richtwert */
  --lager-vorphase: rgba(42, 120, 214, 0.07);   /* Trainingslager: Entlastung davor */
  --lager-camp: rgba(176, 95, 0, 0.13);          /* das Camp selbst */
  --lager-erholung: rgba(28, 122, 55, 0.09);     /* Erholung danach */

  /* --- Erholungsverlauf (1.91) ---
     Blau fuer die eigenen Messwerte, Bernstein fuer einen auffaelligen Tag und den HRV-Status unter
     dem Bereich, das Band als blaue Waschung dahinter. Blau/Bernstein ist in beiden Farbwelten gegen
     die Flaeche geprueft (dataviz-Pruefskript: Farbfehlsichtigkeit dE 27, Kontrast >= 3:1); das
     dunkle Bernstein ist bewusst dunkler als --warn, das als Textfarbe zu hell fuer eine Markierung
     waere. Krankheit nimmt --schlecht-leise, Camps --lager-camp. */
  --kurve-erholung: #2a78d6;
  --kurve-auffaellig: #94600a;
  --erholung-band: rgba(42, 120, 214, 0.12);
  --erholung-band-kante: rgba(42, 120, 214, 0.40);

  --r-s: 8px;
  --r-m: 12px;
  --r-l: 16px;
  --r-rund: 999px;

  --a1: 0.25rem;
  --a2: 0.5rem;
  --a3: 0.75rem;
  --a4: 1rem;
  --a6: 1.5rem;
  --a8: 2rem;

  --schatten: 0 1px 2px rgba(16, 32, 48, 0.06), 0 6px 20px rgba(16, 32, 48, 0.08);
  --schatten-klein: 0 1px 2px rgba(16, 32, 48, 0.07);
  --schleier: rgba(12, 20, 28, 0.42);

  --kopf-hoehe: 3.4rem;

  /* Breite der Gespraechsspalte - siehe die ausfuehrliche Begruendung bei .bubble. */
  --gespraechsbreite: min(60ch, 100%);
  /* Breite der kompakten Wochenplan-Spalte neben dem Gespraech (nur am breiten Schirm). */
  --planspalte-breite: 21rem;
}

/* Die dunklen Werte stehen zweimal: einmal fuer die ausdrueckliche Wahl, einmal fuer
   "der Systemeinstellung folgen". Inhaltlich identisch - CSS kennt keine Moeglichkeit,
   einen Variablenblock unter zwei Bedingungen zu stellen, ohne ihn zu wiederholen. */

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

  --grund: #0f141a;
  --flaeche: #171e26;
  --flaeche-hoch: #202a34;
  --flaeche-still: #1b232c;

  --rand: #28323d;
  --rand-stark: #3b4956;

  --text: #e6edf3;
  --text-leise: #a3b1bf;
  --text-still: #78889a;

  --akzent: #5aa5f5;
  --akzent-hell: #7ab8f8;
  --akzent-leise: #17293d;
  --akzent-text: #071019;

  --gut: #5fd08a;
  --gut-leise: #14301f;
  --warn: #e0a640;
  --warn-leise: #33270f;
  --schlecht: #f2837a;
  --schlecht-leise: #391a18;
  --regen: #62b4ff;

  --sport-schwimmen: #3fc0dc;
  --sport-rad: #e0a03d;
  --sport-laufen: #b18cf0;
  --sport-kraft: #67c396;
  --sport-sonst: #93a3b1;

  --phase-vorbereitung: #eb8cc4;

  /* --- Formkurve (siehe Erlaeuterung oben im hellen Block) --- */
  --kurve-fitness: #3987e5;
  --kurve-ermuedung: #be5126;
  --kurve-form: #e6edf3;
  --korridor: rgba(57, 135, 229, 0.22);
  --korridor-kante: rgba(57, 135, 229, 0.58);
  --band-abtrainiert: rgba(250, 178, 25, 0.12);
  --band-frisch: rgba(57, 135, 229, 0.11);
  --band-aufbau: rgba(12, 163, 12, 0.13);
  --band-risiko: rgba(208, 59, 59, 0.14);
  --ziel: #c9a0ee;
  --ziel-flaeche: rgba(201, 160, 238, 0.13);
  --lager-vorphase: rgba(95, 160, 235, 0.10);
  --lager-camp: rgba(230, 150, 60, 0.17);
  --lager-erholung: rgba(90, 190, 120, 0.13);
  --kurve-erholung: #3987e5;
  --kurve-auffaellig: #c08018;
  --erholung-band: rgba(57, 135, 229, 0.16);
  --erholung-band-kante: rgba(57, 135, 229, 0.50);

  --schatten: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.45);
  --schatten-klein: 0 1px 2px rgba(0, 0, 0, 0.35);
  --schleier: rgba(0, 0, 0, 0.6);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="hell"]) {
    color-scheme: dark;

    --grund: #0f141a;
    --flaeche: #171e26;
    --flaeche-hoch: #202a34;
    --flaeche-still: #1b232c;

    --rand: #28323d;
    --rand-stark: #3b4956;

    --text: #e6edf3;
    --text-leise: #a3b1bf;
    --text-still: #78889a;

    --akzent: #5aa5f5;
    --akzent-hell: #7ab8f8;
    --akzent-leise: #17293d;
    --akzent-text: #071019;

    --gut: #5fd08a;
    --gut-leise: #14301f;
    --warn: #e0a640;
    --warn-leise: #33270f;
    --schlecht: #f2837a;
    --schlecht-leise: #391a18;
    --regen: #62b4ff;

    --sport-schwimmen: #3fc0dc;
    --sport-rad: #e0a03d;
    --sport-laufen: #b18cf0;
    --sport-kraft: #67c396;
    --sport-sonst: #93a3b1;

    --phase-vorbereitung: #eb8cc4;

    /* --- Formkurve (siehe Erlaeuterung oben im hellen Block) --- */
    --kurve-fitness: #3987e5;
    --kurve-ermuedung: #be5126;
    --kurve-form: #e6edf3;
    --korridor: rgba(57, 135, 229, 0.22);
    --korridor-kante: rgba(57, 135, 229, 0.58);
    --band-abtrainiert: rgba(250, 178, 25, 0.12);
    --band-frisch: rgba(57, 135, 229, 0.11);
    --band-aufbau: rgba(12, 163, 12, 0.13);
    --band-risiko: rgba(208, 59, 59, 0.14);
    --ziel: #c9a0ee;
    --ziel-flaeche: rgba(201, 160, 238, 0.13);
    --lager-vorphase: rgba(95, 160, 235, 0.10);
    --lager-camp: rgba(230, 150, 60, 0.17);
    --lager-erholung: rgba(90, 190, 120, 0.13);
    --kurve-erholung: #3987e5;
    --kurve-auffaellig: #c08018;
    --erholung-band: rgba(57, 135, 229, 0.16);
    --erholung-band-kante: rgba(57, 135, 229, 0.50);

    --schatten: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.45);
    --schatten-klein: 0 1px 2px rgba(0, 0, 0, 0.35);
    --schleier: rgba(0, 0, 0, 0.6);
  }
}

/* ===================================================================
   2. Grundlagen
   =================================================================== */

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100dvh;
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  font-size: 1rem;
  line-height: 1.5;
  background: var(--grund);
  color: var(--text);
  -webkit-text-size-adjust: 100%;
}

.hidden { display: none !important; }
/* Das hidden-Attribut muss auch gegen eine eigene display-Regel gewinnen. Die Browser-Vorgabe
   dafuer ist schwaecher als jede Autorenregel: .menue-eintrag { display: flex } hob sie auf, und
   der Coach-Einblick stand bis 0.100 bei JEDEM Konto im Menue, obwohl kopfzeile.js ihn verbarg. */
[hidden] { display: none !important; }
.error { color: var(--schlecht); }

h1, h2, h3, h4 { line-height: 1.25; }

/* Eingebetteter Code muss notfalls mitten im Wort umbrechen duerfen - ueberall, nicht je
   Behaelter. Ein <code> im Fliesstext hat keinen scrollenden Behaelter um sich (anders als ein
   <pre>), und ein langer Bezeichner ohne Leerzeichen hat keine Stelle zum Umbrechen: Gemessen
   schob "COACH_MOCK_INTERVALS_DIR=tests/fixtures" mit 351 px eine 390-px-Seite auf 409 px, und
   dann rutscht beim Wischen das ganze Dokument seitlich weg. Erst traf es die Anleitung
   (.doku-body), nach einem Changelog-Eintrag mit demselben Wort die Ueber-Seite - zwei Behaelter,
   dieselbe Ursache, und der dritte waere gefolgt. Deshalb hier an der Wurzel und nicht dreimal
   einzeln.
   Im Codeblock ausdruecklich wieder aus: Dort scrollt das <pre> fuer sich, und eine mitten im
   Wort zerrissene Befehlszeile waere schlechter als eine, die man schiebt. */
code { overflow-wrap: anywhere; }
pre code { overflow-wrap: normal; }
h1 { font-size: 1.15rem; margin: 0; letter-spacing: -0.01em; }
h2 { margin-top: 0; font-size: 1.05rem; letter-spacing: -0.01em; }
h3 { font-size: 0.95rem; }

a { color: var(--akzent); }

::selection { background: var(--akzent-leise); }

:focus-visible {
  outline: 2px solid var(--akzent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* --- Knoepfe: gefuellt (Standard), still (.subtle), Umriss (.nav-item) --- */

button {
  font: inherit;
  padding: 0.45rem 0.95rem;
  border: 1px solid transparent;
  border-radius: var(--r-s);
  background: var(--akzent);
  color: var(--akzent-text);
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
button:hover { background: var(--akzent-hell); }
button:disabled { opacity: 0.45; cursor: default; }
button:disabled:hover { background: var(--akzent); }

.subtle {
  background: transparent;
  border-color: var(--rand-stark);
  color: var(--text-leise);
  font-weight: 400;
}
.subtle:hover { background: var(--flaeche-hoch); border-color: var(--rand-stark); }

/* Umriss-Knopf, auch fuer Links verwendet (Dialogschliesser, Aktionen neben Ueberschriften) */
.nav-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font: inherit;
  font-size: 0.92rem;
  font-weight: 400;
  line-height: 1.2;
  padding: 0.36rem 0.75rem;
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-s);
  background: var(--flaeche);
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
}
.nav-item:hover { background: var(--flaeche-hoch); border-color: var(--text-still); }
.nav-item-quiet { color: var(--text-leise); }

/* --- Eingabefelder --- */

input, textarea, select {
  font: inherit;
  color: inherit;
  background: var(--flaeche);
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-s);
  padding: 0.45rem 0.55rem;
}
input:focus, textarea:focus, select:focus {
  border-color: var(--akzent);
  outline: none;
  box-shadow: 0 0 0 3px var(--akzent-leise);
}

/* --- Flaechen --- */

section {
  background: var(--flaeche);
  border: 1px solid var(--rand);
  border-radius: var(--r-l);
  padding: var(--a4) var(--a4) var(--a4);
}

/* Am Telefon keine Karten (1.72): Rand und Innenabstand der Karte kamen zum Seitenrand hinzu und
   kosteten bei Voreinstellung 38 px Zeilenbreite, bei der groessten Schriftstufe 53 px - auf
   einem 390 px breiten Telefon fast ein Fuenftel der Zeile. Die Seite wird selbst zur Flaeche, die Abschnitte trennt eine Linie, der
   Seitenrand von 1rem bleibt. Was IN einem Abschnitt eine eigene Karte ist (Tageskarten,
   Kennzahlen, Eingabefeld, "Bisheriger Plan"), behaelt sie: das sind Einheiten, keine Rahmen.
   Der Status-Bereich bleibt ebenfalls eine Karte - der farbige Rahmen IST dort die Warnung.
   Das :not(#status-section) traegt doppelt: Es nimmt den Bereich aus und hebt die Regel ueber
   .kurve-karte (eine Klasse, steht weiter hinten) - sonst behielte die Formkurve ihren Rahmen.
   Die Betreiberseite Chatablauf ist mit #chatablauf-main .card noch spezifischer und behaelt
   ihre Karten; die gehoeren dort zur Zeichnung. */
@media (max-width: 46rem) {
  body { background: var(--flaeche); }
  main section:not(#status-section) {
    background: none;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
  }
  /* ~ statt +: Auf der Formseite stehen die Kennzahlen zwischen zwei Abschnitten. */
  main section:not(#status-section) ~ section {
    border-top: 1px solid var(--rand);
    padding-top: var(--a4);
  }
}

/* Kleine Marke fuer Zahlenangaben (Dauer, Belastung, Uhrzeit) */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  padding: 0.1rem 0.5rem;
  border-radius: var(--r-rund);
  background: var(--flaeche-hoch);
  color: var(--text-leise);
  white-space: nowrap;
}

.badge {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 500;
  border-radius: var(--r-rund);
  padding: 0.1rem 0.55rem;
  margin-left: 0.4rem;
  background: var(--flaeche-hoch);
  color: var(--text-leise);
}
.badge.absolviert { background: var(--gut-leise); color: var(--gut); }
.badge.ausgefallen { background: var(--schlecht-leise); color: var(--schlecht); }
/* "nicht_aufgezeichnet" bekommt absichtlich keine eigene Regel: Es behaelt das neutrale Grau der
   Grundregel, weil eine fehlende Aufzeichnung weder gut noch schlecht ist. Bitte nicht einfaerben. */
/* "bestaetigt" = vom Athleten selbst gemeldet, nicht gemessen. Deshalb die Farbe von "absolviert",
   aber als Umriss statt als Flaeche: dieselbe Aussage, sichtbar andere Quelle. */
.badge.bestaetigt {
  background: transparent;
  color: var(--gut);
  box-shadow: inset 0 0 0 1px var(--gut);
}
.badge.prio { background: var(--warn-leise); color: var(--warn); }

.phase-note { color: var(--text-leise); font-size: 0.88rem; margin-top: 0.4rem; }
/* Eine Notiz, die eine Warnung ist, muss auch danach aussehen. Ohne diese Zeile bleibt sie leise
   grau: .error steht weiter oben in der Datei und hat dieselbe Spezifitaet, also gewinnt die
   spaetere Regel. Dieselbe Falle hat .watch-hint.error weiter unten schon einmal geschlagen. */
.phase-note.error { color: var(--schlecht); }

/* Ueberschrift links, Aktionen rechts - sie bleiben rechts, auch wenn die Ueberschrift
   umbricht. */
.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.section-head h2 { margin-bottom: 0.5rem; }
.head-actions { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.plan-stand { color: var(--text-still); font-size: 0.85rem; }
/* Die Wochensummen unter den Tageskarten (1.86, seit 1.88 dort): reine Information, deshalb ohne
   Warnfarbe. Je Woche die Stunden, darunter etwas leiser die Minuten je Sportart. */
.plan-wochen { margin: var(--a3) 0 0; }
.plan-woche { margin: 0; font-size: 0.92rem; color: var(--text); }
.plan-woche-sport { margin: 0 0 var(--a2); font-size: 0.88rem; color: var(--text-still); }
.plan-wochen .phase-note { margin-top: var(--a1); }

/* Aufklappbarer Abschnitt: die Ueberschrift steht in der Zusammenfassungszeile und bleibt
   anklickbar, der Zaehler daneben sagt zugeklappt, was drinsteckt. */
details > summary { cursor: pointer; }
details > summary > h2 { display: inline; }
.summary-count { font-weight: 400; font-size: 0.88rem; color: var(--text-still); }
details[open] > summary { margin-bottom: 0.5rem; }

/* ===================================================================
   3. Kopfzeile, Menue-Popup, Unterleiste
   Der gesamte Inhalt der Kopfzeile wird von kopfzeile.js erzeugt - hier steht nur die Optik.
   =================================================================== */

#app-header {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: var(--a3);
  min-height: var(--kopf-hoehe);
  padding: 0.4rem var(--a4);
  /* Zwei Angaben mit Absicht: color-mix und backdrop-filter kennt nicht jeder Browser, und
     ohne Rueckfallwert waere der Kopf dort durchsichtig und der Text unlesbar. Wer color-mix
     nicht versteht, verwirft nur die zweite Zeile und behaelt die deckende Flaeche. */
  background: var(--flaeche);
  background: color-mix(in srgb, var(--flaeche) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rand);
}

.kopf-marke {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}
/* Claudias Zeichen vor dem Namen (seit 1.61, davor eine Raute als Schriftzeichen): ein
   Roboterkopf als eingebettetes SVG in currentColor, damit er die Akzentfarbe beider Farbwelten
   traegt (ein Emoji braechte seine eigene Farbe mit und saehe auf jedem Geraet anders aus).
   Groesse in rem, damit er mit A-/A+ mitwaechst - auf allen Breiten dieselbe, er ist breiter
   als die Raute und kostet die drei Zeichen am Ende der Wortmarke auf dem schmalsten Telefon
   ohnehin schon Platz. Ein SVG hat keine Schriftlinie - seine Unterkante gaelte als Grundlinie
   und er staende zu hoch -, deshalb mittig statt auf der Grundlinie der Wortmarke. */
.kopf-marke .kopf-roboter {
  color: var(--akzent);
  width: 1.5rem;
  height: 1.5rem;
  flex: 0 0 auto;
  align-self: center;
}
.kopf-marke-name { font-weight: 600; font-size: 1rem; letter-spacing: -0.01em; }
.version-tag { font-size: 0.68rem; font-weight: 400; color: var(--text-still); }

/* Die drei Disziplinen hinter der Versionsnummer. Groesse in rem, damit sie mit der
   A-/A+-Einstellung mitwachsen; die Zeichen bringen ihre Farbe selbst mit und brauchen deshalb
   keine aus dem Tokenblock. Auf der Grundlinie stehen sie eine Spur zu hoch, weil Emoji ueber
   die Schriftlinie hinausragen - daher die kleine Verschiebung nach unten. */
.kopf-embleme {
  font-size: 0.9rem;
  letter-spacing: 0.08em;
  position: relative;
  top: 0.08em;
  white-space: nowrap;
}
/* Rad und Laufen gespiegelt, damit sie wie der Schwimmer nach rechts ziehen (Kopfzeile und
   Warteanimation). inline-block, sonst greift transform an einem Inline-Element nicht. */
.emblem-gespiegelt { display: inline-block; transform: scaleX(-1); }

/* Auf einer Unterseite steht hinter der Wortmarke, wo man ist - die Reiterleiste kennt nur
   die drei Haupteinstiege. */
.kopf-trenner { color: var(--rand-stark); }
.kopf-seitentitel { font-size: 1rem; font-weight: 600; margin: 0; white-space: nowrap; }

/* Die drei Haupteinstiege. Der aktive traegt Akzentfarbe und einen Unterstrich; die beiden
   anderen sind ruhig, damit "wo bin ich" ohne Lesen erkennbar bleibt. */
.kopf-nav { display: flex; align-items: center; gap: 0.15rem; margin-left: var(--a2); }
.kopf-tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font: inherit;
  font-size: 0.95rem;
  padding: 0.45rem 0.7rem;
  border: none;
  border-radius: var(--r-s);
  background: transparent;
  color: var(--text-leise);
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.kopf-tab:hover { background: var(--flaeche-hoch); color: var(--text); }
.kopf-tab.aktiv { color: var(--akzent); font-weight: 600; }
.kopf-tab.aktiv::after {
  content: "";
  position: absolute;
  left: 0.7rem;
  right: 0.7rem;
  bottom: -0.45rem;
  height: 2px;
  border-radius: 2px;
  background: var(--akzent);
}
/* Der Pfeil sagt: oeffnet in einem eigenen Tab. Das ist keine Zierde - eine Navigation weg
   von der Uebersicht wuerde den laufenden Chat-Stream abreissen. */
.kopf-tab .extern { font-size: 0.7em; opacity: 0.7; }

.kopf-rechts { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; }
#header-user { font-weight: 600; font-size: 0.92rem; white-space: nowrap; }

.kopf-menue-knopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.3rem;
  height: 2.3rem;
  padding: 0;
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-s);
  background: var(--flaeche);
  color: var(--text);
}
.kopf-menue-knopf:hover { background: var(--flaeche-hoch); }
.kopf-menue-knopf svg { width: 1.15rem; height: 1.15rem; }

/* --- Menue-Popup: ein <dialog>, damit Escape und die Fokusfalle vom Browser kommen, aber
   rechts oben angesetzt statt mittig. --- */
.menue-dialog {
  position: fixed;
  top: calc(var(--kopf-hoehe) + 0.3rem);
  right: var(--a4);
  left: auto;
  bottom: auto;
  margin: 0;
  width: min(21rem, 94vw);
  max-height: calc(100dvh - var(--kopf-hoehe) - 1.4rem);
  overflow-y: auto;
  padding: var(--a2);
  border: 1px solid var(--rand);
  border-radius: var(--r-l);
  background: var(--flaeche);
  color: var(--text);
  box-shadow: var(--schatten);
}
.menue-dialog::backdrop { background: var(--schleier); }

.menue-gruppe { padding: var(--a2) 0; border-top: 1px solid var(--rand); }
.menue-gruppe:first-child { border-top: none; padding-top: 0.25rem; }
.menue-gruppen-kopf {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-still);
  padding: 0 0.6rem 0.3rem;
}
.menue-eintrag {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  font: inherit;
  font-size: 0.95rem;
  text-align: left;
  padding: 0.5rem 0.6rem;
  border: none;
  border-radius: var(--r-s);
  background: transparent;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
}
.menue-eintrag:hover { background: var(--flaeche-hoch); }
.menue-eintrag.aktiv { color: var(--akzent); font-weight: 600; }
.menue-nutzer { margin-left: auto; font-weight: 600; }
.menue-eintrag .extern { margin-left: auto; font-size: 0.75em; color: var(--text-still); }
.menue-zeile {
  display: flex;
  flex-wrap: wrap;   /* bei grosser Schrift und langen Woertern (fr "Système Clair Sombre") rutscht
                        der Umschalter in die naechste Zeile, statt ueber den Rand zu ragen */
  align-items: center;
  gap: 0.6rem;
  padding: 0.35rem 0.6rem;
  font-size: 0.95rem;
}
.menue-zeile > span:first-child { color: var(--text-leise); }
.menue-zeile > .segment, .menue-zeile > .scale-buttons { margin-left: auto; }

/* Segmentumschalter (System | Hell | Dunkel) */
.segment {
  display: inline-flex;
  padding: 2px;
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-s);
  background: var(--flaeche-hoch);
}
.segment button {
  font: inherit;
  font-size: 0.85rem;
  font-weight: 400;
  padding: 0.2rem 0.55rem;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-leise);
}
.segment button:hover { background: transparent; color: var(--text); }
.segment button[aria-pressed="true"] {
  background: var(--flaeche);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--schatten-klein);
}

/* Textgroessen-Einstellung: "A-" / Stufe / "A+". Ausgegraut an den Enden. */
.scale-buttons { display: inline-flex; align-items: center; gap: 0.3rem; }
.scale-button { padding: 0.2rem 0.5rem; min-width: 2.1rem; line-height: 1.1; font-size: 0.85rem; }
.scale-stufe { font-size: 0.85rem; color: var(--text-leise); font-variant-numeric: tabular-nums; min-width: 2.6rem; text-align: center; }

/* --- Unterleiste am Telefon: die drei Haupteinstiege plus Menue. Der Kopf schrumpft dann
   auf Wortmarke und Menueknopf. --- */
.unterleiste { display: none; }

@media (max-width: 46rem) {
  .kopf-nav { display: none; }

  .unterleiste {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 30;
    display: flex;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: var(--flaeche);
    background: color-mix(in srgb, var(--flaeche) 94%, transparent);
    backdrop-filter: blur(10px);
    border-top: 1px solid var(--rand);
  }
  .unterleiste-eintrag {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    padding: 0.4rem 0.2rem 0.5rem;
    font: inherit;
    font-size: 0.72rem;
    border: none;
    background: transparent;
    color: var(--text-leise);
    text-decoration: none;
    cursor: pointer;
  }
  .unterleiste-eintrag svg { width: 1.3rem; height: 1.3rem; }
  .unterleiste-eintrag.aktiv { color: var(--akzent); font-weight: 600; }
  .unterleiste-eintrag:hover { background: transparent; }

  /* Platz fuer die Leiste, damit sie nichts verdeckt. */
  body { padding-bottom: 3.8rem; }
  body.voll-hoehe { padding-bottom: 0; }
  body.voll-hoehe #app-main { padding-bottom: 3.8rem; }

  /* Der Seitentitel neben der Wortmarke wuerde den Kopf sprengen; wo man ist, sagt auf dem
     Telefon die Leiste unten. Der NAME der Anwendung bleibt dagegen stehen - bis 0.68 hiess
     sie "Triathlon-Coach" und musste hier weichen, "Claudia" passt auch bei der groessten
     Schriftstufe neben Version, Zeichen und Menueknopf. */
  .kopf-trenner, .kopf-seitentitel { display: none; }
  /* Auf sehr schmalen Geraeten und zugleich groesster Schriftstufe wird es eng. Ohne diese
     zwei Zeilen kann die Wortmarke (white-space: nowrap) nicht schrumpfen und schiebt den
     Menueknopf aus dem Bild - das Menue waere dann gar nicht mehr erreichbar. So gibt statt
     dessen der Schwanz der Wortmarke nach, also die drei Zeichen. */
  .kopf-marke { min-width: 0; overflow: hidden; }
  #header-user { display: none; }

  /* Das Menue wird zum Blatt am unteren Rand - dort ist der Daumen. */
  .menue-dialog {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    max-width: 100%;    /* sonst klammert die Browser-Vorgabe, siehe .page-dialog */
    max-height: 80dvh;
    padding-bottom: calc(var(--a4) + env(safe-area-inset-bottom, 0px));
    border-radius: var(--r-l) var(--r-l) 0 0;
  }
}

/* ===================================================================
   4. Dialoge (Saisonplan, Legende) und der Anmeldebildschirm
   =================================================================== */

.page-dialog {
  border: 1px solid var(--rand);
  border-radius: var(--r-l);
  padding: var(--a6);
  /* max-width MUSS hier stehen. Ein <dialog> traegt in der Browser-Vorgabe
     max-width: calc(100% - 6px - 2em); die gewinnt gegen jede eigene width-Angabe, weil sie
     eine andere Eigenschaft ist und nicht ueberschrieben, sondern nur geklammert wird. Ohne
     diese Zeile bleibt ein Dialog am Telefon rund 40px schmaler als gewollt. */
  max-width: min(46rem, 94%);
  width: 94%;
  max-height: 88dvh;
  overflow-y: auto;
  color: var(--text);
  background: var(--flaeche);
  box-shadow: var(--schatten);
}
/* Schmaler Dialog fuer ein kurzes Formular (Passwortwechsel). Zwei Klassen, damit die
   Spezifitaet die max-width von .page-dialog sicher schlaegt. */
.page-dialog.dialog-schmal { max-width: min(28rem, 94%); }
.dialog-knoepfe { justify-content: flex-end; margin-top: var(--a4); }
.page-dialog::backdrop { background: var(--schleier); }
/* Am Telefon wird der Dialog zum Blatt am unteren Rand wie das Menue (1.72): volle Breite und
   1rem statt 1,5rem Innenabstand - zusammen gut 40 px mehr Zeile. Die schmale Variante steht mit
   in der Liste, weil sie mit zwei Klassen die max-width sonst wieder setzte. */
@media (max-width: 46rem) {
  .page-dialog, .page-dialog.dialog-schmal {
    margin: auto 0 0;
    width: 100%;
    max-width: 100%;
    padding: var(--a4);
    padding-bottom: calc(var(--a4) + env(safe-area-inset-bottom, 0px));
    border-width: 1px 0 0;
    border-radius: var(--r-l) var(--r-l) 0 0;
  }
}
.page-dialog h2 { margin: 0 0 0.8rem 0; font-size: 1.15rem; }
.page-dialog h3 { margin: 1.2rem 0 0.4rem 0; font-size: 1rem; }
.dialog-actions { display: flex; justify-content: flex-end; margin-top: var(--a6); }

/* Kopfzeile eines Dialogs: Titel links, Schliesskreuz rechts. */
.dialog-kopf { display: flex; align-items: center; gap: var(--a3); margin-bottom: var(--a4); }
.dialog-kopf h2 { margin: 0; flex: 1; }
.dialog-zu {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-s);
  background: transparent;
  color: var(--text-leise);
}
.dialog-zu:hover { background: var(--flaeche-hoch); color: var(--text); }
.dialog-zu svg { width: 1rem; height: 1rem; }

/* --- Saisonplan: Zeitleiste der Phasen --- */
/* Die Segmentbreiten stehen als flex-grow im Markup (saison.js), berechnet aus der Dauer in
   Tagen. Marken fuer "heute" und die Wettkaempfe liegen absolut ueber der Leiste. */
.saison-leiste-rahmen { margin: var(--a4) 0 var(--a6); }
.saison-marken { position: relative; height: 2.1rem; }
/* Nullbreiter Anker auf dem Datum: Beide Zeilen sind darauf zentriert, die Spitze sitzt also
   genau ueber dem Tag, den sie meint. Eine einzeilige Marke ("▼ heute") liesse sich nicht
   ausrichten - zentriert man die Zeichenkette, wandert das Dreieck an ihrem Rand um deren halbe
   Breite weg; richtet man das Dreieck aus, steht der Text ueber der Leiste hinaus. */
.saison-marke {
  position: absolute;
  bottom: 0;
  width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.1;
  font-size: 0.75rem;
  white-space: nowrap;
  color: var(--text-leise);
}
.saison-marke-heute { color: var(--text); font-weight: 600; }
.saison-marke-rennen { color: var(--warn); font-weight: 600; }
.saison-marke-lager { color: var(--sport-rad); font-weight: 600; }

/* --- Trainingslager: Markierung der Tage im Wochenplan (seit 1.15) --- */
/* Darf umbrechen: "Trainingslager Mallorca · Anreise" passt am Telefon nicht neben das Datum. */
.lager-chip { font-weight: 600; white-space: normal; min-width: 0; }
.lager-chip.lager-camp { background: var(--lager-camp); color: var(--text); }
.lager-chip.lager-vorphase { background: var(--lager-vorphase); color: var(--text); }
.lager-chip.lager-erholung { background: var(--lager-erholung); color: var(--text); }

.saison-leiste {
  display: flex;
  height: 1.6rem;
  border-radius: var(--r-rund);
  overflow: hidden;
  background: var(--flaeche-hoch);
}
.saison-segment {
  min-width: 0;
  background: var(--sport-sonst);
  opacity: 0.42;
}
.saison-segment.laufend { opacity: 1; }
/* Der Farbstreifen vor dem Phasennamen in der Tabelle. Steht VOR den Farbregeln
   .saison-segment-<art>, damit deren Farbe diesen Vorgabewert ueberschreibt - beide sind
   einklassige Selektoren, es entscheidet also die Reihenfolge in der Datei. */
.saison-punkt {
  display: inline-block;
  width: 0.3rem;
  height: 0.85em;
  margin-right: 0.5rem;
  vertical-align: -0.1em;
  border-radius: var(--r-rund);
  background: var(--sport-sonst);
  opacity: 0.42;
}
.saison-punkt.laufend { opacity: 1; }
.saison-segment-vorbereitung { background: var(--phase-vorbereitung); }
.saison-segment-base { background: var(--sport-schwimmen); }
.saison-segment-build { background: var(--sport-rad); }
.saison-segment-peak { background: var(--sport-laufen); }
.saison-segment-race { background: var(--schlecht); }
.saison-segment-uebergang { background: var(--sport-kraft); }

/* Dieselbe Flex-Aufteilung wie die Leiste darueber: so bekommt jeder Name genau die Breite
   seines Segments und ueberlagert keinen Nachbarn (siehe saison.js). */
.saison-namen { display: flex; margin-top: 0.3rem; }
.saison-name {
  min-width: 0;
  padding: 0 2px;
  font-size: 0.75rem;
  color: var(--text-leise);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.saison-name.laufend { color: var(--text); font-weight: 600; }
.saison-spanne {
  display: flex;
  justify-content: space-between;
  margin-top: 0.15rem;
  font-size: 0.72rem;
  color: var(--text-still);
  font-variant-numeric: tabular-nums;
}

.saison-jetzt {
  padding: var(--a3) var(--a4);
  border-radius: var(--r-m);
  background: var(--akzent-leise);
  color: var(--text);
  font-size: 0.95rem;
}
.saison-jetzt strong { font-weight: 600; }

/* Tabelle der Phasen und die Wettkampfliste */
/* Breite Tabellen scrollen in sich, statt aus ihrem Kasten zu ragen - dieselbe Regel wie fuer
   Tabellen in Coach-Antworten (.md table). */
.tabellen-rahmen { overflow-x: auto; }
.saison-tabelle { border-collapse: collapse; width: 100%; font-size: 0.92rem; }
.saison-tabelle th {
  text-align: left;
  padding: 0.3rem 0.5rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-still);
  font-weight: 600;
  border-bottom: 1px solid var(--rand);
}
.saison-tabelle td { text-align: left; padding: 0.45rem 0.5rem; border-bottom: 1px solid var(--rand); }
/* Phasenname und Zeitraum sollen nicht umbrechen - "Base 1" auf zwei Zeilen liest sich wie
   zwei Phasen. Der Fokus in der dritten Spalte darf dafuer beliebig lang werden. */
.saison-tabelle td:first-child, .saison-tabelle td:nth-child(2) { white-space: nowrap; }
.saison-tabelle tr:last-child td { border-bottom: none; }
.saison-tabelle tr.current td { background: var(--akzent-leise); font-weight: 600; }
.saison-tabelle tr.current td:first-child { border-radius: var(--r-s) 0 0 var(--r-s); }
.saison-tabelle tr.current td:last-child { border-radius: 0 var(--r-s) var(--r-s) 0; }

.rennen-zeile {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.5rem 0.5rem;
  border-bottom: 1px solid var(--rand);
  font-size: 0.92rem;
}
.rennen-zeile:last-child { border-bottom: none; }
.rennen-zeile { flex-wrap: wrap; }
.rennen-datum { flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--text-leise); white-space: nowrap; }
/* Echte Wunschbreite statt flex-basis 0 - siehe die Begruendung bei .termin-titel. */
.rennen-name { flex: 1 1 12rem; min-width: 0; }
.rennen-zeile .badge { flex: 0 0 auto; }

/* Ausnahmen fuer schmale Schirme stehen bewusst HIER, direkt hinter den Regeln, die sie
   aufheben - und nicht beim Rest der Telefon-Anpassungen in Abschnitt 3. Ein @media-Block
   erhoeht die Spezifitaet nicht; bei gleicher Spezifitaet gewinnt die spaetere Regel. Ein
   "display: none" weiter oben in der Datei wuerde also von der Basisregel weiter unten wieder
   ueberschrieben. Genau das ist beim ersten Versuch passiert. */
@media (max-width: 46rem) {
  /* Acht Phasen auf 310 px ergaeben "P... Bas... Bas... Bas..." - das sagt nichts. Die Leiste
     zeigt weiter die Verhaeltnisse und wo "heute" liegt, welche Phase laeuft steht im Kasten
     darunter, und alle Namen stehen vollstaendig in der Tabelle. */
  .saison-namen { display: none; }
  /* Drei Spalten passen am Telefon nicht in den Dialog - der Fokustext allein braucht mehr
     Breite, als uebrig ist. Statt zu quetschen oder waagerecht scrollen zu lassen, wird jede
     Phase zu einem Block gestapelt: Name, darunter der Zeitraum leise, darunter der Fokus.
     Die Kopfzeile entfaellt dabei, weil die drei Angaben sich von selbst erklaeren. */
  .saison-tabelle, .saison-tabelle tbody, .saison-tabelle tr, .saison-tabelle td {
    display: block;
    width: 100%;
  }
  .saison-tabelle thead { display: none; }
  .saison-tabelle tbody tr {
    padding: 0.5rem 0.6rem;
    border-bottom: 1px solid var(--rand);
  }
  .saison-tabelle tbody tr:last-child { border-bottom: none; }
  .saison-tabelle td { border-bottom: none; padding: 0; white-space: normal; }
  .saison-tabelle td:first-child { font-weight: 600; }
  .saison-tabelle td:nth-child(2) { color: var(--text-leise); font-size: 0.85rem; }
  .saison-tabelle td:nth-child(3):not(:empty) { margin-top: 0.2rem; }
  /* Die laufende Phase bleibt hervorgehoben, jetzt als ganzer Block. */
  .saison-tabelle tr.current { border-radius: var(--r-s); }
  .saison-tabelle tr.current td { background: none; }
  .saison-tabelle tr.current { background: var(--akzent-leise); }
}

/* --- Legende --- */
dl.legend { margin: 0; }
dl.legend dt { font-weight: 700; margin-top: 0.9rem; }
dl.legend dt:first-child { margin-top: 0; }
dl.legend dd { margin: 0.15rem 0 0 0; color: var(--text-leise); font-size: 0.92rem; }

/* --- Anmeldung: Benutzername und Passwort (seit 0.61) --- */
#login-overlay {
  position: fixed;
  inset: 0;
  display: flex;
  /* Senkrecht, seit unter dem Formular der Zertifikatshinweis steht: In der Vorgabe (Reihe)
     saesse er neben der Maske statt darunter. Sichtbar ist immer nur eines der beiden
     Formulare, die Aufteilung bleibt fuer sie also dieselbe wie vorher. */
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--a6);
  padding: var(--a4);
  background: var(--grund);
  z-index: 40;
}
#login-form {
  background: var(--flaeche);
  padding: var(--a8);
  border: 1px solid var(--rand);
  border-radius: var(--r-l);
  box-shadow: var(--schatten);
  display: flex;
  flex-direction: column;
  gap: var(--a3);
  width: min(22rem, 100%);
}
/* Die Erstanmeldung teilt sich das Aussehen mit der Anmeldung - ein zweiter Regelsatz waere
   dieselbe Gestaltung ein zweites Mal und liefe beim naechsten Feinschliff auseinander. */
#code-form {
  background: var(--flaeche);
  padding: var(--a8);
  border: 1px solid var(--rand);
  border-radius: var(--r-l);
  box-shadow: var(--schatten);
  display: flex;
  flex-direction: column;
  gap: var(--a3);
  width: min(24rem, 100%);
}
#login-form h1, #code-form h1 { font-size: 1.4rem; margin-bottom: 0.2rem; }
#login-form label, #code-form label { font-size: 0.9rem; color: var(--text-leise); }
#login-form input, #code-form input { padding: 0.6rem 0.7rem; font-size: 1.05rem; }
#login-form button, #code-form button { padding: 0.6rem; font-size: 1rem; }
#code-form .hinweis { font-size: 0.9rem; color: var(--text-leise); margin: 0; }
/* Der Hinweis zum Wurzelzertifikat unter der Anmeldemaske. Er soll die Maske nicht ueberragen,
   deshalb dieselbe Breitengrenze wie das Formular. */
.zertifikat-hinweis {
  width: min(22rem, 100%);
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-leise);
  text-align: center;
}
/* Der Menuepfad ist Beschriftung, keine Betonung - aber er darf umbrechen: Am Stueck waere er
   auf einem schmalen Schirm breiter als die Seite und schoebe sie waagerecht weg. */
.zertifikat-hinweis em { font-style: normal; }

/* Ein Knopf, der wie ein Verweis aussieht: Erstanmeldung und Rueckweg sind Nebenwege, sie
   sollen den eigentlichen Knopf nicht um Aufmerksamkeit bringen. Kein <a>, weil hier nichts
   navigiert wird - ein Verweis ohne Ziel waere fuer die Tastaturbedienung irrefuehrend. */
.link-knopf {
  background: none;
  border: none;
  padding: 0;
  color: var(--akzent);
  font-size: 0.9rem;
  text-align: center;
  text-decoration: underline;
  cursor: pointer;
}
.link-knopf:hover { color: var(--akzent-hell); }

/* ===================================================================
   5. Uebersicht: das Gespraech mit dem Coach, daneben der Wochenplan in Kurzform
   Seit 0.98 steht am breiten Schirm eine schmale Plan-Spalte neben dem Chat - vorher stand
   das Gespraech allein und wirkte an den linken Rand geheftet. Die Spalte bleibt bewusst
   kompakt (eine Zeile je Tag): Das Gespraech behaelt den meisten Platz, die ausfuehrlichen
   Tageskarten stehen weiter auf der Wochenplan-Seite. Am Telefon gibt es sie nicht.
   =================================================================== */

/* Die Uebersicht faellt auf Bildschirmhoehe: der Verlauf scrollt, die Eingabe steht fest
   unten. Dafuer muss die Hoehe vom body bis zum Chatprotokoll durchgereicht werden -
   jedes Zwischenglied braucht min-height: 0, sonst waechst es ueber den Schirm hinaus. */
body.voll-hoehe { display: flex; flex-direction: column; height: 100dvh; }
body.voll-hoehe #app-main { flex: 1; min-height: 0; }

/* Gespraech und Plan-Spalte stehen als GRUPPE mittig: Die Hauptflaeche ist genau so breit wie
   beide zusammen, und der Weissraum verteilt sich auf beide Raender. Unterhalb der Grenze weiter
   unten (keine Plan-Spalte) faellt die Begrenzung weg. */
#app-main {
  display: flex;
  flex-direction: column;
  gap: var(--a4);
  padding: var(--a4);
  width: 100%;
  max-width: calc(60ch + var(--planspalte-breite) + var(--a6) + 2 * var(--a4));
  margin-inline: auto;
}
#uebersicht-reihe {
  flex: 1;
  min-height: 0;
  display: flex;
  gap: var(--a6);
}
/* Das ganze Gespraech steht in EINER Spalte: Verlauf, Wartezeichen und Eingabe untereinander
   in derselben Breite. Die Lesebreite nur am Text zu begrenzen reicht nicht - die eigene
   Nachricht ist rechtsbuendig und rutschte sonst an den Bildschirmrand, sodass Frage und
   Antwort am breiten Schirm durch eine Luecke getrennt stuenden. */
#uebersicht-reihe > #chat-section {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
}
#chat-section > * { width: 100%; max-width: var(--gespraechsbreite); }
/* Der Startknopf behaelt seine natuerliche Breite, sonst liefe er ueber die ganze Spalte. */
#chat-section > #btn-start-chat { width: auto; }

/* Kompakter Wochenplan neben dem Gespraech. Er waechst nur so hoch wie sein Inhalt und scrollt
   in sich, wenn der Schirm niedriger ist - die Eingabe des Chats bleibt davon unberuehrt. */
#plan-spalte {
  flex: 0 0 var(--planspalte-breite);
  align-self: flex-start;
  max-height: 100%;
  overflow-y: auto;
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  background: var(--flaeche);
  padding: var(--a3) var(--a4);
}
#plan-spalte h2 { font-size: 0.95rem; margin: 0 0 var(--a2); }
.plan-spalte-tage { list-style: none; margin: 0; padding: 0; }
.plan-tag {
  display: flex;
  gap: var(--a2);
  padding: 0.35rem 0.4rem;
  border-top: 1px solid var(--rand);
  font-size: 0.88rem;
  line-height: 1.35;
}
.plan-tag:first-child { border-top: none; }
.plan-tag.heute { background: var(--akzent-leise); border-radius: var(--r-s); border-top-color: transparent; }
.plan-tag.heute + .plan-tag { border-top-color: transparent; }
.plan-tag-datum { flex: 0 0 5.3rem; color: var(--text-leise); font-variant-numeric: tabular-nums; }
.plan-tag.heute .plan-tag-datum { color: var(--akzent); font-weight: 600; }
.plan-tag-einheiten { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }
.plan-tag-frei { color: var(--text-still); font-style: italic; }
.plan-kurz {
  --sportfarbe: var(--sport-sonst);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 0.35rem;
  border-left: 3px solid var(--sportfarbe);
  padding-left: 0.4rem;
  overflow-wrap: anywhere;
}
.plan-kurz-sport { font-weight: 600; }
.plan-kurz-meta { color: var(--text-leise); }
.plan-kurz-erledigt { color: var(--gut); font-weight: 600; }
.plan-spalte-link { display: inline-block; margin-top: var(--a3); font-size: 0.88rem; }

/* Unterhalb dieser Breite reicht der Platz nicht fuer beide Spalten: Das Gespraech bekommt
   wieder die ganze Flaeche. Steht bewusst HINTER den Grundregeln oben - ein @media-Block erhoeht
   die Spezifitaet nicht, weiter vorn wuerde er von ihnen ueberschrieben. */
@media (max-width: 64rem) {
  #app-main { max-width: none; }
  #plan-spalte { display: none; }
}

/* Technische Details zu Sync- und Modellproblemen. Bleibt verborgen, solange nichts anliegt. */
#status-section {
  flex: none;
  border-color: var(--warn);
  background: var(--warn-leise);
  padding: var(--a3) var(--a4);
}
#status-section h2 { font-size: 0.92rem; color: var(--warn); }
#status-section .section-head h2 { margin-bottom: var(--a2); }
#status-section .nav-item { font-size: 0.85rem; padding: 0.2rem 0.6rem; }
/* Eigener Scrollbereich (1.63): Bis dahin wuchs der Bereich mit jeder Meldung und schob den Chat
   auf der Bildschirmhoehe der Uebersicht zusammen. vh wie bei "Bisheriger Plan" darunter. */
#status-list { max-height: 25vh; overflow-y: auto; }
/* overflow-wrap (2c): Die Gruende nennen Werkzeug- und Feldnamen ohne Leerstelle
   ("update_planned_workouts", "duration_min"), und im Franzoesischen haengen « … » ueber
   geschuetzte Leerzeichen am Wort - bei A+ am Telefon ragte das sonst aus der Zeile. */
.status-row { font-size: 0.88rem; color: var(--text-leise); padding: 0.2rem 0; overflow-wrap: anywhere; }
.status-row .status-time { color: var(--text-still); font-variant-numeric: tabular-nums; margin-right: 0.5rem; }

#last-plan-details {
  flex: none;
  margin-bottom: var(--a3);
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  padding: 0.5rem 0.75rem;
  background: var(--flaeche-still);
}
#last-plan-details > summary { font-size: 0.9rem; color: var(--text-leise); }
#last-plan-details > div {
  margin-top: var(--a2);
  max-height: 40vh;
  overflow-y: auto;
  font-size: 0.9rem;
}

/* Eigener Scrollbereich: es sind immer nur die letzten Zeilen sichtbar, nach oben laesst sich
   scrollen. Das Mitlaufen ans Ende uebernimmt app.js - bis man selbst hochscrollt; dann fuehrt
   der Pfeil darunter (#btn-nach-unten) zurueck. */
#chat-log {
  flex: 1;
  min-height: 6rem;
  display: flex;
  flex-direction: column;
  gap: var(--a4);
  margin-bottom: var(--a3);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  padding-right: 0.2rem;
}

/* Der Pfeil zurueck ans Ende schwebt mittig ueber dem unteren Rand des Verlaufs, wie in der
   Claude-App. Der Halter ist null hoch und nimmt keinen Platz weg; der Abstand nach unten
   ueberbrueckt den Aussenabstand des Verlaufs. Die Groessen in rem/em, damit er mit der
   A-/A+-Einstellung mitwaechst. */
.nach-unten-halter { flex: none; position: relative; height: 0; }
#btn-nach-unten {
  position: absolute;
  left: 50%;
  bottom: calc(var(--a3) + 0.5rem);
  transform: translateX(-50%);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.3rem;
  height: 2.3rem;
  padding: 0;
  border: 1px solid var(--rand-stark);
  border-radius: 50%;
  background: var(--flaeche);
  color: var(--text);
  box-shadow: var(--schatten);
}
#btn-nach-unten:hover { background: var(--flaeche-hoch); }
#btn-nach-unten svg {
  width: 1.15em;
  height: 1.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Eine Nachricht: kleine Rollenzeile, darunter der Text. Die Coach-Antwort laeuft ohne
   Blasenhintergrund ueber die Breite - Markdown mit Ueberschriften und Tabellen liest sich
   in einer Sprechblase schlecht. Die eigene Nachricht bleibt eine rechtsbuendige Blase. */
.nachricht { display: flex; flex-direction: column; gap: 0.25rem; }
.nachricht.von-mir { align-items: flex-end; }
.rolle-zeile {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-still);
}
/* Fussnote unter einer Coach-Antwort: welches Sprachmodell sie geschrieben hat. Zurueckhaltend
   gesetzt - die Angabe ist zum Nachschlagen da (welches Profil hat diesen Plan geschrieben?),
   nicht zum Mitlesen. Sie steht unter der Antwort, weil erst dort feststeht, wer sie verfasst
   hat: Bricht der Strom eines Modells ab, uebernimmt das naechste der Kette. */
.modell-zeile {
  font-size: 0.7rem;
  color: var(--text-still);
  font-style: italic;
  margin-top: 0.1rem;
}
.rolle-punkt { width: 0.5rem; height: 0.5rem; border-radius: 50%; background: var(--akzent); }
.nachricht.von-mir .rolle-punkt { background: var(--text-still); }

/* Lesebreite (gesetzt wird sie eine Ebene hoeher, an #chat-section > * ueber die Marke
   --gespraechsbreite; hier steht die Begruendung dazu).
   Ueber die volle Fensterbreite waeren es am Rechner rund 130 Zeichen je Zeile;
   angenehm liest sich etwa die Haelfte, weil das Auge beim Zeilenwechsel sonst den Anschluss
   verliert. Begrenzt wird ausdruecklich nur die TEXTZEILE; am breiten Schirm steht daneben die
   kompakte Plan-Spalte (Abschnitt 5), der ausfuehrliche Wochenplan hat seine eigene Seite.
   Die Einheit ch haengt an der Schriftgroesse: die Zeichenzahl bleibt damit ueber alle fuenf
   Stufen der A-/A+-Einstellung gleich, statt bei grosser Schrift zu einer schmalen Saeule zu
   werden. Das min(..., 100%) ist tragend - ohne das ragten 60ch am Telefon aus der Seite.
   Der Wert ist nachgemessen, nicht geschaetzt: 60ch ergeben in dieser Schrift rund 69 Zeichen
   je Zeile (Mittel ueber 5.900 Zeichen echter Coach-Antworten, 86 Zeilen). Nicht 60: ch ist die
   Breite der Null und damit breiter als ein mittlerer Buchstabe, und Umbrueche fallen auf
   Wortgrenzen. Der typografische Richtwert liegt bei 66. */
.bubble {
  max-width: 100%;
  white-space: pre-wrap;
  font-size: 0.97rem;
  line-height: 1.6;
}
.bubble.user {
  padding: 0.55rem 0.85rem;
  border-radius: var(--r-m) var(--r-m) 4px var(--r-m);
  background: var(--akzent-leise);
}
.bubble.coach { color: var(--text); }
.bubble.streaming::after { content: "▍"; animation: blink 1s step-end infinite; color: var(--akzent); }
@keyframes blink { 50% { opacity: 0; } }

.notice-line { color: var(--text-still); font-size: 0.85rem; font-style: italic; }
.timing-line { color: var(--text-still); font-size: 0.78rem; margin: -0.15rem 0 0.35rem; }
.error-line {
  color: var(--schlecht);
  font-size: 0.9rem;
  background: var(--schlecht-leise);
  border-radius: var(--r-s);
  padding: 0.4rem 0.6rem;
  overflow-wrap: anywhere;   /* wie .status-row: Meldungen des Dienstes mit langen Wortketten */
}
#chat-status { color: var(--text-leise); font-style: italic; }

/* Warteanimation: ein Triathlet zieht von links nach rechts seine Bahn; die Disziplin
   (Schwimmen/Rad/Laufen) wechselt je Durchlauf - app.js hoert dafuer auf animationiteration.
   Gespiegelt werden nur Rad und Laufen (.emblem-gespiegelt, von app.js gesetzt): Die blicken auf
   den meisten Plattformen nach links, der Schwimmer schwimmt schon nach rechts. */
#thinking {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  color: var(--text-leise);
  font-style: italic;
  font-size: 0.9rem;
  margin: 0.3rem 0;
}
.thinking-track {
  position: relative;
  flex: 0 0 65px;
  height: 1.5em;
  overflow: hidden;
  border-bottom: 2px dashed var(--rand-stark);
}
/* Halbe Bahn bei unveraenderter Rundendauer heisst halbes Tempo - beides war so gewuenscht.
   Wer eines von beiden aendert, aendert damit auch das andere: die Dauer bestimmt zugleich,
   wie oft die Disziplin wechselt (app.js hoert auf animationiteration). */
#thinking-athlete { position: absolute; bottom: 0; left: 0; display: inline-block; animation: thinking-race 2.6s linear infinite; }
@keyframes thinking-race {
  from { transform: translateX(-1.4em); }
  to   { transform: translateX(70px); }
}

/* Das Wartewort wechselt alle zwei Sekunden (app.js). Die Einblendung wird bei jedem Wechsel
   neu angestossen, indem die Klasse ab- und wieder angelegt wird - ohne sie liefe die
   Animation nur ein einziges Mal. */
/* Das Wort steht links, die Bahn rechts davon. Die Mindestbreite ist das, was die Bahn
   ruhig haelt: ohne sie ruckte der Triathlet bei jedem Wortwechsel um die Laengendifferenz
   der beiden Woerter hin und her. Nur die laengsten Eintraege schieben ihn noch.
   overflow-wrap: "Laktat-Schwellen-Shifting" hat keine Leerstelle zum Umbrechen und wuerde
   bei grosser Schrift sonst aus der Zeile ragen. */
#thinking-wort {
  display: inline-block;
  flex: 0 1 auto;
  min-width: min(13rem, 55%);   /* nie mehr als die halbe Zeile - sonst schoebe die
                                   Mindestbreite bei grosser Schrift die Bahn aus dem Bild */
  overflow-wrap: anywhere;
}
#thinking-wort.wort-wechsel { animation: wartewort-ein 0.4s ease-out; }
@keyframes wartewort-ein {
  from { opacity: 0; transform: translateY(0.25em); }
  to   { opacity: 1; transform: none; }
}

/* Eingabe: bleibt unten stehen, waechst mit dem Text mit. Leeres Senden ist tragend
   (Onboarding-Frage ueberspringen bzw. "nichts Besonderes"), der Senden-Knopf darf deshalb
   bei leerem Feld NICHT gesperrt werden. */
#chat-form { flex: none; }
/* Feld und Knopf duerfen umbrechen (1.70, Meldung #17). Das Textfeld hatte "flex: 1", also
   flex-basis 0: Die Zeile galt dem Browser immer als passend, und bei grosser Schrift am Telefon
   nahm der Knopf den Platz, waehrend das Feld auf 80 px schrumpfte und schon der Platzhalter
   mitten im Wort umbrach. Mit einer echten Wunschbreite entscheidet der Browser zuerst ueber den
   Umbruch: Unter 8rem fuer das Feld rutscht der Knopf in die naechste Zeile, rechtsbuendig. In
   rem, damit die Grenze mit der A-/A+-Einstellung mitwaechst - bei normaler Schrift bleibt alles
   in einer Zeile. */
.eingabe-feld {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--a2);
  padding: var(--a2);
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-l);
  background: var(--flaeche);
}
.eingabe-feld:focus-within { border-color: var(--akzent); box-shadow: 0 0 0 3px var(--akzent-leise); }
#chat-form textarea {
  flex: 1 1 8rem;
  min-width: 0;
  font: inherit;
  font-size: 0.97rem;
  line-height: 1.5;
  padding: 0.35rem 0.4rem;
  border: none;
  background: transparent;
  resize: none;
  max-height: 30vh;
  overflow-y: auto;
}
#chat-form textarea:focus { box-shadow: none; }
.chat-buttons { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
/* Die Rueckmeldung neben den Knoepfen ist ein ganzer Satz und braucht eine echte
   Wunschbreite - ohne sie gilt sie dem Browser als beliebig schmal, bricht nie um und
   steht am Telefon wortweise untereinander (dieselbe Falle wie beim Termin-Titel). */
.chat-buttons .phase-note { flex: 1 1 14rem; margin: 0; }
#btn-send { border-radius: var(--r-m); }
#chat-form .chat-buttons { margin-left: auto; }

/* Zeichenzaehler: unauffaellig, solange noch Luft ist, deutlich ab der Grenze. */
.chat-counter { font-size: 0.8rem; color: var(--text-still); font-variant-numeric: tabular-nums; }
.chat-counter.over { color: var(--schlecht); font-weight: 600; }
.eingabe-fuss {
  display: flex;
  align-items: center;
  gap: var(--a3);
  margin-top: 0.35rem;
  padding: 0 0.3rem;
  font-size: 0.8rem;
  color: var(--text-still);
}
.eingabe-fuss .chat-counter { margin-left: auto; }

/* --- Markdown-Darstellung (Coach-Antworten, "Bisheriger Plan") --- */
.bubble.md, .md { white-space: normal; }
.md p { margin: 0.5em 0; }
.md > :first-child { margin-top: 0; }
.md > :last-child { margin-bottom: 0; }
.md h3, .md h4, .md h5, .md h6 { margin: 1em 0 0.35em; font-size: 1em; font-weight: 600; }
.md h3 { font-size: 1.08em; }
.md ul, .md ol { margin: 0.5em 0; padding-left: 1.35em; }
.md li { margin: 0.2em 0; }
.md table { border-collapse: collapse; margin: 0.7em 0; display: block; max-width: 100%; overflow-x: auto; }
.md th, .md td { border: 1px solid var(--rand); padding: 0.3em 0.6em; text-align: left; font-size: 0.92em; }
.md th { background: var(--flaeche-hoch); font-weight: 600; }
.md code {
  background: var(--flaeche-hoch);
  border-radius: 4px;
  padding: 0.08em 0.35em;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.9em;
}
.md pre { background: var(--flaeche-hoch); border-radius: var(--r-s); padding: 0.6em 0.8em; overflow-x: auto; }
.md pre code { background: none; padding: 0; }
.md hr { border: none; border-top: 1px solid var(--rand); margin: 1em 0; }
.md blockquote {
  margin: 0.6em 0;
  padding: 0.2em 0.9em;
  border-left: 3px solid var(--rand-stark);
  color: var(--text-leise);
}

/* ===================================================================
   6. Wochenplan
   =================================================================== */

/* Der Planungsmodus steht VOR dem Plan: ob der Coach diese Woche ueberhaupt frei planen
   durfte, entscheidet, wie die Karten darunter zu lesen sind. Drei Zustaende mit eigener
   Farbe, damit der Unterschied auch ohne Lesen auffaellt - normal ist ruhig, ein
   Wiedereinstieg ist ein Hinweis, eine ausgesetzte Planung ist eine Warnung. */
.planungsmodus {
  border: 1px solid var(--rand);
  border-left: 4px solid var(--text-still);
  background: var(--flaeche);
  color: var(--text-leise);
  padding: var(--a3) var(--a4);
  border-radius: var(--r-m);
  font-size: 0.92rem;
}
.planungsmodus-rampe { border-left-color: var(--warn); background: var(--warn-leise); color: var(--warn); }
.planungsmodus-ausgesetzt { border-left-color: var(--schlecht); background: var(--schlecht-leise); color: var(--schlecht); }
.planungsmodus-head { font-weight: 600; margin-bottom: 0.25rem; color: inherit; }
.planungsmodus .phase-note { color: inherit; opacity: 0.85; }
.planungsmodus-zeilen { display: flex; flex-wrap: wrap; gap: 0.15rem 1.4rem; }
/* Kein white-space: nowrap. Ein Flex-Kind darf von sich aus nicht unter seine Inhaltsbreite
   schrumpfen; min-width: 0 hebt das auf, sonst ragt die laengste Zeile auf dem Telefon
   rechts aus der Seite heraus. */
.planungsmodus-zeilen div { min-width: 0; overflow-wrap: break-word; }

/* --- Tageskarten --- */
.day-card {
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  background: var(--flaeche);
  padding: var(--a3) var(--a4);
  margin-bottom: var(--a3);
}
.day-card.today { border-color: var(--akzent); background: var(--akzent-leise); }
.day-card .day-head {
  display: flex;
  /* Umbrechen statt ueberlaufen (2b): Bei A+ und laengeren Uebersetzungen ("Allègement avant le
     stage") passt der Camp-Chip nicht mehr neben das Datum. */
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.5rem;
  font-weight: 600;
  font-size: 0.95rem;
  margin-bottom: 0.4rem;
}
.day-card.today .day-head { color: var(--akzent); }
.day-card .rest { color: var(--text-still); font-style: italic; font-size: 0.9rem; }

/* Die Wetterzeile unter dem Datum (2.1): leise, damit der Plan vorn bleibt; Gewitter, Glaette und
   stuermische Boeen in der Warnfarbe. Umbrechen statt ueberlaufen - auf dem Telefon stehen Lage und
   Pendelfenster untereinander. */
.day-wetter {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.15rem 0.6rem;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-leise);
  margin: -0.15rem 0 0.45rem;
}
.day-wetter > span { min-width: 0; }
/* Das Symbol der Wetterlage und der Pendelfenster: etwas groesser als die Zahlen, in em, damit es mit
   der A-/A+-Einstellung mitwaechst. In einem Chip sorgt dessen gap fuer den Abstand. */
.wetter-symbol { font-size: 1.1em; line-height: 1; margin-right: 0.3em; }
.chip .wetter-symbol { font-size: 1em; margin-right: 0; }
.wetter-fenster-teil { white-space: nowrap; }
/* Der Windpfeil (2.2), seit 2.3 gezeichnet: per transform gedreht - das geht nur an einem Block- oder
   Inline-Block-Element -, in der Textfarbe und um so kraeftiger, je staerker die Boeen (wind-stufe-0
   bis -3, aus dem Coach-Dienst). Die Strichstaerke rechnet in Einheiten der 16er-viewBox, die Groesse
   in em, damit beides mit der A-/A+-Einstellung mitwaechst. Farbe: die volle Textfarbe statt der
   gedaempften der Wetterzeile (fast schwarz hell, fast weiss dunkel, Wunsch des Athleten) - nur bei
   stuermischen Boeen die Warnfarbe wie der Hinweis daneben. */
.wetter-symbol.wind-pfeil {
  color: var(--text);
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.12em;
  margin: 0 0.25em 0 0;
  fill: none;
  stroke: currentColor;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 1.6;
}
.wetter-symbol.wind-pfeil.wind-stufe-1 { stroke-width: 2.3; width: 1.1em; height: 1.1em; }
.wetter-symbol.wind-pfeil.wind-stufe-2 { stroke-width: 3; width: 1.2em; height: 1.2em; }
.wetter-symbol.wind-pfeil.wind-stufe-3 { stroke-width: 3.4; width: 1.3em; height: 1.3em; color: var(--warn); }
/* Die Regentropfen (2.3): einer, zwei oder drei nach der Regenmenge, in Blau (--regen, Wunsch des
   Athleten: Wasser ist blau) - auch die drei bei viel Regen, die Anzahl sagt die Menge. Die Breite
   folgt der viewBox (10 Einheiten je Tropfen plus Rand) bei 0,95em Hoehe. */
.wetter-symbol.regen-tropfen {
  color: var(--regen);
  fill: currentColor;
  display: inline-block;
  height: 0.95em;
  vertical-align: -0.1em;
  margin: 0 0 0 0.3em;
}
.wetter-symbol.regen-tropfen.regen-stufe-1 { width: 0.71em; }
.wetter-symbol.regen-tropfen.regen-stufe-2 { width: 1.31em; }
.wetter-symbol.regen-tropfen.regen-stufe-3 { width: 1.9em; }
.day-wetter.warnung .wetter-lage { color: var(--warn); font-weight: 600; }
.day-wetter .chip.wetter-warnung { background: var(--warn-leise); color: var(--warn); font-weight: 600; }

/* Eine geplante Einheit: farbiger Streifen mit Sport-Emoji links, Inhalt rechts.
   Die Farbe kommt ueber data-sport aus sportFamilie() in planbausteine.js - eine unbekannte
   Sportart faellt auf "sonst" zurueck und verschwindet nie. Dieselben Farben traegt die
   kompakte Plan-Spalte der Uebersicht (.plan-kurz). */
.workout {
  display: flex;
  gap: var(--a3);
  padding: var(--a2) 0;
  border-top: 1px solid var(--rand);
  --sportfarbe: var(--sport-sonst);
}
.day-card .workout:first-of-type { border-top: none; padding-top: 0.15rem; }
.workout[data-sport="schwimmen"], .plan-kurz[data-sport="schwimmen"] { --sportfarbe: var(--sport-schwimmen); }
.workout[data-sport="rad"],       .plan-kurz[data-sport="rad"]       { --sportfarbe: var(--sport-rad); }
.workout[data-sport="laufen"],    .plan-kurz[data-sport="laufen"]    { --sportfarbe: var(--sport-laufen); }
.workout[data-sport="kraft"],     .plan-kurz[data-sport="kraft"]     { --sportfarbe: var(--sport-kraft); }

.workout-rail {
  flex: 0 0 auto;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  width: 2rem;
  padding-top: 0.1rem;
  border-left: 3px solid var(--sportfarbe);
  border-radius: 2px;
  font-size: 1.1rem;
  line-height: 1.2;
}
.workout-inhalt { flex: 1; min-width: 0; }
/* Die Chips im Kopf einer Einheit ("Charge cible 75") duerfen in sich umbrechen - sonst ragen sie
   bei A+ auf dem Telefon aus der Karte (2b). */
.workout-kopf .chip { white-space: normal; }
.workout-titel { min-width: 0; overflow-wrap: break-word; }
.workout-kopf {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.workout-titel { font-weight: 600; }
.workout-titel .sportname { color: var(--sportfarbe); }
.workout-chips { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-left: auto; }

.workout .descr {
  white-space: pre-wrap;         /* description traegt mehrzeilige Intervall-/Zonenangaben */
  font-family: inherit;
  background: var(--flaeche-hoch);
  border-radius: var(--r-s);
  padding: 0.5rem 0.65rem;
  margin: 0.4rem 0 0;
  font-size: 0.9rem;
  line-height: 1.5;
  overflow-x: auto;
}

/* Liegt die Einheit startbereit auf der Garmin-Uhr? */
.watch-hint { font-size: 0.85rem; color: var(--gut); margin-top: 0.35rem; }
.watch-hint.error { color: var(--schlecht); }
.watch-warning {
  border: 1px solid var(--schlecht);
  border-left-width: 4px;
  background: var(--schlecht-leise);
  color: var(--schlecht);
  padding: var(--a3) var(--a4);
  margin-bottom: var(--a3);
  border-radius: var(--r-m);
  font-size: 0.92rem;
}
.watch-warning-head { font-weight: 600; margin-bottom: 0.2rem; }

/* --- Soll/Ist-Rueckblick: je Zeile ein Statuspunkt --- */
.past-row {
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  padding: 0.45rem 0;
  border-bottom: 1px solid var(--rand);
  font-size: 0.92rem;
}
.past-row:last-child { border-bottom: none; }
.past-punkt { flex: 0 0 auto; width: 0.55rem; height: 0.55rem; border-radius: 50%; background: var(--text-still); }
.past-punkt.absolviert { background: var(--gut); }
.past-punkt.ausgefallen { background: var(--schlecht); }
/* "nicht_aufgezeichnet" bleibt beim stillen Grau der Grundregel - siehe .badge oben. */
.past-row { flex-wrap: wrap; }
/* Echte Wunschbreite statt flex-basis 0 - siehe die Begruendung bei .termin-titel. */
.past-text { flex: 1 1 14rem; min-width: 0; overflow-wrap: break-word; }
.past-punkt.bestaetigt { background: transparent; box-shadow: inset 0 0 0 2px var(--gut); }
.past-row .badge { flex: 0 0 auto; }

/* Der Melde-Knopf bleibt bewusst zurueckhaltend: Er steht an jeder unbelegten Einheit und soll die
   Rueckblick-Liste nicht in eine Knopfleiste verwandeln. */
.melde-button {
  flex: 0 0 auto;
  font: inherit;
  font-size: 0.78rem;
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-rund);
  padding: 0.1rem 0.6rem;
  background: var(--flaeche);
  color: var(--text-leise);
  cursor: pointer;
}
.melde-button:hover:not(:disabled) { border-color: var(--akzent); color: var(--akzent); }
.melde-button:disabled { opacity: 0.5; cursor: default; }
.melde-button.gemeldet { color: var(--text-still); }
/* RPE einer selbst gemeldeten Einheit (1.28): klein wie der Meldeknopf daneben, damit die
   Rueckblickzeile nicht umbricht, nur weil eine Anstrengung eingetragen ist. */
.rpe-wahl {
  flex: 0 0 auto;
  width: auto;
  font-size: 0.78rem;
  padding: 0.1rem 0.4rem;
  border-radius: var(--r-rund);
}

/* --- Termine ---
   Eine Zeile je Termin: Uhrzeit als Marke, Titel, Merkmale, rechts das Loeschen. Die Notiz
   bekommt ueber flex-basis: 100% eine eigene Zeile darunter. */
.termin-zeile {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.5rem 0;
  border-top: 1px solid var(--rand);
}
.day-card .termin-zeile:first-of-type { border-top: none; padding-top: 0.15rem; }
.termin-zeit { flex: 0 0 auto; font-weight: 600; color: var(--text); }
/* flex-basis MUSS eine echte Breite sein, nicht 0. Mit "flex: 1" (Kurzform fuer
   flex-basis: 0) haelt der Browser die Zeile immer fuer passend und bricht nie um; die
   Marken und der Loeschen-Knopf behalten ihre Breite, und der Titel wird auf wenige Pixel
   gequetscht, bis er buchstabenweise untereinander steht ("Pendeln mit dem Rad" senkrecht).
   Mit einer echten Wunschbreite entscheidet der Browser zuerst ueber den Umbruch und erst
   danach ueber das Schrumpfen - genau in dieser Reihenfolge. */
.termin-titel { flex: 1 1 12rem; min-width: 0; font-weight: 500; overflow-wrap: break-word; }
.termin-zeile .badge { flex: 0 0 auto; margin-left: 0; }
.termin-zeile > button { flex: 0 0 auto; margin-left: auto; }
.termin-notiz { flex-basis: 100%; font-size: 0.88rem; color: var(--text-leise); }

.copy-text {
  width: 100%;
  font: inherit;
  font-size: 0.9rem;
  white-space: pre;
  overflow-wrap: normal;
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-s);
  padding: 0.5rem;
  background: var(--flaeche-hoch);
}

/* ===================================================================
   7. Unterseiten
   =================================================================== */

#plan-main, #prompt-main, #profil-main, #konto-main, #about-main, #termine-main,
#ereignisse-main, #form-main, #wettkaempfe-main, #ausruestung-main, #schwellen-main, #meldungen-main,
#frei-main, #erholung-main {
  max-width: 58rem;
  margin: 0 auto;
  padding: var(--a4);
  display: flex;
  flex-direction: column;
  gap: var(--a4);
}

/* Anleitungsseite: Lesebreite begrenzen, damit lange Absaetze nicht ueber den ganzen
   Bildschirm laufen; breite Tabellen und Codebloecke duerfen dafuer in sich scrollen. */
#doku-main { max-width: 52rem; margin: 0 auto; padding: var(--a4); }
.doku-toc {
  border: 1px solid var(--rand);
  background: var(--flaeche);
  padding: var(--a3) var(--a4);
  margin-bottom: var(--a6);
  border-radius: var(--r-m);
}
.doku-toc-head { font-weight: 600; margin-bottom: 0.3rem; }
.doku-toc ul { list-style: none; margin: 0; padding: 0; }
.doku-toc li { padding: 0.1rem 0; font-size: 0.92rem; }
.doku-toc li.toc-sub { padding-left: 1.2rem; font-size: 0.88rem; }
/* Ohne Unterstrich: das Verzeichnis hat 22 Eintraege, durchgehend unterstrichen waere es eine
   Wand aus Linien. Beim Zeigen kommt er zurueck. */
.doku-toc a { text-decoration: none; }
.doku-toc a:hover { text-decoration: underline; }
.doku-body { line-height: 1.6; }
/* Die Anleitung bringt ihren eigenen Dokumenttitel mit (das "#" der Datei). Er darf groesser
   sein als die Ueberschrift der Kopfzeile - er gehoert zum Text, nicht zur Anwendung. */
.doku-body h1 { font-size: 1.5rem; margin: 0 0 1rem; }
.doku-body h2 { margin-top: 2rem; border-bottom: 1px solid var(--rand); padding-bottom: 0.3rem; font-size: 1.15rem; }
/* Groesser als der Fliesstext - klingt selbstverstaendlich, war es aber nicht: Die globale Regel
   h3 { font-size: 0.95rem } gilt den kompakten Bedienflaechen der App, und ohne eigene Angabe erbte
   die Anleitung sie. Eine Unterueberschrift stand damit in 17,1 px ueber einem Absatz in 18 px und
   las sich wie gewoehnlicher, nur fetter Text. Aufgefallen beim Nachmessen im Browser; im
   Quelltext sieht die Regel voellig unauffaellig aus. */
.doku-body h3 { margin-top: 1.4rem; font-size: 1.05rem; }
.doku-body h4 { margin-top: 1.2rem; font-size: 1rem; }
/* Sprungziele des Inhaltsverzeichnisses tiefer anhalten lassen, als der Browser es taete.
   Die Kopfzeile klebt oben (position: sticky), der Browser scrollt ein Sprungziel aber genau an
   den oberen Rand - die angesprungene Ueberschrift und die erste Textzeile standen damit HINTER
   der Kopfzeile, bei allen 33 Eintraegen der Anleitung. Im Quelltext ist das nicht zu sehen und
   im jsdom-Test auch nicht (der rechnet kein Layout); aufgefallen ist es auf einem Bild.
   In rem statt px, weil die Kopfzeile mit der A-/A+-Einstellung mitwaechst und ein fester
   Pixelwert bei grosser Schrift wieder zu knapp waere. */
.doku-body h2, .doku-body h3 { scroll-margin-top: 5rem; }
.doku-body table { display: block; overflow-x: auto; max-width: 100%; }
.doku-body pre { overflow-x: auto; background: var(--flaeche-hoch); padding: 0.6rem 0.8rem; border-radius: var(--r-s); }
.doku-body blockquote { border-left: 3px solid var(--rand-stark); margin: 0.6rem 0; padding: 0.2rem 0.9rem; color: var(--text-leise); }

/* --- Profil: Beschriftung links, Eingabefeld rechts (mobil untereinander) --- */
.field-row {
  display: flex;
  gap: var(--a3);
  padding: 0.55rem 0;
  border-bottom: 1px solid var(--rand);
  align-items: baseline;
}
.field-row:last-of-type { border-bottom: none; }
.field-row label { flex: 0 0 15.5rem; font-size: 0.9rem; font-weight: 600; color: var(--text-leise); }
.field-row input, .field-row textarea { flex: 1; font-size: 0.92rem; min-width: 0; }
/* Mehrzeilige Freitextfelder: Beschriftung ueber dem Feld, Feld ueber die volle Breite.
   In der zweispaltigen Zeile bliebe fuer laengere Texte nur ein schmaler Rest. */
.field-row.field-block { flex-direction: column; align-items: stretch; gap: 0.3rem; }
.field-row.field-block label { flex: none; }
/* In der einspaltigen Variante liegt die Hauptachse SENKRECHT. Dort darf an den Feldern
   kein "flex: 1" stehen: das ist flex-basis: 0, und auf der Hauptachse schlaegt flex-basis
   die height. Die Hoehe, die profilkarten.js zum Mitwachsen setzt, waere damit wirkungslos, und das
   Feld bliebe auf seiner rows-Hoehe stehen - der Text darunter abgeschnitten. Genau das war
   seit 0.20 der Fall und faellt in keinem jsdom-Test auf, weil dort nichts gerechnet wird. */
.field-row.field-block input,
.field-row.field-block textarea { flex: none; }
/* Ein Feld mit kurzem Hinweis darunter (1.85, Wochenstunden): beide teilen sich die rechte Spalte.
   Die Hauptachse liegt darin senkrecht, das Feld traegt deshalb kein "flex: 1" (siehe oben). */
.feld-mit-hinweis { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.2rem; }
.feld-mit-hinweis input { flex: none; }
.feld-mit-hinweis .phase-note { margin: 0; }
.field-row textarea {
  line-height: 1.5;
  resize: vertical;
  overflow-y: auto;
  max-height: 60vh;
}
.field-pair { flex: 1; display: flex; align-items: center; gap: 0.4rem; }
.field-pair input { flex: 0 1 auto; }
/* Zeitspanne "von bis": zwei gleich breite Spalten mit dem "bis" dazwischen, zusammen so breit
   wie jedes andere Feld der Seite. Bis 0.94 lief das ueber .field-pair, und die Zeile endete
   mitten in der Spalte, waehrend alle Felder darueber und darunter bis zum rechten Rand gingen.
   Am Telefon kam hinzu, dass die Schmalregel ".field-row input { flex: none }" das Schrumpfen
   aufhob: Ein Browser mit breiten Zeitfeldern schob das zweite Feld ueber den Rand. Ein Raster
   mit minmax(0, 1fr) schrumpft bei jeder Breite mit und ist von den flex-Regeln unberuehrt.
   flex: 1 1 auto statt flex: 1 - in der einspaltigen Schmalansicht liegt die Hauptachse
   senkrecht, und flex-basis: 0 wuerde dort die Hoehe bestimmen. */
.field-spanne {
  flex: 1 1 auto;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: 0.5rem;
}
.field-spanne input { width: 100%; min-width: 0; box-sizing: border-box; }
.field-spanne > span { color: var(--text-leise); }

/* Geraetekarten auf der Ausruestungsseite (bis 1.9 auf der Profilseite), seit 1.12 je ein
   zugeklapptes <details>: Kopfzeile mit Name und Typ, darunter leiser die Kurzfassung. Als eigene
   Zeile und nicht dahinter: Am Telefon brach sie sonst mitten in "ohne Wattmessung" um. */
.gear-card {
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  padding: var(--a3) var(--a4);
  margin-bottom: var(--a3);
}
.gear-head { font-weight: 600; }
.gear-kurz { display: block; margin-top: 0.15rem; font-size: 0.85rem; color: var(--text-still); }
.gear-kurz:empty { display: none; }
.gear-fields { display: flex; flex-wrap: wrap; gap: 0.6rem 1rem; }
.gear-fields label { display: flex; flex-direction: column; font-size: 0.85rem; color: var(--text-leise); gap: 0.15rem; }
.gear-fields input, .gear-fields select { font-size: 0.95rem; }
.gear-actions { display: flex; align-items: center; gap: 0.6rem; margin-top: 0.5rem; }

/* --- Freie Anfrage (1.79) ---
   Die Antwort steht wie eine Coach-Antwort als Markdown in einer Flaeche, nur lesbar; der
   Sicherheitstext des Gates bekommt den Warnrand wie der Statusbereich. Der Zaehler unter jedem
   Textfeld rechtsbuendig und leise, rot erst ueber der Grenze. */
.frei-zaehler { align-self: flex-end; margin-top: 0; font-size: 0.8rem; }
.frei-antwort {
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  background: var(--flaeche-hoch);
  padding: var(--a3) var(--a4);
  overflow-wrap: anywhere;
  min-height: 3rem;
}
.frei-antwort.frei-sicherheit { border-color: var(--warn); border-width: 2px; }
.frei-hinweise { margin: 0.4rem 0 0; padding-left: 1.2rem; color: var(--warn); font-size: 0.88rem; }
.frei-verlauf { margin-top: var(--a3); }
.frei-verlauf summary { cursor: pointer; font-weight: 600; color: var(--text-leise); }
.frei-verlauf ul { list-style: none; margin: 0.4rem 0 0; padding: 0; }
.frei-verlauf li { margin: 0.25rem 0; }
.frei-verlauf-eintrag {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  width: 100%;
  text-align: left;
  gap: 0.1rem;
}
.frei-verlauf-kopf { font-size: 0.85rem; color: var(--text-still); }
.frei-verlauf-anfang { font-size: 0.92rem; overflow-wrap: anywhere; }

/* --- Coach-Einblick --- */
.prompt-text {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  background: var(--flaeche-hoch);
  border: 1px solid var(--rand);
  border-radius: var(--r-s);
  padding: var(--a3) var(--a4);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 0.82rem;
  line-height: 1.55;
  max-height: 60vh;
  overflow-y: auto;
}
.char-count { font-weight: 400; color: var(--text-still); font-size: 0.85rem; }
.tool-row { display: flex; gap: var(--a3); padding: 0.5rem 0; border-bottom: 1px solid var(--rand); align-items: baseline; }
.tool-row:last-child { border-bottom: none; }
.tool-name { flex: 0 0 15.5rem; font-family: ui-monospace, "SF Mono", Consolas, monospace; font-size: 0.85rem; color: var(--akzent); }
.tool-descr { font-size: 0.9rem; color: var(--text-leise); }
/* Wettkaempfe und Ziele (0.106, seit 1.10 auf eigener Seite): der Loeschen-Knopf rechts in der
   Zeile, das Formular zum Anlegen mit etwas Abstand darunter. */
.ziel-zeile > button { flex: 0 0 auto; margin-left: auto; }
.ziel-form { margin-top: var(--a3); }
.lager-hinweis { margin-top: 0.25rem; color: var(--warn); }

/* --- Ueber-Seite --- */
.about-facts { display: grid; grid-template-columns: fit-content(55%) minmax(0, 1fr); gap: 0.35rem 1.2rem; margin: 0; }
.about-facts dt { font-weight: 600; color: var(--text-leise); }
.about-facts dd { margin: 0; }
.changelog-entry { border-top: 1px solid var(--rand); padding-top: var(--a4); margin-top: var(--a4); }
.changelog-entry:first-child { border-top: none; padding-top: 0; margin-top: 0; }
.changelog-entry h3 { margin: 0 0 0.2rem 0; font-size: 1.05rem; }
.changelog-entry h4 { margin: 0.9rem 0 0.2rem 0; font-size: 0.92rem; color: var(--text-leise); }
.changelog-date { font-weight: normal; color: var(--text-still); }
.changelog-list { margin: 0; padding-left: 1.2rem; }
.changelog-list li { margin-bottom: 0.35rem; }

/* --- Ereignisseite ---
   Zwei Spalten je Eintrag: links der eigene Wortlaut, rechts die abgeleiteten Groessen, mit
   denen der Coach tatsaechlich plant. Das Nebeneinander ist der ganze Zweck der Seite,
   deshalb sind es Spalten und keine Abschnitte untereinander - erst so sieht man auf einen
   Blick, was aus einem Satz wie "seit gestern erkaeltet" geworden ist. Unter 640px klappen
   sie untereinander, dann steht der Wortlaut oben. */
.ereignis-card {
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  padding: var(--a3) var(--a4);
  margin-bottom: var(--a3);
}
.ereignis-card-laufend { border-color: var(--schlecht); background: var(--schlecht-leise); }
.ereignis-head { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.6rem; }
.ereignis-titel { font-weight: 600; }
.ereignis-id { color: var(--text-still); font-size: 0.8rem; margin-left: auto; }
.ereignis-marke {
  font-size: 0.75rem;
  font-weight: 500;
  padding: 0.1rem 0.5rem;
  border-radius: var(--r-rund);
  background: var(--flaeche-hoch);
  color: var(--text-leise);
}
.ereignis-marke-laufend { background: var(--schlecht-leise); color: var(--schlecht); }
.ereignis-marke-kuenftig { background: var(--warn-leise); color: var(--warn); }
.ereignis-spalten { display: flex; flex-wrap: wrap; gap: var(--a3) var(--a6); }
/* min-width: 0 laesst die Spalten unter ihre Inhaltsbreite schrumpfen - ohne das ragt ein
   langer Wortlaut rechts aus der Seite heraus, statt umzubrechen. */
.ereignis-wortlaut, .ereignis-abgeleitet { flex: 1 1 16rem; min-width: 0; }
.ereignis-spaltenkopf {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-still);
  margin-bottom: 0.3rem;
}
.ereignis-notiz { margin: 0; font-size: 0.92rem; overflow-wrap: break-word; }
.ereignis-notiz-leer { color: var(--text-still); font-style: italic; }
.ereignis-fussnote { color: var(--text-still); font-size: 0.8rem; margin-top: 0.35rem; }
.ereignis-angaben { display: flex; flex-direction: column; gap: 0.15rem; font-size: 0.9rem; }
.ereignis-angabe { display: flex; gap: 0.5rem; align-items: baseline; }
.ereignis-angabe-name { color: var(--text-still); flex: 0 0 8.5rem; }
.ereignis-angabe-wert { min-width: 0; overflow-wrap: break-word; }
.ereignis-rampe { margin-top: 0.6rem; padding-top: 0.5rem; border-top: 1px solid var(--rand); }
/* Aendern/Loeschen am Fuss der Karte. Echte Wunschbreite statt flex:1, damit der Hinweistext bei
   einem gesperrten Eintrag umbricht, statt die Knoepfe zu zerquetschen (siehe die Termin-Zeile). */
.ereignis-aktionen {
  display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center;
  margin-top: 0.7rem; padding-top: 0.6rem; border-top: 1px solid var(--rand);
}
.ereignis-aktionen .phase-note { flex: 1 1 14rem; margin: 0; }

@media (max-width: 700px) {
  /* Schmal wird JEDE Feldzeile einspaltig - damit gilt die Begruendung von oben hier fuer
     alle Felder, nicht nur fuer die mehrzeiligen. */
  .field-row { flex-direction: column; gap: 0.2rem; align-items: stretch; }
  .field-row label { flex: none; }
  .field-row input, .field-row textarea { flex: none; }
  .feld-mit-hinweis { flex: none; }
  .tool-row { flex-direction: column; gap: 0.15rem; }
  .tool-name { flex: none; }
}
@media (max-width: 640px) {
  .ereignis-angabe { flex-direction: column; gap: 0; }
  .ereignis-angabe-name { flex: none; font-size: 0.8rem; }
}

/* ===================================================================
   8. Uebungskartei und Abspieler
   Die Klassennamen .ex-* und .pl-* sind Vertrag: tests/web/test_uebungen.js greift genau
   auf sie zu. Hier wird nur die Optik geaendert, nie ein Name.
   =================================================================== */

.ex-ref {
  padding: 0 0.15em;
  border: none;
  border-radius: 4px;
  background: var(--akzent-leise);
  color: var(--akzent);
  font: inherit;
  text-decoration: underline dotted;
  text-underline-offset: 0.15em;
  cursor: pointer;
}
.ex-ref:hover { background: var(--akzent-leise); color: var(--akzent-hell); }
.ex-ref::after { content: " ⓘ"; font-size: 0.8em; opacity: 0.75; }

.ex-dialog {
  width: min(38rem, 94vw);
  max-height: 90dvh;
  overflow: auto;
  padding: var(--a6);
  border: 1px solid var(--rand);
  border-radius: var(--r-l);
  background: var(--flaeche);
  color: var(--text);
  box-shadow: var(--schatten);
}
.ex-dialog::backdrop { background: var(--schleier); }
/* Am Telefon ein Blatt am unteren Rand wie .page-dialog (Abschnitt 4). Der Abspieler bleibt,
   wie er ist: Er hat keinen Innenabstand, sein Rahmen kostet keine Zeile. */
@media (max-width: 46rem) {
  .ex-dialog {
    margin: auto 0 0;
    width: 100%;
    max-width: 100%;
    padding: var(--a4);
    padding-bottom: calc(var(--a4) + env(safe-area-inset-bottom, 0px));
    border-width: 1px 0 0;
    border-radius: var(--r-l) var(--r-l) 0 0;
  }
}
.ex-kopf { display: flex; align-items: flex-start; gap: var(--a4); margin-bottom: var(--a3); }
.ex-kopf h2 { margin: 0; font-size: 1.15rem; flex: 1; }
.ex-zu { background: transparent; border: 1px solid var(--rand-stark); color: var(--text-leise); }
.ex-zu:hover { background: var(--flaeche-hoch); }

.ex-bild {
  position: relative;
  width: 100%;
  aspect-ratio: 3 / 2;
  border-radius: var(--r-m);
  overflow: hidden;
  background: var(--flaeche-hoch);
}
.ex-bild.leer { display: none; }
.ex-bild img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 1.2s ease-in-out;
}
/* Das erste Bild ist der Grund und bleibt stehen; die weiteren blenden darueber auf. */
.ex-bild img.grund { opacity: 1; }
.ex-bild img.sichtbar { opacity: 1; }

.ex-text { margin-top: var(--a3); }
.ex-bereich { font-size: 0.85rem; color: var(--text-still); }
.ex-ablauf { margin: 0.4rem 0 0 0; line-height: 1.55; }
.ex-achten-kopf { margin-top: var(--a3); font-weight: 600; font-size: 0.95rem; }
.ex-achten { margin: 0.3rem 0 0 0; padding-left: 1.2rem; line-height: 1.5; }
.ex-achten li { margin-bottom: 0.2rem; }

/* --- Abspielmodus: eine Einheit Schritt fuer Schritt, jeder Schritt wird bestaetigt --- */
.ex-player {
  width: min(34rem, 96vw);
  max-height: 94dvh;
  padding: 0;
  border: 1px solid var(--rand);
  border-radius: var(--r-l);
  background: var(--flaeche);
  color: var(--text);
  overflow: hidden;
  box-shadow: var(--schatten);
}
.ex-player::backdrop { background: var(--schleier); }
.pl-kopf {
  display: flex;
  flex-wrap: wrap;   /* fr "Exercice 1 sur 4" + "Terminer" neben einem langen Titel: der Knopf
                        rutscht in die naechste Zeile, statt ueber den Rand zu ragen */
  align-items: center;
  gap: 0.7rem;
  padding: var(--a3) var(--a4);
  background: var(--flaeche-hoch);
  border-bottom: 1px solid var(--rand);
}
.pl-titel { font-weight: 600; flex: 1; }
.pl-zaehler { font-size: 0.85rem; color: var(--text-leise); white-space: nowrap; }
.pl-zu { margin-left: auto; background: transparent; border: 1px solid var(--rand-stark); color: var(--text-leise); padding: 0.25rem 0.6rem; }
.pl-zu:hover { background: var(--flaeche); }
.pl-fortschritt { height: 0.25rem; background: var(--rand); }
.pl-fortschritt > div { height: 100%; width: 0; background: var(--akzent); transition: width 0.3s ease; }
.pl-koerper { padding: var(--a4); max-height: 66dvh; overflow: auto; }
.pl-runde {
  display: inline-block;
  margin-bottom: 0.4rem;
  padding: 0.1rem 0.55rem;
  border-radius: var(--r-rund);
  background: var(--flaeche-hoch);
  font-size: 0.8rem;
  color: var(--text-leise);
}
.pl-name { margin: 0 0 0.2rem 0; font-size: 1.3rem; overflow-wrap: break-word; }
/* Seit 1.53, nur auf en/fr: Claudias deutsche Beschriftung klein unter dem uebersetzten Namen.
   Sie haelt die Zuordnung zum Plan und zum Chat, wo die Uebung deutsch heisst. */
.pl-name-de,
.pl-name-katalog {
  margin: -0.1rem 0 0.35rem 0;
  font-size: 0.85rem;
  color: var(--text-leise);
  overflow-wrap: break-word;
}
.pl-vorgabe { color: var(--text-leise); margin-bottom: 0.7rem; }
.pl-bild { margin-bottom: 0.7rem; }
/* Seit 1.64: Die Kartei liess sich nicht laden - an der Stelle von Bild und Beschreibung. */
.pl-ohne-kartei {
  margin-bottom: 0.7rem;
  padding: 0.5rem 0.7rem;
  border-left: 3px solid var(--warn);
  background: var(--warn-leise);
  color: var(--warn);
  font-size: 0.9rem;
}
.pl-uhr {
  font-size: 2.8rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  letter-spacing: 0.02em;
}
.pl-uhr.ohne { color: var(--text-still); font-size: 1.6rem; }
.pl-uhr.fertig { color: var(--gut); animation: pl-blink 0.9s ease-in-out 3; }
@keyframes pl-blink { 50% { opacity: 0.25; } }
/* Pause zwischen zwei Uebungen: Titel und Uhr in der Akzentfarbe, damit sie sich auf einen Blick
   von einer laufenden Uebung unterscheidet, darunter die naechste Uebung samt Bild. */
.pl-zwischenpause { text-align: center; }
.pl-pausentitel { font-size: 1.3rem; font-weight: 600; color: var(--akzent); }
.pl-zwischenpause .pl-uhr { color: var(--akzent); }
.pl-als-naechstes { color: var(--text-leise); margin: 0.3rem 0 0.7rem; }
.pl-fuss {
  display: flex;
  flex-wrap: wrap;   /* bei A+ passt "Naechste Uebung ›" nicht mehr neben Zurueck und Pause - dann
                        bekommt der Weiter-Knopf eine eigene Zeile in voller Breite */
  gap: 0.5rem;
  padding: var(--a3) var(--a4);
  border-top: 1px solid var(--rand);
  background: var(--flaeche-still);
}
.pl-zurueck, .pl-pause { background: transparent; border: 1px solid var(--rand-stark); color: var(--text-leise); }
.pl-zurueck:hover, .pl-pause:hover { background: var(--flaeche-hoch); }
.pl-weiter { flex: 1; background: var(--gut); color: var(--flaeche); font-weight: 600; }
.pl-weiter:hover { background: var(--gut); filter: brightness(1.08); }

/* Vermerk auf dem Coach-Einblick: Die Seite gehoert allein dem Betreiber. Warnfarbig, nicht
   fehlerfarbig - es ist ein Hinweis auf Sichtbarkeit, keine Stoerung. */
.nur-du {
  border-left: 3px solid var(--warn);
  background: var(--warn-leise);
  color: var(--text);
  padding: 0.5rem 0.75rem;
  border-radius: var(--r-s);
  margin: 0 0 0.8rem;
}

/* Startknopf einer abspielbaren Einheit auf der Wochenplan-Seite */
.pl-start {
  margin-top: 0.5rem;
  background: transparent;
  border: 1px solid var(--gut);
  color: var(--gut);
  font-size: 0.88rem;
  padding: 0.3rem 0.75rem;
}
.pl-start:hover { background: var(--gut-leise); }

/* Startknopf und "Hab ich gemacht" stehen an der heutigen Einheit nebeneinander. Echte
   Wunschbreiten statt flex: 1 - in einer umbrechenden Reihe waere flex-basis: 0 immer "passt",
   und der laengere Knopf wuerde zusammengequetscht statt umzubrechen. */
.workout-knoepfe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.workout-knoepfe .pl-start { flex: 0 1 auto; }
.workout-knoepfe .melde-button { flex: 0 1 auto; font-size: 0.82rem; padding: 0.3rem 0.75rem; }

/* Abschluss des Abspielers: die Frage, ob die Einheit als gemacht in den Wochenplan soll. */
.pl-abschluss { text-align: center; padding: 0.5rem 0; }
.pl-abschluss-frage { color: var(--text-leise); margin: 0.4rem 0 1rem; }
.pl-abschluss-fehler { color: var(--schlecht); margin: 0 0 0.8rem; }
/* Wie anstrengend war's? Zehn Stufen zum Antippen, zwei Reihen zu fuenf - gross genug fuer
   verschwitzte Finger am Telefon (1.28). */
.pl-rpe { margin: 0 0 1rem; }
.pl-rpe-frage { color: var(--text-leise); margin: 0 0 0.5rem; }
.pl-rpe-stufen {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 0.4rem;
  max-width: 20rem;
  margin: 0 auto;
}
.pl-rpe-stufe {
  background: transparent;
  border: 1px solid var(--rand-stark);
  color: var(--text);
  padding: 0.45rem 0;
  font-variant-numeric: tabular-nums;
}
.pl-rpe-stufe[aria-pressed="true"] {
  background: var(--akzent);
  border-color: var(--akzent);
  color: var(--akzent-text);
}
.pl-rpe-wort { min-height: 1.4em; margin: 0.4rem 0 0; color: var(--text-leise); }
.pl-abschluss-knoepfe {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
}
.pl-melden {
  flex: 0 1 14rem;
  background: var(--gut);
  color: var(--flaeche);
  font-weight: 600;
  padding: 0.5rem 1rem;
}
.pl-melden:hover:not(:disabled) { filter: brightness(1.08); }
.pl-melden:disabled { opacity: 0.6; cursor: default; }
.pl-nur-schliessen {
  flex: 0 1 10rem;
  background: transparent;
  border: 1px solid var(--rand-stark);
  color: var(--text-leise);
  padding: 0.5rem 1rem;
}
.pl-nur-schliessen:hover:not(:disabled) { background: var(--flaeche-hoch); }

/* Uhr selbst starten, wenn der Coach keine Zeit vorgegeben hat */
.pl-schnellstart {
  display: flex;
  flex-wrap: wrap;   /* fr "Lancer le chrono :" bei A+ - die Knoepfe rutschen in die naechste Zeile */
  align-items: center;
  gap: 0.4rem;
  padding: 0 var(--a4) 0.6rem var(--a4);
  font-size: 0.85rem;
  color: var(--text-leise);
}
.pl-schnellstart button {
  padding: 0.2rem 0.55rem;
  font-size: 0.85rem;
  background: transparent;
  border: 1px solid var(--rand-stark);
  color: var(--text-leise);
}
.pl-schnellstart button:hover { background: var(--flaeche-hoch); }

/* --- Formkurve ---
   Zwei Diagramme uebereinander mit EINER Zeitachse, nicht eines mit zwei Werteachsen: Fitness
   und Form haben verschiedene Groessenordnungen, und zwei Achsen in einem Bild taeuschen einen
   Zusammenhang vor, der aus der Wahl der Skalen kommt und nicht aus den Daten. */
.kurve-karte {
  background: var(--flaeche);
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  box-shadow: var(--schatten-klein);
  padding: var(--a4);
  position: relative;
}
.kurve-kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--a3);
  flex-wrap: wrap;
}
.kurve-kopf h3 { margin: 0; font-size: 1rem; }
.kurve-kopf p { margin: 0; font-size: 0.86rem; color: var(--text-leise); }
.kurve-plot + .kurve-plot {
  margin-top: var(--a4);
  padding-top: var(--a4);
  border-top: 1px solid var(--rand);
}
.kurve-bild { display: block; width: 100%; height: auto; touch-action: pan-y; }
.kurve-bild text { fill: var(--text-leise); }
.kurve-bild .kurve-tick { fill: var(--text-still); font-variant-numeric: tabular-nums; }

/* Kennzahlen ueber den Diagrammen: erst die Aussage, dann der Verlauf. */
.kurve-zahlen { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--a3); }
/* Fuenf Kennzahlen auf der Erholungsseite: mit der schmaleren Mindestbreite am Rechner in einer
   Reihe statt 4 + 1, am Telefon zu zweit statt einzeln untereinander. */
#erholung-zahlen { grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); }
.kurve-kachel {
  background: var(--flaeche);
  border: 1px solid var(--rand);
  border-radius: var(--r-m);
  padding: var(--a3);
  display: flex;
  flex-direction: column;
  gap: var(--a1);
}
.kurve-kachel-label { font-size: 0.82rem; color: var(--text-leise); }
.kurve-kachel-wert { font-size: 1.7rem; font-weight: 600; line-height: 1.15; }
.kurve-kachel-fuss { font-size: 0.82rem; color: var(--text-still); }
.kurve-chip {
  align-self: flex-start;
  font-size: 0.78rem;
  padding: 0.05rem var(--a2);
  border-radius: var(--r-rund);
  border: 1px solid currentColor;
}
.kurve-chip-gut { color: var(--gut); }
.kurve-chip-warn { color: var(--warn); }
.kurve-chip-schlecht { color: var(--schlecht); }
.kurve-chip-neutral { color: var(--text-leise); }

/* Zeitraumwahl */
.kurve-filter { display: flex; align-items: center; gap: var(--a2); flex-wrap: wrap; }
.kurve-filter-label {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-still);
}
/* max-width und min-width 0 (2b): Bei A+ auf dem Telefon ist die Zeile breiter als der Bildschirm.
   Dann schrumpfen die Knoepfe und brechen in sich um ("180 / jours"), statt dass der letzte
   abgeschnitten wird. */
.kurve-segment { display: inline-flex; max-width: 100%; border: 1px solid var(--rand-stark); border-radius: var(--r-s); overflow: hidden; }
.kurve-segment button { min-width: 0; }
.kurve-segment button {
  border: 0;
  border-radius: 0;
  background: var(--flaeche);
  color: var(--text-leise);
  font-size: 0.88rem;
  padding: 0.2rem var(--a3);
}
.kurve-segment button + button { border-left: 1px solid var(--rand); }
.kurve-segment button:hover { background: var(--flaeche-hoch); }
.kurve-segment button[aria-pressed="true"] { background: var(--akzent-leise); color: var(--akzent); font-weight: 600; }
/* Der Schalter "bis zum A-Wettkampf" neben der Zeitraumwahl (1.7) */
.kurve-schalter { display: inline-flex; align-items: center; gap: var(--a2); font-size: 0.88rem; color: var(--text-leise); cursor: pointer; }
.kurve-schalter input { margin: 0; accent-color: var(--ziel); }

/* Legende: ohne sie traegt allein die Farbe die Zuordnung, und das reicht nicht. */
.kurve-legende {
  display: flex;
  flex-wrap: wrap;
  gap: var(--a2) var(--a4);
  margin-top: var(--a3);
  font-size: 0.85rem;
  color: var(--text-leise);
}
.kurve-legende-eintrag { display: inline-flex; align-items: center; gap: var(--a2); }
.kurve-strich { width: 22px; height: 3px; border-radius: 2px; flex: 0 0 auto; }
.kurve-strich-gestrichelt { height: 0; border-top: 2px dashed var(--kurve-fitness); border-radius: 0; }
.kurve-strich-ziel { height: 0; border-top: 2px dashed var(--ziel); border-radius: 0; }
.kurve-feld { width: 18px; height: 12px; border-radius: 3px; flex: 0 0 auto; }
.kurve-strich-regel { height: 0; border-top: 2px dotted var(--text-leise); border-radius: 0; }
/* Punkte der Erholungsseite in der Legende - dieselben drei Arten wie im Bild. */
.kurve-punkt { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.kurve-punkt-wert { background: var(--kurve-erholung); opacity: 0.55; }
.kurve-punkt-auffaellig { background: var(--kurve-auffaellig); }
.kurve-punkt-hohl { border: 1.5px solid var(--text-still); }
.kurve-zeichen { font-weight: 700; color: var(--text); line-height: 1; }

/* Erklaerzeile am Zeiger. Sie ergaenzt die Kurve, sie ersetzt sie nicht: dieselben Zahlen
   stehen auch in der Tabelle darunter, die ganz ohne Zeigegeraet auskommt. */
.kurve-tooltip {
  position: absolute;
  z-index: 5;
  pointer-events: none;
  min-width: 12rem;
  background: var(--flaeche);
  border: 1px solid var(--rand-stark);
  border-radius: var(--r-s);
  box-shadow: var(--schatten);
  padding: var(--a2) var(--a3);
  font-size: 0.85rem;
  opacity: 0;
}
.kurve-tooltip[data-sichtbar="true"] { opacity: 1; }
.kurve-tooltip-datum { font-weight: 600; margin-bottom: var(--a1); }
.kurve-tooltip-zeile { display: flex; align-items: center; gap: var(--a2); }
.kurve-tooltip-zeile .kurve-strich { width: 14px; }
.kurve-tooltip-name { color: var(--text-leise); }
.kurve-tooltip-wert { margin-left: auto; font-weight: 600; font-variant-numeric: tabular-nums; }
.kurve-tooltip-notiz {
  margin-top: var(--a1);
  padding-top: var(--a1);
  border-top: 1px solid var(--rand);
  color: var(--text-leise);
}

/* Tabellenansicht - jeder Wert auch ohne Zeigegeraet erreichbar */
.kurve-tabelle { overflow-x: auto; margin-top: var(--a3); }
.kurve-tabelle table { border-collapse: collapse; width: 100%; font-size: 0.86rem; }
.kurve-tabelle th, .kurve-tabelle td {
  text-align: right;
  padding: 0.2rem var(--a3);
  border-bottom: 1px solid var(--rand);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.kurve-tabelle th:first-child, .kurve-tabelle td:first-child { text-align: left; }
.kurve-tabelle th { color: var(--text-leise); font-weight: 600; }
.kurve-tabelle tr[data-geplant="true"] td { color: var(--text-leise); font-style: italic; }
.kurve-tabelle tr[data-auffaellig="true"] td { color: var(--kurve-auffaellig); }
.erholung-regeln { margin: var(--a2) 0 0; padding-left: 1.2rem; display: flex; flex-direction: column; gap: var(--a2); }
.erholung-regeln li { color: var(--text-leise); }
.erholung-regeln strong { color: var(--text); font-variant-numeric: tabular-nums; }
