/* Grundgeruest der Oberflaeche.
   Mobile-first: die Regeln gelten fuer das Handy, Groesseres kommt ueber
   min-width dazu. Die Handy-Ansicht ist der Hauptfall, nicht der Sonderfall. */

:root {
    --grund:        #ffffff;
    --grund-tief:   #f2f4f6;
    --schrift:      #1f2933;
    --schrift-matt: #61707d;
    --linie:        #d7dde3;
    --akzent:       #1f6feb;
    --akzent-tief:  #16509f;
    --gut:          #1a7f4b;
    --warnung:      #a15c00;
    --fehler:       #b3261e;
    /* Signalorange. Zwei Verwendungen, und beide meinen dasselbe: „hier stimmt
       etwas nicht, aber es ist nichts kaputt und nichts gelöscht" — die
       übersprungene Übung im Training und das gesperrte Konto in der
       Benutzerverwaltung. Bewusst NICHT --warnung (#a15c00): Auf einem 4 px
       breiten Balken ist ein abgedunkeltes Braunorange nicht mehr von Grau zu
       unterscheiden.

       ACHTUNG bei Text darauf: Weiß kommt auf 2,94:1, dunkle Schrift auf
       5,03:1. Zum Vergleich --fehler: 6,54:1 auf Weiß — das Rot ist dunkler,
       als es wirkt, deshalb funktioniert Weiß dort und hier nicht von selbst.

       Trotzdem steht auf diesem Ton überall #fff — Knopf wie Abzeichen.
       Ausdrückliche Entscheidung des Benutzers am 2026-08-17, getroffen
       NACHDEM er beide Fassungen am eigenen Gerät gesehen hat: erst den Knopf,
       dann das Abzeichen nachgezogen, damit zwei gleichfarbige Elemente
       nebeneinander nicht unterschiedlich beschriftet sind.

       Das liegt unter WCAG AA. Nicht stillschweigend zurückdrehen, sondern
       nachfragen — die App hat genau einen Benutzerkreis, und der hat das
       am eigenen Bildschirm beurteilt. */
    --signal:       #ff6600;
    --radius:       10px;
    /* Fingerkuppe, nicht Mauszeiger: nichts Antippbares kleiner als das. */
    --tippziel:     44px;
}

* { box-sizing: border-box; }

/* Das hidden-Attribut muss gewinnen — immer.
 *
 * Browser blenden [hidden] nur über ihr eigenes Standard-Stylesheet aus, und
 * JEDE Regel aus diesem Stylesheet schlägt das, unabhängig von der
 * Spezifität. `button { display: inline-flex }` macht damit auch einen
 * `<button hidden>` sichtbar. Genau das ist beim Wiederholen-Knopf in der
 * Trainingsansicht passiert: Er stand dauerhaft in jeder Übungszeile, obwohl
 * er nur nach einem fehlgeschlagenen Speichern erscheinen soll.
 *
 * Diese Regel gehört an den Anfang und braucht das !important, weil sie sonst
 * von jeder späteren display-Angabe wieder ausgehebelt würde. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

/* Links tragen die Akzentfarbe — auch BESUCHTE.
 *
 * Ohne diese Regel gibt es hier gar keine Angabe zur Linkfarbe, und dann gilt
 * das Standard-Stylesheet des Browsers: Blau, nach dem ersten Klick Lila. Genau
 * so stand „Aktuellen Split wechseln" in der Trainingsansicht irgendwann in
 * einem Lila da, das in dieser Oberfläche sonst nirgends vorkommt.
 *
 * Ein zweiter Selektor für `:visited` ist NICHT nötig: Die Regel des Autors
 * schlägt die des Browsers, gleich welcher Spezifität — Herkunft geht in der
 * Kaskade vor Spezifität. (`:visited` erlaubt aus Datenschutzgründen ohnehin
 * fast nur Farbangaben.)
 *
 * Betroffen sind allein die Links im Fließtext. Jeder Link mit eigener Klasse
 * — Kopfzeile, Knöpfe, Filter, Planwahl, Adminkacheln — setzt seine Farbe
 * selbst und gewinnt über die Spezifität; nachgesehen, nicht angenommen. */
a { color: var(--akzent); }

body {
    margin: 0;
    background: var(--grund-tief);
    color: var(--schrift);
    font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    /* Damit die App im Vollbild nicht unter der Statusleiste klebt. */
    padding: env(safe-area-inset-top) env(safe-area-inset-right)
             env(safe-area-inset-bottom) env(safe-area-inset-left);
}

/* --- Kopf und Navigation ------------------------------------------------ */

.kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1rem;
    padding: 0.6rem 1rem;
    background: var(--schrift);
    color: var(--grund);
}

.marke {
    font-weight: 700;
    letter-spacing: 0.02em;
    margin-right: auto;
    /* .kopf a ist inline-flex — darin wird der Text zu anonymen Flex-Elementen,
       und das Leerzeichen vor <span class="marke-name"> fällt dabei weg.
       „Trainingsplan fürOliver" kam genau daher; der Abstand muss als gap
       zurückkommen. */
    gap: 0.3em;
}

/* .konto stand hier bis 1.2.3 daneben -- der Block rechts mit Geräte, Konto und
   Abmelden. Er ist ersatzlos entfallen: Geräte und Abmelden liegen jetzt auf der
   Kontoseite, und „Konto" ist ein Menüpunkt wie jeder andere. Damit verschwindet
   auch die ungleiche Lücke davor, die aus dem column-gap von .kopf stammte. */
.haupt-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.15rem;
}

.kopf a {
    display: inline-flex;
    align-items: center;
    min-height: 2rem;
    padding: 0.25rem 0.6rem;
    border-radius: var(--radius);
    color: var(--grund);
    text-decoration: none;
}

/* ALLE :hover-Regeln dieser Datei stehen hinter `hover: hover`, und zwar aus
   einem am Gerät beobachteten Grund: Auf einem Touchscreen gibt es kein
   Verlassen mit dem Zeiger, deshalb bleibt :hover am zuletzt angetippten
   Element KLEBEN. Im Training sah das so aus, dass Satzkopf und „+ Satz" beim
   Tippen die Blautöne untereinander tauschten (--akzent gegen --akzent-tief) —
   als reagierte die Oberfläche auf etwas anderes als den Tipp. Am Zeigegerät
   ändert die Abfrage nichts. */
@media (hover: hover) {
    .kopf a:hover { background: rgba(255, 255, 255, 0.12); }

    button:hover, .knopf:hover { background: var(--akzent-tief); }

    /* Das Bild ist ein Knopf, sieht aber keiner — die Regel nimmt zurück, was
       button:hover darüber setzt, und gehört deshalb in dieselbe Abfrage. */
    .bild-knopf:hover { background: none; }

    .saetze-block > .saetze-kopf:hover { background: var(--linie); }

    .summary-knopf:hover { background: var(--akzent-tief); }
}

.kopf a.aktiv { background: rgba(255, 255, 255, 0.22); }

/* Der Name gehört zur Marke, ist aber nicht sie — etwas leiser gesetzt, damit
   „Trainingsplan für Oliver" als ein Titel und nicht als zwei liest. */
.marke-name { font-weight: 500; color: rgba(255, 255, 255, 0.85); }

/* --- Inhalt -------------------------------------------------------------- */

.inhalt {
    max-width: 46rem;
    margin: 0 auto;
    padding: 1rem 1rem 4rem;
}

h1 { font-size: 1.4rem; margin: 0.5rem 0 1rem; }
h2 { font-size: 1.15rem; margin: 1.5rem 0 0.5rem; }

.karte {
    background: var(--grund);
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    padding: 1rem;
    margin-bottom: 0.75rem;
}

.matt { color: var(--schrift-matt); }

/* --- Formulare ----------------------------------------------------------- */

label { display: block; margin: 0.75rem 0 0.25rem; font-weight: 600; }

input[type="text"], input[type="password"], input[type="search"],
select, textarea {
    width: 100%;
    min-height: var(--tippziel);
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    background: var(--grund);
    color: inherit;
    /* Unter 16px zoomt iOS beim Hineintippen in das Feld. */
    font-size: 16px;
}

input:focus-visible, select:focus-visible, textarea:focus-visible,
button:focus-visible, a:focus-visible {
    outline: 2px solid var(--akzent);
    outline-offset: 2px;
}

.feld-fehler {
    color: var(--fehler);
    font-size: 0.9rem;
    margin-top: 0.25rem;
}

input[aria-invalid="true"] { border-color: var(--fehler); }

button, .knopf {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-height: var(--tippziel);
    padding: 0.5rem 1rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: var(--akzent);
    color: #fff;
    font: inherit;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
}

/* button:hover steht oben gesammelt hinter @media (hover: hover). */
button:disabled { opacity: 0.55; cursor: not-allowed; }

button.leise {
    background: var(--grund);
    color: var(--schrift);
    border-color: var(--linie);
}

button.gefahr { background: var(--fehler); }

/* Ankreuzfeld mit Beschriftung in einer Zeile, statt Beschriftung darueber. */
.zeile-wahl {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-height: var(--tippziel);
    font-weight: 400;
}

.zeile-wahl input[type="checkbox"],
.zeile-wahl input[type="radio"] {
    width: 1.25rem;
    height: 1.25rem;
    flex: none;
}

/* Überschrift für eine Feldgruppe, die kein einzelnes <label> tragen kann —
   drei Radiobuttons haben je ein eigenes. Sieht aus wie ein <label>, ist aber
   keins: Ein <label> ohne `for` wäre für Screenreader ein Versprechen ohne
   Ziel. */
.feld-titel {
    display: block;
    margin-top: 0.8rem;
    font-weight: 600;
}

/* Die drei Ausrichtungen nebeneinander, solange die Breite reicht. Am Handy
   brechen sie um — untereinander sind sie immer noch lesbar, und jede Zeile
   behält ihr 44px-Tippziel aus .zeile-wahl. */
.zuschnitt-wahl {
    display: flex;
    flex-wrap: wrap;
    gap: 0 1.2rem;
}

.hinweis-warnung {
    border-left: 4px solid var(--warnung);
    background: #fdf7ee;
}

.liste-schlicht {
    list-style: none;
    margin: 0;
    padding: 0;
}

.geraet-kopf {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.abzeichen {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: var(--grund-tief);
    border: 1px solid var(--linie);
    color: var(--schrift-matt);
    font-size: 0.8rem;
}

/* --- Trainingsgerät ------------------------------------------------------ */

/* Der Symbolvorrat aus lib/view_geraet_symbole.php steht im Dokument, damit
   <use href="#geraet-…"> greift — sichtbar sein darf er nicht. */
.symbol-vorrat { display: none; }

/* Die Erfassungsart, aber nur wo sie von der Regel abweicht: Ein Abzeichen an
   jeder der hundert Kraftübungen trüge keine Information. Ohne eigenes Symbol,
   weil das Geräte-Abzeichen daneben schon eines hat und zwei Piktogramme in
   einer Zeile sich als ein einziges lesen. Farbe wie ein Hinweis und nicht wie
   eine Warnung — es ist eine Eigenschaft, kein Mangel. */
.erfassung-ausdauer {
    border-color: var(--gut);
    color: var(--gut);
}

/* Dieselbe Sorte Hinweis: eine Eigenschaft der Übung, kein Mangel. In der
   Akzentfarbe, damit es sich neben „Ausdauer" nicht als dasselbe liest. */
.wirkung-unterstuetzung {
    border-color: var(--akzent);
    color: var(--akzent);
}

/* Symbol UND Text: Ein Piktogramm allein spart Platz, verlangt aber, dass man
   zehn Zeichen auswendig kennt. inline-flex statt inline-block, damit das
   Symbol auf der Textmitte sitzt und nicht auf der Grundlinie. */
.geraet {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    border-color: var(--akzent);
    color: var(--akzent-tief);
}

/* 1.5em und nicht weniger: Bei den 1.05em aus 1.0.15 rendern die Symbole mit
   rund 13px, und dabei verlieren sie ihre Form — "Multipresse" und "Langhantel"
   waren nicht mehr zu unterscheiden. Am 2026-08-10 in drei Größen gegen die
   laufende Instanz verglichen. Die Strichstärke bleibt bei 1.8; sie sitzt in den
   Symbolen selbst (lib/view_geraet_symbole.php). */
/* 1.5em ist zugleich die Zeilenhöhe des Textes daneben (line-height 1.5 auf
   0.8rem = 19.2px). Deshalb ist das Geräte-Abzeichen exakt so hoch wie die
   Ausführung neben ihm — nachgemessen, beide 24.4px. Wer diesen Wert erhöht,
   macht das Abzeichen höher als seine Nachbarn in derselben Zeile; dann gehört
   die Zeilenhöhe mit angefasst. */
.geraet-symbol {
    width: 1.5em;
    height: 1.5em;
    flex: none;
}

/* Fehlt die Angabe, sagt das Abzeichen es ausdrücklich — in der Warnfarbe wie
   die Platzhaltermarke. Die Übungen aus der Zeit vor diesem Feld sollen ins
   Auge fallen, damit sie nachgepflegt werden. */
.geraet-fehlt {
    border: 1px dashed var(--warnung);
    background: transparent;
    color: var(--warnung);
}

/* --- Verwaltungszeilen (Muskelgruppen, Übungen) -------------------------- */

.gruppe-zeile {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: flex-start;
}

.gruppe-felder {
    flex: 1 1 14rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.gruppe-knoepfe {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.gruppe-knoepfe button {
    padding: 0.5rem 0.75rem;
}

/* Die Pfeiltasten quadratisch halten, damit sie nicht wie Textknöpfe wirken. */
.gruppe-knoepfe .hoch,
.gruppe-knoepfe .runter {
    width: var(--tippziel);
    padding: 0;
    font-size: 1.1rem;
}

.zeilen-fehler { margin-top: 0.5rem; }

/* --- Filterleiste -------------------------------------------------------- */

.filterleiste {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
    align-items: center;
    margin: 1rem 0;
}

.filter-gruppe {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.filter-gruppe a {
    display: inline-flex;
    align-items: center;
    min-height: 2.25rem;
    padding: 0.3rem 0.75rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    background: var(--grund);
    color: var(--schrift);
    text-decoration: none;
}

.filter-gruppe a.aktiv {
    background: var(--schrift);
    border-color: var(--schrift);
    color: #fff;
}

/* Die Filterleiste ist EINE Zeile: links die drei Zustände, rechts die beiden
   Auswahlfelder nebeneinander. Das eigene Flex-Layout ist Pflicht — sonst erben
   die <select> das `width: 100%` aus der Formularregel ganz oben, jedes belegt
   eine eigene Zeile, und aus einer Filterleiste werden auf dem Desktop drei
   Zeilen (so stand es in 1.0.15). max-width fängt lange Optionstexte am Handy
   ab, wo `width: auto` sonst über den Rand hinausliefe. */
.filter-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-left: auto;
}

.filter-form select {
    width: auto;
    max-width: 100%;
}

/* Beschriftung nur für Screenreader. */
.nur-lesbar {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* --- Muskelgruppen-Auswahl ----------------------------------------------- */

.gruppen-wahl {
    margin: 1rem 0;
    padding: 0.75rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
}

.gruppen-wahl legend { font-weight: 600; padding: 0 0.35rem; }

/* Zwei gleich breite Spalten für „primär" und „sekundär", damit die
   Überschriften genau über ihren Bedienelementen stehen. */
.gruppen-kopf,
.gruppen-zeile {
    display: grid;
    grid-template-columns: 4.5rem 4.5rem 1fr;
    align-items: center;
    gap: 0.5rem;
}

.gruppen-kopf {
    font-size: 0.85rem;
    color: var(--schrift-matt);
    border-bottom: 1px solid var(--linie);
    padding-bottom: 0.35rem;
    margin-bottom: 0.35rem;
}

.spalte-primaer, .spalte-sekundaer { text-align: center; }

/* Eine gesperrte Sekundär-Checkbox ist die Zeile, die gerade primär ist —
   sie soll erkennbar gesperrt sein, nicht bloß unauffällig. */
.gruppen-zeile input[type="checkbox"]:disabled { opacity: 0.25; }

.gruppen-zeile { min-height: var(--tippziel); }

.gruppen-zeile input[type="radio"],
.gruppen-zeile input[type="checkbox"] {
    width: 1.25rem;
    height: 1.25rem;
    justify-self: center;
}

.gruppen-zeile input:disabled { opacity: 0.35; }

.gruppen-zeile label {
    margin: 0;
    font-weight: 400;
}

/* --- Übungsliste --------------------------------------------------------- */

/* Bild und Text nebeneinander, Gerät + Ausführung darunter über die volle
   Breite. Bis 2026-08-17 war das ein Flex-Container mit zwei Kindern; seit die
   Schwerpunktzeile als drittes dazukam, muss es ein Grid sein — als Flex-Kind
   hätte sie sich RECHTS neben den Text gestellt statt darunter. */
.uebung-kopf {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 0.75rem;
    align-items: start;
}

.uebung-kopf > .schwerpunkt-zeile {
    grid-column: 1 / -1;
    margin: 0.5rem 0 0;
}

/* Übungsverwaltung: oben Bild und Text nebeneinander, darunter die Knöpfe über
   die GANZE Breite — derselbe Aufbau wie im Training (.uebung-kopf über
   .position-aktionen).

   Bis 1.0.12 trug das Bild beide Zeilen, die Knopfzeile begann also erst
   rechts daneben. Am Handy blieben davon rund 80px weniger übrig, und die
   Zeile brach in zwei um. Die Knöpfe gehören unter das Bild, nicht neben es. */
.uebung-raster {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 0.75rem;
    align-items: center;
}

/* Mit Bild ist das erste Rasterelement der Knopf, ohne Bild der leere
   Platzhalter — beide stehen in der oberen Zeile neben dem Text. */
.uebung-raster > .bild-knopf,
.uebung-raster > .uebung-bild {
    grid-row: 1;
    align-self: center;
}

.uebung-raster > .uebung-text { grid-column: 2; }

/* Über beide Spalten, damit die Knöpfe die volle Breite der Karte haben. */
.uebung-raster > .uebung-knoepfe { grid-column: 1 / -1; }

/* Gerät und Ausführung stehen seit 2026-08-17 ebenfalls über beide Spalten,
   also UNTER dem Bild statt rechts daneben. Damit muss sich die Textspalte
   die Breite nicht mehr mit ihnen teilen, und das Bild durfte wachsen. */
.uebung-raster > .schwerpunkt-zeile {
    grid-column: 1 / -1;
    margin: 0.5rem 0 0;
}

.uebung-bild {
    width: 112px;
    height: 112px;
    flex: none;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--grund-tief);
}

/* Welche Seite eines breiten Motivs stehen bleibt (exercises.image_crop).
   `object-fit: cover` schneidet ohne diese Angabe links und rechts gleich viel
   weg und trifft damit neben das Gerät, sobald es nicht mittig im Bild steht.

   'mitte' bekommt bewusst KEINE Klasse — das ist der Vorgabewert von
   object-position, und eine Klasse dafür wäre Rauschen im Markup. Die Werte
   entstehen in bild_zuschnitt_klasse() (PHP) und in vorschlagMarkup()
   (assets/app.js); beide Hälften gehören zusammen geändert.

   Gilt für alle drei Bildgrößen — Übungsliste, Trainingsansicht, Planposition
   und Tauschvorschlag —, damit dasselbe Motiv überall gleich ausgerichtet ist. */
.bild-links  { object-position: 0% 50%; }
.bild-rechts { object-position: 100% 50%; }

/* 'ganz': nichts abschneiden, sondern verkleinern, bis das Bild in der Breite
   ganz hineinpasst (seit 1.4.12).

   `img.bild-ganz` und nicht bloß `.bild-ganz`: .vorschlag-bild und
   .position-bild setzen `object-fit: cover` WEITER UNTEN in dieser Datei, bei
   gleicher Spezifität gewönne also die spätere Regel — und die Einstellung
   täte an genau zwei von vier Stellen nichts, ohne Fehlermeldung.

   Durchsichtiger Grund statt --grund-tief: Oben und unten bleibt Platz, und ein
   grauer Balken darüber und darunter läse sich wie ein Rahmen um das Bild. */
img.bild-ganz {
    object-fit: contain;
    background: transparent;
}

.uebung-bild-leer {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px dashed var(--linie);
    color: var(--schrift-matt);
}

/* Auf der Trainingsseite sind die Bilder größer: Im Studio wird am Gerät danach
   gesucht, nicht gelesen. Das Thumbnail liegt mit 320px Kantenlänge vor, 150px
   sind also auch auf feinen Displays scharf.

   Beide Größen sind am 2026-08-17 gewachsen (80→112, 120→150). Möglich wurde
   das, weil Gerät und Ausführung aus der Textspalte unter das Bild gewandert
   sind — die Rückmeldung lautete „in der Breite mehr Platz für die Bilder". */
#uebungen .uebung-bild {
    width: 150px;
    height: 150px;
}

.uebung-text { min-width: 0; }

/* Der englische Name gehört UNTER den deutschen, nicht daneben: Beide sind
   inline, und nebeneinander gelesen verschmelzen sie zu einem langen Namen.
   `display: block` trennt sie, ohne ein zusätzliches Element zu brauchen.

   Die Klasse sitzt seit 1.2.5 am Namen SELBST (uebung_name() in
   lib/helpers.php, uebungName() in app.js) und nicht mehr am Elternteil.
   Vorher hing der Umbruch an `.uebung-text > .matt` — im Tauschfenster
   (.vorschlag-text) und im Verlauf gibt es diesen Behälter nicht, dort wären
   beide Namen wieder in einer Zeile gelandet. */
.name-en {
    display: block;
    margin: 0.15rem 0;
}

/* Die übrigen matten Zusätze in der Textspalte — der Archiv-Hinweis in der
   Übungsverwaltung — behalten denselben engen Abstand. */
.uebung-text > .matt { margin: 0.15rem 0; }

.gruppen-anzeige {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin: 0.4rem 0;
}

/* Die Primärgruppe trägt die Tauschlogik — sie muss auf einen Blick
   von den mittrainierten Gruppen unterscheidbar sein. */
.gruppe-primaer {
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: var(--akzent);
    color: #fff;
    font-size: 0.85rem;
    font-weight: 600;
}

.gruppe-sekundaer {
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    background: var(--grund-tief);
    border: 1px solid var(--linie);
    color: var(--schrift-matt);
    font-size: 0.85rem;
}

/* Trainingsgerät und Ausführung stehen in einer eigenen Zeile UNTER den
   Muskelgruppen: Beide sind keine Tauschklasse, und zwischen den Gruppen-
   abzeichen war das nicht erkennbar. Flex statt Textfluss, damit die beiden
   Marken am Handy sauber umbrechen statt an einer Grundlinie zu kleben. */
.schwerpunkt-zeile {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
    margin: 0.35rem 0 0;
}

.uebung-knoepfe {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.6rem;
}

/* Die abschließende Aktion steht rechts außen, abgesetzt vom Bearbeiten —
   dieselbe Trennung wie zwischen „Tauschen" und „Entfernen" in der
   Planverwaltung. Auf schmalen Geräten rutscht sie in die nächste Zeile und
   bleibt auch dort rechtsbündig. */
.uebung-knoepfe .archivieren,
.uebung-knoepfe .loeschen { margin-left: auto; }

/* Archiviert heißt sichtbar abgesetzt, nicht unsichtbar. */
.ist-archiviert { opacity: 0.75; border-left: 4px solid var(--schrift-matt); }
.ist-archiviert .uebung-bild { filter: grayscale(1); }

.abzeichen-archiv {
    background: var(--warnung);
    border-color: var(--warnung);
    color: #fff;
}

.uebung-formular { margin-top: 1rem; border-top: 1px solid var(--linie); padding-top: 0.5rem; }

#neu-bereich summary {
    cursor: pointer;
    min-height: var(--tippziel);
    display: flex;
    align-items: center;
}

/* --- Pläne und Rotation -------------------------------------------------- */

.zeile-eingabe {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    margin-top: 0.75rem;
}

.zeile-eingabe input[type="text"],
.zeile-eingabe select { flex: 1 1 12rem; width: auto; }

.rotation-kette {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
    margin: 0.5rem 0;
}

/* Die Kette sagt die Reihenfolge, sonst nichts: Bis 1.2.18 hob .rotation-naechster
   den vorgeschlagenen Plan blau hervor. Auf der Planseite ist das die falsche
   Auskunft — welches Training als Nächstes kommt, steht in der Trainingsansicht.
   Damit sehen alle Glieder gleich aus, und die Regel ist eine statt zweier. */
.rotation-glied {
    padding: 0.2rem 0.7rem;
    border-radius: 999px;
    border: 1px solid var(--linie);
    background: var(--grund-tief);
}

.rotation-pfeil { color: var(--schrift-matt); }

.plan-kopf { margin-bottom: 0.5rem; }

/* Die Nummerierung macht ein CSS-Zähler, nicht der Listenpunkt des Browsers.
   Grund: Der Marker eines <li> hängt an der Textgrundlinie im Inhalt. Weil der
   Inhalt hier ein Raster ist und keine Textzeile, setzte der Browser die Zahl
   an den unteren Rand der Position — bei einer zweizeiligen Zeile mit 72px-Bild
   stand sie sichtbar zu tief. Als Zähler ist sie ein Rasterelement wie jedes
   andere und lässt sich mittig setzen.

   Beim Umsortieren (admin_plans.js schiebt die <li> im DOM) zählt der Zähler
   von selbst neu — genauso wie es der Marker tat. */
.positions-liste {
    list-style: none;
    counter-reset: position;
    margin: 0.5rem 0;
    padding-left: 0;
}

/* Zweizeilig: oben Bild und Name, darunter die Knöpfe. Alles in einer Zeile
   ließ dem Namen bei vier Knöpfen kaum Platz.

   Die Nummer hat seit 1.2.19 KEINE eigene Spalte mehr. Sie stand links neben
   dem Bild und mittig zur oberen Zeile; Zahl und Abstand nahmen dem 72px-Bild
   rund 26px der Breite weg — auf einem 360px-Gerät ein spürbarer Teil. Jetzt
   liegt sie in der oberen linken Ecke, wie auf der Trainingsseite, und die
   Zeile daneben beginnt am linken Rand. */
.position {
    counter-increment: position;
    /* Bezugsrahmen für die Nummer in der Ecke. */
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--linie);
}

/* Die Nummer als Kästchen in der oberen linken Ecke — dieselbe Anordnung wie
   .position-nummer im Training, nur mit einem Unterschied, der aus der Lage
   folgt: Dort trifft das Kästchen auf den Rand der Karte und braucht deshalb
   nur zwei gezeichnete Linien; hier steht es frei über dem Bild und bekommt
   einen vollen Rahmen mit --radius ringsum.

   Deshalb auch eine eigene Fläche (--grund): Der Rahmen liegt teilweise auf
   dem Thumbnail, und eine Zahl ohne Grund wäre auf einem dunklen Motiv nicht
   zu lesen.

   `top: 0.5rem` ist der Innenabstand der Zeile darüber, also die Oberkante
   des Inhalts: Damit steht die Zahl auf einer Höhe mit der ersten Zeile des
   deutschen Namens. Ändert sich das padding oben, gehört dieser Wert mit
   geändert.

   ANZEIGE UND KEIN BEDIENELEMENT: `pointer-events: none`, weil das Bild
   darunter ein Knopf ist (Beschreibung und großes Bild). Die Ecke ist die
   Stelle, die man beim Zielen zuerst trifft — sie darf nicht tot sein.

   Ziffern gleicher Laufweite, damit ab der zehnten Übung nichts springt. */
.position::before {
    content: counter(position);
    position: absolute;
    top: 0.5rem;
    left: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.3rem;
    padding: 0 0.3rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    background: var(--grund);
    color: var(--schrift-matt);
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
}

.position:last-child { border-bottom: 0; }

/* Nur noch die obere Zeile: Bild und Text nebeneinander. Die Knöpfe liegen
   eine Ebene höher, direkt im <li> — siehe .position-knoepfe.

   Bis 1.0.12 trug das Bild beide Zeilen; die Knopfzeile fing dadurch erst
   hinter ihm an und war am Handy um Bild und Abstand (rund 82px) zu schmal —
   „Entfernen" rutschte in eine zweite Zeile. */
.position-raster {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: 0.6rem;
    align-items: center;
}

/* Der Textblock einer Planposition ist derselbe wie im Training und in der
   Übungsverwaltung (.uebung-text): Name, englischer Name daneben, darunter die
   Muskelgruppen, darunter die Ausführung. Hier steht nur, wo er im Raster
   sitzt — wie er aussieht, steht einmal weiter oben und gilt für alle drei
   Seiten. Bis 1.0.11 hatte die Planseite dafür ein eigenes .position-name mit
   nur der Primärgruppe in einer Zeile; das war die Abweichung. */
.position-raster > .uebung-text { grid-column: 2; }

/* Die Knopfzeile ist ein Kind des <li> und spannt über dessen ganze Breite:
   „Tauschen" beginnt damit am linken Rand der Karte, so wie die Aktionszeile
   im Training. Läge sie im .position-raster, finge sie erst hinter dem Bild
   an — sichtbar eingerückt und rund 78px schmaler.

   `grid-column: 1 / -1` stammt aus der Zeit, als das <li> zwei Spalten hatte
   (Nummer und Inhalt). Es bleibt stehen und stimmt weiter: Bei einer Spalte
   ist es genau diese eine.

   Grundzustand ist ein umbrechender Flex-Fluss: Passen die drei nicht
   nebeneinander, rutscht „Entfernen" in eine eigene Zeile und bleibt dort über
   das auto-Margin rechtsbündig.

   Ein Grid bricht NICHT um. Als eines ohne Umschaltpunkt lief die Zeile am
   Handy rechts aus der Karte heraus — „Entfernen" war halb abgeschnitten, und
   zwar auf jedem üblichen Telefon im Hochformat. Am Desktop sieht man davon
   nichts, deshalb blieb es bis 1.0.10 unbemerkt. Gleiche Bauweise und gleicher
   Grund wie bei .position-aktionen im Training. */
.position-knoepfe {
    grid-column: 1 / -1;
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    align-items: center;
    margin-top: 0.4rem;
}

.position-knoepfe .pos-entfernen { margin-left: auto; }

/* Schmaler als die Vorgabe — wie „Tauschen" im Training, nur noch etwas enger:
   Hier stehen VIER Knöpfe in der Zeile. Gemessen brauchen sie mit dem üblichen
   Innenabstand von 1rem 302px, mit 0.5rem sind es 289px; auf einem 360px-Gerät
   stehen 296px zur Verfügung. Das entscheidet dort über eine oder zwei Zeilen.
   Die Höhe bleibt unangetastet: Die Pfeile sind var(--tippziel) breit,
   darunter geht es nicht. */
.position-knoepfe .pos-tauschen,
.position-knoepfe .pos-entfernen { padding: 0.5rem; }

.pos-pfeile {
    display: flex;
    gap: 0.3rem;
}

/* Ab hier ist Platz für alle drei nebeneinander: Tauschen links, Pfeile in der
   Mitte, Entfernen rechts. Die beiden äußeren Spalten sind gleich breit,
   dadurch sitzen die Pfeile exakt mittig — dieselbe Anordnung wie in der
   Aktionszeile im Training.

   Derselbe Umschaltpunkt wie im Training: Die Zeile hat dort wie hier die
   ganze Kartenbreite. Bis 1.0.12 waren es 32rem — damals lagen in dieser Zeile
   auch noch die Positionsnummer und das 72px-Bild.

   Gemessen brauchen die vier Knöpfe 289px, ab 24rem (384px) sind 320px da.
   Etwas Luft muss bleiben: Wie breit eine Beschriftung ausfällt, hängt an der
   Schrift des Geräts, und ein Grid bricht nicht um — zu knapp bemessen liefe
   die Zeile aus der Karte statt in eine zweite Zeile. */
@media (min-width: 24rem) {
    .position-knoepfe {
        display: grid;
        grid-template-columns: 1fr auto 1fr;
    }

    .position-knoepfe .pos-tauschen  { justify-self: start; }
    .pos-pfeile                      { justify-self: center; }

    /* Das auto-Margin von oben würde sonst gegen justify-self arbeiten. */
    .position-knoepfe .pos-entfernen { justify-self: end; margin-left: 0; }
}

.position-knoepfe .pos-hoch,
.position-knoepfe .pos-runter {
    width: var(--tippziel);
    padding: 0;
}

/* --- Zustaende einer Uebungszeile ---------------------------------------- */

/* Der Balken am linken Rand ist das Leitsystem beim Scrollen durch acht Karten.
   Vier Zustaende, und die Zuordnung ist bewusst so herum:

     gruen  = HIER BIST DU. Die Uebung, an der man gerade steht, waehrend ein
              Training laeuft. Das ist die Information, die man beim Blick aufs
              Handy zuerst sucht -- nicht, was schon fertig ist.
     blau   = erledigt. Abgeschlossen und abgelegt, in der ruhigen Hausfarbe.
     orange = UEBERSPRUNGEN. Noch offen, obwohl man schon weiter ist -- das
              belegte Geraet, zu dem man zurueckwill.
     grau   = kommt noch.

   Frueher war Gruen die Farbe fuer "erledigt". Das ist verlockend, aber falsch
   herum: Gruen zieht den Blick, und den soll das ziehen, was als Naechstes zu
   tun ist.

   Orange ist bewusst NICHT --warnung (#a15c00): Der Balken ist 4px breit, und
   ein abgedunkeltes Braunorange ist auf diesem Streifen nicht mehr von Grau zu
   unterscheiden. Hier zaehlt Signalwirkung ueber vier Pixel, nicht Lesbarkeit
   von Text -- deshalb Safety-Orange, definiert als --signal in :root. Seit
   1.1.12 traegt das gesperrte Konto (§6.1) denselben Ton, aus demselben Grund:
   auffaellig, aber ausdruecklich nicht rot. Rot bleibt dem Loeschen. */
.zeile-offen         { border-left: 4px solid var(--linie); }
.zeile-aktiv         { border-left: 4px solid var(--gut); }
.zeile-erledigt      { border-left: 4px solid var(--akzent); }
.zeile-uebersprungen { border-left: 4px solid var(--signal); }

/* Ein fehlgeschlagenes Speichern bleibt sichtbar stehen, bis es geklappt hat --
   es wird nie stillschweigend verschluckt. */
.zeile-fehler     { border-left: 4px solid var(--fehler); background: #fdf3f2; }

/* Gesetzt, aber vom Server noch nicht bestaetigt.
   Geaendert wird NUR die Strichart, nicht die Farbe: Der Balken behaelt, was
   sein Zustand ihm gibt, und strichelt kurz. Bis 1.1.1 wurde er dabei orange --
   das sah aus wie ein Fehler, obwohl gerade alles seinen Gang geht. Wie viele
   Eingaben tatsaechlich ausstehen, sagt die Leiste am oberen Rand.

   Muss NACH den Zustandsklassen stehen: Alles hier sind einfache Klassen, es
   entscheidet also die Reihenfolge im Stylesheet. */
.zeile-wartet     { border-left-style: dashed; }

/* --- Erledigte Karte: der Schleier ---------------------------------------- */

/* Der blaue Balken allein sagte zu leise, dass eine Übung erledigt ist — im
   Studio sucht man beim Blick aufs Handy die nächste offene Position, und dafür
   muss das Abgehakte zurücktreten. Die ganze Kartenfläche bekommt deshalb einen
   grauen Schleier; lesbar bleibt sie ausdrücklich.

   WARUM EIN DUNKLER SCHLEIER UND NICHT `opacity`. Das ist die Stelle, an der der
   naheliegende Griff das Gegenteil bewirkt, deshalb nachgerechnet gegen die
   Palette:

     Verfahren                     Fläche      Haupttext   matter Text
     heute (weiße Karte)           #ffffff     14,76:1     5,09:1
     Schleier #1f2933 @ 12 %       #e4e5e7     11,71:1     4,61:1
     opacity 0.55                  #ffffff      3,53:1      —
     weißer Schleier @ 50 %        #ffffff      3,06:1      —

   `opacity` und ein heller Schleier lassen die Karte gegen den weißen Grund
   verblassen — dabei verliert ausgerechnet der TEXT seinen Kontrast, und die
   Fläche bleibt fast weiß. Der dunkle Schleier tut das Umgekehrte: Er dunkelt
   die Fläche ab und lässt den Text praktisch in Ruhe, weil Text und Schleier
   fast dieselbe Farbe haben. Deshalb 12 % von --schrift.

   NACH OBEN BEGRENZT DER MATTE TEXT, nicht der Haupttext („zuletzt 40 kg",
   --schrift-matt): Bei 14 % steht er auf 4,50:1 und damit exakt auf der
   AA-Grenze, darüber fällt er darunter. Wer den Schleier kräftiger will, muss
   im selben Zug --schrift-matt in der erledigten Karte anheben.

   Zum Maßstab, wie deutlich 12 % sind: Weiße Karte gegen verschleierte Karte
   sind 1,26:1 — der Unterschied zwischen weißer Karte und Seitenhintergrund,
   den man ohne Nachdenken sieht, beträgt 1,10:1.

   Die Fläche stammt aus einem eigenen Layer und nicht aus `background`: So
   werden Bild, Geräteabzeichen und Muskelgruppen ohne eigene Regel mitgedämpft.
   Eine Liste einzelner Elemente wäre die Stelle, an der man das nächste
   farbige Ding vergisst.

   `inset: 0` deckt die PADDING-Box ab, nicht den Rahmen — der farbige Balken
   am linken Rand bleibt also in voller Kraft, und das Leitsystem aus (c) bleibt
   lesbar. `pointer-events: none` ist Pflicht: Sonst schluckt der Layer jeden
   Griff nach Bild, Satzblock und Häkchen.

   Der Layer liegt auf JEDER Positionskarte und ist nur unsichtbar. Entstünde er
   erst mit der Klasse, gäbe es nichts zu überblenden, und der Schleier spränge
   hart an; so blendet er in 150 ms. Bewegt wird dabei nichts — nur Deckkraft,
   Fallstrick 19 (a) bleibt gewahrt. */
.position-karte { position: relative; }

.position-karte::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: rgba(31, 41, 51, 0.12);   /* --schrift, siehe Rechnung oben */
    opacity: 0;
    transition: opacity 0.15s ease;
    pointer-events: none;
}

.zeile-erledigt::after { opacity: 1; }

/* Der Weg zurück muss sichtbar bleiben: „Erledigt" liegt ÜBER dem Schleier,
   damit man auf einen Blick sieht, wo man die Übung wieder öffnet. Bewusst ohne
   eigenen Hintergrund (Entscheidung des Benutzers am 2026-08-23) — die Zeile
   soll sich nicht als weiße Insel aus der Karte lösen.

   Den Unterschied macht dabei weniger der Text als das Ankreuzfeld: Dunkler
   Text gewinnt unter dem Schleier ohnehin fast nichts (11,7:1 gegen 11,7:1),
   das blaue Häkchen dagegen leuchtet ungedämpft.

   Nur das Häkchen, nicht die ganze Aktionszeile: „Tauschen" ist an einer
   erledigten Position ohnehin gesperrt (§7.5) und gehört zurückgenommen. */
.zeile-erledigt .erledigt-wahl {
    position: relative;
    z-index: 1;
}

/* Ein fehlgeschlagenes Speichern schlägt den Schleier. Beides zugleich gibt es
   wirklich — ab-wählen kann scheitern, dann steht die Karte noch als erledigt
   da und ist bereits rot markiert. Der Schleier über dem zarten Rosa ergäbe ein
   schmutziges Grau und nähme der Warnung ihre Farbe; sichtbar unbestätigt zu
   bleiben ist wichtiger als der Hinweis, dass hier schon abgehakt war. */
.zeile-erledigt.zeile-fehler::after { opacity: 0; }

/* Die Nummer der Übung, in die obere linke Ecke der Karte GESETZT — nicht
   daneben gelegt.

   Gezeichnet werden nur ZWEI Linien: rechts der Zahl von oben nach unten, dann
   unter ihr nach links bis an den farbigen Balken. Die anderen beiden Seiten
   des Rechtecks hat die Karte schon — ihr oberer Rand und der Balken links.
   Deshalb `border-right` und `border-bottom` und sonst nichts: Ein volles
   Kästchen zöge dort, wo die Karte ohnehin eine Linie hat, eine zweite
   daneben. Wo die beiden Linien zusammentreffen, ist die Ecke abgerundet —
   siehe unten bei `border-bottom-right-radius`.

   `top: 0; left: 0` bezieht sich auf die PADDING-Box, also auf die Innenkante
   genau dieser beiden Ränder — das Kästchen sitzt damit bündig in der Ecke,
   ohne dass eine Zahl im Stylesheet die Randstärken nachbilden müsste.

   Kein eigener Grund: Die Zahl selbst steht im Innenabstand der Karte (1rem)
   und damit nie über dem Bild; nur die beiden Linien streifen dessen obere
   linke Ecke, und die ist wegen `border-radius` ohnehin abgerundet. Das hält
   die Ecke auch auf der roten Fehlerkarte richtig, wo ein weißes Kästchen als
   Fremdkörper säße.

   ANZEIGE UND KEIN BEDIENELEMENT: kein Tippziel-Maß und `pointer-events: none`
   — das Bild darunter ist ein Knopf (Beschreibung und großes Bild), und der
   Kreis darf ihm keine Fläche wegnehmen, ausgerechnet in der Ecke, die man
   beim Zielen zuerst trifft.

   KEIN z-index: So bleibt die Nummer unter dem Schleier der erledigten Karte
   (`::after` ist das letzte Kind und gewinnt damit die Malreihenfolge) und
   tritt mit ihr zurück — anders als „Erledigt", der Weg zurück.

   `line-height: 1` hält die Höhe unabhängig von der globalen Zeilenhöhe;
   tabular-nums hält die Breite bei zweistelligen Nummern gleich. */
.position-nummer {
    position: absolute;
    top: 0;
    left: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.3rem;
    padding: 0 0.3rem;
    border-right: 1px solid var(--linie);
    border-bottom: 1px solid var(--linie);
    /* Die eine Ecke, die aus zwei gezeichneten Linien entsteht: dort, wo die
       Linie nach links umbiegt. Sie bekommt --radius wie jede andere Box,
       jeder Knopf und jede Auswahl -- eine spitze 90-Grad-Ecke war der einzige
       harte Winkel der ganzen Oberflaeche.

       Nur DIESE Ecke: Oben links rundet die Karte selbst, die beiden anderen
       sind gar keine Ecken, sondern Stellen, an denen eine Linie auf den
       Kartenrand bzw. den farbigen Balken trifft. */
    border-bottom-right-radius: var(--radius);
    color: var(--schrift-matt);
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
}

/* --- Leisten-Stapel ------------------------------------------------------- */

/* Der Stapel klebt, die Leisten darin nicht. Warum, steht ausführlich in
   lib/view_header.php; kurz: Zwei Elemente mit `top: 0` legen sich übereinander,
   und zurAktivenSpringen() braucht EINE Höhe, die von selbst stimmt.

   Die Reihenfolge darin ist nach Beständigkeit sortiert — oben, was dauerhaft
   dasteht (Trainingsleiste), unten, was nur im Ausnahmefall dazukommt
   (Verbindungsleiste). Andersherum schöbe die zweite die erste hin und her.

   Steht über allem, auch über dem Kopf. */
.leisten-stapel {
    position: sticky;
    top: 0;
    z-index: 20;
}

/* --- Verbindungsleiste ---------------------------------------------------- */

/* Rot, und nur bei einem echten Verbindungsverlust.

   Bis 1.2.14 war sie zweifarbig: orange für den flüchtigen Zustand
   („n Eingaben werden gespeichert …"), rot für „Keine Verbindung". Den
   flüchtigen gibt es nicht mehr — er erschien bei jedem Abhaken für einen
   Sekundenbruchteil und meldete nichts, wonach jemand handeln könnte. Übrig
   bleibt die Warnung, und eine Warnung hat genau eine Farbe. Damit ist auch
   `.leiste-schwebt` entfallen: Der Schwebe-Kniff aus 1.2.10 war ausschließlich
   dafür da, dass das Aufblitzen die Seite nicht verschiebt. Was nur bei einem
   echten Zustandswechsel erscheint, darf einmal schieben — und darf den Inhalt
   darunter auch nicht dauerhaft verdecken, läuft also im Fluss mit. */
.verbindungs-leiste {
    padding: 0.5rem 1rem;
    background: var(--fehler);
    color: #fff;
    font-size: 0.9rem;
    text-align: center;
}

/* Rückfall für Seiten ohne Leisten-Stapel: Dann klebt die Leiste selbst, so
   wie bis 1.1.13. Greift heute nirgends — alle Seiten binden view_header.php
   ein —, kostet aber nichts und verhindert, dass die Meldung bei einer neuen
   Seite ohne Kopf lautlos wegscrollt. */
.verbindungs-leiste.leiste-allein {
    position: sticky;
    top: 0;
    z-index: 20;
}

/* --- Trainingsleiste (§7.4) ----------------------------------------------- */

/* Nur bei laufender Einheit, sonst gäbe es nichts zu zählen. Dunkel wie der
   Kopf: Orange und Rot sind der Verbindungsleiste vorbehalten, und zwei
   farbige Balken übereinander wären Jahrmarkt.

   `justify-content: space-between` ist hier keine Kosmetik: Der Text rechts
   wechselt im Minutentakt zwischen „seit 9 min" und „seit 10 min" und wird
   dabei breiter. Läge alles in einem Fluss, ruckte die linke Hälfte mit —
   und was sich von selbst ändert, darf nichts verschieben (Fallstrick 19).
   So wächst die rechte Hälfte in die Lücke hinein und sonst bewegt sich
   nichts. `tabular-nums` hält zusätzlich die Ziffern gleich breit. */
.training-leiste {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.45rem 1rem;
    background: var(--schrift);
    color: #fff;
    font-size: 0.9rem;
    font-variant-numeric: tabular-nums;
}

.training-leiste .leiste-dauer { white-space: nowrap; }

/* Zwei Gruppen: „4/6 beendet · 2 übersprungen".

   Sie ergänzen sich nicht zur Anzahl der Übungen, und das ist Absicht — sie
   beantworten zwei Fragen. „beendet" ist der Fortschritt, „übersprungen" die
   Merkliste: was man ausgelassen hat und noch nachholen will. Der Rest (offen
   und noch nicht drangewesen) braucht keine Zahl; er kostete nur die Breite,
   die der Bruch jetzt bekommt.

   Der Fortschritt steht vorn (seit 1.2.16), weil man ihn dauernd abliest; die
   übersprungenen sind die Ausnahme. */
.leiste-zahlen {
    display: flex;
    align-items: baseline;
    gap: 0.35rem;
    /* NICHT umbrechen, und lieber abschneiden als wachsen: Ein Umbruch machte
       die Leiste zweizeilig und schöbe damit alles darunter — dieselbe Regel
       wie bei .zeile-wartet. Auslösen könnte ihn schon eine Zahl, die von 9 auf
       10 springt, also mitten im Training. */
    flex-wrap: nowrap;
    min-width: 0;
    overflow: hidden;
}

/* Der Trennpunkt gehört zur FOLGENDEN Gruppe, nicht zwischen die beiden — so
   verschwindet er mit ihr, statt verwaist am Rand stehen zu bleiben, falls
   einmal eine Gruppe wegfällt. */
.zahl-gruppe + .zahl-gruppe::before {
    content: "· ";
    opacity: 0.6;
}

/* Die übersprungenen sind im Normalfall 0, und dann sollen sie nichts von sich
   reden machen. Gibt es welche, trägt die Zahl die Farbe der Balken, die man in
   der Liste dazu sieht — dasselbe Signalorange, dieselbe Aussage: „steht noch
   aus". Die Klasse setzt index.php beim Aufbau und zahlenSchreiben() im
   Betrieb.

   #ff6600 auf dem dunklen Grund der Leiste (--schrift) kommt auf 5,0:1 und
   liegt damit über AA. Das ist die andere Richtung als beim Abzeichen auf
   orangem Grund (2,94:1, siehe oben bei --signal) — hier trägt der dunkle
   Hintergrund die helle Farbe. */
.training-leiste .zahl-uebersprungen { opacity: 0.7; }

.training-leiste .zahl-uebersprungen.zaehlt {
    opacity: 1;
    color: var(--signal);
}

/* --- Kurzmeldung --------------------------------------------------------- */

.meldung {
    position: fixed;
    left: 50%;
    bottom: 1.25rem;
    transform: translate(-50%, 1rem);
    max-width: calc(100vw - 2rem);
    padding: 0.6rem 1rem;
    border-radius: var(--radius);
    background: var(--schrift);
    color: #fff;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s, transform 0.15s;
}

.meldung.sichtbar { opacity: 1; transform: translate(-50%, 0); }
.meldung-fehler   { background: var(--fehler); }
.meldung-gut      { background: var(--gut); }

/* --- Groessere Bildschirme ----------------------------------------------- */

@media (min-width: 40rem) {
    h1 { font-size: 1.7rem; }
    .inhalt { padding: 1.5rem 1.5rem 4rem; }
}

@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}

.abzeichen-admin {
    background: var(--akzent);
    border-color: var(--akzent);
    color: #fff;
}

/* Gesperrtes Konto (§6.1). Dasselbe Muster wie .ist-archiviert bei den
   Übungen: gedämpft und mit Balken am linken Rand, damit die Zeile in einer
   langen Liste auch ohne Lesen des Abzeichens auffällt.

   ORANGE UND NICHT ROT, und das ist der Kern: Rot gehört in dieser Liste dem
   Löschen, und die beiden Dinge sind gegensätzlich — das eine nimmt die Daten
   mit, das andere lässt sie ausdrücklich stehen. Zwei rote Knöpfe nebeneinander
   luden dazu ein, sie für gleich gefährlich zu halten. Derselbe Ton wie die
   übersprungene Übung im Training (--signal): auffällig, aber nicht endgültig.

   Weiße Schrift wie am Knopf daneben — siehe die Notiz bei --signal. */
.abzeichen-gesperrt {
    background: var(--signal);
    border-color: var(--signal);
    color: #fff;
}

.ist-gesperrt {
    opacity: 0.75;
    border-left: 4px solid var(--signal);
}

/* Der Sperren-/Entsperren-Knopf trägt dieselbe Farbe wie das, was er auslöst.
   Beim eigenen Konto ist er deaktiviert und damit über das globale
   button:disabled ohnehin blass — es braucht dafür keine eigene Regel.

   Weiße Schrift auf Wunsch des Benutzers (2026-08-17), obwohl der Kontrast mit
   2,94:1 rechnerisch zu niedrig ist — Begründung und Messwerte oben bei
   --signal. Das Abzeichen daneben trägt dasselbe Weiß. */
button.sperr-knopf {
    background: var(--signal);
    border-color: var(--signal);
    color: #fff;
}

.reset-formular { margin-top: 0.75rem; }

/* --- Vorbelegung neuer Sätze (§7.4) --------------------------------------- */

/* Eingerückt und mit Linie: Die Wahl gehört zum Expertenmodus darüber und ist
   ohne ihn wirkungslos. `fieldset[disabled]` blendet die Bedienelemente selbst
   ab; die Blässe des ganzen Blocks kommt hier dazu, damit auch der Erklärtext
   als „gerade nicht in Kraft" zu lesen ist. */
.vorlage-wahl {
    margin: 1rem 0 0;
    padding: 0 0 0 1rem;
    border: 0;
    border-left: 3px solid var(--linie);
}

.vorlage-wahl legend { padding: 0; font-weight: 600; }
.vorlage-wahl[disabled] { opacity: 0.55; }

.vorlage-beispiel { margin-bottom: 0.25rem; }

/* Der Radiobutton oben, die Beschriftung samt Beispiel daneben — nicht
   mittig, sonst steht der Knopf bei zwei Zeilen Text in der Luft. */
.vorlage-zeile {
    align-items: flex-start;
    min-height: 0;
    padding: 0.4rem 0;
}

/* Die drei Beispielsätze. Als Flex mit Umbruch statt als Tabelle: Auf einem
   schmalen Gerät rutscht „Satz 3" dann unter die anderen beiden, statt die
   Karte seitlich zu sprengen. Feste Mindestbreite, damit die zwei Zeilen
   untereinander bündig stehen und der Unterschied ins Auge springt. */
.vorlage-saetze {
    display: flex;
    flex-wrap: wrap;
    gap: 0.15rem 0.9rem;
    margin-top: 0.15rem;
    font-variant-numeric: tabular-nums;
}

.vorlage-saetze > span { min-width: 5.5rem; }
.vorlage-saetze .matt { margin-right: 0.2rem; font-size: 0.85em; }

/* --- Handy-Ansicht ------------------------------------------------------- */

.einheit-laeuft { border-left: 4px solid var(--gut); }

.fortschritt { font-size: 1.1rem; font-weight: 600; margin: 0.5rem 0; }

/* Seit 1.2.10 das ERSTE Element des Auswahlkastens (vorher stand sie unter
   Knopf und Erklärung), deshalb ohne eigenen Abstand nach oben — das Polster
   der Karte genügt. */
.plan-wahl {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.plan-wahl a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tippziel);
    padding: 0.3rem 0.9rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    background: var(--grund);
    color: var(--schrift);
    text-decoration: none;
}

.plan-wahl a.aktiv {
    background: var(--akzent);
    border-color: var(--akzent);
    color: #fff;
    font-weight: 600;
}

/* Das Bild ist antippbar — der Knopf darf aber nicht wie einer aussehen.
   Gilt auf allen drei Seiten mit Übungsbildern: Training, Übungs- und
   Planverwaltung. */
.bild-knopf {
    padding: 0;
    background: none;
    border: 0;
    min-height: 0;
    /* Der Zeiger kündigt an, was der Klick tut — Gegenstück zum zoom-out auf
       dem großen Bild im Dialog. */
    cursor: zoom-in;
}

/* .bild-knopf:hover ebenfalls oben, siehe @media (hover: hover). */

.letzter-wert { margin: 0.2rem 0 0.6rem; font-size: 0.9rem; }
/* Die Satzliste vom letzten Mal in einer eigenen Zeile unter "zuletzt 3 Sätze
   am …" (seit 1.5.3). */
.letzter-wert-liste { display: block; }

/* Tauschen — Gewicht — Erledigt in einer Zeile. Auf sehr schmalen Geräten
   bricht sie um; die Reihenfolge bleibt dabei erhalten. */
.position-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
}

/* Das Gewichtsfeld nahm als Vollbreiten-Eingabe mehr Platz ein als beide
   Aktionen zusammen — gebraucht werden drei bis vier Zeichen. */
.wert-feld {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    flex: none;
}

.wert-feld input[type="text"] {
    width: 4.5rem;
    text-align: right;
    padding-inline: 0.5rem;
}

.wert-einheit { color: var(--schrift-matt); font-size: 0.9rem; }

/* Der Tausch ist die seltenere Aktion und darf sich schmal machen. */
.position-aktionen .tauschen { padding: 0.5rem 0.7rem; }

/* Ab dieser Breite steht die Zeile sicher nebeneinander — dann drei Spalten,
   die beiden äußeren gleich breit: „Tauschen" links, „Erledigt" rechts. Mit
   flex klebte alles am linken Rand. Darunter bleibt es beim umbrechenden
   Flex-Fluss: Ein Grid bricht nicht um, die Zeile liefe sonst aus der Karte
   heraus.

   DIE MITTLERE SPALTE IST LEER — und bleibt es absichtlich. Bis 1.4.2 stand
   dort im einfachen Modus das Gewichtsfeld; seit es den Modus nicht mehr gibt
   (§7.4), steht das Gewicht in jedem Satz. Das Raster bleibt trotzdem
   dreispaltig: Eine leere `auto`-Spalte ist 0 px breit und kostet nichts, hält
   aber den Platz für eine spätere Angabe zwischen den beiden Knöpfen frei
   (Wunsch des Benutzers, 2026-08-30). Wer dort etwas einsetzt, gibt ihm
   `justify-self: center` — mehr braucht es nicht. */
@media (min-width: 24rem) {
    .position-aktionen {
        display: grid;
        grid-template-columns: 1fr auto 1fr;
    }

    /* BEIDE Spalten ausdrücklich zuweisen, nicht der automatischen Platzierung
       überlassen. Das ist der Punkt, an dem die leere Mitte kippt: Ein Grid
       füllt seine Spalten der Reihe nach, und mit nur zwei Kindern landet das
       Häkchen in Spalte 2 statt 3. `justify-self: end` richtet es dann brav am
       rechten Rand SEINER Spalte aus — und die ist die mittlere. Genau so stand
       „Erledigt" in 1.4.3 auf einmal neben „Tauschen" statt rechts.

       Wer die mittlere Spalte später füllt, gibt dem Element `grid-column: 2`
       und `justify-self: center`. */
    .position-aktionen .tauschen      { grid-column: 1; justify-self: start; }
    .position-aktionen .erledigt-wahl { grid-column: 3; justify-self: end; }

    /* Der Wiederholen-Knopf erscheint nur im Fehlerfall und bekommt dann eine
       eigene Zeile unter den dreien. */
    .position-aktionen .wiederholen {
        grid-column: 1 / -1;
        justify-self: start;
    }
}

/* Die Pace steht unter der Intervallliste, aus der sie entsteht. Sie ist IMMER da —
   auch als „—" —, damit die Karte beim Tippen nicht wächst und schrumpft
   (Fallstrick 19a). Tabellenziffern, weil sich beim Tippen nur einzelne
   Stellen ändern und die Zeile sonst zappelt. */
.pace-zeile {
    margin: 0 0 0.6rem;
    font-variant-numeric: tabular-nums;
}

/* Das Häkchen ist die Hauptaktion der Seite — entsprechend groß, und es
   nimmt den Rest der Zeile ein, damit es am rechten Rand steht. */
.erledigt-wahl {
    flex: 1 1 auto;
    justify-content: flex-end;
    /* label bringt global 0.75rem Abstand nach oben mit — in einer Zeile mit
       Knopf und Eingabefeld zieht das die Zeile grundlos auseinander. */
    margin: 0;
    font-weight: 600;
}

.erledigt-wahl input[type="checkbox"] {
    width: 1.6rem;
    height: 1.6rem;
}

.wiederholen { background: var(--warnung); }

/* --- Sätze (Expertenmodus) ------------------------------------------------ */

.saetze-block { margin: 0 0 0.6rem; }

/* Der Satzkopf ist ein .summary-knopf, aber ein leiser: Acht Übungskarten mit
   je einem vollflächig blauen Balken ergäben eine Seite, auf der nichts mehr
   hervorsticht. Der Aufklapp-Marker bleibt, die Farbe geht zurück — kräftig
   ist stattdessen „+ Satz", der Knopf, den man zwischen zwei Sätzen ohne
   Hinsehen trifft.

   ZWEI Klassen im Selektor, und das ist kein Zufall: `.summary-knopf` steht
   weiter unten in dieser Datei und hat dieselbe Spezifität. Mit nur
   `.saetze-kopf` gewänne die spätere Regel, und der Kopf bliebe blau — genau
   das war bis 1.1.0 der Fall. */
.saetze-block > .saetze-kopf {
    display: flex;
    width: 100%;
    background: var(--grund-tief);
    color: var(--schrift);
    border: 1px solid var(--linie);
    font-weight: 500;
}

.saetze-zusammenfassung { font-variant-numeric: tabular-nums; }

.satz-liste {
    list-style: none;
    margin: 0 0 0.5rem;
    padding: 0;
}

/* Eine Satzzeile muss auf ein schmales Handy passen, ohne dass ein Tippziel
   unter die Fingerkuppe schrumpft. Die Rechnung, damit sie nachvollziehbar
   bleibt — verfügbar sind Fensterbreite minus 66 px (je 1 rem Polster an
   `.inhalt` und an der Karte, plus deren Rahmen):

     Nummer 20 + Stepper 142 + „×" 12 + Feld 81 + Löschen 44 + Abstände 16 = 315

   Das passt ab 400 px Fensterbreite. Darunter entfallen gestaffelt die reinen
   Beschriftungen (siehe die beiden Media Queries unten) — die Knöpfe bleiben in
   jedem Fall bei voller Größe, sie sind der Grund für die ganze Rechnung. */
.satz-zeile {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.15rem 0;
}

.satz-nr {
    width: 1.25rem;
    color: var(--schrift-matt);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.satz-mal { color: var(--schrift-matt); }

/* −  12  +  als eine zusammenhängende Einheit. Die Wiederholungen ändern sich
   fast immer nur um ±1 gegenüber dem Vorschlag — dafür sind zwei große Ziele
   schneller und sicherer als die Zifferntastatur mit feuchten Fingern. Die
   Zahl selbst bleibt ein Eingabefeld, für den Sprung von 12 auf 25. */
/* Der Abstand ist kein Zierrat, sondern macht Platz fuer den Fokusrahmen des
   Zahlenfeldes. Ohne ihn lag der Rahmen ueber dem Minus- und unter dem
   Plus-Knopf -- die Malreihenfolge entscheidet das, nicht die Absicht. */
.stepper {
    display: flex;
    align-items: center;
    gap: 3px;
    flex: none;
}

/* Und damit die 3px reichen, liegt der Rahmen hier eng am Feld statt mit den
   ueblichen 2px Abstand. */
.stepper input:focus-visible { outline-offset: 0; }

.stepper button {
    min-width: var(--tippziel);
    min-height: var(--tippziel);
    padding: 0;
    font-size: 1.3rem;
    line-height: 1;
}

.stepper input[type="text"] {
    width: 3rem;
    text-align: center;
    padding-inline: 0.2rem;
    border-radius: 0;
    font-variant-numeric: tabular-nums;
}

.satz-zeile .wert-feld { flex: none; }

.satz-zeile .wert-feld input[type="text"] { width: 3.5rem; }

.satz-weg {
    min-width: var(--tippziel);
    min-height: var(--tippziel);
    margin-left: auto;
    padding: 0;
    color: var(--fehler);
    font-size: 1.1rem;
    line-height: 1;
}

/* Der Hauptknopf des Modus: ein Tipp je Satz, und im Text steht schon, was
   dabei herauskommt. Über die volle Breite, weil man ihn zwischen zwei Sätzen
   ohne Hinsehen treffen können muss. */
.satz-hinzu {
    width: 100%;
    justify-content: center;
    font-variant-numeric: tabular-nums;
}

/* Eine Intervallzeile (Ausdauer, 1.4.0) ist deutlich schmaler als die
   Satzzeile darüber — der Stepper entfällt, es bleiben zwei Textfelder:

     Nummer 20 + Feld 88 + „in" 16 + Feld 80 + Löschen 44 + Abstände 16 = 264

   Das passt ab 330 px und damit auf jedem Gerät, das die Kraftzeile trägt. Die
   beiden Media Queries unten greifen trotzdem mit; sie blenden nur
   Beschriftungen aus, und was dort für „×" gilt, gilt hier für „in". */
.zeile-ausdauer .satz-distanz { width: 4rem; }

.zeile-ausdauer .satz-dauer { width: 3.8rem; text-align: right; }

.satz-in { color: var(--schrift-matt); font-size: 0.9rem; }

/* Abgehakt heißt festgeschrieben (§7.4). Die Knöpfe sind dann `disabled` und
   damit über `button:disabled` schon blass; die Eingabefelder brauchen einen
   eigenen Hinweis, sonst sähen sie weiter aus wie Eingabefelder und man tippt
   vergeblich hinein. Gedämpfter Grund statt Ausgrauen der Schrift: Die Werte
   sollen lesbar bleiben — man schaut sie ja an. */
.satz-zeile input[readonly] {
    background: var(--grund-tief);
    color: var(--schrift-matt);
    border-color: var(--linie);
    cursor: default;
}

/* Bis 400 px (die meisten Handys im Hochformat) fallen „×" und „kg" weg: reine
   Beschriftungen, die keine Information tragen, welche nicht auch aus der
   Anordnung hervorginge. Spart rund 41 px, macht 274 — passt ab 340 px. */
@media (max-width: 25rem) {
    .satz-mal,
    .satz-in,
    .satz-zeile .wert-einheit { display: none; }
}

/* Unter 352 px (die schmalsten Geräte) geht zusätzlich die Satznummer. Sie
   steht als Reihenfolge ohnehin in der Liste. Macht 250 — passt ab 316 px.
   Die aria-label der Felder nennen Nummer, Einheit und Bedeutung weiterhin,
   für alle drei Stufen gleich. */
@media (max-width: 22rem) {
    .satz-nr { display: none; }
}

/* --- Dialoge ------------------------------------------------------------- */

dialog {
    width: min(32rem, calc(100vw - 2rem));
    max-height: 85vh;
    padding: 1rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    background: var(--grund);
    color: var(--schrift);
}

dialog::backdrop { background: rgba(0, 0, 0, 0.5); }

dialog h2 { margin-top: 0; }

#info-bild {
    display: block;
    width: 100%;
    height: auto;
    max-height: 50vh;
    object-fit: contain;
    border-radius: var(--radius);
    background: var(--grund-tief);
    /* Ein Tipp darauf schließt den Dialog (index.js) — der Zeiger sagt es an. */
    cursor: zoom-out;
}

/* --- Übungsauswahl (Planverwaltung) -------------------------------------- */

/* Die Trefferliste kann den ganzen Bestand umfassen. Deshalb scrollt sie in
   sich, statt den Dialog zu sprengen: Filterzeile und Schließen-Knopf bleiben
   sichtbar, sonst müsste man erst ans Ende blättern, um weiterzufiltern.

   Das [open] ist Pflicht und keine Zierde: Ein geschlossener <dialog> ist nur
   über das Standard-Stylesheet unsichtbar, und ein Autoren-`display: flex`
   schlägt das — der Dialog stünde dauerhaft offen in der Seite. Derselbe
   Fallstrick wie bei [hidden] ganz oben. */
#waehlen-dialog[open] {
    display: flex;
    flex-direction: column;
}

#waehlen-liste {
    overflow-y: auto;
    /* Der Rest des Dialogs (Titel, Filter, Fußzeile) belegt den Vorrang; die
       Liste bekommt, was übrig bleibt. */
    min-height: 4rem;
}

.waehlen-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.75rem;
}

/* Am Handy untereinander über die volle Breite — zwei halbe Auswahlfelder
   nebeneinander sind mit dem Daumen nicht zu treffen. */
.waehlen-filter > span { flex: 1 1 12rem; }
.waehlen-filter select { width: 100%; }

/* Der Gerätefilter im Tauschdialog (Training). Volle Breite: Im Studio wird er
   mit dem Daumen bedient, oft einhändig. */
.tausch-filter { margin: 0 0 0.75rem; }

.vorschlag {
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--linie);
}

.vorschlag:last-of-type { border-bottom: 0; }

/* Bild links, Text rechts — dieselbe Anordnung wie in der Übungsliste. */
.vorschlag-kopf {
    display: flex;
    gap: 0.6rem;
    align-items: flex-start;
}

.vorschlag-text { min-width: 0; }

.vorschlag-bild {
    width: 72px;
    height: 72px;
    flex: none;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--grund-tief);
}

.vorschlag-bild-leer {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px dashed var(--linie);
    color: var(--schrift-matt);
}

/* Die Knopfzeile steht RECHTS, der Text darüber links. Zwei Gründe: Am Handy
   liegt der Daumen am rechten Rand, und eine linksbündige Knopfzeile unter
   linksbündigem Text verschwimmt mit ihm zu einem Block — rechts gesetzt ist
   sofort erkennbar, wo die Karte aufhört und die Handlung anfängt.

   Gilt für ALLE drei Listen, die vorschlagMarkup() erzeugt: beide
   Tauschdialoge und die Übungsauswahl. Sie teilen sich das Markup bewusst
   (siehe assets/app.js); eine Liste, die anders ausgerichtet ist als die
   andere, sähe nach zwei verschiedenen Bausteinen aus. */
.vorschlag-knoepfe {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.4rem;
    margin: 0.4rem 0 0;
}

/* Übungsauswahl: „Schon in Ganzkörper A" DIREKT links neben dem Knopf (§6.4).
   Bewusst kein margin-right: auto — an den linken Rand geschoben las sich der
   Hinweis wie eine Zeile für sich und nicht wie die Auskunft zu genau diesem
   Knopf. Er läuft also in derselben rechtsbündigen Zeile mit; wird es am Handy
   zu eng, bricht sie um, und der Hinweis steht direkt über dem Knopf.
   Zurückgenommen wie jeder Nebentext — er soll die Wahl erleichtern, nicht den
   Knopf überschreien. */
.im-split-hinweis {
    align-self: center;
    color: var(--schrift-matt);
    font-size: 0.85rem;
}

/* --- Platzhalter-Seiten --------------------------------------------------- */

/* Geplante, noch nicht gebaute Menüpunkte: sichtbar, aber erkennbar
   zurückgenommen — sonst sucht man dort Funktionen, die es nicht gibt. */
.kopf a.geplant { opacity: 0.6; }
.kopf a.geplant span { font-size: 0.8em; }

.platzhalter { border-left: 4px solid var(--warnung); }

.platzhalter-marke {
    display: inline-block;
    margin: 0 0 0.5rem;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
    background: var(--warnung);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.platzhalter-liste { margin: 0.5rem 0 0; }
.platzhalter-liste dt { font-weight: 600; margin-top: 0.6rem; }
.platzhalter-liste dd { margin: 0.1rem 0 0; color: var(--schrift-matt); }

/* Schwerpunkt innerhalb der Primärgruppe („oben", „stehend").
   Bewusst anders als die Muskelgruppen-Abzeichen: Er ist Information, keine
   Tauschklasse — das soll man auf einen Blick auseinanderhalten. */
.schwerpunkt {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: 999px;
    border: 1px dashed var(--akzent);
    color: var(--akzent-tief);
    background: transparent;
    font-size: 0.8rem;
}

/* --- Aufklappbare Bereiche ----------------------------------------------- */

/* Das <summary> sah bisher aus wie Text, obwohl es das Hauptbedienelement der
   Seite ist — und auf der Muskelgruppen-Seite war der Bereich gar nicht
   zuklappbar. Beides jetzt einheitlich und wie ein Knopf. */
.summary-knopf {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: var(--tippziel);
    padding: 0.5rem 1rem;
    border-radius: var(--radius);
    background: var(--akzent);
    color: #fff;
    font-weight: 600;
    cursor: pointer;
    list-style: none;
}

.summary-knopf::-webkit-details-marker { display: none; }

.summary-knopf::before {
    content: "+";
    font-size: 1.15rem;
    line-height: 1;
}

details[open] > .summary-knopf { margin-bottom: 0.5rem; }
details[open] > .summary-knopf::before { content: "−"; }

/* .summary-knopf:hover ebenfalls oben, siehe @media (hover: hover). */

.summary-knopf:focus-visible {
    outline: 2px solid var(--akzent);
    outline-offset: 2px;
}

/* --- Muskelgruppen-Baum --------------------------------------------------- */

.untergruppen-halter { list-style: none; }

.untergruppen {
    margin-left: 1.5rem;
    border-left: 2px solid var(--linie);
    padding-left: 0.75rem;
}

.gruppe-haupt { border-left: 4px solid var(--akzent); }
.gruppe-unter { background: var(--grund-tief); }

/* Untergruppen als Baum unter ihrer Hauptgruppe.
 *
 * Gezeichnet mit Rändern, nicht mit den Zeichen ├ und └: Die springen je nach
 * Schriftart in Breite und Höhe und sitzen nie sauber auf der Linie. Ränder
 * sind pixelgenau und skalieren mit der Schriftgröße mit.
 *
 * Die Linien liegen absolut positioniert links in der ersten Rasterspalte —
 * dadurch verschieben sie die Ankreuzfelder NICHT, und die Spalten bleiben
 * unter ihren Überschriften „primär" und „sekundär" stehen. */
.gruppen-zeile-unter { position: relative; }

/* Senkrechte Linie, läuft durch die ganze Zeile. */
.gruppen-zeile-unter::before {
    content: "";
    position: absolute;
    left: 0.7rem;
    top: 0;
    bottom: 0;
    border-left: 2px solid var(--linie);
}

/* Bei der letzten Untergruppe endet sie auf halber Höhe — das ergibt das ⌐,
   das einen Ast abschließt. */
.gruppen-zeile-letzte::before { bottom: 50%; }

/* Waagerechter Abzweig zur Zeile, auf halber Höhe. */
.gruppen-zeile-unter::after {
    content: "";
    position: absolute;
    left: 0.7rem;
    top: 50%;
    width: 0.85rem;
    border-top: 2px solid var(--linie);
}

.gruppen-zeile-unter label { color: var(--schrift-matt); }

/* Thumbnail in der Planverwaltung.

   112px seit 1.2.21, vorher 72 — und die Zahl ist keine Kosmetik, sondern der
   Grund, warum sie überhaupt auffiel: 72px sind knapp drei Textzeilen, der
   Textblock daneben hat aber vier (Name, englischer Name, Muskelgruppen,
   Gerät/Ausführung). Das Bild stand also mittig in einer Zeile, die es nicht
   ausfüllte. Der Wert stammte aus 1.0.12, als das Bild noch BEIDE Zeilen der
   Position trug, die Knopfzeile eingeschlossen.

   112px ist dieselbe Größe wie in der Übungsverwaltung (.uebung-bild) und
   bleibt unter den 150px der Trainingsansicht — dort sucht man am Gerät nach
   dem Bild, hier baut man den Plan. Möglich wurde sie durch 1.2.19: Seit die
   Positionsnummer in der Ecke sitzt statt in einer eigenen Spalte, ist die
   Breite dafür da. */
.position-bild {
    width: 112px;
    height: 112px;
    flex: none;
    object-fit: cover;
    border-radius: var(--radius);
    background: var(--grund-tief);
}

.position-bild-leer {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px dashed var(--linie);
    color: var(--schrift-matt);
}

/* Hauptgruppe als Gliederungsüberschrift in der Übungsmaske.
   Sie ist nicht wählbar — die Tauschklasse ergibt sich aus der Untergruppe.
   Deshalb hat sie keine Bedienelemente und sieht bewusst anders aus. */
.gruppen-ueberschrift {
    /* Kein linker Abstand: Der Name steht über dem Linienanfang, so wie der
       Kasten in der Vorlage über der senkrechten Linie sitzt. */
    margin: 0.75rem 0 0.2rem;
    padding-bottom: 0.15rem;
    border-bottom: 1px solid var(--linie);
    font-weight: 700;
    font-size: 0.95rem;
    letter-spacing: 0.02em;
    color: var(--schrift);
}

.gruppen-ueberschrift:first-of-type { margin-top: 0.25rem; }

.hinweis-tausch {
    padding: 0.5rem 0.7rem;
    border-left: 3px solid var(--akzent);
    background: var(--grund-tief);
    border-radius: 0 var(--radius) var(--radius) 0;
}

/* --- Trainingshistorie --------------------------------------------------- */

.einheit-karte details > summary,
.karte details > summary { cursor: pointer; }

.einheit-kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem 0.75rem;
    min-height: var(--tippziel);
    padding: 0.2rem 0;
    list-style: none;
}

.einheit-kopf::-webkit-details-marker { display: none; }

/* Aufklapp-Zeichen selbst setzen — das Standard-Dreieck sitzt je nach Browser
   an anderer Stelle und bricht die Ausrichtung der Zeile. */
.einheit-kopf::before {
    content: "▸";
    color: var(--schrift-matt);
    flex: none;
}

details[open] > .einheit-kopf::before { content: "▾"; }

.einheit-datum { font-variant-numeric: tabular-nums; }
.einheit-plan  { font-weight: 600; }
.einheit-eckdaten { margin-left: auto; font-size: 0.9rem; }

.verlauf-tabelle {
    width: 100%;
    border-collapse: collapse;
    margin: 0.5rem 0;
}

.verlauf-tabelle th,
.verlauf-tabelle td {
    padding: 0.35rem 0.25rem;
    border-bottom: 1px solid var(--linie);
    text-align: left;
}

.verlauf-tabelle th {
    font-size: 0.85rem;
    color: var(--schrift-matt);
    font-weight: 600;
}

.verlauf-tabelle tr:last-child td { border-bottom: 0; }

/* Zahlen rechtsbündig und mit gleichen Ziffernbreiten — sonst springt die
   Spalte bei 9,5 gegenüber 62,5. */
.spalte-zahl {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.verlauf-kurve-halter {
    flex: 0 1 6rem;
    min-width: 3rem;
    color: var(--akzent);
}

.verlauf-kurve {
    display: block;
    width: 100%;
    height: 1.75rem;
}

/* Die Satzfolge ist der längste Zellinhalt der Tabelle und darf sich schmal
   machen — sie steht dicht neben ihrem Datum und wird nicht überflogen,
   sondern gelesen. */
.satz-spalte {
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

/* Die Sätze standen bis 2026-08-17 als eine Zeile mit `white-space: nowrap`
   ("12×40 · 10×40 · 9×45"). Neben Datum, Volumen, 1RM und Gewicht sprengte das
   auf einem Pixel 10 Pro XL die Breite, und die Tabelle rollte seitwärts.

   Zwei nebeneinander, der Rest in weiteren Zeilen — das hält die Spalte schmal
   und bleibt lesbar, weil jeder Satz für sich steht. `max-content` statt `1fr`,
   damit die Spalten sich an den Zahlen ausrichten und nicht die Zelle aufziehen.

   Das Gitter sitzt auf einem <span> IM <td>, nicht auf dem <td> selbst: `display:
   grid` auf einer Tabellenzelle nimmt ihr das `table-cell`-Verhalten, und die
   Spalten der Tabelle richten sich dann nicht mehr aneinander aus. */
.satz-gitter {
    display: grid;
    grid-template-columns: repeat(2, max-content);
    gap: 0.15rem 0.55rem;
}

/* Auf dem Desktop ist die Breite da — dort stehen vier nebeneinander, sonst
   wird die Zelle unnötig hoch. */
@media (min-width: 40rem) {
    .satz-gitter { grid-template-columns: repeat(4, max-content); }
}

/* Das Datum trägt in der Übungstabelle ein zweistelliges Jahr (siehe
   `format_datum_kurz()`). Tag und Uhrzeit sind zwei Elemente, nicht ein String:

   Am Handy steht die Uhrzeit UNTER dem Datum — nebeneinander war die Spalte dort
   die breiteste der fünf und drückte die Sätze zusammen. Ein erzwungener Umbruch
   mitten in "17.08.26 14:30" wäre schlechter gewesen als beides sauber getrennt.

   Ab 40rem ist die Breite da, dort stehen sie wieder nebeneinander — sonst wird
   jede Zeile der Tabelle ohne Grund doppelt so hoch. */
/* Die Pace-Zelle traegt zwei Angaben untereinander (km/h ueber min/km) --
   dasselbe Muster wie Datum und Uhrzeit darunter, und aus demselben Grund: Als
   zwei Spalten waeren es sechs, und bei fuenf ist die Breite hier schon einmal
   knapp geworden. Anders als beim Datum bleiben sie AUCH auf breiten Schirmen
   untereinander: "10,9 km/h 5:30 /km" in einer Zeile liest sich wie eine
   einzige krumme Angabe. */
.pace-spalte { font-variant-numeric: tabular-nums; }

.pace-kmh, .pace-jekm { display: block; white-space: nowrap; }

.pace-jekm { color: var(--schrift-matt); }

/* In der Einheiten-Ansicht steht die Pace dagegen in EINER Zeile: Dort ist sie
   eine Zelle unter vielen, und die Zeilen der Tabelle sollen gleich hoch
   bleiben. Das Format kommt aus pace_text(). */
.pace-zelle { white-space: nowrap; }

.datum-spalte {
    font-variant-numeric: tabular-nums;
}

.datum-tag,
.datum-zeit {
    display: block;
    white-space: nowrap;
}

.datum-zeit { color: var(--schrift-matt); }

@media (min-width: 40rem) {
    .datum-tag,
    .datum-zeit { display: inline; }
}

/* Netz für den Rest: sehr viele Sätze oder ein sehr schmales Gerät können die
   fünf Spalten weiterhin sprengen. Dann rollt die Tabelle in ihrem eigenen
   Kasten; die Seite selbst bleibt fest. */
.tabelle-rollt { overflow-x: auto; }

/* Volumen- und 1RM-Kurve stehen im aufgeklappten Bereich, jede mit ihrer
   Beschriftung davor — ohne sie wären zwei gleich aussehende Linien
   nicht auseinanderzuhalten. */
.kurven-zeile {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.35rem 0;
}

.kurve-titel {
    flex: none;
    min-width: 8.5rem;
    font-size: 0.85rem;
    color: var(--schrift-matt);
}

/* In der Zusammenfassungszeile teilt sich die Kurve den Platz mit Name und
   Eckdaten und bleibt deshalb schmal. Hier hat sie die Zeile für sich. */
.kurven-zeile .verlauf-kurve-halter { flex: 1 1 auto; }

.diff-plus  { color: var(--gut); font-weight: 600; }
.diff-minus { color: var(--warnung); font-weight: 600; }

/* --- Wartungsseite -------------------------------------------------------- */

.status-gitter {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
    gap: 0.5rem;
    margin-bottom: 1rem;
}

.status-karte {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.6rem 0.75rem;
    background: var(--grund);
    border: 1px solid var(--linie);
    border-radius: var(--radius);
}

.status-wert {
    font-size: 1.15rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.status-karte .matt { font-size: 0.85rem; }

.aktions-zeile {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.75rem 0 0;
}

/* Dauerhafte Meldung — anders als der Toast, der nach Sekunden verschwindet.
   Ein Wartungsergebnis will man in Ruhe lesen können. */
.melde-gut    { border-left: 4px solid var(--gut);    background: #f2faf5; }
.melde-fehler { border-left: 4px solid var(--fehler); background: #fdf3f2; }

/* Der Knopf einer Pflege-Zeile steht IMMER unter dem Text, nie daneben.
   Vorher entschied darüber die Länge des Textes: Ein Knopf ist inline-flex,
   also floss er hinter den letzten Satz — bei „WAL zurückschreiben" blieb dort
   Platz, bei den anderen dreien nicht, und die Liste sah aus, als folgte sie
   keiner Regel. Gemeldet am 2026-08-26.

   Gelöst wird das im MARKUP (der Knopf steht in einem eigenen <p>) und nicht
   hier über den Layout-Modus. Der naheliegende Griff — dem <dd> eine
   Flex-Spalte zu geben — war schon eingebaut und wieder falsch: In einem
   Flex-Container wird JEDES Element ein eigenes Element der Spalte, also auch
   ein <code> mitten im Satz. „Sinnvoll, wenn die -wal-Datei groß ist" stand
   dadurch auf drei Zeilen, mit `-wal` allein in der mittleren. */
.platzhalter-liste dd > p { margin: 0.4rem 0 0; }

#verwaiste-liste { margin: 0.4rem 0; }
#verwaiste-liste li { padding: 0.15rem 0; overflow-wrap: anywhere; }

.einheit-aktionen {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.75rem 0 0;
}

/* Nachträgliche Korrektur einer Einheit (§7.8, 1.5.1). Die Satzzeilen sind
   dieselben Bausteine wie im Training, nur ohne Stepper — die Wiederholungen
   stehen deshalb in einem gewöhnlichen Wertfeld, schmaler als das Gewicht. */
.korrektur-position .uebung-text { margin-bottom: 0.4rem; }

.korrektur-position .satz-zeile .satz-reps { width: 3rem; }

.korrektur-position .satz-hinzu { margin-top: 0.25rem; }

/* --------------------------------------------------------------------------
   Workout-Splits (§6.4, §7.6)

   Zwei Listen auf splits.php, und der optische Unterschied trägt die
   Fachlichkeit: Eigene Splits sind der Bestand, auf dem trainiert wird — der
   aktive darunter ist grün markiert, aus demselben Grund wie .zeile-aktiv im
   Training. Vorlagen sind Katalog und deshalb ruhig.
   -------------------------------------------------------------------------- */

/* Sekundärer Knopf als LINK — button.leise geht dafür nicht, der Selektor
   trifft nur <button>. Dieselbe Optik, damit „Pläne bearbeiten“ neben
   „Umbenennen“ nicht wie eine andere Art von Bedienelement aussieht. */
.knopf.zweit {
    background: var(--grund);
    color: var(--schrift);
    border-color: var(--linie);
}

.split-kopf { align-items: center; }

/* Der Kartenkopf IST der Titel der Karte — bei mehreren Splits zugleich das
   Feld, mit dem man zwischen ihnen wechselt (seit 1.3.2). Etwas kräftiger als
   ein gewöhnliches Formularfeld, damit er als Überschrift gelesen wird und
   nicht als Eingabe; die 16px aus der select-Grundregel bleiben, sonst zoomt
   iOS beim Antippen. */
.split-wechsel { font-weight: 600; }

.split-titel {
    font-size: 1.05rem;
    /* Auf der Höhe des Auswahlfelds, damit eine Karte ohne Geschwister nicht
       flacher steht als eine mit. */
    display: flex;
    align-items: center;
    min-height: var(--tippziel);
}

.split-kopf .abzeichen {
    background: var(--gut);
    border-color: var(--gut);
    color: #fff;
}

/* Grün = hier bist du, wie beim Balken der Übungskarte. */
.split.ist-aktiv { border-left: 4px solid var(--gut); }

.hinweis-vorlage {
    border-left: 4px solid var(--schrift-matt);
    background: var(--grund-tief);
}

/* Welcher Split gerade gilt, steht klein unter der Planauswahl — es ist eine
   Einordnung und keine Aktion, deshalb kein Knopf. */
.split-zeile { margin-top: 0.5rem; }

/* Die Abschlusszeile der Splitkarte: links das Aneignen, rechts das
   Abschließende, dazwischen die ganze Kartenbreite. Sie ist ein EIGENER
   Behälter und nicht das Ende der Reihe darüber — nur so bleibt die Lage der
   Knöpfe gleich, egal wie die anderen umbrechen.

   Was drinsteht, hängt von den Rechten ab (siehe splits.php):

     eigener Split           [Löschen]
     Vorlage als Admin       [Zu mir kopieren]            [Löschen]
     Vorlage als Benutzer    [Zu mir kopieren]           [Als Text]

   Deshalb `:last-child` und nicht ein Knopf beim Namen: Rechts steht immer
   der LETZTE der Zeile, und das ist in allen drei Fällen der richtige. Steht
   nur einer da, schiebt ihn dasselbe `auto` an den rechten Rand — genau so
   soll „Löschen" allein stehen.

   `justify-content: space-between` täte das nicht: Es zöge einen einzelnen
   Knopf nach links. Wer hier einen vierten Knopf ergänzt, muss wissen, dass
   er damit den rechten Platz übernimmt. */
.split-abschluss { margin-top: 0.4rem; }
.split-abschluss > :last-child { margin-left: auto; }

/* --- Herkunft eines Splits (§6.4) ----------------------------------------- */

/* Ein gerahmter Kasten zwischen den beiden Knopfreihen der Splitkarte.

   Bis 1.2.12 war es eine Zeile ÜBER den Knopfreihen, gleich unter der
   Planvorschau — sie las sich dadurch wie eine Angabe zum Split und stand mit
   voller Schriftgröße im Weg. Sie ist aber eine Einstellung, die man selten
   anfasst (Rückmeldung 2026-08-23). Also: nach unten, Rahmen und eigener Grund
   als Zeichen „gesonderte Funktion", Beschriftung klein.

   NACH UNTEN heißt nicht ans Ende. In `1.2.13` stand der Kasten hinter der
   Abschlusszeile und schob „Löschen" mitten in die Karte; seit `1.2.14` sitzt
   er davor. Die Abschlusszeile bleibt die letzte Zeile — dort steht rechts der
   rote Knopf, und der ist nur so lange unverwechselbar, wie nichts darunter
   kommt.

   HALBIERT IST DIE ÜBERSCHRIFT, NICHT DAS AUSWAHLFELD, und das ist keine
   Nachlässigkeit: Ein <select> unter 16 px lässt iOS beim Antippen in die
   Seite zoomen (siehe die Formularregel ganz oben). Die 16 px sind also
   gesetzt und bleiben; leise wird das Feld über Grund und Rahmen, nicht über
   die Schriftgröße.

   Das Feld liegt auf `--grund`, der Kasten auf `--grund-tief`: Auf gleichem
   Grund verschwämme das Feld mit dem Kasten, und man sähe nicht mehr, dass es
   eines ist. */
.split-herkunft {
    /* Luft nach beiden Seiten: Der Kasten steht zwischen zwei Knopfreihen und
       darf an keiner kleben — sonst liest er sich als deren Fortsetzung. */
    margin: 0.75rem 0 0.6rem;
    padding: 0.5rem 0.6rem 0.6rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    background: var(--grund-tief);
}

.split-herkunft label {
    display: block;
    margin: 0;
    font-weight: normal;
}

/* Die Überschrift steht in einer eigenen Zeile über dem Feld — nicht daneben:
   Nebeneinander bliebe für das Feld am Handy nur ein Rest, und die Auswahl ist
   das, worum es hier geht. */
.split-herkunft-titel {
    display: block;
    margin-bottom: 0.3rem;
    color: var(--schrift-matt);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.split-herkunft select {
    width: 100%;
    background: var(--grund);
}

/* Der Knopf steht UNTER dem Feld und über die ganze Breite. Daneben passte er
   am Handy ohnehin nicht in dieselbe Zeile, und ein Knopf, der mal neben und
   mal unter dem Feld sitzt, sieht nach Zufall aus.

   Die Schrift ist kleiner, die Höhe nicht: var(--tippziel) aus der
   Knopfregel bleibt unangetastet — dezent heißt leiser, nicht schwerer zu
   treffen. */
.split-herkunft button {
    width: 100%;
    margin-top: 0.4rem;
    font-size: 0.85rem;
}

/* --- Split als Text (§6.4) ------------------------------------------------ */

/* Das [open] ist Pflicht und keine Zierde — dieselbe Falle wie bei
   #waehlen-dialog weiter oben: Ein geschlossener <dialog> ist nur über das
   Standard-Stylesheet unsichtbar, jede Autorenregel schlägt das, und der
   Dialog stünde dauerhaft offen in der Seite. */
#text-dialog[open] {
    display: flex;
    flex-direction: column;
}

/* Feste Laufweite: Der Text ist eine Liste untereinander, und in einer
   Proportionalschrift franst sie aus. Sie ist außerdem die Vorschau auf das,
   was nachher im Chatfenster steht — dort steht es ebenfalls monospaced.

   Kein `white-space: nowrap`: Ein langer Übungsname soll umbrechen statt die
   Textarea waagrecht scrollen zu lassen. Die Zeilenumbrüche im Wert bleiben
   davon unberührt — sie stehen als echte \n darin. */
#text-inhalt {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    line-height: 1.45;
    resize: vertical;
    /* Über die Höhe des Dialogs hinaus scrollt das Feld in sich, statt den
       Kopieren-Knopf aus dem Bild zu schieben. */
    overflow-y: auto;
    min-height: 8rem;
}

/* --- Admin-Einstieg (admin.php) ------------------------------------------
   Kacheln statt einer Linkliste: Am Handy ist die ganze Fläche das Tippziel,
   nicht nur das Wort. Breiter als das Statusraster der Wartungsseite, weil
   hier ein erklärender Satz mitläuft — bei 8rem bräche der in Silben um. */

.admin-gitter {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 0.75rem;
}

.admin-kachel {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    min-height: var(--tippziel);
    padding: 0.9rem 1rem;
    border: 1px solid var(--linie);
    border-radius: var(--radius);
    background: var(--grund);
    color: var(--schrift);
    text-decoration: none;
}

.admin-kachel strong { font-size: 1.05rem; }
.admin-kachel .matt  { font-size: 0.9rem; }

@media (hover: hover) {
    .admin-kachel:hover { border-color: var(--akzent); background: var(--grund-tief); }
}

/* --- Verlauf: Eingrenzen, Gruppen, Einzelansicht (1.4.8) ------------------ */

/* Das Auswahlfeld der Einheit trägt Datum, Plan und Split und ist damit das
   längste der Leiste. Am Handy nimmt es die ganze Breite, damit der Text im
   geschlossenen Feld nicht abgeschnitten wird; die Sortierung steht darunter. */
.verlauf-filter { margin-left: 0; }

@media (max-width: 40rem) {
    .verlauf-filter,
    .verlauf-filter select { width: 100%; }
}

/* Der Übungsname in der Einheitentabelle ist ein Link. Nur der deutsche Name
   trägt die Akzentfarbe — der englische bleibt matt darunter, sonst stünden zwei
   blaue Zeilen da, die man als zwei Links liest. */
.uebung-link { text-decoration: none; color: inherit; }
.uebung-link strong { color: var(--akzent); }

@media (hover: hover) {
    .uebung-link:hover strong { text-decoration: underline; }
}

/* Das Datum in der Übungstabelle führt zur Einheit zurück; die Farbe genügt
   als Hinweis, eine Unterstreichung zöge sich durch Datum UND Uhrzeit. */
.datum-spalte a { text-decoration: none; }

/* Die Zeile der gewählten Einheit. Eine Fläche statt fetter Schrift: Fett
   verbreiterte die Zahlen, und die rechtsbündige Spalte spränge. */
.verlauf-tabelle tr.zeile-gewaehlt td { background: var(--grund-tief); }

/* Zwischenüberschriften bei "Nach Muskelgruppe" — über dem Kasten, nicht darin
   (CLAUDE.md, Frontend). */
.gruppen-titel { margin-bottom: 0.25rem; }

.gruppen-untertitel {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--schrift-matt);
    margin: 0.75rem 0 0.35rem;
}

.verlauf-zurueck { margin: 0 0 0.75rem; }

/* Ziel des Datums-Links: Die Einheit soll nicht an der Oberkante kleben. */
.einheit-karte { scroll-margin-top: 1rem; }

/* --- Verlauf: Sätze je Muskelgruppe (§7.8, seit 1.5.5) ------------------- */

/* Zahlen rechtsbündig und mit gleichen Ziffernbreiten — sonst springt die
   Spalte bei 9,5 gegenüber 12. */
.muskel-tabelle .zahl {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.muskel-tabelle th[scope="row"] {
    font-weight: 600;
    color: var(--schrift);
    font-size: 1rem;
}

/* Die Namensspalte bleibt stehen, wenn die Tabelle am schmalen Gerät
   seitwärts rollt — sonst sähe man Zahlen ohne zu wissen, wessen. Der
   Hintergrund ist Pflicht, die Zahlen liefen sonst unter ihr durch. */
.muskel-tabelle th:first-child {
    position: sticky;
    left: 0;
    background: var(--grund);
}

.muskel-tabelle .muskel-unter th[scope="row"] {
    font-weight: 400;
    font-size: 0.9rem;
    padding-left: 0.75rem;
    color: var(--schrift-matt);
}

.muskel-tabelle .muskel-unter td { font-size: 0.9rem; }

/* Die Wochenspalten wechseln sich im Ton ab, damit man eine Zahl ohne
   Linienzählen ihrer Woche zuordnet (Wunsch des Benutzers, 2026-09-29). Die
   aktuelle Woche ist deshalb nicht mehr über die Fläche markiert — ein dritter
   Ton neben zwei wechselnden läse sich als Muster —, sondern über den Kopf. */
/* Weiß muss ausdrücklich dastehen: Der Seitengrund ist selbst --grund-tief,
   und eine Zelle ohne eigenen Hintergrund ist durchsichtig. So sahen in
   1.5.6 alle Spalten gleich grau aus — die "weißen" zeigten nur die Seite. */
.muskel-tabelle td,
.muskel-tabelle thead th { background: var(--grund); }

/* Der zweite Ton darf NICHT --grund-tief sein — das ist die Farbe der Seite,
   und die getönten Spalten sahen dann aus, als fehlte dort die Tabelle
   (gemeldet am 2026-09-29). Ein helles Blau aus der Akzentfarbe gehört
   sichtbar zur Tabelle. #ecf3fd ist das dunkelste, auf dem die matte 0 noch
   AA hält: --schrift-matt steht darauf bei 4,56:1 (#e8f0fb wären 4,44:1). */
.muskel-tabelle td:nth-child(odd),
.muskel-tabelle thead th:nth-child(odd):not(:first-child) {
    background: #ecf3fd;
}

.muskel-tabelle thead th.woche-aktuell { color: var(--akzent); }

/* Drei Linienstärken, von außen nach innen (Wunsch des Benutzers,
   2026-09-30): Die GANZE Tabelle samt Kopf- und Gesamtzeile steht in einem
   3-px-Rahmen; zwischen den Hauptgruppen — und damit auch unter dem Kopf und
   über „Gesamt“ — trennt eine 2-px-Linie; zwischen den Untergruppen bleibt
   die dünne Zeilenlinie aus `.verlauf-tabelle`.

   Die 2-px-Linie hängt als Oberkante am <tbody>, das es je Hauptgruppe
   einmal gibt, und am <tfoot>. Bei `border-collapse: collapse` gewinnt je
   Kante die breiteste Linie — über die 1 px der Zeilen und über das
   `border-bottom: 0` aus `.verlauf-tabelle tr:last-child td`. Seitliche
   Ränder braucht der Block nicht mehr, die gibt der Tabellenrahmen. */
.muskel-tabelle { border: 3px solid var(--schrift-matt); }
.muskel-tabelle .muskel-block,
.muskel-tabelle tfoot { border-top: 2px solid var(--schrift-matt); }

/* Die Gesamtzeile steht unter dem letzten Rahmen, fett, ohne eigenen Rahmen —
   sie ist keine Muskelgruppe. `.verlauf-tabelle tr:last-child td` nimmt ihr
   die Unterkante ohnehin. */
.muskel-tabelle .muskel-gesamt th,
.muskel-tabelle .muskel-gesamt td {
    font-weight: 700;
    color: var(--schrift);
    padding-top: 0.6rem;
}

.woche-datum {
    display: block;
    font-weight: 400;
    font-size: 0.75rem;
}

.muskel-hinweis { font-size: 0.85rem; }

.muskel-blaettern {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
}
