/* ====================================================================
 * Tamedes UI — die zentrale Design-Basis
 * https://hub.tamedes.de/ui/v1/tamedes.css
 *
 * Baustein T3, ADR 0004. Diese Datei ist der Grund, aus dem es den
 * Goldrand nicht mehr geben kann: was hier steht, gilt in jeder App,
 * die zwei Zeilen einbindet — und nirgends sonst wird es gepflegt.
 *
 *   <link rel="stylesheet" href="https://hub.tamedes.de/ui/v1/tamedes.css">
 *   <link rel="icon" href="https://hub.tamedes.de/ui/v1/favicon.svg">
 *
 * ZWEI REGELN, DIE HIER DURCHGESETZT WERDEN
 *
 * 1. Gold ist Fokusring und Rand. Nie Text. Auf Weiss erreicht
 *    #b5984e nur 2.77:1 — als Schriftfarbe waere es unlesbar und
 *    zugleich das auffaelligste Element der Seite. tests/
 *    test_ui_kontrast.py laesst kein `color:` mit Gold durch.
 * 2. Farbe nie allein als Signal. Jede Statusfarbe hat ein Symbol
 *    oder ein Wort daneben. Die Komponenten unten sind so gebaut,
 *    dass die farblose Variante immer noch verstaendlich ist.
 *
 * KASKADENEBENEN — warum diese Datei bestehende Apps nicht zerlegt
 *
 * Alles steht in `@layer tamedes.*`. Regeln in einer Ebene verlieren
 * gegen ungeschichtete Regeln, unabhaengig von der Spezifitaet. Die
 * 17 Alt-Apps bringen ihr eigenes CSS mit; deren Regeln gewinnen also
 * immer. Tamedes UI ist der Boden, nicht die Decke. Genau das macht
 * die Umstellung je App risikoarm (ADR 0004, Abschnitt Risiken).
 *
 * Ausnahme, die man kennen muss: setzt eine App `outline: none` auf
 * :focus, gewinnt sie auch gegen den Fokusring hier. Das steht in der
 * Umstellungsanleitung als Pruefpunkt (docs/ui-umstellung.md).
 *
 * NAMEN
 *
 * Alle Tokens und Klassen tragen das Praefix `tm-`. Konzept v2.1 §9.7
 * nennt die Tokens ohne Praefix (--space-md); in fremden Apps waere
 * ein so allgemeiner Name eine Kollision mit Ansage. Die Werte sind
 * unveraendert, nur die Namen tragen `--tm-`.
 *
 * VERSION
 *
 * /ui/v1/ bleibt eingefroren. Ergaenzungen sind erlaubt, solange sie
 * nichts brechen (T4 legt hier die Fristen-Spine dazu). Alles, was
 * bestehende Einbindungen beschaedigen koennte, kommt unter /ui/v2/.
 * ==================================================================== */

@layer tamedes.tokens, tamedes.basis, tamedes.komponenten;

/* --------------------------------------------------------------------
 * Schriften — lokal vom Hub, kein fremdes CDN (Konzept v2.1, §9.6)
 *
 * Die Pfade sind relativ und loesen sich gegen die Adresse DIESER
 * Datei auf: eine App auf zuzahlung.tamedes.de laedt sie also vom Hub.
 * Damit das gelingt, sendet der Hub auf /ui/v1/fonts/
 * `Access-Control-Allow-Origin: *` — ohne diesen Header bleiben in
 * allen Apps still die Systemschriften stehen (ADR 0004).
 *
 * Jost und Inter sind variable Schriften: eine Datei deckt den ganzen
 * Gewichtsbereich ab. IBM Plex Mono liegt in zwei Schnitten vor.
 * Lizenzen: SIL OFL 1.1, Textfassungen unter /ui/v1/fonts/OFL-*.txt.
 * ------------------------------------------------------------------ */

@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/jost-400-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/jost-400-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/inter-400-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/inter-400-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('fonts/ibm-plex-mono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ====================================================================
 * 1 — TOKENS
 * ==================================================================== */

@layer tamedes.tokens {

  /* Hell. Die Werte stammen aus Konzept v2.1, §9.5 und §9.7.
   * Die Zahl hinter jeder Statusfarbe ist ihr gemessener Kontrast
   * gegen die Karte (#ffffff); gerechnet wird er in app/
   * farbkontrast.py, nachgehalten in tests/test_ui_kontrast.py. */
  :root {
    /* Flaechen */
    --tm-chrome: #1a3328;          /* Navigation, Kopfzeile, Kacheln */
    --tm-chrome-tief: #14281f;     /* Kopfzeile auf Kopfzeile        */
    --tm-chrome-hoch: #234133;     /* Zeiger, Fokus, aktiv, offen —
                                    11.19:1 gegen Weiss, 3.23:1 gegen
                                    --tm-gold-rand (T10). Ein echter
                                    Ton und keine durchscheinende
                                    Schicht: was durchscheint, kann
                                    kein Test messen. */
    --tm-flaeche: #f7f8f7;         /* Arbeitsflaeche                 */
    --tm-karte: #ffffff;           /* Datenflaechen                  */
    --tm-flaeche-tief: #eef1ef;    /* Tabellenkopf, ruhige Streifen  */

    /* Schrift */
    --tm-text: #16211c;            /* 16.55:1 */
    --tm-text-gedaempft: #4a5a52;  /*  7.30:1 */
    --tm-text-auf-chrome: #ffffff; /* 13.56:1 gegen --tm-chrome */
    --tm-text-auf-chrome-leise: #bfc6c3; /* 7.80:1 gegen --tm-chrome.
                                    Fuer Zonenueberschriften. Ein Ton
                                    und KEIN `opacity`: Opazitaet
                                    veraendert den Kontrast, ohne dass
                                    eine Farbe im Stylesheet steht —
                                    fuer jede Pruefung unsichtbar. */
    /* DIE GRUPPENUEBERSCHRIFT IN DER LINKEN SPALTE (Ralf, 02.08.2026,
     * N17.4). Ein warmer Sandton, damit sich Gruppe und App auch am
     * FARBTON unterscheiden und nicht nur an der Helligkeit: #bfc6c3
     * und #ffffff sind beide grau, der Unterschied ist eine Nuance.
     *
     * Die Farbe kommt DAZU und ersetzt nichts — Groesse, Versalien und
     * Sperrung bleiben, wie sie sind. Wer sie ersetzte, machte den
     * Unterschied fuer Farbenblinde und in Graustufen wieder
     * unsichtbar (tests/test_arbeitsflaeche.py zaehlt weiter drei von
     * vier farbfreie Merkmale).
     *
     * NICHT --tm-gold: das ist laut Palette der Zierrand „wo daneben
     * nichts vom Kontrast abhaengt", und Fokus und aktiver Zustand
     * tragen es bereits als Bedeutung. Eigener Ton, eigener Zweck.
     * 7.86:1 gegen --tm-chrome, 6.49:1 gegen --tm-chrome-hoch. */
    --tm-gruppe-auf-chrome: #d8c48a;
    /* Derselbe Ton, heller: Zeiger und aufgeklappter Zustand. Die
     * Rueckmeldung aendert die Helligkeit, nicht die Bedeutung.
     * 10.60:1 gegen --tm-chrome, 8.75:1 gegen --tm-chrome-hoch. */
    --tm-gruppe-auf-chrome-hoch: #eee3c4;

    /* ROT IN DER LINKEN SPALTE (Ralf, 06.08.2026, N30.1: „der Button
     * Alle schliessen bitte rot machen").
     *
     * WARUM NICHT --tm-kritisch. Das ist die Statusfarbe der
     * ARBEITSFLAECHE und im hellen Thema ein dunkles Rot (#a3231d,
     * 7.45:1 gegen Weiss). Auf dem dunklen Chrome der Spalte erreicht
     * es 1.82:1 — nachgerechnet mit app/farbkontrast.py, nicht
     * geschaetzt. Es waere also nicht „rot", sondern unleserlich.
     *
     * Das Chrome ist in BEIDEN Themen dunkel; deshalb steht hier
     * derselbe Wert wie im dunklen Block, und zwar aus demselben Grund
     * wie bei --tm-text-auf-chrome.
     * 6.28:1 gegen --tm-chrome, 5.18:1 gegen --tm-chrome-hoch,
     * 7.18:1 gegen --tm-chrome-tief. */
    --tm-kritisch-auf-chrome: #ef9a94;
    /* Heller unter dem Zeiger — Helligkeit als Rueckmeldung, wie bei
     * --tm-gruppe-auf-chrome-hoch.
     * 8.82:1 / 7.28:1 / 10.09:1. */
    --tm-kritisch-auf-chrome-hoch: #f8c4c0;

    /* Linien */
    --tm-linie: #dfe4e1;
    --tm-linie-kraeftig: #7e8d85;  /* 3.47:1 — Rand von Feldern
                                    und Knoepfen, WCAG 1.4.11 */

    /* Status — bewusst dunkel und gesaettigt (Konzept §9.5) */
    --tm-kritisch: #a3231d;        /* 7.45:1 — ueberfaellig */
    --tm-warnung: #8a5a00;         /* 5.92:1 — heute faellig */
    --tm-gut: #1d6b47;             /* 6.46:1 — erledigt */
    --tm-info: #1f4f73;            /* 8.66:1 — Hinweis */

    /* Statusflaechen: sehr helle Toenung derselben Farbe. Nur als
     * Hintergrund, der Text darauf bleibt --tm-text. */
    --tm-kritisch-flaeche: #fbeceb;
    --tm-warnung-flaeche: #fbf2e2;
    --tm-gut-flaeche: #e8f4ee;
    --tm-info-flaeche: #eaf1f7;

    /* Gold. Zwei Toene, beide nie Text:
     * --tm-gold      Zierrand, wo daneben nichts vom Kontrast abhaengt
     * --tm-gold-rand tragende Raender und der Fokusring. 3.46:1 gegen
     *                Weiss — WCAG 1.4.11 verlangt 3:1 fuer alles, was
     *                nicht Text ist. Das Markengold selbst erreicht
     *                nur 2.60:1 und darf deshalb keinen Fokus tragen. */
    --tm-gold: #b5984e;
    --tm-gold-rand: #a4863f;

    /* Fristen-Spine (T4). Die Schraffur ueber dem Band kennzeichnet
     * Beispielwerte. Sie ist eine eigene Farbe und keine Deckschicht,
     * damit der Kontrast der Zahlen gegen BEIDE Toene messbar bleibt —
     * tests/test_ui_kontrast.py prueft genau das. */
    --tm-spine-flaeche: #ffffff;
    --tm-spine-schraffur: #e7ebe8;

    /* Abstaende — Konzept v2.1, §9.7 */
    --tm-space-xs: 4px;
    --tm-space-sm: 8px;
    --tm-space-md: 16px;
    --tm-space-lg: 24px;
    --tm-space-xl: 40px;

    /* Formen und Groessen */
    --tm-radius-control: 6px;
    --tm-radius-card: 10px;
    --tm-radius-rund: 999px;
    --tm-control-height: 44px;     /* Touchflaeche, Konzept §9.8 */
    --tm-row-height-dense: 32px;

    /* Shell (T4) */
    --tm-navi-breite: 14rem;
    --tm-spine-breite: 56px;
    /* Jeder Reiter ist gleich breit (T10, ADR 0011 N15.2). Vorher
     * richtete sich die Breite nach dem Namen: „Zuzahlung" war halb so
     * breit wie „Auto-Rechnungen", und die Leiste sprang bei jedem
     * Oeffnen um. Wer einen Reiter an seinem Platz sucht, sucht ihn an
     * einer Stelle, die sich nicht bewegt. */
    --tm-reiter-breite: 13rem;

    /* DER RUHENDE REITER (Ralf, 04.08.2026, ADR 0011 N27.3).
     *
     * „Der aktive Reiter hebt sich farblich deutlich von den uebrigen
     * ab, nicht nur durch den Unterstrich."
     *
     * In HELL ist das ohne Zutun erfuellt: der aktive Reiter traegt die
     * Arbeitsflaeche (#f7f8f7), die ruhenden das dunkle Chrome — 12:1.
     * In DUNKEL nicht: dort ist die Arbeitsflaeche DUNKLER als das
     * Chrome, gemessen 1.31:1 zwischen rgb(18,25,21) und rgb(36,49,43).
     * Man sah nur noch den Unterstrich — genau Ralfs Befund.
     *
     * Deshalb steht die Deckkraft als Token: in dunkel werden die
     * RUHENDEN Reiter angehoben, statt den aktiven aufzuhellen. Nur so
     * bleibt die Grundform erhalten — der aktive Reiter ist die
     * Arbeitsflaeche, sichtbar in die Leiste hineingeschnitten. Zoege
     * man stattdessen den aktiven ins Helle, saehe er aus wie eine
     * Marke und nicht mehr wie die Flaeche darunter.
     *
     * Die Werte sind gerechnet, nicht geschaetzt: 0.32 ueber #16241d
     * ergibt rgb(97,106,101) und damit 3.21:1 gegen die Arbeitsflaeche
     * (WCAG 1.4.11 verlangt 3:1 fuer Zustaende). Die Schrift darauf
     * muss dann voll deckend sein — 86 % Weiss erreichen dort nur
     * 4.59:1, und das ist zu knapp an der Grenze von 4.5:1, um es einer
     * Schriftaenderung zu ueberlassen. */
    --tm-reiter-ruhe: rgba(255, 255, 255, 0.06);
    --tm-reiter-ruhe-zeiger: rgba(255, 255, 255, 0.14);
    --tm-reiter-ruhe-text: rgba(255, 255, 255, 0.86);

    /* Schriften */
    --tm-schrift-kopf: 'Jost', 'Trebuchet MS', system-ui, sans-serif;
    --tm-schrift: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    --tm-schrift-mono: 'IBM Plex Mono', ui-monospace, 'Cascadia Code', 'Source Code Pro', monospace;

    /* Schatten — sparsam. Tiefe entsteht hier ueber Linien. */
    --tm-schatten: 0 1px 2px rgba(22, 33, 28, 0.06);
    /* Fuer alles, was ueber der Seite schwebt — Dialoge, Filterpanel,
     * App-Wechsler. Die Komponenten dazu baut T4. */
    --tm-schatten-hoch: 0 6px 20px rgba(22, 33, 28, 0.13);

    /* Breiten */
    --tm-lesebreite: 68ch;
    --tm-spaltenbreite: 72rem;

    color-scheme: light;
  }

  /* Dunkel. Zwei Wege fuehren hierher:
   *   1. Die Systemeinstellung (dieser Block).
   *   2. data-thema="dunkel" am <html> — das setzt T4 persistent.
   * Der Attributweg steht zusaetzlich weiter unten, damit eine
   * bewusste Wahl die Systemeinstellung ueberstimmt. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-thema="hell"]) {
      --tm-chrome: #16241d;
      --tm-chrome-tief: #101c17;
      --tm-chrome-hoch: #233a31;
      --tm-flaeche: #121915;
      --tm-karte: #1a2420;
      --tm-flaeche-tief: #16201c;

      --tm-text: #e8eeea;            /* 13.55:1 gegen die Karte */
      --tm-text-gedaempft: #a3b3ab;  /*  7.28:1 */
      --tm-text-auf-chrome: #ffffff;
      --tm-text-auf-chrome-leise: #bfc6c3;
      /* 9.34:1 gegen das dunklere Chrome — derselbe Wert genuegt. */
      --tm-gruppe-auf-chrome: #d8c48a;
      --tm-gruppe-auf-chrome-hoch: #eee3c4;
      /* Das Chrome ist auch hier dunkel, also derselbe Ton wie im
       * hellen Thema: 7.45:1 bzw. 10.46:1 gegen --tm-chrome. */
      --tm-kritisch-auf-chrome: #ef9a94;
      --tm-kritisch-auf-chrome-hoch: #f8c4c0;

      --tm-linie: #2c3a34;
      --tm-linie-kraeftig: #647a70;  /* 3.46:1 */

      --tm-kritisch: #f08b85;        /* 6.63:1 */
      --tm-warnung: #e0a83c;         /* 7.46:1 */
      --tm-gut: #6cc79b;             /* 7.80:1 */
      --tm-info: #79b8e8;            /* 7.46:1 */

      --tm-kritisch-flaeche: #2e1a19;
      --tm-warnung-flaeche: #2b2318;
      --tm-gut-flaeche: #17281f;
      --tm-info-flaeche: #17222b;

      --tm-gold: #c9ae68;
      --tm-gold-rand: #c9ae68;       /* 8.28:1 — traegt hier selbst */

      /* Gerechnet ueber --tm-chrome #16241d, Begruendung oben bei den
       * Grundwerten: 0.32 -> rgb(97,106,101), 3.21:1 gegen die
       * Arbeitsflaeche. Weisse Schrift darauf: 5.54:1. */
      --tm-reiter-ruhe: rgba(255, 255, 255, 0.32);
      --tm-reiter-ruhe-zeiger: rgba(255, 255, 255, 0.38);
      /* Voll deckend, nicht 86 % wie in hell: die ruhende Flaeche ist
       * hier heller, und 86 % Weiss erreichen darauf nur 4.59:1 —
       * zu knapp an der Grenze von 4.5:1. Als `rgba` geschrieben wie
       * ihre Geschwister; hell und dunkel tragen dieselbe Schreibweise. */
      --tm-reiter-ruhe-text: rgba(255, 255, 255, 1);

      --tm-spine-flaeche: #1a2420;
      --tm-spine-schraffur: #24302b;

      --tm-schatten: 0 1px 2px rgba(0, 0, 0, 0.4);
      --tm-schatten-hoch: 0 6px 20px rgba(0, 0, 0, 0.5);

      color-scheme: dark;
    }
  }

  :root[data-thema="dunkel"] {
    --tm-chrome: #16241d;
    --tm-chrome-tief: #101c17;
    --tm-chrome-hoch: #233a31;
    --tm-flaeche: #121915;
    --tm-karte: #1a2420;
    --tm-flaeche-tief: #16201c;

    --tm-text: #e8eeea;
    --tm-text-gedaempft: #a3b3ab;
    --tm-text-auf-chrome: #ffffff;
    --tm-text-auf-chrome-leise: #bfc6c3;
    --tm-gruppe-auf-chrome: #d8c48a;
    --tm-gruppe-auf-chrome-hoch: #eee3c4;
    --tm-kritisch-auf-chrome: #ef9a94;
    --tm-kritisch-auf-chrome-hoch: #f8c4c0;

    --tm-linie: #2c3a34;
    --tm-linie-kraeftig: #647a70;

    --tm-kritisch: #f08b85;
    --tm-warnung: #e0a83c;
    --tm-gut: #6cc79b;
    --tm-info: #79b8e8;

    --tm-kritisch-flaeche: #2e1a19;
    --tm-warnung-flaeche: #2b2318;
    --tm-gut-flaeche: #17281f;
    --tm-info-flaeche: #17222b;

    --tm-gold: #c9ae68;
    --tm-gold-rand: #c9ae68;

    --tm-reiter-ruhe: rgba(255, 255, 255, 0.32);
    --tm-reiter-ruhe-zeiger: rgba(255, 255, 255, 0.38);
    --tm-reiter-ruhe-text: rgba(255, 255, 255, 1);

    --tm-spine-flaeche: #1a2420;
    --tm-spine-schraffur: #24302b;

    --tm-schatten: 0 1px 2px rgba(0, 0, 0, 0.4);
    --tm-schatten-hoch: 0 6px 20px rgba(0, 0, 0, 0.5);

    color-scheme: dark;
  }
}

/* ====================================================================
 * 2 — BASIS
 * Elementregeln. Sie machen aus zwei eingebundenen Zeilen sichtbar
 * eine Tamedes-App, ohne dass ein einziges class-Attribut noetig ist.
 * ==================================================================== */

@layer tamedes.basis {

  *, *::before, *::after { box-sizing: border-box; }

  html {
    -webkit-text-size-adjust: 100%;
    /* Anker springen nicht unter die Kopfzeile. */
    scroll-padding-top: var(--tm-space-xl);
  }

  body {
    margin: 0;
    background: var(--tm-flaeche);
    color: var(--tm-text);
    font-family: var(--tm-schrift);
    font-size: 16px;
    line-height: 1.55;
    /* Inters optische Feinheiten: echte Ligaturen fuer Zahlenpaare
     * abschalten waere falsch, tabellarische Ziffern gehoeren aber
     * nur an Zahlen — deshalb dort, nicht hier (siehe .tm-zahl). */
    font-synthesis-weight: none;
    text-rendering: optimizeLegibility;
  }

  h1, h2, h3, h4, h5, h6 {
    font-family: var(--tm-schrift-kopf);
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: 0.005em;
    margin: 0 0 var(--tm-space-sm);
    text-wrap: balance;
  }
  h1 { font-size: 1.6rem; }
  h2 { font-size: 1.25rem; }
  h3 { font-size: 1.075rem; }
  h4, h5, h6 { font-size: 1rem; }

  p { margin: 0 0 var(--tm-space-md); max-width: var(--tm-lesebreite); }
  p:last-child { margin-bottom: 0; }

  a {
    color: var(--tm-info);
    text-underline-offset: 0.15em;
    text-decoration-thickness: max(1px, 0.06em);
  }
  a:hover { text-decoration-thickness: max(2px, 0.1em); }

  strong, b { font-weight: 600; }

  /* Sichtbarer Tastaturfokus (Konzept v2.1, §9.9).
   * Gold traegt hier den Ring — als Rand, nicht als Text. Der Ring
   * liegt aussen mit Abstand, damit er auch auf gefuellten Flaechen
   * sichtbar bleibt. */
  :focus-visible {
    outline: 3px solid var(--tm-gold-rand);
    outline-offset: 2px;
    border-radius: var(--tm-radius-control);
  }
  /* Kein Ring bei Mausklick, aber niemals gar keiner: Browser ohne
   * :focus-visible bekommen den alten :focus-Ring. */
  :focus:not(:focus-visible) { outline: none; }

  code, kbd, samp, pre {
    font-family: var(--tm-schrift-mono);
    font-size: 0.9em;
  }
  code {
    background: var(--tm-flaeche-tief);
    border: 1px solid var(--tm-linie);
    border-radius: 4px;
    padding: 1px 5px;
  }
  pre {
    background: var(--tm-flaeche-tief);
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
    padding: var(--tm-space-md);
    overflow-x: auto;
  }
  pre code { background: none; border: none; padding: 0; }

  hr {
    border: none;
    border-top: 1px solid var(--tm-linie);
    margin: var(--tm-space-lg) 0;
  }

  img, svg, video { max-width: 100%; height: auto; }

  /* Tabellen: nur Grundordnung. Die dichte Fassung ist eine
   * Komponente, damit Alt-Apps mit eigenen Tabellen nichts merken. */
  table { border-collapse: collapse; }
  th { text-align: left; font-weight: 600; }

  /* Formularelemente erben die Schrift. Ohne diese Zeile bleiben
   * Knoepfe und Felder in jeder App bei der Browserschrift stehen —
   * das ist der sichtbarste Teil des einheitlichen Aussehens. */
  button, input, select, textarea { font: inherit; color: inherit; }

  ::placeholder { color: var(--tm-text-gedaempft); opacity: 1; }

  ::selection { background: var(--tm-gold); color: #16211c; }

  /* Bewegung nur, wenn erwuenscht (Konzept v2.1, §9.9). */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* ====================================================================
 * 3 — KOMPONENTEN
 * Alles mit Praefix tm-. Nichts davon greift ungefragt in eine
 * bestehende App ein.
 * ==================================================================== */

@layer tamedes.komponenten {

  /* --- Hilfsmittel -------------------------------------------------- */

  .tm-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .tm-sprungmarke {
    position: absolute;
    left: var(--tm-space-md);
    top: -100%;
    z-index: 100;
    padding: var(--tm-space-sm) var(--tm-space-md);
    background: var(--tm-karte);
    color: var(--tm-text);
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-control);
    text-decoration: none;
    font-weight: 600;
  }
  .tm-sprungmarke:focus { top: var(--tm-space-md); }

  .tm-stapel { display: flex; flex-direction: column; gap: var(--tm-space-md); }
  .tm-stapel--eng { gap: var(--tm-space-sm); }
  .tm-reihe {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--tm-space-sm);
  }
  .tm-reihe--weit { justify-content: space-between; }
  .tm-schieber { margin-left: auto; }

  .tm-spalte {
    max-width: var(--tm-spaltenbreite);
    margin-inline: auto;
    padding-inline: var(--tm-space-md);
  }
  .tm-spalte--schmal { max-width: 36rem; }

  /* Was gelesen wird, bekommt eine Lesebreite. Was verglichen,
   * gescannt oder bedient wird, bekommt die Flaeche.
   * (T10, ADR 0011, Abschnitt 5)
   *
   * EINE KLASSE DAZU, KEINE UMGEDEUTET. .tm-spalte deckelt bei
   * --tm-spaltenbreite und bleibt genau so: diese Datei ist DIESELBE,
   * die die Fach-Apps mit zwei Zeilen einbinden (ADR 0004). Wer
   * .tm-spalte umdefiniert, formatiert sechzehn Apps um, ohne dass in
   * ihnen jemand etwas geaendert haette.
   *
   * Die Lesebreite braucht hier keine Regel: `p` traegt sie schon
   * selbst (--tm-lesebreite, weiter oben). Sie ist eine Eigenschaft
   * des Absatzes und nicht des Behaelters — das ist die richtige
   * Ebene und der Grund, warum die Regel so einfach zu haben ist.
   * Eine Tabelle mit zwoelf Spalten, die bei 72 rem abgeschnitten
   * wird, zwingt zum Seitwaertsscrollen an einem Bildschirm, der breit
   * genug waere; ein Absatz ueber die volle Breite eines 27-Zoellers
   * ist unlesbar. Beides ist derselbe Fehler: eine Breite fuer alles. */
  .tm-flaeche {
    max-width: none;
    margin-inline: 0;
    padding-inline: var(--tm-space-md);
  }

  .tm-gedaempft { color: var(--tm-text-gedaempft); }
  .tm-klein { font-size: 0.875rem; }

  /* Zahlen, Kennungen, Zeiten. IBM Plex Mono mit tabellarischen
   * Ziffern — VO-Nummern werden gescannt, nicht gelesen (§9.6). */
  .tm-zahl, .tm-kennung {
    font-family: var(--tm-schrift-mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'zero' 1;   /* durchgestrichene Null */
    white-space: nowrap;
  }
  .tm-kennung { font-size: 0.92em; letter-spacing: 0.01em; }

  /* --- Kopfzeile ---------------------------------------------------- */

  .tm-kopf {
    background: var(--tm-chrome);
    color: var(--tm-text-auf-chrome);
    /* Der einzige Ort, an dem Gold flaechig auftritt: eine Haarlinie
     * als Abschluss. Kein Text, kein Bedeutungstraeger. */
    border-bottom: 2px solid var(--tm-gold);
  }
  /* Dichte, nicht Trefferflaeche: 44 px am Mauszeiger, 56 px sobald
   * ein grobes Zeigegeraet im Spiel ist (Block `any-pointer: coarse`
   * weiter unten). Die Knoepfe darin tragen ihre eigene Groesse. */
  /* DIE MARKE STEHT GANZ LINKS, BUENDIG MIT DER LINKEN SPALTE
   * (Ralf, 02.08.2026, ADR 0011 N17.1).
   *
   * Hier stand `max-width: var(--tm-spaltenbreite); margin-inline:
   * auto` — die Kopfzeile war eine zentrierte 72-rem-Spalte. Auf einem
   * breiten Bildschirm begann das Logo damit irgendwo in der Mitte
   * links, ueber dem Arbeitsbereich statt ueber der Navigation. Die
   * Kopfzeile spannt aber ueber alle drei Rasterspalten ("kopf kopf
   * kopf"), also gehoert ihr Inhalt an deren linken Rand.
   *
   * Das linke Innenmass ist ABSICHTLICH --tm-space-xs und nicht -md:
   * es ist dasselbe, das .tm-navi traegt. Nur so beginnt das Logo
   * genau dort, wo darunter die App-Namen beginnen. Rechts bleibt es
   * bei -md, sonst kleben Sperren und Abmelden am Bildschirmrand.
   *
   * Einspaltig (Anmelde- und Offline-Seite) gibt es keine linke
   * Spalte, an der etwas buendig sein koennte — dort bleibt die
   * zentrierte Spalte stehen, damit die Marke ueber der Anmeldekarte
   * sitzt und nicht daneben. */
  .tm-kopf__innen {
    display: flex;
    align-items: center;
    gap: var(--tm-space-sm);
    flex-wrap: wrap;
    padding: var(--tm-space-xs) var(--tm-space-md)
             var(--tm-space-xs) var(--tm-space-xs);
    /* DIE ZEILE BESTIMMT DIE HOEHE, NICHT DAS BILD (Ralf, 03.08.2026,
     * ADR 0011 N20.2). Beide Zahlen stehen HIER beieinander: wie hoch
     * die Zeile ist und wie hoch die Marke darin sein darf. Stuende
     * die zweite unten am Bild, waere sie eine eigene Entscheidung —
     * und genau so entsteht ein Logo, das aus der Zeile herauswaechst.
     * Der Rest der Zeile ist Luft ueber und unter der Marke. */
    --tm-kopfzeile-hoehe: 44px;
    --tm-marke-hoehe: 2rem;
    min-height: var(--tm-kopfzeile-hoehe);
  }
  .tm-huelle--schlicht .tm-kopf__innen,
  .tm-huelle--nackt .tm-kopf__innen {
    max-width: var(--tm-spaltenbreite);
    margin-inline: auto;
    padding-inline: var(--tm-space-md);
  }
  .tm-kopf__marke {
    display: flex;
    align-items: center;
    gap: var(--tm-space-sm);
    color: inherit;
    text-decoration: none;
  }
  /* DAS LOGO IN DER KOPFZEILE (Ralf, 02.08.2026, ADR 0011 N16).
   *
   * Die Hoehe steht, die Breite ergibt sich — `width: auto` und dazu
   * `object-fit: contain`. Das Paar ist die eigentliche Zusicherung:
   * ohne `contain` wuerde eine greifende `max-width` das Bild in die
   * Breite stauchen, statt es zu verkleinern. Ein gestauchtes Logo ist
   * schlimmer als ein kleines.
   *
   * `max-width` ist die Lehre aus dem waagerechten Scrollen (N15.1):
   * ein Flex-Kind darf schmaler werden als sein Inhalt, ein Bild aber
   * schiebt sonst die ganze Kopfzeile auf. 133 px bei 2 rem Hoehe
   * greift auf keinem ueblichen Geraet — es ist der Riegel fuer den
   * Fall, den man nicht vorhersieht.
   *
   * EIN GRUND, KEINE ZWEITE FASSUNG. Das Logo ist hell und
   * freigestellt, und die Kopfzeile traegt `--tm-chrome`: in BEIDEN
   * Themen ein dunkles Gruen (#1a3328 hell, #16241d dunkel). Der Hub
   * hat kein helles Chrome, deshalb braucht es weder eine dunkle
   * Unterlegung noch eine zweite Datei. Gemessen und festgehalten in
   * tests/test_shell.py. */
  .tm-kopf__logo {
    display: block;
    /* Die Hoehe kommt aus der Zeile darueber und steht nicht hier —
     * `--tm-marke-hoehe` wird auf .tm-kopf__innen gesetzt. Der
     * Rueckfallwert greift nur, wenn dieses Bild einmal ausserhalb
     * einer Kopfzeile landet. */
    height: var(--tm-marke-hoehe, 2rem);
    width: auto;
    /* `max-width` faengt die schmale Zeile: ein Bild schiebt sonst die
     * ganze Kopfzeile auf (N15.1).
     *
     * Gegen den Fall vom 03.08.2026 hilft hier NICHTS: hatte der
     * Browser das alte Stylesheet, kennt er diese ganze Regel nicht,
     * und es bleibt die Sammelregel fuer img, svg und video aus der
     * Basisebene — max-width 100 Prozent, Hoehe automatisch. Das ist
     * nicht in dieser Datei zu reparieren, sondern nur an ihrer
     * Adresse: siehe app/ansicht.py, `css_marke`. */
    max-width: 100%;
    object-fit: contain;
    object-position: left center;
  }
  .tm-kopf__titel {
    display: block;
    font-family: var(--tm-schrift-kopf);
    font-size: 1.1rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1.2;
  }
  .tm-kopf__unterzeile {
    display: block;
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.82);
  }
  .tm-kopf a:focus-visible { outline-color: var(--tm-gold); }

  .tm-navigation { display: flex; flex-wrap: wrap; gap: var(--tm-space-xs); }
  .tm-navigation a {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding: 0 var(--tm-space-md);
    border-radius: var(--tm-radius-control);
    border: 1px solid rgba(255, 255, 255, 0.22);
    color: var(--tm-text-auf-chrome);
    text-decoration: none;
  }
  /* Derselbe benannte Ton wie in der linken Spalte, aus demselben
   * Grund: eine durchscheinende Schicht laesst sich nicht messen
   * (T10, 31.07.2026). Hier war es nie unlesbar — die Regel gilt
   * trotzdem, sonst steht sie beim naechsten Mal wieder zur
   * Verhandlung. */
  .tm-navigation a:hover { background: var(--tm-chrome-hoch); }
  .tm-navigation a[aria-current] {
    background: var(--tm-chrome-hoch);
    border-color: var(--tm-gold);
  }

  /* --- Reiterband einer Seite ---------------------------------------
   * Die Verwaltung ist EINE Seite mit vier Reitern, keine vier
   * Menuepunkte (T10, ADR 0011 N4). Waagerecht, scrollbar, und der
   * aktive Reiter traegt drei Merkmale: Flaeche, goldene Kante, fette
   * Schrift. Faellt die Farbe aus, bleiben zwei. */
  /* `position: relative` aus demselben Grund wie an der Reiterspur:
   * dieses Band rollt waagerecht und traegt `.tm-sr-only`-Spannen.
   * Ohne Bezugsrahmen schoeben sie die Seite (N22.1). */
  .tm-reiterband {
    position: relative;
    display: flex;
    gap: var(--tm-space-xs);
    max-width: var(--tm-spaltenbreite);
    margin-inline: auto;
    padding: 0 var(--tm-space-md);
    overflow-x: auto;
    border-bottom: 1px solid var(--tm-linie);
  }
  .tm-reiterband__punkt {
    display: inline-flex;
    align-items: center;
    gap: var(--tm-space-xs);
    flex: 0 0 auto;
    min-height: 36px;
    padding: 0 var(--tm-space-md);
    border-bottom: 3px solid transparent;
    color: var(--tm-text-gedaempft);
    background: var(--tm-flaeche);
    text-decoration: none;
    white-space: nowrap;
  }
  .tm-reiterband__punkt:hover { color: var(--tm-text); background: var(--tm-flaeche-tief); }
  .tm-reiterband__punkt[aria-current] {
    color: var(--tm-text);
    border-bottom-color: var(--tm-gold-rand);
    font-weight: 600;
  }
  .tm-reiterband__symbol {
    display: inline-grid;
    place-items: center;
    width: 20px;
    font-size: 0.8rem;
  }
  /* Die Trefferflaeche der Reiter steht im einen `any-pointer:
   * coarse`-Block weiter unten — es gibt genau einen, und das haelt
   * tests/test_anmeldeseite.py fest. */

  /* --- Monogramm-Kachel --------------------------------------------
   * Dieselbe Kachel wie /ui/v1/icon/XX.svg, nur als CSS — fuer
   * Stellen, an denen ohnehin schon Text steht. Beide entstehen aus
   * derselben Registry, keiner der beiden Wege wird von Hand
   * gepflegt. Rand: keiner. Genau der war das Problem. */
  .tm-kachel {
    display: inline-grid;
    place-items: center;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: var(--tm-radius-control);
    background: var(--tm-chrome);
    color: #ffffff;
    font-family: var(--tm-schrift-kopf);
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: 0.06em;
    text-decoration: none;
    text-transform: uppercase;
  }
  .tm-kachel--gross { width: 64px; height: 64px; font-size: 1.4rem; border-radius: var(--tm-radius-card); }
  .tm-kachel--klein { width: 32px; height: 32px; font-size: 0.78rem; }
  /* Auf dunklem Grund verschwaende die Kachel sonst mit der Flaeche. */
  :root[data-thema="dunkel"] .tm-kachel,
  :root[data-thema="dunkel"] .tm-app__symbol { outline: 1px solid var(--tm-linie); }
  @media (prefers-color-scheme: dark) {
    :root:not([data-thema="hell"]) .tm-kachel,
    :root:not([data-thema="hell"]) .tm-app__symbol { outline: 1px solid var(--tm-linie); }
  }

  /* --- Karte -------------------------------------------------------- */

  .tm-karte {
    background: var(--tm-karte);
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
    padding: var(--tm-space-lg);
    box-shadow: var(--tm-schatten);
  }
  .tm-karte--flach { box-shadow: none; }
  .tm-karte__kopf {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--tm-space-sm);
    margin-bottom: var(--tm-space-md);
  }
  .tm-karte__kopf > :first-child { margin-bottom: 0; }
  .tm-karte__fuss {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tm-space-sm);
    margin-top: var(--tm-space-lg);
    padding-top: var(--tm-space-md);
    border-top: 1px solid var(--tm-linie);
  }

  /* --- Knoepfe ------------------------------------------------------
   * Sprache: „Freigeben", nicht „Absenden" (Konzept §9.10). Die
   * Beschriftung ist Sache der App, die Form steht hier. */

  .tm-knopf {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--tm-space-sm);
    min-height: var(--tm-control-height);
    padding: 0 var(--tm-space-lg);
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    color: var(--tm-text);
    background: var(--tm-karte);
    border: 1px solid var(--tm-linie-kraeftig);
    border-radius: var(--tm-radius-control);
    cursor: pointer;
    transition: background-color 120ms ease, border-color 120ms ease;
  }
  .tm-knopf:hover { background: var(--tm-flaeche-tief); border-color: var(--tm-text-gedaempft); }
  .tm-knopf:active { transform: translateY(1px); }

  .tm-knopf--haupt {
    background: var(--tm-chrome);
    border-color: var(--tm-chrome);
    color: #ffffff;
  }
  .tm-knopf--haupt:hover { background: var(--tm-chrome-tief); border-color: var(--tm-chrome-tief); }

  .tm-knopf--kritisch {
    background: var(--tm-kritisch);
    border-color: var(--tm-kritisch);
    color: #ffffff;
  }
  /* Dunkel ist --tm-kritisch ein heller Ton; weisse Schrift darauf
   * waere unlesbar. Dort traegt die Flaeche die dunkle Textfarbe. */
  :root[data-thema="dunkel"] .tm-knopf--kritisch { color: #16211c; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-thema="hell"]) .tm-knopf--kritisch { color: #16211c; }
  }

  .tm-knopf--leise {
    background: transparent;
    border-color: transparent;
    color: var(--tm-text-gedaempft);
    font-weight: 400;
  }
  .tm-knopf--leise:hover { background: var(--tm-flaeche-tief); color: var(--tm-text); }

  .tm-knopf--klein { min-height: 34px; padding: 0 var(--tm-space-md); font-size: 0.9rem; }
  .tm-knopf--breit { width: 100%; }

  /* Touchgeraete: 44 px, auch fuer den kleinen Knopf.
   *
   * §9.8 verlangt 44 px. Woertlich gelesen heisst das: ueberall, auf
   * jedem Geraet. Ralf hat am 30.07.2026 anders entschieden
   * (ADR 0009, Nachtrag N3 und N6): 44 px auf allem, was einen
   * Touchbildschirm HAT, 34 px sonst.
   *
   * `any-pointer` und nicht `pointer`. Der Unterschied entscheidet den
   * Fall, um den es geht: `pointer: coarse` fragt nach dem
   * HAUPTSAECHLICHEN Eingabegeraet, und ein Notebook mit Touchscreen
   * meldet dort `fine`. Es bliebe bei 34 px — und genau dieses Geraet
   * steht in der Praxis. In material wird am Tablet, am Handy UND am
   * Rechner bestellt.
   *
   * Der Preis ist ein etwas groesserer Knopf am Rechner mit
   * Touchscreen: ein Schoenheitsfehler. 34 px am Tablet waeren ein
   * Bedienfehler. Zwischen den beiden ist nicht abzuwaegen.
   *
   * Es trifft ALLE .tm-knopf--klein, nicht nur die Kopfzeile — auf dem
   * Tablet werden damit auch die Tabellenzeilen hoeher. Das ist der
   * Zweck und nicht der Schaden: Bestellen heisst Tabellenzeilen
   * antippen. */
  @media (any-pointer: coarse) {
    .tm-knopf--klein { min-height: 44px; }

    /* DICHTE ZURUECK AUF TREFFERFLAECHE (T10, ADR 0011, N4).
     *
     * Die linke Spalte und die Kopfzeile sind am Mauszeiger dichter —
     * der Hub zeigte bei gleicher Bildschirmgroesse deutlich weniger
     * als das Portal. Sobald irgendein Zeigegeraet grob ist, gilt
     * wieder §9.8: 44 px. Dieselbe Abwaegung wie beim kleinen Knopf
     * (ADR 0009, N3 und N6), und derselbe Preis: ein etwas luftigerer
     * Rechner mit Touchscreen.
     *
     * Die Zahl steht hier NICHT noch einmal, sondern kommt aus
     * --tm-control-height. Zwei Stellen mit derselben Zahl waeren die
     * naechste stille Abweichung. */
    .tm-navi { --tm-navi-hoehe: var(--tm-control-height); }
    /* Die Zeile waechst von 44 auf 56 px — das Logo waechst mit, sonst
     * schwimmt es in der Kopfzeile. Beide Zahlen stehen hier
     * beieinander und nicht eine davon unten am Bild (N20.2). */
    .tm-kopf__innen {
      --tm-kopfzeile-hoehe: 56px;
      --tm-marke-hoehe: 2.5rem;
    }
    .tm-reiterband__punkt { min-height: var(--tm-control-height); }

    /* DIE THEMENSCHALTER AUCH (04.08.2026, N27.6).
     *
     * Sie standen bei 32 px. Solange sie nur unangemeldet in der
     * Kopfzeile standen, war das eine Ecke des Hubs; seit sie auf
     * JEDER Seite stehen, ist es der Empfangstablet-Alltag — und
     * §9.8 verlangt dort 44 px (ADR 0009, N3 und N6). Die Zahl kommt
     * aus --tm-control-height und steht nicht noch einmal von Hand. */
    .tm-themenwahl button { min-height: var(--tm-control-height); }

    /* SCHLIESSEN UND SCROLLEN AM FINGER (T10, ADR 0011 N15.3 und N15.4).
     *
     * Das x war 22 px und die Rolleiste `thin` — beides am Tablet nicht
     * zu treffen. `min-width`/`min-height` und nicht `width`/`height`:
     * die Grundform steht weiter unten in derselben Ebene und wuerde
     * eine gleich spezifische `width` hier schlagen. Die Zahl kommt
     * aus --tm-control-height, nicht noch einmal von Hand.
     *
     * Der Reiter wird dabei breiter, sonst frisst das groessere x den
     * Namen auf. Der Wert haengt am Element und schlaegt damit den
     * geerbten aus :root — ohne Ruecksicht auf die Quellreihenfolge. */
    .tm-reiter__schliessen {
      min-width: var(--tm-control-height);
      min-height: var(--tm-control-height);
    }
    .tm-reiterleiste { --tm-reiter-breite: 15.5rem; }

    /* Die Rolleiste kennt kein `min-height` — hier muss die Zahl also
     * wirklich stehen, und sie braucht mehr Spezifitaet als die
     * Grundform weiter unten. Beides ist der Preis dafuer, dass es nur
     * EINEN coarse-Block gibt (tests/test_anmeldeseite.py). */
    .tm-huelle .tm-reiterleiste__spur::-webkit-scrollbar { height: 18px; }
  }

  .tm-knopf:disabled,
  .tm-knopf[aria-disabled="true"] {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
  }
  .tm-knopf:disabled:hover { background: var(--tm-karte); border-color: var(--tm-linie-kraeftig); }

  /* Laufender Vorgang. aria-busy sagt es der Vorleseprogramm-Nutzerin,
   * der Punkt sagt es dem Auge. */
  .tm-knopf[aria-busy="true"] { cursor: progress; }
  .tm-knopf[aria-busy="true"]::before {
    content: "";
    width: 1em;
    height: 1em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: tm-dreht 700ms linear infinite;
  }
  @keyframes tm-dreht { to { transform: rotate(360deg); } }

  .tm-knopfgruppe { display: flex; flex-wrap: wrap; gap: var(--tm-space-sm); }

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

  .tm-formular { display: flex; flex-direction: column; gap: var(--tm-space-lg); }
  .tm-formular--eng { gap: var(--tm-space-md); }

  .tm-feld { display: flex; flex-direction: column; gap: var(--tm-space-xs); }
  .tm-feld > label { font-weight: 600; font-size: 0.94rem; }
  .tm-feld__hinweis {
    margin: 0;
    font-size: 0.85rem;
    color: var(--tm-text-gedaempft);
    max-width: var(--tm-lesebreite);
  }
  /* Fehler sagen, was passiert ist und was jetzt zu tun ist (§9.10).
   * Das Symbol steht davor, damit die Farbe nicht allein traegt. */
  .tm-feld__fehler {
    margin: 0;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--tm-kritisch);
    display: flex;
    gap: var(--tm-space-xs);
  }
  .tm-feld__fehler::before { content: "\26A0"; }

  .tm-eingabe,
  .tm-auswahl,
  .tm-textfeld {
    min-height: var(--tm-control-height);
    padding: var(--tm-space-sm);
    background: var(--tm-karte);
    color: var(--tm-text);
    border: 1px solid var(--tm-linie-kraeftig);
    border-radius: var(--tm-radius-control);
    width: 100%;
  }
  .tm-textfeld { min-height: calc(var(--tm-control-height) * 2); resize: vertical; }
  .tm-auswahl { appearance: none; padding-right: var(--tm-space-xl); background-image: none; }

  .tm-eingabe:disabled,
  .tm-auswahl:disabled,
  .tm-textfeld:disabled {
    background: var(--tm-flaeche-tief);
    color: var(--tm-text-gedaempft);
    cursor: not-allowed;
  }
  .tm-eingabe[aria-invalid="true"],
  .tm-auswahl[aria-invalid="true"],
  .tm-textfeld[aria-invalid="true"] {
    border-color: var(--tm-kritisch);
    border-width: 2px;
  }
  .tm-eingabe[readonly] { background: var(--tm-flaeche-tief); }

  .tm-feldgruppe {
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
    padding: var(--tm-space-md);
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--tm-space-sm);
  }
  .tm-feldgruppe > legend { font-weight: 600; padding-inline: var(--tm-space-xs); }

  .tm-ankreuz {
    display: flex;
    align-items: flex-start;
    gap: var(--tm-space-sm);
    min-height: var(--tm-row-height-dense);
    font-weight: 400;
    cursor: pointer;
  }
  .tm-ankreuz input {
    width: 20px;
    height: 20px;
    margin: 4px 0 0;
    flex: 0 0 auto;
    accent-color: var(--tm-chrome);
  }
  .tm-ankreuz:has(input:disabled) { color: var(--tm-text-gedaempft); cursor: not-allowed; }

  .tm-suchzeile { display: flex; flex-wrap: wrap; gap: var(--tm-space-sm); }
  .tm-suchzeile .tm-eingabe,
  .tm-suchzeile .tm-auswahl { flex: 1 1 14rem; width: auto; }

  /* --- Statusmarken -------------------------------------------------
   * Farbe nie allein: .tm-marke__symbol ist Pflicht, nicht Zierde. */

  .tm-marke {
    display: inline-flex;
    align-items: center;
    gap: var(--tm-space-xs);
    padding: 2px var(--tm-space-sm);
    border-radius: var(--tm-radius-rund);
    border: 1px solid currentColor;
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
    color: var(--tm-text-gedaempft);
  }
  .tm-marke--kritisch { color: var(--tm-kritisch); background: var(--tm-kritisch-flaeche); }
  .tm-marke--warnung { color: var(--tm-warnung); background: var(--tm-warnung-flaeche); }
  .tm-marke--gut { color: var(--tm-gut); background: var(--tm-gut-flaeche); }
  .tm-marke--info { color: var(--tm-info); background: var(--tm-info-flaeche); }
  .tm-marke--neutral { background: var(--tm-flaeche-tief); }
  .tm-marke__symbol { font-size: 0.9em; }

  /* --- Meldungen ----------------------------------------------------
   * Was ist passiert, was ist jetzt zu tun (§9.10). Der farbige
   * Streifen links traegt nie allein: Titel und Symbol stehen dabei. */

  .tm-meldung {
    display: flex;
    gap: var(--tm-space-md);
    padding: var(--tm-space-md);
    background: var(--tm-karte);
    border: 1px solid var(--tm-linie);
    border-left: 4px solid var(--tm-text-gedaempft);
    border-radius: var(--tm-radius-card);
  }
  .tm-meldung__symbol { font-size: 1.15rem; line-height: 1.3; flex: 0 0 auto; }
  .tm-meldung__titel { margin: 0 0 var(--tm-space-xs); font-size: 1rem; }
  .tm-meldung__text { margin: 0; }
  .tm-meldung--kritisch { border-left-color: var(--tm-kritisch); background: var(--tm-kritisch-flaeche); }
  .tm-meldung--kritisch .tm-meldung__symbol { color: var(--tm-kritisch); }
  .tm-meldung--warnung { border-left-color: var(--tm-warnung); background: var(--tm-warnung-flaeche); }
  .tm-meldung--warnung .tm-meldung__symbol { color: var(--tm-warnung); }
  .tm-meldung--gut { border-left-color: var(--tm-gut); background: var(--tm-gut-flaeche); }
  .tm-meldung--gut .tm-meldung__symbol { color: var(--tm-gut); }
  .tm-meldung--info { border-left-color: var(--tm-info); background: var(--tm-info-flaeche); }
  .tm-meldung--info .tm-meldung__symbol { color: var(--tm-info); }

  /* --- Ziel-App auf der Anmeldeseite --------------------------------
   * Baustein T8, ADR 0009. Wer porto.tamedes.de aufruft und keine
   * Sitzung hat, landete bisher auf einer nackten Maske ohne Bezug zu
   * dem, was er wollte — also auf der Seite, die aussieht wie eine
   * Phishing-Seite. Dieser Streifen ist die Antwort darauf.
   *
   * Name und Monogramm stammen ausschliesslich aus der App-Registry
   * (ADR 0009 Abschnitt 3). Hier steht nie, was in der Adresse stand. */

  .tm-ziel {
    display: flex;
    align-items: center;
    gap: var(--tm-space-md);
    margin-bottom: var(--tm-space-lg);
    padding: var(--tm-space-md);
    background: var(--tm-flaeche-tief);
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
  }
  .tm-ziel__symbol {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border-radius: var(--tm-radius-control);
  }
  .tm-ziel__name { display: block; font-weight: 600; }
  .tm-ziel__text {
    display: block;
    font-size: 0.9rem;
    color: var(--tm-text-gedaempft);
  }

  /* --- Reiter -------------------------------------------------------
   * Ein Reiter ist kein geoeffnetes Fenster, er hat einen fachlichen
   * Kontext (Konzept v2.1, §6). Die Zustaende der Leiste stehen hier;
   * ihr Verhalten baut T5.
   *
   * Der aktive Reiter ist an DREI Merkmalen erkennbar: heller Grund,
   * fette Schrift, goldene Unterkante. Faellt die Farbe aus, bleiben
   * zwei. */

  /* NUR DIE LEISTE SCROLLT, NIE DIE SEITE (T10, ADR 0011 N15.1).
   *
   * `overflow-x: auto` steht hier seit T3 — und trotzdem schob sich bei
   * vielen Reitern das ganze Fenster nach rechts, samt Kopfzeile und
   * linker Spalte. Die Regel „diese Kiste darf schmaler sein als ihr
   * Inhalt" stand nirgends ausgeschrieben: sie hing daran, dass ein
   * Rasterkind mit eigenem Ueberlauf sie sich von selbst gibt. Jetzt
   * steht sie da, an jedem Kind des Rasters (siehe .tm-huelle unten). */
  /* DIE LEISTE IST DER RAHMEN, DIE SPUR ROLLT (N17.5).
   *
   * Bis zum 02.08.2026 war das ein Element: die Leiste selbst rollte,
   * und alles darin rollte mit — auch „Alle schliessen". Der Knopf ist
   * aber kein Reiter. Jetzt haelt die Leiste den Platz, und nur die
   * Spur darin faengt den Ueberlauf.
   *
   * `overflow-x: clip` an der Leiste, nicht `visible`: sonst fliesst
   * die Spur zwar in sich, aber ihr Rand malt weiter nach aussen — und
   * genau das schiebt die SEITE (siehe .tm-huelle unten, N17.6). */
  .tm-reiterleiste {
    display: flex;
    align-items: stretch;
    gap: var(--tm-space-xs);
    background: var(--tm-chrome);
    padding: var(--tm-space-xs) var(--tm-space-sm) 0;
    max-width: 100%;
    min-width: 0;
    overflow-x: clip;
  }
  /* WER WAAGERECHT ROLLT, MUSS AUCH BEZUGSRAHMEN SEIN
   * (03.08.2026, gemessen, ADR 0011 N22.1).
   *
   * Ohne diese eine Zeile schob die SEITE waagerecht, obwohl an der
   * Leiste `overflow-x: clip` stand und an der Spur `overflow-x: auto`.
   * Der Grund ist eine Regel, die man einem Stylesheet nicht ansieht:
   * ein `position: absolute` gesetztes Element wird von einem Vorfahren
   * mit Ueberlauf NUR dann beschnitten, wenn sein BEZUGSRAHMEN in
   * diesem Vorfahren liegt. Liegt er darueber, fliesst es an jedem
   * `clip` und jedem `hidden` vorbei — bis hinauf zur Seite.
   *
   * Genau das taten die `.tm-sr-only`-Spannen in den Reitern: 1 px
   * breit, fuer das Auge nichts, fuer das Vorleseprogramm alles — und
   * am rechten Ende der gerollten Spur, bei x ≈ 2100. Die Seite war
   * damit 2156 px breit in einem 1280 px breiten Fenster.
   *
   * ES WAR NICHT MIT DEM AUGE ZU FINDEN UND NICHT MIT EINEM CSS-TEST.
   * Gefunden hat es scripts/messen.py, indem es jedes Element einmal
   * ausblendete und neu mass. Drei Anlaeufe am Stylesheet davor lagen
   * daneben, weil sie die Kaskade lasen statt die Seite. */
  .tm-reiterleiste__spur {
    position: relative;
    display: flex;
    gap: var(--tm-space-xs);
    align-items: stretch;
    /* Nimmt den Platz, den der Knopf uebrig laesst — und gibt ihn her,
     * statt ihn zu fordern: ohne die Null darunter schoebe sie den
     * Knopf bei vielen Reitern aus der Leiste hinaus. */
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;

    /* DIE ROLLEISTE IST ZUM TREFFEN DA, NICHT ZUM ANSEHEN (N15.4).
     * `thin` stand hier bis zum 01.08.2026 — am Tablet ein Strich von
     * wenigen Pixeln. Die Farbe steht mit dabei: ohne sie zeichnet der
     * Browser eine helle Leiste auf den dunklen Chrome-Grund und man
     * sieht die Spur, aber nicht den Griff. */
    scrollbar-width: auto;
    scrollbar-color: var(--tm-text-auf-chrome-leise) transparent;
  }
  .tm-reiterleiste__spur::-webkit-scrollbar { height: 12px; }
  .tm-reiterleiste__spur::-webkit-scrollbar-track { background: transparent; }
  .tm-reiterleiste__spur::-webkit-scrollbar-thumb {
    background: var(--tm-text-auf-chrome-leise);
    border-radius: var(--tm-radius-rund);
  }

  .tm-reiter {
    display: inline-flex;
    align-items: center;
    gap: var(--tm-space-sm);
    min-height: 40px;
    min-width: 0;
    padding: 0 var(--tm-space-md);
    border: none;
    border-bottom: 3px solid transparent;
    border-radius: var(--tm-radius-control) var(--tm-radius-control) 0 0;
    background: var(--tm-reiter-ruhe);
    color: var(--tm-reiter-ruhe-text);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
  }
  .tm-reiter:hover { background: var(--tm-reiter-ruhe-zeiger); color: #ffffff; }
  .tm-reiter[aria-selected="true"] {
    background: var(--tm-flaeche);
    color: var(--tm-text);
    font-weight: 600;
    border-bottom-color: var(--tm-gold-rand);
  }
  /* GROESSER UND LAUTER (T10, ADR 0011 N15.3).
   *
   * 22 px und durchsichtig — Ralf traf es am Mauszeiger schlecht und
   * am Tablet gar nicht. Der Griff traegt jetzt eine eigene Flaeche,
   * auch ohne Zeiger darauf: ein Knopf, den man erst beim Darueberfahren
   * sieht, ist am Finger kein Knopf. */
  .tm-reiter__schliessen {
    display: inline-grid;
    place-items: center;
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border: none;
    border-radius: var(--tm-radius-rund);
    background: rgba(127, 127, 127, 0.26);
    color: inherit;
    cursor: pointer;
    font-size: 1.15rem;
    line-height: 1;
  }
  .tm-reiter__schliessen:hover { background: rgba(127, 127, 127, 0.5); }

  /* DAS X SITZT MITTIG, GEMESSEN AN DER TINTE (Ralf, 04.08.2026, N27.3).
   *
   * Der Knopf ist ein Raster mit `place-items: center`, und der
   * Zeilenkasten des Zeichens sass damit exakt in der Mitte: 0.0 px
   * Versatz, waagerecht wie senkrecht. Trotzdem sah es zu tief aus —
   * und es WAR zu tief. Ein Zeilenkasten ist nicht das Zeichen darin:
   * das × von Inter steht auf der mathematischen Achse und laesst
   * darunter mehr Raum als darueber. Gemessen mit `measureText` lag
   * seine Tinte 1.5 px unter der Mitte des Kreises.
   *
   * Zwei Messungen, zwei verschiedene Antworten — und nur die zweite
   * ist die, die Ralf sieht. Deshalb misst scripts/messen.py seither
   * die Tinte und nicht den Kasten.
   *
   * DER WERT STEHT IN `em` UND NICHT IN PIXELN. 1.5 px bei 18.4 px
   * Schriftgroesse sind 0.08 em; als feste Pixelzahl waere die
   * Korrektur bei jeder Groessenaenderung wieder falsch — und still. */
  .tm-reiter__schliessen > span[aria-hidden] {
    display: block;
    line-height: 1;
    transform: translateY(-0.08em);
  }

  /* „Alle schliessen" steht in der KOPFZEILE (Ralf, 03.08.2026, N22.4),
   * und zwar LINKS (Ralf, 05.08.2026, N28.4).
   *
   * Es war bis zum 03.08. ein fester Platz am rechten Rand der
   * Reiterleiste. Dort blieb es zwar stehen, waehrend die Spur rollte —
   * aber unter 46 rem gibt es die Leiste gar nicht, und dann war der
   * Knopf weg.
   *
   * DER ORT STEHT NICHT HIER, SONDERN IN DER REIHENFOLGE DER VORLAGE.
   * Der Knopf steht in basis.html VOR `.tm-schieber`, und der Schieber
   * traegt `margin-left: auto` — mehr braucht es nicht. Eine eigene
   * Regel (`order`, `margin-right: auto`) waere eine zweite Stelle, an
   * der die Reihenfolge festgelegt ist, und die beiden koennten
   * auseinanderlaufen.
   *
   * Er traegt das gewoehnliche Knopfkleid der Kopfzeile
   * (`tm-knopf tm-knopf--klein`); hier steht nur, was ihn von Abmelden
   * unterscheidet: das Kreuz vor dem Wort. */
  .tm-kopf__alle {
    gap: var(--tm-space-xs);
    white-space: nowrap;
  }

  /* Badges nach Konzept §6. Jeder traegt ein sichtbares Zeichen und
   * einen Text fuer Vorleseprogramme — kein blosser Farbpunkt. */

  /* OHNE QUELLE UNSICHTBAR — dieselbe Bauart wie bei der Spine: ein
   * Zustand, kein Schalter. Die Vorlage setzt `hidden`, T6 nimmt es
   * weg, sobald eine Kennzahl da ist.
   *
   * DIESE ZEILE IST NOETIG, WEIL `display` UNTEN `hidden` UEBERSTIMMT.
   * `[hidden] { display: none }` steht im Browser-Stylesheet, und ein
   * Autoren-Stylesheet gewinnt dagegen IMMER — unabhaengig von der
   * Spezifitaet. Die Vorlage hat also seit T5 korrekt `hidden` gesetzt
   * und der Browser hat trotzdem leere Kreise und Punkte gezeichnet:
   * ein grauer Kreis, ein blauer Punkt und ein Stift an jedem Reiter,
   * die nichts bedeuten (ADR 0011, N10.1).
   *
   * Sie steht VOR .tm-badge und traegt trotzdem: `[hidden]` ist
   * spezifischer als die blosse Klasse. */
  .tm-badge[hidden] { display: none; }

  .tm-badge {
    display: inline-grid;
    place-items: center;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: var(--tm-radius-rund);
    background: var(--tm-flaeche-tief);
    color: var(--tm-text);
    font-family: var(--tm-schrift-mono);
    font-size: 0.72rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }
  .tm-badge--punkt {
    min-width: 10px;
    width: 10px;
    height: 10px;
    padding: 0;
    background: var(--tm-info);
  }
  .tm-badge--warnung { background: var(--tm-warnung-flaeche); color: var(--tm-warnung); border: 1px solid currentColor; }
  .tm-badge--stift { background: transparent; color: inherit; font-family: var(--tm-schrift); }

  /* --- Tabellen -----------------------------------------------------
   * Der Rahmen scrollt, nicht die Seite. Bis 360 px bedienbar
   * (Konzept §9.9) heisst fuer eine dichte Tabelle: sie darf schmal
   * werden, aber sie darf die Seite nicht sprengen. */

  .tm-tabellenrahmen {
    /* Bezugsrahmen, damit die `<caption class="tm-sr-only">` und jede
     * andere absolut gesetzte Spanne im Rahmen bleibt und nicht die
     * Seite schiebt (N22.1). */
    position: relative;
    overflow-x: auto;
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
    background: var(--tm-karte);
  }
  .tm-tabelle { width: 100%; border-collapse: collapse; font-size: 0.94rem; }
  .tm-tabelle th,
  .tm-tabelle td {
    text-align: left;
    padding: var(--tm-space-sm) var(--tm-space-md);
    border-bottom: 1px solid var(--tm-linie);
    vertical-align: top;
  }
  .tm-tabelle thead th {
    position: sticky;
    top: 0;
    background: var(--tm-flaeche-tief);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    white-space: nowrap;
    z-index: 1;
  }
  .tm-tabelle tbody tr:last-child td,
  .tm-tabelle tbody tr:last-child th { border-bottom: none; }
  .tm-tabelle tbody tr:hover { background: var(--tm-flaeche-tief); }
  .tm-tabelle--dicht th,
  .tm-tabelle--dicht td {
    padding: var(--tm-space-xs) var(--tm-space-sm);
    height: var(--tm-row-height-dense);
  }
  .tm-tabelle td.tm-rechts, .tm-tabelle th.tm-rechts { text-align: right; }

  /* Eine Zeile mit Frist: der Streifen links wiederholt, was die
   * Marke in der Zeile schon sagt. */
  .tm-zeile--kritisch { box-shadow: inset 3px 0 0 var(--tm-kritisch); }
  .tm-zeile--warnung { box-shadow: inset 3px 0 0 var(--tm-warnung); }

  /* --- App-Kachelraster ---------------------------------------------
   * Die Startseite des Hubs und der App-Wechsler auf dem Telefon. */

  .tm-appraster {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    /* 22rem UND NICHT 15 (03.08.2026) UND NICHT MEHR 20 (04.08.2026).
     * Beide Male gemessen, ADR 0011 N23.8 und N25.3.
     *
     * Mit 15rem stellte das Raster bei 900 px zwei Spalten zu 249 px
     * nebeneinander — und dann wurde der NAME gekürzt, nicht nur die
     * Beschreibung: „Rezeptzentr…". Gekürzt statt zerrissen ist zwar
     * die Regel, aber ein Name, den man raten muss, ist keine Kachel.
     *
     * WARUM 20 NICHT MEHR REICHT. Seit dem 04.08.2026 startet die linke
     * Spalte zugeklappt (N25.2): 4.75rem statt 14rem. Die Arbeitsfläche
     * ist damit 148 px breiter, und bei 900 und 1380 px passt eine
     * Spalte MEHR ins Raster — jede davon dann genau am Minimum.
     * Gemessen bei 1380 px: Kachel 322 px, „Pflegeheim Nachweise"
     * braucht 173 px und hat 170. Drei Pixel.
     *
     * Die Zahl ist nicht auf diese drei Pixel gesetzt. Gemessen ist der
     * Aufschlag der Kachel um den Namen herum (Polster, Knopf, Abstand)
     * mit 152 px; der längste Name braucht 173. Nötig sind also 325 px,
     * 22rem sind 352. Die 27 px Luft sind für den nächsten Namen, nicht
     * für diesen — eine Zahl, die genau passt, passt beim nächsten
     * Registry-Eintrag nicht mehr.
     *
     * Der Feger (320–1920 px in 20er-Schritten) belegt beides: mit
     * 20rem meldet er 900 und 1380, mit 22rem keine Breite. */
    grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
    gap: var(--tm-space-sm);
  }
  /* EIN RASTERKIND IST VON SICH AUS NIE SCHMALER ALS SEIN INHALT
   * (03.08.2026, gemessen, ADR 0011 N23.7).
   *
   * Ohne diese Zeile hat das Kuerzen aus N23.2 einen neuen Fehler
   * erzeugt: `white-space: nowrap` macht die Mindestbreite eines
   * Rasterkindes so gross wie die ganze Textzeile. Bei 390 px wuchs
   * die Spalte auf 426 px in einem 308 px breiten Kasten, und der
   * Knopf „Zeigen" lag draussen.
   *
   * Die Seite rollte dabei NICHT: `overflow-x: clip` am Arbeitsbereich
   * verdeckte den Ueberstand. Eine Messung, die nur die SEITE ansieht,
   * haelt so etwas fuer heil — deshalb misst scripts/messen.py seither
   * auch, ob eine Kachel in ihre Liste passt. */
  .tm-appraster > li { min-width: 0; }
  .tm-app {
    display: flex;
    align-items: center;
    gap: var(--tm-space-md);
    padding: var(--tm-space-sm) var(--tm-space-md);
    min-height: 64px;
    background: var(--tm-karte);
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
    color: inherit;
    text-decoration: none;
  }
  .tm-app:hover { border-color: var(--tm-gold-rand); }
  .tm-app__symbol { flex: 0 0 auto; width: 44px; height: 44px; border-radius: var(--tm-radius-control); }

  /* KÜRZEN STATT ZERREISSEN (Ralf, 03.08.2026, ADR 0011 N23.2).
   *
   * Gemessen bei 900 px Fensterbreite: die Kachel 249 px, der
   * Textkasten darin 63 px, „Rezeptzentrum" braucht 117 px. Der
   * Browser schnitt mitten im Wort ab, ohne Zeichen dafür —
   * „Wochenplanun", „Eingangsrechn", „Materialbestan". Bei 1280 px
   * passierte es nicht; die Messung an drei runden Breiten hat es
   * deshalb zuerst übersehen (N23.1).
   *
   * `min-width: 0` am Wortkasten ist die Bedingung dafür, dass das
   * Kürzen überhaupt greift: ein Flexkind ist von sich aus nie
   * schmaler als sein Inhalt, und dann kürzt `text-overflow` nichts,
   * weil nichts zu kürzen ist — es läuft stattdessen hinaus. */
  .tm-app__wort {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }
  .tm-app__name,
  .tm-app__text,
  .tm-app__hinweis {
    display: block;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .tm-app__name { font-weight: 600; }
  .tm-app__text { font-size: 0.85rem; color: var(--tm-text-gedaempft); }

  /* Der Knopf gibt seinen Platz NICHT her. Gäbe er ihn her, würde
   * „Als Reiter" selbst gekürzt — und ein Knopf, auf dem „Als Rei…"
   * steht, ist kein Knopf. */
  .tm-app .tm-schieber { flex: 0 0 auto; }

  /* DER MOBIL-HINWEIS ZEIGT SICH NUR, WO ER ETWAS HEISST (N23.4).
   *
   * „Für kleine Bildschirme nicht geprüft" stand bis zum 03.08.2026
   * ANSTELLE der Beschreibung — die Vorlage schrieb
   * `mobil_hinweis or description`. Am Schreibtisch ist der Satz keine
   * Auskunft; am Telefon ist er eine. Also steht er dort und nur
   * dort. */
  .tm-app__hinweis { display: none; }

  /* --- Leerer Zustand ------------------------------------------------
   * Laedt zum Handeln ein (§9.10) — nie nur „keine Daten". */

  .tm-leer {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--tm-space-sm);
    padding: var(--tm-space-xl) var(--tm-space-md);
    text-align: center;
    border: 1px dashed var(--tm-linie-kraeftig);
    border-radius: var(--tm-radius-card);
    color: var(--tm-text-gedaempft);
  }
  .tm-leer__titel { color: var(--tm-text); margin: 0; }

  /* --- Laden ---------------------------------------------------------
   * Der Balken bewegt sich; bei prefers-reduced-motion steht er still
   * und bleibt trotzdem sichtbar. */

  .tm-skelett {
    background: linear-gradient(90deg,
      var(--tm-flaeche-tief) 25%, var(--tm-linie) 37%, var(--tm-flaeche-tief) 63%);
    background-size: 400% 100%;
    animation: tm-schimmer 1.4s ease infinite;
    border-radius: var(--tm-radius-control);
    height: 1em;
  }
  @keyframes tm-schimmer {
    0% { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
  }

  /* --- Kennwerte ----------------------------------------------------- */

  .tm-kennwerte {
    display: flex;
    flex-wrap: wrap;
    gap: var(--tm-space-lg);
    margin: 0;
  }
  .tm-kennwerte dt {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--tm-text-gedaempft);
  }
  .tm-kennwerte dd {
    margin: 0;
    font-family: var(--tm-schrift-kopf);
    font-size: 1.5rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }

  /* --- Fusszeile ----------------------------------------------------- */

  .tm-fuss {
    max-width: var(--tm-spaltenbreite);
    margin-inline: auto;
    padding: var(--tm-space-lg) var(--tm-space-md);
    color: var(--tm-text-gedaempft);
    font-size: 0.85rem;
  }

  /* --- Schmale Geraete ----------------------------------------------
   * Konzept §9.8: Smartphone bekommt Karten statt Tabellen und
   * Touchflaechen. Bedienbar bis 360 px (§9.9) — deshalb hier keine
   * festen Breiten, sondern nur Nachgeben. */

  @media (max-width: 46rem) {
    .tm-kopf__innen { padding: var(--tm-space-sm); }
    .tm-navigation { width: 100%; }
    .tm-navigation a { flex: 1 1 auto; justify-content: center; }
    .tm-karte { padding: var(--tm-space-md); }
    /* `minmax(0, 1fr)` und nicht `1fr`: die Kurzform bedeutet
     * `minmax(auto, 1fr)`, und `auto` heisst hier „mindestens so
     * breit wie der Inhalt" — genau das, was nicht sein soll. */
    .tm-appraster { grid-template-columns: minmax(0, 1fr); }
    .tm-app__hinweis {
      display: block;
      font-size: 0.78rem;
      color: var(--tm-warnung);
    }
    /* AM TELEFON BRICHT DER NAME UM, STATT GEKUERZT ZU WERDEN
     * (03.08.2026, gemessen, N23.8).
     *
     * Bei 320 bis 400 px bleiben dem Text neben dem Knopf rund 150 px,
     * und „Pflegeheim Nachweise" passt da nicht hinein. Waagerecht ist
     * kein Platz mehr zu holen — senkrecht schon: ein Telefon rollt
     * ohnehin. Zwei Zeilen Name sind besser als ein geratener.
     *
     * Die BESCHREIBUNG bleibt einzeilig und gekuerzt: sie ist die
     * Zugabe, der Name ist die Sache. */
    .tm-app__name { white-space: normal; overflow: visible; }
    .tm-knopf--breit-mobil { width: 100%; }
  }

  @media (max-width: 24rem) {
    :root { --tm-space-lg: 16px; }

    /* DIE KACHEL BRICHT UM, STATT DEN NAMEN ZU QUETSCHEN
     * (03.08.2026, gemessen, N23.8).
     *
     * Bei 320 px bleiben dem Text neben dem Knopf 118 px, und
     * „Rezeptpruefung" braucht 124 — EIN Wort, das sich nicht umbrechen
     * laesst, ohne es zu zerreissen. Waagerecht ist nichts mehr zu
     * holen, also nimmt der Knopf die naechste Zeile. Er wird dabei
     * breit genug fuer einen Finger, was auf einem 320-px-Geraet
     * ohnehin richtig ist.
     *
     * 320 px liegt unter der Grenze aus Konzept §9.9 („bedienbar bis
     * 360 px"). Die Messung faehrt trotzdem bis dorthin: eine Grenze
     * belegt man, indem man darunter nachsieht. */
    .tm-app { flex-wrap: wrap; }
    .tm-app .tm-schieber { margin-left: 0; width: 100%; }
    .tm-app .tm-schieber .tm-knopf { width: 100%; }
    .tm-spalte { padding-inline: var(--tm-space-sm); }
    .tm-karte { padding: var(--tm-space-sm); border-radius: var(--tm-radius-control); }
    .tm-kennwerte { gap: var(--tm-space-md); }
    .tm-kennwerte dd { font-size: 1.25rem; }
    .tm-knopf { padding-inline: var(--tm-space-md); }

    /* Kopfzeile bei 360 px: umbrechen, nicht schieben (T8, ADR 0009
     * Abschnitt 5). Seit T8 stehen dort zwei Knoepfe statt einem.
     * `tm-schieber` schiebt sie sonst so weit nach rechts, dass die
     * Wortmarke aus dem Bild geraet — hier nehmen sie die ganze
     * zweite Zeile und richten sich darin rechts aus. */
    .tm-kopf__innen { gap: var(--tm-space-sm); }
    .tm-kopf .tm-schieber {
      margin-left: 0;
      width: 100%;
      justify-content: flex-end;
    }
  }

  /* --- Druck ---------------------------------------------------------
   * Nachweise werden ausgedruckt und abgeheftet. */

  @media print {
    .tm-kopf, .tm-navigation, .tm-reiterleiste, .tm-sprungmarke { display: none !important; }
    .tm-karte { border: 1px solid #999; box-shadow: none; }
    .tm-tabellenrahmen { overflow: visible; border: none; }
  }
}

/* ====================================================================
 * 4 — HUB-SHELL (T4, ADR 0005)
 *
 * Ergaenzung, kein Bruch: /ui/v1/ bleibt gueltig, bestehende
 * Einbindungen merken davon nichts. Was hier steht, benutzt heute nur
 * der Hub — eine Alt-App koennte es benutzen, muss aber nicht.
 * ==================================================================== */

@layer tamedes.komponenten {

  /* --- Das Raster ----------------------------------------------------
   * Kopfzeile oben, darunter drei Spalten: Navigation, Fristen-Spine,
   * Arbeitsbereich. Die Spine sitzt am linken Rand des
   * ARBEITSBEREICHS, also rechts von der Navigation (Konzept §9.4).
   */

  /* DIE LINKE SPALTE REICHT DURCH, DIE REITERLEISTE BEGINNT DANEBEN
   * (T10, 31.07.2026).
   *
   * Bis dahin lief die Reiterleiste ueber die ganze Breite, und der
   * erste Reiter sass ueber dem Menue. Das las sich, als gehoere er
   * dorthin. Ein Reiter gehoert aber zur Arbeitsflaeche und nicht zur
   * Navigation: die Spalte laeuft deshalb von der Kopfzeile bis nach
   * unten durch, und die Leiste setzt buendig an ihrer Kante an. */
  .tm-huelle {
    display: grid;
    grid-template-areas:
      "kopf   kopf   kopf"
      "navi   reiter reiter"
      "navi   spine  inhalt"
      "navi   spine  fuss";
    grid-template-columns: var(--tm-navi-breite) auto minmax(0, 1fr);
    grid-template-rows: auto auto 1fr auto;
    min-height: 100vh;
    min-height: 100dvh;

    /* DIE SCHALE STEHT, DER INHALT ROLLT (Ralf, 04.08.2026, N27.4).
     *
     * „Die linke Spalte muss immer vollstaendig sichtbar bleiben —
     * eigene Scrollflaeche, volle Hoehe, der Inhalt scrollt fuer sich."
     *
     * Bis dahin rollte das DOKUMENT. Die linke Spalte ist ein Rasterkind
     * und so hoch wie die ganze Seite: auf /konto 2068 px in einem
     * 900 px hohen Fenster. Beim Rollen wanderte ihr oberer Rand auf
     * -1168 px — die Apps standen ueber dem Bildschirm, `overflow-y:
     * auto` an der Spalte lief nie an, weil die Spalte gar nicht zu
     * hoch WAR, sondern die Seite (gemessen 04.08.2026).
     *
     * `height` und nicht `min-height`: erst damit hoert das Raster auf
     * mitzuwachsen, und erst dann bekommt der Arbeitsbereich einen
     * Ueberlauf, den er selbst auffangen kann.
     *
     * Was das kostet, steht in scripts/messen.py: ein Vollbild des
     * Browsers zeigt jetzt nur noch das Fenster. Der Messlauf rollt
     * deshalb den Arbeitsbereich ans Ende und legt ein zweites Bild ab
     * (N24.5 gilt weiter). */
    height: 100dvh;
  }
  /* `min-width: 0` AN JEDEM KIND, NICHT NUR AM ARBEITSBEREICH
   * (T10, ADR 0011 N15.1).
   *
   * Es stand bis zum 01.08.2026 allein an `.tm-arbeit` — dort war die
   * Not zuerst aufgefallen, und dort blieb die Regel stehen. Ein
   * Rasterkind ist von sich aus mindestens so breit wie sein Inhalt;
   * wer das nicht widerruft, hat eine Seite, die waagerecht scrollt,
   * sobald irgendein Kind laenger wird. Bei vielen Reitern war das die
   * Leiste, und mit der Seite wanderten Kopfzeile und linke Spalte aus
   * dem Bild.
   *
   * Die Regel gilt fuer alle, auch dort, wo heute nichts ueberlaeuft.
   * Eine Regel, die nur an der Stelle steht, an der es schon einmal weh
   * getan hat, ist keine Regel, sondern eine Narbe. */
  .tm-huelle > .tm-kopf { grid-area: kopf; min-width: 0; }
  .tm-huelle > .tm-reiterleiste { grid-area: reiter; min-width: 0; }
  .tm-huelle > .tm-navi { grid-area: navi; min-width: 0; }
  .tm-huelle > .tm-spine { grid-area: spine; min-width: 0; }
  .tm-huelle > .tm-arbeit { grid-area: inhalt; min-width: 0; }
  .tm-huelle > .tm-fuss { grid-area: fuss; min-width: 0; }

  /* DIE ZWEITE HAELFTE DERSELBEN REGEL (Ralf, 02.08.2026, N17.6).
   *
   * `min-width: 0` sagt: das Kind muss nicht so breit werden wie sein
   * Inhalt. Es sagt NICHT, was mit dem Inhalt geschieht, der nicht
   * hineinpasst. Bei `overflow: visible` — der Vorgabe — malt der
   * Browser ihn ausserhalb der Kiste weiter und rechnet ihn zum
   * scrollbaren Bereich des DOKUMENTS. Das Raster bleibt schmal, und
   * die Seite scrollt trotzdem waagerecht. Genau das war noch offen,
   * nachdem N15.1 die Breite in Ordnung gebracht hatte.
   *
   * Der auffaellige Fall ist die linke Spalte: jeder Eintrag traegt
   * `white-space: nowrap`, und die Spalte ist 14 rem breit. Ein langer
   * App-Name lief bisher stillschweigend nach rechts hinaus — sichtbar
   * nicht in der Spalte, sondern daran, dass die ganze Seite wanderte.
   *
   * `clip` und nicht `hidden`, wo die Achse allein steht: `hidden`
   * macht aus der Kiste einen Rollbehaelter, und ein Rollbehaelter
   * kann `position: sticky` darin brechen und laesst sich mit der
   * Tastatur verschieben, ohne dass eine Rolleiste zu sehen waere.
   * `clip` schneidet nur ab. Wo eine Achse rollen MUSS — die linke
   * Spalte senkrecht — steht `hidden`, weil `clip` neben `auto` ohnehin
   * zu `hidden` rechnet.
   *
   * Die Reiterleiste steht mit Absicht nicht in dieser Liste: sie
   * faengt ihren Ueberlauf selbst auf, und zwar in der Spur. Sie ist
   * die EINZIGE Stelle im Rahmen, an der etwas rollen darf. */
  .tm-huelle > .tm-kopf,
  .tm-huelle > .tm-spine,
  .tm-huelle > .tm-fuss { overflow-x: clip; }
  /* DER ARBEITSBEREICH IST DER ROLLER (N27.4). Waagerecht bleibt es
   * beim Abschneiden; `clip` neben `auto` rechnet der Browser ohnehin
   * zu `hidden`, und genau das ist hier auch gemeint. */
  .tm-huelle > .tm-arbeit { overflow-x: clip; overflow-y: auto; }
  .tm-huelle > .tm-navi { overflow-x: hidden; overflow-y: auto; }

  /* Damit der abgeschnittene Name nicht mitten im Buchstaben endet:
   * dieselbe Kuerzung wie am Reiter (N15.2). Der volle Name steht im
   * `title` und bleibt fuer das Vorleseprogramm im Text. */
  .tm-navi__wort {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Eingeklappt: nur die Kacheln bleiben. Der Wert steht am Raster,
   * damit eine Regel genuegt. */
  .tm-huelle--navi-zu { --tm-navi-breite: 4.75rem; }

  /* Ohne Anmeldung gibt es weder Navigation noch Spine — die
   * Anmeldeseite und die Offline-Seite laufen einspaltig. */
  .tm-huelle--schlicht {
    grid-template-areas: "kopf" "inhalt" "fuss";
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr auto;
  }

  /* Nackt: die Seite steht in einem Reiter-Platz. Kopfzeile,
   * Navigation und Spine stehen schon aussen herum — ein zweites Mal
   * waere Unsinn (T5, ADR 0006). */
  .tm-huelle--nackt {
    grid-template-areas: "inhalt";
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: 1fr;
    min-height: 0;
    /* Im Reiter-Platz bestimmt der Rahmen die Hoehe, nicht die Schale.
     * `100dvh` waere hier die Hoehe des Rahmenfensters und damit
     * zufaellig — deshalb zurueck auf `auto` (N27.4). */
    height: auto;
  }
  .tm-huelle--nackt .tm-arbeit { padding: var(--tm-space-md); }

  .tm-arbeit { padding: var(--tm-space-lg) var(--tm-space-md); }

  /* ENGER AUF /konto UND IN DER VERWALTUNG (Ralf, 04.08.2026, N27.5).
   *
   * „Schriftgroessen und Abstaende um 10 % verkleinern. Nur diese
   * Seiten, nicht global."
   *
   * WARUM EINE KLASSE AM ARBEITSBEREICH UND KEINE ZWEITE FASSUNG DER
   * BAUTEILE: die Abstaende der Karten, Formulare und Tabellen kommen
   * alle aus --tm-space-*. Werden diese Werte hier neu gesetzt, erben
   * sie alle Kinder auf einen Schlag — und ausserhalb dieses `main`
   * aendert sich nichts. Kopfzeile, linke Spalte, Reiterleiste und
   * Fusszeile behalten ihre Masse, weil sie nicht darin stehen.
   *
   * DIE SCHRIFTGROESSEN MUESSEN EINZELN DASTEHEN. `font-size` erbt
   * zwar, aber h1 bis h3 sind in `rem` gesetzt und damit an die WURZEL
   * gebunden, nicht an den Vorfahren — sie wuerden sonst unveraendert
   * bleiben und die Ueberschriften waeren ploetzlich gross gegen ihren
   * Text.
   *
   * WAS AUSDRUECKLICH NICHT SCHRUMPFT: --tm-control-height. Das ist die
   * Trefferflaeche fuer den Finger (Konzept §9.8, 44 px), keine
   * Dichteangabe. Die beiden auseinanderzuhalten ist dieselbe Regel wie
   * bei der linken Spalte (ADR 0011 N4).
   *
   * UNTERGRENZE 14 px. 0.9 * 16 px = 14.4 px fuer den Fliesstext;
   * `.tm-klein` steht bei 0.875 rem = 14 px und bleibt, wo es ist —
   * eine Zeile tiefer waere es unter der Grenze. */
  .tm-arbeit--dicht {
    font-size: 0.9rem;             /* 14.4 px */
    --tm-space-sm: 7px;            /* aus 8  */
    --tm-space-md: 14px;           /* aus 16 */
    --tm-space-lg: 22px;           /* aus 24 */
    --tm-space-xl: 36px;           /* aus 40 */
  }
  .tm-arbeit--dicht h1 { font-size: 1.44rem; }    /* aus 1.6   */
  .tm-arbeit--dicht h2 { font-size: 1.125rem; }   /* aus 1.25  */
  .tm-arbeit--dicht h3 { font-size: 0.97rem; }    /* aus 1.075 */
  .tm-arbeit--dicht h4,
  .tm-arbeit--dicht h5,
  .tm-arbeit--dicht h6 { font-size: 0.9rem; }
  /* Die grossen Kennzahlen auf /konto tragen die Dichte mit: 1.5 rem
   * neben 14.4 px Fliesstext waere ein Sprung, den es vorher nicht gab. */
  .tm-arbeit--dicht .tm-kennwerte dd { font-size: 1.35rem; }
  /* DIE UNTERGRENZE GILT AUCH FUER DAS KLEINGEDRUCKTE. Der Hinweis
   * unter einem Feld steht bei 0.85 rem = 13.6 px und faellt damit
   * unter die 14 px, die Ralf gezogen hat („Mindestens 6 Zeichen." auf
   * /konto, gemessen 04.08.2026). Er wird auf diesen Seiten also
   * GROESSER, nicht kleiner — die Grenze schlaegt die Verkleinerung.
   * Ausserhalb bleibt er, wo er war: die Regel gilt fuer die Seiten,
   * fuer die Ralf sie gezogen hat. */
  .tm-arbeit--dicht .tm-feld__hinweis { font-size: 0.875rem; }

  /* --- Linke Navigation ---------------------------------------------- */

  /* DICHTE UND TREFFERFLAECHE SIND ZWEI VERSCHIEDENE MASSE.
   *
   * --tm-navi-hoehe ist die DICHTE: wie viel Inhalt auf einen
   * Bildschirm passt. Am Mauszeiger darf sie klein sein.
   * --tm-control-height ist die TREFFERFLAECHE: wie gross ein Ziel
   * sein muss, damit ein Finger es trifft. Sie bleibt 44 px (Konzept
   * §9.8, ADR 0009 N3/N6) und wird unten unter `any-pointer: coarse`
   * zurueckgeholt.
   *
   * Wer die beiden zusammenzieht, verliert eines von beidem: entweder
   * ist der Hub am Rechner luftig wie ein Tablet, oder er ist am
   * Tablet nicht mehr zu treffen. Zwischen den beiden ist nicht
   * abzuwaegen (T10, ADR 0011, Nachtrag N4). */
  .tm-navi {
    --tm-navi-hoehe: 2.125rem;     /* 34 px — Dichte, nicht Trefferflaeche */
    /* EINE LINIE STATT ZWEIER KANTEN (Ralf, 06.08.2026, N30.3):
     * „Die Gruppen fangen dann an, wo der Frame der Apps anfängt."
     *
     * Die Spalte steht NEBEN der Reiterleiste (Raster in ADR 0011,
     * Abschnitt 1) und beginnt deshalb 44 px weiter oben als die
     * Arbeitsflaeche. Gemessen am 06.08.2026: die Leiste endet bei
     * 90 px, die erste Gruppe fing bei 102 px an — 12 px daneben, und
     * genau das sieht man.
     *
     * 6 px statt 8: darunter steht der Knopf „Alle schliessen" mit
     * 34 px Hoehe und 4 px Luft. 46 + 6 + 34 + 4 = 90 — seine
     * Trennlinie liegt damit AUF der Unterkante der Reiterleiste, und
     * darunter fangen Gruppen und Arbeitsflaeche gemeinsam an.
     *
     * Die Zahl haengt an drei anderen. Wer eine davon aendert, prueft
     * das hier mit: tests/test_gemessene_flaeche.py rechnet nach. */
    --tm-navi-kopfluft: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--tm-navi-kopfluft) var(--tm-space-xs) var(--tm-space-sm);
    background: var(--tm-chrome);
    color: var(--tm-text-auf-chrome);
    font-size: 0.9375rem;
  }
  .tm-navi__punkt {
    display: flex;
    align-items: center;
    gap: var(--tm-space-sm);
    min-height: var(--tm-navi-hoehe);
    padding: 0 var(--tm-space-sm);
    border-radius: var(--tm-radius-control);
    border: 1px solid transparent;
    /* Beide Seiten ausdruecklich, nie geerbt und nie durchscheinend:
     * ein Paar, das nicht im Stylesheet steht, kann kein Test messen
     * (T10, der Kontrastfehler vom 31.07.2026). */
    color: var(--tm-text-auf-chrome);
    background: var(--tm-chrome);
    text-decoration: none;
    white-space: nowrap;
    /* Der Eintrag darf schmaler sein als sein Name — sonst kuerzt
     * `.tm-navi__wort` darunter nichts (N17.6). */
    min-width: 0;
  }
  .tm-navi__punkt:hover { background: var(--tm-chrome-hoch); }
  /* Der aktive Punkt traegt drei Merkmale: hellerer Grund, goldener
   * Rand, fette Schrift. Faellt die Farbe aus, bleiben zwei. */
  .tm-navi__punkt[aria-current] {
    background: var(--tm-chrome-hoch);
    border-color: var(--tm-gold-rand);
    font-weight: 600;
  }
  .tm-navi__symbol {
    flex: 0 0 auto;
    display: inline-grid;
    place-items: center;
    width: 24px;
    font-size: 0.95rem;
  }

  /* DAS KUERZEL TRAEGT NUR DEN SCHMALEN STREIFEN (T10, 31.07.2026).
   *
   * Neben dem Namen ist „RZ" doppelt gemoppelt: es sagt nichts, was
   * „Rezeptzentrum" daneben nicht schon sagt, und nimmt der Zeile
   * 24 px + Abstand — in einer Spalte, deren Zweck der Name ist.
   *
   * Eingeklappt ist es dagegen das Einzige, was bleibt. Deshalb steht
   * es weiter in der Vorlage und wird hier ein- und ausgeblendet: die
   * Frage ist nicht, OB es gebraucht wird, sondern WANN. Die Kacheln
   * auf der Startseite behalten es ohnehin — dort ist es das Bild. */
  .tm-navi__symbol--kuerzel { display: none; }
  .tm-huelle--navi-zu .tm-navi__symbol--kuerzel { display: inline-grid; }
  .tm-navi__schalter { margin-top: auto; }
  .tm-navi a:focus-visible,
  .tm-navi button:focus-visible { outline-color: var(--tm-gold); }

  /* Ein Punkt, der etwas AENDERT, ist ein Knopf und kein Verweis: eine
   * App als Reiter zu oeffnen und eine Gruppe zu klappen sind
   * Aenderungen (T10, ADR 0011). Er soll deshalb wie ein Knopf
   * funktionieren und wie ein Navigationspunkt aussehen. */
  /* HIER LAG DER FEHLER VOM 31.07.2026 (T10, Nachtrag N4).
   *
   * Die Regel setzte `background: transparent` und verliess sich
   * darauf, dass die Flaeche der Spalte durchscheint. Ein `<button>`
   * bringt aber eine eigene, HELLE Flaeche mit — die des Betriebs-
   * systems —, und die kam durch: helle Schrift auf hellem Grund in
   * einer dunklen Spalte. Vorher hing dieselbe Erklaerung an einem
   * einzigen Schalter mit `style="…"`; mit T10 wurden daraus einundzwanzig
   * Knoepfe.
   *
   * Deshalb jetzt: die native Darstellung ausdruecklich abschalten UND
   * beide Farben benennen. Eine Farbe, die nicht dasteht, kann kein
   * Test messen — genau daran ist der Kontrast-Test vorbeigelaufen. */
  .tm-navi__punkt--knopf {
    appearance: none;
    -webkit-appearance: none;
    width: 100%;
    background: var(--tm-chrome);
    color: var(--tm-text-auf-chrome);
    font: inherit;
    font-size: inherit;
    text-align: left;
    cursor: pointer;
  }
  .tm-navi__punkt--app .tm-navi__symbol { font-size: 0.72rem; letter-spacing: 0.02em; }

  /* Ueberschrift einer Zone: „Angeheftet", „Hub-Verwaltung". Kein
   * Bedienelement — sie fuehrt nirgendwohin. */
  .tm-navi__titel {
    margin: var(--tm-space-xs) 0 2px;
    padding-inline: var(--tm-space-sm);
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    /* Ein Ton, kein `opacity`: gedaempft wird ueber die Farbe, damit
     * das Paar messbar bleibt. */
    color: var(--tm-text-auf-chrome-leise);
  }

  /* „Alle schliessen" oben in der Spalte (Ralf, 05.08.2026, N29.1).
   *
   * Es ist kein Punkt der Navigation, sondern eine Handlung an den
   * Reitern — und steht deshalb abgesetzt, mit einer Linie darunter.
   * Aussehen und Trefferflaeche bleiben die eines Punktes: eingeklappt
   * ist die Spalte ein Streifen, und dort muss diese Zeile so gross und
   * so hoch sein wie jede andere, sonst trifft der Finger daneben.
   *
   * Das Kreuz sitzt im Symbolkasten der Spalte, nicht in einem eigenen:
   * die Zeichen der Spalte stehen alle auf derselben senkrechten Linie,
   * und ein Kreuz, das zwei Pixel daneben steht, faellt genau dort auf. */
  /* ROT, WEIL ER ETWAS WEGNIMMT (Ralf, 06.08.2026, N30.1).
   *
   * Er ist der einzige Punkt der Spalte, der nicht hinfuehrt, sondern
   * abraeumt. Die Farbe sagt das, bevor jemand den Text liest.
   *
   * NICHT --tm-kritisch: das ist im hellen Thema ein dunkles Rot fuer
   * die helle Arbeitsflaeche und erreicht auf dem dunklen Chrome
   * 1.82:1. Begruendung und Zahlen stehen bei den Farbtokens oben.
   *
   * Rot ist hier das VIERTE Merkmal, nicht das einzige: Stellung ganz
   * oben, das Kreuz als Zeichen, der eigene Abschluss nach unten und
   * dann die Farbe. Wer sie nicht sieht, verliert nichts (dieselbe
   * Regel wie bei Gruppe und App, N17.4). */
  .tm-navi__alle {
    margin-bottom: var(--tm-space-xs);
    padding-bottom: var(--tm-space-xs);
    border-bottom: 1px solid var(--tm-chrome-hoch);
    color: var(--tm-kritisch-auf-chrome);
  }
  .tm-navi__alle:hover,
  .tm-navi__alle:focus-visible { color: var(--tm-kritisch-auf-chrome-hoch); }
  .tm-navi__alle .tm-navi__symbol { font-size: 1.05rem; line-height: 1; }

  /* WO NICHTS ANGEHEFTET IST, STEHT NICHTS (T10, ADR 0011 N15.5).
   *
   * Hier stand bis zum 01.08.2026 `.tm-navi__leer` — ein gestrichelter
   * Kasten mit dem Satz „Hier steht, was Sie anheften …". Die
   * Begruendung war „eine leere Spalte liest sich als Fehler". Sie
   * stimmt fuer eine Spalte, die ganz leer ist; diese ist es nicht, sie
   * traegt darunter alle Gruppen. Der Kasten stand also dauerhaft im
   * Bild und erklaerte eine Handlung an einer Stelle, an der man sie
   * gar nicht ausfuehren kann. Der Satz steht jetzt einmal am
   * Anheften-Knopf im Reitermenue — dort, wo man anheftet. */

  /* GRUPPE UND APP SIND ZWEIERLEI — UND MUESSEN ES AUSSEHEN
   * (T10, 31.07.2026).
   *
   * Bis dahin standen beide als gleich grosse Zeilen untereinander,
   * unterschieden nur durch ein Dreieck und eine hellere Flaeche im
   * aufgeklappten Zustand. Ralf konnte am Bildschirm nicht sagen, was
   * eine Gruppe und was eine App ist — und wer das nicht sieht, klickt
   * auf eine Ueberschrift und wartet, dass etwas aufgeht.
   *
   * Die Gruppe ist eine UEBERSCHRIFT: kleiner, leiser, gesperrt
   * gesetzt, mit Dreieck, ohne eigene Flaeche.
   * Die App ist ein EINTRAG: normale Groesse, eingerueckt, mit Flaeche
   * unter dem Zeiger und im aktiven Zustand.
   *
   * VIER MERKMALE, NICHT EINES. Schriftgroesse, Versalien, Sperrung und
   * Einrueckung tragen den Unterschied auch dann, wenn die Farbe
   * ausfaellt — bei Farbenblindheit, in Graustufen, auf einem
   * ausgeblichenen Bildschirm. Ein Unterschied, der allein an der Farbe
   * haengt, ist fuer einen Teil der Leute gar keiner. Der Test dazu
   * steht in tests/test_arbeitsflaeche.py und zaehlt sie.
   *
   * Die Hoehe bleibt --tm-navi-hoehe. Sie kleiner zu machen waere
   * bequem und falsch: unter `any-pointer: coarse` ist das die
   * Trefferflaeche, und eine Ueberschrift, die man klappt, ist ein
   * Ziel wie jedes andere (Dichte ist nicht Trefferflaeche, N4). */
  .tm-navi__gruppe {
    margin-top: var(--tm-space-sm);
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    /* Der Sandton statt des grauen Leise-Tons (N17.4). Er traegt den
     * Unterschied zur App am FARBTON und nicht nur an der Helligkeit —
     * und zwar in JEDEM Zustand: haette der aufgeklappte Zustand wie
     * bisher auf Weiss gewechselt, waere die Farbe genau dann weg,
     * wenn man sie am meisten sieht. Aufgeklappt ist der Normalfall. */
    color: var(--tm-gruppe-auf-chrome);
    background: var(--tm-chrome);
  }
  /* Das Dreieck bleibt lesbar gross — es ist die Richtungsangabe und
   * eingeklappt das Einzige, was von der Zeile bleibt. */
  .tm-navi__gruppe .tm-navi__symbol { font-size: 0.9rem; }

  /* Ohne eigene Flaeche, auch unter dem Zeiger und aufgeklappt: eine
   * Ueberschrift, die sich wie eine Zeile fuellt, ist wieder eine
   * Zeile. Die Rueckmeldung gibt stattdessen die Schrift — heller im
   * selben Ton, nicht in einem anderen. */
  .tm-navi__gruppe:hover {
    background: var(--tm-chrome);
    color: var(--tm-gruppe-auf-chrome-hoch);
    text-decoration: underline;
  }
  .tm-navi__gruppe[aria-expanded="true"] {
    background: var(--tm-chrome);
    color: var(--tm-gruppe-auf-chrome-hoch);
  }

  /* Eingerueckt unter ihrer Ueberschrift. Ungruppierte Apps bekommen
   * das nicht — eingerueckt unter nichts waere eine Behauptung. */
  .tm-navi__punkt--in-gruppe { padding-left: var(--tm-space-lg); }

  /* Abgesetzt: zwischen Apps und Hub-Verwaltung liegt eine Linie und
   * der ganze uebrige Platz. */
  /* Luft unter der Kontozeile (Ralf, 02.08.2026, N17.3). Sie sass
   * direkt am unteren Bildschirmrand — `margin-top: auto` schiebt sie
   * ganz nach unten, und darunter kam nichts mehr. Das Mass ist
   * dasselbe, das .tm-navi oben traegt: die Spalte ist damit oben und
   * unten gleich gefasst. */
  .tm-navi__unten {
    margin-top: auto;
    padding-top: var(--tm-space-xs);
    padding-bottom: var(--tm-space-sm);
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  /* Die Kontozeile (T10, abgesetzt seit dem 31.07.2026).
   *
   * Sie stand zuerst in derselben Groesse und ohne Abstand zwischen den
   * uebrigen Zeilen — und verschwand darin. Sie ist aber keine weitere
   * Menuezeile, sondern die Antwort auf „wer bin ich hier gerade", und
   * das Zahnrad ist seit T10 der einzige Weg in die Verwaltung. Also:
   * eigene Trennlinie, eigene Luft, ein sichtbares Ziel. */
  .tm-navi__konto {
    display: flex;
    align-items: center;
    gap: var(--tm-space-sm);
    margin-top: var(--tm-space-sm);
    padding: var(--tm-space-sm) var(--tm-space-xs);
    border-top: 1px solid var(--tm-chrome-hoch);
    color: var(--tm-text-auf-chrome);
    background: var(--tm-chrome);
  }
  .tm-navi__initialen {
    flex: 0 0 auto;
    display: inline-grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border-radius: var(--tm-radius-rund);
    border: 1px solid var(--tm-gold-rand);
    background: var(--tm-chrome-hoch);
    color: var(--tm-text-auf-chrome);
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.02em;
  }
  .tm-navi__person {
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.25;
  }
  .tm-navi__personname {
    font-size: 0.8125rem;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .tm-navi__personrolle {
    font-size: 0.6875rem;
    color: var(--tm-text-auf-chrome-leise);
  }
  /* Die Zeile fuehrt auf /konto. Sie ist ein Ziel und muss sich als
   * eines lesen — ohne die Zeile in einen zweiten Knopf zu verwandeln:
   * das Zahnrad daneben fuehrt woanders hin (Verwaltung), und zwei
   * gleich aussehende Flaechen nebeneinander waeren eine Verwechslung
   * mit Folgen. Deshalb Flaeche erst bei Zeiger und Tastatur. */
  .tm-navi__kontoweg {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    gap: var(--tm-space-sm);
    min-width: 0;
    padding: var(--tm-space-xs);
    margin: calc(var(--tm-space-xs) * -1);
    border-radius: var(--tm-radius-control);
    /* AUSGESCHRIEBEN, NICHT GEERBT. `color: inherit` stand hier zuerst
     * und wurde vom Kontrasttest abgewiesen — zu Recht: eine geerbte
     * Farbe laesst sich nicht messen, und genau daran ist die linke
     * Spalte am 31.07.2026 schon einmal gescheitert (N4.1, N5.2). */
    color: var(--tm-text-auf-chrome);
    text-decoration: none;
  }
  .tm-navi__kontoweg:hover { background: var(--tm-chrome-hoch); }
  .tm-navi__kontoweg:focus-visible { background: var(--tm-chrome-hoch); }
  .tm-navi__kontoweg[aria-current] {
    background: var(--tm-chrome-hoch);
    box-shadow: inset 2px 0 0 var(--tm-gold-rand);
  }

  /* Ein Ziel, das man als Ziel erkennt: sichtbarer Rand und eine
   * eigene Flaeche. Ein blosses Zeichen auf gleichem Grund liest sich
   * als Verzierung, nicht als Knopf. */
  .tm-navi__zahnrad {
    flex: 0 0 auto;
    margin-left: auto;
    display: inline-grid;
    place-items: center;
    width: var(--tm-navi-hoehe);
    height: var(--tm-navi-hoehe);
    border-radius: var(--tm-radius-control);
    border: 1px solid var(--tm-chrome-hoch);
    color: var(--tm-text-auf-chrome);
    background: var(--tm-chrome-hoch);
    text-decoration: none;
    font-size: 1.1rem;
  }
  .tm-navi__zahnrad:hover { background: var(--tm-chrome-hoch); }
  .tm-navi__zahnrad[aria-current] {
    background: var(--tm-chrome-hoch);
    border-color: var(--tm-gold-rand);
  }

  /* Eingeklappt bleibt von der Kontozeile der Kreis — er ist die
   * Person, und er ist selbst im Streifen von 4.75 rem erkennbar. */
  .tm-huelle--navi-zu .tm-navi__konto { flex-direction: column; padding: 0; }
  .tm-huelle--navi-zu .tm-navi__kontoweg {
    flex-direction: column;
    margin: 0;
    padding: var(--tm-space-xs) 0;
  }
  .tm-huelle--navi-zu .tm-navi__zahnrad { margin-left: 0; }

  .tm-huelle--navi-zu .tm-navi__wort { display: none; }
  .tm-huelle--navi-zu .tm-navi__punkt { justify-content: center; padding: 0; }
  /* Eingeklappt bleibt nur die Spur der Symbole. Eine Ueberschrift
   * ohne Wort waere ein Strich ohne Aussage. */
  .tm-huelle--navi-zu .tm-navi__titel { display: none; }

  /* --- Fristen-Spine -------------------------------------------------
   * Kein Widget, sondern Teil des Rahmens: in jeder Ansicht sichtbar.
   * Ueberfaellig steht oben und ist hervorgehoben, danach der Verlauf
   * in die Zukunft (Konzept §9.4).
   */

  .tm-spine {
    display: flex;
    flex-direction: column;
    width: var(--tm-spine-breite);
    background: var(--tm-spine-flaeche);
    border-right: 1px solid var(--tm-linie);
  }
  .tm-spine__liste {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
  }
  .tm-spine__stufe {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    padding: var(--tm-space-sm) var(--tm-space-xs);
    border-bottom: 1px solid var(--tm-linie);
    /* Farbe nie allein: der Streifen links wiederholt, was Symbol und
     * Wort schon sagen. */
    border-left: 3px solid transparent;
    text-align: center;
  }
  .tm-spine__symbol { font-size: 0.95rem; line-height: 1; }
  .tm-spine__zahl {
    font-family: var(--tm-schrift-kopf);
    font-size: 1.25rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
  }
  .tm-spine__titel {
    font-size: 0.62rem;
    line-height: 1.15;
    color: var(--tm-text-gedaempft);
    hyphens: auto;
  }
  .tm-spine__quellen { display: none; }

  /* Ueberfaellig ist hervorgehoben — die einzige Stufe mit Flaeche. */
  .tm-spine__stufe--kritisch {
    border-left-color: var(--tm-kritisch);
    background: var(--tm-kritisch-flaeche);
  }
  .tm-spine__stufe--kritisch .tm-spine__symbol,
  .tm-spine__stufe--kritisch .tm-spine__zahl { color: var(--tm-kritisch); }
  .tm-spine__stufe--warnung { border-left-color: var(--tm-warnung); }
  .tm-spine__stufe--warnung .tm-spine__symbol,
  .tm-spine__stufe--warnung .tm-spine__zahl { color: var(--tm-warnung); }
  .tm-spine__stufe--info { border-left-color: var(--tm-info); }
  .tm-spine__stufe--info .tm-spine__symbol { color: var(--tm-info); }
  .tm-spine__stufe--ruhig .tm-spine__symbol { color: var(--tm-text-gedaempft); }

  /* Keine Angabe ist NICHT null. Eine stille Null waere eine
   * Falschaussage ueber Fristen (ADR 0005). */
  .tm-spine__zahl--keine {
    font-size: 1.1rem;
    color: var(--tm-text-gedaempft);
  }

  /* Beispielwerte: Schraffur UEBER dem Band, Beschriftung IM Band.
   * Ein Band, das „12 ueberfaellig" zeigt, kann in einer Praxis echte
   * Reaktionen ausloesen — deshalb keine Fussnote (ADR 0005, 3a). */
  .tm-spine--beispiel .tm-spine__liste {
    background-image: repeating-linear-gradient(
      45deg,
      var(--tm-spine-schraffur) 0 6px,
      transparent 6px 14px
    );
  }
  .tm-spine__beispiel {
    margin: 0;
    padding: var(--tm-space-xs) 2px;
    background: var(--tm-warnung-flaeche);
    border-bottom: 1px solid var(--tm-warnung);
    color: var(--tm-warnung);
    font-size: 0.58rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-align: center;
    text-transform: uppercase;
  }
  .tm-spine__stand {
    margin: 0;
    padding: var(--tm-space-xs) 2px;
    border-top: 1px solid var(--tm-linie);
    color: var(--tm-text-gedaempft);
    font-family: var(--tm-schrift-mono);
    font-size: 0.6rem;
    text-align: center;
  }

  /* Ab 80 rem bleibt die Spine dauerhaft aufgeklappt und zeigt, aus
   * welchen Apps die Zahlen kommen. */
  @media (min-width: 80rem) {
    .tm-huelle { --tm-spine-breite: 13rem; }
    .tm-spine__stufe {
      display: grid;
      grid-template-columns: 1.5rem auto 1fr;
      align-items: baseline;
      gap: var(--tm-space-xs) var(--tm-space-sm);
      text-align: left;
      padding: var(--tm-space-sm);
    }
    .tm-spine__titel { font-size: 0.8rem; }
    .tm-spine__quellen {
      display: block;
      grid-column: 1 / -1;
      list-style: none;
      margin: var(--tm-space-xs) 0 0;
      padding: 0;
      font-size: 0.72rem;
      color: var(--tm-text-gedaempft);
    }
    .tm-spine__quelle {
      display: flex;
      justify-content: space-between;
      gap: var(--tm-space-xs);
    }
    .tm-spine__quelle--beispiel::after { content: "≈"; }
    .tm-spine__beispiel { font-size: 0.7rem; letter-spacing: 0.1em; }
  }

  /* --- Untere Navigation (Telefon) ----------------------------------- */

  .tm-untennavi { display: none; }

  /* --- Umschalter fuer das Thema -------------------------------------
   * Ein Formular, kein Skript: die Wahl gehoert der Benutzerin auch
   * dann, wenn JavaScript nicht laeuft.
   */

  /* DIE ZEILE BESTIMMT DIE HOEHE, AUCH HIER (04.08.2026, N27.6).
   *
   * `padding: 2px` machte die Gruppe 38 px hoch: 32 + 4 + 2 Rand. Die
   * Kopfzeile gibt ihrem Inhalt 36 px (44 minus 2×4 Innenmass), und die
   * zwei Pixel darueber schoben die ganze Zeile auf 46 px — gemessen am
   * 04.08.2026, als die Wahl aus /konto zurueck in die Kopfzeile kam.
   * Vorher fiel es nicht auf, weil sie dort nur unangemeldet stand.
   *
   * Mit 1 px passt beides genau: am Mauszeiger 32+2+2 = 36, am Finger
   * 44+2+2 = 48 in einer 56-px-Zeile. N20.2 gilt damit weiter — die
   * Zeile bestimmt die Hoehe, nicht das, was hineingelegt wird. */
  .tm-themenwahl {
    display: flex;
    gap: 2px;
    padding: 1px;
    border: 1px solid rgba(255, 255, 255, 0.22);
    border-radius: var(--tm-radius-rund);
  }
  .tm-themenwahl button {
    min-height: 32px;
    min-width: 34px;
    padding: 0 var(--tm-space-sm);
    border: none;
    border-radius: var(--tm-radius-rund);
    background: transparent;
    color: inherit;
    cursor: pointer;
    font-size: 0.85rem;
  }
  .tm-themenwahl button:hover { background: rgba(255, 255, 255, 0.14); }
  .tm-themenwahl button[aria-pressed="true"] {
    background: rgba(255, 255, 255, 0.18);
    box-shadow: inset 0 0 0 1px var(--tm-gold-rand);
    font-weight: 600;
  }

  /* --- Tabellenkleinkram --------------------------------------------- */

  /* `.tm-zaehler` ist am 07.08.2026 entfallen (ADR 0011, N32.1).
   * Ralf: „dahinter stehen auch die Anzahlen. Die können weg."
   * Die Regel wird mit entfernt und nicht auskommentiert liegen
   * gelassen: eine tote Regel im Stylesheet ist eine Einladung, sie
   * wieder zu benutzen. */
  .tm-mitte { text-align: center; }
  .tm-nicht-fett { font-weight: 400; }

  /* DIE DREI STUFEN (Ralf, 07.08.2026, ADR 0016): Benutzer > Rolle >
   * Recht. Die Kette war gebaut und stand nirgends. Kein eigenes
   * Bauteil, nur ein Absatz mit Luft zwischen den Wortern — was mit
   * zwei Regeln auskommt, bekommt keine dritte. */
  .tm-stufen { display: flex; flex-wrap: wrap; gap: var(--tm-space-xs); }
  .tm-stufen > span[aria-hidden] { color: var(--tm-text-gedaempft); }

  /* Rechtematrix.
   *
   * BIS ZUM 07.08.2026 standen Wort UND Kennung uebereinander, in der
   * Kopfzeile wie in den Unterzeilen (N14.3). In den Unterzeilen ist
   * die Kennung seither weg: sie wiederholte fuenfundvierzigmal den
   * App-Namen, der eine Zeile darueber schon steht (N32.2).
   *
   * BEIDE REGELN BLEIBEN. `.tm-recht__code` traegt weiterhin die
   * Kopfzeile jeder App — dort ist die Kennung die Wahrheit, sie steht
   * im Protokoll und wird am Telefon vorgelesen — und die drei
   * Hub-Rechte, fuer die es kein Aktionswort gibt. `display: block`
   * gilt also nach wie vor fuer einen Fall, in dem beide auftreten. */
  .tm-recht__wort { display: block; }
  .tm-recht__code {
    display: block;
    font-size: 0.75rem;
    color: var(--tm-text-gedaempft);
  }
  /* --- DIE RECHTEMATRIX ALS EINE TABELLE (Ralf, 06.08.2026, N31.1)
   *
   * „Die Rechtematrix ist unbenutzbar." Sie war es durch Bauart: je App
   * eine eigene Karte mit eigener Kopfzeile und eigener Tabelle —
   * sechzehnmal dieselben Spaltenkoepfe, mehrere Bildschirme weit. Wer
   * zwei Apps vergleichen wollte, musste sich die Spalten merken.
   *
   * Jetzt EINE Tabelle: je App eine Zeile mit dem Recht, das ueber
   * alles andere entscheidet (`oeffnen`). Die drei uebrigen erscheinen
   * beim Aufklappen — sie sind gegenstandslos, solange Oeffnen fehlt.
   *
   * Der Spaltenkopf ist ohnehin `position: sticky` (siehe .tm-tabelle);
   * genau das war in sechzehn getrennten Tabellen wirkungslos, weil
   * jede ihren eigenen mitbrachte. Erst mit einer Tabelle traegt er. */

  .tm-rechte__klappe {
    display: inline-flex;
    align-items: baseline;
    gap: var(--tm-space-xs);
    color: inherit;
    text-decoration: none;
  }
  .tm-rechte__klappe:hover .tm-rechte__name,
  .tm-rechte__klappe:focus-visible .tm-rechte__name { text-decoration: underline; }
  /* Feste Breite, damit die Namen untereinander an derselben Stelle
   * anfangen — mit und ohne Pfeil. Ein Zeichen, das die Spalte
   * verschiebt, liest sich als Unruhe. */
  .tm-rechte__pfeil { width: 0.9em; flex: none; font-size: 0.8em; }
  .tm-rechte__name { font-weight: 600; }
  /* Die Unterzeilen gehoeren sichtbar zur Zeile darueber: eingerueckt,
   * leiser Grund, und der Trennstrich faellt weg. DREI Merkmale, damit
   * die Zusammengehoerigkeit auch in Graustufen traegt. */
  .tm-rechte__unterzeile > th,
  .tm-rechte__unterzeile > td {
    background: var(--tm-flaeche-tief);
    border-bottom: none;
  }
  .tm-rechte__aktion { padding-left: var(--tm-space-lg) !important; }
  .tm-rechte__kopf > th,
  .tm-rechte__kopf > td { border-top: 1px solid var(--tm-linie); }

  .tm-brotkrumen { margin: 0; font-size: 0.9rem; color: var(--tm-text-gedaempft); }
  .tm-einzelheiten { font-size: 0.85rem; }
  .tm-einzelheiten .tm-paar { display: block; }

  .tm-paare {
    margin: 0;
    display: grid;
    grid-template-columns: 3rem 1fr;
    gap: var(--tm-space-xs) var(--tm-space-md);
  }
  .tm-paare dt { font-family: var(--tm-schrift-mono); font-weight: 600; }
  .tm-paare dd { margin: 0; }

  /* --- Schalter der Rechtematrix ------------------------------------- */

  .tm-schalter {
    width: 34px;
    height: 34px;
    font-weight: 700;
    border-radius: var(--tm-radius-control);
    border: 1px solid var(--tm-linie-kraeftig);
    background: var(--tm-karte);
    color: var(--tm-text-gedaempft);
    cursor: pointer;
  }
  .tm-schalter--an {
    background: var(--tm-gut);
    border-color: var(--tm-gut);
    color: #ffffff;
  }
  :root[data-thema="dunkel"] .tm-schalter--an { color: #16211c; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-thema="hell"]) .tm-schalter--an { color: #16211c; }
  }
  .tm-schalter--aus:hover { border-color: var(--tm-gut); color: var(--tm-gut); }

  /* --- Zustandsanzeige der Backends ---------------------------------- */

  .tm-zustandsliste { list-style: none; margin: 0; padding: 0; }
  .tm-zustand {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--tm-space-sm);
    min-height: var(--tm-row-height-dense);
    padding: var(--tm-space-sm);
    border-left: 3px solid var(--tm-linie);
    border-radius: var(--tm-radius-control);
  }
  .tm-zustand + .tm-zustand { margin-top: var(--tm-space-xs); }
  .tm-zustand__symbol { font-weight: 700; }
  .tm-zustand--gut { border-left-color: var(--tm-gut); }
  .tm-zustand--gut .tm-zustand__symbol { color: var(--tm-gut); }
  .tm-zustand--kritisch { border-left-color: var(--tm-kritisch); }
  .tm-zustand--kritisch .tm-zustand__symbol { color: var(--tm-kritisch); }
  .tm-zustand__name { font-weight: 600; min-width: 9rem; }
  .tm-zustand__text { color: var(--tm-text-gedaempft); }
  .tm-zustand__dauer {
    margin-left: auto;
    font-family: var(--tm-schrift-mono);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    color: var(--tm-text-gedaempft);
  }

  /* --- Tablet: Navigation einklappbar -------------------------------- */

  @media (max-width: 60rem) {
    .tm-huelle { --tm-navi-breite: 4.75rem; }
    .tm-navi__wort { display: none; }
    .tm-navi__punkt { justify-content: center; padding: 0; }
    /* Wer sie aufgeklappt haben will, bekommt sie aufgeklappt. */
    .tm-huelle--navi-auf { --tm-navi-breite: 14rem; }
    .tm-navi__titel { display: none; }
    .tm-huelle--navi-auf .tm-navi__wort { display: inline; }
    .tm-huelle--navi-auf .tm-navi__titel { display: block; }
    .tm-huelle--navi-auf .tm-navi__punkt {
      justify-content: flex-start;
      padding: 0 var(--tm-space-sm);
    }
    /* Im Streifen traegt das Kuerzel die Zeile — hier steht ja kein
     * Name daneben. Aufgeklappt faellt es wieder weg. */
    .tm-navi__symbol--kuerzel { display: inline-grid; }
    .tm-huelle--navi-auf .tm-navi__symbol--kuerzel { display: none; }
    /* Aufgeklappt bleibt die Einrueckung; im Streifen nicht, dort ist
     * jede Zeile mittig und traegt nur ein Zeichen. */
    .tm-huelle--navi-auf .tm-navi__punkt--in-gruppe {
      padding-left: var(--tm-space-lg);
    }
  }

  /* --- Telefon: untere Navigation, Spine als Zeile -------------------
   * Bedienbar bis 360 px (Konzept §9.9). Ein dauerhaftes Band am
   * linken Rand und 360 px Gesamtbreite schliessen einander aus —
   * also wird das Band um 90 Grad gedreht, statt zu verschwinden.
   */

  @media (max-width: 46rem) {
    .tm-huelle {
      grid-template-areas: "kopf" "spine" "inhalt" "fuss";
      grid-template-columns: minmax(0, 1fr);
      grid-template-rows: auto auto 1fr auto;
    }
    .tm-huelle > .tm-navi { display: none; }
    .tm-arbeit {
      padding: var(--tm-space-md) var(--tm-space-sm);
      /* Platz fuer die untere Navigation. */
      padding-bottom: calc(var(--tm-control-height) + var(--tm-space-xl));
    }

    .tm-spine {
      /* Bezugsrahmen, weil sie hier waagerecht rollt: sonst faellt eine
       * absolut gesetzte Spanne darin aus dem Ueberlauf heraus und
       * schiebt die SEITE. Dieselbe Ursache wie an der Reiterspur
       * (03.08.2026, ADR 0011 N22.1) — hier vorsorglich, gefunden hat
       * es der Test, nicht die Messung. */
      position: relative;
      width: auto;
      flex-direction: row;
      align-items: stretch;
      border-right: none;
      border-bottom: 1px solid var(--tm-linie);
      overflow-x: auto;
      scrollbar-width: thin;
    }
    .tm-spine__liste { flex-direction: row; }
    .tm-spine__stufe {
      flex-direction: row;
      align-items: baseline;
      gap: var(--tm-space-xs);
      border-bottom: none;
      border-right: 1px solid var(--tm-linie);
      border-left: none;
      border-top: 3px solid transparent;
      white-space: nowrap;
    }
    .tm-spine__stufe--kritisch { border-top-color: var(--tm-kritisch); }
    .tm-spine__stufe--warnung { border-top-color: var(--tm-warnung); }
    .tm-spine__stufe--info { border-top-color: var(--tm-info); }
    .tm-spine__titel { font-size: 0.7rem; }
    .tm-spine__zahl { font-size: 1rem; }
    .tm-spine__beispiel {
      display: grid;
      place-items: center;
      border-bottom: none;
      border-right: 1px solid var(--tm-warnung);
      padding-inline: var(--tm-space-sm);
      writing-mode: horizontal-tb;
    }
    .tm-spine__stand { display: none; }

    .tm-untennavi {
      position: fixed;
      inset: auto 0 0 0;
      z-index: 20;
      display: flex;
      justify-content: space-around;
      gap: 2px;
      padding: var(--tm-space-xs);
      padding-bottom: max(var(--tm-space-xs), env(safe-area-inset-bottom));
      background: var(--tm-chrome);
      border-top: 2px solid var(--tm-gold);
    }
    .tm-untennavi__punkt {
      flex: 1 1 0;
      min-width: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 2px;
      min-height: var(--tm-control-height);
      padding: 2px;
      border-radius: var(--tm-radius-control);
      color: var(--tm-text-auf-chrome);
      text-decoration: none;
      font-size: 0.66rem;
      text-align: center;
    }
    .tm-untennavi__punkt[aria-current] {
      background: rgba(255, 255, 255, 0.16);
      box-shadow: inset 0 -2px 0 var(--tm-gold-rand);
      font-weight: 600;
    }
    .tm-untennavi__wort {
      max-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .tm-untennavi__symbol { font-size: 1.05rem; line-height: 1; }
    .tm-untennavi a:focus-visible { outline-color: var(--tm-gold); }
  }

  @media print {
    .tm-spine, .tm-navi, .tm-untennavi { display: none !important; }
    /* `height: auto` gehoert dazu: eine Schale von 100dvh druckt EINE
     * Seite und schneidet den Rest ab (N27.4). */
    .tm-huelle { display: block; height: auto; }
    .tm-huelle > .tm-arbeit { overflow: visible; }
  }
}

/* ====================================================================
 * 5 — REITER MIT FACHLICHEM KONTEXT (T5, ADR 0006)
 *
 * Ergaenzung, kein Bruch. Die Grundformen .tm-reiter, .tm-reiterleiste
 * und .tm-badge stehen seit T3 weiter oben; hier kommt dazu, was die
 * Leiste bedienbar macht und was den Arbeitsbereich teilt.
 * ==================================================================== */

@layer tamedes.komponenten {

  /* --- Reiterleiste --------------------------------------------------- */

  /* ALLE GLEICH BREIT, LANGE NAMEN GEKUERZT (T10, ADR 0011 N15.2).
   *
   * `flex: 0 0 var(--tm-reiter-breite)` — weder wachsen noch schrumpfen.
   * Waeren sie schrumpfbar, verteilte die Leiste die Enge auf alle und
   * am Ende passte gar kein Name mehr; so bleibt jeder Reiter lesbar
   * und die Leiste scrollt stattdessen. Das ist der Grund, warum
   * Punkt 1 (nur die Leiste scrollt) VOR diesem hier stehen musste. */
  .tm-reitergruppe {
    display: flex;
    align-items: stretch;
    gap: 0;
    flex: 0 0 var(--tm-reiter-breite);
    min-width: 0;
    max-width: var(--tm-reiter-breite);
    border-radius: var(--tm-radius-control) var(--tm-radius-control) 0 0;
    background: var(--tm-reiter-ruhe);
    color: var(--tm-reiter-ruhe-text);
  }
  .tm-reitergruppe:hover { background: var(--tm-reiter-ruhe-zeiger); }
  .tm-reitergruppe:has(.tm-reiter[aria-selected="true"]) {
    background: var(--tm-flaeche);
  }
  .tm-reitergruppe .tm-reiter {
    background: transparent;
    flex: 1 1 auto;
    min-width: 0;
    padding-inline: var(--tm-space-sm);
  }

  /* Der Name traegt die Kuerzung, nicht der Reiter: `text-overflow`
   * wirkt nur an der Kiste, in der der Text wirklich steht. Der volle
   * Name bleibt am `title` des Reiters und im Vorleseprogramm — er ist
   * verkuerzt, nicht verloren. */
  .tm-reiter__name {
    font-weight: inherit;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .tm-reiter__kontext {
    font-size: 0.78rem;
    opacity: 0.85;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 0 1 auto;
  }
  .tm-reiter__nadel { font-size: 0.7rem; }

  /* Menue je Reiter. <details> statt Skript: Klick und Tastatur
   * koennen das von sich aus, und ohne JavaScript bleibt es
   * bedienbar. */
  /* Das Schliessen-Formular ist das Flexkind, nicht der Knopf darin —
   * ohne diese Zeile haengt das x oben in der gestreckten Zeile und
   * schrumpft mit, sobald der Name lang wird. */
  /* NUR NOCH DIE REITERGRUPPE. `.tm-reiterleiste > form` galt dem
   * Knopf „Alle schliessen", solange er an der Leiste hing; seit N22.4
   * steht er in der Kopfzeile und seit N28.4 dort links. Die Regel traf
   * seit dem 03.08.2026 nichts mehr (05.08.2026). */
  .tm-reitergruppe > form {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
  }

  .tm-reiter__menue { position: relative; display: flex; flex: 0 0 auto; }
  .tm-reiter__mehr {
    display: grid;
    place-items: center;
    width: 26px;
    cursor: pointer;
    list-style: none;
    color: inherit;

    /* NUR AM ZEIGER, AM FOKUS UND AM AKTIVEN REITER
     * (Ralf, 04.08.2026, ADR 0011 N27.3).
     *
     * Das Menue traegt sechs Funktionen und bleibt deshalb — aber es
     * stand an jedem der neun Reiter dauernd im Bild und nahm dort
     * Platz weg, an dem der Name knapp ist.
     *
     * `visibility: hidden` UND NICHT `display: none`: der Kasten bleibt
     * im Satz stehen, seine 26 px bleiben reserviert, und der Reiter
     * springt beim Ein- und Ausblenden nicht. Genau das war die
     * Bedingung. Mit `display: none` waere jeder Reiter beim
     * Darueberfahren 26 px schmaler geworden — und ein Ziel, das unter
     * dem Zeiger seine Groesse aendert, ist schwerer zu treffen als
     * eines, das steht.
     *
     * DER AKTIVE REITER ZEIGT ES IMMER. Am Tablet gibt es keinen
     * Zeiger; ohne diese Zeile waere das Menue dort nur ueber die
     * Tastatur erreichbar, und eine Tastatur hat das Empfangstablet
     * auch nicht. Der aktive Reiter ist die eine Stelle, an der man
     * ohne Zeiger sicher hinkommt. */
    visibility: hidden;
  }
  .tm-reitergruppe:hover .tm-reiter__mehr,
  .tm-reitergruppe:focus-within .tm-reiter__mehr,
  .tm-reitergruppe:has([aria-selected="true"]) .tm-reiter__mehr {
    visibility: visible;
  }
  .tm-reiter__mehr::-webkit-details-marker { display: none; }
  .tm-reiter[aria-selected="true"] ~ .tm-reiter__menue .tm-reiter__mehr,
  .tm-reitergruppe:has([aria-selected="true"]) .tm-reiter__mehr,
  .tm-reitergruppe:has([aria-selected="true"]) .tm-reiter__schliessen {
    color: var(--tm-text);
  }
  .tm-reiter__auswahl {
    position: absolute;
    z-index: 30;
    top: 100%;
    left: 0;
    min-width: 12rem;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--tm-space-xs);
    background: var(--tm-karte);
    color: var(--tm-text);
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
    box-shadow: var(--tm-schatten-hoch);
  }
  .tm-reiter__auswahl form { display: flex; gap: 2px; }
  .tm-reiter__auswahl .tm-knopf { flex: 1 1 auto; justify-content: flex-start; }

  /* Der Plusknopf nimmt die feste Reiterbreite NICHT an: er ist kein
   * Reiter, sondern ein Knopf an der Leiste. Er ist seit N22.4 der
   * einzige — „Alle schliessen" stand hier bis dahin daneben. */
  .tm-reiter--neu {
    align-self: center;
    flex: 0 0 auto;
    min-height: 32px;
    padding-inline: var(--tm-space-md);
    font-size: 1.1rem;
    line-height: 1;
  }

  /* --- Plaetze -------------------------------------------------------
   * Zwei Plaetze, nicht drei. Geteilt wird, um zu vergleichen, nicht
   * um zu sammeln (ADR 0006).
   */

  .tm-plaetze {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--tm-space-md);
    height: 100%;
    min-height: 0;
  }

  /* DER LEERZUSTAND STEHT IN DER BUEHNE UND GEHT VON SELBST WEG
   * (Ralf, 04.08.2026, ADR 0011 N27.1).
   *
   * Bis dahin entschied der Server zwischen Buehne und Leerzustand.
   * Ohne belegten Platz gab es die Buehne gar nicht — und damit auch
   * nicht das `[data-buehne]`, an dem static/reiter.js haengt. Ein
   * Klick in der Leiste tat dann nichts.
   *
   * WARUM `:has()` UND NICHT DER SERVER: nach einem Reiterwechsel ohne
   * Neuladen hat der Server nichts mehr zu sagen. Das Stylesheet sieht
   * dagegen genau das, was das Skript setzt — `data-platz` am
   * Abschnitt. Dieselbe Stelle, die auch reiter.js liest; eine zweite
   * Wahrheit entsteht nicht.
   *
   * Der leere Wert muss ausgeschlossen werden: beim Abraeumen setzt das
   * Skript `data-platz=""` und nicht etwa das Attribut zurueck. */
  .tm-buehne__leer { grid-column: 1 / -1; order: 0; }
  .tm-plaetze:has(.tm-platz[data-platz]:not([data-platz=""])) .tm-buehne__leer {
    display: none;
  }
  .tm-platz {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 24rem;
    border: 1px solid var(--tm-linie);
    border-radius: var(--tm-radius-card);
    background: var(--tm-karte);
    overflow: hidden;
  }
  .tm-platz__kopf {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--tm-space-sm);
    padding: var(--tm-space-xs) var(--tm-space-sm);
    background: var(--tm-flaeche-tief);
    border-bottom: 1px solid var(--tm-linie);
  }
  .tm-platz__titel {
    font-weight: 600;
    font-size: 0.9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .tm-platz__karte { padding: var(--tm-space-md); overflow-y: auto; }
  .tm-platz > .tm-meldung { margin: var(--tm-space-md); }

  /* WELCHER ABSCHNITT WO STEHT, ENTSCHEIDET `order` — NICHT DIE
   * REIHENFOLGE IM DOKUMENT (T10 Etappe 2, ADR 0011 Abschnitt 3).
   *
   * Seit Etappe 2 steht für JEDEN offenen Reiter ein Abschnitt in der
   * Bühne; sichtbar sind die auf einem Platz. Ein Wechsel blendet um,
   * er verschiebt nichts: ein `<iframe>`, den man im Baum verschiebt,
   * lädt neu — und genau das soll aufhören.
   *
   * `.tm-platz` setzt `display: flex` und würde damit das `hidden` des
   * Browsers überstimmen. Deshalb die eine Zeile darunter; ohne sie
   * stünden alle offenen Reiter gleichzeitig da. */
  .tm-platz[hidden] { display: none; }
  .tm-platz[data-platz="a"] { order: 1; }
  .tm-platz[data-platz="b"] { order: 2; }

  .tm-rahmen {
    flex: 1 1 auto;
    width: 100%;
    min-height: 22rem;
    border: none;
    background: var(--tm-flaeche);
  }

  /* Nebeneinander erst, wenn wirklich Platz ist. Zwei Spalten auf
   * einem schmalen Schirm sind zwei unbrauchbare Spalten. */
  @media (min-width: 75rem) {
    .tm-plaetze--zwei { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  }
  /* Tablet quer: dieselbe Breite ist hier mehr wert, weil die Hoehe
   * fehlt und nebeneinander besser passt als untereinander. */
  @media (min-width: 60rem) and (orientation: landscape) {
    .tm-plaetze--zwei { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  }

  /* --- Telefon: keine Leiste, dafuer der App-Wechsler ---------------- */

  @media (max-width: 46rem) {
    .tm-huelle {
      grid-template-areas: "kopf" "spine" "inhalt" "fuss";
      grid-template-rows: auto auto 1fr auto;
    }
    .tm-huelle > .tm-reiterleiste { display: none; }
    .tm-plaetze { gap: var(--tm-space-sm); }
    .tm-platz { min-height: 18rem; }
  }
}
