/* Grundgestaltung von Family Board.
 *
 * ===========================================================================
 * DIE WERTE KOMMEN AUS DEM ENTWURF (FAM-29)
 * ===========================================================================
 * Bis zum 13.09.2026 standen hier vorlaeufige Zahlen - mit Verstand gewaehlt,
 * aber nicht gestaltet. Sie sind ersetzt. Die Quelle ist der Entwurf unter
 * docs/Design/canvas/, dieselben Werte stehen in docs/Design.md Regel 10, und
 * `tests/test_ansichten.py` haelt beides zusammen: Wer hier eine Zahl aendert,
 * ohne die Doku nachzuziehen, bekommt einen roten Testlauf.
 *
 * Die Farben liegen auf **Richtung A ("Garten", Farbton 170)** aus dem
 * Entwurf. Sie sind der einzige Teil, der nicht einzeln entschieden wurde -
 * A ist die Richtung, in der die vier Ansichten gezeichnet sind, und der
 * bisherige Stand. Ein Wechsel auf B (250, blau) oder C (350, beere) ist der
 * Farbton in Abschnitt 1 und sonst nichts; genau dafuer liegt jede Farbe an
 * genau einer Stelle.
 *
 * ===========================================================================
 * WIE DIE VIER ANSICHTEN FUNKTIONIEREN (Design.md, Regel 0)
 * ===========================================================================
 * Nicht die *Eigenschaft* wird zweimal gesetzt, sondern der *Wert* an einer
 * Stelle gewaehlt:
 *
 *   1. Alle Masse liegen als Merkmalsvariablen.
 *   2. Automatik: Die Haltepunkte unten setzen sie auf :root.
 *   3. Uebersteuerung: body[data-ansicht="..."] setzt dieselben Variablen.
 *      Ein Attributselektor auf body wiegt schwerer als :root und gewinnt
 *      deshalb UNABHAENGIG von der Bildschirmbreite.
 *   4. Alle Regeln darunter lesen nur die Variablen.
 *
 * Daraus folgt: **Kein @media ausserhalb des Haltepunkt-Abschnitts**, und
 * keine Zahl direkt in einer Regel, wo es eine Variable gibt. Eine fest
 * verdrahtete Menuebreite bliebe beim Wechsel auf Infoboard stehen.
 *
 * **Jede Ansicht setzt jede Variable.** Ein vergessener Wert faellt lautlos
 * auf die Automatik zurueck - das Infoboard saehe an dieser einen Stelle aus
 * wie ein Desktop, und nichts daran wirkte kaputt. Bewacht von
 * `test_jede_ansicht_setzt_dieselben_merkmale`.
 */

/* ===========================================================================
   1. Farben - jede genau einmal (Design.md, Regel 10)
   ===========================================================================
   Alles in oklch, kein color-mix(): Gemischte Farben driften.

   ## Richtung: Blau 265 (Design 2a, A1 - 27.09.2026)

   **Hier stand „Richtung A aus dem Entwurf: Grundton 170 (gruen-tuerkis) -
   bewusst nicht Audits Blau 250"**, seit dem 18.09.2026 als *Limette* auf
   150. **Design 2a dreht es auf Blau 265**, und der Satz von damals ist
   dabei eingeloest worden: *„Ein Wechsel der Richtung ist dieser Block und
   sonst nichts."*

   **Was daran haengt, haengt automatisch mit**: `app/farben.py` liest den
   Akzent fuer `theme-color` und das Manifest, `scripts/icons.py` erzeugt
   die sechs Icons daraus. **Beide sind nachgezogen** - das zweite ist ein
   Lauf, keine Handarbeit (`python scripts/icons.py`).

   **Zwei Toene liegen jetzt nah beieinander, und das ist zu wissen**:
   `--laeuft` (die Spalte *In Arbeit* am Projektbrett, FAM-175) steht auf
   250, der Akzent auf 265. *Wer dort eines Tages einen Unterschied braucht,
   verschiebt `--laeuft`, nicht den Akzent* - er traegt jede Hauptaktion der
   Anwendung.

   **Die Personenfarben wandern wieder nicht mit** (siehe unten), und der
   zweite Akzent `--warm` bleibt auf 60: Er ist die Gegenfarbe, und gegen
   Blau traegt Warm besser als gegen Gruen.

   Ein Wechsel der Richtung ist dieser Block und sonst nichts. */

:root {
  /* **Akzent Blau** (2a: `oklch(50% 0.19 265)`) - aktive Zustaende, Badges,
     Fokus. */
  --akzent:        oklch(50% 0.19 265);
  --akzent-dunkel: oklch(40% 0.17 265);
  --akzent-blass:  oklch(96% 0.03 265);

  /* Der zweite Akzent - fuer das, was nicht die Hauptaktion ist. */
  --warm:          oklch(56% 0.16 60);
  --warm-blass:    oklch(96.5% 0.045 75);

  /* **Der Seitenhintergrund ist warm, nicht blau** (2a: `96.5% 0.008 100`)
     - *ein Papierton*, und er ist die Flaeche, auf der die weissen Karten
     liegen. */
  --grund:         oklch(96.5% 0.008 100);
  --flaeche:       oklch(100% 0 0);
  --linie:         oklch(90% 0.01 270);

  /* **Tinte** (2a) - der Text ist nicht schwarz, sondern sehr dunkles
     Blaugrau. */
  --text:          oklch(21% 0.02 270);
  --text-leise:    oklch(48% 0.02 270);

  /* --- Die Farben der Wetterbilder (10.10.2026, auf Wunsch) -------------

     *„Jeder Tag soll angezeigt werden wie eine schoene Kinderzeichnung mit
     einer lustigen Sonne … oder mit Regen, so in blau, Regentropfen
     gezeichnet auf dem Kinderbild."*

     **Sie stehen hier und nicht im SVG** (Regel 10): Eine Farbe im Markup
     waere ein zweiter Ort fuer die Farbrichtung - woertlich die Zusage, die
     `test_keine_farbe_steht_im_markup` am Chat haelt. Die Zeichnungen
     tragen **Klassen**, die Farbe steht hier.

     **Sie sind bunter als der Rest der Anwendung, und das ist Absicht**:
     Ein Wachsmalstift kennt keine gedaempften Toene. Sie gehoeren trotzdem
     zur Familie - jeder Ton ist einer, den das Blatt schon kennt (Himmel am
     Akzent 265, Wiese am Gruen 150 der alten Richtung, Sonne am warmen 60).

     **Der Stift ist nicht schwarz**, sondern dieselbe Tinte wie der Text,
     nur eine Spur heller: *Ein Kind malt mit einem Stift, nicht mit einem
     Drucker.* */
  --wetterbild-himmel:        oklch(91% 0.06 235);
  --wetterbild-himmel-trueb:  oklch(87% 0.025 255);
  --wetterbild-wiese:         oklch(80% 0.14 145);
  --wetterbild-stift:         oklch(32% 0.03 265);
  --wetterbild-sonne:         oklch(86% 0.17 90);
  --wetterbild-sonne-rand:    oklch(68% 0.17 62);
  --wetterbild-wolke:         oklch(99.5% 0.003 250);
  --wetterbild-wolke-trueb:   oklch(84% 0.02 260);
  --wetterbild-tropfen:       oklch(58% 0.16 245);
  --wetterbild-blitz:         oklch(84% 0.19 85);
  --wetterbild-schnee:        oklch(100% 0 0);
  --wetterbild-wind:          oklch(99% 0.01 240);
  /* Der Baum - **vier Jahreszeiten, vier Farben** (10.10.2026, auf einen
     Kommentar im Canvas). Der Winter braucht keine: *ein kahler Baum ist
     Stamm und Aeste.* */
  --wetterbild-stamm:         oklch(42% 0.05 45);
  --wetterbild-laub-fruehling: oklch(85% 0.17 135);
  --wetterbild-laub-sommer:   oklch(65% 0.17 148);
  --wetterbild-laub-herbst:   oklch(72% 0.18 62);
  --wetterbild-bluete:        oklch(85% 0.09 350);
  /* Die leise Schrift im Eckzettel, seit er keinen Grund mehr hat
     (10.10.2026). **44 % statt 48 %** - `--text-leise` faellt auf dem
     trueben Himmel auf 4,42 durch, dieses traegt mit 5,26. */
  --wetterecke-leise:         oklch(44% 0.02 270);

  /* Die Personenfarben im Chat (FAM-123) - eine je Mensch.

     **Sie teilen Helligkeit und Chroma UNTEREINANDER** (52% / 0.11): So
     tragen alle sechs gleich viel Gewicht und unterscheiden sich nur im Ton.

     **Mit den Akzenten teilen sie es seit der Frischen Ebene nicht mehr.**
     Hier stand bis dahin, sie teilten es mit `--akzent` und `--warm`; die
     sind jetzt kraeftiger (0.16). Das ist Absicht und keine Nachlaessigkeit:
     Ein Akzent traegt eine **Aktion**, eine Personenfarbe nur einen **Namen**
     - sie soll nicht lauter sein als der Knopf daneben. Wer sie doch
     mitziehen will, aendert diese sechs Zeilen und diesen Absatz.

     **Der Farbton 25 bleibt frei.** Dort liegt `--fehler`, und ein Name in
     Fehlerrot saehe aus wie eine Warnung.

     **Die Farbe ist die Bequemlichkeit, nicht die Auskunft** (FAM-65): Der
     Name steht daneben, der eigene Beitrag steht ausserdem rechts. Wer
     rot-gruen-blind ist, verliert hier nichts. */
  /* **Seit dem 22.09.2026 steht hier die REZEPTUR und nicht mehr die
     Farbe** (auf Wunsch: *ein Farbselector, bei dem sich jede Farbe
     auswaehlen laesst*). Die sechs Werte, die hier standen, unterschieden
     sich **nur im dritten Feld** - also ist der Farbton die Angabe, und
     Helligkeit und Buntheit bleiben, was sie waren.

     **Damit ist der Kontrast weiterhin gerechnet und nicht gewaehlt**
     (5,06 bis 5,82 : 1, FAM-162): Er haengt an diesen beiden Zahlen, und die
     sind fest. *Es gibt keine zu helle und keine zu dunkle Wahl mehr* - und
     deshalb muss auch keine abgewiesen werden.

     **Regel 10 haelt:** Aus Python kommt eine ZAHL (`--person-ton`), die
     Farbe entsteht hier. */
  --person-helligkeit: 52%;
  --person-buntheit:   0.11;

  /* **Die fertige Farbe steht NICHT hier**, und das ist gemessen
     (22.09.2026): Ein Custom Property wird an der Stelle **seiner
     Deklaration** substituiert, nicht beim Leser. Auf `:root` gibt es kein
     `--person-ton`, also waere die Rezeptur hier *ungueltig zur
     Berechnungszeit* - **leer**, und leer vererbt.

     Hier stand genau das, mit dem Kommentar *„das `var()` wird beim LESER
     aufgeloest"*. **Der Satz war falsch, und keine Zeile Code hat es
     gemeldet:** Im Browser war danach gar keine Personenfarbe zu sehen, und
     die ganze Testsuite blieb gruen.

     Sie steht deshalb bei `.gefaerbt`, weiter unten - *dort, wo der Ton
     wirklich liegt.* */

  /* Die Papierfarben des Sticker Boards (FAM-169) - eine je Mensch.

     **Dieselben sechs Toene wie die Personenfarben darueber, und das ist die
     ganze Bauart:** Nummer und Wort teilen sie sich mit ihnen
     (`app/personenfarben.py` - *Orange, Gruen, Tuerkis, Blau, Lila, Pink*).
     Ein zweiter Satz Woerter waere die zweite Wahrheit, und dann schriebe
     jemand „Blau“ an einen gruenen Zettel.

     **Nur die Helligkeit ist eine andere, und sie ist gerechnet.** Eine
     Personenfarbe liegt bei 52% - **darauf ist kein Text lesbar**, weder
     dunkler noch heller; deshalb traegt ein Termin seit FAM-162 einen
     *Streifen* und keinen farbigen Grund. Ein Zettel ist aber **ganz**
     Papier.

     **Gemessen gegen `--text`** (Regel 5, die Karte verlangt mindestens
     4,5 : 1):

       Orange 12,34 - Gruen 12,71 - Tuerkis 12,80
       Blau   12,51 - Lila  12,28 - Pink    12,35

     und gegen `--text-leise`, das die Fusszeile traegt: **4,71 bis 4,91**.

     **Die Rechnung ist gegengeprueft**, an einem Paar, dessen Wert man
     kennt: `--text` auf `--flaeche` ergibt 17,71 : 1 und `--flaeche` auf
     sich selbst genau 1,00. *Wer etwas zu Gutes misst, prueft zuerst die
     Messung* - in FAM-162 hat eine doppelte Gamma-Dekodierung 14 : 1 fuer
     mittelhelle Farben gemeldet. */
  /* **Dieselbe Umstellung wie eine Zeile hoeher** (22.09.2026): derselbe
     Ton, andere Rezeptur. Es gibt damit **keinen zweiten Satz Werte**, der
     auseinanderlaufen koennte - die Papierfarbe eines Zettels entsteht aus
     der Farbe seines Schreibers, ohne dass jemand sie pflegt. */
  --papier-helligkeit: 93%;
  --papier-buntheit:   0.055;
  /* **Die Rezeptur steht bei `.sticker.gefaerbt`**, aus demselben Grund wie
     eine Zeile hoeher: Hier gibt es kein `--papier-ton`. */

  /* **Hier standen `--brett` und `--brett-linie`** - das Whiteboard, an dem
     die Zettel hingen (FAM-169).

     **Weggefallen am 24.09.2026**, auf Wunsch: *„Bitte baue das Sticker
     Board so um wie das Board fuer Projekte aussieht."* Seitdem haengen die
     Zettel an derselben **Filzwand** wie die Projektkarten
     (`--brett-filz`, weiter unten), und die beiden Farben hatten keinen
     Leser mehr.

     **Gemessen, nicht angenommen** - und die Messung hat eine Behauptung
     widerlegt, die hier schon geschrieben stand: *die Einkaufsliste
     benutze sie weiter.* Sie tut es nicht (`grep var(--brett)` findet in
     `app.css` nichts). *Eine Farbe, die niemand liest, ist keine Reserve,
     sondern eine Zeile, die beim naechsten Mal jemand benutzt, weil sie
     dasteht.*

     **Sie stehen weiterhin in `scripts/gp169.py`** - das ist die
     Gegenprobensammlung jener Karte und ein **Protokoll**, kein Blatt.
     *Ein gelaufener Versuch bleibt, was er war.* */

  /* Was gerade laeuft (FAM-175) - die Spalte *In Arbeit* am Projektbrett.
     **Die erste Farbe ausserhalb der beiden Akzente seit `--fehler`**, und
     sie steht genau einmal da (Regel 10).

     **Warum ueberhaupt eine:** `app/board.py` sagt, welche **Bedeutung** eine
     Spalte hat (`TON_BLAU` fuer *laeuft*, `TON_GRUEN` fuer *ist fertig*) -
     und *„das Blatt uebersetzt die vier Toene in Farben; welche, entscheidet
     Karte 2."* Ohne diese Zeile uebersetzte es *blau* in irgendetwas anderes,
     **und der Name in board.py waere eine Behauptung.**

     **Dieselbe Helligkeit und Buntheit wie `--akzent` und `--warm`** (52%,
     0.12-0.16) - so traegt sie gleich viel Gewicht und unterscheidet sich
     nur im Ton, genau wie die Personenfarben. Der Farbton 250 ist der vierte
     freie neben 150 (Akzent), 60 (warm) und 25 (Fehler).

     **Gerechnet gegen `--grund`: 5,30 : 1** (auf `--flaeche` 5,49) - ueber
     den 3 : 1, die eine Linie braucht, und ueber den 4,5 : 1 fuer Text. Die
     beiden Nachbarn liegen daneben: `--erfolg` 5,94, `--text-leise` 5,70.

     **Gegengeprueft an einem Paar, dessen Wert man kennt** (der Fund von
     FAM-162, wo eine doppelte Gamma-Dekodierung 14 : 1 fuer mittelhelle
     Farben meldete): `--text` auf `--flaeche` ergibt 17,71 : 1, `--flaeche`
     auf sich selbst genau 1,00. */
  --laeuft:        oklch(52% 0.12 250);

  /* **Gefahr Rot** (2a: `oklch(46% 0.17 30)`) - Loeschen, und die Schrift
     auf einem gefaehrlichen Knopf. */
  --fehler:        oklch(46% 0.17 30);
  --fehler-blass:  oklch(96% 0.03 30);
  /* **Erledigt Gruen** (2a) - zwei Werte, denn der Stempel im Archiv traegt
     Rand und Text in verschiedenen Toenen: *ein Rand darf leiser sein als
     die Schrift darin.* */
  --erfolg:        oklch(45% 0.1 160);
  --erfolg-text:   oklch(40% 0.1 160);
  --erfolg-blass:  oklch(96% 0.03 160);

  /* ------------------------------------------------------------------
     Design 2a, A1 - was es vorher nicht gab (27.09.2026)
     ------------------------------------------------------------------ */

  /* **Warnung Orange** - ueberfaellig. **Nicht Rot**, und das ist dieselbe
     Trennung wie am Projektbrett: *Rot gehoert dem Blocker* (FAM-179), Orange
     dem, was draengt. Der Hinweistext daneben ist dunkler, weil er **Text**
     ist und nicht eine Flaeche. */
  --warnung:       oklch(56% 0.17 45);
  --warnung-text:  oklch(46% 0.14 40);

  /* **Text auf Blau oder Tinte** - nicht reines Weiss (2a: `98% 0 0`). */
  --auf-dunkel:    oklch(98% 0 0);

  /* **Die Linie unter einem Abschnittstitel** - leiser als `--linie`, weil
     sie ueber die ganze Breite laeuft. */
  --linie-abschnitt: oklch(88% 0.01 270);

  /* **Der Rahmen der Huelle** - die Kante zwischen Seitenleiste und Inhalt
     (2a: `box-shadow: 0 0 0 1px`). */
  --linie-huelle:  oklch(91% 0.01 270);

  /* **Hover in der Navigation** - eine Zeile, die sich hebt, ohne eine
     Farbe zu bekennen. */
  --navi-hover:    oklch(95% 0.01 270);

  /* **Die Abdunklung hinter der Handy-Schublade** - mit Deckkraft, denn
     dahinter steht die Seite. */
  --abdunklung:    oklch(20% 0.02 270 / .35);

  /* **Der gestrichelte Rand einer Eingabemaske** (2a, A6) - *hier wird
     etwas ausgefuellt, es steht noch nicht fest.* */
  --maskenrand:    oklch(72% 0.08 265);

  /* **Der Kartenschatten** (2a, A1) - zwei Lagen: eine harte Kante und ein
     weicher Wurf darunter. Er steht genau einmal da (Regel 10). */
  --kartenschatten: 0 1px 1px oklch(30% 0.05 270 / .1),
                    0 10px 20px -12px oklch(30% 0.05 270 / .4);

  /* ------------------------------------------------------------------
     Der Einkaufszettel (Einkauf 2a, §6; Aenderungen §3 bis §5)

     **Ein abgerissenes Blatt**, und es hat noch weniger Farben als vorher:
     die Linie jeder Zeile, der Rand des Kaestchens und der Kaffeefleck.
     *Papier hat wenige Farben.*

     > **Drei sind am 28.09.2026 weggefallen bzw. neutral geworden.** Der
     > **rote Randstrich** entfaellt ganz (*„Den `linear-gradient` am Blatt
     > entfernen"*), und Linie wie Kaestchen sind **nicht mehr blaeulich**:
     > Auf einem abgerissenen Blatt ohne Rand sah die Schulpapier-Linie aus
     > wie ein zweiter Akzent. *Eine Farbe, die niemand mehr liest, ist
     > keine Reserve* - `--zettel-rand` gibt es deshalb nicht mehr.
     ------------------------------------------------------------------ */

  /* Das Loch im Riss - grau, damit es nach Stanzung aussieht. */
  --zettel-loch:   oklch(78% 0.01 270);

  /* Die Linie einer Zeile - **neutral grau** (Aenderungen, §4). */
  --zettel-linie:  oklch(88% 0 0);

  /* Der Rand des Kaestchens - **grau statt Tinte** (Aenderungen, §4). */
  --zettel-kasten: oklch(70% 0 0);

  /* Der Schatten des ganzen Zettels - **zwei Lagen wie beim Kartenschatten**,
     nur als `drop-shadow`: Der Spiralrand hat Loecher, und ein
     `box-shadow` legte sich als Rechteck darunter. */
  --zettel-schatten: drop-shadow(0 1px 1px oklch(30% 0.05 270 / .1))
                     drop-shadow(0 12px 14px oklch(30% 0.05 270 / .18));

  /* ------------------------------------------------------------------
     Die Filzwand (24.09.2026, auf Auftrag)

     Das Projektbrett sieht aus wie eine Moderationswand: graue Filzflaeche
     im Aluminiumrahmen, Karten aus Papier mit Stecknadeln, Aufkleber fuer
     Frist und Blocker, Zeilen an einer Schnur.

     **Jede Farbe steht hier und nirgends sonst** (Regel 10). Die Regeln
     weiter unten lesen nur Variablen.
     ------------------------------------------------------------------ */

  /* Wand und Rahmen */
  --brett-filz:          oklch(56% 0.022 245);
  --brett-filz-korn-d:   oklch(20% 0.02 250 / 0.18);
  --brett-filz-korn-h:   oklch(90% 0.01 250 / 0.14);
  /* **Der Raum um das Brett ist die Seite selbst** (24.09.2026, auf Zuruf:
     *„den beigen Hintergrund gegen weiß austauschen, dass nur das Board zu
     sehen ist"*). Der Auftrag *Brett 1b* nennt hier `oklch(92% 0.008 80)` –
     ein warmes Beige, das eine Wand andeutet, auf der das Brett hängt.
     **Am Gerät war es ein zweiter Rahmen um den Rahmen.**

     **`var(--flaeche)` und nicht noch einmal Weiß**: `.inhalt` trägt schon
     diese Farbe, der Raum ist also *dieselbe Fläche* – und *zwei Zeilen für
     dieselbe Farbe laufen auseinander* (Regel 10). Wer die Seitenfläche
     eines Tages tönt, tönt den Raum mit, ohne hier nachzusehen.

     **Der Abstand bleibt** (`--brett-raum-abstand`): Er hält das Brett vom
     Rand weg, und das tut er unsichtbar genauso. */
  --brett-raum:          var(--flaeche);
  --brett-alu-1:         oklch(95% 0.004 250);
  --brett-alu-2:         oklch(84% 0.006 250);
  --brett-alu-3:         oklch(76% 0.008 250);
  --brett-alu-4:         oklch(88% 0.005 250);
  --brett-ecke-hell:     oklch(38% 0.01 250);
  --brett-ecke-dunkel:   oklch(24% 0.01 250);

  /* Papier */
  --papier-weiss:        oklch(97% 0.008 90);
  --papier-kraft:        oklch(80% 0.07 75);
  --papier-kraft-blass:  oklch(90% 0.02 80);
  --papier-tinte:        oklch(24% 0.02 260);   /* Filzstift auf Karte */
  --papier-kraft-tinte:  oklch(24% 0.03 60);
  --papier-gruppe-tinte: oklch(35% 0.02 260);
  --papier-wort:         oklch(38% 0.03 60);

  /* Schnur */
  --schnur-rot:          oklch(55% 0.18 27);
  --schnur-weiss:        oklch(94% 0.01 80);

  /* Aufkleber */
  --aufkleber-frist:            oklch(92% 0.15 100);
  --aufkleber-frist-rand:       oklch(80% 0.14 95);
  --aufkleber-frist-draengt:    oklch(82% 0.17 82);
  --aufkleber-frist-draengt-rand: oklch(68% 0.16 75);
  --aufkleber-frist-text:       oklch(26% 0.05 80);
  --aufkleber-blocker:          oklch(54% 0.2 27);
  --aufkleber-blocker-text:     oklch(99% 0 0);
  --aufkleber-blocker-feld:     oklch(92% 0.05 27);

  /* Stecknadeln - Kopf und Schattenseite.

     **Fuenf Farben, gewechselt ueber die Nummer der Karte** - dieselbe
     Bauart wie die Neigung des Zettels (FAM-169): *gerechnet, nicht
     gewuerfelt*, damit dieselbe Karte immer dieselbe Nadel traegt. */
  --nadel-rot:      oklch(58% 0.2 27);
  --nadel-rot-d:    oklch(38% 0.15 27);
  --nadel-blau:     oklch(55% 0.16 255);
  --nadel-blau-d:   oklch(35% 0.12 255);
  --nadel-gelb:     oklch(82% 0.16 95);
  --nadel-gelb-d:   oklch(58% 0.13 85);
  --nadel-gruen:    oklch(58% 0.15 150);
  --nadel-gruen-d:  oklch(38% 0.1 150);
  --nadel-weiss:    oklch(96% 0.005 80);
  --nadel-weiss-d:  oklch(70% 0.01 80);
  --nadel-dunkel:   oklch(38% 0.03 260);
  --nadel-dunkel-d: oklch(22% 0.02 260);

  /* **Schatten und Glanz stehen einmal da und werden mit Alpha gelesen** -
     `oklch(from var(--schatten) l c h / .25)`.

     **Die relative Farbsyntax ist der Preis**, und er ist derselbe wie bei
     `mod()` an der Neigung (FAM-169): Ein Browser, der sie nicht kennt,
     verwirft die Deklaration - dann fehlt ein Schatten. *Nuechtern, nicht
     kaputt.* Die Alternative waere eine eigene Variable je Alphastufe, und
     davon gaebe es hier ueber zwanzig. */
  --schatten:            oklch(0% 0 0);
  --glanz:               oklch(100% 0 0);

  /* **Die Filzstift-Schrift** - und sie kommt NICHT von einer fremden
     Adresse (Regel 4, `09-compliance.md`). Der Auftrag nennt *Kalam* von
     Google Fonts; genau das hat `Design.md` beim Entwurf schon einmal
     abgelehnt (*„Der Entwurf laedt Outfit von fonts.googleapis.com“*), und
     hier sitzen Kinder vor dem Bildschirm.

     **Entschieden am 24.09.2026, auf Rueckfrage: die Systemschrift.** Was
     auf dem Geraet liegt, wird genommen; wo nichts liegt, greift `cursive`.
     *Sie steht an einer Stelle* - wer sie eines Tages selbst ausliefert,
     aendert diese Zeile und sonst nichts.

     ## Und dieser Tag ist der 27.09.2026

     **Der Satz darueber ist eingeloest**, und es ist wirklich bei einer
     Zeile geblieben: *Comic Neue* liegt seitdem unter
     `app/static/fonts/` - **selbst ausgeliefert**, nicht von Google
     geholt.

     **Der Auftrag *Design 2a* verlangte den Webfont** (*„Comic Neue ueber
     Google Fonts laden"*), und das verstoesst gegen Regel 4 und
     `09-compliance.md`: Der Browser **jedes Kindes** gaebe dabei seine
     IP-Adresse an einen Dritten. **Entschieden auf Rueckfrage: selbst
     ausliefern** - genau der dritte Weg, den Regel 4 vorsieht (*„Eine
     Bibliothek, die gebraucht wird, wird selbst ausgeliefert - ihre
     Groesse geht voll zu unseren Lasten, und ihre Lizenz ist zu
     pruefen"*).

     **Die Reihenfolge ist die Zusage**: Erst das, was auf dem Geraet
     liegt (nichts zu laden), dann die eigene Datei. *Ein Windows-Rechner
     laedt damit kein einziges Byte*, und Android und Linux bekommen zum
     ersten Mal ueberhaupt eine Handschrift. */
  --schrift-hand: "Segoe Print", "Bradley Hand", "Comic Neue", "Comic Sans MS", cursive;

  /* **Die Titelschrift** (27.09.2026, Design 2a, A2) - Logo, Seitentitel,
     Abschnittstitel und Kartentitel.

     **Sie hat keinen Vorlaeufer auf dem Geraet**, anders als die
     Handschrift: *Young Serif* gibt es nur als Datei, also laedt sie
     jeder. **Der Preis ist gemessen** und steht in `02-tech-stack.md`. */
  --schrift-titel: "Young Serif", Georgia, "Times New Roman", serif;

  /* **Die Schrift des Seitentitels** (Einkauf 2a, Abschnitt 1 - er ersetzt
     die Angaben aller bisherigen Auftraege).

     **Eine Grotesk, keine Serifenschrift**, und das ist eine
     Arbeitsteilung: Der Seitentitel sagt, *wo man ist* - er steht gross und
     soll ruhig stehen. Young Serif bleibt den **Abschnitten** und den
     Titeln auf Karten.

     **Century Gothic ist eine Systemschrift** (Windows/Office) und wird
     **nicht** als Webfont eingebunden - Regel 4 verlangt es. Wo es sie
     nicht gibt, springen die Verwandten ein und zuletzt `sans-serif`.

     **Sie trug bis zum 29.09.2026 nur der Einkauf** (`--schrift-einkauf`,
     Einkauf 2a Aenderungen §1/§6), alle anderen Module Segoe UI Bold. **Das
     war sichtbar**, und der Nutzer hat es gemeldet: *„Die Ueberschrift in
     Einkauf hat eine andere Schriftart oder Groesse als in Termine. Bitte
     genau die Groesse und Art der Ueberschrift in Einkauf in alle anderen
     Module uebernehmen."*

     **Eine Variable, nicht zwei.** `--schrift-einkauf` gibt es nicht mehr -
     *zwei Namen fuer dieselbe Frage laufen auseinander* (FAM-52, FAM-117),
     und der Auftrag von gestern (*„bleibt fuer die anderen Module
     bestehen"*) ist von dem Wunsch von heute ueberstimmt.

     **Das Gewicht gehoert dazu**: `500`, nicht 700. Century Gothic ist eine
     geometrische Grotesk mit duennen Rundungen - fett gesetzt verliert sie
     genau das, wofuer sie ausgesucht wurde, *und dann waere es wieder eine
     andere Ueberschrift als im Einkauf.* */
  --schrift-seitentitel: "Century Gothic", "CenturyGothic", "AppleGothic",
                         "URW Gothic", sans-serif;

  /* **Die Schrift des Monatsgitters** (29.09.2026).

     Eine Grotesk, und das ist gemessen: In einer 50 px breiten Spalte
     traegt Segoe Print **6,3 Zeichen**, eine Grotesk **8,0** - und
     genau so viel zeigt das Vorbild des Nutzers (*„Kennenle“*).

     **Nur im Gitter**, nicht ueberall: Die Handschrift ist die Schrift
     dieser Anwendung, sie passt nur nicht in eine Spalte von einem
     Siebtel Bildschirm. */
  --schrift-monat: "Segoe UI", system-ui, -apple-system, sans-serif;
}


/* ===========================================================================
   1b. Die eigenen Schriften - selbst ausgeliefert (27.09.2026)
   ===========================================================================
   **Nichts von fremden Servern** (Regel 4, `09-compliance.md`). Beide
   Schriften stehen unter der **SIL Open Font License 1.1**, die das
   Selbsthosten ausdruecklich erlaubt; die Lizenztexte liegen daneben
   (`OFL-YoungSerif.txt`, `OFL-ComicNeue.txt`) - **die OFL verlangt, dass
   sie mitgeliefert werden.**

   **Segoe Print ist nicht dabei, und das ist kein Versehen**: Sie ist eine
   Windows-Systemschrift und darf **nicht** weitergegeben werden. Sie steht
   nur im Stapel oben - wer sie hat, sieht sie; wer nicht, bekommt Comic
   Neue.

   **`font-display: swap`** - der Text steht sofort da, in der
   Ersatzschrift, und wird nachtraeglich ausgetauscht. *Die Alternative
   waere eine Seite, die zwei Sekunden lang leer ist*, und auf einem
   Wohnzimmer-Touchscreen sieht das aus wie ein Fehler.

   **Zwei Dateien fuer Young Serif** - `latin` und `latin-ext`: Der
   `unicode-range` entscheidet, welche geladen wird, und **die zweite laedt
   nur, wenn ein Zeichen daraus vorkommt**. Deutsche Umlaute liegen in
   `latin`; *ein Name wie „Łukasz“ holt die andere nach.*
   =========================================================================== */

@font-face {
  font-family: "Young Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/young-serif-400.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: "Young Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/young-serif-400-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: "Comic Neue";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/comic-neue-400.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: "Comic Neue";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/comic-neue-700.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;
}



/* ===========================================================================
   2. Die Masse - Automatik ueber Haltepunkte
   ===========================================================================
   Die Werte kommen aus dem Entwurf (docs/Design/canvas/) und stehen in
   docs/Design.md Regel 10. Handy zuerst, dann breiter.

   Das Infoboard kommt hier NICHT vor: Ein Wohnzimmer-Touchscreen ist breit
   und saehe nach Desktop aus. Es gibt ihn nur ueber die Auswahl - genau
   dafuer ist die Uebersteuerung da.

   **Warum das Handy groesser schreibt als der Desktop** (17px gegen 16px):
   Ein Handy liegt eine Armlaenge weg und wird in Bewegung gelesen, ein
   Bildschirm steht ruhig auf dem Tisch. Dieselbe Ueberlegung fuehrt zu 48px
   Bedienflaeche gegen 36px - eine Fingerkuppe ist breiter als ein Mauszeiger.
   Der Entwurf dreht damit die uebliche Annahme "klein = weniger" um. */

:root {
  /* Handy */
  --brett-raum-abstand: 8px;
  --brett-rahmen: 9px;
  --brett-innen-y: 16px;
  --brett-innen-x: 16px;
  --brett-spaltenabstand: 18px;
  --brettkarte-min: 150px;
  --brettkarte-min-voll: 220px;
  --brettkarte-titel: 23px;
  --brettkarte-titel-voll: 28px;
  --brettkarte-text-anzeige: none;
  --brettkarte-textgroesse: 13px;
  --brettkopf-titel: 22px;
  --kopfhoehe:      56px;
  --marke-anzeige:  inline;
  --menuetafel-breite: min(78%, 300px);   /* die Schublade (A3) */
  --menueliste-spalten: repeat(2, 1fr);
  --aufgabe-umbruch: wrap;
  --aufgabe-titel-breite: 100%;
  --detail-spalten: 1fr;
  --detail-listenanzeige: none;
  --kalender-spalten: 1fr;
  --kalender-leertag-anzeige: block;
  --monat-spalten: 22px repeat(7, minmax(0, 1fr));
  --monat-reihe-spalten: 2 / -1;
  --monat-kw-anzeige: block;
  --monat-schrift: 11px;
  --monat-zeilenhoehe: 34px;
  --essensplan-spalten: 1fr;
  --board-spalten: repeat(4, 262px);
  --board-zeilen-richtung: column;
  --brettkarte-neigung: 0.6deg;
  --schrift:        12px;
  --schrift-gross:  16px;
  --schaltflaeche:  36px;
  --abstand:        10px;
  --kachel-min:     140px;
  --maske-breite: 100%;
  --knopfwort-anzeige: inline;
  --geraetewort: "deinem Handy";
  --kopftrenner-anzeige: none;
  --knopfzusatz-anzeige: none;
  --einstellungsliste-spalten: 1fr;
  --infoboardboxen-anzeige: block;
  --qrcode-breite: 180px;
  --blatt-breite: 100%;
  --blatt-rand: 18px;
  --blatt-kante: 14px;
  --feld-richtung: row;
  --feld-beschriftung-breite: 40%;
  --feld-eingabe-basis: 0;
  --kontaktwert-basis: 100%;
  --sticker-breite: 100%;
  --sticker-feld-zeiger: none;
  --bereich-spalten: 1fr;
  --sticker-ziehen: default;
  --sticker-zug: auto;
  --sticker-griff: none;
  --sticker-bereich-anzeige: flex;
  --plan-anzeige: block;
  --plan-spalten: 1fr;
  --baum-reihe-anzeige: block;
  --wetterbild-hoehe: 150px;
  --wetterbild-breite: 100%;
  --wettertag-anzeige: block;
  --wetterecke-lage: absolute;
  --wetterecke-spalte: auto;
  --wetterbild-hub: 56px;
  --baum-schub: 96px;
  --baum-wuchs: 1;
  --baum-heb: 8px;
  --wetter-grad: 34px;
  --wetter-wort: 13px;
  --wetter-wind-wort: 11px;
  --wetterwoche-spalten: 1fr;
  --baum-kasten-breite: 100%;
  --baum-ausrichtung: flex-start;
  --baum-linie-anzeige: none;
  --baum-kaesten-breite: 100%;
  --baum-kaesten-umbruch: wrap;
  --baum-kaesten-rand: 0;
  --baum-tafel-richtung: column;
  --baum-paar-trenner: 1px solid var(--linie);
  --baum-paar-strich: 0;
  --baum-tafel-breite: 100%;
  --plan-tag-anzeige: block;
  --ausfall-strich: 3px;
  --planzeiten-anzeige: none;
  --planzelle-zeit-anzeige: block;
  --plankopf-lage: static;
  --brett-korn: 10px;
  --sticker-neigung: 0.6deg;
  --blatt-neigung: 0.5deg;
  --feldhinweis-anzeige: none;
  --feldgruppe-innen: 0px;
  --feldgruppe-rahmen: 0px;
  --radius:         14px;
  --radius-karte:   20px;
  --radius-klein:   2px;
  --chatbild-hoehe:   180px;
  --huelle-lage: relative;
  --huelle-breite: auto;
  --huelle-richtung: row;
  --rumpf-einzug: 0px;
  --menueknopf-anzeige: flex;
  --menuetafel-inhalt: hidden;
  --menuetafel-lage: absolute;
  --abdunklung-anzeige: block;
  --seitentitel: 17px;
  --seitenkopf-richtung: column;
  --huelle-mindestbreite: max-content;
  --marke-reihenfolge: 3;
  --menuetafel-rahmen: 1px;
  --menuetafel-schatten: 0 8px 24px oklch(27% 0.02 170 / 0.18);
  --menuepunkt-schrift: 14px;
  --menuepunkt-radius: 3px;
  --seitenkopf-ausrichtung: stretch;
  --huelle-verteilung: space-between;
  /* **Die Marke nimmt nur noch ihren Text** (09.10.2026, gemessen).

     Hier stand `1 1 0`, und die Begruendung dafuer ist abgelaufen: *„Menue
     und Marke teilen sich den Rand zu gleichen Teilen - erst dadurch steht
     der Titel in der Mitte des Rahmens"* (Abschnitt 2). **Der Titel war
     damals ein Flexkind zwischen beiden**; seit dem 09.10.2026 steht er
     `fixed` und zentriert sich selbst (Abschnitt 17).

     **Geblieben war eine Klickflaeche von 172,5 px fuer einen Text von
     78** - sie lag zur Haelfte **ueber dem Titel**, und die 44-px-Flaeche
     des Fragezeichens stiess darin an. *Eine Flaeche, die groesser ist als
     das, was man sieht, ist der stille Teil eines Treffers am falschen
     Ort.*

     **Optisch aendert sich nichts**: Der Text stand schon rechtsbuendig in
     seiner Box und steht gemessen weiter an derselben Stelle (286,6 bis
     365). **Am Infoboard bleibt `1 1 0`** - dort ist die Kopfzeile eine
     Zeile mit drei Teilen, und der Titel steht im Seitenkopf. */
  --marke-dehnung: 0 0 auto;
  --marke-schrift: 12px;
  --titelgruppe-lage: fixed;
  --titelgruppe-hoehe: var(--kopfhoehe);
  /* **Eine Rechnung, keine zweite Stelle** (09.10.2026, gemessen).

     Am Handy hochkant sind **150 px** richtig: Der Kasten steht dann von
     112,5 bis 262,5, und der Text *Family Board* beginnt bei 286,6.

     **Im Querformat ist derselbe Streifen 812 px breit**, und zwischen dem
     Wort *Menue* (endet bei 65,7) und der Marke liegen gemessen **657,9 px**
     frei - *„Was nicht geklappt hat"* brach darin trotzdem um. **260 px sind
     gemessen**: Der laengste Titel der Anwendung ist *„Auf deinem Handy
     installieren"* mit **240,8 px**, dazu 4,8 Abstand und 13,4 fuer das
     Fragezeichen - **259,0**.

     **Warum die Zeile nicht im Querformat-Block steht**: Der ist eine
     **Berichtigung des Stundenplans** und holt jeden Wert beim Desktop
     (`test_das_querformat_setzt_dieselben_werte_wie_der_desktop`) - *eine
     Breite, die der Desktop nicht kennt, waere dort ein eigener Entwurf.*
     **Gesagt hat es die Wache, nicht das Nachdenken.**

     **`40vw` ist hergeleitet und nicht geraten**: 150 / 375 ist genau 0,4.
     Dieselbe Rechnung ergibt bei 812 px **324,8** und laeuft damit in den
     Deckel; an der unteren Kante des Querformats (568) sind es 227,2, und
     der Kasten steht dort von 170,4 bis 397,6 - der Text der Marke beginnt
     erst bei 483. */
  --titelgruppe-breite: min(260px, 40vw);
  --titelgruppe-ausrichtung: center;
  --titelgruppe-ueberlauf: hidden;
}

/* **Der Tablet-Satz gilt ab 768 px - ausser auf einem gedrehten Handy**
   (FAM-235). Ein Handy im Querformat ist rund 812 px breit und lag damit
   bisher ueber diesem Haltepunkt: Es bekam die Seitenleiste (240 px) und die
   Liste je Tag.

   **Die zweite Bedingung schliesst genau ein Fenster aus**: 768 bis 960 px
   breit UND hoechstens 500 px hoch. Das ist ein gedrehtes Handy und sonst
   nichts - ein Tablet im Querformat ist 744 px hoch oder mehr, ein flaches
   Fenster am Desktop breiter als 960.

   **Eine Medienliste mit Komma, kein zweiter Wertesatz.** Der naheliegende
   Weg waere ein fuenfter Satz mit den Handy-Werten gewesen; gemessen sind
   das **73 von 98 Variablen**, also eine zweite Stelle fuer dieselbe Sache.
   So gilt im Querformat weiter, was am `:root` steht - und nur der Plan wird
   unten zum Gitter. */
@media (min-width: 768px) and (min-height: 501px),
       (min-width: 961px) {
  :root {
    /* Tablet */
    --brett-raum-abstand: 16px;
    --brett-rahmen: 14px;
    --brett-innen-y: 20px;
    --brett-innen-x: 18px;
    --brett-spaltenabstand: 14px;
    --brettkarte-min: 120px;
    --brettkarte-min-voll: 180px;
    --brettkarte-titel: 21px;
    --brettkarte-titel-voll: 24px;
    --brettkarte-text-anzeige: none;
    --brettkarte-textgroesse: 12px;
    --brettkopf-titel: 20px;
    --kopfhoehe:      64px;
    --marke-anzeige:  inline;
    --menuetafel-breite: 400px;
    --menueliste-spalten: 1fr;
    --aufgabe-umbruch: nowrap;
    --aufgabe-titel-breite: auto;
    --detail-spalten: 1fr;
    --detail-listenanzeige: none;
    --kalender-spalten: repeat(7, 1fr);
    --kalender-leertag-anzeige: block;
    --monat-spalten: 30px repeat(7, minmax(0, 1fr));
    --monat-reihe-spalten: 2 / -1;
    --monat-kw-anzeige: block;
    --monat-schrift: 13px;
    --monat-zeilenhoehe: 60px;
    --essensplan-spalten: 1fr;
    --board-spalten: repeat(4, minmax(0, 1fr));
    --board-zeilen-richtung: column;
    --brettkarte-neigung: 1.2deg;
    --schrift:        17px;
    --schrift-gross:  26px;
    --schaltflaeche:  48px;
    --abstand:        20px;
    --kachel-min:     220px;
    --maske-breite: 46ch;
    --knopfwort-anzeige: inline;
    --geraetewort: "deinem Tablet";
    --kopftrenner-anzeige: block;
    --knopfzusatz-anzeige: inline;
    --einstellungsliste-spalten: 1fr;
    --infoboardboxen-anzeige: grid;
    --qrcode-breite: 220px;
    --blatt-breite: 100%;
    --blatt-rand: 40px;
    --blatt-kante: 20px;
    --feld-richtung: column;
    --feld-beschriftung-breite: auto;
    --feld-eingabe-basis: auto;
    --kontaktwert-basis: 14ch;
    --sticker-breite: 28ch;
    --sticker-feld-zeiger: auto;
    --bereich-spalten: 1fr;
    --sticker-ziehen: grab;
    --sticker-zug: none;
    --sticker-griff: block;
    --sticker-bereich-anzeige: flex;
    --plan-anzeige: block;
    --plan-spalten: 1fr;
    --baum-reihe-anzeige: block;
    --wetterbild-hoehe: 190px;
    --wetterbild-breite: 100%;
    --wettertag-anzeige: block;
    --wetterecke-lage: absolute;
    --wetterecke-spalte: auto;
    --wetterbild-hub: 56px;
    --baum-schub: 116px;
    --baum-wuchs: 1;
    --baum-heb: 8px;
    --wetter-grad: 44px;
    --wetter-wort: 15px;
    --wetter-wind-wort: 12px;
    --wetterwoche-spalten: 1fr;
    --baum-kasten-breite: 15ch;
    --baum-ausrichtung: flex-start;
    --baum-linie-anzeige: block;
    --baum-kaesten-breite: max-content;
    --baum-kaesten-umbruch: nowrap;
    --baum-kaesten-rand: auto;
    --baum-tafel-richtung: row;
    --baum-paar-trenner: 0;
    --baum-paar-strich: 1px solid var(--linie);
    --baum-tafel-breite: auto;
    --plan-tag-anzeige: block;
    --ausfall-strich: 4px;
    --planzeiten-anzeige: none;
    --planzelle-zeit-anzeige: block;
    --plankopf-lage: static;
    --brett-korn: 14px;
    --sticker-neigung: 1.2deg;
    --blatt-neigung: 0.6deg;
    --feldhinweis-anzeige: block;
    --feldgruppe-innen: 20px;
    --feldgruppe-rahmen: 1px;
    --radius:         18px;
    --radius-karte:   26px;
    --radius-klein:   2px;
    --chatbild-hoehe:   220px;
    --huelle-lage: fixed;
    --huelle-breite: 240px;
    --huelle-richtung: column;
    --rumpf-einzug: 240px;
    --menueknopf-anzeige: none;
    --menuetafel-inhalt: visible;
    --menuetafel-lage: static;
    --abdunklung-anzeige: none;
    --seitentitel: clamp(28px, 4cqi, 40px);
    --seitenkopf-richtung: row;
    --huelle-mindestbreite: 0;
    --marke-reihenfolge: 0;
    --menuetafel-rahmen: 0px;
    --menuetafel-schatten: none;
    --menuepunkt-schrift: 14px;
    --menuepunkt-radius: 3px;
    --seitenkopf-ausrichtung: flex-end;
    --huelle-verteilung: flex-start;
    --marke-dehnung: 0 0 auto;
    --marke-schrift: 22px;
    --titelgruppe-lage: static;
    --titelgruppe-hoehe: auto;
    --titelgruppe-breite: auto;
    --titelgruppe-ausrichtung: baseline;
    --titelgruppe-ueberlauf: visible;
  }
}

@media (min-width: 1200px) {
  :root {
    /* Desktop */
    --brett-raum-abstand: 16px;
    --brett-rahmen: 14px;
    --brett-innen-y: 24px;
    --brett-innen-x: 28px;
    --brett-spaltenabstand: 28px;
    --brettkarte-min: 150px;
    --brettkarte-min-voll: 220px;
    --brettkarte-titel: 23px;
    --brettkarte-titel-voll: 28px;
    --brettkarte-text-anzeige: none;
    --brettkarte-textgroesse: 13px;
    --brettkopf-titel: 24px;
    --kopfhoehe:      56px;
    --marke-anzeige:  inline;
    --menuetafel-breite: 400px;
    --menueliste-spalten: 1fr;
    --aufgabe-umbruch: nowrap;
    --aufgabe-titel-breite: auto;
    --detail-spalten: 1fr 1fr;
    --detail-listenanzeige: block;
    --kalender-spalten: repeat(7, 1fr);
    --kalender-leertag-anzeige: block;
    --monat-spalten: 34px repeat(7, minmax(0, 1fr));
    --monat-reihe-spalten: 2 / -1;
    --monat-kw-anzeige: block;
    --monat-schrift: 14px;
    --monat-zeilenhoehe: 74px;
    --essensplan-spalten: 1fr;
    --board-spalten: repeat(4, minmax(0, 1fr));
    --board-zeilen-richtung: column;
    --brettkarte-neigung: 1.2deg;
    --schrift:        16px;
    --schrift-gross:  26px;
    --schaltflaeche:  36px;
    --abstand:        20px;
    --kachel-min:     260px;
    --maske-breite: 46ch;
    --knopfwort-anzeige: inline;
    --geraetewort: "diesem Computer";
    --kopftrenner-anzeige: block;
    --knopfzusatz-anzeige: inline;
    --einstellungsliste-spalten: 1fr;
    --infoboardboxen-anzeige: grid;
    --qrcode-breite: 200px;
    --blatt-breite: 794px;
    --blatt-rand: 94px;
    --blatt-kante: 20px;
    --feld-richtung: column;
    --feld-beschriftung-breite: auto;
    --feld-eingabe-basis: auto;
    --kontaktwert-basis: 14ch;
    --sticker-breite: 28ch;
    --sticker-feld-zeiger: auto;
    --bereich-spalten: repeat(3, minmax(0, 1fr));
    --sticker-ziehen: grab;
    --sticker-zug: none;
    --sticker-griff: block;
    --sticker-bereich-anzeige: none;
    --plan-anzeige: grid;
    --plan-spalten: auto repeat(5, minmax(0, 1fr));
    --baum-reihe-anzeige: grid;
    --wetterbild-hoehe: 190px;
    --wetterbild-breite: 770px;
    --wettertag-anzeige: flex;
    --wetterecke-lage: static;
    --wetterecke-spalte: 250px;
    --wetterbild-hub: 88px;
    --baum-schub: 66px;
    --baum-wuchs: 1.9;
    --baum-heb: 0px;
    --wetter-grad: 44px;
    --wetter-wort: 15px;
    --wetter-wind-wort: 12px;
    --wetterwoche-spalten: 1fr;
    --baum-kasten-breite: 16ch;
    --baum-ausrichtung: center;
    --baum-linie-anzeige: block;
    --baum-kaesten-breite: max-content;
    --baum-kaesten-umbruch: nowrap;
    --baum-kaesten-rand: auto;
    --baum-tafel-richtung: row;
    --baum-paar-trenner: 0;
    --baum-paar-strich: 1px solid var(--linie);
    --baum-tafel-breite: auto;
    --plan-tag-anzeige: contents;
    --ausfall-strich: 4px;
    --planzeiten-anzeige: contents;
    --planzelle-zeit-anzeige: none;
    --plankopf-lage: sticky;
    --brett-korn: 14px;
    --sticker-neigung: 1.2deg;
    --blatt-neigung: 1.2deg;
    --feldhinweis-anzeige: block;
    --feldgruppe-innen: 20px;
    --feldgruppe-rahmen: 1px;
    --radius:         16px;
    --radius-karte:   22px;
    --radius-klein:   2px;
    --chatbild-hoehe:   220px;
    --huelle-lage: fixed;
    --huelle-breite: 240px;
    --huelle-richtung: column;
    --rumpf-einzug: 240px;
    --menueknopf-anzeige: none;
    --menuetafel-inhalt: visible;
    --menuetafel-lage: static;
    --abdunklung-anzeige: none;
    --seitentitel: clamp(28px, 4cqi, 40px);
    --seitenkopf-richtung: row;
    --huelle-mindestbreite: 0;
    --marke-reihenfolge: 0;
    --menuetafel-rahmen: 0px;
    --menuetafel-schatten: none;
    --menuepunkt-schrift: 14px;
    --menuepunkt-radius: 3px;
    --seitenkopf-ausrichtung: flex-end;
    --huelle-verteilung: flex-start;
    --marke-dehnung: 0 0 auto;
    --marke-schrift: 22px;
    --titelgruppe-lage: static;
    --titelgruppe-hoehe: auto;
    --titelgruppe-breite: auto;
    --titelgruppe-ausrichtung: baseline;
    --titelgruppe-ueberlauf: visible;
  }
}

/* --- Das Handy im Querformat (FAM-235) ----------------------------------- */

/* **Wer sein Handy dreht, sieht die ganze Woche.** Breit **und flach** ist
   ein gedrehtes Handy; breit und hoch ein Tablet. Die Hoehe ist der ehrliche
   Unterscheider - und dieselbe Bedingung nimmt der Tablet-Satz oben aus.

   **Es stehen hier fuenf Zeilen und keine 98.** Der Handy-Satz gilt in
   diesem Fenster weiter (er steht am `:root`, und der Tablet-Satz greift
   nicht); geaendert wird nur, was aus der Liste ein Gitter macht. *Ein
   zweiter Wertesatz waere eine zweite Stelle fuer dieselbe Sache.*

   **Gemessen am 07.10.2026 bei 812 x 375** (Abzug der echten Maske, DPR 1):

   | | Spalte | dem Fach bleiben | gekuerzt |
   |---|---|---|---|
   | vorher: Tablet-Satz, Liste | 492,1 px | 476,1 px | 0 von 24 |
   | nur die fuenf Zeilen, Seitenleiste bleibt | 74,6 px | 58,6 px | **10 von 24** |
   | **die fuenf Zeilen ohne Seitenleiste** | **127,4 px** | **111,4 px** | **0** |

   Die mittlere Zeile ist der Fehlschlag von FAM-192 am Tablet (54,5 px, 13
   von 27) **noch einmal** - und genau deshalb darf der Tablet-Satz hier
   nicht greifen. Die untere traegt mehr als der Desktop selbst (106,1 px).

   **Was das nicht entscheidet:** Querformat ist **keine fuenfte Ansicht**.
   Es steht nicht in der Ansichtsauswahl, denn es ist ein Zustand des
   Geraets und keine Wahl - und wer ausdruecklich *Handy* waehlt, bekommt
   ueber `body[data-ansicht]` weiterhin die Liste (Regel 0). */
@media (min-width: 568px) and (max-width: 960px) and (max-height: 500px) {
  :root {
    /* Handy im Querformat */
    --plan-anzeige: grid;
    --plan-spalten: auto repeat(5, minmax(0, 1fr));
    --plan-tag-anzeige: contents;
    --planzeiten-anzeige: contents;
    --planzelle-zeit-anzeige: none;
    --plankopf-lage: sticky;
}
}

/* ===========================================================================
   3. Die Uebersteuerung - schlaegt die Haltepunkte
   ===========================================================================
   Dieselben Variablen, hoehere Gewichtung. Das Attribut setzt der Server in
   base.html aus dem Cookie (app/ansichten.py).

   Jeder Satz setzt **jede** Variable - ein vergessener Wert faellt lautlos
   auf die Automatik zurueck (test_jede_ansicht_setzt_dieselben_merkmale). */

body[data-ansicht="handy"] {
  --brett-raum-abstand: 8px;
  --brett-rahmen: 9px;
  --brett-innen-y: 16px;
  --brett-innen-x: 16px;
  --brett-spaltenabstand: 18px;
  --brettkarte-min: 150px;
  --brettkarte-min-voll: 220px;
  --brettkarte-titel: 23px;
  --brettkarte-titel-voll: 28px;
  --brettkarte-text-anzeige: none;
  --brettkarte-textgroesse: 13px;
  --brettkopf-titel: 22px;
  --kopfhoehe:      56px;
  --marke-anzeige:  inline;
  --menuetafel-breite: min(78%, 300px);   /* die Schublade (A3) */
  --menueliste-spalten: repeat(2, 1fr);
  --aufgabe-umbruch: wrap;
  --aufgabe-titel-breite: 100%;
  --detail-spalten: 1fr;
  --detail-listenanzeige: none;
  --kalender-spalten: 1fr;
  --kalender-leertag-anzeige: block;
  --monat-spalten: 22px repeat(7, minmax(0, 1fr));
  --monat-reihe-spalten: 2 / -1;
  --monat-kw-anzeige: block;
  --monat-schrift: 11px;
  --monat-zeilenhoehe: 34px;
  --essensplan-spalten: 1fr;
  --board-spalten: repeat(4, 262px);
  --board-zeilen-richtung: column;
  --brettkarte-neigung: 0.6deg;
  --schrift:        12px;
  --schrift-gross:  16px;
  --schaltflaeche:  36px;
  --abstand:        10px;
  --kachel-min:     140px;
  --maske-breite: 100%;
  --knopfwort-anzeige: inline;
  --geraetewort: "deinem Handy";
  --kopftrenner-anzeige: none;
  --knopfzusatz-anzeige: none;
  --einstellungsliste-spalten: 1fr;
  --infoboardboxen-anzeige: block;
  --qrcode-breite: 180px;
  --blatt-breite: 100%;
  --blatt-rand: 18px;
  --blatt-kante: 14px;
  --feld-richtung: row;
  --feld-beschriftung-breite: 40%;
  --feld-eingabe-basis: 0;
  --kontaktwert-basis: 100%;
  --sticker-breite: 100%;
  --sticker-feld-zeiger: none;
  --bereich-spalten: 1fr;
  --sticker-ziehen: default;
  --sticker-zug: auto;
  --sticker-griff: none;
  --sticker-bereich-anzeige: flex;
  --plan-anzeige: block;
  --plan-spalten: 1fr;
  --baum-reihe-anzeige: block;
  --wetterbild-hoehe: 150px;
  --wetterbild-breite: 100%;
  --wettertag-anzeige: block;
  --wetterecke-lage: absolute;
  --wetterecke-spalte: auto;
  --wetterbild-hub: 56px;
  --baum-schub: 96px;
  --baum-wuchs: 1;
  --baum-heb: 8px;
  --wetter-grad: 34px;
  --wetter-wort: 13px;
  --wetter-wind-wort: 11px;
  --wetterwoche-spalten: 1fr;
  --baum-kasten-breite: 100%;
  --baum-ausrichtung: flex-start;
  --baum-linie-anzeige: none;
  --baum-kaesten-breite: 100%;
  --baum-kaesten-umbruch: wrap;
  --baum-kaesten-rand: 0;
  --baum-tafel-richtung: column;
  --baum-paar-trenner: 1px solid var(--linie);
  --baum-paar-strich: 0;
  --baum-tafel-breite: 100%;
  --plan-tag-anzeige: block;
  --ausfall-strich: 3px;
  --planzeiten-anzeige: none;
  --planzelle-zeit-anzeige: block;
  --plankopf-lage: static;
  --brett-korn: 10px;
  --sticker-neigung: 0.6deg;
  --blatt-neigung: 0.5deg;
  --feldhinweis-anzeige: none;
  --feldgruppe-innen: 0px;
  --feldgruppe-rahmen: 0px;
  --radius:         14px;
  --radius-karte:   20px;
  --radius-klein:   2px;
  --chatbild-hoehe:   180px;
  --huelle-lage: relative;
  --huelle-breite: auto;
  --huelle-richtung: row;
  --rumpf-einzug: 0px;
  --menueknopf-anzeige: flex;
  --menuetafel-inhalt: hidden;
  --menuetafel-lage: absolute;
  --abdunklung-anzeige: block;
  --seitentitel: 17px;
  --seitenkopf-richtung: column;
  --huelle-mindestbreite: max-content;
  --marke-reihenfolge: 3;
  --menuetafel-rahmen: 1px;
  --menuetafel-schatten: 0 8px 24px oklch(27% 0.02 170 / 0.18);
  --menuepunkt-schrift: 14px;
  --menuepunkt-radius: 3px;
  --seitenkopf-ausrichtung: stretch;
  --huelle-verteilung: space-between;
  /* **Die Marke nimmt nur noch ihren Text** (09.10.2026, gemessen).

     Hier stand `1 1 0`, und die Begruendung dafuer ist abgelaufen: *„Menue
     und Marke teilen sich den Rand zu gleichen Teilen - erst dadurch steht
     der Titel in der Mitte des Rahmens"* (Abschnitt 2). **Der Titel war
     damals ein Flexkind zwischen beiden**; seit dem 09.10.2026 steht er
     `fixed` und zentriert sich selbst (Abschnitt 17).

     **Geblieben war eine Klickflaeche von 172,5 px fuer einen Text von
     78** - sie lag zur Haelfte **ueber dem Titel**, und die 44-px-Flaeche
     des Fragezeichens stiess darin an. *Eine Flaeche, die groesser ist als
     das, was man sieht, ist der stille Teil eines Treffers am falschen
     Ort.*

     **Optisch aendert sich nichts**: Der Text stand schon rechtsbuendig in
     seiner Box und steht gemessen weiter an derselben Stelle (286,6 bis
     365). **Am Infoboard bleibt `1 1 0`** - dort ist die Kopfzeile eine
     Zeile mit drei Teilen, und der Titel steht im Seitenkopf. */
  --marke-dehnung: 0 0 auto;
  --marke-schrift: 12px;
  --titelgruppe-lage: fixed;
  --titelgruppe-hoehe: var(--kopfhoehe);
  /* **Eine Rechnung, keine zweite Stelle** (09.10.2026, gemessen).

     Am Handy hochkant sind **150 px** richtig: Der Kasten steht dann von
     112,5 bis 262,5, und der Text *Family Board* beginnt bei 286,6.

     **Im Querformat ist derselbe Streifen 812 px breit**, und zwischen dem
     Wort *Menue* (endet bei 65,7) und der Marke liegen gemessen **657,9 px**
     frei - *„Was nicht geklappt hat"* brach darin trotzdem um. **260 px sind
     gemessen**: Der laengste Titel der Anwendung ist *„Auf deinem Handy
     installieren"* mit **240,8 px**, dazu 4,8 Abstand und 13,4 fuer das
     Fragezeichen - **259,0**.

     **Warum die Zeile nicht im Querformat-Block steht**: Der ist eine
     **Berichtigung des Stundenplans** und holt jeden Wert beim Desktop
     (`test_das_querformat_setzt_dieselben_werte_wie_der_desktop`) - *eine
     Breite, die der Desktop nicht kennt, waere dort ein eigener Entwurf.*
     **Gesagt hat es die Wache, nicht das Nachdenken.**

     **`40vw` ist hergeleitet und nicht geraten**: 150 / 375 ist genau 0,4.
     Dieselbe Rechnung ergibt bei 812 px **324,8** und laeuft damit in den
     Deckel; an der unteren Kante des Querformats (568) sind es 227,2, und
     der Kasten steht dort von 170,4 bis 397,6 - der Text der Marke beginnt
     erst bei 483. */
  --titelgruppe-breite: min(260px, 40vw);
  --titelgruppe-ausrichtung: center;
  --titelgruppe-ueberlauf: hidden;
}

body[data-ansicht="tablet"] {
  --brett-raum-abstand: 16px;
  --brett-rahmen: 14px;
  --brett-innen-y: 20px;
  --brett-innen-x: 18px;
  --brett-spaltenabstand: 14px;
  --brettkarte-min: 120px;
  --brettkarte-min-voll: 180px;
  --brettkarte-titel: 21px;
  --brettkarte-titel-voll: 24px;
  --brettkarte-text-anzeige: none;
  --brettkarte-textgroesse: 12px;
  --brettkopf-titel: 20px;
  --kopfhoehe:      64px;
  --marke-anzeige:  inline;
  --menuetafel-breite: 400px;
  --menueliste-spalten: 1fr;
  --aufgabe-umbruch: nowrap;
  --aufgabe-titel-breite: auto;
  --detail-spalten: 1fr;
  --detail-listenanzeige: none;
  --kalender-spalten: repeat(7, 1fr);
  --kalender-leertag-anzeige: block;
  --monat-spalten: 30px repeat(7, minmax(0, 1fr));
  --monat-reihe-spalten: 2 / -1;
  --monat-kw-anzeige: block;
  --monat-schrift: 13px;
  --monat-zeilenhoehe: 60px;
  --essensplan-spalten: 1fr;
  --board-spalten: repeat(4, minmax(0, 1fr));
  --board-zeilen-richtung: column;
  --brettkarte-neigung: 1.2deg;
  --schrift:        17px;
  --schrift-gross:  26px;
  --schaltflaeche:  48px;
  --abstand:        20px;
  --kachel-min:     220px;
  --maske-breite: 46ch;
  --knopfwort-anzeige: inline;
  --geraetewort: "deinem Tablet";
  --kopftrenner-anzeige: block;
  --knopfzusatz-anzeige: inline;
  --einstellungsliste-spalten: 1fr;
  --infoboardboxen-anzeige: grid;
  --qrcode-breite: 220px;
  --blatt-breite: 100%;
  --blatt-rand: 40px;
  --blatt-kante: 20px;
  --feld-richtung: column;
  --feld-beschriftung-breite: auto;
  --feld-eingabe-basis: auto;
  --kontaktwert-basis: 14ch;
  --sticker-breite: 28ch;
  --sticker-feld-zeiger: auto;
  --bereich-spalten: 1fr;
  --sticker-ziehen: grab;
  --sticker-zug: none;
  --sticker-griff: block;
  --sticker-bereich-anzeige: flex;
  --plan-anzeige: block;
  --plan-spalten: 1fr;
  --baum-reihe-anzeige: block;
  --wetterbild-hoehe: 190px;
  --wetterbild-breite: 100%;
  --wettertag-anzeige: block;
  --wetterecke-lage: absolute;
  --wetterecke-spalte: auto;
  --wetterbild-hub: 56px;
  --baum-schub: 116px;
  --baum-wuchs: 1;
  --baum-heb: 8px;
  --wetter-grad: 44px;
  --wetter-wort: 15px;
  --wetter-wind-wort: 12px;
  --wetterwoche-spalten: 1fr;
  --baum-kasten-breite: 15ch;
  --baum-ausrichtung: flex-start;
  --baum-linie-anzeige: block;
  --baum-kaesten-breite: max-content;
  --baum-kaesten-umbruch: nowrap;
  --baum-kaesten-rand: auto;
  --baum-tafel-richtung: row;
  --baum-paar-trenner: 0;
  --baum-paar-strich: 1px solid var(--linie);
  --baum-tafel-breite: auto;
  --plan-tag-anzeige: block;
  --ausfall-strich: 4px;
  --planzeiten-anzeige: none;
  --planzelle-zeit-anzeige: block;
  --plankopf-lage: static;
  --brett-korn: 14px;
  --sticker-neigung: 1.2deg;
  --blatt-neigung: 0.6deg;
  --feldhinweis-anzeige: block;
  --feldgruppe-innen: 20px;
  --feldgruppe-rahmen: 1px;
  --radius:         18px;
  --radius-karte:   26px;
  --radius-klein:   2px;
  --chatbild-hoehe:   220px;
  --huelle-lage: fixed;
  --huelle-breite: 240px;
  --huelle-richtung: column;
  --rumpf-einzug: 240px;
  --menueknopf-anzeige: none;
  --menuetafel-inhalt: visible;
  --menuetafel-lage: static;
  --abdunklung-anzeige: none;
  --seitentitel: clamp(28px, 4cqi, 40px);
  --seitenkopf-richtung: row;
  --huelle-mindestbreite: 0;
  --marke-reihenfolge: 0;
  --menuetafel-rahmen: 0px;
  --menuetafel-schatten: none;
  --menuepunkt-schrift: 14px;
  --menuepunkt-radius: 3px;
  --seitenkopf-ausrichtung: flex-end;
  --huelle-verteilung: flex-start;
  --marke-dehnung: 0 0 auto;
  --marke-schrift: 22px;
  --titelgruppe-lage: static;
  --titelgruppe-hoehe: auto;
  --titelgruppe-breite: auto;
  --titelgruppe-ausrichtung: baseline;
  --titelgruppe-ueberlauf: visible;
}

body[data-ansicht="desktop"] {
  --brett-raum-abstand: 16px;
  --brett-rahmen: 14px;
  --brett-innen-y: 24px;
  --brett-innen-x: 28px;
  --brett-spaltenabstand: 28px;
  --brettkarte-min: 150px;
  --brettkarte-min-voll: 220px;
  --brettkarte-titel: 23px;
  --brettkarte-titel-voll: 28px;
  --brettkarte-text-anzeige: none;
  --brettkarte-textgroesse: 13px;
  --brettkopf-titel: 24px;
  --kopfhoehe:      56px;
  --marke-anzeige:  inline;
  --menuetafel-breite: 400px;
  --menueliste-spalten: 1fr;
  --aufgabe-umbruch: nowrap;
  --aufgabe-titel-breite: auto;
  --detail-spalten: 1fr 1fr;
  --detail-listenanzeige: block;
  --kalender-spalten: repeat(7, 1fr);
  --kalender-leertag-anzeige: block;
  --monat-spalten: 34px repeat(7, minmax(0, 1fr));
  --monat-reihe-spalten: 2 / -1;
  --monat-kw-anzeige: block;
  --monat-schrift: 14px;
  --monat-zeilenhoehe: 74px;
  --essensplan-spalten: 1fr;
  --board-spalten: repeat(4, minmax(0, 1fr));
  --board-zeilen-richtung: column;
  --brettkarte-neigung: 1.2deg;
  --schrift:        16px;
  --schrift-gross:  26px;
  --schaltflaeche:  36px;
  --abstand:        20px;
  --kachel-min:     260px;
  --maske-breite: 46ch;
  --knopfwort-anzeige: inline;
  --geraetewort: "diesem Computer";
  --kopftrenner-anzeige: block;
  --knopfzusatz-anzeige: inline;
  --einstellungsliste-spalten: 1fr;
  --infoboardboxen-anzeige: grid;
  --qrcode-breite: 200px;
  --blatt-breite: 794px;
  --blatt-rand: 94px;
  --blatt-kante: 20px;
  --feld-richtung: column;
  --feld-beschriftung-breite: auto;
  --feld-eingabe-basis: auto;
  --kontaktwert-basis: 14ch;
  --sticker-breite: 28ch;
  --sticker-feld-zeiger: auto;
  --bereich-spalten: repeat(3, minmax(0, 1fr));
  --sticker-ziehen: grab;
  --sticker-zug: none;
  --sticker-griff: block;
  --sticker-bereich-anzeige: none;
  --plan-anzeige: grid;
  --plan-spalten: auto repeat(5, minmax(0, 1fr));
  --baum-reihe-anzeige: grid;
  --wetterbild-hoehe: 190px;
  --wetterbild-breite: 770px;
  --wettertag-anzeige: flex;
  --wetterecke-lage: static;
  --wetterecke-spalte: 250px;
  --wetterbild-hub: 88px;
  --baum-schub: 66px;
  --baum-wuchs: 1.9;
  --baum-heb: 0px;
  --wetter-grad: 44px;
  --wetter-wort: 15px;
  --wetter-wind-wort: 12px;
  --wetterwoche-spalten: 1fr;
  --baum-kasten-breite: 16ch;
  --baum-ausrichtung: center;
  --baum-linie-anzeige: block;
  --baum-kaesten-breite: max-content;
  --baum-kaesten-umbruch: nowrap;
  --baum-kaesten-rand: auto;
  --baum-tafel-richtung: row;
  --baum-paar-trenner: 0;
  --baum-paar-strich: 1px solid var(--linie);
  --baum-tafel-breite: auto;
  --plan-tag-anzeige: contents;
  --ausfall-strich: 4px;
  --planzeiten-anzeige: contents;
  --planzelle-zeit-anzeige: none;
  --plankopf-lage: sticky;
  --brett-korn: 14px;
  --sticker-neigung: 1.2deg;
  --blatt-neigung: 1.2deg;
  --feldhinweis-anzeige: block;
  --feldgruppe-innen: 20px;
  --feldgruppe-rahmen: 1px;
  --radius:         16px;
  --radius-karte:   22px;
  --radius-klein:   2px;
  --chatbild-hoehe:   220px;
  --huelle-lage: fixed;
  --huelle-breite: 240px;
  --huelle-richtung: column;
  --rumpf-einzug: 240px;
  --menueknopf-anzeige: none;
  --menuetafel-inhalt: visible;
  --menuetafel-lage: static;
  --abdunklung-anzeige: none;
  --seitentitel: clamp(28px, 4cqi, 40px);
  --seitenkopf-richtung: row;
  --huelle-mindestbreite: 0;
  --marke-reihenfolge: 0;
  --menuetafel-rahmen: 0px;
  --menuetafel-schatten: none;
  --menuepunkt-schrift: 14px;
  --menuepunkt-radius: 3px;
  --seitenkopf-ausrichtung: flex-end;
  --huelle-verteilung: flex-start;
  --marke-dehnung: 0 0 auto;
  --marke-schrift: 22px;
  --titelgruppe-lage: static;
  --titelgruppe-hoehe: auto;
  --titelgruppe-breite: auto;
  --titelgruppe-ausrichtung: baseline;
  --titelgruppe-ueberlauf: visible;
}

/* Das Infoboard: Wohnzimmer, zwei Meter Abstand, Bedienung mit dem Finger.

   **Es zeigt weniger, nicht mehr.** Ein Wohnzimmerbildschirm ist kein
   Arbeitsplatz - wer daran vorbeigeht, soll in zwei Sekunden sehen, was
   ansteht, nicht suchen. Deshalb 380px Mindestkachel statt 260: vier grosse
   Kacheln statt acht kleiner. */
body[data-ansicht="infoboard"] {
  --brett-raum-abstand: 24px;
  --brett-rahmen: 20px;
  --brett-innen-y: 36px;
  --brett-innen-x: 42px;
  --brett-spaltenabstand: 40px;
  --brettkarte-min: 220px;
  --brettkarte-min-voll: 320px;
  --brettkarte-titel: 34px;
  --brettkarte-titel-voll: 40px;
  --brettkarte-text-anzeige: none;
  --brettkarte-textgroesse: 18px;
  --brettkopf-titel: 36px;
  --kopfhoehe:      96px;
  --marke-anzeige:  inline;
  /* **Fuenf Spalten seit FAM-172, und die Breite gehoert dazu.**

     Der dreizehnte Menuepunkt (das *Sticker Board*) kostet hier eine
     Zeile - **gemessen 74 px**, und aufgeklappt waren nur **71 px** Luft:
     Die Tafel scrollte um drei Pixel. Design.md hatte genau das
     vorhergesagt.

     **Fuenf Spalten allein reichen nicht.** In 1000 px waeren das 200 px
     je Punkt, und dann brechen vier Woerter um (*Neuigkeiten*,
     *Geburtstage*, *Essensplan*, *Sticker Board*) - **ein umgebrochenes
     Wort ist schlimmer als drei Pixel Bildlauf**.

     **1250 px sind 250 je Punkt - genau so viel wie bisher bei vier.**
     Getragen haette auch 1150 (230 px, gemessen ohne Umbruch); das waere
     die Kante, und der naechste laengere Modulname braeche dort. *Eine
     Zahl, die gerade noch reicht, ist keine Entscheidung.* */
  --menuetafel-breite: 1250px;
  --menueliste-spalten: repeat(5, 1fr);
  --aufgabe-umbruch: nowrap;
  --aufgabe-titel-breite: auto;
  --detail-spalten: 1fr 1fr;
  --detail-listenanzeige: block;
  --kalender-spalten: 1fr;
  --kalender-leertag-anzeige: block;
  --monat-spalten: 54px repeat(7, minmax(0, 1fr));
  --monat-reihe-spalten: 2 / -1;
  --monat-kw-anzeige: block;
  --monat-schrift: 22px;
  --monat-zeilenhoehe: 120px;
  --essensplan-spalten: 1fr;
  --board-spalten: repeat(4, minmax(0, 1fr));
  --board-zeilen-richtung: column;
  --brettkarte-neigung: 1.6deg;
  --schrift:        24px;
  --schrift-gross:  40px;
  --schaltflaeche:  72px;
  --abstand:        32px;
  --kachel-min:     380px;
  --maske-breite: 46ch;
  --knopfwort-anzeige: inline;
  --geraetewort: "diesem Bildschirm";
  --kopftrenner-anzeige: block;
  --knopfzusatz-anzeige: inline;
  --einstellungsliste-spalten: repeat(2, 1fr);
  --infoboardboxen-anzeige: grid;
  --qrcode-breite: 320px;
  --blatt-breite: 992px;
  --blatt-rand: 118px;
  --blatt-kante: 32px;
  --feld-richtung: column;
  --feld-beschriftung-breite: auto;
  --feld-eingabe-basis: auto;
  --kontaktwert-basis: 14ch;
  --sticker-breite: 28ch;
  --sticker-feld-zeiger: auto;
  --bereich-spalten: repeat(3, minmax(0, 1fr));
  --sticker-ziehen: grab;
  --sticker-zug: none;
  --sticker-griff: block;
  --sticker-bereich-anzeige: none;
  --plan-anzeige: grid;
  --plan-spalten: auto repeat(5, minmax(0, 1fr));
  --baum-reihe-anzeige: block;
  --wetterbild-hoehe: 280px;
  --wetterbild-breite: 1080px;
  --wettertag-anzeige: flex;
  --wetterecke-lage: static;
  --wetterecke-spalte: 320px;
  --wetterbild-hub: 88px;
  --baum-schub: 66px;
  --baum-wuchs: 1.9;
  --baum-heb: 0px;
  --wetter-grad: 64px;
  --wetter-wort: 22px;
  --wetter-wind-wort: 16px;
  --wetterwoche-spalten: 1fr;
  --baum-kasten-breite: 20ch;
  --baum-ausrichtung: center;
  --baum-linie-anzeige: block;
  --baum-kaesten-breite: max-content;
  --baum-kaesten-umbruch: nowrap;
  --baum-kaesten-rand: auto;
  --baum-tafel-richtung: row;
  --baum-paar-trenner: 0;
  --baum-paar-strich: 1px solid var(--linie);
  --baum-tafel-breite: auto;
  --plan-tag-anzeige: contents;
  --ausfall-strich: 6px;
  --planzeiten-anzeige: contents;
  --planzelle-zeit-anzeige: none;
  --plankopf-lage: sticky;
  --brett-korn: 22px;
  --sticker-neigung: 1.6deg;
  --blatt-neigung: 1.6deg;
  --feldhinweis-anzeige: block;
  --feldgruppe-innen: 32px;
  --feldgruppe-rahmen: 1px;
  --radius:         22px;
  --radius-karte:   30px;
  --radius-klein:   2px;
  --chatbild-hoehe:   320px;
  --huelle-lage: relative;
  --huelle-breite: auto;
  --huelle-richtung: row;
  --rumpf-einzug: 0px;
  --menueknopf-anzeige: flex;
  --menuetafel-inhalt: hidden;
  --menuetafel-lage: absolute;
  --abdunklung-anzeige: block;
  --seitentitel: 36px;
  --seitenkopf-richtung: column;
  --huelle-mindestbreite: max-content;
  --marke-reihenfolge: 3;
  --menuetafel-rahmen: 1px;
  --menuetafel-schatten: 0 8px 24px oklch(27% 0.02 170 / 0.18);
  --menuepunkt-schrift: 24px;
  --menuepunkt-radius: 22px;
  --seitenkopf-ausrichtung: stretch;
  --huelle-verteilung: space-between;
  --marke-dehnung: 1 1 0;
  --marke-schrift: var(--schrift-gross);
  --titelgruppe-lage: static;
  --titelgruppe-hoehe: auto;
  --titelgruppe-breite: auto;
  --titelgruppe-ausrichtung: baseline;
  --titelgruppe-ueberlauf: visible;
}

/* ===========================================================================
   4. Grundgeruest - nur der Inhalt scrollt (Design.md, Regel 2)
   =========================================================================== */

* { box-sizing: border-box; }

html, body {
  /* **Die Seite ist so hoch wie das, was man sieht** (FAM-270, 04.11.2026,
     auf Rückmeldung vom Handy: *„Wo kann ich denn jetzt hier speichern?"*).

     **`height: 100%` ist am Handy nicht die sichtbare Höhe.** Android
     Chrome rechnet Prozente gegen den **großen** Viewport – den ohne
     Adressleiste. Steht die Leiste da, ist die Seite um deren Höhe zu
     hoch, und genau so weit lässt sich das Dokument verschieben.

     **`overflow: hidden` hält nur den Finger auf, nicht den Browser.** Wer
     in ein Feld tippt, bekommt es vom Browser ins Bild geschoben – und
     dabei rutscht die ganze Anwendung nach oben. **Zurück kommt man
     nicht**: Scrollen lässt sich die Seite ja gerade nicht.

     **Gemessen am Bildschirmfoto des Nutzers**, und die Zahl steht auf
     beiden Seiten: Oben fehlten Kopfzeile und Seitenkopf – und mit dem
     Seitenkopf der Knopf *Eintragen* –, stehen geblieben war allein der
     Titel, *das einzige Element mit `position: fixed`*. Unten stand ein
     grauer Streifen **derselben Höhe, rund 145 px**. Das ist die
     Verschiebung, kein freier Platz.

     **`dvh` ist die sichtbare Höhe** und wächst und schrumpft mit der
     Adressleiste. Damit gibt es nichts mehr zu verschieben: Die Seite ist
     so hoch wie ihr Fenster, der Überlauf ist **null** – und *was keinen
     Überlauf hat, kann auch kein Browser wegschieben.*

     **Die Prozentzeile bleibt als Rückfall stehen.** Ein Browser, der
     `dvh` nicht kennt, überspringt die zweite Zeile und bekommt den alten
     Stand; alle anderen überschreiben sie. *Eine Zeile, die nur manchmal
     greift, ist hier genau richtig* – sie ist der Notnagel, nicht die
     Regel.

     > **Was das nicht löst, steht daneben**: Die Tastatur schrumpft nur
     > den **sichtbaren** Viewport, nicht den, gegen den `dvh` rechnet.
     > Dafür trägt die Seite seit heute `interactive-widget=resizes-content`
     > (`base.html`) – erst beides zusammen schließt die Tür. */
  /* **`svh`, nicht `dvh`** (05.11.2026, gemeldet vom Gerät – dreimal, und
     beim dritten Mal mit dem Bild, das es entschieden hat).

     **`dvh` ist die *dynamische* Höhe**: Sie meint den Zustand, den der
     Browser gerade für den richtigen hält – und das ist auf Android der
     **große** Viewport, sobald er die Adressleiste für einziehbar hält.
     Steht sie trotzdem da, ist die Seite um deren Höhe zu hoch.

     **`svh` ist die *kleine* Höhe**: die **mit** Adressleiste. Für eine
     Seite, die nicht scrollt, ist das die einzige richtige Wahl – *die
     Leiste kann sich gar nicht einziehen, weil es nichts zu scrollen
     gibt.*

     **Warum das die ganze Kette bricht, ist gemessen** (05.11.2026): Ragt
     irgendetwas unter dem Körper heraus, bekommt das Dokument eine
     Scrollhöhe – und **`overflow: hidden` hält nur den Finger auf, nicht
     den Browser**. Gemessen ließ sich das Dokument trotz `hidden` um
     **150 px** schieben, sobald ein Element so weit herausragte; **mit
     `overflow: clip` genauso.** Wer das Schieben verhindern will, muss den
     **Überstand** verhindern, nicht das Scrollen.

     **Die Prozentzeile bleibt als Rückfall** – ein Browser ohne `svh`
     überspringt die zweite Zeile. `svh` und `dvh` sind zusammen
     ausgeliefert worden (Chrome 108, Safari 15.4); wer das eine kennt,
     kennt das andere.

     > **Hier stand `dvh`, seit FAM-270** (04.11.2026), mit derselben
     > Begründung und der richtigen Beobachtung – *nur mit der falschen
     > Einheit.* Der Satz von damals steht noch: *„Was es nicht beweist:
     > ob es am Gerät des Nutzers hält."* Es hat nicht gehalten. */
  height: 100%;
  margin: 0;
  overflow: hidden;          /* Die Seite selbst scrollt nicht. */
}

/* **Die Schale hängt am Viewport, nicht im Dokument** (05.11.2026, vierte
   Runde desselben Fehlers – und die erste, die ihn an der Wurzel fasst).

   **Gemessen am Gerät des Nutzers**, mit einer eigens gebauten Messseite:
   Geht die Tastatur auf, schrumpft der Viewport von **674 auf 455 px** –
   und der Browser **schiebt das Dokument um 340 px**, um das Feld ins Bild
   zu holen. *`overflow: hidden` hält den Finger auf, nicht den Browser*
   (eigene Messung: 150 px Überstand = 150 px schiebbar, trotz `hidden`
   **und** trotz `clip`). Zurück kommt niemand, weil der Finger ja nicht
   scrollen darf.

   **`position: fixed` mit `inset: 0` nimmt den Körper aus dem Fluss.**
   Danach hat das Dokument **keinen Inhalt mehr, der scrollen könnte** –
   die Schiebestrecke ist nicht „verboten", sondern **null**. Es gibt nichts
   mehr zu schieben, also auch nichts, was hängen bleibt.

   **Und es braucht gar keine Viewport-Einheit mehr.** `inset: 0` ist der
   Viewport, wie er gerade ist – mit Adressleiste, ohne, mit Tastatur.
   *Drei Runden lang habe ich die richtige Einheit gesucht (`100%`, `dvh`,
   `svh`); die richtige Antwort ist, keine zu benutzen.*

   > **Was dabei NICHT passiert**: `position: fixed` am Körper macht ihn
   > **nicht** zum Bezug für fest gesetzte Nachkommen – das tun nur
   > `transform`, `filter`, `perspective`, `will-change` und `contain`.
   > `--titelgruppe-lage: fixed` rechnet also weiter gegen den Viewport.

   **Der Preis, ausgesprochen**: Ein fest gesetzter Körper kann nicht mehr
   mitwachsen. *Das ist hier keine Einschränkung, sondern die Zusage* –
   Regel 2 sagt seit dem ersten Tag, dass nur der Inhalt scrollt. */
body {
  position: fixed;
  inset: 0;

  /* **`clip`, nicht nur `hidden`** (05.11.2026, gemessen an einem echten
     Fall).

     **`hidden` haelt nur den Finger auf, nicht den Browser**: Der Koerper
     bleibt ein Scrollkasten, und wer `scrollIntoView` ruft, scrollt **jeden**
     scrollbaren Vorfahren mit - auch ihn. Genau das tat
     `heute-anspringen.js` auf der Terminseite: gemessen `body.scrollTop =
     163`, die Kopfzeile damit bei `y = -163`, *und der Finger holt sie nicht
     zurueck.*

     **`clip` macht gar keinen Scrollkasten auf.** Es gibt dann nichts zu
     scrollen - auch nicht fuer den Browser. Gemessen am selben Aufruf:
     `body.scrollTop` bleibt **0**, die Kopfzeile bei **0**, und der innere
     Kasten scrollt wie vorher auf 310.

     > **Hier stand am selben Tag, `clip` helfe nicht.** Das war eine Messung
     > unter der Viewport-Emulation des Browser-Panes, und die hat damals
     > eine Zahl geliefert, die dem Pane gehoerte (`11-fallstricke.md`).
     > *Eine Abhilfe, die nichts bewirkt, ist zuerst ein Verdacht gegen die
     > Messung* - an einem echten Ueberstand wirkt sie.

     **`hidden` bleibt als Rueckfall davor** - ein Browser ohne `clip`
     ueberspringt die zweite Zeile und bekommt den alten Stand. */
  overflow: hidden;
  overflow: clip;
}

body {
  display: flex;
  flex-direction: column;
  background: var(--grund);
  color: var(--text);
  font-size: var(--schrift);
  /* **Die Handschrift ist die Standardschrift** (27.09.2026, Design 2a, A2).

     Hier stand bis dahin `Inter, "Segoe UI", system-ui, …` - eine Grotesk.
     **Der Auftrag dreht das um**: Family Board schreibt von Hand, und die
     Ausnahmen sind die Titel (`--schrift-titel`) und alles, was eine feste
     Zeichenbreite braucht (Kennungen, Protokolle).

     *Der Preis steht in `Design.md`*: Eine Handschrift ist fuer ein Kind
     schlechter zu lesen als eine Grotesk - deshalb ist die Zeilenhoehe
     gemessen und nicht geraten (1.75 am Zettel, FAM-169). */
  font-family: var(--schrift-hand);
  line-height: 1.5;
}

/* **Formularelemente erben keine Schrift** - das ist die Falle, die dieses
   Blatt fuenfmal getroffen hat (`textarea`, `file`, `time`, `number`,
   `color`; `11-fallstricke.md`).

   **Deshalb steht hier eine Positivliste und kein `font: inherit` an
   `input`**: Jenes naehme auch Groesse und Gewicht mit, und ein
   `<input type="date">` braucht beides anders. *Wer ein neues Feldelement
   einbaut, traegt es hier ein* - bewacht von
   `test_jedes_eingabeelement_erbt_die_schrift`. */
button, input, select, textarea, optgroup {
  font-family: inherit;
}

/* **Die Titelschrift** (Design 2a, A2) - Logo, Seitentitel, Abschnittstitel.

   **`h3` ist ausdruecklich nicht dabei**: Der Auftrag nennt `h1`, `h2` und
   Kartentitel. Am Brett ist `h3` der **Spaltenkopf**, und der traegt seit
   der Filzwand die Handschrift (FAM-175) - *zwei Entwuerfe, und der
   juengere gilt nur dort, wo er etwas sagt.* */
/* **Ueberschriften tragen die Grotesk** (27.09.2026, auf Wunsch: *„Bitte
   auch Ueberschriften wie Erledigt oder Archiv in Segoe UI umwandeln"*).

   Bis heute Vormittag war das Young Serif fuer beide; der Auftrag *Einkauf
   2a* hat `h1` auf Segoe UI gestellt und `h2` bei der Serifenschrift
   gelassen. **Der Wunsch zieht `h2` nach** - und das ist die konsequentere
   Fassung: *Eine Ueberschrift ist eine Ueberschrift, ob sie „Verliehen"
   heisst oder „Archiv".*

   **Young Serif bleibt dort, wo Papier ist**: auf den Titeln der Karten
   (`.kartentitel`) und im Satz eines leeren Abschnitts.

   **Seit dem 29.09.2026 ist es Century Gothic 500** (auf Wunsch) - die
   Schrift, die bis dahin nur der Einkauf trug. Das Gewicht gehoert zur
   Schrift und steht deshalb hier daneben, nicht in einem Maskenabschnitt. */
h1, h2 {
  font-family: var(--schrift-seitentitel);
  font-weight: 500;
  letter-spacing: -.01em;
}

.kopfzeile {
  flex: 0 0 var(--kopfhoehe);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand);
  padding: 0 var(--abstand);
  background: var(--akzent);
  color: var(--flaeche);
}

/* **Die Kopfzeile traegt seit FAM-107 nur noch zwei Dinge**: die Marke und
   den Knopf, der das Menue oeffnet.

   Bis dahin standen dort ausserdem eine Auswahlliste, ein Knopf *Wechseln*,
   der eigene Name und *Abmelden* - und das war der Grund fuer eine ganze
   Reihe von Notbehelfen: Der Inhalt war am Handy 624 px breit in einem
   375-px-Fenster, die Marke musste weichen, und zwei Elemente mussten
   ausdruecklich schrumpfen duerfen. **Die Ursache ist weg, also sind es die
   Notbehelfe auch.**

   `position: relative` ist die einzige neue Zeile: Die Menuetafel haengt sich
   darunter, ohne die Seite laenger zu machen. */
.kopfzeile { position: relative; }

/* **Die Marke steht wieder in allen vier Ansichten.**

   Sie war am Handy seit FAM-35 ausgeblendet, weil Ansichtsauswahl, Name und
   „Abmelden“ sonst nicht nebeneinander passten - *„das ist der Notbehelf,
   nicht der Entwurf"* (Design.md, Regel 10). Die drei sind jetzt im Menue. */
/* --- Die Anordnung der Kopfzeile: links, Mitte, rechts -------------------

   **Auf Wunsch (20.09.2026):** *„Family Board auf die rechte Seite und die
   Ueberschrift, wo man gerade ist, in die Mitte oben und fett."*

   **Sie steht an EINER Stelle**, und das ist die eigentliche Arbeit dieser
   Aenderung. Vorher stand sie an dreien - hier (`space-between`), in
   Abschnitt 8 (`margin-left: auto` am Menue) und in Abschnitt 11 (`order`
   und `margin-right: auto`). Im Blatt gewinnt die letzte, und wer eine davon
   sucht, findet die falsche: **genau die Falle aus FAM-107**, wo zwei alte
   Notbehelfe die neue Menuetafel schlugen und eine Ueberschrift 177 px breit
   mitten darin stand. *Auf einem Bild sieht das nach Absicht aus.*

   **Zentriert mit `margin: 0 auto`, nicht mit `position: absolute`.** Ein
   absolut gesetzter Titel staende exakt in der Seitenmitte und liefe bei
   einem langen Namen **unter** das Menue und die Marke - und die Frage aus
   FAM-50 und FAM-84 lautet nicht *„sieht es gut aus“*, sondern *„gibt es ein
   Bedienelement, das nirgends erreichbar ist"*. Mit auto-Abstaenden teilt
   sich der Titel den uebrigen Platz und schrumpft mit Ellipse; Menue und
   Marke behalten ihre Breite.

   **Ohne Titel bleiben zwei Kinder**, und dann traegt `space-between` sie
   wie vorher nach links und rechts. */
/* **Die Reihenfolge kommt seit dem 27.09.2026 aus einem Merkmal** (Design
   2a, A3): In der Zeile steht die Marke rechts (`3`), in der Leiste oben
   (`0`). *Sie steht weiterhin an genau dieser einen Stelle* - Abschnitt 16
   setzt sie nicht noch einmal, er liefert nur den Wert. */
.kopfzeile > .menue       { order: 1; flex: 1 1 0; }

/* **`flex: 1 1 0` teilt in einer Zeile den Rand und in einer Spalte die
   Hoehe** - gemessen war die Marke damit **502 px hoch**, und das Menue fing
   in der Bildmitte an. *In der Leiste ist sie ein Logo, kein Randstueck.*

   **Alles, was den Platz der Marke entscheidet, steht in dieser einen
   Regel** - Reihenfolge, Dehnung und Ausrichtung. Die Wache aus FAM-107
   zaehlt mit. */
.kopfzeile > .marke       { order: var(--marke-reihenfolge);
                            flex: var(--marke-dehnung);
                            align-self: center; }

/* **Menue und Marke teilen sich den Rand zu gleichen Teilen.** Erst dadurch
   steht der Titel in der Mitte des **Rahmens** und nicht nur in der Mitte
   des uebrigen Platzes - gemessen waren das sonst 17 px Versatz am Handy und
   **50 px am Infoboard**, weil die Marke breiter ist als der Menueknopf.

   **`min-width: max-content` ist die Zeile, die es sicher macht:** Beide
   duerfen wachsen, aber **nie unter ihren Inhalt schrumpfen**. Der
   Menueknopf ist der einzige Weg ins Menue - die Frage aus FAM-50 und
   FAM-84 lautet *„gibt es ein Bedienelement, das nirgends erreichbar
   ist"*. Reicht der Platz nicht, schrumpft stattdessen der **Titel**, und
   zwar mit Ellipse. */
/* **`min-width` kommt ebenfalls aus einem Merkmal.** In der Zeile
   verhindert `max-content`, dass der Hamburger unter seinen Inhalt
   schrumpft - *der einzige Weg ins Menue.* In der Leiste gibt es diese Enge
   nicht, und die Zeile machte die Tafel **346 px breit in einer 216 px
   breiten Leiste** (gemessen am 27.09.2026). */
.kopfzeile > .menue,
.kopfzeile > .marke { min-width: var(--huelle-mindestbreite); }

/* Titel und Fragezeichen gehoeren zusammen - **ein Kasten, nicht zwei
   Kinder** (siehe base.html). */
.seitenmitte {
  display: flex;
  align-items: center;
  gap: 0.4em;
  min-width: 0;
  /* **Der Rueckfall, wenn die Raender nicht wachsen koennen.** Solange Menue
     und Marke da sind, teilen sie sich den Rand (siehe oben) und es bleibt
     kein freier Platz, den diese Zeile verteilen koennte. Blendet jemand die
     Marke aus (`--marke-anzeige`), steht der Titel damit trotzdem mittig. */
  margin: 0 auto;
}

.marke {
  display: var(--marke-anzeige);
  /* Sie fuellt ihre Haelfte des Randes, ihr Text steht am rechten Ende. */
  text-align: right;
  color: inherit;
  font-size: var(--schrift-gross);
  font-weight: 600;
  text-decoration: none;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   Das Menue (FAM-107) - ein `<details>`, kein JavaScript
   ---------------------------------------------------------------------------
   Es klappt auf und zu wie der Erledigt-Block (FAM-65) und die Artikelzeile
   (FAM-70). Ein Knopf, der ohne Skript nichts taete, gehoerte nicht ins HTML
   (Design.md, Regel 7).

   **Der Hamburger gilt in allen vier Ansichten**, und das ist die
   Entscheidung dieser Karte. Die Vorlage schlug vor, am Desktop und am
   Infoboard ein stehendes Menue zu lassen. Dagegen sprechen zwei Dinge: Zwei
   Bauarten derselben Sache laufen auseinander, und der Preis waere eine
   CSS-Regel, die ein zugeklapptes `<details>` von aussen aufzwingt - die
   haengt an der Browserfassung und ist genau die Art Zusage, die still
   bricht. Der Gewinn steht daneben: 232 px mehr Inhalt am Desktop, 320 px am
   Infoboard. */

/* **Der Griff sitzt in der Mitte der Kopfzeile** (29.09.2026, auf
   Rueckmeldung am Geraet: *„vertikal nicht mittig ausgerichtet"*).

   Das `<details>` ist ein Flex-Kind der Kopfzeile und wird auf deren ganze
   Hoehe gestreckt (`align-items: stretch`); der `<summary>` darin sass
   damit **oben**. **Gemessen am Handy: seine Mitte bei 18 px, die der
   Kopfzeile bei 28** - zehn Pixel, und daneben stand die Marke richtig.

   **`align-self`, nicht `align-items`** - und das ist gemessen, nicht
   geraten: Ein `display: flex; align-items: center` am `<details>` hat
   **nichts** bewegt (der Griff blieb bei `t = 0`), `align-self: center`
   setzt ihn auf `t = 10`. *Die Kopfzeile streckt den Kasten; zu zentrieren
   ist der Kasten, nicht sein Inhalt.*

   > **Und `display: flex` am `<details>` waere hier die gefaehrlichere
   > Haelfte gewesen**: Seit Chrome 131 haengt das Aufklappen am
   > Pseudo-Element `::details-content` (Design 2a, A3) - *eine Anzeigeart
   > am `<details>` selbst ist genau die Stelle, an der das schon einmal
   > gekippt ist.* */
.menue {
  position: static;
  align-self: center;
}

/* **Der Knopf ist die Bedienflaeche, nicht das Zeichen darin.** `--schaltflaeche`,
   keine erfundene Zahl (die Lehre aus FAM-62, der Fund aus FAM-84). */
.menueknopf {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) / 2);
  min-height: var(--schaltflaeche);
  padding: 0 calc(var(--abstand) / 2);
  border-radius: var(--radius);
  cursor: pointer;
  /* **So gross wie die Marke daneben** (29.09.2026, auf Rueckmeldung am
     Geraet: *„Menue ist zu klein"*).

     Gemessen am Handy: **12 px gegen 18** - `--schrift` ist die Groesse des
     Fliesstextes, und der Menueknopf steht nicht im Fliesstext, sondern in
     der Kopfzeile. **Die beiden Woerter dort gehoeren zusammen**, also
     tragen sie dieselbe Groesse; *eine eigene Zahl waere eine dritte
     Stelle, an der ueber die Kopfzeile entschieden wird.* */
  font-size: var(--marke-schrift);
  font-weight: 600;

  /* **Das Aufklappzeichen wird selbst gesetzt** (die Lehre aus FAM-70).
     Dort hat ein `display: flex` am `<summary>` den `::marker` des Browsers
     geloescht - gemessen `marker_content: normal`, und zu sehen war nichts:
     Ein ganzer Bedienweg war unauffindbar. Hier steht es ausdruecklich da,
     statt sich auf denselben Nebeneffekt zu verlassen. */
  list-style: none;
}
.menueknopf::-webkit-details-marker { display: none; }

/* **Und es WECHSELT, es dreht sich nicht** - derselbe Grundsatz wie beim
   Haken, bei der Sonne (FAM-60) und beim ueberfaelligen Termin (FAM-65). */
.menueknopf::before {
  content: "\2630";          /* ☰ */
  font-size: 1.2em;
  line-height: 1;
}
.menue[open] > .menueknopf::before { content: "\2715"; }   /* ✕ */

.menueknopf:hover { background: var(--akzent-dunkel); }

/* Die aufgeklappte Tafel haengt unter der Kopfzeile und **verlaengert die
   Seite nicht** (Regel 2).

   **Sie ist linksbuendig, weil der Knopf links steht** (21.09.2026, auf
   Wunsch: „Das Menü geht rechts auf, obwohl der Menübutton links ist“).

   Hier stand `margin-left: auto` und daneben die Begruendung *„sie ist
   rechtsbuendig, weil der Knopf rechts steht"*. Das war richtig, als es
   geschrieben wurde (FAM-107) - **seit dem 20.09.2026 steht der Knopf
   links**, und die Regel ist nicht mitgewandert. Woertlich dieselbe Falle
   wie die alten `.abmelden`-Regeln (FAM-107) und das Fragezeichen in der
   gruenen Kopfzeile (FAM-109): *Wer einen Baustein verschiebt, sucht seine
   alten Regeln im ganzen Blatt.*

   **Sie scrollt in sich.** Fuenfzehn Menuepunkte, der Einstellungsblock,
   Abmelden und die Fussnote passen am Handy nicht auf einen Bildschirm -
   und die Seite selbst scrollt nicht. */
.menuetafel {
  position: absolute;
  top: 100%;
  z-index: 20;

  /* **Sie haengt an der linken Kante - und die Regel sagt es** (21.09.2026).

     Hier stand `left: 0` UND `right: 0` UND eine Breite, aufgeloest ueber
     `margin-right: auto`. Gemessen stand die Tafel damit richtig (x = 0 in
     allen vier Ansichten), **aber die Regel sagte es nicht**: Sie verliess
     sich darauf, dass CSS 2.1 die Gleichung nach dem einen `auto`-Rand
     aufloest. Nimmt jemand spaeter `margin-right` weg - es sieht ja
     ueberfluessig aus -, ist die Angabe **ueberbestimmt**, und dann gilt,
     was der Browser ignoriert.

     **`right: auto` ist die Aussage**: Die linke Kante steht, die rechte
     ergibt sich aus der Breite. Dieselbe Linie wie `flex: 0 0 auto` statt
     einer blossen `width` (FAM-64) - *wer eine Zusage macht, sagt sie,
     statt sie ausrechnen zu lassen.*

     Am Handy deckelt nichts (`auto`), sonst steht die Tafel als Schublade
     am linken Rand - unter ihrem Knopf. */
  left: 0;
  right: auto;
  width: 100%;
  max-width: var(--menuetafel-breite);

  /* **`dvh`, nicht `vh`** (05.11.2026, gemeldet vom Gerät): `100vh` ist am
     Handy die Höhe **ohne** Adressleiste - die Tafel dürfte damit höher
     werden als das, was man sieht, und ragt bei vielen Menüpunkten unten
     aus dem Bild. *Wörtlich der Fund von FAM-270, nur an der Tafel.*

     **Die `vh`-Zeile bleibt als Rückfall davor**: Ein Browser ohne `dvh`
     verwirft die zweite und behält die erste. */
  max-height: calc(100vh - var(--kopfhoehe));
  max-height: calc(100svh - var(--kopfhoehe));
  overflow-y: auto;

  padding: calc(var(--abstand) / 2);
  background: var(--flaeche);
  color: var(--text);
  border: 1px solid var(--linie);
  border-radius: 0 0 var(--radius-karte) var(--radius-karte);   /* oben bricht sie an der Kopfzeile ab */
  box-shadow: 0 8px 24px oklch(27% 0.02 170 / 0.18);
}

.menueliste {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* **Die Modulliste steht in Spalten, sobald sie nicht mehr auf den Schirm
   passt** (21.09.2026, auf Wunsch: *„Bei Fuhrpark versuchst du das Menue
   immer ganz anzuzeigen, indem du die Punkte enger zusammenschiebst, kannst
   du das bei diesem Menue auch machen?"*).

   **Gemessen ging es nicht ueber die Hoehe.** Am Infoboard brauchen allein
   die vierzehn Punkte **1008 px** (14 x `--schaltflaeche`), und der Platz
   unter der Kopfzeile ist **984 px** - enger zusammenschieben reicht dort
   rechnerisch nicht, und die Bedienflaeche kleinzurechnen ist genau das,
   wovor Regel 10 seit FAM-62 warnt: *auf einem Bild sehen 41 px neben 48 px
   gleich aus.* Am Infoboard wiegt es doppelt, denn dort wird aus zwei Metern
   getippt.

   **Die Breite lag dagegen brach**: 420 von 1920 px. Also mehr Spalten statt
   weniger Hoehe - `--menueliste-spalten`, je Ansicht gemessen (Regel 10).

   **Zeilenweise, nicht spaltenweise** (`grid-auto-flow: row`, die Vorgabe).
   Spaltenweise laese sich eine Liste natuerlicher - und ein **neues Modul
   verschoebe dann die halbe zweite Spalte**. Das Menue waechst *angehaengt,
   nicht einsortiert* (FAM-86, siebenmal bestaetigt): Wer Termine blind
   trifft, soll sie weiter blind treffen. Zeilenweise behaelt jeder Punkt
   seinen Platz, wenn hinten einer dazukommt.

   **Nur die Modullisten** - die Liste in den *Einstellungen* traegt einen
   einzigen Punkt, und ein halbbreiter Punkt neben einer leeren Spalte
   saehe aus wie ein Fehler.

   **Der Selektor hat am 17.10.2026 die Ebene gewechselt**: Bis dahin stand
   hier `.menuetafel > .menueliste`, und das war richtig, solange die
   Listen unmittelbar in der Tafel standen. Seit die Bereiche zuklappbar
   sind, steckt jede in einem `<details class="menuebereich">` - *ein
   `>`-Selektor, dem eine Huelle dazwischengeschoben wird, greift lautlos
   nicht mehr*, und die Punkte stuenden untereinander statt in zwei
   Spalten. **Die Abo-Liste steht weiter unmittelbar in der Tafel**, also
   nennt der Selektor beide Wege. */
.menuetafel > .menueliste,
.menuebereich > .menueliste {
  display: grid;
  grid-template-columns: var(--menueliste-spalten);
}

/* **Der Kopf eines Bereichs** (17.10.2026) - die Zeile, die man antippt.

   **Kein Knopf im Sinne von Abschnitt 15**: Ein `<summary>` ist weder
   `button` noch `.schaltflaeche`, und ein Textmarker unter einem
   Bereichsnamen saehe aus wie eine Aktion. *Das Menue bleibt das Menue*
   (Auftrag vom 27.09.2026, Abschnitt 8).

   **Die Trefferflaeche ist trotzdem `--knopf-flaeche`** - sie ist der
   Boden, den dieses Projekt jedem Ziel gibt, und hier tippt ein Kind. */
.menuebereichkopf {
  display: flex;
  align-items: center;
  gap: 0.5ch;
  min-height: var(--knopf-flaeche);
  cursor: pointer;
  color: var(--text-leise);
  font-size: calc(var(--menuepunkt-schrift) * 0.85);
  list-style: none;
}

/* **Das Zeichen wechselt, also wird es selbst gesetzt** - die Falle steht
   fuenfmal im Index: `display: flex` an einem `<summary>` loescht den
   `::marker`. */
.menuebereichkopf::marker,
.menuebereichkopf::-webkit-details-marker {
  display: none;
}

.menuebereichkopf::after {
  /* **Das Zeichen steht hier als Zeichen, nicht als Escape** (17.10.2026).
     Hier stand `"BE"`, durch ein Python-Skript geschrieben - und ``
     ist dort ein **Oktalescape**: In der Datei landete das Steuerzeichen
     0x15 und daneben der Text `BE`. **Genau das stand am Menue**, und im
     ersten Bereich spiegelverkehrt, weil `[open]` es um 180 Grad dreht.
     *Ein Escape, das man nicht braucht, kann auch nicht kaputtgehen* -
     die Falle steht seit dem 15.09.2026 im Index, dies ist das dritte
     Mal. */
  content: "▾";
  margin-inline-start: auto;
  transition: transform 0.15s ease;
}

.menuebereich[open] > .menuebereichkopf::after {
  transform: rotate(180deg);
}

/* **Der Strich sitzt am Bereich, nicht mehr an der Liste** (17.10.2026) -
   sonst zoege ihn der Bereichskopf nicht mit, und ein zugeklappter Bereich
   haette seine Trennlinie **unter** sich statt ueber sich. */
.menuebereich + .menuebereich,
.menuebereich + .menueliste {
  margin-top: var(--abstand);
  padding-top: var(--abstand);
  border-top: 1px solid var(--linie);
}

/* **Menuepunkte duerfen umbrechen** (gemessen am 13.09.2026, FAM-29).

   In der Tafel ist der Platz da - anders als in der 88 px schmalen Leiste,
   die es bis FAM-107 gab. Die Zeile bleibt trotzdem stehen: „Einkaufsliste“
   ist keinen Deut kuerzer als „Uebersicht“, und `anywhere` ist hier der
   Unterschied, der zaehlt - `break-word` laesst die Mindestbreite beim
   laengsten unzerbrechlichen Stueck. */
.menuetafel a,
.menuegruppe > summary,
.ansichtsliste button,
.abmelden button {
  display: flex;
  align-items: center;

  /* **Zwei Leerzeichen zwischen Symbol und Wort** (21.09.2026, auf Wunsch).
     Er steht hier und nicht am Symbol: Ein Menuepunkt ohne Bild - den gibt
     es nicht mehr, aber er koennte wiederkommen - saehe sonst eingerueckt
     aus.

     **Er haengt an der SCHRIFT, nicht am Abstand**, und das ist die
     Aenderung. Hier stand `calc(var(--abstand) / 2)`; gemessen waren das
     5 / 10 / 10 / 16 px neben einem Leerzeichen von 3,3 / 4,7 / 4,4 / 6,6 px
     - also **1,5 Leerzeichen am Handy und 2,4 am Infoboard**. Der Wunsch ist
     typografisch („zwei Leerzeichen“), `--abstand` ist ein Layoutwert, und
     deshalb lief beides auseinander.

     **0.6em sind gemessen 2,2 Leerzeichen in jeder Ansicht** - dieselbe
     Rechnung wie beim Mengenfeld des Einkaufs, wo `ch` den Text misst und
     `--abstand` den Rand (18.09.2026). */
  gap: 0.6em;
  min-height: var(--schaltflaeche);
  width: 100%;
  padding: calc(var(--abstand) / 4) calc(var(--abstand) / 2);
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  text-decoration: none;
  /* **Ein Menuename wird nicht getrennt** (28.09.2026, auf Rueckmeldung:
     *„Einstellungen sollen nicht Umbrechen"*). Hier stand `hyphens: auto`
     und `overflow-wrap: anywhere`, und die Zeile brach mitten im Wort:
     *Einstellun-gen*.

     **Noetig war das einmal**, als am Infoboard fuenf Spalten zu 200 px
     standen (FAM-172) - jene Karte hat es anders geloest (*„ein
     umgebrochenes Wort ist schlimmer als drei Pixel Bildlauf"*, die Tafel
     wuchs auf 1250 px), **und die Trennung blieb trotzdem stehen**.

     Mehrwortige Eintraege brechen weiterhin am **Leerzeichen**: *„Auf dem
     Geraet installieren"* braucht gemessen 283 px und bekommt zwei Zeilen.
     *Ein Satz darf umbrechen, ein Name nicht.* */
  hyphens: none;
  cursor: pointer;
}

.menuetafel a:hover,
.menuegruppe > summary:hover,
.ansichtsliste button:hover,
.abmelden button:hover { background: var(--akzent-blass); }

/* **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65) - deshalb steht
   `aria-current` im Markup und hier nur, was man sieht. */
.menuetafel .aktiv {
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-weight: 600;
}

/* Die kleine Ueberschrift ueber der Ansichtsauswahl und ueber dem Abmelden.
   Kein `<h2>`: Die Tafel ist eine Navigation, keine Gliederung. */
/* **Und im Menue ist die Luft zwischen den Bloecken halb so gross**
   (21.09.2026, auf Wunsch: *„kannst du die Punkte enger zusammenschieben?“*).

   **Das ist der Teil des Wunsches, der nichts kostet.** Kleiner wird der
   Abstand zwischen den Bloecken - **nicht die Flaeche, die ein Daumen
   treffen muss** (woertlich die Lehre aus FAM-109, und die Wache
   `test_keine_bedienflaeche_wird_kleiner_gerechnet` haelt sie seit FAM-62).

   Gemessen holt es am Infoboard **64 px**, am Tablet und Desktop 40, am
   Handy 20 - genug, damit die Tafel auch mit aufgeklappten *Einstellungen*
   auf den Schirm passt.

   **Die Fussnote steht auch auf den beiden Anmeldeseiten und im
   Betriebsbereich** - dort ist ihr Rand richtig, und deshalb wird die Klasse
   nicht angefasst, sondern nur ihr Vorkommen im Menue. */
.menuetafel .menueueberschrift,
.menuetafel > .menuegruppe,
.menuetafel > .fussnote { margin-top: calc(var(--abstand) / 2); }

.menueueberschrift {
  margin: var(--abstand) 0 4px;
  padding: 0 calc(var(--abstand) / 2);
  color: var(--text-leise);
  font-size: 0.88em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* **Der Einstellungsblock** (21.09.2026, auf Wunsch): Die Ansichtsauswahl
   liegt hinter einem Aufklapper, statt offen in der Tafel zu stehen.

   **Das Aufklappzeichen wird selbst gesetzt und WECHSELT** (▾/▴) - zum
   vierten Mal dieselbe Zeile nach FAM-70, FAM-136 und FAM-146: Ein
   `display: flex` an einem `<summary>` loescht den `::marker` des Browsers,
   und dann sieht ein Aufklapper nicht mehr wie einer aus. Er funktioniert
   dabei weiter, **und genau das ist das Gefaehrliche**: Niemand weiss, dass
   man tippen kann. */
.menuegruppe {
  margin-top: var(--abstand);
  border-top: 1px solid var(--linie);
}

.menuegruppe > summary {
  min-height: var(--schaltflaeche);
  width: 100%;
  padding: calc(var(--abstand) / 4) calc(var(--abstand) / 2);
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;
}
.menuegruppe > summary::-webkit-details-marker { display: none; }

.menuegruppe > summary::after {
  content: "▾";
  margin-left: auto;
  color: var(--text-leise);
}
.menuegruppe[open] > summary::after { content: "▴"; }

/* Der Block darin rueckt ein - er gehoert sichtbar zu seinem Aufklapper.
   **Die Bedienflaeche schrumpft dabei nicht** (die Lehre aus FAM-62): Es
   wandert der Rand, nicht die Hoehe. */
.menuegruppe .ansichtsliste {
  margin-left: calc(var(--abstand) / 2);
  border-top: 0;
}

/* **Ein Knopf je Ansicht, keine Auswahlliste** (FAM-107). Fuenf
   48-px-Flaechen statt eines Auswahlfeldes und eines Knopfes *Wechseln* -
   ein Tipp statt zweier, und ohne eine Zeile JavaScript. */
.ansichtsliste,
.abmelden {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
  border-top: 1px solid var(--linie);
}

/* **Die Ansichtsliste steht in denselben Spalten wie die Modulliste**
   (21.09.2026): Fuenf Ansichten und die Hilfe untereinander waren am
   Infoboard 432 px - mehr als ein Drittel des Platzes fuer etwas, das man
   selten braucht.

   **Sie steht hier und nicht oben bei `.menuegruppe .ansichtsliste`**, und
   das ist gemessen: Dort stand sie zuerst, also **vor** dem `display: flex`
   darueber - gleiche Gewichtung, und **im Blatt gewinnt die spaetere**. Die
   Messung meldete danach Zahl fuer Zahl dieselben Werte wie ohne die Regel.
   *Genau die Falle aus FAM-107 und FAM-84.*

   **`.abmelden` bleibt eine Spalte** - Name und Knopf gehoeren untereinander,
   und zwei Knoepfe nebeneinander hiessen, dass man sich beim Abmelden
   vertippen kann. */
.ansichtsliste {
  display: grid;
  grid-template-columns: var(--menueliste-spalten);
}

/* **Die Liste unter *Einstellungen* steht am Infoboard in zwei Spalten**
   (22.09.2026) - und das ist gemessen, nicht gestaltet.

   Mit dem dritten Punkt (*Installieren*) wuchs die aufgeklappte Tafel dort
   von 913 auf **987 px** bei **982 px** Platz: Sie scrollte um fuenf Pixel.
   **Woertlich die Lage von FAM-172**, nur einen Block weiter - und die
   Antwort ist dieselbe: *mehr Spalten statt weniger Hoehe.* Kleiner wird der
   Abstand, die Zeile, der Rahmen - **nicht die Flaeche, die ein Daumen
   treffen muss** (FAM-109, FAM-62).

   **Nur am Infoboard.** In den drei anderen Ansichten passt die Tafel
   bequem (673 / 913 / 719 px), und ein Punkt neben einem anderen waere dort
   schmaler, ohne dass es etwas brauchte.

   **Drei Punkte in zwei Spalten heisst eine leere Zelle**, und das ist kein
   Widerspruch zu der Entscheidung von gestern: Dort standen **zwei** Punkte
   in **vier** Spalten - also zwei leere Zellen neben zwei Woertern, *und das
   sieht aus wie ein Fehler.* Eine halbe Zeile ist der Normalfall jeder
   Rasterliste. */
.menuegruppe .menueliste {
  display: grid;
  grid-template-columns: var(--einstellungsliste-spalten);
}

/* Der eigene Name fuehrt zum eigenen Passwort (FAM-35). Er steht ueber dem
   Abmeldeknopf, damit nicht beides wie ein Knopf aussieht. */
.abmelden .wer {
  padding: calc(var(--abstand) / 4) calc(var(--abstand) / 2);
  color: var(--text);
  overflow-wrap: anywhere;
}

.fussnote {
  margin: var(--abstand) 0 0;
  padding: calc(var(--abstand) / 2);
  border-top: 1px solid var(--linie);
  color: var(--text-leise);
  font-size: 0.85em;
}

/* **Der Rumpf scrollt, nicht die Seite** (Design.md, Regel 2 - gemessen am
   13.09.2026 in der Vier-Ansichten-Pruefung zu FAM-29). Breite Inhalte
   scrollen in ihrem eigenen Kasten, nicht durch Verbreitern der Seite. */
.rumpf {
  flex: 1 1 auto;
  display: flex;
  min-height: 0;             /* Ohne das waechst der Rumpf ueber die Seite. */
  overflow-x: auto;
}

.hauptbereich {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}

.werkzeugleiste {
  flex: 0 0 auto;
  display: flex;
  /* **Sie muss umbrechen duerfen** (FAM-84). Bis dahin hatte keine Maske mehr
     als drei Aktionen, und die Leiste stand mit `nowrap` da, ohne dass es
     auffiel. Der Kalender hat vier - gemessen am Handy bei 375 px lagen
     *Heute* und *Woche* danach RECHTS AUSSERHALB des Fensters, und zwar bei
     jeder Scrollposition (die Seite scrollt nicht, Regel 2).

     Dieselbe Falle wie FAM-50, wo *abhaken* im Seitwaerts-Scrollbereich lag -
     nur eine Ebene hoeher: Dort war eine Tabellenzeile unteilbar, hier war es
     die Leiste selbst. **Eine Leiste, die eine Aktion abschneidet, ist keine
     Werkzeugleiste**, und das gilt fuer jede Maske, nicht nur fuer diese. */
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) / 2);

  /* **Sie ist kein Kasten mehr** (29.09.2026, auf Auftrag).

     Hier standen `padding`, `background: var(--flaeche)` und eine
     Unterlinie - gedacht fuer eine **Leiste ueber dem Inhalt** (A4). Seit
     der Kopf des Einkaufs das Konzept aller Module ist, steht sie **in der
     Titelzeile**, neben dem Titel: Dort sah der weisse Kasten aus wie ein
     Block, der ueber die Zeile hinausragt (*„Abo bitte im Hintergrund
     transparent"*, 28.09.2026).

     **Die Zeilen sind hier weggefallen statt weiter unten ueberschrieben**
     - es gab den Selektor sonst **zweimal** im Blatt, und im Browser gewann
     die spaetere. *Zwei Regeln fuer dieselbe Frage laufen auseinander.* */
  padding: 0;
  background: transparent;
  border-bottom: 0;
}

/* **Eine Aktion in der Leiste, die etwas AENDERT, ist ein Formular** - kein
   Verweis (FAM-32). Bis FAM-71 kam das nicht vor: Speichern bedient das
   Formular der Maske ueber `form="..."`, und alles andere waren Verweise.

   „Einkauf fertig“ ist die erste Aktion, die in der Leiste selbst ein POST
   abschickt. Ohne diese Zeile stuende das Formular als Block darin und
   riesse die Leiste auseinander.

   **Die Regel steht hier und nicht bei der Einkaufsliste**: Sie gilt fuer
   jede Maske, die als naechstes so einen Knopf bekommt. */
.werkzeugleiste form {
  display: flex;
  align-items: center;
}

.inhalt {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;          /* Nur hier wird gescrollt. */
  padding: var(--abstand);
}

/* ===========================================================================
   5. Benachrichtigungen (FAM-115) und Meldungen (Design.md, Regel 3)
   ===========================================================================

   **Zwei Baender, nicht eines**, und die Benachrichtigung steht oben. Eine
   Meldung ist die Antwort auf etwas, das man gerade selbst getan hat; eine
   Benachrichtigung ist etwas, das jemand anders getan hat - die
   ueberraschendere von beiden.

   Zusammenlegen ginge nicht: Eine Meldung verblasst nach vier Sekunden
   (Regel 3), eine Benachrichtigung darf das **nicht**. Die Zeile haette dann
   zwei Verhalten, und niemand saehe, welches gerade gilt. */

.benachrichtigungsleiste {
  flex: 0 0 auto;
  padding: calc(var(--abstand) / 2) var(--abstand) 0;
}

/* **Der warme Akzent - auffaellig, und der Text traegt trotzdem allein.**

   Wer rot-gruen-blind ist, sieht die Farbe nicht, und am Infoboard aus zwei
   Metern verschwimmt sie (FAM-65). Beides gilt gleichzeitig und ist kein
   Widerspruch: auffaellig genug, dass man hinsieht - und der Satz sagt allein,
   worum es geht. */
.benachrichtigung {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) / 2);
  margin-bottom: 4px;
  padding-right: 4px;
  background: var(--warm-blass);
  border: 1px solid var(--warm);
  border-radius: var(--radius);
}

/* **Der Satz ist der Knopf.** Ein Klick fuehrt zur Quelle und raeumt weg -
   beides zugleich, deshalb ein Formular und kein Verweis (FAM-53).

   Er sieht nicht aus wie ein Knopf, weil er die ganze Zeile ist; er verhaelt
   sich aber wie einer, und `--schaltflaeche` ist die Flaeche, die ein Daumen
   treffen muss (Regel 10). */
.benachrichtigungstext {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  min-height: var(--knopf-flaeche);
  padding: 0 var(--abstand);
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

button.benachrichtigungstext:hover { text-decoration: underline; }

/* --- Nachlesen (FAM-117) ---------------------------------------------

   **Kein warmer Hintergrund, und das ist Absicht.** Die Leiste oben faellt
   auf, damit man hinsieht; hier ist man schon da. Eine Maske, in der jede
   Zeile auffaellt, hat keine, die auffaellt. */
.nachleseliste {
  list-style: none;
  margin: 0;
  padding: 0;
}

.nachlesezeile {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) / 2);
  /* **Umbrechen darf sie** - die Lehre aus FAM-50 und FAM-93: Eine Zeile,
     die nicht umbrechen kann, schneidet am Handy die letzte Angabe ab, und
     die Messung meldet dabei `seitwaerts: 0`. */
  flex-wrap: wrap;
  padding: calc(var(--abstand) / 4) 0;
  border-bottom: 1px solid var(--linie);
}

/* Der Satz traegt die Aussage. Er ist zugleich der Weg zur Quelle, also eine
   Bedienflaeche - `--schaltflaeche`, keine erfundene Zahl (FAM-62, FAM-84). */
.nachlesetext {
  flex: 1 1 12ch;
  min-width: 0;
  display: flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  color: var(--text);
}

a.nachlesetext:hover { text-decoration: underline; }

/* **Der Zustand als Wort** (FAM-65) - nicht als Farbe und nicht als Punkt. */
.nachlesezustand {
  flex: 0 0 auto;
  color: var(--text-leise);
}

.nachlesezeit {
  flex: 0 0 auto;
  color: var(--text-leise);
}

/* Das ✕ - quadratisch, und es erreicht dieselbe Bedienflaeche.
   **`--knopf-flaeche`, nicht `--schaltflaeche`** (05.10.2026): *44 sind der
   Boden*, und mit dem kleineren Wert waere es am Handy und am Desktop
   36 × 36 gewesen - dieselbe Zeile, die `.tagesknopf` acht Tage lang zu
   schmal gehalten hat. */
.wegklicken {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  border: 0;
  background: none;
  color: var(--warm);
  cursor: pointer;
}

.meldungsleiste {
  flex: 0 0 auto;
  padding: calc(var(--abstand) / 2) var(--abstand);
}

.meldung {
  display: flex;
  align-items: center;
  max-height: calc(var(--schaltflaeche) - 4px);
  margin: 0 0 4px;
  padding: 0 var(--abstand);
  border-radius: var(--radius);
}

.meldung-fehler   { background: var(--fehler-blass); color: var(--fehler); }
.meldung-erfolg   { background: var(--erfolg-blass); color: var(--erfolg); }
.meldung-hinweis  { background: var(--akzent-blass); color: var(--akzent-dunkel); }

/* ===========================================================================
   6. Inhalt
   =========================================================================== */

h1 {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) / 2);
  margin: 0 0 var(--abstand);
  font-size: var(--schrift-gross);
}

h2 { font-size: calc(var(--schrift) * 1.2); margin: var(--abstand) 0 calc(var(--abstand) / 2); }
h3 { font-size: var(--schrift); margin: 0 0 4px; }

a { color: var(--akzent-dunkel); }

.hilfezeichen {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6em; height: 1.6em;
  border: 1px solid var(--linie);
  border-radius: 50%;
  font-size: 0.7em;
  text-decoration: none;
  /* Traegt die Trefferflaeche darunter (siehe die Regel gleich darunter). */
  position: relative;
}

/* **Das Fragezeichen ist ein Weg, also ein Knopf** (09.10.2026, gemessen).

   **Gemessen waren 13,4 px** am Handy, 19 am Tablet, 17,9 am Desktop und
   26,9 am Infoboard – in **jeder** Ansicht unter den **44**, die Abschnitt
   15 den Boden nennt (`--knopf-flaeche`, *„44 ist der Boden, nicht die
   Decke"*). Der Grund ist die Rechnung darueber: `1.6em` von `0.7em` sind
   **1,12em** des Umfelds, und am Handy ist das Umfeld 12 px.

   **Aufgefallen ist es erst, als der Titel am 09.10.2026 in den
   Kopfstreifen gezogen ist** – zu klein war es vorher genauso. *Eine Maske
   kann alle Tests bestehen und trotzdem falsch gebaut sein*
   (`11-fallstricke.md`).

   ## Gewachsen ist die Flaeche, nicht der Kreis

   Ein `min-width` am Kasten selbst machte aus dem Ring einen **44 px
   grossen Reifen** um ein Zeichen von 8,4 px – am Infoboard 72. *Das
   Fragezeichen steht neben einer Ueberschrift und ist nicht ihr Nachbar an
   Gewicht.* Deshalb liegt die Flaeche in einem Pseudo-Element **ueber** dem
   Ring: unsichtbar, mittig, und sie nimmt die Klicks fuer ihn entgegen.

   `::after` war an diesem Element frei – *wer ein Pseudo-Element setzt,
   sucht erst, ob es vergeben ist* (`11-fallstricke.md`).

   **`translate` und nicht `transform`**: Es ist eine eigene Eigenschaft und
   nimmt einem spaeteren `transform` nichts weg. */
.hilfezeichen::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: var(--knopf-flaeche);
  height: var(--knopf-flaeche);
  border-radius: 50%;
}

/* **Das Fragezeichen steht seit FAM-109 in der Kopfzeile** - und die ist
   gruen. Seine Farbe war fuer einen hellen Hintergrund gewaehlt: gemessen
   `oklch(0.42 0.11 170)` auf `oklch(0.52 0.11 170)`, also derselbe Farbton
   eine Spur dunkler. **Es war da und nicht zu sehen.**

   Dieselbe Falle wie die alten `.abmelden`-Regeln in FAM-107: Ein Baustein
   zieht um, seine Farbe bleibt am alten Ort stehen - und auf einem Bild
   sieht das nach Absicht aus.

   `inherit` statt einer zweiten Farbe: Die Kopfzeile setzt bereits
   `color: var(--flaeche)`, und `currentColor` am Rahmen zieht mit. Kein
   `color-mix()` (Regel 10 - gemischte Farben driften). */
.kopfzeile .hilfezeichen {
  color: inherit;
  border-color: currentColor;
  opacity: 0.75;
  flex: 0 0 auto;
}
.kopfzeile .hilfezeichen:hover { opacity: 1; }

.einleitung { color: var(--text-leise); max-width: 62ch; }

.hinweiskasten {
  max-width: 70ch;
  margin: var(--abstand) 0;
  padding: var(--abstand);
  background: var(--akzent-blass);
  border-left: 3px solid var(--akzent);
  border-radius: var(--radius);
}

.hilfetext { max-width: 70ch; }

.themenliste { padding-left: var(--abstand); }

/* Die Kacheln der geplanten Module - bewusst ohne Verweis (Regel 5). */
.kachelliste {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--kachel-min), 1fr));
  gap: var(--abstand);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* **Lange Woerter muessen brechen duerfen** (gefunden am 13.09.2026 in der
   Vier-Ansichten-Pruefung zu FAM-29).

   Am Handy ist eine Kachel 164px breit, der Text darin 130px - und
   "Geschenkewuensche" braucht 163px. Es lief heraus. Deutsch ist voll von
   solchen Woertern: Ansichtsauswahl, Familienprojekte, Stundenplan.

   `hyphens: auto` trennt sauber (das `lang="de"` steht in base.html),
   `overflow-wrap` ist der Notnagel fuer den Fall, dass kein Trennpunkt passt.
   Zusammen statt einzeln: Ohne den Notnagel laeuft ein Wort ohne moegliche
   Trennstelle weiterhin heraus. */
/* **Die ganze Kachel ist klickbar** (11.10.2026, auf Wunsch).

   **Der Verweis im Titel waechst ueber die Kachel**, statt dass ein zweiter
   um sie herumgelegt wird: Ein Vorleseprogramm haette dann denselben Weg
   zweimal, und ein `<div>` mit Klickbehandler waere ein Bedienelement, das
   ohne Skript nichts tut (Regel 7).

   **`.kachel` traegt dafuer schon `position: relative`** - die Zeile steht
   seit jeher darunter, und genau deshalb geht das hier ohne eine neue.

   **Der Umriss bleibt am Titel.** Das ist der Preis dieser Bauart: Wer mit
   der Tastatur ankommt, sieht den Fokus am Wort, nicht an der Kachel. Die
   Alternative waere ein `:focus-within` an der Kachel - *und dann saehe ein
   Klick ins Leere aus wie ein gesetzter Fokus.*

   **Was spaeter in eine Kachel kommt, liegt darunter.** Ein zweiter Knopf
   in einer Kachel waere von dieser Flaeche verdeckt; wer einen einbaut,
   gibt ihm `position: relative` und ist damit wieder obenauf. */
.kachel h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

/* **Die Zahl des Moduls** (11.10.2026, auf Wunsch). Sie steht unter dem
   Satz, nicht darueber: *Erst was es ist, dann wie viel davon* - und eine
   Kachel ohne Zahl sieht damit nicht aus, als fehle ihr etwas.

   **`--akzent-dunkel` und nicht `--akzent`**: Auf der hellen Kachelflaeche
   faellt der helle Akzent auf 3,16 bis 4,26 (gemessen am Pergament der
   Familienregeln, 08.10.2026) - *„leicht farbig" darf nicht heissen
   „nicht lesbar"*. */
.kachelzahl {
  margin: 4px 0 0;
  color: var(--akzent-dunkel);
  font-weight: 600;
}

.kachel {
  position: relative;
  padding: var(--abstand);
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: var(--radius-karte);
  hyphens: auto;
  overflow-wrap: break-word;
}

.kachel p { margin: 0; color: var(--text-leise); font-size: 0.92em; }

.kachel-geplant { opacity: 0.75; }

.marke-geplant {
  position: absolute;
  top: var(--abstand);
  right: var(--abstand);
  padding: 1px 8px;
  background: var(--akzent-blass);
  border-radius: 999px;
  color: var(--akzent-dunkel);
  font-size: 0.72em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* ===========================================================================
   7. Eingabeelemente - eine Hoehe, ein Rahmen
   =========================================================================== */

/* **`input[type="time"]` ist hier dazugekommen** (FAM-83), und die Karte hat
   es vorhergesagt: Es stand nicht in dieser Zeile - genauso wenig, wie
   `<textarea>` es tat, als es in FAM-57 gemessene **13,3 px Monospace neben
   24 px im Infoboard** hatte.

   **Das ist ein Verstoss gegen Regel 0**, auch ohne eine Zahl im Blatt: Ein
   Element, das `font: inherit` nie liest, ist fuer die Ansichtsauswahl
   unerreichbar - genauso fest verdrahtet wie eine hingeschriebene Pixelzahl,
   nur unsichtbar. Die Testsuite rendert keine Schriftgroessen; gefunden wird
   so etwas nur beim **Messen** im Ansichtsdurchgang. */
/* **Diese Zeile ist eine Positivliste, und sie ist viermal vergessen worden.**

   Ein Eingabeelement, das nicht daraufsteht, nimmt die Voreinstellung des
   Browsers - gemessen 13,3 px Monospace, waehrend im Infoboard alles andere
   24 px hat. Das ist ein Verstoss gegen Regel 0, auch ohne eine Zahl im
   Blatt: Was die Merkmalsvariable nie liest, ist fuer die Ansichtsauswahl
   unerreichbar.

   | Element | gefunden in | wie |
   |---|---|---|
   | `<textarea>` | FAM-57 | im Ansichtsdurchgang, 13,3 px neben 24 px |
   | `input[type="file"]` | FAM-64 | im Ansichtsdurchgang |
   | `input[type="time"]` | FAM-83 | **die Karte hat es vorhergesagt** |
   | `input[type="number"]` | FAM-85 | im Ansichtsdurchgang, 21 px statt 48 px |
   | `input[type="color"]` | 22.09.2026 | **die Wache hat es gefangen**, am Tag seines Entstehens |
   | `input[type="url"]` | 03.10.2026 | **die Wache hat es gefangen**, im Gesamtlauf vor dem Deploy |
   | `input[type="search"]` | 25.10.2026 | **die Wache hat es gefangen**, im Gesamtlauf vor dem Deploy - *zum siebten Mal dieselbe Liste* |

   **Seit FAM-85 wartet darauf eine Wache**
   (`test_jedes_eingabefeld_liest_die_schrift_der_ansicht`): Sie liest, welche
   `type="..."` die Masken wirklich benutzen, und verlangt fuer jedes eine
   Regel. Eine Liste, die jemand pflegen muss, wird irgendwann nicht
   gepflegt - eine, die sich beschwert, schon. */
/* **`button` steht hier seit dem 28.09.2026 nicht mehr.** Seit der
   Textmarker-Ebene gehoert ein Knopf ganz Abschnitt 15: Dort steht
   `all: unset` und danach `min-height: var(--knopf-flaeche)` - also der
   Boden von 44 px. **Diese Zeile haette ihm 36 gegeben**, und sie sah
   dabei aus, als gaelte sie. Gefunden hat es
   `test_die_trefferflaeche_wird_an_einer_stelle_gerechnet`. */
select, input[type="text"], input[type="password"], input[type="date"], input[type="time"], input[type="number"], input[type="email"], input[type="url"], input[type="search"] {
  /* **`min-height`, nicht `height`** (28.09.2026, auf Rueckmeldung: *„Text
     bei Abteilung wird beim Einkaufszettel abgeschnitten"*).

     `--schaltflaeche` ist die **Bedienflaeche**, also ein Boden - keine
     Decke. Mit einer festen Hoehe entscheidet die Summe aus Hoehe, Polster
     und Rahmen, wie viel fuer die Zeile bleibt, **und keine der beteiligten
     Regeln weiss von der anderen**: Die Eingabemaske (A6) legt `8px 0` und
     eine 1,5 px starke Unterlinie darauf, es bleiben **18,5 px** - Segoe
     Print braucht bei 16 px **20**. Gemessen `scrollHeight 38` gegen
     `clientHeight 35`, und an einem `<select>` setzt Chrome `overflow:
     clip`: Die Unterlaengen verschwinden, *„Getraenke" sieht aus wie
     „Getranke"*.

     **Wo der Inhalt hineinpasst, aendert sich nichts** - dann ist die
     Mindesthoehe die Hoehe. Die Zeile kann also nur dort wirken, wo heute
     geschnitten wird. */
  min-height: var(--schaltflaeche);
  padding: 0 var(--abstand);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  color: var(--text);
  font: inherit;
}

/* **Das Textfeld steht bewusst nicht in der Zeile darueber** (FAM-57): Dort
   gilt `height: var(--schaltflaeche)`, und die sechs Zeilen der Notiz waeren
   damit auf eine Schaltflaechenhoehe zusammengedrueckt. Es bekommt denselben
   Rahmen, aber eine eigene Hoehe.

   `font: inherit` ist der Grund, warum es hier ueberhaupt steht. Ohne das
   nimmt der Browser seine Voreinstellung - gemessen **13,3 px Monospace**,
   waehrend im Infoboard alles andere auf 24 px steht. Eine feste Groesse,
   an die die Ansichtsauswahl nicht herankommt (Design.md, Regel 0). Dieselbe
   Luecke hatte `input[type="date"]` seit FAM-53; es steht jetzt oben mit in
   der Zeile. */
textarea {
  padding: calc(var(--abstand) / 2) var(--abstand);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  color: var(--text);
  font: inherit;
  line-height: 1.5;
  /* Nur senkrecht - waagerecht wuerde die Maske breiter machen, und die
     Seite selbst scrollt nicht (Design.md, Regel 2). */
  resize: vertical;
}

/* **Das Dateifeld steht aus demselben Grund nicht in der Zeile oben** wie
   das Textfeld: Dort gilt `height: var(--schaltflaeche)`, und ein
   Dateifeld traegt den Knopf „Durchsuchen“ des Browsers **in sich** - eine
   feste Aussenhoehe drueckt ihn zusammen.

   `font: inherit` ist wieder der eigentliche Grund, warum es hier steht.
   FAM-57 hat diese Luecke am `<textarea>` gefunden (gemessen 13,3 px
   Monospace neben 24 px im Infoboard, `docs/11-fallstricke.md`), und sie
   waere hier genauso entstanden - unsichtbar fuer die Testsuite, denn die
   rendert keine Schriftgroessen.

   `max-width` haelt den Dateinamen, den der Browser hineinschreibt, davon
   ab, die Maske breiter zu machen als den Bildschirm (Regel 2). */
input[type="file"] {
  padding: calc(var(--abstand) / 2) 0;
  color: var(--text);
  font: inherit;
  max-width: 100%;
}

button { cursor: pointer; }
button:hover { background: var(--akzent-blass); }

button.betont {
  background: var(--akzent);
  border-color: var(--akzent);
  color: var(--flaeche);
}

:focus-visible {
  outline: 2px solid var(--akzent-dunkel);
  outline-offset: 1px;
}

/* ===========================================================================
   8. Masken, Listen und Schaltflaechen (FAM-31)
   ===========================================================================
   Alles ueber Merkmalsvariablen - damit die vier Ansichten auch hier greifen
   (Design.md, Regel 0). Keine feste Zahl, wo es eine Variable gibt. */

/* Die Werkzeugleiste: Verweise sehen aus wie Schaltflaechen und sind
   genauso gross, damit die Leiste nicht springt. */
.schaltflaeche {
  display: inline-flex;
  align-items: center;
  height: var(--schaltflaeche);
  padding: 0 var(--abstand);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  color: var(--text);
  text-decoration: none;
  white-space: nowrap;
}
.schaltflaeche:hover { background: var(--akzent-blass); color: var(--akzent-dunkel); }

.schaltflaeche.betont,
button.betont {
  background: var(--akzent);
  border-color: var(--akzent);
  color: var(--flaeche);
}
.schaltflaeche.betont:hover,
button.betont:hover { background: var(--akzent-dunkel); color: var(--flaeche); }

/* **Ein Knopf, der nur sein Zeichen traegt** (FAM-109, erweitert am
   18.09.2026). Er ist quadratisch, damit er neben einem Feld nicht die halbe
   Zeile nimmt - der ganze Zweck der Aenderung war, Platz zu sparen.

   **Die Flaeche bleibt eine Bedienflaeche**: Hoehe UND Breite sind
   `--schaltflaeche`, keine ausgedachte Zahl daneben - **auch nicht die 4 px
   weniger, die ein gewoehnlicher Knopf seit der Frischen Ebene hat.** Dort
   sind sie der Rand um ein Wort; hier ist die Flaeche das Ziel des Daumens,
   und es gibt nichts, was sie kleiner machen duerfte.

   Das Zeichen selbst liest `--schrift-gross`, denn bei 12 px Grundschrift
   waere ein Haken nicht mehr zu erkennen - und ein Symbol, das man sucht,
   ist keines.

   **Das Wort steht in `.nurvorlesen`, nicht in `.knopfwort`** - siehe
   `knopfzeichen` in `app/templates/_symbole.html`. */
button.drauf,
button.zeichenknopf {
  width: var(--knopf-flaeche);
  height: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  flex: 0 0 auto;
  font-size: var(--schrift-gross);
}

/* **Symbol und Wort** (FAM-109, `app/templates/_symbole.html`).

   Das Symbol traegt `1em` und `currentColor` - es waechst und faerbt sich
   mit seinem Knopf, in allen vier Ansichten. Eine feste Pixelzahl waere hier
   derselbe Fehler wie eine feste Menuebreite (Regel 0). */
.symbol {
  width: 1.25em;
  height: 1.25em;
  flex: 0 0 auto;
}

/* **Am Handy bleibt nur das Symbol** - so ausdruecklich gewuenscht.

   `display: none` nimmt das Wort auch aus dem Vorlesebaum, und ein Knopf
   ohne Namen sagt einem Vorleseprogramm nur „Schaltflaeche“. Deshalb traegt
   **jeder** dieser Knoepfe zusaetzlich ein `aria-label` mit demselben Wort -
   das ueberlebt das Ausblenden. Bewacht von
   `test_ein_knopf_mit_symbol_hat_einen_namen`. */
.knopfwort { display: var(--knopfwort-anzeige); }

/* **Das Geraetewort** (22.09.2026, auf Wunsch): *„auf dem Handy oder auf dem
   Tablet installieren, je nachdem, auf was fuer einem Geraet man gerade
   ist."*

   **Es steht im Blatt und nicht im Server**, und das ist Regel 0: Im
   Normalfall entscheiden die Haltepunkte, welche Ansicht gilt - der Server
   sieht kein Cookie, solange niemand etwas gewaehlt hat. Ein Blick in den
   User-Agent kennt ausserdem die **Ansichtsauswahl** nicht, und *wer am
   Desktop „Handy“ waehlt, bekaeme trotzdem „diesem Computer“.*

   **Ein Wort im Dokument, nicht drei.** Der naheliegende Weg waeren drei
   `<span>`, von denen das Blatt zwei ausblendet - das ist genau der zweite
   ausgeblendete Block, den FAM-84 verboten hat. Mit `content` steht es
   **einmal** da, und die Variable waehlt es. */
.geraetewort::after { content: var(--geraetewort); }

/* **Der Menuepunkt traegt sein Wort im Dokument** (`Installieren`) und das
   Geraet als Zugabe. Der erste Bau setzte ein `<span>` **in** das
   `.menuewort` - die Wache aus FAM-109 hat es gefangen, und sie hatte recht:
   *Ein Vorleseprogramm liest verlaesslich das Dokument, nicht jedes
   generierte `content`.* **„Installieren“ allein ist verstaendlich**, das
   Geraet ist die Zugabe (FAM-65). */
.geraetepunkt::after { content: " auf " var(--geraetewort); }

/* **Der QR-Code zum Anmeldelink** (22.09.2026, auf Wunsch).

   **Die Groesse steht hier und nicht im Bild**: `app/qr.py` gibt ein SVG
   ohne `width` und `height` aus, nur mit `viewBox`. *Eine Pixelzahl dort
   waere fuer die Ansichtsauswahl unerreichbar* (Regel 0) - und am Infoboard
   soll er groesser sein, damit man ihn aus zwei Metern abfilmen kann.
   Gemessen sind 4,15 / 4,88 / 4,39 / 7,02 px je Modul.

   **Die Farbe kommt aus `currentColor`**, also aus dem Blatt (Regel 10) -
   sonst staende sie im Markup, und bei einer dunklen Farbrichtung waere der
   Code schwarz auf dunkel.

   **`--flaeche` als Grund ist kein Luxus**: Ein QR-Code braucht hellen
   Untergrund, und der Hinweiskasten darum ist eingefaerbt. *Ein Code auf
   getoenter Flaeche wird von aelteren Kameras schlechter gelesen, und das
   faellt demjenigen auf, der ihn scannen will.* */
.qrblock { margin-top: var(--abstand); }

.qrcode {
  margin: 0;
  display: inline-block;
  text-align: center;
}

.qrbild {
  width: var(--qrcode-breite);
  max-width: 100%;
  padding: calc(var(--abstand) / 2);
  border-radius: var(--radius);
  background: var(--flaeche);
  color: var(--text);
}

.qrbild svg { display: block; width: 100%; height: auto; }

.qrcode figcaption {
  margin-top: calc(var(--abstand) / 2);
  color: var(--text-leise);
  font-size: 0.85em;
}

/* **Der Titel der Maske in der Kopfzeile** (FAM-109): „Family Board - Neuer
   Termin". Er darf schrumpfen und abgeschnitten werden, die Marke und der
   Menueknopf nicht - sonst waere bei einem langen Titel der EINZIGE Weg ins
   Menue weg (die Frage aus FAM-50/FAM-84: gibt es ein Bedienelement, das
   nirgends erreichbar ist). */
.seitentitel {
  min-width: 0;
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* **`font-size` und `font-weight` standen hier und sind weggefallen**
   (29.09.2026). Diese Regel stammt aus der Zeit, als der Titel **in der
   Kopfzeile** stand; seit A4 steht er im Seitenkopf, und die Regel dort
   setzt beides neu. Gemessen war das Ergebnis richtig – *die beiden Zeilen
   haben nichts getan.*

   **Weg sind sie trotzdem**, und das ist die Lehre desselben Tages: Eine
   Deklaration, die nur deshalb nicht wirkt, weil eine spaetere sie schlaegt,
   kommt zurueck, sobald jemand die spaetere umbaut. *Das Gewicht des
   Seitentitels steht jetzt an einer Stelle.* */
/* **Der Gedankenstrich ist weggefallen** (20.09.2026). Er verband die Marke
   mit dem Titel - *„Family Board – Neuer Termin“* -, und seit der Titel in
   der Mitte allein steht, verbindet er nichts mehr: Er haenge dann vor einer
   Ueberschrift, die fuer sich steht.

   Er stand ohnehin im Blatt und nicht im Text (FAM-109), damit ein
   Vorleseprogramm „Neuer Termin“ sagt und nicht „Strich“ - und genau deshalb
   kostet sein Wegfall hier eine Zeile und keine Maske. */

/* Symbol und Wort brauchen einen Abstand - aber nur, solange beide da sind. */
button .knopfwort,
.schaltflaeche .knopfwort { margin-left: 0.5em; }

/* **Nur-Symbol-Knoepfe werden quadratisch**, sonst bliebe die Breite eines
   Wortes stehen, das niemand sieht - und der ganze Zweck war, Platz zu
   sparen. */
.werkzeugleiste button:has(> .symbol),
.werkzeugleiste .schaltflaeche:has(> .symbol) { padding: 0 var(--abstand); }

/* --- Der Abo-Knopf (FAM-116) ---------------------------------------------

   **Zwei Knoepfe, die zusammengehoeren** - „Diese Liste“ und „Alle Listen“.
   `.abogruppe` haelt sie beieinander und laesst sie am Handy umbrechen, wie
   die Leiste selbst (FAM-84). */
.abogruppe {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: calc(var(--abstand) / 2);
}

/* **Der Zustand steht als Wort da, nicht als Farbe** (FAM-65): Wer
   rot-gruen-blind ist, sieht eine Farbe nicht, und am Infoboard aus zwei
   Metern verschwimmt sie. Er darf deshalb zurueckhaltend aussehen, aber er
   muss lesbar sein - `--schrift`, nicht kleiner. */
.abozustand {
  color: var(--text-leise);
  white-space: nowrap;
  margin-right: calc(var(--abstand) / 2);
}

/* Trennt zusammengehoerende Gruppen in der Leiste (Design.md, Regel 1). */
.trenner {
  width: 1px;
  height: calc(var(--schaltflaeche) - 10px);
  margin: 0 4px;
  background: var(--linie);
}

/* Breite Inhalte scrollen in ihrem eigenen Kasten, nicht durch Verbreitern
   der Seite (Design.md, Regel 2). */
.tabelle-umbruch { overflow-x: auto; }

.liste { width: 100%; border-collapse: collapse; }
.liste th, .liste td {
  padding: 10px var(--abstand) 10px 0;
  border-bottom: 1px solid var(--linie);
  text-align: left;
  vertical-align: middle;
}
.liste th { color: var(--text-leise); font-size: 0.88em; font-weight: 600; }

.leise { color: var(--text-leise); }

/* --- Die Aufgabenliste (FAM-53, umgebaut in FAM-50) ----------------------
   **Sie war eine Tabelle, und am Handy war sie unbedienbar.** Gemessen beim
   Durchgang durch die vier Ansichten: 428px Inhalt in 343px Kasten, vom
   Knopf `abhaken` blieben **9 von 78 Pixeln** stehen. Der Rest lag im
   waagerechten Scrollbereich - also hinter einer Geste, die am Handy
   niemand an einer Liste erwartet.

   Eine Tabelle kann das nicht loesen: Ihre Zeile ist unteilbar, sie wird
   breiter statt umzubrechen. Deshalb eine **Liste**, deren Zeile umbricht.
   Fachlich ist das ohnehin richtiger - es sind Aufgaben, keine Messwerte
   zum Spaltenvergleich.

   Gesteuert ueber zwei Merkmalsvariablen, nicht ueber `@media` (Regel 0):
   Wer am Desktop *Handy* waehlt, bekommt die Handy-Anordnung.

   | | schmal | breit |
   |---|---|---|
   | `--aufgabe-umbruch`      | `wrap`  | `nowrap` |
   | `--aufgabe-titel-breite` | `100%`  | `auto`   |

   Am Handy nimmt der Titel damit die ganze Zeile, und Name, Datum und
   Knoepfe stehen darunter - **alles im Sichtfeld, ohne Seitwaertsscrollen**
   (Regel 2 gilt weiter: Die Seite selbst scrollt nicht). */
.aufgabenliste { list-style: none; margin: 0; padding: 0; }

.aufgabe {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  padding: 10px 0;
  border-bottom: 1px solid var(--linie);
}

/* **Keine Spaltenueberschriften** - und der Weg dahin gehoert dazu.

   Erst stand hier eine Kopfzeile `Was | Wer | Bis wann`, wie die Tabelle
   sie geschenkt bekam. Ohne Tabelle muss man sie ausrichten, und das kostete
   zwei Anlaeufe: eine feste Knopfspalte (sonst steht `Wer` ueber dem Datum,
   gemessen 44px daneben) und den Verzicht auf die kleinere Schrift (sonst
   sind die `ch`-Breiten der Kopfzeile enger, 26px daneben). Das Ergebnis
   fluchtete - und presste den Titel am Tablet auf fuenf Zeilen.

   Eine Ueberschrift, die so viel kostet, ist den Platz nicht wert: `alle`
   und `13.09.2026` erklaeren sich selbst. Das Datum traegt zusaetzlich ein
   `title`, damit `faellig` und `erledigt` unterscheidbar bleiben. */

/* Der Titel: nimmt breit den Restplatz, schmal die ganze Zeile. Die
   Mindestbreite haelt ihn davon ab, auf das laengste Wort zu schrumpfen,
   sobald die Knoepfe daneben Platz verlangen. */
.aufgabe-was {
  flex: 1 1 var(--aufgabe-titel-breite);
  min-width: 12ch;
  hyphens: auto;
  overflow-wrap: break-word;
}

/* Name und Termin: **schrumpfbar, aber nicht verschwindend** (`0 1`, nicht
   `0 0`).

   Mit `0 0` standen sie unverrueckbar breit - und als FAM-58 den Stern
   davorsetzte, passte die Zeile am **Infoboard** nicht mehr: Der Inhalt
   scrollte seitwaerts, und vier `loeschen`-Knoepfe lagen ausserhalb des
   Bildes. Bei 24px Schrift ist `11ch` eben doppelt so breit wie bei 12px.

   Ohne `min-width` schrumpfen sie hoechstens auf ihren Inhalt (`min-content`)
   - sie werden also eng, aber nie null. Das ist der Unterschied zur Falle aus
   FAM-56, wo ein ausdrueckliches `min-width: 0` ein Feld auf null Pixel
   gehen liess. */
.aufgabe-wer  { flex: 0 1 11ch; }
.aufgabe-wann { flex: 0 1 10ch; }

/* Die Knoepfe stehen immer rechts - auch, wenn sie umgebrochen sind.

   **Dieselbe Variable wie die Zeile**, und das ist keine Sparsamkeit: Sie
   beantwortet dieselbe Frage. Wo die Zeile umbrechen darf, duerfen es auch
   die Knoepfe; wo sie es nicht darf, sollen sie nebeneinander bleiben und
   der Titel nachgeben. Mit festem `wrap` standen sie am Tablet
   uebereinander und machten die Zeile 123px hoch, obwohl daneben Platz war. */
.aufgabe-aktionen {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  gap: 6px;
  margin-left: auto;
  justify-content: flex-end;
  /* Inhaltsbreit. Eine feste Breite stand hier, solange es eine Kopfzeile
     zum Ausrichten gab - sie nahm dem Titel zu viel Platz. */
  flex: 0 0 auto;
}
/* Die Formulare drumherum sind reine Huellen - ohne das brechen sie die
   Reihe auf, weil ein <form> ein Blockelement ist. */
.aufgabe-aktionen form { display: contents; }

/* Abgehakt: durchgestrichen und zurueckgenommen - erkennbar, aber nicht im
   Weg. Die Zeile bleibt lesbar, damit man findet, was man versehentlich
   abgehakt hat. */
.aufgabenliste .erledigt {
  text-decoration: line-through;
  color: var(--text-leise);
}

/* --- Die Schritte einer Aufgabe (FAM-56) --------------------------------- */

/* Der Stand in der Liste - „2 von 5“. Klein und leise: Er sagt etwas ueber
   den Weg, nicht ueber die Aufgabe selbst. */
.schrittstand {
  margin-left: 0.6em;
  color: var(--text-leise);
  font-size: 0.85em;
  white-space: nowrap;
}

/* **Der Block „Am Sticker Board“ teilt sich die Regel mit den Schritten**
   (FAM-170) - er steht in derselben Maske, traegt dieselbe Ueberschrift und
   soll genauso abgesetzt sein. **Eine dritte wortgleiche Regel waere die
   dritte Kopie**: `.notizvorschau` sagt weiter unten schon einmal dasselbe.

   > **Und das ist ein Befund, kein Lob**: Die beiden stehen seit FAM-57 und
   > FAM-56 doppelt da. Zusammengelegt sind sie hier nicht - *eine sichtbare
   > Aenderung an einer fremden Maske gehoert in ihre eigene Karte*
   > (FAM-108, FAM-136). Die dritte kommt wenigstens nicht dazu. */
.schritte, .boardblock { margin-top: calc(var(--abstand) * 1.5); }
.schritte h2, .boardblock h2 {
  margin: 0 0 var(--abstand);
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-leise);
}
.schritte h2 .leise { text-transform: none; letter-spacing: 0; }

.schrittliste { list-style: none; margin: 0; padding: 0; }

/* Eine Zeile je Schritt: Knopf, Text, Weg. **Dieselbe Umbruchregel wie die
   Aufgabenliste** (`--aufgabe-umbruch`, FAM-50) - am Handy bricht sie um,
   statt breiter zu werden als der Bildschirm. Eine zweite Variable waere
   eine zweite Antwort auf dieselbe Frage. */
.schritt {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}
.schritt form { display: flex; gap: 6px; align-items: center; }

/* Die Knoepfe eines Schrittes bleiben **zusammen** und rechts - wie in der
   Aufgabenliste. Ohne die Klammer verteilt der Umbruch sie einzeln, und der
   Abhaken-Knopf landet neben dem falschen Schritt. */
.schritt-aktionen {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  gap: 6px;
  margin-left: auto;
}

/* Das Textfeld nimmt den Rest - schmal die ganze Zeile.

   **`min-width` ist hier nicht Kosmetik, sondern der Unterschied zwischen
   bedienbar und unsichtbar.** Zuerst stand am Eingabefeld `min-width: 0`;
   damit darf ein Flex-Element beliebig schrumpfen, und am Handy blieb neben
   dem Knopf `uebernehmen` **null Pixel** fuer das Feld uebrig. Gemessen beim
   Durchgang: `textfeld_breite: 0`. Auf dem Bildschirm sah die Zeile dabei
   nicht kaputt aus - da stand einfach kein Feld.

   Dazu darf die Zeile umbrechen (dieselbe Variable wie ueberall im Modul):
   Wo es eng wird, rutscht der Knopf unter das Feld, statt es zu erdruecken. */
.schritt-text {
  flex: 1 1 var(--aufgabe-titel-breite);
  flex-wrap: var(--aufgabe-umbruch);
  min-width: 12ch;
}
.schritt-text input { flex: 1 1 10ch; min-width: 10ch; }
.schritt-text input.erledigt {
  text-decoration: line-through;
  color: var(--text-leise);
}

.schritt-neu {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  margin-top: var(--abstand);
}
.schritt-neu input { flex: 1 1 var(--aufgabe-titel-breite); min-width: 12ch; }

/* Der erledigte Block ist abgesetzt, nicht versteckt - und seit FAM-65
   zuklappbar, ohne eine Zeile JavaScript (`<details>`). */
.erledigte { margin-top: calc(var(--abstand) * 1.5); }
.erledigte h2 {
  /* `display: inline` - sonst rutscht die Ueberschrift unter das
     Aufklappdreieck, das der Browser vor das `<summary>` setzt. */
  display: inline;
  margin: 0;
  /* **Farbe, Grossbuchstaben und Sperrung sind weg** (27.09.2026): Sie
     machten aus der Ueberschrift ein Label. Groesse und Schrift kommen aus
     Abschnitt 17. */
}
.erledigte summary {
  /* Der ganze Kopf ist die Schaltflaeche - am Handy soll man nicht das
     Dreieck treffen muessen (Bedienflaeche, Design.md Regel 10). */
  padding: 6px 0;
  cursor: pointer;
  min-height: var(--schaltflaeche);
  display: flex;
  align-items: center;
}
.erledigte summary:hover h2 { color: var(--text); }
.erledigte[open] summary { margin-bottom: var(--abstand); }

/* **Das Aufklappzeichen setzt der Block selbst - und es wechselt** (▾/▴).

   Die Zeile darueber (`display: flex`) loescht den `::marker` des Browsers -
   genau die Falle, an der in FAM-70 ein ganzer Bedienweg unauffindbar war,
   und seitdem zum fuenften Mal (FAM-136, FAM-146, das Menue am 21.09.2026).

   **Sie stand zwei Karten lang nur hinter `.leihgruppe`** (FAM-136), und der
   Kommentar dort sagte woertlich: *„Der Erledigt-Block der Aufgaben liest
   dieselbe Regel und hat damit vermutlich dasselbe Problem - Messen liess er
   sich hier nicht: In der Entwicklungsdatenbank gibt es keine erledigte
   Aufgabe."* **Er hatte es**, gemeldet am 23.09.2026 mit drei erledigten
   Aufgaben auf dem Bildschirm und ohne ein Zeichen daran.

   *Eine Regel hinter einer fremden Klasse hilft der naechsten Maske nicht*
   (FAM-85, FAM-108) - sie steht deshalb hier und gilt fuer beide Bloecke.

   **Das Zeichen WECHSELT, es dreht sich nicht** - derselbe Grundsatz wie
   beim Haken, bei der Sonne (FAM-60) und beim ueberfaelligen Termin
   (FAM-65): Eine Fassung, in der nur die Neigung wechselt, sieht bei
   abgeschalteter Bewegung gleich aus.

   **Und es steht direkt da, nicht als CSS-Escape** - `\25be` ist auf dem Weg
   durch eine Shell schon zweimal zu einem Oktalescape geworden (FAM-109,
   FAM-136). *Ein Escape, das man nicht braucht, kann auch nicht
   kaputtgehen.* */
.erledigte > summary { list-style: none; }
.erledigte > summary::-webkit-details-marker { display: none; }
.erledigte > summary::after {
  content: "▾";
  margin-left: auto;
  color: var(--text-leise);
}
.erledigte[open] > summary::after { content: "▴"; }

/* **Das Archiv** (23.09.2026) - derselbe Block, eine Stufe leiser.

   Es benutzt `.erledigte` mit und holt sich damit Abstand, Kopf, Zahl und
   das Aufklappzeichen aus **einer** Stelle; hier steht nur, was anders ist.

   **Der Abstand ist groesser**, und das ist die einzige Aussage dieser
   Regel: Zwischen *gerade fertig* und *zu den Akten* liegt mehr als zwischen
   zwei Zeilen desselben Blocks. */
.erledigte.archiv { margin-top: calc(var(--abstand) * 2.5); }

/* **Die Sammelknoepfe eines Blocks.**

   Sie stehen unter der Liste und nicht in der Werkzeugleiste - Regel 1 nennt
   „eingebettete Bloecke mit eigenen Aktionen“ ausdruecklich als Ausnahme,
   und ein *Erledigte loeschen* oben haette einen Bezug, den man nicht sieht.

   **`flex-wrap: wrap`** - zwei Knoepfe passen am Handy (375 px) nebeneinander,
   ein dritter braeuchte die zweite Zeile. *Eine Leiste, die eine Aktion
   abschneidet, ist keine Leiste* (FAM-84).

   **Keine eigene Hoehe**: Die Knoepfe tragen `--schaltflaeche` aus
   Abschnitt 7 und Abschnitt 11 - eine Zahl daneben waere die zweite, und sie
   liefe auseinander (FAM-62, FAM-109). */
.blockaktionen {
  display: flex;
  flex-wrap: wrap;
  gap: var(--abstand);
  margin-top: var(--abstand);
}

/* Die Anzahl - **gezaehlt, nicht geschaetzt** (Regel 5). */
.erledigte .anzahl {
  margin-left: 0.5em;
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--akzent-blass);
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* --- Die Notiz (FAM-57) -------------------------------------------------- */

/* Das Zeichen in der Liste - leise, wie der Schrittstand daneben. Es sagt
   nur, DASS es eine Notiz gibt. */
.notizzeichen {
  margin-left: 0.5em;
  color: var(--text-leise);
  font-size: 0.95em;
}

/* Die Vorschau in der Maske: **wie sie aussieht, wenn sie fertig ist.**
   Abgesetzt wie der erledigte Block, damit klar ist, dass hier nichts mehr
   zu tippen ist. */
.notizvorschau { margin-top: calc(var(--abstand) * 1.5); }
.notizvorschau h2 {
  margin: 0 0 var(--abstand);
  color: var(--text-leise);
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.notiztext {
  padding: var(--abstand);
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
}
/* Die Tags, die `app/auszeichnung.py` erzeugt - und nur die. */
.notiztext p:first-child { margin-top: 0; }
.notiztext p:last-child { margin-bottom: 0; }
.notiztext ul { margin: 0.5em 0; padding-left: 1.4em; }
.notiztext li { margin: 0.2em 0; }

/* --- Der Stern (FAM-58) -------------------------------------------------- */

/* **Nur fuer die Vorlesehilfe.** Ein Knopf, auf dem nur ein Zeichen steht,
   ist fuer ein Vorleseprogramm stumm - „Stern“ sagt nicht, was passiert.
   `display: none` ginge nicht: Was so versteckt ist, liest auch die
   Vorlesehilfe nicht mehr. Deshalb ein Pixel abseits statt weg. */
.nurvorlesen {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Der Knopf traegt die Bedienflaeche der Ansicht. Ein Stern, den man dreimal
   antippen muss, ist schlimmer als keiner.

   **Gemessen am 27.09.2026: 36 px am Handy** - und damit unter den 44, die
   Abschnitt 15 jedem anderen Knopf gibt. *Der Satz darueber stand hier seit
   dem ersten Tag und nannte 48 px; die Frische Ebene hat `--schaltflaeche`
   am Handy laengst auf 36 gesetzt* (Regel 10). **Eine Begruendung kann
   ablaufen, ohne falsch auszusehen.**

   Gerechnet wird deshalb dieselbe Untergrenze wie im Grundstil.

   > **Hier stand, die Sonne daneben brauche keine eigene Zeile: *„Sie hat nie
   > eine gehabt."* Das war falsch** (05.10.2026), und der Satz ist der Grund,
   > warum es acht Tage so blieb: `.tagesknopf` setzt seine Maße **selbst**
   > (Abschnitt *Mein Tag*), und zwar aus `--schaltflaeche`. Gemessen am
   > Desktop **36 × 44** - die Höhe trug der Stern daneben, die Breite nicht.
   > *Eine Behauptung über eine Nachbarregel ist keine Messung.* */
.sternform { display: flex; flex: 0 0 auto; }
button.stern {
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  border-color: transparent;
  background: none;
  color: var(--text-leise);
  font-size: 1.3em;
  line-height: 1;
  /* **Leicht, damit das gesetzte Zeichen schwer sein kann** (21.10.2026).
     Gemessen stand hier **700** (aus dem Knopfgrundstil), gesetzt 800 -
     *ein Unterschied von hundert ist auf einem Zeichen nicht zu sehen*,
     und damit traege die Form nichts bei: Es bliebe allein die Farbe, und
     die sieht ein rot-gruen-blinder Mensch nicht (FAM-65). Jetzt sind es
     400 gegen 800. */
  font-weight: 400;
}
button.stern:hover { border-color: var(--linie); color: var(--text); }

/* **Gesetzt: rot und fett** (21.10.2026, auf einen Kommentar im Canvas:
   *„Bitte anstatt einem Stern ein rotes Ausrufezeichen"*).

   **Hier stand `--warm`**, der zweite Akzent aus dem Entwurf, mit der
   Begruendung *„der Stern ist ein Hinweis, keine Aktion"*. Der Wunsch sagt
   **rot**, und das ist eine Entscheidung des Nutzers.

   > **Der Preis steht dabei**: Rot trug in diesem Blatt bisher *Gefahr*
   > (`.gefahr`, das Loeschen) und *ueberfaellig* - und *zwei Bedeutungen
   > fuer eine Farbe kosten bei jeder Karte Zeit* (Design 2a B1). Hier
   > traegt es jetzt eine dritte, und sie ist der zweiten nah: **beide
   > sagen „das draengt".**

   **Fett ist das zweite Merkmal, und es ist keine Zugabe.** Der Stern
   unterschied sich in der **Form** (gefuellt gegen hohl); ein
   Ausrufezeichen hat nur eine. Bliebe allein die Farbe, saehe ein
   rot-gruen-blinder Mensch keinen Unterschied (FAM-65) - woertlich die
   Begruendung des ueberfaelligen Termins zwei Regeln weiter unten: *Auch
   in Graustufen hebt sich die Zeile ab.* */
button.stern.gesetzt {
  color: var(--fehler);
  font-weight: 800;
}
button.stern.gesetzt:hover { color: var(--fehler); border-color: var(--fehler); }

/* Ein ueberfaelliger Termin (FAM-65).

   **Die Farbe ist der Zusatz, nicht die Aussage.** Der Text sagt es schon
   („seit gestern“, „seit 12.09.“), denn wer rot-gruen-blind ist, sieht das
   Rot nicht - und am Infoboard aus zwei Metern verschwimmt es ohnehin.
   Deshalb zusaetzlich fett: Auch in Graustufen hebt sich die Zeile ab. */
.aufgabe-wann.ueberfaellig {
  color: var(--fehler);
  font-weight: 600;
}

.maske {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  /* **Die Lesebreite ist eine Ansichtsentscheidung, keine feste Zahl.**
     Hier stand `46ch`. `ch` haengt an der Schriftgroesse - mit
     `--schrift: 12px` (FAM-109, zweiter Durchgang) waren das nur noch
     **298 px auf einem 412 px breiten Handy**, ein Fuenftel der Breite lag
     ungenutzt daneben.

     Das ist der Fund von FAM-108 eine Ebene hoeher: Dort fuellte ein **Feld**
     seine Maske nicht aus, hier fuellt die **Maske** ihren Platz nicht aus.
     **Sichtbar ist davon nichts** - eine etwas schmalere Maske sieht nach
     Absicht aus.

     Am Handy gilt deshalb `100%`, sonst `46ch`: Eine Zeile, die ueber 46
     Zeichen laeuft, liest sich schlecht - aber auf 412 px kommt sie gar
     nicht erst so weit. Entschieden im BLATT ueber eine Merkmalsvariable
     (Regel 0), damit es auch greift, wenn jemand am Desktop *Handy* waehlt. */
  max-width: var(--maske-breite);
  margin-top: var(--abstand);
}
/* **Ein einzelnes Feld in einer Maske** (FAM-108).

   Bis dahin hatte `.feld` **gar keine Regel**, und das war kein Versehen mit
   einer falschen Zahl, sondern eines mit einer **fehlenden**: `input` und
   `textarea` nahmen die Voreinstellung des Browsers (`size`, `cols`).
   Gemessen am Handy: Die Maske ist **343 px** breit, das Textfeld war
   **226 px**, das mehrzeilige **194 px** - rund zwanzig Zeichen je Zeile,
   bei 149 px ungenutztem Platz daneben.

   **Sichtbar ist davon nichts.** Ein etwas schmaleres Feld sieht nach Absicht
   aus, und die Testsuite rendert keine Pixel - dieselbe Familie wie die
   vergessene Selektorzeile mit `font: inherit` (FAM-57, FAM-64, FAM-83,
   FAM-85) und wie `width` statt `flex-basis` (FAM-64).

   **Die Regel steht hier und nicht in einer Maske.** Seit FAM-99 stand sie an
   `.umfragemaske`; damit haette die naechste Maske sie wieder vergessen -
   dieselbe Ueberlegung wie bei `_scoped_query` und `darf()`.

   **`box-sizing` steht nicht dabei**, und das ist kein Vergessen: `*` traegt
   es seit dem ersten Tag (Abschnitt 4). Ein zweites Mal hier waere eine
   zweite Stelle fuer dieselbe Zusage. */
/* **Am Handy steht die Beschriftung NEBEN dem Feld, sonst darueber**
   (FAM-109).

   Gemessen war die Terminmaske am Handy **1657 px hoch bei 692 px Platz** -
   2,4 Bildschirme, auf einem davon anderthalb Felder. Die Vorlage
   (`docs/Design/vorlagen/handy-terminmaske-vorbild.jpg`) zeigt elf.

   **Der Unterschied ist die Beschriftungszeile.** Sie kostet je Feld 22 px
   und sagt dasselbe, was auch neben dem Feld stehen kann. Am Desktop bleibt
   sie oben: Dort ist Platz, und eine Zeile mit Beschriftung und Wert
   nebeneinander liest sich schlechter.

   **Was NICHT schrumpft, ist die Bedienflaeche.** `--schaltflaeche` bleibt
   48 px am Handy - genau der Fehler aus FAM-62, wo ein selbst ausgedachtes
   `* 0.85` daraus 41 px machte und *auf einem Bild 41 px neben 48 px gleich
   aussehen*. Kleiner wird der Abstand, die Zeile, der Rahmen; nicht die
   Flaeche, die ein Daumen treffen muss.

   **`div.feldgruppe` ist dabei dasselbe wie `.feld`** - ein Wrapper um EIN
   Feld. `fieldset.feldgruppe` ist etwas anderes: ein Kasten um eine GRUPPE,
   und der bleibt untereinander. Damit ist die Doppeldeutigkeit benannt, die
   FAM-108 gefunden hat. */
.maske .feld,
div.feldgruppe {
  display: flex;
  flex-direction: var(--feld-richtung);
  /* Der Hinweis soll in der Zeile UMBRECHEN, nicht eine dritte Spalte
     werden - siehe unten. */
  flex-wrap: wrap;
  gap: 5px var(--abstand);
}

/* **Die Beschriftung bleibt ein `<label>`** - sichtbar, nicht als
   Platzhalter. Ein Platzhalter verschwindet beim Tippen, und wer dann nicht
   mehr weiss, was er ausfuellt, hat keine zweite Chance. */
.maske .feld > label,
div.feldgruppe > label {
  display: flex;
  align-items: center;
  flex: 0 0 var(--feld-beschriftung-breite);
  min-width: 0;
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* **Alles, was eine Eingabe ist**, nicht nur `text` und `textarea` (FAM-108).
   Der erste Anlauf zaehlte zwei Typen auf - und waere damit die naechste
   Positivliste geworden, die jemand pflegen muss. Ein Ankreuzfeld ist
   ausgenommen: Es traegt keinen Text, und 343 px breit waere es kein
   Kaestchen mehr.

   **`flex: 1 1 auto` traegt beide Richtungen**: In der Zeile nimmt das Feld,
   was die Beschriftung uebrig laesst; untereinander hat der Kasten keinen
   freien Platz zu verteilen, und `width: 100%` entscheidet. */
.maske .feld > input:not([type="checkbox"]):not([type="radio"]),
.maske .feld > select,
.maske .feld > textarea,
div.feldgruppe > input:not([type="checkbox"]):not([type="radio"]),
div.feldgruppe > select,
div.feldgruppe > textarea {
  /* **`--feld-eingabe-basis` ist der Wert, ohne den die Zeile umbricht.**
     `width: 100%` setzt in einer Zeile die Flex-Basis - 137 px Beschriftung
     plus 343 px Feld passen nicht nebeneinander, und `flex-wrap: wrap`
     bricht dann, statt schrumpfen zu lassen.

     Untereinander muss die Basis `auto` bleiben: Mit `0` kaeme die Hoehe
     nicht mehr aus `height: var(--schaltflaeche)`, und das Feld waere null
     hoch. */
  flex: 1 1 var(--feld-eingabe-basis);
  min-width: 0;
  width: 100%;
}

/* **Ein mehrzeiliges Feld bleibt unter seiner Beschriftung** (FAM-109).

   Es ist die eine Ausnahme von der Zeile, und sie ist gemessen: Neben einer
   Beschriftung von 137 px bleiben dem Feld 190 statt 343. In ein Feld, in
   das man sechs Antworten oder eine Wegbeschreibung tippt, gehoert die
   Breite - genau das war der Fund von FAM-108.

   **`:has()` statt einer Klasse im Markup.** Eine Klasse, die jemand setzen
   muss, wird beim naechsten Textfeld vergessen - dieselbe Positivlisten-Falle
   wie bei `font: inherit` (FAM-57, FAM-64, FAM-83, FAM-85). Ein Browser ohne
   `:has()` bekommt die Zeile: enger, aber nicht kaputt. */
.maske .feld:has(textarea),
div.feldgruppe:has(textarea) { flex-direction: column; }
.maske .feld:has(textarea) > label,
div.feldgruppe:has(textarea) > label { flex-basis: auto; }

/* **Und das Feld selbst braucht seine Basis zurueck.**

   `--feld-eingabe-basis` ist am Handy `0` - in einer Zeile ist das die
   Breite, in einer Spalte die **Hoehe**. Hier steht das Feld in einer Spalte,
   und mit Basis 0 war es gemessen **48 px hoch statt 171**: fuenf Zeilen
   Notiz auf eine Knopfhoehe zusammengedrueckt.

   **Keine Testsuite haette das gefunden** - sie rendert keine Pixel. Gesehen
   hat es der Ansichtsdurchgang, und zwar nur, weil die Blockhoehe GEMESSEN
   wurde: 48 px neben 48 px sehen auf einem Bild richtig aus. */
.maske .feld > textarea,
div.feldgruppe > textarea { flex-basis: auto; }

.feldgruppe { display: flex; flex-direction: column; gap: 5px; }
.feldgruppe label { font-weight: 600; }
.feldhinweis { margin: 2px 0 0; color: var(--text-leise); font-size: 0.88em; }

/* **Die Ankreuzliste** (FAM-63) - wer macht die Aufgabe, und es duerfen
   mehrere sein.

   **Die Zeile ist die Bedienflaeche, nicht das Kaestchen.** Ein Ankreuzfeld
   ist rund 13 px gross; wer es treffen muss, trifft daneben. Das `<label>`
   umschliesst beides, also zaehlt seine Hoehe - und die kommt aus
   `--schaltflaeche`, nicht aus einer ausgedachten Zahl. Ein selbst
   erfundener Bruchteil einer gestalteten Zahl ist eine erfundene Zahl
   (die Lehre aus FAM-62).

   Das Ankreuzfeld selbst steht bewusst NICHT in der Selektorzeile mit
   `font: inherit`: Es traegt keinen Text, und `height: var(--schaltflaeche)`
   machte daraus ein Quadrat von 48 px. */
.ankreuzliste {
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  padding: 0 var(--abstand) var(--abstand);
  margin: 0;
  display: flex;
  flex-direction: column;
}

.ankreuzliste legend {
  font-weight: 600;
  padding: 0 4px;
}

.ankreuzzeile {
  display: flex;
  align-items: center;
  gap: var(--abstand);
  min-height: var(--schaltflaeche);
  cursor: pointer;
  /* Der Name soll nicht fett sein - `label` ist es in `.feldgruppe`. */
  font-weight: 400;
}

.ankreuzzeile input[type="checkbox"] {
  /* Gross genug, um es zu sehen; getroffen wird ohnehin die ganze Zeile. */
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
}

.typenliste {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: var(--abstand) 0;
  padding: 0;
  list-style: none;
}
.typenliste li {
  padding: 6px 14px;
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: 999px;
}

.befehl {
  display: inline-block;
  margin: 0;
  padding: 10px 14px;
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
  font-size: 0.92em;
}

/* ===========================================================================
   8b. Der Mensch als runder Kreis (24.10.2026, auf Wunsch)
   ===========================================================================

   *„Es wird als kleiner runder Kreis designt, egal ob Bild oder
   Initialien."*

   **Das aeussere Element traegt die Form, der Inhalt wechselt** - woertlich
   der Wunsch. Zwei Regeln mit derselben Rundung (eine fuer das Bild, eine
   fuer die Buchstaben) liefen auseinander, sobald jemand eine davon
   anfasst.

   **Keine Zeile in Regel 10, und das ist gemessen:** Der Kreis ist `2em`
   gross, waechst also mit `--schrift` seiner Umgebung - 24 px am Handy,
   34 am Tablet, 32 am Desktop, 48 am Infoboard. Eine Merkmalsvariable
   braeuchte vier Zahlen fuer dieselbe Rechnung, und am Infoboard rechnet
   `--schrift-voll` ohnehin noch einmal herunter (18.10.2026). */
.avatar {
  /* **Er schrumpft nicht mit** - in einer engen Zeile ist ein zur Ellipse
     gequetschter Kopf schlimmer als ein Umbruch. */
  flex: 0 0 auto;
  inline-size: 2em;
  block-size: 2em;
  border-radius: 50%;
  /* **Rund wird das Bild hier, nicht am Bild** - ein `border-radius` am
     `<img>` waere die zweite Stelle, an der die Form steht. */
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* **Ohne Farbton grau**, nicht bunt: *Eine erfundene Farbe sieht aus wie
     eine gewaehlte* (Regel 5). `--text-leise` liegt bei 48 % - weisse
     Schrift darauf ist gemessen lesbar. */
  background: var(--text-leise);
  color: var(--flaeche);
  /* Ein Kreis traegt keinen Text, der umbrechen koennte. */
  white-space: nowrap;
  /* **Die Schrift kommt nicht aus der Handschrift**: *Segoe Print* setzt
     zwei Grossbuchstaben deutlich breiter (`ch` ist dort 1,1 je Zeichen,
     FAM-232), und in einem Kreis ist die Breite das Knappe. */
  font-family: var(--schrift-seitentitel);
}

/* **Die Farbe des Menschen** (FAM-162) - dieselbe Rezeptur wie ueberall,
   und das ist der Punkt: *Mia ist in der ganzen Anwendung dieselbe.*

   **Und weisse Schrift darauf ist gemessen lesbar** (24.10.2026, im
   Browser gegen die echten sRGB-Pixel gerechnet, Gegenprobe Schwarz auf
   Weiss = 21,0): ueber alle sechs Toene **5,10 bis 5,82 : 1** - also ueber
   den 4,5 von WCAG AA, auch fuer normalen Text.

   > **Das korrigiert eine Annahme von FAM-162**, die zwei Abschnitte weiter
   > oben steht: *„Eine Personenfarbe liegt bei 52 % - darauf ist kein Text
   > lesbar, weder dunkler noch heller."* **Fuer dunklen Text stimmt das**
   > (`--text` liegt bei 21 %), fuer **weissen** nicht. Der Streifen am
   > Termin bleibt trotzdem richtig: Dort steht der Text **neben** der
   > Farbe, nicht darauf, und er soll `--text` sein. */
.avatar.gefaerbt {
  background: oklch(var(--person-helligkeit) var(--person-buntheit) var(--person-ton));
}

.avatarbild {
  inline-size: 100%;
  block-size: 100%;
  /* **Ein Portrait ist selten quadratisch**, und ein verzerrtes Gesicht ist
     schlimmer als ein beschnittenes (FAM-201). */
  object-fit: cover;
  /* Ohne `block` bleibt unter dem Bild die Grundlinie als heller Spalt. */
  display: block;
}

.avatarwort {
  /* **Kleiner als der Kreis**, sonst stossen die Buchstaben an den Rand -
     gemessen passen zwei Grossbuchstaben bei 0.8em in 2em. */
  font-size: 0.8em;
  font-weight: 700;
  /* Sonst macht die Zeilenhoehe den Kreis hoeher, als er breit ist. */
  line-height: 1;
}

/* ===========================================================================
   8c. Den Ausschnitt eines Portraits waehlen (FAM-268, 28.10.2026)
   ===========================================================================

   *„das Bild auszuwaehlen und dann mit einem Tool … in die richtige Groesse
   und Position zu bringen."*

   **Die Flaeche ist quadratisch, die Maske darin rund** - und beides zeigt
   dasselbe, was spaeter im Kreis steht (`.avatar`, Abschnitt 8b). *Eine
   Vorschau, die anders beschneidet als das Ergebnis, ist keine Vorschau.*

   **Eine Zahl statt vier, und das ist eine Entscheidung** (Regel 0): Wie
   gross die Flaeche sein muss, haengt nicht an der Ansicht, sondern am
   **Finger** - ein Gesicht trifft man ab etwa einer Handflaeche, und
   breiter als sein Kasten darf sie nirgends werden. `min(100%, 280px)`
   sagt genau das; vier Werte waeren vier Zahlen fuer dieselbe Rechnung
   (woertlich die Begruendung des Kreises daneben und der Infoboard-Skizze).

   **Alles darin kommt vom Skript** (`bildausschnitt.js`, Regel 7): Ohne es
   bleibt `.zuschnittflaeche` leer, und das Formular traegt trotzdem - der
   Server nimmt dann die Mitte. */
.zuschnittflaeche:empty {
  /* **Ein leerer Kasten zieht sonst trotzdem seinen Abstand** - die Falle
     steht im Index (17.10.2026). Ohne Skript gibt es hier nichts, also
     auch keine Luecke. */
  display: none;
}

.zuschnittbuehne {
  position: relative;
  inline-size: min(100%, 280px);
  /* **Quadratisch ueber `aspect-ratio`**, nicht ueber eine zweite Zahl:
     *Zwei Angaben fuer dieselbe Kante laufen auseinander*, sobald jemand
     eine davon anfasst. */
  aspect-ratio: 1;
  margin-block: var(--abstand);
  overflow: hidden;
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche-gedaempft, var(--flaeche));
  /* **Der Finger schiebt das Bild, nicht die Seite** - dieselbe Zeile und
     derselbe Grund wie am Griff des Stickers (23.10.2026). */
  touch-action: none;
  cursor: grab;
}

.zuschnittbuehne.zieht {
  cursor: grabbing;
}

.zuschnittbild {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  /* **Die Groesse setzt das Skript** - sie haengt an der Bildkante und am
     Regler, und beides kennt das Blatt nicht. */
  transform-origin: 0 0;
  /* Ohne `block` bleibt unter dem Bild die Grundlinie als heller Spalt. */
  display: block;
  /* **Kein eingebautes Ziehen**: Sonst nimmt der Browser das Bild mit,
     statt es zu verschieben. */
  -webkit-user-drag: none;
  user-select: none;
}

/* **Der Kreis liegt ueber dem Bild, nicht um es herum.** Ein runder
   Ausschnitt mit `border-radius` am Kasten wuerde das Bild selbst
   beschneiden - *und dann sieht man nicht mehr, was gleich wegfaellt.*
   Hier bleibt alles sichtbar, und der Rand daneben liegt unter einem
   Schleier. */
.zuschnittmaske-kreis {
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: var(--radius);
  /* **Zwei Verlaeufe in einem**: innen durchsichtig, aussen gedaempft.
     `radial-gradient` mit harter Kante - *ein weicher Rand saehe aus wie
     ein Effekt, nicht wie eine Grenze.* */
  background: radial-gradient(
    circle at center,
    transparent 0,
    transparent 49.6%,
    oklch(27% 0.02 170 / 0.45) 50%
  );
}

.zuschnittzeile {
  display: flex;
  align-items: center;
  gap: var(--abstand);
  max-inline-size: min(100%, 280px);
}

.zuschnittbeschriftung {
  color: var(--text-leise);
  white-space: nowrap;
}

.zuschnittregler {
  flex: 1 1 auto;
  min-inline-size: 0;
  /* **44 px sind der Boden, nicht die Decke** (Abschnitt 15): Ein Regler,
     den man nicht trifft, ist kein Regler. */
  min-block-size: var(--knopf-flaeche);
}

/* **Das Wegnehmen steht fuer sich**, nicht in der Zeile mit dem Speichern:
   *Ein Loeschknopf neben dem Bestaetigen ist ein Loeschknopf, den jemand
   trifft.* */
.bildweg {
  margin-block-start: var(--abstand);
}

/* ===========================================================================
   9. Anmeldung (FAM-32)
   =========================================================================== */

/* **Der Abmeldeblock steht seit FAM-107 im Menue**, und seine Regeln stehen
   dort, wo das Menue steht (Abschnitt 4).

   Hier stand bis dahin eine waagerechte Zeile mit `nowrap` und einer
   Kuerzung per Ellipse - beides Notbehelfe fuer eine **Kopfzeile**, in der
   Name und Knopf neben der Ansichtsauswahl um den Platz rangen. In der Tafel
   ist Platz; der Name darf umbrechen und braucht keine Kurzform.

   **Sie sind hier stehengeblieben und haben gewonnen** - im Blatt zaehlt die
   Reihenfolge, und diese Zeilen kamen spaeter. Gefunden beim Messen: Die
   Ueberschrift ueber dem Abmelden war 177 px breit, die daneben 357 px. Auf
   einem Bild sieht das nach Absicht aus. */
.abmelden .wer { font-weight: 600; }

/* Die Anmeldeseite: eine zentrierte Karte, kein Menue, keine Werkzeugleiste.
   Sie erbt nicht von base.html - dort setzt alles eine Anmeldung voraus. */
.anmeldeseite {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100%;
  padding: var(--abstand);
  overflow: auto;              /* hier darf die Seite scrollen: kein Rahmen */
}

.anmeldekarte {
  width: 100%;
  max-width: 380px;
  padding: calc(var(--abstand) * 1.5);
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: var(--radius-karte);
}
.anmeldekarte h1 { margin: 0 0 var(--abstand); font-size: var(--schrift-gross); }
.anmeldekarte .maske { max-width: none; }
.anmeldekarte button { width: 100%; justify-content: center; }
.anmeldekarte .meldungsleiste { padding: 0 0 var(--abstand); }
.anmeldekarte .fussnote { padding: var(--abstand) 0 0; text-align: center; }

/* Die Familie ueber dem Login (FAM-66). Sie steht nur da, wenn sie ueber die
   Kennung ausdruecklich gewaehlt wurde - der Name ist die Antwort auf "bin
   ich hier richtig?" und deshalb das Auffaellige an der Zeile, nicht die
   Einleitung davor. */
.anmeldefamilie {
  margin: calc(var(--abstand) * -0.5) 0 var(--abstand);
  color: var(--text-leise);
  font-size: 0.92em;
}
.anmeldefamilie strong {
  display: block;
  margin-top: 2px;
  color: var(--text);
  font-size: 1.15em;
}

/* ===========================================================================
   10. Zugang (FAM-35)
   =========================================================================== */

/* Derselbe Kasten, nur in der Fehlerfarbe - fuer den Hinweis, der eine
   schwer umkehrbare Handlung begleitet. Kein eigener Bautyp: Ein zweiter
   Kasten mit eigenen Massen liefe beim naechsten Mal auseinander. */
.hinweiskasten.warnung {
  background: var(--fehler-blass);
  border-left-color: var(--fehler);
}

/* Der eigene Name fuehrt zum eigenen Passwort (FAM-35). Er sieht aus wie
   Text, nicht wie ein Menuepunkt - er ist auch keiner.

   **`var(--text)`, nicht `inherit`** (FAM-107): Solange er in der gruenen
   Kopfzeile stand, war `inherit` genau richtig. In der weissen Tafel erbte
   er damit Weiss auf Weiss. */
.abmelden a.wer { color: var(--text); text-decoration: none; }
.abmelden a.wer:hover { text-decoration: underline; }

/* --- Die Aufgabenlisten (FAM-62) ----------------------------------------- */

/* Die Leiste ueber der Aufgabenliste. **Alles ueber Merkmalsvariablen** -
   die Reiter sind auf dem Infoboard gross und am Handy fingerbreit, ohne
   dass hier eine Zahl steht (Design.md, Regel 0).

   `flex-wrap`: Sechs Listen passen am Handy nicht in eine Zeile. Sie sollen
   umbrechen und nicht in einen Seitwaerts-Scrollbereich rutschen - genau die
   Falle aus FAM-50, wo der Knopf `abhaken` dort landete. */
.listenleiste {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: var(--abstand);
  padding-bottom: var(--abstand);
  border-bottom: 1px solid var(--linie);
}

.listenreiter {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  /* **Die volle Bedienflaeche, kein selbst ausgedachter Bruchteil.**
     Hier stand `calc(var(--schaltflaeche) * 0.85)` - mit der Begruendung, die
     Leiste solle die Liste nicht nach unten druecken. Gemessen waren das am
     Handy **41 px statt 48**, und 48 steht in Regel 10, weil eine Fingerkuppe
     breiter ist als ein Mauszeiger. Der Gewinn waeren 7 px je Zeile gewesen,
     der Preis ein Ziel, das man verfehlt. Der Faktor war eine erfundene Zahl
     (Regel 5, sinngemaess) gegen eine gestaltete. */
  min-height: var(--schaltflaeche);
  padding: 0 var(--abstand);
  border: 1px solid var(--linie);
  border-radius: 999px;
  background: var(--flaeche);
  color: var(--text-leise);
  text-decoration: none;
}
.listenreiter:hover { background: var(--akzent-blass); color: var(--text); }

/* **Die aktive Liste ist nicht nur farbig markiert**, sondern auch fett und
   mit kraeftigem Rand. Dieselbe Ueberlegung wie beim ueberfaelligen Termin
   (FAM-65): Wer rot-gruen-blind ist, sieht die Farbe nicht, und am Infoboard
   aus zwei Metern verschwimmt sie. Zusaetzlich traegt der Reiter
   `aria-current` - fuer ein Vorleseprogramm ist das die eigentliche Auskunft. */
.listenreiter.aktiv {
  border-color: var(--akzent);
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-weight: 600;
}

.listenreiter .anzahl {
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--akzent-blass);
  color: var(--text);
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
}
.listenreiter.aktiv .anzahl { background: var(--flaeche); }

/* Das Anlegen auf der Verwaltungsseite: Feld und Knopf in einer Zeile.

   `min-width: 10ch` am Feld ist die Lehre aus FAM-56: Ein Flex-Element mit
   `min-width: 0` faellt am Handy auf null Pixel zusammen, und das Feld ist
   dann unsichtbar da. Es darf schrumpfen, aber nicht verschwinden. */
.listenneu {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: var(--abstand) 0;
}
.listenneu label { font-weight: 600; }
.listenneu input[type="text"] { flex: 1 1 10ch; min-width: 10ch; }

.listenverwaltung { margin: var(--abstand) 0; padding: 0; list-style: none; }

/* Wie die Aufgabenzeile gebaut: **Text links, Knoepfe rechts**, und der
   Umbruch entscheidet sich ueber dieselben Merkmalsvariablen. */
.listenzeile {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 8px var(--abstand);
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}

.listenname {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  flex: 1 1 var(--aufgabe-titel-breite);
}
.listenname input[type="text"] { flex: 1 1 10ch; min-width: 10ch; }

/* `flex: 0 1 …` und nicht `0 0 …` - die Lehre aus FAM-58: Eine feste Breite
   schob am Infoboard die Knoepfe aus dem Bild. Schrumpfen auf den Inhalt ja,
   auf null nein. */
.listenstand { flex: 0 1 14ch; color: var(--text-leise); }

.listenaktionen {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

/* --- Die Wiederholung (FAM-59) ------------------------------------------- */

/* Das Zeichen in der Liste - leise, wie der Stift und der Schrittstand
   daneben. Es sagt nur, DASS die Aufgabe wiederkommt; wie oft, steht im
   `title` und in der Maske. */
.wiederholungszeichen {
  margin-left: 0.5em;
  color: var(--text-leise);
  font-size: 0.95em;
}

/* Einheit und Intervall nebeneinander.

   `flex-wrap` und `min-width` am Zahlenfeld sind die Lehre aus FAM-56: Ein
   Flex-Element ohne Mindestbreite faellt am Handy auf null Pixel zusammen und
   ist dann unsichtbar da. Es darf schrumpfen, nicht verschwinden. */
.wiederholungsfelder {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.wiederholungsfelder select { flex: 1 1 14ch; min-width: 14ch; }
.wiederholungsabstand { color: var(--text-leise); }

/* **Volle Bedienflaeche, kein ausgedachter Bruchteil** - die Lehre aus
   FAM-62, wo `* 0.85` am Handy 41 px statt 48 ergab. */
.wiederholungsfelder input[type="number"] {
  height: var(--schaltflaeche);
  width: 7ch;
  min-width: 7ch;
  padding: 0 calc(var(--abstand) / 2);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  color: var(--text);
  /* Ohne das nimmt der Browser seine Voreinstellung - gemessen 13,3 px
     Monospace, waehrend im Infoboard alles andere auf 24 px steht (FAM-57,
     `docs/11-fallstricke.md`). Ein neues Eingabeelement erbt die Schrift
     nicht von selbst. */
  font: inherit;
}

/* --- Liste links, Aufgabe rechts (FAM-61) -------------------------------- */

/* **Kein `@media` hier** (Regel 0). Ob es eine oder zwei Spalten gibt und ob
   die Liste ueberhaupt erscheint, steht in zwei Merkmalsvariablen, die jede
   der vier Ansichten setzt:

   | Merkmal                  | Handy  | sonst     |
   |--------------------------|--------|-----------|
   | `--detail-spalten`       | `1fr`  | `1fr 1fr` |
   | `--detail-listenanzeige` | `none` | `block`   |

   Am Handy bleibt damit genau das uebrig, was es vor dieser Karte gab: die
   Maske allein. Eine zweite Spalte neben 375 px waere keine.

   **Und es greift auch, wenn jemand am Desktop `Handy` auswaehlt** - genau
   dafuer sind es Variablen und keine Haltepunkte. */
.arbeitsflaeche {
  display: grid;
  grid-template-columns: var(--detail-spalten);
  gap: var(--abstand);
  /* Ohne `start` zoege die kuerzere Spalte sich auf die Hoehe der laengeren
     auf, und ihr Hintergrund endete weit unter ihrem Inhalt. */
  align-items: start;
}

/* `min-width: 0` an beiden Spalten: Ein Grid-Element hat `min-width: auto`
   und wird damit **mindestens so breit wie sein laengster Inhalt** - eine
   lange Aufgabenzeile schoebe die Spalte auf und die Seite ins
   Seitwaerts-Scrollen (Regel 2). Hier ist `0` richtig, anders als bei den
   Flex-Feldern in FAM-56: Dort ist `0` die Falle, weil ein Eingabefeld
   verschwinden kann; eine Spalte kann das nicht, sie hat Inhalt. */
/* **Wo es nur eine Spalte gibt, fällt die andere weg – und welche, sagt die
   Seite** (28.09.2026, auf Wunsch: *„Man muss eine Aufgabe anklicken, damit
   die Felder angezeigt werden."*).

   | Seite | `data-haupt` | was bleibt |
   |---|---|---|
   | `/todos` | `liste` | die Liste |
   | `/todos/<n>`, `/todos/neu` | `detail` | die Maske |

   **Eine Variable, eine Frage.** `--detail-listenanzeige` beantwortet *„gibt
   es Platz für die zweite Spalte?"*; welche das ist, entscheidet das Markup.
   Zwei Variablen – eine je Spalte – könnten sich widersprechen, *und dann
   stünde entweder nichts da oder beides zu eng.*

   > **Der Name der Variablen trägt seitdem eine Bedeutung mehr**, und das
   > gehört gesagt: Sie hieß nach der **Listenspalte**, weil es bis heute nur
   > die eine Möglichkeit gab, wegzufallen. Umbenannt ist sie nicht –
   > *ein Name, der in 42 Zeilen steht, wiegt schwerer als ein genauerer.* */
.arbeitsflaeche[data-haupt="detail"] > .listenspalte,
.arbeitsflaeche[data-haupt="liste"] > .detailspalte {
  display: var(--detail-listenanzeige);
}

.listenspalte {
  min-width: 0;

  /* **Die Liste NEBEN der Maske ist nie so breit wie die Liste allein**
     (28.09.2026, auf Rueckmeldung: *„Hier wird ueber die Zellen
     geschrieben."*).

     `--aufgabe-umbruch: nowrap` ist eine Entscheidung fuer die **ganze
     Seite** (FAM-58): Dort hat eine Zeile am Desktop rund 1060 px. Hier
     bekommt sie die **Haelfte** - und `loeschen` stand bis zu **186 px**
     ueber dem Rand, **mitten in der Maske daneben**.

     **Gemessen in allen drei Ansichten mit zwei Spalten**, und keine hat
     gereicht:

     | | Spalte | die Zeile braucht |
     |---|---|---|
     | Tablet | 216 px | 652 |
     | Desktop | 463 px | 649 |
     | Infoboard | 544 px | 932 |

     **Also ist es keine Frage der Ansicht, sondern eine der Spalte** - und
     deshalb steht die Antwort hier und nicht in den vier Wertesaetzen
     (Regel 10 meint Werte je *Ansicht*; dies ist ein *Zusammenhang*).

     **Eine Variable, nicht zwei**, und das ist gemessen: Am Handy steht
     neben `wrap` auch `--aufgabe-titel-breite: 100%` - der Titel bekommt
     dort eine eigene Zeile. **Hier kostet das 30 px je Zeile und bringt
     nichts**:

     | | mit `100%` | wie es ist (`auto`) |
     |---|---|---|
     | Desktop, eine Zeile | 174 px | **144** |
     | Desktop, vier Zeilen | 696 px | **576** |
     | Tablet / Infoboard | 242 / 266 | **gleich** |

     *In den beiden schmalen Ansichten faellt der Titel ohnehin auf eine
     eigene Zeile; am Desktop nicht - und dort ist Platz fuer Name und Datum
     daneben.* Woertlich Regel 5: **Was nichts aendert, wird nicht
     festgelegt.**

     **Und es ist dieselbe Variable, die auch die Knoepfe umbrechen laesst**
     (siehe `.aufgabe-aktionen`) - *wo die Zeile umbrechen darf, duerfen es
     auch sie.* Eine eigene Regel daneben waere die zweite Stelle, an der
     ueber denselben Umbruch entschieden wird.

     > **Warum kein `overflow: hidden`**: Das versteckte die Knoepfe, statt
     > sie hinzustellen - *ein Bedienelement, das man nicht trifft, ist
     > keines* (Regel 7). Und warum kein `@container`: Es gibt schon eine
     > Variable fuer genau diese Frage. */
  --aufgabe-umbruch: wrap;
}
.detailspalte { min-width: 0; }

/* **Die Liste und das erste Feld fangen auf derselben Hoehe an**
   (28.09.2026, auf Wunsch: *„Die Aufgabe soll in der gleichen y-Koordinate
   anfangen wie die erste Box `Was ist zu tun?`."*).

   **Gemessen waren es 20 px Versatz** - `.maske` traegt einen oberen Abstand
   (`margin-top: var(--abstand)`), den sie dort braucht, wo sie **allein**
   unter dem Seitenkopf steht. **Hier steht sie in einem Raster, und das hat
   seinen Abstand schon** (`gap`).

   *Der Abstand war nicht falsch, er war doppelt* - und er stand nur auf
   einer der beiden Spalten. */
.arbeitsflaeche .maske { margin-top: 0; }

/* Die Liste daneben ist die Nebensache - der Rahmen setzt sie ab, ohne sie
   in den Vordergrund zu holen. */
.listenspalte {
  padding-right: var(--abstand);
  border-right: 1px solid var(--linie);
}

/* **Die aufgeschlagene Aufgabe ist markiert** - und nicht nur farbig.
   Wer rot-gruen-blind ist, sieht die Farbe nicht; der Strich links sieht
   jeder (dieselbe Ueberlegung wie beim ueberfaelligen Termin, FAM-65). */
.aufgabe.geoeffnet {
  border-left: 3px solid var(--akzent);
  padding-left: calc(var(--abstand) / 2);
  background: var(--akzent-blass);
}

/* --- Mein Tag (FAM-60) ---------------------------------------------------- */

/* Das Kalenderblatt steht neben dem Stern - gleiche Groesse, gleiche
   Bedienflaeche. **Kein ausgedachter Bruchteil** (die Lehre aus FAM-62:
   `* 0.85` ergab 41 px statt 48).

   **Und die Untergrenze ist `--knopf-flaeche`, nicht `--schaltflaeche`**
   (05.10.2026): *44 sind der Boden*, und `--schaltflaeche` ist am Handy und
   am Desktop 36 (Regel 10). Gemessen vorher **36 × 44** - der Stern daneben
   hat die Hoehe getragen, die Breite nicht.

   > **Der Durchgang vom 27.09.2026 hat ihn uebersehen**, obwohl er
   > *„kleinste Trefferflaeche 44/48/44/72"* gemessen hat: Er hat die
   > Knoepfe **mit der Klasse `.schaltflaeche`** gemessen, und dieser traegt
   > sie nicht. *Eine Messung gilt fuer das, was gemessen wurde.*

   **`min-height` statt `height`**: Eine feste Hoehe entscheidet nicht, wie
   viel fuer die Zeile bleibt (`11-fallstricke.md`). */
.tagesform { flex: 0 0 auto; }

.tagesknopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: none;
  color: var(--text-leise);
  font-size: 1.2em;
  line-height: 1;
}
.tagesknopf:hover { background: var(--akzent-blass); color: var(--text); }

/* **Gesetzt ist nicht nur farbig.** Wer rot-gruen-blind ist, sieht die Farbe
   nicht; am Infoboard aus zwei Metern verschwimmt sie. Das Zeichen selbst
   wechselt deshalb mit - **seit dem 04.10.2026 vom leeren Kalenderblatt zum
   Blatt mit dem Punkt** (`kalenderblatt` zu `kalenderblatt-voll`) -, und der
   Rahmen kommt dazu: dieselbe Ueberlegung wie beim ueberfaelligen Termin
   (FAM-65) und beim Stern.

   **Hier stand `(☼ zu ☀)`, und das war die Mechanik bis dahin**: zwei
   Unicode-Zeichen in der Vorlage. *Eine Begruendung kann ablaufen, ohne
   falsch auszusehen* - gefunden hat es nicht das Nachdenken, sondern eine
   Messung am Abzug, in dem dieses Blatt eingebettet war. */
.tagesknopf.gesetzt {
  border-color: var(--akzent);
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
}


/* --- Die Anhaenge (FAM-64) ----------------------------------------------- */

/* Das Zeichen in der Liste - leise, wie der Stift und der Schrittstand
   daneben. Es sagt, DASS und WIE VIELE, nie WAS. */
.anhangzeichen {
  margin-left: 0.5em;
  color: var(--text-leise);
  font-size: 0.95em;
}

/* Der Block in der Maske - aufgebaut wie die Schritte darueber, damit man
   nicht zweimal lernen muss, wie eine Untergruppe aussieht. */
/* --- Dazu gesagt (FAM-273, 06.11.2026) -----------------------------------

   *„Reden am einzelnen Termin"* - der Block unter der Terminmaske.

   **Er sieht aus wie der Dateiblock daneben** (`.anhaenge` darunter): Beide
   stehen unter derselben Maske, und *zwei Abschnitte, die verschieden
   aussehen, sehen aus wie zwei verschiedene Sachen.* Deshalb teilen sie den
   Kopf, den Abstand und die Trennlinie - **keine neue Zahl und keine neue
   Farbe** (Regel 10).

   **Keine Sprechblasen wie im Chat.** Dort ist der Verlauf die ganze Maske,
   hier ist es ein Block unter einem Formular: Blasen brauchen Breite, und
   die Maske ist `--maske-breite` schmal. *Dasselbe Gespraech, eine andere
   Umgebung.* */
.gesagt { margin-top: calc(var(--abstand) * 1.5); }
.gesagt h2 {
  margin: 0 0 var(--abstand);
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-leise);
}
.gesagt h2 .leise { text-transform: none; letter-spacing: 0; }

.gesagtliste { list-style: none; margin: 0; padding: 0; }

.gesagtzeile {
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}

/* **Der Kopf einer Zeile**: wer, wann - und rechts das Wegnehmen.

   `flex-wrap` nach derselben Variablen wie im ganzen Modul
   (`--aufgabe-umbruch`, FAM-50): Am Handy bricht die Zeile um, statt breiter
   zu werden als der Bildschirm - **und zwar dann, wenn die Ansichtsauswahl
   es sagt** (Regel 0). */
.gesagtkopf {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  margin: 0;
}

.gesagtwer { font-weight: 600; }

.gesagtwann {
  font-size: 0.85em;
  color: var(--text-leise);
}

/* **Das Wegnehmen rutscht nach rechts** - `margin-inline-start: auto` und
   nicht `justify-content: space-between`: Bricht die Zeile um, stuende der
   Knopf sonst allein in einer Zeile links. */
.gesagtweg { display: flex; margin-inline-start: auto; }

.gesagttext {
  margin: 2px 0 0;
  overflow-wrap: anywhere;
}

/* **Das Feld nimmt die Zeile, der Knopf steht darunter** - die Maske ist
   schmal, und ein Knopf neben einem zweizeiligen Feld laesst beide
   schrumpfen. */
.gesagtneu {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  margin-top: var(--abstand);
}
.gesagtneu textarea { width: 100%; }

.anhaenge { margin-top: calc(var(--abstand) * 1.5); }
.anhaenge h2 {
  margin: 0 0 var(--abstand);
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-leise);
}
.anhaenge h2 .leise { text-transform: none; letter-spacing: 0; }

.anhangliste { list-style: none; margin: 0; padding: 0; }

/* **Dieselbe Umbruchregel wie ueberall im Modul** (`--aufgabe-umbruch`,
   FAM-50): Am Handy bricht die Zeile um, statt breiter zu werden als der
   Bildschirm - und zwar dann, wenn die Ansichtsauswahl es sagt, nicht wenn
   die Bildschirmbreite es sagt. */
.anhang {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}

/* Der Link auf die Datei nimmt den Rest der Zeile. `min-width` aus
   demselben Grund wie beim Schritt-Textfeld: Ohne ihn darf ein
   Flex-Element auf null schrumpfen, und am Handy blieb dort einmal
   wirklich nichts uebrig (FAM-56). */
.anhang-name {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) / 2);
  flex: 1 1 var(--aufgabe-titel-breite);
  min-width: 12ch;
  color: var(--text);
  text-decoration: none;
}
.anhang-name:hover { text-decoration: underline; }

/* Die Vorschau ist das Bild selbst, klein. **Quadratisch beschnitten**
   (`object-fit: cover`), sonst bestimmt das Seitenverhaeltnis des Fotos die
   Zeilenhoehe, und eine Liste aus Hoch- und Querformaten wird zum Zickzack.

   Die Groesse haengt an `--schaltflaeche`: **die Merkmalsvariable, nicht ein
   Bruchteil davon.** FAM-62 hat hier `* 0.85` stehen gehabt und damit am
   Handy 41 px statt der gestalteten 48 erzeugt - eine erfundene Zahl, die
   wie eine gestaltete aussah (`docs/11-fallstricke.md`). */
.anhang-vorschau {
  /* **`flex-shrink: 0` ist hier kein Feinschliff.** Ohne das darf ein
     Flex-Element unter seine `width` schrumpfen, und im Infoboard ist es
     das auch: gemessen **66 statt 72 Pixel** neben einem langen Dateinamen
     bei 24 px Schrift.

     Auf dem Bildschirm faellt es nicht auf - `object-fit: cover` beschneidet
     das Foto, statt es zu verzerren, und ein etwas schmaleres Vorschaubild
     sieht nach Absicht aus. Gefunden hat es nur die Messung im Durchgang
     durch die vier Ansichten (FAM-64). */
  flex: 0 0 auto;
  width: var(--schaltflaeche);
  height: var(--schaltflaeche);
  object-fit: cover;
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
}

/* Fuer alles, was kein Bild ist: ein Kaestchen in derselben Groesse, damit
   die Zeilen nicht springen. */
.anhang-zeichen {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Dasselbe wie oben, und hier waere es schlimmer: Gemessen **44 statt 72
     Pixel**, und in ein 44 px breites Kaestchen passt „PDF“ bei 24 px
     Schrift nicht - der Text waere beschnitten. */
  flex: 0 0 auto;
  width: var(--schaltflaeche);
  height: var(--schaltflaeche);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  color: var(--text-leise);
  font-size: 0.75em;
  letter-spacing: 0.04em;
}

/* Der Name darf umbrechen - ein Dateiname kann lang sein, und `nowrap`
   machte die Maske breiter als den Bildschirm (Regel 2). `min-width: 0`
   ist hier richtig und im Gegensatz zum Elternteil nicht gefaehrlich: Es
   erlaubt dem Text ueberhaupt erst, umzubrechen statt seine Zeile zu
   dehnen. */
.anhang-text {
  min-width: 0;
  overflow-wrap: anywhere;
}
.anhang-text .leise { margin-left: 0.5em; white-space: nowrap; }

.anhang-weg { margin-left: auto; }

.anhang-neu {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  margin-top: var(--abstand);
}
/* **`min-width` ist hier der Unterschied zwischen bedienbar und
   unsichtbar** - genau die Falle aus FAM-56. Ohne ihn darf ein Flex-Element
   beliebig schrumpfen, und im Infoboard auf einem schmalen Fenster blieb
   fuer das Feld **null Pixel** uebrig (gemessen: `dateifeld_breite: 0`).

   Auf dem Bildschirm sieht die Zeile dabei nicht kaputt aus - da steht
   einfach kein Feld. Dass die Zeile umbrechen darf, faengt den Rest ab. */
.anhang-neu input[type="file"] {
  flex: 1 1 var(--aufgabe-titel-breite);
  min-width: 12ch;
}

/* Die Beschriftung soll nicht zu „Noch / eine / Datei“ zerfallen, aber auch
   nicht die Zeile sprengen: Sie bleibt in einer Zeile und darf dafuer
   schrumpfen, bis der Umbruch greift. */
.anhang-neu label { white-space: nowrap; }


/* --- Die Einkaufsliste (FAM-70) ----------------------------------------- */

/* **Das Eintragfeld steht oben und ist das Erste, was man sieht.**

   Bei den Aufgaben steht das Feld fuer einen neuen Schritt unter der Liste;
   hier ist es umgekehrt. Auf der Einkaufsliste ist Eintragen der haeufigste
   Vorgang, und die Liste waechst im Laden nach unten - wer erst scrollen
   muss, schreibt es lieber nicht auf. */
/* **Die Regel fuer `.artikel-neu` stand bis zum 28.09.2026 hier** (FAM-50):
   eine Zeile aus Feld und Knopf, mit `flex-wrap: var(--aufgabe-umbruch)`.

   **Sie ist in Abschnitt 19 aufgegangen**, und das ist kein Umzug aus
   Ordnungsliebe: Seit dem Umbau der Eingabemaske gab es den Selektor
   **zweimal** im Blatt. Im Browser gewann die spaetere - *und die erste sah
   an ihrer Stelle vollstaendig aus.* Gefunden hat es die Wache
   `test_die_eingabemaske_steht_in_einer_zeile`, die `re.search` benutzt und
   damit die **erste** fand. */

/* **Hier stand die Breitenregel des Namensfeldes**, und sie ist am
   28.09.2026 weggefallen (auf Rueckmeldung).

   Sie lautete `flex: 1 1 var(--aufgabe-titel-breite); min-width: 10ch` und
   war richtig, solange `.artikel-neu` eine **Zeile** war: Das Feld nahm den
   Rest, und die Mindestbreite hielt es davon ab, auf null zu schrumpfen
   (FAM-56, FAM-64).

   **Seit Einkauf 2a steht es in einer Spalte** (`.maskenfeld`,
   `flex-direction: column`), und dort ist `flex-grow` das Wachsen **in die
   Hoehe**. Gemessen 116,9 px statt 36 - und weil `.maskenfelder` seine
   Zellen unten ausrichtet, hob das Feld **seine eigene Beschriftung** um
   76 px ueber die der Nachbarn. *Dieselbe Zeile, zwei Bedeutungen.*

   **Die Breite entscheidet jetzt das Raster**: `repeat(auto-fit,
   minmax(170px, 1fr))` - mehr als die zehn Zeichen, die hier standen. */

/* **Die Menge ist schmal, aber nicht winzig.** Man soll beim Tippen sehen,
   dass hier wenig Platz ist - „die grosse Packung“ gehoert nicht hinein.

   **`ch` misst den Text, nicht das Feld** - und genau daran ist der
   Platzhalter abgeschnitten worden (gemeldet am 18.09.2026, Bildschirmfoto:
   „Meng“). Hier stand `flex: 0 1 9ch; min-width: 7ch`, und das liest sich
   wie „sieben Zeichen passen hinein“. **Der Innenabstand kommt dazu**, und
   der ist keine Schriftgroesse: Er kommt aus `--abstand` und ist je Ansicht
   verschieden.

   Gemessen im Abzug, vorher:

   | Ansicht | Feld | davon innen | „Menge“ braucht |
   |---|---|---|---|
   | Desktop | 77,6 px | **35,6** | **49,6 px** |
   | Handy   | 58,2 px | **36,2** | **37,2 px** |

   Es fehlten 14 px am Desktop und 1 px am Handy - in beiden Ansichten zu
   wenig, in der einen sichtbar und in der anderen knapp. **Ein Platzhalter,
   der abgeschnitten ist, sagt nicht, was in das Feld gehoert.**

   Gerechnet statt geraten: `7ch` Text (das Wort misst 5,75ch, in **jeder**
   Ansicht - `ch` waechst mit der Schrift) plus zweimal `--abstand` fuer das,
   was der Text nicht ausfuellt. Dieselbe Familie wie *`width` ist kein
   `flex-basis`* (FAM-64): eine Zahl, die etwas anderes misst, als sie zu
   messen scheint. */
/* **Und sie ist am 28.09.2026 weggefallen, aus demselben Grund wie die
   Regel darueber**: In einer Spalte ist `flex-basis` eine **Hoehe**, und die
   Mindestbreite hat keinen Leser mehr - im Raster ist jede Zelle mindestens
   **170 px** breit, also weit mehr als die 7ch + 2 x Abstand, die hier
   gerechnet wurden.

   *Die Messung bleibt trotzdem stehen*: Sie sagt, warum eine Zahl in `ch`
   nicht misst, was sie zu messen scheint - und das gilt beim naechsten Feld
   genauso. */

.artikelliste { list-style: none; margin: 0; padding: 0; }

/* Eine Zeile je Artikel: Haken, Text, Weg. */
.artikel {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}
.artikel form { display: flex; gap: 6px; align-items: center; }

/* **Der Abhak-Knopf ist die groesste Flaeche der Zeile** - er ist das, was
   im Laden getroffen werden muss, oft einhaendig und in Bewegung.

   Die Breite ist `--knopf-flaeche`, **kein Bruchteil davon**: Ein selbst
   ausgedachter Faktor unterbietet genau das Mass, das der Entwurf fuer das
   Handy festgelegt hat (die Lehre aus FAM-62, gemessene 41 px statt 48).

   **Und `--knopf-flaeche` statt `--schaltflaeche`** (05.10.2026): *44 sind
   der Boden*, und hier wiegt das schwerer als anderswo - der Satz darueber
   nennt den Grund selbst (*einhaendig und in Bewegung*). Mit dem kleineren
   Wert war der Knopf am Handy und am Desktop **36 × 36**. */
.haken {
  flex: 0 0 auto;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  color: var(--text-leise);
  line-height: 1;
}
.haken:hover { background: var(--akzent-blass); color: var(--text); }

/* **Gesetzt ist nicht nur farbig** - dieselbe Ueberlegung wie bei der Sonne
   (FAM-60) und beim ueberfaelligen Termin (FAM-65): Wer rot-gruen-blind ist,
   sieht die Farbe nicht, und am Infoboard aus zwei Metern verschwimmt sie.
   Das Zeichen selbst wechselt deshalb mit (leer zu Haken).

   **Wie deutlich, entscheidet Abschnitt 11** - dort ist der Knopf seit dem
   18.09.2026 das Kaestchen selbst, gefuellt statt blass angetoent. */
.haken.gesetzt {
  border-color: var(--akzent);
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
}

/* **Das Aendern steckt in einem `<details>`** - und das ist die wichtigste
   Entscheidung dieser Zeile.

   Zuerst standen die Eingabefelder offen da, wie bei den Schritten einer
   Aufgabe (FAM-56). Gemessen im Durchgang: **173 px je Zeile, 4,7 Zeilen je
   Handybildschirm** - bei zwanzig Artikeln vier Bildschirme Scrollen. Das
   steht der Karte entgegen, die im Laden abgehakt werden will.

   Zugeklappt bleibt, was man dort braucht: Kaestchen und Name. Kein
   JavaScript noetig (wie der Erledigt-Block in FAM-65). */
.artikel-aendern {
  /* **Hier steht bewusst nicht `--aufgabe-titel-breite`.**

     Die Variable ist `100%` am Handy, und das ist fuer die *Aufgabenliste*
     richtig: Dort stehen Titel, Zuweisung, Termin und drei Knoepfe in einer
     Zeile, die umbrechen muss (FAM-50).

     Diese Zeile hat zwei Teile - Kaestchen und Name -, und sie sollen
     **nebeneinander bleiben**: Der Daumen trifft im Laden das Kaestchen
     neben dem Namen, nicht darueber. Gemessen: mit der Variablen 119 px je
     Zeile, ohne sie 64 - das ist der Unterschied zwischen sieben und zwoelf
     Artikeln je Handybildschirm.

     `flex: 1 1 0` ist dabei **kein hingeschriebenes Mass** (Regel 0), das
     eine Merkmalsvariable uebergeht, sondern ein Verhaeltnis: „nimm den
     Rest der Zeile". Das Mass steht weiter in `--schaltflaeche` daneben. */
  flex: 1 1 0;
  min-width: 8ch;
}

/* Der Name ist die Flaeche, die aufklappt - **und sie ist so hoch wie der
   Abhak-Knopf**, damit die Zeile eine Zeile bleibt und der Daumen sie
   trifft. */
.artikel-aendern > summary {
  display: flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  padding: 0 calc(var(--abstand) / 2);
  border-radius: var(--radius);
  cursor: pointer;
}
/* **Ein Balken darunter, keine gruene Flaeche** (18.09.2026, auf Wunsch).

   Eine ganze Zeile einzufaerben ist viel Farbe fuer *„hier steht der
   Zeiger"* - und sie sieht aus wie ein Zustand, den die Zeile haette
   (ausgewaehlt, markiert, erledigt). **Ein Balken sagt nur, wo man ist.**

   `box-shadow` statt `border-bottom`: Ein Rahmen verschiebt den Inhalt um
   seine eigene Hoehe, und dann huepft die Zeile unter dem Zeiger. */
.artikel-aendern > summary:hover {
  background: none;
  box-shadow: inset 0 -2px 0 var(--akzent);
}

/* **Das Aufklappdreieck muss selbst gesetzt werden.**

   `display: flex` an einem `<summary>` unterdrueckt den `::marker` des
   Browsers - gemessen: `marker_content: normal`, und zu sehen war nichts.
   Damit gaebe es keinen Hinweis darauf, dass hier etwas aufgeht, und die
   Aenderungsfelder waeren unauffindbar.

   **Das Zeichen wechselt, nicht nur die Richtung einer Drehung** - derselbe
   Grundsatz wie beim Haken, bei der Sonne (FAM-60) und beim ueberfaelligen
   Termin (FAM-65): Wer wenig sieht, liest das Zeichen, nicht die Feinheit. */
.artikel-aendern > summary::after {
  content: "\25BE";           /* ▾ */
  margin-left: auto;
  padding-left: calc(var(--abstand) / 2);
  color: var(--text-leise);
}
.artikel-aendern[open] > summary::after { content: "\25B4"; }  /* ▴ */

/* Ein langer Name bricht um, statt die Seite zu verbreitern (Regel 2). */
.artikel-titel { overflow-wrap: anywhere; }

/* **Gekauftes ist durchgestrichen und leiser, aber lesbar.** Es soll im
   Laden zeigen, was schon im Wagen liegt - unlesbar waere es nutzlos. */
.artikel.gekauft .artikel-titel {
  text-decoration: line-through;
  color: var(--text-leise);
}

/* Aufgeklappt: die Felder und der weg-Knopf, eingerueckt unter dem Namen. */
.artikel-werkzeug {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  padding: calc(var(--abstand) / 2) calc(var(--abstand) / 2)
           calc(var(--abstand) / 2) 0;
}

/* **Beide Regeln sind am 28.09.2026 weggefallen** (auf Rueckmeldung), und
   zwar aus demselben Grund wie ihre Nachbarn oben: `.artikel-text` war eine
   **Zeile** neben dem Weg-Knopf, sein Namensfeld `10ch` breit. Seit Einkauf
   2a ist das Formular eine **Eingabemaske** mit einem Raster darin, und
   `flex: 1 1 10ch` bedeutet dort `10ch` **hoch** - gemessen 116,9 px.

   *In einer Zeile ist `10ch` eine Breite, in einer Spalte eine Hoehe.* */

.artikel-weg { margin-left: auto; }

/* --- Die Abteilungen (FAM-74) -------------------------------------------- */

/* Die Ueberschrift einer Gruppe - **wie „Im Wagen“ gebaut**, damit die beiden
   Absaetze der Liste gleich aussehen und nicht wie zwei Erfindungen. */
.abteilungsgruppe + .abteilungsgruppe { margin-top: var(--abstand); }
.abteilungsname {
  margin: 0 0 8px;
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-leise);
}

/* Die Auswahl in der aufgeklappten Zeile. `min-width`, damit sie im
   Flex-Umbruch nicht auf null schrumpft - die Lehre aus FAM-64, wo das
   Dateifeld am schmalen Fenster **0 Pixel breit** war und die Zeile dabei
   nicht kaputt aussah: Da stand einfach kein Feld. */
.abteilungswahl { flex: 1 1 12ch; min-width: 12ch; }

/* Die Nummerierung der Abteilungsmaske sagt nichts, was der Platz nicht
   besser gebrauchen koennte - die Reihenfolge sieht man an der Reihenfolge. */
.abteilungsliste { list-style: none; }

/* --- Die Liste Kaeufe (23.09.2026) --------------------------------------- */

/* **Hier standen die Regeln von `.gekaufte`**, und sie sind gefallen: Der
   Wagen ist seit dem 23.09.2026 ein `<details class="erledigte">` wie die
   Bloecke der beiden anderen Module - **eine Regel, die keine Vorlage mehr
   liest, ist eine, die beim naechsten Mal jemand sucht.**

   Die Zeitueberschrift eines Kaufs traegt die Masse, die jene Regeln der
   Blockueberschrift gaben: klein, leise, gesperrt. Sie ist jetzt ein `h3`,
   denn sie steht **in** einem Block und nicht darueber. */
.kaufgruppe + .kaufgruppe { margin-top: calc(var(--abstand) * 1.5); }

.kaufzeit {
  margin: 0;
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-leise);
}

/* **Der Kopf eines Kaufs** (06.10.2026, auf Wunsch): Zeitpunkt links, das
   Kreuz rechts. Der untere Abstand ist von `.kaufzeit` hierher gewandert -
   *er gehoert der Zeile, nicht der Ueberschrift darin.* */
.kaufkopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand);
  margin-bottom: calc(var(--abstand) / 2);
}

/* **Zwei Klassen, und das ist gemessen** (der Fund vom 05.10.2026):
   `all: unset` in Abschnitt 15 nennt `button.gefahr` - **(0,1,1)**, und das
   schlaegt eine einzelne Klasse, unabhaengig von der Reihenfolge im Blatt.
   `.gefahr.kaufwegknopf` ist **(0,2,0)** und gewinnt.

   **Beide Achsen** - *eine Zusage auf einer Achse ist keine*: Bei vier der
   sieben Knoepfe vom 05.10.2026 stand der Boden an der HOEHE und fehlte an
   der BREITE. */
.gefahr.kaufwegknopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  flex: 0 0 auto;
}

/* **Keine Bedienflaeche JE POSTEN** - und das ist der Unterschied zur
   Artikelzeile darueber. Ein Kauf ist vergangen: Hier wird nichts abgehakt,
   geaendert oder einsortiert. Deshalb eine schlichte Liste und keine
   `.listenzeile` mit `--schaltflaeche` je Zeile; bei zwanzig Posten waeren
   das gemessen 720 px am Handy fuer etwas, das man nur liest.

   **Seit dem 06.10.2026 hat der KOPF eine** (auf Wunsch: *„Einkaeufe, die
   abgeschlossen sind, einzeln loeschbar machen"*) - ein Kreuz je **Kauf**,
   nicht je Posten. *Die Rechnung oben bleibt damit stehen: Sie gilt der
   Liste, und die hat weiter keine.* */
.kaufliste { list-style: none; margin: 0; padding: 0; }

.kaufposten {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--abstand) / 2);
  padding: calc(var(--abstand) / 3) 0;
  border-bottom: 1px solid var(--linie);
}
.kaufposten:last-child { border-bottom: 0; }

/* Der Name darf schrumpfen, die Menge nicht - sonst bricht ein langer Name
   die Menge in eine eigene Zeile (`min-width: 0`, die Lehre aus FAM-84). */
.kaufname { flex: 1 1 auto; min-width: 0; }

/* --- Was nicht geklappt hat (FAM-93) -------------------------------------- */

/* **Wie die Listenzeile gebaut** (FAM-62/73): Text links, Knoepfe rechts, und
   der Umbruch entscheidet sich ueber dieselben Merkmalsvariablen. Ein eigener
   Bautyp mit eigenen Massen liefe beim naechsten Mal auseinander. */
.nachrichtenliste { list-style: none; margin: 0; padding: 0; }

/* **`wrap`, nicht `var(--aufgabe-umbruch)` - und das ist eine Messung.**
   Mit `nowrap` (Tablet, Desktop, Infoboard) stand die Zeile bei 375 px
   Fensterbreite **192 px ueber**, und das Abgeschnittene war der Knopf
   `Wegraeumen` - genau die Falle aus FAM-50.

   Hier ist `nowrap` auch nicht noetig: Bei der Aufgabenliste haelt es den
   Titel neben den Knoepfen, weil ein langer Titel sonst die ganze Zeile
   umwirft. Hier schrumpft stattdessen der Textblock (`min-width: 0`), und
   umgebrochen wird erst, wenn wirklich kein Platz mehr ist.

   **Regel 0 bleibt gewahrt**: Wo die Zeile bricht, entscheidet weiterhin
   `--aufgabe-titel-breite` - am Handy `100%`, also immer. */
.nachrichtenzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px var(--abstand);
  padding: var(--abstand) 0;
  border-bottom: 1px solid var(--linie);
}

.nachrichtentext { flex: 1 1 var(--aufgabe-titel-breite); min-width: 0; }
.nachrichtentext p { margin: 0 0 4px; }
.nachrichtentext p:last-child { margin-bottom: 0; }

.nachrichtenkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}

/* Das Wort traegt die Aussage, nicht die Farbe (FAM-65). Die Farbe kommt
   dazu - sie ersetzt nichts. */
.nachrichtenzustand {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.88em;
}
.nachrichtenzustand.zustand-offen { color: var(--text-leise); }
.nachrichtenzustand.zustand-aufgegeben { color: var(--fehler); }
.nachrichtenzustand.zustand-abgelehnt { color: var(--fehler); }

.nachrichtenwann { color: var(--text-leise); }

.nachrichtengrund { color: var(--text-leise); }

/* **`nowrap` und `flex: 0 0 auto`, und das ist eine Messung**: Mit `wrap`
   stapelten sich die beiden Knoepfe je nach Laenge des Vermerks mal
   uebereinander und mal nebeneinander - **in derselben Liste, Zeile fuer
   Zeile verschieden.** Schrumpfen soll der Text (`min-width: 0` oben), nicht
   die Bedienung. Wird es wirklich eng, bricht die ganze ZEILE um; das
   entscheidet `--aufgabe-umbruch` und damit die Ansicht. */
.nachrichtenaktionen {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: nowrap;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

/* --- Der Abgleich (FAM-94) ----------------------------------------------- */

/* **Derselbe Bautyp wie die Nachrichtenzeile darunter** - und aus demselben
   Grund: Zwei Listen auf einer Maske, die verschieden gebaut sind, sehen aus
   wie zwei Masken. */
.abgleich { margin-bottom: calc(var(--abstand) * 1.5); }
.abgleich h2 {
  margin: 0 0 var(--abstand);
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-leise);
}

.befundliste { list-style: none; margin: 0; padding: 0; }

.befundzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px var(--abstand);
  padding: var(--abstand) 0;
  border-bottom: 1px solid var(--linie);
}

.befundtext { flex: 1 1 var(--aufgabe-titel-breite); min-width: 0; }
.befundtext p { margin: 0 0 4px; }
.befundtext p:last-child { margin-bottom: 0; }

.befundkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
}

/* Das Wort traegt die Aussage, nicht die Farbe (FAM-65) - die Farbe kommt
   dazu und ersetzt nichts. */
.befundart {
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.88em;
}
.befundart.art-fehlt { color: var(--fehler); }
.befundart.art-ueberzaehlig { color: var(--fehler); }
.befundart.art-weicht_ab { color: var(--akzent-dunkel); }
.befundart.art-nicht_pruefbar { color: var(--text-leise); }

.befundregel { color: var(--text-leise); }
.befundgrund { color: var(--text-leise); }

/* `nowrap` und `flex: 0 0 auto` - dieselbe Messung wie bei
   `.nachrichtenaktionen` (FAM-93). */
.befundaktion {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: nowrap;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}

/* --- Der Kalender: am Handy eine Liste, ab Tablet ein Gitter (FAM-84) ----

   **Ein Kalender ist nicht ueberall ein Gitter.** Sieben Spalten sind am
   Handy bei 375 px Breite rund 53 px je Tag - schmaler als die 48 px, die
   Regel 10 als Bedienflaeche setzt, und darin soll ein Text stehen. Das ist
   derselbe Fehler wie FAM-50, wo eine Tabellenzeile am Handy nicht umbrechen
   konnte und *abhaken* im Seitwaerts-Scrollbereich lag.

   **Und das Infoboard bekommt ebenfalls die Liste.** Es zeigt weniger, nicht
   mehr (Regel 10): Wer im Vorbeigehen zwei Sekunden hinsieht, will wissen,
   WAS ansteht - nicht, welcher Wochentag leer ist.

   | Merkmal | Handy | Tablet | Desktop | Infoboard |
   |---|---|---|---|---|
   | `--kalender-spalten`         | `1fr`  | `repeat(7, 1fr)` | `repeat(7, 1fr)` | `1fr`  |
   | `--kalender-leertag-anzeige` | `block` | `block`          | `block`          | `block` |

   **Die beiden gehoeren zusammen**: Eine Spalte heisst Liste, und in einer
   Liste sind sieben leere Zeilen nur Weg. Dass sie sich nicht widersprechen
   koennen, haelt test_liste_und_gitter_koennen_sich_nicht_widersprechen fest -
   sonst waere das hier eine zweite Stelle, an der dieselbe Auskunft steht. */
.kalender {
  display: grid;
  grid-template-columns: var(--kalender-spalten);
  gap: var(--abstand);
  align-items: start;
}

.kalendertag {
  min-width: 0;            /* sonst sprengt ein langer Titel seine Spalte */
  border-top: 2px solid var(--linie);
  padding-top: 8px;
}

/* --- Die Liste (FAM-275, 06.11.2026) -------------------------------------

   *„Was kommt als Nächstes"* - und dieselbe Liste zeigt die Treffer der
   Suche (FAM-276).

   **Sie sieht aus wie die Woche am Handy**, und zwar mit denselben Zahlen:
   Linie oben, 8 px Luft, der heutige Tag mit dem Akzent. *Zwei Listen
   derselben Anwendung, die verschieden aussehen, sehen aus wie zwei
   verschiedene Sachen.*

   **Keine Merkmalsvariable** (Regel 0): Die Liste ist in allen vier
   Ansichten eine Liste - *sie ist die Antwort auf „was kommt", und die
   hängt nicht an der Bildschirmbreite.* Eine Variable mit viermal demselben
   Wert wäre eine Zeile, die nichts entscheidet. */
.listentage {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
}

.listentag {
  min-width: 0;
  border-top: 2px solid var(--linie);
  padding-top: 8px;
}

/* **Das Wort trägt die Aussage, nicht die Farbe** (FAM-65): Im Kopf steht
   „heute", und zwar aus `app/datum.py`. Die Linie ist die Zugabe für den,
   der sie sieht - nicht die Auskunft. */
.listentag.heute { border-top-color: var(--akzent); }

.listentagkopf {
  margin: 0 0 4px;
  font-weight: 600;
}

/* --- Das Suchfeld (FAM-276) ----------------------------------------------

   **Eine Zeile aus Feld und Knopf** - und das Feld nimmt den Rest: Es ist
   das, was man anfasst.

   **`min-width: 0` am Feld**, sonst darf ein Flex-Element nicht unter
   seinen Mindestinhalt schrumpfen und hängt am Handy aus der Zeile (die
   Falle steht im Index, FAM-56). */
.terminsuche {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  margin-bottom: var(--abstand);
}
.terminsuche input[type="search"] {
  flex: 1 1 14ch;
  min-width: 0;
  /* **So hoch wie der Knopf daneben** - gemessen war das Feld **36 px**
     neben einem 44er Knopf. *Ein Suchfeld, das man mit dem Finger verfehlt,
     löst das Problem nicht, das es lösen soll* - wörtlich die Nachbesserung
     an der Stammbaumsuche (25.10.2026).

     **Der Boden, nicht die Decke** (`--knopf-flaeche` ist
     `max(44px, --schaltflaeche)`): Am Infoboard wächst beides mit. */
  min-height: var(--knopf-flaeche);
}

/* Im Gitter bleibt ein leerer Tag stehen - sonst saesse der Mittwoch unter
   dem Dienstag. In der Liste ist er weg. */
/* **Ein leerer Tag steht jetzt ueberall da** (25.10.2026, auf Wunsch:
   *„würde ich gerne unten an einem Tag ein + Termin Button haben"*).

   **Hier stand `none` am Handy und am Infoboard**, mit einer gemessenen
   Begruendung aus FAM-84: *„sieben leere Zeilen sagen niemandem etwas"* -
   im Gitter bleibt der Kasten stehen, damit der Mittwoch nicht unter dem
   Dienstag sitzt, in der Liste war er weg.

   **Der Satz ist abgelaufen, nicht falsch geworden**: Seit heute traegt
   jeder Tag sein `+`, und *ein leerer Tag ist genau der, an dem man einen
   Termin eintragen will.* Eine Zeile, die eine Handlung traegt, ist keine
   leere Zeile mehr.

   **Die Variable bleibt** - wer es zurueckdrehen will, setzt sie auf
   `none` und bekommt die Liste von gestern. */
.kalendertag.leer { display: var(--kalender-leertag-anzeige); }

/* **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65): Im Kopf steht
   „Heute“, und zwar aus app/datum.py. Die Linie ist die Zugabe fuer den, der
   sie sieht - nicht die Auskunft. */
.kalendertag.heute { border-top-color: var(--akzent); }

.kalenderkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  margin: 0 0 4px;
}
.kalenderwochentag { font-weight: 600; }
.kalenderdatum { color: var(--text-leise); font-size: 0.92em; }

/* Welche Woche gezeigt wird - einmal ueber dem Kalender, nicht an jedem Tag. */
.kalenderwoche {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  margin: 0 0 var(--abstand);
}

/* --- Wessen Termine: der Filter (FAM-164) --------------------------------

   **Keine neue Merkmalsvariable, und das ist Absicht.** Der Filter ist eine
   Liste von Bedienflaechen, und dafuer gibt es `.ankreuzliste` seit FAM-63 -
   sie behaelt ihren Rahmen auch am Handy, ausdruecklich (Regel 10, FAM-109):
   *Sie ist keine `.feldgruppe`, sondern eine Liste von Bedienflaechen.*

   **Zugeklappt kostet er eine Zeile** - gemessen steht die Zahl in
   `docs/Test/FAM-164.md`. */
.terminfilter {
  margin: 0 0 var(--abstand);
}

/* Die zugeklappte Zeile **ist die Bedienflaeche**, nicht das Dreieck daneben -
   wie die Esserzeile (FAM-146), die Artikelzeile (FAM-70) und der
   Termintitel (FAM-84). */
.terminfilterzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px var(--abstand);
  min-height: var(--schaltflaeche);
  cursor: pointer;
}

.filterwort { font-weight: 600; }

/* **Was gefiltert ist, steht dabei** - und ein langer Name bricht um, statt
   die Seite zu verbreitern (Regel 2). */
.filternamen {
  min-width: 0;
  overflow-wrap: anywhere;
  color: var(--text-leise);
}

/* **Das Zeichen wird selbst gesetzt und wechselt** (nicht gedreht) - wie bei
   der Artikelzeile (FAM-70), der Leihzeile (FAM-136) und der Esserzeile
   (FAM-146).

   **`list-style: none` steht ausdruecklich da**, statt sich darauf zu
   verlassen, dass `display: flex` den `::marker` ohnehin loescht: In FAM-70
   war damit ein ganzer Bedienweg unauffindbar, und ein Nebeneffekt ist keine
   Zusage. */
.terminfilter > summary { list-style: none; }
.terminfilter > summary::-webkit-details-marker { display: none; }
.terminfilter > summary::after { content: "\25BE"; margin-left: auto; }
.terminfilter[open] > summary::after { content: "\25B4"; }

.filterformular {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  padding-bottom: var(--abstand);
}

/* Die Knoepfe nebeneinander - **und sie duerfen umbrechen** (FAM-84): Eine
   Leiste, die eine Aktion abschneidet, ist keine. */
.filterknoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: var(--abstand);
  margin: 0;
}

/* **Der Preis, und er steht auch am Handy da** - kein `.feldhinweis`, den
   `--feldhinweis-anzeige` dort ausblendet (FAM-109). Wer nach einem Menschen
   filtert und trotzdem das Grillfest sieht, wundert sich genau dort. */
.filterpreis {
  margin: 0;
  color: var(--text-leise);
  font-size: 0.92em;
}

/* --- Der Monat (FAM-163, umgebaut am 29.09.2026) -------------------------

   | Merkmal | Handy | Tablet | Desktop | Infoboard |
   |---|---|---|---|---|
   | `--monat-schrift`     | 11px | 13px | 14px  | 22px  |
   | `--monat-zeilenhoehe` | 34px | 60px | 74px  | 120px |

   **Alle vier Ansichten bekommen das Gitter** (auf Wunsch) - bis dahin nur
   der Desktop. Die Vorlage war ein Bildschirmfoto des Outlook-Kalenders am
   Handy; was daran haengt, steht im Kopf von `termine/_monat.html`.

   ## Warum die Messung von FAM-163 nicht mehr dagegen spricht

   Dort stand: Bei sieben Spalten misst ein Fach am Tablet 84,7 px, und dem
   Titel bleiben **41,6 px** - rund fuenf Zeichen (*„Spagh"*, FAM-145).

   **Zwei Dinge haben sich geaendert**, und beide sind gemessen:

   | | |
   |---|---|
   | **Ein Balken hat die Breite seiner Tage** | Ein Urlaub ueber elf Tage stand in elf Faechern einzeln; jetzt steht er **einmal** ueber elf Spalten |
   | **Die Handschrift ist im Gitter zu breit** | In einer 50 px breiten Spalte: Segoe Print **6,3 Zeichen**, eine Grotesk **8,0**. Deshalb `--schrift-monat` |

   ## Drei Merkmale sind weggefallen

   `--monat-kopf-anzeige`, `--monat-leertag-anzeige` und
   `--monat-fremdtag-anzeige` gab es, weil es **zwei Bauarten** gab: Gitter
   und Liste. *Es gibt nur noch eine* - und damit keine Frage mehr, ob
   Spaltenkoepfe und Nachbartage dazugehoeren. Sie tun es immer.

   **`--monat-reihe-spalten` ist keine vierte Bauart**, sondern die Stelle,
   an der die Tageszahlen und die Spuren dieselbe Breite bekommen wie die
   Spalten darueber: `2 / -1`, also alles rechts der Kalenderwoche.

   **Eigene Merkmale, obwohl sie heute dieselben Werte tragen wie
   `--kalender-*`** - *uebernommene Merkmalsvariablen sind eine Entscheidung,
   keine Vorlage* (FAM-93, FAM-136, FAM-145). */
.monat { display: flex; flex-direction: column; gap: 2px; }

/* Die Spaltenueberschriften - jetzt in **allen** Ansichten, denn es gibt
   keine Liste mehr, in der sie stoerten. Die erste Zelle haelt der
   Kalenderwoche den Platz frei. */
.monatskopf {
  display: grid;
  grid-template-columns: var(--monat-spalten);
  gap: 2px;
  padding-bottom: 2px;
}
.monatswochentag {
  font-family: var(--schrift-monat);
  font-size: var(--monat-schrift);
  font-weight: 600;
  color: var(--text-leise);
  text-align: center;
}

.monatsgitter {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* **Eine Wochenzeile** - die Kalenderwoche links, darin zwei Raster
   uebereinander: die Tageszahlen und die Spuren.

   Die senkrechten Trennlinien sind ein **Verlauf**, kein Rahmen je Fach:
   Ein Balken laeuft ueber mehrere Spalten, und ein Rahmen darunter
   schnitte ihn optisch in Stuecke. */
.monatswoche {
  display: grid;
  grid-template-columns: var(--monat-spalten);
  gap: 2px;
  align-items: start;
  border-top: 1px solid var(--linie);
  padding-top: 3px;
}

/* **Die Kalenderwoche** - eine Auskunft, kein Weg (Regel 7). Sie steht
   ueber die ganze Hoehe ihrer Zeile. */
.monatskw {
  display: var(--monat-kw-anzeige);
  align-self: stretch;
  font-family: var(--schrift-monat);
  font-size: var(--monat-schrift);
  color: var(--text-leise);
  text-align: center;
  padding-top: 2px;
}

/* Die sieben Zahlen und die Spuren teilen sich dieselben Spalten - deshalb
   steht `grid-column: 2 / -1` an beiden und darin noch einmal ein Raster
   aus sieben gleichen Spalten. */
.monatstageszeile,
.monatsspuren {
  grid-column: var(--monat-reihe-spalten);
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 2px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.monatsspuren {
  /* **So hoch, wie die Woche Spuren hat** - plus eine Zeile fuer „+n mehr".
     `--spuren` kommt aus der Vorlage und damit aus dem Server: *eine feste
     Zahl waere eine leere Zeile unter jeder ruhigen Woche.* */
  grid-template-rows: repeat(calc(var(--spuren) + 1), auto);
  min-height: var(--monat-zeilenhoehe);
  row-gap: 1px;
  margin-top: 2px;
}

/* Das Datum ist der Weg in die Woche - also eine Bedienflaeche. **44 px
   sind der Boden** (`--knopf-flaeche`), und im Gitter waere das die halbe
   Zeile: Die Zahl bekommt deshalb ihre eigene Hoehe, und der ganze Tag
   bleibt ueber die Spalte erreichbar. */
.monatszahl {
  display: flex;
  align-items: center;
  justify-content: center;
  /* **Die Trefferflaeche bleibt der Boden** (FAM-62, FAM-84) - auch
     im Gitter, wo sie fast die halbe Zeile ist. Der Kreis sieht
     deshalb aus wie im Vorbild, *und er ist anfassbar.* */
  min-height: var(--knopf-flaeche);
  font-family: var(--schrift-monat);
  font-size: var(--monat-schrift);
  font-weight: 600;
  border-radius: 999px;

  /* **Eine Zahl, kein Link** - im Gitter stehen sieben davon nebeneinander,
     und siebenmal Blau mit Unterstrich liest sich wie eine Werkzeugleiste.
     *Der Weg bleibt* (Regel 7): Sie ist ein `<a>`, sie sieht nur nicht so
     aus, bis man sie anfasst. */
  color: var(--text);
  text-decoration: none;
}
.monatszahl:hover { background: var(--akzent-blass); }
.monatszahl.heute:hover { background: var(--akzent-dunkel); }

/* **Ein fremder Tag ist leiser, nicht unsichtbar** - wer am 31. August
   etwas vorhat, soll es in der Zeile sehen, in der er steht. */
.monatszahl.fremd { color: var(--text-leise); font-weight: 400; }

/* **Heute traegt die Markenfarbe** - wie im Vorbild des Nutzers ein
   gefuellter Kreis. Das Wort steht im `title`, die Farbe ist die Zugabe
   (FAM-65). */
.monatszahl.heute {
  background: var(--akzent);
  color: var(--auf-akzent);
}

/* --- Der Balken ----------------------------------------------------------
   Ein Eintrag im Monatsgitter ist eine **gefuellte Flaeche**, kein
   unterstrichener Verweis: Bei sieben Spalten nebeneinander traegt die
   Flaeche die Zugehoerigkeit, die eine Linie nicht traegt.

   **Die Farbe kommt aus derselben Rezeptur wie ueberall** (FAM-162,
   FAM-169): Der Ton steht am Element, Helligkeit und Buntheit im Blatt.
   Ohne Zuweisung ist es die Familienfarbe - *eine erfundene waere die
   Sorte Behauptung aus Regel 5.* */
.terminzeile.kompakt.balken {
  /* **Ohne Zuweisung: kein Farbton, sondern die gedaempfte Flaeche.**
     Ein Termin der ganzen Familie traegt keine Personenfarbe
     (`app/personenfarben.py`), und eine ausgedachte waere genau die
     Behauptung aus Regel 5. `--linie-abschnitt` steht schon in Abschnitt 1
     und ist die neutrale Flaeche dieses Blattes. */
  background: var(--linie-abschnitt);
  border-radius: 4px;
  padding: 1px 5px;
  min-width: 0;
  overflow: hidden;
}

/* **Mit Zuweisung: dieselbe Rezeptur wie das Papier am Brett** (FAM-169) -
   der Ton steht am Element, Helligkeit und Buntheit im Blatt (Regel 10).
   `.gefaerbt` setzt der Baustein genau dann, wenn es **einen** Zugewiesenen
   gibt (FAM-162). */
.terminzeile.kompakt.balken.gefaerbt {
  background: oklch(var(--papier-helligkeit) var(--papier-buntheit) var(--person-ton));
}

.terminzeile.kompakt.balken .terminname {
  font-family: var(--schrift-monat);
  font-size: var(--monat-schrift);
  line-height: 1.45;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin: 0;
}

.terminzeile.kompakt.balken a { color: var(--text); text-decoration: none; }
.terminzeile.kompakt.balken a:hover { text-decoration: underline; }

/* **Ein angeschnittener Balken hat eine gerade Kante** - er laeuft in die
   Nachbarwoche weiter, und eine runde Ecke behauptete, er hoere hier auf. */
.terminzeile.kompakt.balken.vorher { border-start-start-radius: 0; border-end-start-radius: 0; }
.terminzeile.kompakt.balken.spaeter { border-start-end-radius: 0; border-end-end-radius: 0; }

/* „+2 mehr" - ebenfalls ein Weg, also ebenfalls eine Bedienflaeche. */
.monatmehrzeile { min-width: 0; }
.monatmehr {
  display: block;
  font-family: var(--schrift-monat);
  font-size: var(--monat-schrift);
  color: var(--text-leise);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 0 4px;
}
.terminzeile.kompakt { padding: 0; border-bottom: 0; }
.terminzeile.kompakt .terminname {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.terminzeile.kompakt .terminzeit { flex: 0 0 auto; font-size: 0.92em; }
.terminzeile.kompakt .terminname a {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  /* **Kein `overflow-wrap: anywhere`** wie in der vollen Fassung: Hier soll
     der Titel abgeschnitten werden und nicht umbrechen - ein Fach, dessen
     Titel auf drei Zeilen laeuft, macht seine ganze Gitterzeile hoch.

     **`display: block`, nicht `flex`** - eine Ellipse greift in einem
     Flex-Kasten nicht. Damit faellt `align-items: center` weg, und die
     Zeile wird stattdessen ueber `line-height` mittig.

     **`min-height` bleibt trotzdem stehen**, und das ist kein Zierrat: Es
     ist die Zusage aus FAM-84, dass der Titel eine Bedienflaeche ist. Der
     erste Entwurf hatte hier nur `line-height` - das sieht gleich aus und
     ist es nicht, sobald die Schrift groesser wird als die Zeile. Gefunden
     hat es test_der_termintitel_ist_eine_bedienflaeche. */
  display: block;
  min-height: var(--schaltflaeche);
  line-height: var(--schaltflaeche);
}
.terminzeile.kompakt .terminquelle { flex: 0 0 auto; }

/* --- Ein Termin im Tageskasten (FAM-83, umgebaut in FAM-84) --------------

   Bis FAM-84 trug jede Zeile ihr eigenes Datum und war deshalb flach gebaut.
   Jetzt steht das Datum ueber dem Kasten, und die Zeile ist ein Block - der
   in eine 84 px breite Spalte passen muss. */
.terminliste { list-style: none; margin: 0; padding: 0; }

.terminzeile {
  min-width: 0;
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}
.terminzeile:last-child { border-bottom: 0; padding-bottom: 0; }
.terminzeile p { margin: 0 0 2px; }
.terminzeile p:last-child { margin-bottom: 0; }

.terminkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
}

.terminzeit { color: var(--text-leise); }
.terminbis { color: var(--text-leise); }

/* **Dass sich etwas wiederholt, traegt das Wort** - der Pfeil davor ist die
   Zugabe (FAM-65). Wer ihn nicht erkennt, liest „woechentlich“. */
.terminwiederholung { color: var(--text-leise); }

/* **Woher ein Eintrag kommt, traegt ebenfalls ein Wort** (FAM-87) - „Aufgabe“.
   Die Flaeche darum ist die Zugabe fuer den, der sie sieht; wer rot-gruen-blind
   ist oder zwei Meter vom Infoboard entfernt steht, liest das Wort. */
.terminquelle {
  display: inline-block;
  padding: 0 6px;
  border-radius: var(--radius);
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-size: 0.82em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* **Woher ein Geburtstag kommt** (FAM-159) - dieselbe Marke wie am Termin
   darueber, und aus demselben Grund: *Das Wort traegt die Aussage, nicht die
   Farbe* (FAM-65). Sie sagt zugleich, warum sich der Tag hier nicht aendern
   laesst. */
.geburtstag-quelle {
  display: inline-block;
  padding: 0 6px;
  border-radius: var(--radius);
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-size: 0.82em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* „ganzer Tag“ ist eine Auskunft, kein Schmuck - und steht deshalb da, wo
   sonst die Uhrzeit stuende. */
.terminganztags {
  color: var(--text-leise);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 0.88em;
}

/* **`overflow-wrap`, weil eine Spalte schmaler sein kann als ein Wort.**
   „Elternsprechtag“ bei 24 px Schrift ist breiter als mancher Kasten, und
   ein Wort, das ueber seinen Rand laeuft, erzeugt genau das waagerechte
   Scrollen, gegen das diese Karte gebaut ist. */
/* **Der Titel ist der einzige Weg in die Maske - also ist er eine
   Bedienflaeche.** Gemessen war er am Handy 23 px hoch; die Karte verlangt
   ausdruecklich kein Bedienelement unter 48 px, und eine Fingerkuppe ist
   breiter als ein Mauszeiger (Regel 10).

   `var(--schaltflaeche)`, **keine hingeschriebene Zahl**: 48 am Handy, 36 am
   Desktop, 72 im Infoboard. Ein selbst ausgedachter Bruchteil davon waere
   eine erfundene Zahl (die Lehre aus FAM-62). */
.terminname a {
  display: flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  font-size: 1.05em;
  overflow-wrap: anywhere;
}
.terminwer { color: var(--text-leise); overflow-wrap: anywhere; }

/* Wie das Stift-Zeichen an der Aufgabe (FAM-57): Es sagt nur, DASS es eine
   Notiz gibt. Der Inhalt zerrisse die Zeile. */
.terminnotizzeichen {
  margin-left: 0.5em;
  color: var(--text-leise);
}

/* --- Der Wochenplan des Essensplans (FAM-145) -----------------------------

   **Der Wochenplan ist in allen vier Ansichten eine Liste** - und das ist
   eine andere Antwort als beim Kalender daneben, der ab Tablet ein Gitter
   zeigt. Die Karte schlaegt das Gitter ausdruecklich vor; **gemessen traegt
   es nirgends**, und deshalb steht es nirgends:

   | Ansicht | Spalte | Feld innen | Zeichen | „Spaghetti Bolognese“ braucht |
   |---|---|---|---|---|
   | Handy, Liste     |  340 px | 182,8 px | 17,0 | 109,3 px |
   | Tablet, Liste    |  713 px | 495,4 px | 32,5 | 154,9 px |
   | Tablet, GITTER   |  86,8   |  44,8    |  2,9 | 154,9 px |
   | Desktop, GITTER  |  160    |  74,0    |  5,1 | 145,8 px |
   | Infoboard, Liste | 1841    | 1528,5   | 70,9 | 218,7 px |

   **Der Unterschied zum Kalender ist das Eingabefeld.** Dort steht in der
   Spalte ein Text, der umbrechen darf; hier steht ein Feld, in das man tippt -
   und ein Feld kann nicht beliebig schmal werden, ohne unbrauchbar zu sein.
   **Fuenf Zeichen von „Spaghetti Bolognese“ sind „Spagh“**, und damit ist der
   Plan genau dort unlesbar, wo man ihn liest.

   **Die Variable bleibt trotzdem**, obwohl sie viermal dasselbe sagt - wie
   `--marke-anzeige`: Sie haelt die Entscheidung fest und ist die eine Stelle,
   an der ein Gitter wieder entstehen koennte. Das braucht dann aber eine
   Maske, in der das Fach **Text** zeigt und das Eintragen woanders steht -
   also eine Entscheidung ueber die Bedienung, und *vorgebaute Oberflaeche ist
   ungepruefte Oberflaeche* (FAM-73).

   **Und es gibt keine zweite Variable fuer den leeren Tag.** Der Kalender hat
   eine (`--kalender-leertag-anzeige`), weil dort sieben leere Zeilen nur Weg
   sind. Hier ist ein leerer Tag die Stelle, an der man etwas eintraegt - wer
   ihn ausblendet, kann am Handy den Donnerstag nicht mehr planen. */
.essensplan {
  display: grid;
  grid-template-columns: var(--essensplan-spalten);
  gap: var(--abstand);
  align-items: start;
}

.essenstag {
  min-width: 0;            /* sonst sprengt ein langes Gericht seine Spalte */
  border-top: 2px solid var(--linie);
  padding-top: 8px;
}

/* **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65): Im Kopf steht
   „Heute“, und zwar aus app/datum.py. Die Linie ist die Zugabe fuer den, der
   sie sieht - nicht die Auskunft. */
.essenstag.heute { border-top-color: var(--akzent); }

.essenstagkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  margin: 0 0 4px;
}
.essenstagname { font-weight: 600; }
.essenstagdatum { color: var(--text-leise); font-size: 0.92em; }

/* Welche Woche gezeigt wird - einmal ueber dem Plan, nicht an jedem Tag. */
.essensplanwoche {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  margin: 0 0 var(--abstand);
}

/* --- Ein Fach: Mittag oder Abend -----------------------------------------

   **`wrap`, keine Merkmalsvariable.** Die Einkaufszeile hat sich das von der
   Aufgabenliste geliehen und in der Tablet-Ansicht zwei Bedienelemente
   abgeschnitten (FAM-136) - *uebernommene Merkmalsvariablen sind eine
   Entscheidung, keine Vorlage* (FAM-93). Hier ist die Entscheidung einfach:
   In einer Gitterspalte MUSS das Feld unter seine Beschriftung rutschen
   duerfen, sonst steht es draussen. */
/* **Der Kasten um ein Fach** (FAM-146) - er traegt die Trennlinie, seit im
   Fach zwei Formulare stehen: das Essen und die Frage, wer mitisst. Ein
   `<form>` im `<form>` gibt es in HTML nicht.

   **Ein gewoehnlicher Block in einem Block** - er aendert an der Aufteilung
   nichts, er fasst nur zusammen, was zusammengehoert. Vorher lagen Abstand
   und Linie an `.essensfach`. */
.fach {
  padding: 6px 0;
  border-bottom: 1px solid var(--linie);
}
.fach:last-child { border-bottom: 0; }

.essensfach {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
}

/* „Mittag“ und „Abend“ stehen sichtbar da, nicht nur als Platzhalter: Ein
   Platzhalter verschwindet beim Tippen (FAM-109). */
.essensfachzeit {
  flex: 0 0 auto;
  color: var(--text-leise);
  font-size: 0.88em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* **`min-width: 0` an jedem Flex-Element, das eine Eingabe traegt** - die
   Lehre aus FAM-56 und FAM-64: Ohne sie war das Feld am schmalen Fenster
   gemessen 0 px breit, und die Zeile sah dabei nicht kaputt aus, da stand
   einfach kein Feld. */
.essensfach input[type="text"] {
  flex: 1 1 12ch;
  min-width: 9ch;
}

/* --- Wer isst mit (FAM-146) -----------------------------------------------

   **Zugeklappt**, wie das Aendern in der Einkaufszeile (FAM-70): Der haeufige
   Fall ist *alle essen mit*, die Auswahl ist die Ausnahme. Offen kostete
   jedes Fach eine ganze Ankreuzliste, und bei vierzehn Faechern waere der
   Plan nicht mehr lesbar.

   **Und `<details>` kostet kein JavaScript** (Regel 7). */
.esserwahl > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  /* **Die Zeile ist die Bedienflaeche**, nicht das Dreieck am Rand - dieselbe
     Ueberlegung wie bei `.ankreuzzeile` (FAM-63) und beim Termintitel
     (FAM-84). Keine erfundene Zahl, sondern die aus Regel 10. */
  min-height: var(--schaltflaeche);
  cursor: pointer;
  color: var(--text-leise);
  font-size: 0.88em;
}

/* **Das Zeichen wird selbst gesetzt und wechselt** (nicht gedreht) - wie bei
   der Artikelzeile (FAM-70), bei der Leihzeile (FAM-136) und beim Menue
   (FAM-107).

   **`list-style: none` steht ausdruecklich da**, statt sich darauf zu
   verlassen, dass `display: flex` den `::marker` ohnehin loescht: In FAM-70
   war damit ein ganzer Bedienweg unauffindbar, und ein Nebeneffekt ist keine
   Zusage. */
.esserwahl > summary { list-style: none; }
.esserwahl > summary::-webkit-details-marker { display: none; }
.esserwahl > summary::after { content: "▾"; margin-left: auto; }
.esserwahl[open] > summary::after { content: "▴"; }

/* **Die Zahl ist gezaehlt** (Regel 5) und steht vorn - sie ist die Auskunft,
   wegen der es die Zeile gibt: *fuer wie viele wird gekocht.* */
.esserzahl { font-weight: 600; }

/* Ein langer Name bricht um, statt die Seite zu verbreitern (Regel 2). */
.essernamen { min-width: 0; overflow-wrap: anywhere; }

.esserformular {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  padding-bottom: var(--abstand);
}
/* Der Knopf nimmt seine Breite, nicht die der Liste darueber. */
.esserformular > button { align-self: flex-start; }

/* --- Wie es geschmeckt hat (FAM-148) --------------------------------------

   **Die Bewertung ist die ZWEITE Zeile eines Gerichts**, kein weiteres
   Element neben Namen und Zahl: Die Namen der anderen brauchen Platz.

   **Dafuer darf `.listenzeile` hier umbrechen** - und das steht ausdruecklich
   da, statt sich auf `--aufgabe-umbruch` zu verlassen. Der Wert ist auf
   Tablet, Desktop und Infoboard `nowrap`; die Einkaufszeile hat sich genau
   diese Variable von der Aufgabenliste geliehen und in der Tablet-Ansicht
   zwei Bedienelemente abgeschnitten (FAM-136). *Uebernommene
   Merkmalsvariablen sind eine Entscheidung, keine Vorlage* (FAM-93).

   **Nur in dieser Liste**, nicht an `.listenzeile` allgemein: Die
   Abteilungen und die Aufgabenlisten haben keine zweite Zeile, und was sie
   nicht brauchen, bekommen sie nicht. */
.gerichteliste .listenzeile { flex-wrap: wrap; }

.bewertung {
  flex: 1 1 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px var(--abstand);
}

/* **Gezaehlt, nicht gemittelt** (Regel 5) - und **offen, mit Namen**
   (FAM-100). Leise gesetzt wie die Zahl daneben: Sie ist die Auskunft, nicht
   die Ueberschrift. */
.bewertungsstand {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--abstand);
  margin: 0;
  min-width: 0;
  color: var(--text-leise);
  font-size: 0.88em;
}

.bewertungsstufe {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  min-width: 0;
}

/* Die Zahl steht vorn im Blick - sie ist das, was beim Planen zaehlt. */
.bewertungszahl { font-weight: 600; }

/* Fuenf Namen brechen um, statt die Seite zu verbreitern (Regel 2). */
.bewertungsnamen { min-width: 0; overflow-wrap: anywhere; }

/* **Die drei Knoepfe rechts**, das eigene Urteil als Wort davor.

   **Keine Markierung am Knopf** (FAM-65): Wer rot-gruen-blind ist, saehe sie
   nicht, und am Infoboard aus zwei Metern verschwimmt sie. Das Wort traegt
   die Aussage - *„Du: mag ich“*.

   Die Hoehe der Knoepfe kommt aus Abschnitt 7 und 11 und ist nirgends hier
   kleiner gerechnet (FAM-62, FAM-109). */
.bewertungswahl {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  margin-left: auto;
}

.bewertungmeins { color: var(--text-leise); font-size: 0.88em; }

/* --- Die Familie fragen (FAM-149) -----------------------------------------

   Beides steht in einem leeren Fach des Wochenplans - **entweder der Knopf
   oder der Weg**, nie beides: Laeuft eine Frage, gibt es nichts mehr zu
   fragen.

   **Die Zeile ist eine Bedienflaeche und misst deshalb
   `--schaltflaeche`** - sie ist der Weg zur Frage, nicht eine Auskunft
   daneben (wie der Termintitel, FAM-84, und die Esserzeile, FAM-146). Hier
   wird nichts kleiner gerechnet; die Hoehe des Knopfes kommt aus Abschnitt 7
   und 11 (FAM-62, FAM-109). */
.fragezeile {
  display: flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  color: var(--akzent);
  font-size: 0.92em;
}

/* **Leise, nicht betont.** In einer Woche stehen bis zu vierzehn davon; ein
   gefuellter Knopf je Fach machte aus dem Plan eine Knopfleiste, und der
   haeufige Vorgang ist das **Eintragen** (FAM-145). */
.fragenknopf {
  background: none;
  box-shadow: none;
  color: var(--akzent);
  font-size: 0.92em;
  padding-left: 0;
  padding-right: 0;
}

/* --- Die Terminmaske ------------------------------------------------------ */

/* Eine Gruppe zusammengehoerender Felder. `fieldset` bringt einen eigenen
   Rahmen und eigene Abstaende mit - beides wird zurueckgesetzt, damit die
   Maske aussieht wie jede andere. */
/* **Der Kasten kostet am Handy 34 px je Gruppe** (2 x 16 Innenabstand plus
   Rahmen) - und die Terminmaske hat zwei davon. Am Handy faellt er weg; die
   `legend` traegt die Gruppe dann allein.

   Beide Werte kommen aus Regel 10, keiner ist hier ausgedacht. */
.feldgruppe {
  border: var(--feldgruppe-rahmen) solid var(--linie);
  border-radius: var(--radius);
  padding: var(--feldgruppe-innen);
  margin: 0 0 var(--abstand);
  min-width: 0;
}
.feldgruppe legend {
  padding: 0 8px;
  color: var(--text-leise);
  font-size: 0.88em;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* --- Die Feldgruppe, die sich zuklappen laesst (20.10.2026) --------------

   *„Die Bloecke Wann, Kommt das Wieder, wen geht es an bitte zum zuklappen
   machen, wenn es zugeklappt ist soll eine Zusammenfassung des Inhaltes
   neben dem Titel der Gruppierung angezeigt werden."*

   **Kein `display` am `<details>`.** Es traegt nur Rahmen, Polster und
   Abstand - **dieselben drei Werte wie `.feldgruppe`**, aus denselben
   Variablen (Regel 10): am Handy faellt der Kasten weg, der Kopf traegt die
   Gruppe dann allein. *Eine Anzeigeart am `<details>` ist die Stelle, an der
   das Aufklappen seit Chrome 131 kippt* (`11-fallstricke.md`).

   **Eine eigene Klasse und keine zweite Bedeutung von `.feldgruppe`**: Die
   gibt es im Blatt schon zweimal - als `div.feldgruppe` (ein Wrapper um EIN
   Feld) und als `fieldset.feldgruppe` (ein Kasten um eine GRUPPE). *Eine
   dritte Bauart unter demselben Namen waere die Verwechslung, die FAM-109
   teuer gelernt hat.* */
.klappgruppe {
  border: var(--feldgruppe-rahmen) solid var(--linie);
  border-radius: var(--radius);
  padding: var(--feldgruppe-innen);
  margin: 0 0 var(--abstand);
  min-width: 0;
}

/* **Die ganze Zeile ist die Bedienflaeche**, nicht das Dreieck daneben - wie
   die Filterzeile (FAM-164), die Esserzeile (FAM-146), die Artikelzeile
   (FAM-70) und der Termintitel (FAM-84).

   **44 px sind der Boden** (`--knopf-flaeche`): Hier klappt ein Finger eine
   Gruppe auf, und das ist dieselbe Trefferflaeche wie bei jedem Knopf. */
.klappgruppekopf {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px var(--abstand);
  min-height: var(--knopf-flaeche);
  cursor: pointer;
}

/* **Das Zeichen wird selbst gesetzt und wechselt** (nicht gedreht) - wie bei
   der Filterzeile (FAM-164) und der Artikelzeile (FAM-70).

   **`list-style: none` steht ausdruecklich da**, statt sich darauf zu
   verlassen, dass `display: flex` den `::marker` ohnehin loescht: In FAM-70
   war damit ein ganzer Bedienweg unauffindbar, und ein Nebeneffekt ist keine
   Zusage. */
.klappgruppe > summary { list-style: none; }
.klappgruppe > summary::-webkit-details-marker { display: none; }
.klappgruppe > summary::after { content: "\25BE"; margin-left: auto; }
.klappgruppe[open] > summary::after { content: "\25B4"; }

/* Der Name der Gruppe - **dieselbe Schreibweise wie die `legend` daneben**
   (`.feldgruppe legend`), damit eine zuklappbare Gruppe nicht aussieht wie
   eine andere Art Kasten. */
.klappgruppename {
  color: var(--text-leise);
  font-size: 0.88em;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* **Die Zusammenfassung steht nur im zugeklappten Zustand da.** Offen steht
   darunter, was sie zusammenfasst - *dasselbe zweimal nebeneinander ist eine
   Stelle zu viel*, und bei einem Tippfehler widersprechen sich die beiden.

   **Ein langer Stand bricht um**, statt die Maske zu verbreitern (Regel 2) -
   woertlich `.filternamen`. */
.klappgruppestand {
  min-width: 0;
  overflow-wrap: anywhere;
}
.klappgruppe[open] > summary .klappgruppestand { display: none; }

/* Der Inhalt ist der Flexkasten, nicht das `<details>` - siehe oben. Die
   5 px und das Fettgewicht sind die der `.feldgruppe`: *Eine zugeklappte
   Gruppe sieht aufgeklappt aus wie die Gruppen, die es vorher gab.*

   **`> .feld > label` und nicht `label`**: Ein Name in einer Ankreuzzeile
   soll NICHT fett sein (`.ankreuzzeile`, font-weight 400) - und ein
   schlichtes `.klappgruppeinhalt label` schluege diese Zeile, weil es
   staerker waehlt. */
.klappgruppeinhalt { display: flex; flex-direction: column; gap: 5px; }
.klappgruppeinhalt > .feld > label { font-weight: 600; }

/* **Der Eintragen-Knopf neben dem Griff der Eingabemaske** (20.10.2026, auf
   Wunsch: *„oben neben x Schliessen ... und nur in der Breite, die der
   Eintragen Button benoetigt"*).

   **Die Breite macht die absolute Lage.** Unten im Formular stand er
   vorher ueber die **ganze** Zeile - `.eingabemaske` ist ein Flexkasten in
   der Spalte, und dort streckt `align-items: stretch` jedes Kind. Hier
   zaehlt nur noch sein Inhalt.

   **`position` am `<details>` ist harmlos, `display` waere es nicht** -
   siehe oben. Gemessen (Chrome 152, 1280 px): Griff und Knopf stehen beide
   bei y = 29, der Knopf rechts bei x = 1168, und zugeklappt meldet
   `checkVisibility` ihn als **verborgen**. */
.neumaske { position: relative; }

/* **`.neumaske > .maskensenden` und nicht `.maskensenden`** - gemessen, und
   zwar erst am Abzug: Der Knopf stand `position: static` **eine Zeile unter
   dem Griff**, obwohl `position: absolute` im Blatt stand.
   `getComputedStyle` sagte `static`, und die Regel daneben erklaerte es:

       button, .schaltflaeche, .haupt, button.betont, ...  { all: unset; ... }

   **`all: unset` in einem Zwei-Klassen-Selektor schlaegt jede einzelne
   Klasse** - und die Liste traegt `.schaltflaeche.betont`, also **(0,2,0)**:
   Zwei Klassen reichen damit **nicht**, und bei Gleichstand gewinnt die
   spaetere Regel - die Marker-Ebene steht 4600 Zeilen weiter unten.

   **Gemessen in zwei Durchgaengen**: `.maskensenden` allein ergab
   `position: static`, `.neumaske > .maskensenden` ebenfalls - *und beide
   Male stand `absolute` im Blatt.* `button` dazu macht (0,2,1), und das
   schlaegt sie unabhaengig von der Reihenfolge.

   **Das `button` ist dabei die Zusage, nicht die Klasse**: Nimmt jemand dem
   Knopf `.schaltflaeche`, traegt er die Lage weiter - *eine Regel, die an
   einer fremden Klasse haengt, hilft der naechsten Maske nicht* (FAM-108).
   *Dieselbe Falle hat schon einem Zeichenknopf lautlos die Breite genommen*
   (`11-fallstricke.md`). */
.neumaske > button.maskensenden {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
  margin: 0;
}

/* Der Satz unter einem Feld, der sagt, was gemeint ist. Kein `.meldung` -
   die gehoert ausschliesslich in die Meldungsleiste (Regel 3).

   **Am Handy steht er nicht da, bis man im Feld steht** (FAM-109). Fuenf
   Hinweise sind dort **282 px von 1657** - der groesste einzelne Posten der
   Maske.

   **Weglassen waere die bequeme Antwort und die falsche.** Sie sagen Dinge,
   die man wissen muss: *„Lass die Uhrzeit leer, wenn es den ganzen Tag
   dauert"* IST die Bedienung des ganztaegigen Termins (FAM-83 hat dafuer
   bewusst keinen Umschalter gebaut). Und ein Kind, das die Hilfe aufschlaegt,
   bevor es etwas eintraegt, gibt es nicht.

   `:focus-within` bringt ihn zurueck, sobald der Finger im Feld ist - ohne
   eine Zeile JavaScript. **Der Preis steht dabei:** Er erscheint erst dann,
   und am Handy kann die Tastatur ihn verdecken. Wer ihn immer sehen will,
   setzt `--feldhinweis-anzeige` auf `block`.

   **Zwei Selektoren, weil die Masken es zweierlei halten**: In der
   Terminmaske steht der Hinweis als GESCHWISTER neben dem Feld, in der
   Umfrage- und Aufgabenmaske DARIN. Wer nur einen schreibt, repariert die
   halbe Anwendung - und sieht es nicht. */
.feldhinweis {
  display: var(--feldhinweis-anzeige);
  margin: -4px 0 var(--abstand);
  color: var(--text-leise);
  font-size: 0.92em;
  max-width: 62ch;
  /* In der Zeile eine eigene Zeile, keine dritte Spalte. */
  flex: 1 1 100%;
}

.feld:focus-within > .feldhinweis,
.feld:focus-within + .feldhinweis,
div.feldgruppe:focus-within > .feldhinweis { display: block; }

/* --- Die Feldhilfe hinter der Überschrift (28.09.2026, auf Wunsch) --------

   *„Die Beschreibung in Grau unter den Feldern soll ausgeblendet sein. Hinter
   der Überschrift steht ein kleines Fragezeichen, was den Hilfetext
   aufklappt."*

   **Nur in der Aufgabenmaske** – auf ausdrückliche Rückfrage. Die anderen 24
   Vorlagen behalten `--feldhinweis-anzeige` (FAM-109); *der Preis sind zwei
   Bauarten nebeneinander, bis diese sich bewährt hat.*

   **Ein `<details>`, kein Skript** (Regel 7), und **die Überschrift ist der
   Griff** – ein `?` allein wäre am Handy 13 px breit. */
.feldhilfe { margin: 0; }

/* **Der `::marker` wird selbst gesetzt** – zum sechsten Mal dieselbe Falle:
   `display: flex` an einem `<summary>` löscht ihn, und danach gibt es kein
   Zeichen mehr (FAM-136, 23.09.2026, `11-fallstricke.md`). Hier ist das
   Zeichen ein `<span>` im Markup, also wird der Marker schlicht
   abgeschaltet. */
.feldhilfe > summary {
  display: flex;
  align-items: baseline;
  gap: 6px;
  list-style: none;
  cursor: pointer;
  /* **Die Trefferfläche ist die Zeile** (FAM-63) – nicht das Zeichen. */
  min-height: var(--knopf-flaeche);
}
.feldhilfe > summary::-webkit-details-marker { display: none; }

/* Die Überschrift sieht aus wie das `<label>`, das sie ersetzt. */
.feldhilfe .feldname { font-weight: 600; }

/* **Klein, rund und leise** – wie das Fragezeichen am Seitentitel (Regel 9).
   *Es ist eine Auskunft, kein Knopf*, also trägt es keinen Textmarker
   (Abschnitt 15). */
.feldhilfe-zeichen {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4em;
  height: 1.4em;
  border-radius: 50%;
  background: var(--flaeche-leise, transparent);
  box-shadow: 0 0 0 1px var(--linie) inset;
  color: var(--text-leise);
  font-size: 0.8em;
  line-height: 1;
}
.feldhilfe[open] > summary .feldhilfe-zeichen {
  color: var(--text);
  box-shadow: 0 0 0 1px var(--text-leise) inset;
}

/* **Aufgeklappt steht der Hinweis da – auch am Handy.**

   `.feldhinweis` trägt `display: var(--feldhinweis-anzeige)`, und die steht
   dort auf `none` (FAM-109). *Ohne diese Zeile klappte das `<details>` auf
   und zeigte nichts* – und genau das wäre nicht zu sehen gewesen, weil im
   HTML der Satz so oder so steht. */
.feldhilfe[open] > .feldhinweis {
  display: block;
  margin: 4px 0 0;
}

/* **Ein `fieldset` ohne `<legend>` verliert seine Breite** – der Fund vom
   28.09.2026, und er ist eine Ecke, mit der niemand rechnet:

   Ein `<fieldset>` trägt vom Browser `min-inline-size: min-content`, und als
   **Flex-Kind** (`flex: 0 1 auto`) schrumpft es darauf. Solange die
   Überschrift ein `<legend>` war, hielt deren Text die Mindestbreite; seit
   sie der Griff eines Aufklappers ist, tut das niemand mehr.

   **Gemessen am Handy: 131 px statt 358** – die Ankreuzliste stand als
   schmale Säule neben leerem Platz, *und die Namen darin brachen um.*

   Getroffen wird genau, **wo die Überschrift keine mehr ist**
   (`[aria-labelledby]`) – die Ankreuzlisten der anderen Masken behalten
   ihren `<legend>` und brauchen die Zeile nicht. */
.ankreuzliste[aria-labelledby] { inline-size: 100%; }

/* --- Die Umfragen (FAM-99) ------------------------------------------------ */

.umfrageliste { margin: var(--abstand) 0; padding: 0; list-style: none; }

/* Wie die Aufgaben- und die Listenzeile gebaut, und mit denselben
   Merkmalsvariablen: Der Umbruch entscheidet sich nicht an der
   Bildschirmbreite, sondern an der gewaehlten Ansicht (Regel 0). */
.umfragezeile {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 4px var(--abstand);
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}

.umfragename {
  display: flex;
  align-items: center;
  /* `min-width: 0` - sonst schrumpft ein langer Titel nicht und schiebt die
     Zeile aus dem Bild. Die Lehre aus FAM-93. */
  min-width: 0;
  flex: 1 1 var(--aufgabe-titel-breite);
}

/* **Der Titel ist der einzige Weg in die Maske, also eine Bedienflaeche** -
   und die misst `var(--schaltflaeche)`, keine ausgedachte Zahl (Regel 10).
   Genau das war bei FAM-84 der Fund: Der Termintitel war 23 px hoch. */
.umfragetitel {
  display: flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  font-size: 1.05em;
  overflow-wrap: anywhere;
}

/* `flex: 0 1 …` und nicht `0 0 …` - die Lehre aus FAM-58: Eine feste Breite
   schob am Infoboard die Zeile aus dem Bild. */
.umfragestand {
  margin: 0;
  flex: 0 1 auto;
  color: var(--text-leise);
  font-size: 0.92em;
}

/* --- Die Umfragemaske ----------------------------------------------------- */

/* Die Ueberschrift ueber den festgeschriebenen Antworten. Kein `<label>` -
   es gehoert zu keinem Feld, denn genau das ist ja der Punkt: Es gibt hier
   nichts mehr einzugeben. */
.antwortueberschrift {
  margin: 0 0 4px;
  font-weight: 600;
}

.antwortliste {
  margin: 0;
  padding-left: 1.4em;
}
.antwortliste li {
  /* Kein `--schaltflaeche`: Hier wird nichts bedient, hier wird gelesen.
     Eine Bedienhoehe an einer Zeile, die nichts tut, waere ein Knopf, der
     keiner ist (die Lehre aus FAM-70). */
  padding: 2px 0;
  overflow-wrap: anywhere;
}


/* --- Abstimmen und das Ergebnis (FAM-100) --------------------------------- */

.abstimmung {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  max-width: 46ch;
  margin-top: var(--abstand);
}

/* **Die Antwort ist die Bedienflaeche, nicht das Kaestchen.** Die Hoehe kommt
   aus `.ankreuzzeile` und damit aus `--schaltflaeche` - keine erfundene Zahl
   (die Lehre aus FAM-62, der Fund aus FAM-84). Hier steht nur, was daran
   anders ist: Der Text darf umbrechen, und die Marke rutscht dann mit. */
.antwortzeile {
  flex-wrap: wrap;
  padding: 4px 0;
}
.antworttext {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65). Der Haken davor
   ist ein Zeichen, kein Ersatz: Wer rot-gruen-blind ist, sieht eine Farbe
   nicht, und am Infoboard aus zwei Metern verschwimmt sie. */
.eigenestimme {
  color: var(--text-leise);
  font-size: 0.88em;
  white-space: nowrap;
}

/* Die beiden Knoepfe unter dem Abstimmformular. Sie duerfen umbrechen - die
   Lehre aus FAM-84, wo eine Leiste ohne `flex-wrap` zwei Aktionen
   abgeschnitten hat. */
.abstimmknoepfe {
  display: flex;
  flex-wrap: wrap;
  gap: 8px var(--abstand);
}

.ergebnisueberschrift { margin: var(--abstand) 0 8px; }

.ergebnisliste { margin: 0; padding: 0; list-style: none; }

/* **Entscheiden** (FAM-101) - ein Knopf je Antwort.

   Ohne `list-style: none` stehen die Aufzaehlungspunkte des Browsers vor den
   Knoepfen: eine Liste, die wie eine Liste aussieht, obwohl sie eine Reihe
   von Bedienelementen ist. `<ul>` bleibt trotzdem richtig - ein
   Vorleseprogramm sagt dann „4 Eintraege“, und genau so viele Antworten
   stehen zur Wahl.

   **Die Knoepfe stehen untereinander**, nicht nebeneinander: Ihr Text ist
   unterschiedlich lang, und in einer Zeile waere nicht mehr zu erkennen, wo
   einer aufhoert und der naechste anfaengt. */
.entscheiden { margin-top: var(--abstand); }
.entscheidungsliste {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
/* **Der Knopf traegt seinen ganzen Satz.** `white-space: normal` ueberschreibt
   das `nowrap` von `.schaltflaeche`: „Endergebnis festschreiben: Samstagmittag
   bei Oma" passt am Handy sonst nicht in eine Zeile und stuende zur Haelfte
   ausserhalb des Fensters - die Frage aus FAM-50/FAM-84. */
.entscheidungsknopf {
  max-width: 100%;
  white-space: normal;
  text-align: left;
  height: auto;
  min-height: var(--knopf-flaeche);
  padding: 4px var(--abstand);
}

/* **Was gilt** - am Wort erkennbar, nicht an der Farbe (FAM-65). Der Kasten
   traegt deshalb keine eigene Farbe; „Entschieden: Samstag“ steht im Text. */
.entschiedenzeile { margin: 0 0 4px; }
.entschiedenwer { margin: 0 0 var(--abstand); color: var(--text-leise); }

.ergebniszeile {
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}
.ergebniszeile:last-child { border-bottom: 0; }
.ergebniszeile p { margin: 0; }

.ergebniskopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--abstand);
}
.ergebnisantwort {
  min-width: 0;
  flex: 1 1 var(--aufgabe-titel-breite);
  overflow-wrap: anywhere;
}
/* `flex: 0 1 …` und nicht `0 0 …` - die Lehre aus FAM-58: Eine feste Breite
   schob am Infoboard die Zeile aus dem Bild. */
.ergebniszahl {
  flex: 0 1 auto;
  color: var(--text-leise);
  white-space: nowrap;
}

/* Die Namen dahinter - leise, wie der Schrittstand an einer Aufgabe. Sie
   sind die eigentliche Auskunft einer Terminumfrage („wer kann am
   Freitag"), aber sie sollen die Antwort nicht ueberschreien. */
.ergebniswaehler {
  color: var(--text-leise);
  font-size: 0.92em;
  overflow-wrap: anywhere;
}

/* --- Verliehen (FAM-136) ------------------------------------------------- */

/* **Die Eintragezeile steht oben und ist das Erste, was man sieht.**

   Man traegt es in dem Moment ein, in dem man es aus der Hand gibt - an der
   Haustuer, mit dem Gegenstand in der anderen Hand. Wer dafuer erst scrollen
   oder eine Maske oeffnen muss, traegt es spaeter ein, und spaeter heisst
   nie (dieselbe Ueberlegung wie beim Einkauf, FAM-70).

   **Drei Felder statt zwei sind hier die Schwierigkeit.** Am Handy ist die
   Zeile eng - deshalb bricht sie um, und deshalb hat jedes Feld ein
   `min-width`: Ohne das darf ein Flex-Element auf **null** schrumpfen, und
   dann steht dort einfach kein Feld (FAM-56, FAM-64). */
/* **Die alte Eintragezeile ist weg** (27.09.2026, Design 2a A6/B3).

   Hier standen vier Regeln fuer `.leihe-neu` - drei Felder und ein Knopf in
   **einer Zeile**, mit `flex-wrap: wrap` und je einer Flex-Basis in `ch`.
   Die Maske liegt seit Design 2a hinter dem Haupt-Knopf und stellt ihre
   Felder in ein Raster (`.eingabemaske`, Abschnitt 17).

   **Stehen geblieben waeren sie nicht harmlos**: `flex: 1 1 14ch` teilt in
   einer Zeile die Breite und in einer Spalte die **Hoehe** - gemessen war
   das Feld *An wen?* damit **164 px hoch** statt 45. *Eine Regel ohne Leser
   ist keine Reserve.* */

/* --- Die Gruppen --------------------------------------------------------- */

.leihgruppe { margin-top: calc(var(--abstand) * 1.5); }
.leihgruppe > h2 {
  margin: 0 0 6px;
  /* **Farbe, Grossbuchstaben und Sperrung sind weg** (27.09.2026) - siehe
     `.erledigte h2`. */
}

.leihliste { list-style: none; margin: 0; padding: 0; }

/* Eine Zeile je Leihe: Haken, Text, dahinter die Felder. */
.leihe {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: flex-start;
  gap: 6px var(--abstand);
  padding: 8px 0;
  border-bottom: 1px solid var(--linie);
}
.leihe form { display: flex; gap: 6px; align-items: center; }

/* **Zurueckgegebenes ist leiser, aber nicht weg** - es ist die Geschichte,
   nicht die Arbeit. Und es ist **nicht nur die Farbe**: Der Haken daneben
   wechselt mit (FAM-65). */
.leihe.zurueck .leihe-was { color: var(--text-leise); }

/* --- Die Zeile selbst ---------------------------------------------------- */

/* `<details>` haelt die Aenderungsfelder zugeklappt - der haeufige Vorgang
   ist das Zurueckbuchen, der seltene das Korrigieren (FAM-70). */
.leihe-aendern { flex: 1 1 auto; min-width: 0; }

.leihe-aendern > summary {
  /* **`list-style: none` steht hier ausdruecklich.**

     `display: flex` an einem `<summary>` loescht den `::marker` des Browsers
     ohnehin (gemessen in FAM-70) - und genau darauf soll sich niemand
     verlassen: Das Zeichen wird unten selbst gesetzt, und dann darf das des
     Browsers nicht daneben stehen (FAM-107). */
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px var(--abstand);
  padding: 4px 0;
  cursor: pointer;
  /* Der ganze Kopf ist die Schaltflaeche - am Handy soll man nicht das
     Zeichen treffen muessen (Regel 10). */
  min-height: var(--schaltflaeche);
}
.leihe-aendern > summary::-webkit-details-marker { display: none; }

/* **Das Zeichen wird selbst gesetzt und wechselt** (nicht gedreht) - wie bei
   der Artikelzeile (FAM-70) und beim Menue (FAM-107). */
.leihe-aendern > summary::after {
  content: "▾";
  margin-left: auto;
  color: var(--text-leise);
}
.leihe-aendern[open] > summary::after { content: "▴"; }

.leihe-text {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 0.6em;
  min-width: 0;
}
.leihe-was { font-weight: 600; overflow-wrap: anywhere; }
.leihe-anwen { color: var(--text-leise); overflow-wrap: anywhere; }

/* **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65): Wer
   rot-gruen-blind ist, sieht das Rot nicht, und am Infoboard aus zwei Metern
   verschwimmt es. Dort steht deshalb „ueberfaellig seit ...“, und die Farbe
   kommt nur dazu. */
.leihe-frist {
  color: var(--text-leise);
  font-size: 0.92em;
  white-space: nowrap;
}
.leihe-frist.ueberfaellig {
  color: var(--fehler);
  font-weight: 600;
}

/* --- Dahinter: aendern und wegnehmen ------------------------------------- */

.leihe-werkzeug {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  padding: 6px 0 2px;
}
/* **Die zwei Textfelder nehmen ihre Zeile** (17.10.2026, auf Wunsch: *„Die
   Felder, die Text aufnehmen, sollten versuchen die Breite auszunutzen."*).

   **Hier standen vier Zeilen, von denen drei nichts taten** - `flex-wrap`
   und zwei `flex`-Angaben an den Kindern, *ohne ein `display: flex` am
   Formular*. Gemessen war der computed `display` **`block`**; was die
   Felder wirklich breit machte, war die Browservoreinstellung: ein
   `<input>` ohne Breitenregel ist **20 Zeichen** breit, also 164 px am
   Handy und 350 am Infoboard. *Eine fehlende Regel greift die
   Browservoreinstellung* - die Falle steht im Index, und das ist ihr
   sechster Fall.

   **Gemessen vorher / nachher** (Breite des Feldes im 296-px-Kasten):

   | Ansicht | vorher | nachher |
   |---|---|---|
   | Handy | 164 | **296** |
   | Tablet | 242 | **439** |
   | Desktop | 238 | **432** |
   | Infoboard | 350 | **519** |

   **Warum nicht die Zeile reparieren** (`display: flex`, wie es die vier
   Zeilen gemeint haben): Dann teilten sich *Was* und *An wen* **eine**
   Zeile und bekaemen gemessen **154 und 136 px** - weniger als die 164 von
   vorher. *Der Wunsch war mehr Breite, nicht eine Zeile weniger.*

   **Das Datum und der Knopf bleiben, wo sie waren**: Sie tragen ihre
   Breite selbst (Kalendersymbol, Wort) - *ein Datumsfeld ueber die ganze
   Breite ist kein Datumsfeld mehr.* */
.leihe-felder > input[type="text"] { width: 100%; }

/* **Der Block „Wieder da“ hat sein Aufklappzeichen hier verloren - und
   zwar an die Allgemeinheit.**

   Es stand von FAM-136 bis zum 23.09.2026 hier, hinter `.leihgruppe`, und
   der Kommentar sagte dazu, der Erledigt-Block der Aufgaben habe *vermutlich
   dasselbe Problem*. **Er hatte es**, zwei Karten lang, und niemandem fiel
   es auf: Eine Entwicklungsdatenbank ohne erledigte Aufgabe zeigt den Block
   gar nicht erst.

   Die Regel steht jetzt bei `.erledigte` selbst (Abschnitt 5) und gilt fuer
   beide - *eine Regel hinter einer fremden Klasse hilft der naechsten Maske
   nicht* (FAM-85, FAM-108). **Hier steht sie nicht ein zweites Mal**: Zwei
   Stellen fuer dasselbe Zeichen laufen auseinander, und man saehe es erst,
   wenn ein Block auf- und der andere zuzeigt. */

/* Wegnehmen ist die seltene, unumkehrbare Aktion - sie steht hinten und
   leise, aber sie traegt die volle Bedienflaeche (Regel 10). */
.leihe-wegknopf {
  margin-left: auto;
  color: var(--text-leise);
}
.leihe-wegknopf:hover { color: var(--fehler); }


/* --- Die Geburtstage (FAM-130) --------------------------------------------

   **Keine einzige Pixelzahl, die eine Merkmalsvariable haette** (Regel 0):
   Wer am Desktop die Ansicht *Handy* waehlt, bekommt die Handy-Masse - und
   ein hier hingeschriebener Wert bliebe dabei stehen.

   **Und kein `@media`**: Die Haltepunkte stehen an einer Stelle in Abschnitt
   1 und setzen dort die Variablen. Eine Media Query in einem Maskenabschnitt
   kennt die Uebersteuerung nicht. */

.geburtstagsliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.geburtstag {
  border-bottom: 1px solid var(--linie);
}
.geburtstag:last-child { border-bottom: 0; }

/* **Der ganze Text ist der Weg in die Maske**, nicht nur der Name: Am Handy
   soll man nicht ein Wort treffen muessen. Er misst deshalb genau
   `--schaltflaeche` - er ist die Bedienflaeche (die Lehre aus FAM-84, wo ein
   23 px hoher Termintitel der einzige Weg in die Maske war). */
.geburtstag-text {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px var(--abstand);
  min-height: var(--schaltflaeche);
  padding: 4px 0;
  color: inherit;
  text-decoration: none;
}
.geburtstag-text:hover { text-decoration: underline; }

/* **Umbrechen statt abschneiden** (die Lehre aus FAM-84): `min-width: 0`
   laesst den Namen schrumpfen, `overflow-wrap` bricht ein langes Wort - und
   deutsche Namen sind lang. */
.geburtstag-name {
  font-weight: 600;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65) - beim
   Hochzeitstag steht es da, beim Geburtstag nicht: Ein Wort, das immer
   dasteht, wird ueberlesen (FAM-94). */
.geburtstag-art {
  color: var(--text-leise);
  font-size: 0.92em;
}

.geburtstag-tag { white-space: nowrap; }

.geburtstag-wann {
  color: var(--text-leise);
  white-space: nowrap;
}

/* **Heute hebt sich ab - mit einem Wort und mit Farbe**, nicht nur mit
   Farbe: Wer rot-gruen-blind ist, sieht sie nicht, und am Infoboard aus zwei
   Metern verschwimmt sie. Dort steht deshalb „Heute!“. */
.geburtstag-wann.heute {
  color: var(--akzent);
  font-weight: 700;
}
.geburtstag.istheute { background: var(--akzent-blass); }

/* **Ohne Jahrgang steht dort ein Satz und keine Zahl** (Regel 5) - er steht
   leise, weil er eine Auskunft ist und keine Angabe. */
.geburtstag-alter { white-space: nowrap; }
.geburtstag-alter.leise { color: var(--text-leise); font-size: 0.92em; }

/* Das Alter steht am Ende der Zeile, wo Platz ist - am Handy rutscht es mit
   um, statt abgeschnitten zu werden. */
.geburtstag-alter { margin-left: auto; }


/* --- Der Chat (FAM-123) ---------------------------------------------------

   **Keine einzige Pixelzahl, die eine Merkmalsvariable haette** (Regel 0):
   Wer am Desktop die Ansicht *Handy* waehlt, bekommt die Handy-Masse - und
   ein hier hingeschriebener Wert bliebe dabei stehen.

   **Und kein `@media`**: Die Haltepunkte stehen an einer Stelle in Abschnitt
   1 und setzen dort die Variablen. Eine Media Query in einem Maskenabschnitt
   kennt die Uebersteuerung nicht.

   **Und keine Zeile JavaScript.** Das Feld bleibt ueber `position: sticky`
   unten stehen, und beim Oeffnen scrollt ein **Anker** ans Ende - das ist
   eine Adresse, kein Skript. */

/* Der Bereich, den die Sofortanzeige austauscht (FAM-124).

   **Er erzeugt keinen Kasten.** `display: contents` heisst: Das Element
   selbst verschwindet aus der Darstellung, seine Kinder ruecken an seine
   Stelle - der Verlauf steht damit genau dort, wo er vor FAM-124 stand.

   **Das ist der Grund, warum eine Umrahmung ueberhaupt moeglich war, ohne
   die Maske neu zu vermessen.** Ein gewoehnlicher `<div>` waere der
   Elternkasten des klebenden Eingabefeldes geworden und haette dessen
   Grenzen verschoben; hier gibt es keinen. Gemessen in allen vier Ansichten:
   dieselben Zahlen wie bei FAM-123 (`docs/Test/FAM-124.md`). */
.chatbereich { display: contents; }

/* Der Weg zu den aelteren Beitraegen - er steht ueber dem Verlauf, weil
   dorthin gehoert, was davor war. */
.chat-aeltere {
  margin: 0 0 var(--abstand);
  text-align: center;
}

.chatverlauf {
  list-style: none;
  margin: 0;
  /* **Oben und links bleibt Platz fuer das Ausrufezeichen** (11.10.2026).
     Es ragt 4 px ueber die Ecke seines Beitrags - und `.inhalt` schneidet
     waagerecht (`overflow-x: hidden`), senkrecht scrollt es. **Gemessen
     beginnt ein FREMDER Beitrag genau an der Kante von `.inhalt`**, das
     Zeichen laege also davor und waere weg.

     *Ein Ueberstehen ist nur dort ein Abschneiden, wo `overflow` nicht
     `visible` ist* (`11-fallstricke.md`) - und hier ist es das. **Am
     eigenen Beitrag faellt es nicht auf**, denn der steht rechts; genau
     deshalb zeigt der Abzug es nicht von selbst.

     Dieselbe Zahl wie beim Zeichen und bei den Punkten: zweimal die
     Rundung. */
  padding: calc(var(--radius-klein) * 2) 0 0 calc(var(--radius-klein) * 2);
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) / 2);
}

/* **Der eigene Beitrag steht rechts, fremde links** - und das ist eine von
   drei Unterscheidungen, von denen keine die Farbe braucht (FAM-65): dazu das
   Wort „Du“ und der andersfarbige Grund.

   `max-width: 80%` laesst den jeweils anderen Rand frei - ohne ihn stuenden
   beide Seiten uebereinander und die Ausrichtung sagte nichts mehr aus. */
.chatbeitrag {
  align-self: flex-start;
  max-width: 80%;
  padding: calc(var(--abstand) / 2) var(--abstand);
  /* **Kein Rand und ein kleiner Radius** (11.10.2026, auf einen Kommentar
     im Canvas: *„Der kasten soll nur einen kleinen Radius an den Ecken
     haben und keine Rand."*).

     **Der Rand wird nicht gebraucht, seit es einen Schatten gibt**: Er und
     der Schlagschatten sagten dasselbe zweimal - *zwei Mittel fuer eine
     Aussage sind eines zu viel.* Was den Kasten jetzt vom Grund trennt,
     ist der Schatten und die Flaeche: Weiss auf `--grund`.

     **`--radius-klein` ist ein neues Merkmal, keine Zahl im Blatt**
     (Regel 0): 6 px am Handy, 12 am Infoboard. *Ein fester Wert saehe am
     Infoboard aus wie eine scharfe Kante* - ein Radius gehoert zu den
     Dingen, die mit der Ansicht wachsen, wie `--radius` und
     `--radius-karte` daneben. */
  border: 0;
  border-radius: var(--radius-klein);
  background: var(--flaeche);
  /* **Der Bezug fuer die drei Punkte in der Ecke** (11.10.2026). */
  position: relative;
  /* **Ein kleiner Schlagschatten** (11.10.2026, auf einen Kommentar im
     Canvas: *„Der Ganze Kasten soll einen kleinen Schlagschatten
     bekommen."*).

     **`--kartenschatten` und keine eigene Zahl**: Denselben Schatten tragen
     die Kachel des Wetters, die Grundkarte und die Projektkarte - *eine
     zweite Rezeptur fuer dieselbe Erhebung liefe auseinander* (Regel 10).

     Er wirkt ueberhaupt erst, seit der Grund dahinter durchsichtig ist
     (heute frueh): *Ein Schatten auf einer weissen Platte, die genauso
     weiss ist wie die Karte, ist keiner.* */
  box-shadow: var(--kartenschatten);
}


.chatbeitrag.eigen {
  align-self: flex-end;
  background: var(--akzent-blass);
  /* **Ein Stueck von der rechten Kante weg** (11.10.2026, auf Wunsch: *„die
     rechten posts ein wenig nach links verschieben"*).

     **`--abstand` und keine eigene Zahl**: Es ist derselbe Wert, den die
     Karte als Polster traegt und den die Liste zwischen zwei Beitraegen
     laesst - *ein Rand, der zu den Abstaenden daneben passt, sieht nach
     Absicht aus; eine eigene Zahl sieht nach einer eigenen Zahl aus.* Er
     waechst mit der Ansicht (10 / 20 / 20 / 32 px, Regel 10).

     **Nur rechts.** Die fremden Beitraege stehen links und bleiben, wo sie
     sind - gewuenscht war die eine Seite. */
  margin-right: var(--abstand);
}

/* **Ein wichtiger Beitrag traegt einen roten Rand, nicht nur ein Zeichen**
   (11.10.2026) - *der Kasten soll von weitem auffallen, und am Infoboard
   aus zwei Metern ist ein Ausrufezeichen von 14 px nichts.*

   **Die Farbe ist `--fehler`, und das ist Absicht**: Rot bedeutet in dieser
   Anwendung *„hier schau hin"* (Regel 15, der Gefahr-Marker) - **und es
   bedeutet nicht Gefahr**, sondern Dringlichkeit; das Wort daneben sagt,
   was gemeint ist (FAM-65).

   **Diese Regel steht NACH `.eigen`, und das ist der ganze Punkt.** Beide
   sind (0,2,0) schwer, also entscheidet die Reihenfolge im Blatt - *stand
   sie davor, gewann am eigenen Beitrag der blasse Rand, und der rote kam
   nur bei fremden durch.* **Gemessen**: `oklch(0.96 0.03 265)` statt
   `--fehler`, und auf dem Bild sah es nach Absicht aus (`11-fallstricke.md`,
   *„eine alte Regel weiter unten im Blatt gewinnt gegen die neue"*). */
/* **Ein gestrichelter Strich, und nur rechts** (11.10.2026, auf einen
   Kommentar im Canvas: *„Den roten rand schmaler machen und Gestrichelt und
   nur auf der rechten Seite anzeigen"*).

   **Dafuer muss es ein `border` sein, kein Schatten** - hier stand bis
   heute ein `inset`-Ring, und *ein `box-shadow` kennt kein `dashed`.* Der
   Kasten wird dadurch 1 px breiter; das ist der Preis, und er ist kleiner
   als die 2 px, die der Ring vorher nach innen genommen hat.

   **1 px statt 2**, und das ist woertlich das „schmaler": Ein gestrichelter
   Strich faellt bei gleicher Breite ohnehin leiser aus als ein
   durchgezogener - *die Luecken nehmen ihm die Haelfte.*

   **Diese Regel steht NACH `.eigen`, und das ist der ganze Punkt.** Beide
   sind (0,2,0) schwer, also entscheidet die Reihenfolge im Blatt - *stand
   sie davor, gewann am eigenen Beitrag der blasse Rand, und der rote kam
   nur bei fremden durch.* **Gemessen**: `oklch(0.96 0.03 265)` statt
   `--fehler`, und auf dem Bild sah es nach Absicht aus (`11-fallstricke.md`,
   *„eine alte Regel weiter unten im Blatt gewinnt gegen die neue"*).

   **Der Kartenschatten steht hier nicht mehr dabei**: Er kommt aus
   `.chatbeitrag` und wurde nur wiederholt, weil der Ring in derselben
   Eigenschaft sass. *Eine Zeile, die einen Wert wiederholt, laeuft mit ihm
   auseinander.* */
.chatbeitrag.wichtig {
  border-right: 1px dashed var(--fehler);
}

/* **Das Zeichen ragt ueber die Ecke** (11.10.2026, auf denselben Kommentar:
   *„Das Ausrufezeichen als Symbol, was ein klein wenig links und Oben ueber
   den Rand ragt"*).

   **Negative Raender, keine absolute Lage**, und das ist der Unterschied,
   der den Namen rettet: Im Fluss behaelt das Zeichen seine Breite, also
   rueckt der Name von selbst daneben. *Absolut gesetzt laege es ueber dem
   ersten Buchstaben* - und der sitzt seit heute Vormittag genau dort, in
   der Ecke (0 px von oben, 0 von links).

   **Dieselbe Zahl wie bei den Punkten**: zweimal die Rundung. Der Kasten
   hat kein `overflow`, also steht das Zeichen wirklich davor und wird nicht
   abgeschnitten (`11-fallstricke.md`, *„ein Ueberstehen ist nur dort ein
   Abschneiden, wo `overflow` nicht `visible` ist"*). */
.chat-wichtig {
  display: inline-flex;
  color: var(--fehler);
  margin-top: calc(var(--radius-klein) * -2);
  margin-left: calc(var(--radius-klein) * -2);
}

/* **Die Kopfzeile ist eine duenne Zeile ganz oben** (11.10.2026, auf einen
   Kommentar im Canvas: *„Der Name und wann es geschickt wurde viel kleiner
   und nach ganz oben. Der Text soll viel Platz bekommen."*).

   **Der Abstand ist der halbe**, und die Zeile traegt nur noch sich selbst:
   Alles, was Hoehe kostet, ist aus ihr heraus. */
.chat-kopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 calc(var(--abstand) / 2);
  /* **Die Zeile sitzt in der Ecke, nicht im Polster** (11.10.2026, auf
     einen Kommentar im Canvas: *„Viel kleiner und oben links in die
     Ecke"*). Sie wird mit negativen Raendern an die Kante gezogen - *so
     bleibt sie im Fluss, und der Text rutscht nach*; absolut gesetzt
     laege sie ueber ihm.

     **Spiegelbildlich zu den drei Punkten**, die seit heute auf 0 px in
     der rechten Ecke sitzen. */
  margin-top: calc(var(--abstand) / -2);
  margin-inline: calc(var(--abstand) * -1);
  margin-bottom: 2px;
  /* **Buendig links, nicht mit dem Text fluchtend** (11.10.2026): Ein
     `padding-left` von `--abstand` setzte den Namen auf dieselbe Kante wie
     den Text - gemessen 10 px vom Rand. *Gewuenscht ist die Ecke*, und die
     ist bei 0. Der Text ruecken bleibt, wo er war; die Kopfzeile haengt
     darueber wie eine Marke. */
  padding-left: 0;
  padding-right: var(--knopf-flaeche);
}

/* **Der Name schiebt alles andere nach rechts.** `margin-left: auto` am
   letzten Element waere die naheliegende Loesung und die falsche: Das letzte
   ist mal die Uhrzeit, mal der Wegknopf, mal nichts von beidem - die Uhrzeit
   laesst sich ausblenden (Regel 8). So haengt es am Namen, und der steht
   immer da. */
.chat-wer {
  font-weight: 600;
  /* **Ein Buchstabe Abstand zur Kante** (11.10.2026, auf einen Kommentar im
     Canvas: *„Ein Buchstaben nach recht."*).

     **`1ch` und keine Pixelzahl**: Ein Buchstabe ist eine Angabe in Schrift,
     und `ch` ist die Breite der Null - gemessen rund 3,9 px bei dieser
     Schriftgroesse. *Wer hier 4px schreibt, hat beim naechsten
     Schriftwechsel wieder einen anderen Buchstaben.*

     **Am Namen und nicht am Polster der Kopfzeile**, und das ist der Punkt:
     Die Kopfzeile traegt bei einem wichtigen Beitrag das Ausrufezeichen,
     das 4 px nach LINKS ueber die Kante ragen soll. Ein Polster an ihr
     schoebe es zurueck ins Feld - gerechnet auf 0,1 px, also genau auf die
     Kante, und der Ueberstand von vorhin waere weg. */
  margin-left: 1ch;
  margin-right: auto;
  min-width: 0;
  overflow-wrap: anywhere;
  /* **Viel kleiner, zum zweiten Mal** (11.10.2026): 0,65em statt 0,75.
     *Name und Uhrzeit bleiben gleich gross* - sie sind eine Zeile, und
     zwei Groessen darin saehen aus wie ein Versehen. */
  /* **Zwei Punkt dazu** (11.10.2026) - siehe `.chat-text`. Das `em` rechnet
     weiterhin gegen die Karte, die ihre Schriftgroesse nicht geaendert hat;
     nur so ist der Zuwachs wirklich 2 pt und nicht 65 % davon. */
  font-size: calc(0.65em + 2pt);
}

.chat-wann {
  color: var(--text-leise);
  font-size: calc(0.65em + 2pt);
  white-space: nowrap;
}

/* **Umbrechen statt abschneiden** (die Lehre aus FAM-84): `overflow-wrap`
   bricht ein langes Wort - und eine hineinkopierte Adresse ist ein langes
   Wort. Ohne das waere die Seite breiter als der Bildschirm, und die scrollt
   nicht seitwaerts (Regel 2). */
/* **Zwei Punkt groesser** (11.10.2026, auf Wunsch: *„Im Chat alle Texte auf
   der Karte um 2 pt vergroessern"*).

   **`2pt` steht wirklich da, und das ist der Punkt.** CSS kennt die Einheit
   (1 pt = 4/3 px), also heisst `calc(1em + 2pt)` genau das, was gewuenscht
   war - *nicht eine Pixelzahl, die ich aus 2 pt ausgerechnet und dann als
   Zahl hingeschrieben haette.* Wer nachsehen will, was hier gemeint ist,
   liest die Einheit.

   **Addiert, nicht am Kasten gedreht.** Die naheliegende Loesung waere, der
   Karte selbst eine groessere Schrift zu geben - dann waechst alles darin
   mit. **Gemessen waere das aber nicht „2 pt groesser"**: Name und Uhrzeit
   stehen auf `0.65em`, sie bekaemen also nur 65 % des Zuwachses. *Zwei Punkt
   fuer jeden Text heisst zwei Punkt fuer jeden Text.* */
.chat-text {
  margin: 0;
  overflow-wrap: anywhere;
  font-size: calc(1em + 2pt);
}

/* --- Woher ein Beitrag kommt (FAM-273, 06.11.2026) -----------------------

   *„zum Termin"* ueber dem Satz, wenn der Beitrag an einer Sache haengt.

   **Keine neue Zahl und keine neue Farbe** (Regel 10): `--text-leise` und
   `0.85em` sind dieselben Werte wie bei `.tagnaehe` (25.10.2026) - *das
   kleine Wort neben der Auskunft, nicht an ihrer Stelle.*

   **Ueber dem Text, nicht darunter.** Es sagt, wovon die Rede ist, und das
   liest man vor dem Satz - wie das Quellwort am Kalendereintrag (FAM-87). */
.chat-quelle {
  margin: 0 0 2px;
  font-size: 0.85em;
  color: var(--text-leise);
}

/* Das Wegnehmen steht am Beitrag. Der Knopf ist ein `button` und erbt damit
   `height: var(--schaltflaeche)` aus Abschnitt 7 - **er ist die
   Bedienflaeche, nicht das 13 px grosse Kreuz darin** (die Lehre aus
   FAM-115). */
.chat-weg { display: flex; }

/* --- Das Menue am Beitrag (11.10.2026) -----------------------------------

   **Drei Punkte oben rechts, und dahinter ein `<details>`** - *„klickt man
   auf die 3 Punkte geht ein Menue auf, was man machen moechte."*

   **Ohne eine Zeile JavaScript** (Regel 7), dieselbe Bauart wie die
   Feldhilfe und der Wagen. **Das `::marker` muss weg**, und das ist die
   Falle, die in diesem Projekt fuenfmal zugeschlagen hat: *`display: flex`
   an einem `<summary>` loescht es* - hier wird es ausdruecklich genommen,
   statt sich darauf zu verlassen. */
/* **Die Punkte haengen in der Ecke, sie stehen nicht in der Zeile**
   (11.10.2026, nachgebessert): Mit `min-height: var(--knopf-flaeche)` war
   die Kopfzeile **44 px hoch, egal wie klein die Schrift darin war** -
   *eine Zeile, die ihre Hoehe von einem Knopf bekommt, wird durch kleinere
   Schrift nicht duenner.* Jetzt liegt der Knopf absolut in der Ecke, und
   die Zeile ist so hoch wie ihr Text. */
.chatmenue {
  position: absolute;
  /* **In die Ecke, aber nicht auf die Kante** (11.10.2026, auf einen
     Kommentar im Canvas: *„ein klein wenig wieder nach links und nach
     unten. Wenn der Rand rot wird, dann kann man die Punkte nicht mehr
     richtig sehen"*).

     `top/right: 0` bezieht sich auf den **Polsterkasten**, nicht auf den
     Inhalt - der Innenabstand des Beitrags zaehlt hier also nicht mit.
     Bei `0` lag der rechte Punkt damit **auf dem roten Ring**, den ein
     wichtiger Beitrag traegt (`inset 0 0 0 2px var(--fehler)`), und *grau
     auf rot ist kein Zeichen mehr, sondern ein Fleck*.

     **Der Abstand ist der Ring, zweimal** - keine neue Zahl:
     `--radius-klein` ist seit heute 2 px, und der Ring ist 2 px breit -
     dieselbe Kante, einmal als Rundung und einmal als Strich. *Wer den Ring
     dicker macht, rueckt die Punkte mit.*

     **Zweimal und nicht einmal, und das ist gemessen**: Das Zeichen traegt
     selbst schon 1,23 px leeren Rand (die Kreise sitzen bei cx 6/12/18 in
     einem Feld von 24). Bei einem Ring Abstand blieben der Tinte rechts
     **1,23 px** Luft, bei zwei sind es **3,23** - und oben 3,18 gegen 5,18.
     *Beide Seiten haben damit dieselbe Luft*, und genau an beiden hat der
     Kommentar gehangen. */
  top: calc(var(--radius-klein) * 2);
  right: calc(var(--radius-klein) * 2);
}

/* **Gewachsen ist die Flaeche, nicht das Zeichen** - woertlich die Loesung
   des Hilfe-Fragezeichens vom 09.10.2026: Ein `min-width` am Knopf selbst
   machte aus drei Punkten einen 44 px grossen Fleck. Das `::after` liegt
   unsichtbar darueber und traegt die 44. */
.chatmenue-knopf {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  /* **Kein Polster mehr** (11.10.2026): Es hielt das Zeichen 2 px von der
     Kante weg, und gewuenscht ist *„ganz oben rechts in die Ecke"*. Die
     Trefferflaeche haengt ohnehin nicht daran - die traegt das `::after`. */
  padding: 0;
  /* **Genau so gross wie die Uhrzeit daneben** (11.10.2026, auf einen
     Kommentar im Canvas: *„in der gleichen Groesse wie die Zeitangabe und
     auch in der gleichen Schriftfarbe"*).

     **Zwei Zeilen, und beide sind noetig**: `.symbol` traegt sonst 1,25em,
     waere also auch bei gleicher Schriftgroesse ein Viertel groesser als
     der Text daneben. Hier ist es **1em von 0,65em** - dieselbe Zahl, die
     `.chat-wann` als Schriftgroesse hat, gemessen 7,8 px am Handy.

     **Die Farbe war schon dieselbe** (`--text-leise`) und bleibt es - die
     Zeile steht hier, damit der Zusammenhang sichtbar ist: *Wer eine der
     beiden aendert, sieht die andere.* */
  /* **Mitgewachsen, und das ist keine Zugabe, sondern eine Zusage**: Die
     Punkte sollen so gross sein wie die Uhrzeit (11.10.2026, auf einen
     Kommentar im Canvas). Waere hier `0.65em` stehengeblieben, waere genau
     das beim naechsten Wunsch still gebrochen worden. */
  font-size: calc(0.65em + 2pt);
  color: var(--text-leise);
  cursor: pointer;
  list-style: none;
}
.chatmenue-knopf .symbol { width: 1em; height: 1em; }

.chatmenue-knopf::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: var(--knopf-flaeche);
  height: var(--knopf-flaeche);
  border-radius: 50%;
}
.chatmenue-knopf::-webkit-details-marker { display: none; }
.chatmenue-knopf::marker { content: ""; }
.chatmenue-knopf:hover { color: var(--text); }

/* **Die Tafel haengt ueber dem Verlauf, sie schiebt ihn nicht auseinander.**
   *Ein Menue, das die Liste laenger macht, verschiebt beim Oeffnen alles
   darunter* - und man verliert die Zeile, die man gerade meinte. */
.chatmenue-tafel {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: calc(var(--abstand) / 2);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  box-shadow: var(--menuetafel-schatten);
  white-space: nowrap;
}

.chat-wegknopf {
  border: 0;
  background: none;
  color: var(--text-leise);
  padding: 0 calc(var(--abstand) / 2);
}
.chat-wegknopf:hover { color: var(--fehler); background: none; }

/* **Das Eingabefeld bleibt unten stehen, wie im Vorbild.**

   `position: sticky` kostet keine Zeile JavaScript - und `bottom: 0` haelt
   es am unteren Rand des Bereichs, der wirklich scrollt (`.inhalt`, gemessen
   in FAM-100).

   **Der Grund ist nicht durchsichtig**, und das ist Absicht: Sonst liefen die
   Beitraege beim Scrollen sichtbar dahinter durch. */
/* **Kein `sticky` mehr** (11.10.2026): Das Kleben war dafuer da, dass das
   Feld beim Scrollen stehen bleibt. Seit es ausserhalb des Scrollbereichs
   liegt, scrollt nichts daran vorbei - *und ein `sticky` ohne scrollenden
   Vorfahren ist eine Zeile, die nichts tut.* */
.chat-neu {
  display: flex;
  /* **Die Vorschau bekommt eine eigene Zeile** (11.10.2026): Sie steht im
     selben Formular wie Feld und Knopf, und ohne `wrap` quetschte sie sich
     daneben. Ist sie `hidden`, bleibt es bei einer Zeile. */
  flex-wrap: wrap;
  gap: var(--abstand);
  margin-top: var(--abstand);
  padding: var(--abstand) 0;
  background: var(--grund);
  border-top: 1px solid var(--linie);
}

/* **`flex: 1 1 auto` mit `min-width: 0`** - eine `width` waere hier kein
   `flex-basis` (die Lehre aus FAM-64), und ohne `min-width: 0` schrumpft ein
   Flex-Element nicht unter seinen Inhalt: Am Handy waere der Knopf daneben
   dann ausserhalb des Bildschirms, und die Seite scrollt nicht seitwaerts
   (FAM-136). */
.chat-neu input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
}

/* Der Anker am Ende (`#unten`). Er zeigt nichts an - er ist die Stelle, an
   die die Umleitung springt. */
.chat-unten { display: block; }

/* Wenn nichts mehr nachkommt (FAM-124).

   **Sie steht ueber dem Eingabefeld**, also dort, wo der Blick ohnehin ist -
   nicht oben auf der Seite, wo sie niemand sieht, der gerade den letzten
   Beitrag liest (FAM-79).

   Das Aussehen kommt von `.hinweiskasten.warnung`; hier steht nur der
   Abstand nach unten, damit sie nicht am Feld klebt. */
.chat-ausfall { margin-bottom: var(--abstand); }

/* Die Personenfarben - der Name traegt sie, sonst nichts. **Nie eine Farbe
   im Markup** (Regel 10): Die Vorlage setzt nur den TON, die Rezeptur steht
   in Abschnitt 1.

   **Sechs Zeilen sind eine geworden** (22.09.2026) - und das ist der Grund,
   warum sich jede Farbe waehlen laesst, ohne dass das Blatt waechst. */
.chat-wer.gefaerbt { color: var(--personenfarbe); }

/* **Wer nicht mehr da ist, traegt keine Farbe** - „ehemaliges Mitglied“ ist
   keine Person mehr, und eine Farbe daran waere eine Auskunft, die es nicht
   gibt. Die Vorlage laesst `.gefaerbt` dann weg. */
.chat-wer { color: var(--text-leise); font-weight: 500; }


/* ===========================================================================
   VORBEMERKUNG ZU ABSCHNITT 11 - was hier NICHT uebernommen worden ist
   ===========================================================================
   Der Entwurf kommt aus dem Design-Canvas (18.09.2026, „Jugendlich frisches
   Masken-Design"). Drei Stellen sind bewusst anders als dort:

   * **Die Schrift bleibt die Systemschrift.** Der Entwurf laedt *Outfit* von
     `fonts.googleapis.com` - das verstoesst gegen Regel 4 und
     `09-compliance.md`: Der Browser jedes Familienmitglieds gaebe seine
     IP-Adresse an einen Dritten, bei einer Anwendung mit Kinderdaten. Wer
     die Schrift will, liefert sie selbst aus.
   * **Die Personenwahl ist nicht dabei.** Der Entwurf macht aus der
     Ankreuzliste der Aufgabenmaske einen Aufklapper mit Chips. Das aendert
     die **Bedienung**, nicht das Aussehen, und gehoert in eine eigene Karte;
     die Regeln dafuer ohne ihr Markup einzubauen waere vorgebaute
     Oberflaeche (FAM-73).
   * **Die Kachel-Ueberschrift am Infoboard** bleibt, wie sie war - der
     Befund aus FAM-126 ist weiterhin offen.

   **Dies ist eine EBENE, und das hat einen Preis.** Sie steht hinter den
   Abschnitten 1 bis 10 und gewinnt deshalb gegen sie - genau der
   Mechanismus, an dem FAM-107 gestolpert ist (*alte Regeln weiter unten im
   Blatt gewinnen*). Wer in Abschnitt 8 eine Zeile aendert und sie nicht
   wirken sieht, sucht sie hier.

   *Sie steht trotzdem als Ebene da und nicht in die Abschnitte eingearbeitet:
   So laesst sich in einer Zeile sehen, was der Entwurf geaendert hat - und
   in einem Griff zuruecknehmen, wenn er sich am Geraet nicht bewaehrt.*
   =========================================================================== */

/* ===========================================================================
   11. Frische Ebene (Richtung "Limette")
   ===========================================================================
   Nur Aussehen: Farbe, Rundung, Gewicht, Schatten. **Keine Masse und keine
   Anordnung** - die stehen weiter in den Merkmalsvariablen der vier Ansichten
   (Regel 0). Wer hier eine Zahl sucht, die eine Bedienflaeche oder einen
   Abstand bestimmt, findet keine; es gibt nur var(--...).

   **Die Kopfzeile ist hell geworden.** Die Farbe traegt jetzt, was etwas TUT -
   der Menueknopf, der betonte Knopf, der aktive Reiter. Ein Balken, der die
   ganze Breite einnimmt, hat die Hauptfarbe verbraucht, bevor die erste
   Aktion sie brauchte. */

.kopfzeile {
  background: var(--flaeche);
  color: var(--text);
  box-shadow: 0 1px 0 var(--linie), 0 6px 18px oklch(26% 0.03 150 / 0.05);
}
.marke { color: var(--akzent-dunkel); font-weight: 800; letter-spacing: -0.02em; }
/* **Fett und in voller Textfarbe** (20.09.2026, auf Wunsch). Er stand leise
   und halbfett neben der Marke; jetzt steht er allein in der Mitte und sagt,
   **wo man gerade ist** - das ist die Auskunft der Kopfzeile, nicht die
   Marke daneben. */
/* **Die Farbe, nicht das Gewicht** – `font-weight: 700` stand hier bis zum
   29.09.2026 und wurde von der Regel im Seitenkopf geschlagen. *Zwei tote
   Gewichte an einem Selektor waren zwei zu viel.* */
.seitentitel { color: var(--text); }

/* Das Fragezeichen liest nicht mehr inherit aus einer gruenen Zeile -
   dieselbe Falle wie in FAM-109, nur andersherum. */
.kopfzeile .hilfezeichen {
  color: var(--akzent-dunkel);
  border-color: var(--linie);
  opacity: 1;
}
.kopfzeile .hilfezeichen:hover { background: var(--akzent-blass); }

/* **Der Menueknopf ist durchsichtig** - reiner Text mit dem Zeichen davor.
   Er ist der Weg, nicht die Aktion; Farbe bekommt, was etwas TUT.

   **Wo er steht, entscheidet Abschnitt 2** - hier stand bis zum 20.09.2026
   ein `order: -1` daneben, und damit die Anordnung an einer zweiten Stelle.
   *Eine alte Regel weiter unten im Blatt gewinnt gegen die neue* (FAM-107). */
.menueknopf {
  background: none;
  color: var(--text);
  border-radius: 999px;
  font-weight: 600;
}
.menueknopf:hover { background: none; color: var(--akzent-dunkel); }

/* **Der Inhalt steht auf Weiss**, nicht auf dem getoenten Grund. Die
   Werkzeugleiste und das Chatfeld ziehen mit, sonst blitzt der alte Ton
   an einer Kante hervor. */
.inhalt, .werkzeugleiste, .chat-neu { background: var(--flaeche); }

/* --- Schrift ------------------------------------------------------------- */

h1, h2, h3 { letter-spacing: -0.015em; }

/* **Hier standen `h1 { font-weight: 800 }` und `h2, h3 { 700 }`**, und sie
   sind am 29.09.2026 weggefallen - *fuer `h1` und `h2`, nicht fuer `h3`.*

   **Sie waren der Grund, warum der Wunsch zuerst nur halb gewirkt hat.**
   Die Grundregel steht in Abschnitt 4 und setzt seit heute `500`; diese
   Ebene kommt **spaeter im Blatt** und hat sie bei gleichem Gewicht
   geschlagen. Gemessen: Der Seitentitel war richtig (`.seitentitel` ist
   eine Klasse und gewinnt), **ein Abschnittstitel stand weiter auf 700** -
   *und beide sahen fuer sich genommen ordentlich aus.*

   *Zum zweiten Mal an einem Tag: Eine Zusage gehoert in die letzte Regel
   zum Selektor* (das Abo war der erste Fall). */
h3 { font-weight: 700; }
.einleitung { line-height: 1.55; }

/* --- Weiche Schatten statt Linien ---------------------------------------- */

.kachel {
  border: 0;
  box-shadow: 0 1px 2px oklch(26% 0.03 150 / 0.06), 0 10px 22px oklch(26% 0.03 150 / 0.07);
}
.kachel h3 a { text-decoration: none; }
.kachel h3 a:hover { text-decoration: underline; }

/* Eine Zeile wird eine Karte. Die Linie darunter hat die Zeilen getrennt;
   das tut jetzt der Abstand, und der kommt aus --abstand. */
.aufgabe, .schritt, .anhang, .listenzeile, .nachlesezeile {
  border-bottom: 0;
  margin-bottom: calc(var(--abstand) / 2);
  padding: calc(var(--abstand) / 2) var(--abstand);
  background: var(--flaeche);
  border-radius: var(--radius-karte);
  box-shadow: 0 1px 2px oklch(26% 0.03 150 / 0.05), 0 6px 14px oklch(26% 0.03 150 / 0.05);
}
.aufgabe.geoeffnet { background: var(--akzent-blass); }
.liste th, .liste td { border-bottom-color: var(--akzent-blass); }
.notiztext, .ankreuzliste, .anmeldekarte {
  border-color: transparent;
  box-shadow: 0 1px 2px oklch(26% 0.03 150 / 0.05), 0 10px 24px oklch(26% 0.03 150 / 0.07);
}

.hinweiskasten {
  border-left: 0;
  background: var(--warm-blass);
  box-shadow: inset 0 0 0 1px oklch(90% 0.03 75);
}
.hinweiskasten.warnung { box-shadow: inset 0 0 0 1px var(--fehler); }

/* **Schlicht.** Kein farbiger Rahmen, kaum Rundung, nur ein kleiner
   Schlagschatten - der Satz selbst ist die Auffaelligkeit. */
.benachrichtigung {
  border: 0;
  border-radius: 6px;
  background: var(--flaeche);
  box-shadow: 0 1px 3px oklch(26% 0.03 150 / 0.12);
}
.meldung { font-weight: 600; }

/* --- Kraeftige Chips und Pillen ------------------------------------------ */

/* **Knoepfe: eine Kontur, kleine Ecken, flach.** Rueckmeldung am Geraet war
   dreifach - zu rund, zu viele Rahmen, zu gross. Also:

   - **eine** Linie, nicht Rahmen UND Schatten: der Rahmen ist 0, die Kontur
     ist ein inset-Schatten. Zwei Linien uebereinander waren der Grund,
     warum die Leiste voll aussah.
   - 6 px Ecken statt der Pille - dieselbe Kante wie an den Eingabefeldern.
   - flacher und enger: Hoehe und Innenabstand liegen 4 px unter der
     Bedienflaeche. **Das ist eine Abweichung von Regel 10** und hier
     bewusst - notiert, damit sie nicht als Versehen durchgeht.

   Gefuellt ist nur noch nichts; die Hauptaktion traegt eine kraeftige Kontur
   in der Akzentfarbe. */
button, .schaltflaeche {
  height: calc(var(--schaltflaeche) - 4px);
  min-height: calc(var(--schaltflaeche) - 4px);
  padding: 0 calc(var(--abstand) * 0.8);
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--text);
  font-weight: 600;
  box-shadow: inset 0 0 0 1px var(--linie);
}
button:hover, .schaltflaeche:hover {
  background: none;
  color: var(--akzent-dunkel);
  box-shadow: inset 0 0 0 1px var(--akzent);
}
button.betont, .schaltflaeche.betont {
  background: none;
  color: var(--akzent-dunkel);
  font-weight: 700;
  box-shadow: inset 0 0 0 2px var(--akzent);
}
button.betont:hover, .schaltflaeche.betont:hover {
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  box-shadow: inset 0 0 0 2px var(--akzent-dunkel);
}

/* Der Stern, die Sonne, das Kreuz und der Haken tragen keine Kontur - sie
   sind Zeichen, keine Kaesten. */
button.stern, button.tagesknopf, .wegklicken { box-shadow: none; border-radius: 6px; }
.tagesknopf.gesetzt { border-color: transparent; box-shadow: inset 0 0 0 1px var(--akzent); }
button.stern:hover, button.tagesknopf:hover, .wegklicken:hover { box-shadow: none; }

/* **Ein Zeichenknopf ist kein Kasten** (18.09.2026, auf Wunsch: „hinten keine
   Umrandung um Buttons, sondern nur ein gruenes Symbol").

   Er steht in einer Zeile mit Eingabefeldern, und die tragen selbst eine
   Kontur. Eine dritte daneben war genau die Enge, die zurueckgemeldet wurde -
   dieselbe Ueberlegung, aus der die Knoepfe oben **eine** Linie tragen statt
   Rahmen und Schatten.

   **Die Farbe traegt das Zeichen, nicht der Rahmen.** Der Haken ist gruen,
   weil er die Hauptaktion der Zeile ist; der Papierkorb ist leise und wird
   erst beim Verweilen rot. **Das Zeichen selbst sagt, was passiert** - die
   Farbe ist die Zugabe (FAM-65). */
button.zeichenknopf {
  box-shadow: none;
  color: var(--akzent);
}

/* **Kein Hintergrund beim Verweilen** (18.09.2026, auf Wunsch: „lasse diesen
   gruenen Hover-Effekt komplett weg").

   Eine Flaeche, die unter dem Zeiger aufleuchtet, ist wieder ein Kasten -
   nur einer, den man erst sieht, wenn man schon dort ist. **Der Zeichenknopf
   soll ein Zeichen bleiben**, und ein Zeichen wird dunkler, nicht
   hinterlegt.

   **Am Handy gibt es ohnehin kein Verweilen**; dort war der Effekt von
   Anfang an nur die Erinnerung an einen Mauszeiger. */
button.zeichenknopf:hover {
  background: none;
  box-shadow: none;
  color: var(--akzent-dunkel);
}
button.zeichenknopf.gefahr { color: var(--fehler); }
button.zeichenknopf.gefahr:hover { background: none; color: oklch(40% 0.17 25); }

/* **Der Abhak-Knopf traegt keine Umrandung mehr** (18.09.2026, auf Wunsch -
   in drei Schritten, und der letzte war *„bitte keinerlei Umrandung“*).

   Vorher trug er einen Rahmen **und** ein Zeichen ▢ - ein Kasten im Kasten,
   und der innere war ein Viertel so gross. **Jetzt gibt es nur noch das
   Zeichen**: ein Kaestchen, leer oder mit Haken (`_symbole.html`).

   **Die Flaeche bleibt `--schaltflaeche`**, auch ohne Rahmen: Sie ist das,
   was im Laden getroffen werden muss, oft einhaendig und in Bewegung. Nur zu
   sehen ist sie nicht mehr - **was man trifft, ist groesser als das, was man
   sieht**, und das ist hier richtig herum.

   **Die Farbe ist die Zugabe, das Zeichen die Auskunft** (FAM-60, FAM-65):
   Das Kaestchen wechselt seinen Inhalt, nicht nur seinen Ton. */
.haken {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 0;
  /* **`box-shadow: none` ist hier die eigentliche Zeile.** `border: 0` allein
     reichte nicht: Seit der Frischen Ebene ist die Kontur eines Knopfes ein
     `inset`-Schatten und kein Rahmen - gemessen stand nach dem Entfernen des
     Rahmens weiterhin `oklch(0.9 0.02 150) 0 0 0 1px inset` da, also genau
     die Umrandung, die weg sollte. **Wer nur den Rahmen nimmt, nimmt die
     Haelfte**, und auf einem Bild sieht die andere aus wie Absicht. */
  box-shadow: none;
  background: none;
  color: var(--text-leise);
  font-size: var(--schrift-gross);
}
/* **Auch hier kein Hintergrund beim Verweilen** - derselbe Wunsch wie beim
   Zeichenknopf: „den Rand in gruen und grau auch bitte komplett wegnehmen“.
   Es bleibt das Zeichen, und es wird dunkler. */
.haken:hover { background: none; box-shadow: none; color: var(--text); }
.haken.gesetzt {
  border-color: transparent;
  box-shadow: none;
  background: none;
  color: var(--akzent);
}
.haken.gesetzt:hover {
  background: none;
  box-shadow: none;
  color: var(--akzent-dunkel);
}

/* Im Menue bleiben die Punkte Text - eine Kontur je Zeile waere eine Liste
   aus Kaesten. */
.menuetafel a, .menuegruppe > summary, .ansichtsliste button, .abmelden button {
  border-radius: 6px;
  font-weight: 600;
  box-shadow: none;
  height: auto;
  min-height: var(--schaltflaeche);
}
.menuetafel a:hover, .menuegruppe > summary:hover, .ansichtsliste button:hover, .abmelden button:hover {
  background: var(--akzent-blass);
  box-shadow: none;
}
.menueknopf { box-shadow: none; height: auto; }
.listenreiter {
  border-color: transparent;
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-weight: 700;
}
.listenreiter.aktiv { background: var(--akzent); color: var(--flaeche); }
.listenreiter .anzahl { background: var(--flaeche); font-weight: 700; }
.listenreiter.aktiv .anzahl { background: oklch(100% 0 0 / 0.22); color: var(--flaeche); }
.typenliste li {
  border-color: transparent;
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-weight: 600;
}
/* **Die Marke steht UEBER dem Titel, nicht auf ihm.** Sie war absolut
   positioniert und ueberdeckte die letzten Buchstaben von
   "Geschenkewuensche" - mit dem fetteren Titel der Frischen Ebene faellt es
   auf, falsch war es vorher schon. Im Fluss braucht sie keine Zahl. */
.marke-geplant {
  position: static;
  align-self: flex-start;
  display: inline-block;
  margin-bottom: 4px;
  background: var(--warm-blass);
  color: oklch(42% 0.15 60);
  font-size: 0.8em;
  font-weight: 700;
}
.kachel-geplant { display: flex; flex-direction: column; }
.erledigte .anzahl { font-weight: 700; }

/* **Eingabefelder haben nur eine kleine Rundung.** Die weiche Rundung
   gehoert den Karten und den Pillen; ein Feld, in das man tippt, soll eine
   Kante haben, an der das Auge den Anfang der Zeile findet. */
input[type="text"], input[type="password"], input[type="date"], input[type="time"],
input[type="number"], input[type="email"], select, textarea {
  border-color: transparent;
  border-radius: 6px;
  background: var(--flaeche);
  box-shadow: inset 0 0 0 1px var(--linie);
}
/* **Eine Linie, nicht zwei** (gemeldet am 18.09.2026, Bildschirmfoto: zwei
   gruene Rahmen um dasselbe Feld).

   Gemessen mit der Tabulatortaste, vorher: aussen `outline: solid 2px` mit
   `outline-offset: 1px` aus Abschnitt 7, innen `inset 0 0 0 2px` von hier.
   **Beide waren richtig gemeint** - der Umriss ist die Fokusanzeige des
   Browsers, die Kontur die Kante der Frischen Ebene -, und zusammen sind
   sie eine zu viel.

   **Der Umriss faellt weg, nicht die Fokusanzeige.** Was bleibt, ist 2 px in
   der Akzentfarbe an derselben Kante, die das Feld ohnehin hat - wer mit der
   Tastatur navigiert, sieht weiterhin, wo er steht. Ein `outline: none` ohne
   Ersatz waere die schlimmere Haelfte: Dann sieht es niemand mehr, **und
   niemandem faellt es auf**, weil mit der Maus ohnehin nichts erscheint.

   **Nur die Felder.** Knoepfe und Verweise zeichnen keine eigene
   Fokuskontur; ihnen den Umriss zu nehmen hiesse, ihnen die einzige zu
   nehmen. */
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--akzent);
}

/* --- Ein Feld, das gefuellt werden muss ----------------------------------

   **Auf Rueckmeldung** (25.09.2026): *„Bitte nicht den schon geschriebenen
   Text wegwerfen, sondern stehen lassen und dann das Feld, was gefuellt
   werden muss, rot umranden."*

   **Dieselbe Bauart wie die Fokuskontur daneben** - eine Kontur nach innen
   statt eines `border`: Ein Rahmen kostete 2 px Aussenmass, und das Feld
   **spraenge um zwei Pixel**, sobald es rot wird. *Die Zeile darf nicht
   umbrechen, nur weil eine Meldung erscheint.*

   **Die Farbe steht einmal da** (`--fehler`, Abschnitt 1, Regel 10) - es ist
   dieselbe, die die Meldung darueber traegt. *Zwei Rottoene fuer dieselbe
   Aussage waeren zwei Stellen.*

   **Und Rot ist nicht die Aussage, sondern die Wegweisung.** Was fehlt,
   steht in der Meldung (Regel 3: Fehler bleiben stehen), und das Feld traegt
   `aria-invalid` - *wer rot-gruen-blind ist oder vorlesen laesst, verliert
   hier nichts* (FAM-65, Regel 5).

   ## Warum hier `input.fehlt` steht und nicht `.fehlt`

   **Der Fund des Ansichtsdurchgangs** (25.09.2026): Ein schlichtes `.fehlt`
   war **wirkungslos**, und zwar vollstaendig - gemessen trug das Feld
   weiterhin `oklch(0.9 0.02 150) 0 0 0 1px inset`, also seine gewoehnliche
   Kontur.

   **Eine Klasse schlaegt keinen Attributselektor.** Die Feldregel in
   Abschnitt 7 lautet `input[type="text"], …` - Element **und** Attribut,
   also (0,1,1). Eine blosse Klasse ist (0,1,0) und verliert, *ganz gleich,
   wie weit unten im Blatt sie steht.* **Mit `input.fehlt` steht es 1:1**,
   und dann entscheidet die Reihenfolge - diese Regel steht spaeter.

   > **Die Suite konnte das nicht sehen**: Die Klasse stand im HTML, die
   > Regel stand im Blatt, und beide Wachen waren gruen. *Sichtbar ist von
   > all dem nichts Kaputtes* - gefunden hat es eine Messung im Browser, die
   > den **gerechneten** Wert gelesen hat.

   **Der Fokus gewinnt, und auch das steht ausdruecklich da**: Mit (0,2,1)
   schlaegt die Regel darunter die rote - *wer in das Feld tippt, soll
   sehen, dass er darin steht*, und die Aussage „hier fehlt etwas“ traegt
   ohnehin die Meldung. Ohne diese zweite Regel bliebe das Feld auch beim
   Tippen rot: **Dann waere die Fokusanzeige weg, und niemandem fiele es
   auf**, weil mit der Maus ohnehin nichts erscheint. */
input.fehlt, select.fehlt, textarea.fehlt {
  box-shadow: inset 0 0 0 2px var(--fehler);
}

/* **`outline: none` steht hier noch einmal** - und das ist keine Doppelung,
   sondern die Forderung von `test_ein_feld_im_fokus_bekommt_eine_linie_und_
   nicht_zwei`: **Wer eine eigene Fokuskontur zeichnet, nimmt den Umriss
   weg.** Die Wache hat beim Einbau dieser Regel gesprochen, *bevor jemand
   die zwei Linien am Geraet gesehen hat.*

   Heute traegt die Regel darueber es ohnehin mit (beide Selektoren greifen
   an demselben Feld) - **die Zeile macht diese Regel selbsttragend**: Wer
   sie eines Tages an ein Element haengt, das die andere nicht trifft, hat
   sonst zwei Linien um dasselbe Feld. */
input.fehlt:focus-visible,
select.fehlt:focus-visible,
textarea.fehlt:focus-visible {
  outline: none;
  box-shadow: inset 0 0 0 2px var(--akzent);
}

/* --- Der Stand einer Aufgabe als Balken ---------------------------------- */

/* **Gezaehlt, nicht geschaetzt** (Regel 5): Der Anteil im Markup ist der
   Quotient aus denselben zwei Zahlen, die daneben als Wort stehen - "1 von
   3". Das Wort bleibt stehen; der Balken ist die Bequemlichkeit, nicht die
   Auskunft - dieselbe Ueberlegung wie bei den Personenfarben (FAM-65). */
.schrittstand {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  margin-left: 0.6em;
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-weight: 600;
}
.schrittbalken {
  display: block;
  width: 3.5em;
  height: 5px;
  border-radius: 999px;
  background: oklch(88% 0.03 150);
  overflow: hidden;
  flex: 0 0 auto;
}
.schrittbalken::before {
  content: "";
  display: block;
  height: 100%;
  width: var(--anteil, 0%);
  border-radius: 999px;
  background: var(--akzent);
}

/* --- Personenfarben auch in den Listen (FAM-123, erweitert) -------------- */

/* **Die Farbe ist die Bequemlichkeit, nicht die Auskunft.** Der Name steht
   daneben - wer rot-gruen-blind ist, verliert nichts. */
.aufgabe-wer.gefaerbt, .aufgabe-wer.gefaerbt .leise { color: var(--personenfarbe); }
.aufgabe-wer.gefaerbt { font-weight: 600; }
.aufgabe-wer.leise, .aufgabe.erledigt .aufgabe-wer { color: var(--text-leise); font-weight: 400; }

/* **Der Termin traegt die Farbe dessen, den er angeht** (FAM-162).

   Der Wunsch sagt *„dann wird der Termin in der Farbe der Person
   angezeigt"* - ein gefaerbter Name allein waere davon die Haelfte. Also
   ein **Streifen an der Zeile**.

   **Kein farbiger Hintergrund**, und das ist gerechnet: `--person-*` liegt
   bei 52 % Helligkeit. Dunkler Text darauf unterschreitet jeden brauchbaren
   Kontrast, heller ebenso - ein Streifen beruehrt keinen Text.

   **Der Preis ist gemessen und steht in docs/Test/FAM-162.md:** Die Zeile
   verliert 9 px Textbreite. Im Kalendergitter ist eine Spalte 84 px schmal
   (FAM-84) - dort steht **Text, der umbrechen darf**, kein Eingabefeld;
   genau das ist der Unterschied zum Wochenplan (FAM-145, Regel 10). */
.terminzeile.gefaerbt {
  border-inline-start: 3px solid;
  padding-inline-start: 6px;
}
.terminzeile.gefaerbt { border-inline-start-color: var(--personenfarbe); }

/* **Und der Name in derselben Farbe** - dieselbe Nummer, dieselbe
   Entscheidung wie in der Aufgabenliste darueber: *Mia ist in der ganzen
   Anwendung dieselbe.*

   **Ohne `font-weight`**, anders als dort: Die Zeile traegt schon den
   Streifen, und fett daneben waere dieselbe Aussage zum dritten Mal. */
.terminzeile.gefaerbt .terminwer { color: var(--personenfarbe); }


/* --- Sich eine Farbe geben (FAM-162) -----------------------------------

   **Der Fleck ist die Zugabe, das Wort die Auskunft** (FAM-65): Ein Waehler
   aus sechs unbenannten Flecken ist fuer den unbedienbar, fuer den diese
   Regel ueberhaupt geschrieben ist. Die Woerter stehen in
   `app/personenfarben.py`. */
.farbfleck {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  border-radius: 999px;
  vertical-align: -0.1em;
  margin-inline-end: 0.4em;
  /* Die Kante, damit ein heller Fleck auf hellem Grund nicht verschwindet -
     und damit „keine Farbe“ ueberhaupt etwas ist, das man sieht. */
  box-shadow: inset 0 0 0 1px var(--linie);
}
.farbfleck { background: none; }
.farbfleck.gefaerbt { background: var(--personenfarbe); }

/* **Die Rezeptur - an EINER Stelle, und zwar hier** (22.09.2026).

   Sie stand in Abschnitt 1 auf `:root`, und **dort war sie leer**: Ein
   Custom Property wird an der Stelle seiner **Deklaration** substituiert,
   und `--person-ton` gibt es auf `:root` nicht. *Gemessen im Browser*:
   `getComputedStyle(root).getPropertyValue('--personenfarbe')` gab nichts,
   und jede Farbe war transparent - **bei gruener Testsuite.**

   **`.gefaerbt` ist die richtige Stelle**, denn dort liegt der Ton: Die
   Vorlage setzt beides zusammen (`class="… gefaerbt" style="--person-ton:
   250"`) - *eine Klasse sagt, DASS gefaerbt wird, der Ton sagt WOMIT.*

   **Regel 10 gilt unveraendert**: Helligkeit und Buntheit stehen weiterhin
   in Abschnitt 1, und die Rezeptur steht **einmal** da. */
.gefaerbt {
  --personenfarbe: oklch(var(--person-helligkeit) var(--person-buntheit) var(--person-ton));
}

/* **Ein Raster statt einer Liste** (22.09.2026, auf Wunsch). Achtzehn
   Woerter untereinander waeren eine Wand; achtzehn Flaechen nebeneinander
   sind ein Blick.

   **`auto-fill` und keine Spaltenzahl** - die Zahl der Spalten ergibt sich
   aus der Breite, und damit braucht dieses Raster **keine** eigene
   Merkmalsvariable je Ansicht (anders als das Menue, das seine Spalten
   entscheidet). *Eine Variable, die vier gleiche Werte traegt, ist eine
   Entscheidung; eine, die gar nicht noetig ist, ist Ballast.* */
.farbraster {
  list-style: none;
  margin: 0 0 var(--abstand);
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9ch, 1fr));
  gap: calc(var(--abstand) / 2);
}
/* Die Hoehe, den Rahmen und den Innenabstand hat der Knopf schon
   (Abschnitt 11) - hier steht nur, wie Fleck und Wort zueinander liegen.

   **Untereinander, nicht nebeneinander**: Im Raster ist die Zelle schmal,
   und ein Wort neben dem Fleck braeuchte die doppelte Breite. Der Fleck
   steht oben, das Wort darunter - **und es steht weiterhin da** (FAM-65). */
.farbknopf {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.2em;
  width: 100%;
  height: auto;
  padding-block: calc(var(--abstand) / 2);
}
.farbknopf.gewaehlt { box-shadow: inset 0 0 0 2px var(--akzent); }
/* **Der Fleck im Raster misst die Bedienflaeche**, nicht `0.9em` wie der in
   der Liste: Hier ist er das, was ein Daumen trifft. *Wer eine
   Bedienflaeche kleiner rechnet, unterbietet das Mass, das der Entwurf
   festgelegt hat* (FAM-62). */
.farbraster .farbfleck {
  width: var(--schaltflaeche);
  height: var(--schaltflaeche);
  margin-inline-end: 0;
}
/* **Das Wort darf umbrechen, der Knopf nicht schrumpfen.** „Blattgruen“ ist
   das laengste und passt in 9ch; waere es einmal laenger, bricht es um,
   statt die Zelle zu sprengen. */
.farbwort {
  font-size: 0.85em;
  line-height: 1.2;
  text-align: center;
  overflow-wrap: anywhere;
}

/* --- Eine andere Farbe (22.09.2026) ------------------------------------

   **Hinter einem `<details>`, wie alles Seltene** (FAM-65, FAM-70, FAM-107,
   FAM-136): Das Raster ist der normale Weg, der freie Waehler die Ausnahme.
   *Was selten gebraucht wird, beherrscht die Maske nicht.*

   **Das Aufklappzeichen setzt es selbst und es wechselt** (▾/▴) - zum
   fuenften Mal nach FAM-70, FAM-136, FAM-146 und dem Menue: Ein
   `display: flex` an einem `<summary>` loescht den `::marker` des Browsers,
   und *dann sieht ein ganzer Bedienweg nicht mehr aus wie einer.* */
.farbfrei { margin-bottom: var(--abstand); }
.farbfrei > summary {
  display: flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  list-style: none;
  cursor: pointer;
  color: var(--akzent);
}
.farbfrei > summary::-webkit-details-marker { display: none; }
.farbfrei > summary::after { content: "▾"; margin-inline-start: 0.4em; }
.farbfrei[open] > summary::after { content: "▴"; }

.farbfrei-zeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--abstand);
  margin-block: calc(var(--abstand) / 2);
}
/* **Der Farbtopf ist eine Bedienflaeche** - er misst `--schaltflaeche` hoch,
   wie jeder Knopf daneben, und **doppelt so breit**: Ein Browser zeigt darin
   die gewaehlte Farbe, und ein Quadrat von 36 px ist am Handy kleiner als der
   Fleck, den er beschreibt.

   **`* 2` ist groesser, nicht kleiner** - die Wache aus FAM-62 faengt seit
   dem 18.09.2026 trotzdem **jede** Rechnung und verlangt fuer jede einen
   Eintrag. Er steht in `ERLAUBTE_RECHNUNGEN`; *dass etwas harmlos ist, soll
   jemand entschieden haben und nicht nur behaupten.*

   **Und der Selektor steht unbeschraenkt da, nicht hinter `.farbfrei-zeile`.**
   Das ist der Fund des Tages und die fuenfte Auflage derselben Luecke
   (FAM-57, FAM-64, FAM-83, FAM-85): `input[type="color"]` stand in keiner
   Regel mit `font: inherit` - **fuer die Ansichtsauswahl war es
   unerreichbar** (Regel 0). Eine Regel hinter einer fremden Klasse hilft der
   naechsten Maske nicht (woertlich der Fund von FAM-85), deshalb liegt sie
   hier offen - wie die von `<textarea>` und `input[type="file"]`.

   **Nicht in der Zeile in Abschnitt 7**: Dort gilt `padding: 0 var(--abstand)`
   und ein Rahmen, und beides gehoert nicht an einen Farbfleck. */
input[type="color"] {
  width: calc(var(--schaltflaeche) * 2);
  height: var(--schaltflaeche);
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}


/* --- Die Freundesliste (FAM-155) --------------------------------------

   **Keine einzige Pixelzahl, die eine Merkmalsvariable haette** (Regel 0):
   Wer am Desktop die Ansicht *Handy* waehlt, bekommt die Handy-Masse - und
   ein hier hingeschriebener Wert bliebe dabei stehen.

   **Und kein `@media`**: Die Haltepunkte stehen an einer Stelle in Abschnitt
   1 und setzen dort die Variablen. */

/* Der Filter - er steht nur da, wenn es etwas zu filtern gibt (die Vorlage
   entscheidet das, nicht diese Regel).

   **`flex-wrap: wrap`, und das ist die Lehre aus FAM-84 und FAM-136:** Eine
   Leiste, die nicht umbrechen kann, schneidet bei vier Ausschnitten am Handy
   den letzten ab - *und die Seite scrollt nicht seitwaerts*, also kaeme dort
   niemand mehr hin. */
.freundefilter {
  display: flex;
  flex-wrap: wrap;
  gap: var(--abstand);
  margin-bottom: var(--abstand);
}

/* **Jede Wahl ist eine Bedienflaeche** und misst deshalb `--schaltflaeche` -
   dieselbe Zusage wie beim Termintitel (FAM-84) und bei der Esserzeile
   (FAM-146). */
.freundefilter-wahl {
  display: inline-flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  padding: 0 var(--abstand);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px var(--linie);
  color: inherit;
  text-decoration: none;
  white-space: nowrap;
}

/* **Der gewaehlte Ausschnitt traegt Farbe UND Gewicht** (FAM-65): Wer
   rot-gruen-blind ist, saehe die Farbe nicht - das `aria-current` in der
   Vorlage sagt es ausserdem einem Vorleseprogramm. */
.freundefilter-wahl.aktiv {
  background: var(--akzent-blass);
  box-shadow: inset 0 0 0 2px var(--akzent);
  font-weight: 600;
}

.freundeliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.freund {
  border-bottom: 1px solid var(--linie);
}
.freund:last-child { border-bottom: 0; }

/* **Der ganze Text ist der Weg in die Maske**, nicht nur der Name: Am Handy
   soll man nicht ein Wort treffen muessen. Er misst deshalb genau
   `--schaltflaeche` - er ist die Bedienflaeche (die Lehre aus FAM-84). */
.freund-text {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px var(--abstand);
  min-height: var(--schaltflaeche);
  padding: 4px 0;
  color: inherit;
  text-decoration: none;
}
.freund-text:hover { text-decoration: underline; }

/* **Umbrechen statt abschneiden** (FAM-84): `min-width: 0` laesst den Namen
   schrumpfen, `overflow-wrap` bricht ein langes Wort - und deutsche
   Doppelnamen sind lang. */
.freund-name {
  font-weight: 600;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65). Am eigenen
   Eintrag steht keines - er ist der Normalfall, und ein Wort, das immer
   dasteht, wird ueberlesen (FAM-94). */
.freund-wem {
  color: var(--text-leise);
  font-size: 0.92em;
  white-space: nowrap;
}

/* Die Beziehung steht am Ende der Zeile, wo Platz ist - am Handy rutscht sie
   mit um, statt abgeschnitten zu werden. */
.freund-beziehung {
  color: var(--text-leise);
  font-size: 0.92em;
  white-space: nowrap;
  margin-left: auto;
}

/* **Ein Schlagwort steht als Marke da** (FAM-157), und das ist noetig statt
   huebsch: An einem Freund haengen **mehrere**, und leise Woerter
   hintereinander liesen sich als eines - *„Schule Fussball“* saehe aus wie
   ein Schlagwort mit zwei Woertern.

   **Die Form der Marke gibt es seit FAM-54** (`.marke-geplant`); hier ohne
   `text-transform`, denn ein Schlagwort ist selbst getippt und **so
   geschrieben, wie jemand es wollte** - Grossbuchstaben waeren eine
   Aenderung an seiner Eingabe.

   **Und keine Farbe je Wort** (FAM-65): *Das Wort traegt die Aussage.* Bei
   zwanzig Schlagwoertern gaebe es ohnehin nicht zwanzig unterscheidbare
   Farben (die Rechnung aus FAM-123). */
.freund-schlagwort {
  padding: 1px 8px;
  border-radius: 999px;
  background: var(--warm-blass);
  color: var(--text-leise);
  font-size: 0.8em;
  white-space: nowrap;
}

/* **Die Beziehung verliert ihr `margin-left: auto`, sobald Schlagwoerter
   danebenstehen** - sonst schoebe sie sich zwischen Namen und Marken, und
   die Marken staenden am rechten Rand statt bei ihrem Menschen.

   `:has()` statt einer Klasse im Markup: Eine Klasse, die jemand setzen
   muss, wird bei der naechsten Zeile vergessen (FAM-109). */
.freund-text:has(.freund-schlagwort) .freund-beziehung { margin-left: 0; }

/* --- Doppelte erkennen (FAM-158) ----------------------------------------

   **Der Hinweis MELDET, er verbietet nicht** - und deshalb ist er ein
   `.hinweiskasten` und keine Fehlermeldung: *Es gibt wirklich zwei Anna
   Mueller.* Die Farbe kommt aus der Klasse darunter; hier steht nur, was
   die Liste darin braucht. */
.dublettenliste {
  list-style: none;
  margin: var(--abstand) 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--abstand) / 2);
}

/* **Jeder Treffer ist ein Weg zu seinem Eintrag** - also eine Bedienflaeche
   und keine Zeile Text. `.schaltflaeche` bringt `--schaltflaeche` mit. */
.dublettenliste li { margin: 0; }

/* **Eine eigene Zeile fuer die Zugehoerigkeit** (FAM-158).

   Sie steht UEBER der Maske und nicht darin: Es ist ein eigenes Formular,
   damit ein gewoehnliches Speichern sie nicht mitschickt (FAM-146). */
.gehoertform {
  margin-bottom: var(--abstand);
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--abstand) / 2);
}

/* --- Die Kontaktwege eines Freundes (FAM-156) ---------------------------- */

.kontaktbereich { margin-top: calc(var(--abstand) * 1.5); }
.kontaktbereich > h2 { font-size: 1em; margin: 0 0 6px; }

.kontaktliste {
  list-style: none;
  margin: 0 0 var(--abstand);
  padding: 0;
}

/* **Vorn die Bedienflaeche, dahinter das Aendern** - dieselbe Bauart wie die
   Artikelzeile (FAM-70). */
.kontaktweg {
  display: flex;
  align-items: flex-start;
  gap: 4px;
  border-bottom: 1px solid var(--linie);
}
.kontaktweg:last-child { border-bottom: 0; }

/* Der Verweis nach draussen, ins Telefon oder ins Mailprogramm.

   **Er misst genau `--schaltflaeche`** - er ist die Flaeche, die am Handy
   ein Daumen trifft, und *was man trifft, ist groesser als das, was man
   sieht* (Design.md, Frische Ebene). */
.kontaktruf {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  color: var(--akzent);
  text-decoration: none;
}
/* **Ein Weg ohne Verweis bekommt trotzdem den Platz**, damit die Werte
   untereinander auf einer Linie stehen - ein Messenger hat keinen (Regel 7). */
.kontaktruf.leer { pointer-events: none; }

.kontakt-aendern { flex: 1 1 auto; min-width: 0; }

/* **Das Aufklappzeichen wird selbst gesetzt** - `display: flex` an einem
   `<summary>` loescht den `::marker` des Browsers, und dann waere das
   Aendern unauffindbar (FAM-70, FAM-136). */
.kontakt-aendern > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px var(--abstand);
  min-height: var(--schaltflaeche);
  padding: 4px 0;
  cursor: pointer;
  list-style: none;
}
.kontakt-aendern > summary::-webkit-details-marker { display: none; }
.kontakt-aendern > summary::after {
  content: "\25BE";           /* ▾ */
  margin-left: auto;
  padding-left: calc(var(--abstand) / 2);
  color: var(--text-leise);
}
.kontakt-aendern[open] > summary::after { content: "\25B4"; }  /* ▴ */

.kontaktart {
  color: var(--text-leise);
  font-size: 0.92em;
  white-space: nowrap;
}
.kontaktwert {
  font-weight: 600;
  min-width: 0;
  overflow-wrap: anywhere;
}
.kontaktbemerkung {
  color: var(--text-leise);
  font-size: 0.92em;
  overflow-wrap: anywhere;
}

/* **Der eigene Umbruch, nicht der geerbte** (FAM-136, FAM-93): Die Zeile
   traegt VIER Elemente - Art, Wert, Bemerkung und die Knoepfe -, die
   Artikelzeile nur zwei. `--aufgabe-umbruch` steht ab Tablet auf `nowrap`,
   und damit laege hier etwas ausserhalb des Fensters.

   *Uebernommene Merkmalsvariablen sind eine Entscheidung, keine Vorlage.* */
.kontakt-werkzeug,
.kontaktneu {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  padding: calc(var(--abstand) / 2) 0;
}

.kontaktfelder {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  flex: 1 1 100%;
  min-width: 0;
}

.kontaktknoepfe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}

/* **Am Handy bekommt die Nummer eine eigene Zeile** (`--kontaktwert-basis`).

   Gemessen blieben ihr sonst **80 px innen, also rund elf Zeichen** bei
   12 px Schrift - und *„0170 12 34 56“* hat vierzehn. Das Auswahlfeld nahm
   dabei 141 px, mehr als die Nummer selbst: **Das Unwichtigste der drei
   bekam den meisten Platz.**

   Ab Tablet passt alles nebeneinander (gemessen 180 / 445 / 657 px innen),
   und dort bleibt es dabei.

   **Eine Merkmalsvariable und kein `@media` in der Maske** (Regel 0): Wer am
   Desktop *Handy* waehlt, bekommt die Zeile auch dort.

   **Der Innenabstand wird mitgerechnet** - `ch` misst den Text, nicht das
   Feld (der Fund vom 18.09.2026 am Mengenfeld des Einkaufs). */
.kontaktfelder input[name="wert"],
.kontaktneu input[name="wert"] {
  flex: 1 1 var(--kontaktwert-basis);
  min-width: calc(12ch + 2 * var(--abstand));
}
.kontaktnotiz {
  flex: 1 1 10ch;
  min-width: calc(8ch + 2 * var(--abstand));
}
.kontaktartwahl { flex: 0 1 auto; }

/* --- Das Sticker Board (FAM-167) ------------------------------------------

   **Nuechtern, und zwar mit Absicht.** Farbe, Drehung, Schatten, Papierkante
   und Untergrund sind **Karte 4** (FAM-169) - hier steht nur, was das Board
   *bedienbar* macht: eine Eintragezeile, eine Liste, ein Knopf je Zettel.

   *Karte 2 macht es bedienbar, Karte 4 macht es echt; wer beides in eine
   Karte legt, misst am Ende keins von beidem.*

   **Keine Farbe im Markup** (Regel 10) - die Vorlage setzt den FARBTON,
   die Rezeptur steht in Abschnitt 1 (`--personenfarbe`). */

.sticker-neu {
  display: flex;
  /* **`wrap`, wie bei Verliehen** (FAM-136) - und aus demselben Grund: Eine
     Zeile, die eine Aktion abschneidet, ist bei keiner Scrollposition
     erreichbar (FAM-50, FAM-84). Hier sind es nur ein Feld und ein Knopf,
     also traegt es auch am Handy; `wrap` kostet dort nichts und faengt den
     Tag, an dem ein zweites Feld dazukommt. */
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  margin-bottom: var(--abstand);
}

/* **`min-width` an dem Feld, das eine Eingabe traegt** (FAM-64) - und
   **nicht `0`**: Das erlaubte dem Feld, auf nichts zu schrumpfen, statt
   umzubrechen (der Fund von FAM-145).

   **Der Innenabstand wird mitgerechnet** - `ch` misst den Text, nicht das
   Feld (der Fund vom 18.09.2026 am Mengenfeld des Einkaufs). */
.sticker-neu input[type="text"] {
  flex: 1 1 var(--aufgabe-titel-breite);
  min-width: calc(12ch + 2 * var(--abstand));
}

/* --- Das Board selbst ---------------------------------------------------- */

/* **Die Zettel eines Bereichs** (28.09.2026).

   > **Diese Regel hiess bis zum 28.09.2026 `.stickerboard`** und war das
   > Board selbst. Seit die Wand in Bereiche geteilt ist, ist `.stickerboard`
   > das **Raster** (Abschnitt weiter unten) und diese Liste steht **in**
   > einem Bereich. *Ein Name, der etwas anderes meint als vorher, ist
   > schlimmer als ein neuer* (FAM-52, FAM-117). */
.stickerliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  /* **Jeder Zettel ist so hoch, wie sein Text ihn macht.**

     `stretch` ist die Vorgabe eines Flexkastens, und sie war hier ein Fund
     aus dem Ansichtsdurchgang: Ein einziger langer Zettel machte **die ganze
     Reihe** so hoch wie sich selbst - gemessen 259 px am Tablet fuer einen
     Zettel mit vier Woertern. *Auf einem Bild sieht das nach Absicht aus*,
     und an einem echten Brett gibt es das nicht. */
  align-items: flex-start;
  gap: var(--abstand);
}

/* **Ein Zettel ist so breit wie ein Zettel.**

   `--sticker-breite` steht in Abschnitt 1 der vier Wertesaetze (Regel 10) -
   am Handy nimmt er die ganze Breite, ab Tablet haengen mehrere nebeneinander
   wie an einem echten Brett.

   **`flex: 0 1 ...` und keine `width`**: Ein Flex-Element darf unter seine
   `width` schrumpfen, und dann waere der Zettel schmaler als sein Inhalt
   (die Lehre aus FAM-64). */
.sticker {
  position: relative;
  flex: 0 1 var(--sticker-breite);
  min-width: 0;
  box-sizing: border-box;
  padding: var(--abstand);
  /* Bis Karte 4 traegt der Zettel die Flaeche der Anwendung - **keine
     erfundene Farbe** (Regel 5, Regel 10). */
  background: var(--flaeche);
  border-radius: var(--radius-karte);
  box-shadow: 0 0 0 1px var(--linie) inset;
}

/* **Der Text ist der Zettel** - gross, und er darf umbrechen.

   `overflow-wrap: anywhere` faengt das eine lange Wort ohne Leerzeichen; ohne
   die Zeile spraengte es den Zettel, **und am Handy die Seite** (Regel 2). */
.sticker-text {
  margin: 0;
  /* **Die Schrift steht in Abschnitt 12**, nicht hier: Sie macht den Zettel
     *echt*, und dieser Abschnitt haelt ihn ausdruecklich nuechtern. */
  font-size: var(--schrift);
  line-height: 1.4;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  /* **Platz fuer das Abnehmen-Kreuz**, das oben rechts im Zettel sitzt -
     sonst liefe der Text darunter durch. */
  padding-right: var(--schaltflaeche);
}

/* --- Direkt auf dem Zettel schreiben (FAM-168) ----------------------------

   **Das Feld IST der Zettel.** Es traegt dieselbe Klasse wie der Absatz
   darueber (`.sticker-text`) - Schriftgroesse, Zeilenhoehe, Umbruch und der
   Platz fuer das Kreuz stehen damit an **einer** Stelle. Zwei Regeln fuer
   denselben Text liefen auseinander, **und man saehe es erst, wenn ein Zettel
   beim Anklicken springt.**

   Was hier steht, nimmt dem `<textarea>` sein Aussehen als Formularfeld:
   kein Rahmen, keine eigene Fuellung, kein Griff zum Ziehen. */

.sticker-aendern {
  /* **Kein eigener Kasten** (`display: contents` wie der Chatbereich,
     FAM-124): Das Formular ist eine Klammer um Feld und Knopf, keine
     Schachtel - sonst braeuchte der Zettel innen einen zweiten Abstand. */
  display: contents;
}

.sticker-feld {
  display: block;
  width: 100%;
  box-sizing: border-box;
  /* **Der Innenabstand liegt am Zettel, nicht am Feld** - sonst staende der
     Text eingerueckt, sobald er aenderbar ist, und ein Zettel saehe je nach
     Recht anders aus.

     **Und `padding-right` bleibt stehen** - es kommt aus `.sticker-text` und
     haelt den Platz fuer das Abnehmen-Kreuz frei. Ein `padding: 0` nahm es
     mit: Gemessen lief der Text am Handy **335 px** weit statt 299 und damit
     unter das Kreuz (der Fund aus dem Ansichtsdurchgang). */
  padding-block: 0;
  padding-left: 0;
  border: 0;
  background: none;
  color: inherit;
  /* **Kein Griff zum Ziehen.** Ein Zettel hat die Groesse, die sein Text ihm
     gibt (`rows`, gerechnet im Blueprint) - wer daran zoege, bekaeme einen
     Zettel, der nicht mehr zu den anderen passt. */
  resize: none;
  /* **Das Feld ist so hoch wie sein Text** - und zwar auch beim Tippen, ohne
     eine Zeile JavaScript.

     `rows` (gerechnet im Blueprint) ist der **Rueckfall** fuer Browser, die
     `field-sizing` noch nicht kennen. Gemessen in Chrome 153: **22,4 px statt
     67,2** fuer denselben kurzen Text.

     **Ohne diese Zeile waere der Preis am Handy am hoechsten** - dort ist ein
     Zettel 60 Zeichen breit, `ZEICHEN_JE_ZEILE` rechnet aber mit 20 (der
     engsten Ansicht). Gemessen: **130,8 px statt 59,2** je Zettel, und das an
     der einen Stelle, an der man gar nicht schreiben kann. */
  field-sizing: content;
  /* **Keine zweite Fokuslinie.** Abschnitt 7 zeichnet einen `outline`, die
     Frische Ebene eine eigene Kontur - zusammen waeren es zwei (der Fund vom
     18.09.2026). Hier bleibt der `outline`: Er ist die einzige Anzeige, die
     dieses Feld hat, denn eine Kontur traegt es nicht. */
  box-shadow: none;
  /* **Am Handy nicht** - woertlich der Wunsch: *„In der Handyansicht kann man
     den Text nicht direkt auf einem Sticker schreiben."*

     **Entschieden ueber eine Merkmalsvariable, nicht ueber ein `@media`**
     (Regel 0): Eine Media Query kennt die Ansichtsauswahl nicht, und wer am
     Desktop *Handy* waehlt, bekaeme trotzdem ein Feld.

     **Der Preis steht dabei:** Am Handy laesst sich der Text eines Zettels
     auch nicht **markieren**. Das ist der Unterschied zu einem `<p>`, und er
     ist der Preis dafuer, dass es **ein** Element gibt statt zweier. */
  pointer-events: var(--sticker-feld-zeiger);
}

/* **Der Rueckfall fuer Browser ohne `field-sizing`.**

   Dort bleibt `rows` die Hoehe, und die stammt aus dem **alten** Text - der
   Server kann nicht wissen, was jemand gleich tippt. Ohne diese Zeile
   schriebe man dort in ein Feld, das nach der dritten Zeile scrollt, *und der
   Anfang des eigenen Satzes waere weg.*

   **8 Zeilen sind `TEXT_LAENGE / ZEICHEN_JE_ZEILE`** (160 / 20, gerechnet im
   Blueprint) - mehr kann kein Zettel brauchen. Die Zahl wird gegen den Code
   gehalten (`test_die_hoechste_zeilenzahl_steht_im_code_und_im_blatt`).

   **Hinter `@supports`, damit sie nicht beide gelten**: Mit `field-sizing`
   spraenge der Zettel beim Hineinklicken auf acht Zeilen, auch fuer ein Wort.
   Das ist keine Breitenfrage, also kein `@media` und kein Verstoss gegen
   Regel 0. */
@supports not (field-sizing: content) {
  .sticker-feld:focus {
    min-height: calc(8 * 1.4em);
  }
}

/* **Der Knopf ist erst da, wenn jemand den Zettel anfasst.**

   Ein Brett mit zwanzig Speichern-Knoepfen waere kein Brett. `:focus-within`
   ist dieselbe Mechanik, mit der FAM-109 den Feldhinweis am Handy
   zurueckholt - **ohne eine Zeile JavaScript**, und sie greift auch bei der
   Tabulatortaste: *Ein Knopf, den nur die Maus findet, ist fuer ein Kind am
   Touchscreen keiner.*

   **`display: none` und nicht `visibility: hidden`** - gemessen, nicht
   gewaehlt: Mit `visibility` kostete jeder Zettel **50 px**, auch der, den
   niemand anfasst. Bei zehn Zetteln ist das ein halber Bildschirm fuer einen
   Knopf, den niemand sieht.

   **Der Preis ist ein Sprung**, und er ist der kleinere: Er kommt **nach**
   dem Klick, nicht davor - wer ihn sieht, hat sein Ziel schon getroffen. */
.sticker-speichern {
  display: none;
  margin-top: 6px;
}
.sticker:focus-within .sticker-speichern {
  display: inline-flex;
}

/* --- In welchen Bereich er soll (28.09.2026) ------------------------------

   **Erst der Weg, dann das Ziehen obendrauf** - woertlich die Reihenfolge
   der Familienprojekte (FAM-175, dann FAM-180). Wer keine Maus hat oder
   dessen Skript nicht laedt, kommt hier genauso in jeden Bereich.

   **Wo gezogen wird, steht es erst da, wenn jemand den Zettel anfasst** -
   dieselbe Mechanik wie beim Speichern-Knopf darueber (`:focus-within` und
   `:hover`, ohne eine Zeile JavaScript). *Ein Brett mit zwanzig
   Auswahlfeldern waere kein Brett.*

   **Wo NICHT gezogen wird, steht es immer da** (`--sticker-bereich-anzeige`,
   Regel 0), und das ist der Fund vom 28.09.2026:

   > Am Handy traegt das Aenderungsfeld `pointer-events: none`
   > (`--sticker-feld-zeiger`, FAM-168) - **ein Tipp darauf fokussiert also
   > nichts**, `:focus-within` loest nie aus, und `:hover` gibt es dort gar
   > nicht. **Das Auswahlfeld waere unerreichbar gewesen** - und gezogen wird
   > am Handy ebenfalls nicht. *Ein Zettel liesse sich dort in keinen Bereich
   > haengen, und die Maske saehe dabei vollstaendig aus.*
   >
   > **Gefunden hat es die Messung, nicht der Test** - im HTML steht das Feld
   > so oder so.

   **Die beiden Variablen gehoeren zusammen**, und das ist bewacht
   (`test_wo_nicht_gezogen_wird_steht_das_feld_da`): *Wo `--sticker-ziehen`
   auf `default` steht, steht `--sticker-bereich-anzeige` auf `flex`.*

   **`flex-wrap`, damit die Auswahl und der Knopf umbrechen duerfen**: Am
   Handy ist ein Zettel so breit wie sein Bereich, am Tablet 28ch - und eine
   Auswahlliste mit dem Wort *„Noch nicht einsortiert"* passt dort nicht
   neben einen Knopf. */
.sticker-bereich {
  display: var(--sticker-bereich-anzeige);
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
  margin-top: 6px;
}

.sticker:hover .sticker-bereich,
.sticker:focus-within .sticker-bereich {
  display: flex;
}

/* **Die Auswahl darf schrumpfen** - ohne `min-width: 0` haelt ein `select`
   seine Mindestbreite und spraengte den Zettel (die Lehre aus FAM-177).

   **Das Feld nimmt die ganze Zeile, der Knopf kommt darunter**, und das ist
   gemessen (28.09.2026): Die laengste Antwort heisst *„Noch nicht
   einsortiert"* und braucht rund 180 px; neben dem Knopf blieben am Handy
   **159** - sie stand dort als **„Noch nicht einsorti"**, *abgeschnitten an
   der Stelle, an der die Auskunft steht.*

   **`100%` und keine Zahl in `ch`**, obwohl die naeherliegt: `ch` misst
   **die Schrift dieses Feldes**, und die ist je Ansicht eine andere - eine
   Basis, die am Handy umbricht, tut es am Desktop vielleicht nicht. *Eine
   Zahl, die je Ansicht etwas anderes bedeutet, ist keine Entscheidung,
   sondern ein Versuch* (die Lehre aus „`ch` misst den Text, nicht das
   Feld").

   **Der Preis ist gemessen**: am Handy 45 px je Zettel. Ab Tablet kostet er
   nichts, denn dort steht das Feld erst da, wenn jemand den Zettel anfasst
   (`--sticker-bereich-anzeige`). */
.sticker-bereich select {
  min-width: 0;
  flex: 1 0 100%;
}

/* --- Unten in klein: von wem und wann ------------------------------------ */

.sticker-fuss {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px var(--abstand);
  margin: 8px 0 0;
  /* **Klein, aber nicht kleiner als lesbar.** Gerechnet aus der Schrift der
     Ansicht und nicht hingeschrieben (Regel 0, Regel 10) - eine feste
     Pixelzahl waere am Infoboard aus zwei Metern nicht mehr zu lesen.

     **Eine Bedienflaeche ist das hier nicht**, also greift die Wache aus
     FAM-109 nicht: Es ist Text, kein Knopf. */
  font-size: 0.8em;
}
.sticker-wer { font-weight: 600; }
.sticker-wann { color: var(--text-leise); }

/* --- Abnehmen ------------------------------------------------------------ */

/* **Oben rechts am Zettel**, wie die Ecke, an der man einen echten Klebezettel
   abzieht. */
.sticker-weg {
  position: absolute;
  top: 4px;
  right: 4px;
  margin: 0;
}

/* **Die Bedienflaeche bleibt `--schaltflaeche`** (Regel 10) - auch ohne
   Rahmen: Was man trifft, ist groesser als das, was man sieht, und das ist
   hier richtig herum (der Durchgang vom 18.09.2026).

   **Keine Kontur und keine Fuellung** - dieselbe Bauart wie die
   Zeichenknoepfe des Einkaufs: nur das Zeichen, in der Fehlerfarbe, denn
   Abnehmen nimmt wirklich weg. */
.sticker-wegknopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--fehler);
}

/* ===========================================================================
   12. Das echte Brett (FAM-169)
   ===========================================================================
   *„Das Board soll auch extrem echt wirken.“* - Karte 4 des Epics FAM-165.

   **Alles, was einen Zettel echt macht, steht hier**: Papierfarbe, Drehung,
   Schatten, Papierkante und der Untergrund. Karte 2 hat das Board bedienbar
   gemacht und ausdruecklich nuechtern gelassen; *wer beides in eine Karte
   legt, misst am Ende keins von beidem.*

   **Ein Abschnitt, in einem Griff zurueckzunehmen** - wie die Frische Ebene
   (18.09.2026). Er steht hinter ihr und gewinnt damit gegen alles davor:
   genau der Mechanismus, an dem FAM-107 gestolpert ist, hier mit Absicht
   benutzt. Was er ueberschreibt, steht in Abschnitt 11 unter *Das Sticker
   Board (FAM-167)* und bleibt dort lesbar.

   **Keine Zeile JavaScript, kein Bild von einer fremden Adresse** (Regel 4),
   **kein `@media`** (Regel 0): Was je Ansicht verschieden ist, sind
   `--brett-korn` und `--sticker-neigung` aus Abschnitt 1 der vier
   Wertesaetze, und beide stehen in Design.md Regel 10.

   **Hier stand: keine Handschrift-Schrift.** Der Satz ist seit dem
   24.09.2026 ueberholt, und zwar an der Stelle, die er selbst genannt hat.
   Er lautete: *„Eine von Google Fonts verstiesse gegen Regel 4; eine selbst
   ausgelieferte waere erlaubt und trotzdem die falsche Wahl - sie wiegt,
   und sie ist fuer ein Kind schlechter lesbar als die Systemschrift."*

   **Beide Einwaende treffen `--schrift-hand` nicht.** Es ist ein Stapel
   **Systemschriften** (*Segoe Print*, *Bradley Hand*, *Comic Sans MS*), er
   laedt nichts von einer fremden Adresse und wiegt **null Bytes** - genau
   die Bedingung, unter der der alte Satz eine Handschrift zugelassen
   haette. *Wer es doch will, misst vorher* stand dort; gemessen ist es im
   Ansichtsdurchgang dieser Aenderung.

   **Der Rest des Einwands bleibt stehen und ist der Preis**: Eine
   Handschrift ist fuer ein Kind schlechter zu lesen als eine Grotesk.
   Deshalb traegt sie **nur der Zetteltext** - Fusszeile, Quelle und jeder
   Knopf bleiben in der Systemschrift, *und die Zeilenhoehe ist eigens
   nachgemessen worden.* Wer es zurueckdreht, nimmt `font-family` aus
   `.sticker-text` heraus: **eine Zeile.**
   =========================================================================== */

/* --- Der Untergrund ------------------------------------------------------

   **Seit dem 24.09.2026 ist es dieselbe Moderationswand wie am
   Projektbrett** - auf Wunsch: *„Bitte baue das Sticker Board so um wie das
   Board fuer Projekte aussieht."*

   **Den Untergrund liefert jetzt `.brettwand`** (Abschnitt 14): Filz im
   Aluminiumrahmen, vier Eckstuecke, gezeichnet vom Baustein
   `_brettwand.html`. Diese Regel hier traegt deshalb **keinen Hintergrund
   mehr** - sie ordnet nur noch die Zettel an.

   > **Was hier bis dahin stand, war ein Whiteboard**, und es war die
   > Vorlage des Nutzers (FAM-166: *ein Whiteboard mit vier kleinen farbigen
   > Zetteln*). **Er hat es ueberstimmt**, und das ist seine Entscheidung -
   > *eine Vorlage von vorletzter Woche ist kein Einwand gegen einen Wunsch
   > von heute.* Die Zeilen sind weg und nicht auskommentiert: Ein zweiter
   > Untergrund, den niemand sieht, waere die zweite Stelle.

   **`--brett` und `--brett-linie` sind damit unbenutzt** - **gemessen, und
   die Messung hat eine Behauptung von mir widerlegt**: Hier stand zuerst,
   die Einkaufsliste benutze sie weiter. Sie tut es nicht; die beiden Farben
   haben mit dieser Aenderung ihren einzigen Leser verloren und sind aus
   Abschnitt 1 **entfernt**. *Eine Farbe, die niemand liest, ist keine
   Reserve, sondern eine Zeile, die beim naechsten Mal jemand benutzt, weil
   sie dasteht.*

   **Das Layout einer Zettelreihe steht weiter in Abschnitt 11**
   (`.stickerliste`: `display: flex`, `flex-wrap`, `align-items:
   flex-start`) und **nicht hier**: Es ist dasselbe wie vorher, und *zwei
   Regeln fuer dieselbe Anordnung laufen auseinander.*

   **Der Innenabstand faengt die Drehung.** Ein gedrehter Zettel braucht
   seitlich mehr Platz als ein gerader - ohne ihn stuende er ueber den Rand
   des Filzes, **und die Wand scrollte seitwaerts**, wo nichts zu scrollen
   ist (Regel 2). Er ist kleiner als der alte `--abstand`, denn den Abstand
   zum Rahmen bringt jetzt `.brettwand` mit (`--brett-innen-x/y`).

   > **Seit dem 28.09.2026 traegt `.stickerboard` das Raster der Bereiche**
   > und keine Zettel mehr - die Regel dazu steht ein Stueck weiter unten,
   > bei den Bereichen. *Sie steht dort und nicht hier, weil sie die
   > Einteilung der Wand beschreibt und nicht ihren Untergrund.* */

/* --- Die Wand nimmt den Platz, den es gibt -------------------------------

   **Auf Wunsch** (24.09.2026): *„Das Board soll immer die Bildschirmgroesse
   komplett ausnutzen."*

   **Ohne eine einzige gerechnete Zahl**, und das ist der Kern: Hier stand
   zuerst `min-height: calc(100dvh - Kopfhoehe - Knopf - 5 * Abstand)`.
   **Gemessen blieben unten 90 px frei** - die Summe war um genau die
   Polsterung daneben, die sie nicht kannte.

   *Eine Zahl, die beschreibt, was ueber dem Brett steht, ist eine
   Behauptung ueber jede kuenftige Zeile darueber* (Regel 5). Der
   Inhaltsbereich reicht ohnehin bis zum unteren Fensterrand (gemessen: 844
   von 900 px, `overflow: auto`) - **also nimmt die Wand schlicht den
   Rest.**

   **`min-height: 0` ist die Zeile, die man vergisst**: Ein Flex-Kind
   schrumpft nicht unter seinen Inhalt, und die Wand waere dann so hoch wie
   ihre Zettel - *`overflow: auto` haette nichts zu scrollen, und die Seite
   scrollte wieder* (Regel 2).

   **Der Filz behaelt sein `overflow: auto`** (Regel 2): Passen mehr Zettel
   hinein, als Platz ist, scrollt **die Wand** - nicht die Seite. */
.inhalt:has(.stickerboard) {
  display: flex;
  flex-direction: column;
}

.brettwand-raum:has(.stickerboard) {
  display: flex;
  flex: 1;
  min-height: 0;
  /* **Am Sticker Board ist die Wand das letzte Element** - der Abstand nach
     unten stammt vom Projektbrett, wo darunter noch etwas kommt. Gemessen
     kostete er 20 von 70 uebrigen Pixeln. */
  margin-bottom: 0;
}

.brettwand-raum:has(.stickerboard) > .brettwand-rahmen {
  display: flex;
  flex: 1;
  min-width: 0;
}

/* **`min-height: 0` ist die Zeile, die man vergisst**: Ein Flex-Kind
   schrumpft nicht unter seinen Inhalt, und die Wand waere dann so hoch wie
   ihre Zettel statt so hoch wie der Bildschirm - *`overflow: auto` haette
   nichts zu scrollen, und die Seite scrollte wieder* (Regel 2). */
.brettwand-raum:has(.stickerboard) > .brettwand-rahmen > .brettwand {
  flex: 1;
  min-width: 0;
  min-height: 0;
}

/* **Und das Raster fuellt die Wand** - sonst staenden sechs flache
   Bereiche oben und darunter leerer Filz, *und das saehe aus, als sei das
   Brett zu gross geraten.* */
.brettwand-raum:has(.stickerboard) .stickerboard {
  min-height: 100%;
}

/* --- Die Bereiche (28.09.2026, auf Wunsch) --------------------------------

   *„Bitte das Sticker Board in Bereiche einteilen. Diese Bereiche koennen
   dann je nach Groesse des Bildschirmes vernuenftig mit dem Sticker
   verschoben werden."*

   **Wie viele es GIBT, steht in `app/brettwand.py`** (sechs) - **wie viele
   NEBENEINANDER stehen, hier** (`--bereich-spalten`, Regel 0): am Handy
   einer, am Tablet zwei, ab Desktop drei. *Die Zahl der Bereiche ist ein
   Datum und in jeder Ansicht dieselbe; ihre Anordnung ist Gestaltung.*

   **`minmax(0, 1fr)` und nicht `1fr`**: Ein Rasterfeld schrumpft sonst nicht
   unter seinen Inhalt, und ein einziger langer Zettel spraengte die Spalte -
   *die Wand scrollte seitwaerts, wo nichts zu scrollen ist* (Regel 2, die
   Lehre aus FAM-177).

   **`align-content: start`**: Die Bereiche sollen oben stehen und nicht auf
   der Hoehe verteilt werden - die Wand ist so hoch wie der Bildschirm, die
   Zettel sind es nicht.

   > **Hier stand bis zum 28.09.2026 `position: relative`** - der Bezug fuer
   > einen frei gesetzten Zettel. *Es gibt keinen mehr*, und eine Zeile ohne
   > Leser ist keine Reserve (die Lehre vom 27.09.2026). */
.stickerboard {
  padding: 4px 2px;
  display: grid;
  grid-template-columns: var(--bereich-spalten);
  align-content: start;
  gap: var(--abstand);
}

/* **Ein Bereich ist ein Stueck Wand, kein Kasten.**

   **Ein gestrichelter Umriss und kein Rahmen** - woertlich die Loesung von
   FAM-179 und FAM-180: `border` kostete Aussenmass, und der Bereich spraenge
   beim Darueberziehen um zwei Pixel zur Seite. *Ein Ziel, das wackelt, sieht
   aus, als weiche es aus.*

   **Er sieht aus wie Kreppband auf Filz**, und das ist die Vorlage des
   Nutzers: das Foto seiner Wohnzimmerwand, auf dem Kreppband die Spalten
   macht (FAM-174).

   **`min-height`, damit ein leerer Bereich ein Ziel ist**: Ohne sie waere er
   so hoch wie seine Ueberschrift - *man traefe ihn mit dem Finger nicht, und
   das Brett haette sechs Felder, von denen fuenf nicht annehmen.* Gerechnet
   aus `--schaltflaeche`, nicht hingeschrieben (Regel 0). */
/* --- Der Griff am Zettel (23.10.2026) ------------------------------------

   **Die Flaeche, an der ein Zettel sicher traegt.** Er ist noetig, weil der
   Zettel zum groessten Teil ein Eingabefeld ist: Ein Tipp in die obere
   Haelfte trifft das `<textarea>`, und das Ziehskript laesst ihn dann in
   Ruhe (*„Wer auf ein Bedienelement tippt, bedient es"*). **Gemessen am
   23.10.2026**: `elementFromPoint` in der Zettelmitte liefert TEXTAREA.

   **Er sieht aus wie ein Griff und nicht wie ein Knopf** - zwei Striche,
   wie man sie von Listen kennt, in der Linienfarbe. *Ein Knopf verspricht
   einen Klick; dies ist eine Flaeche zum Anfassen.*

   **`touch-action: none` steht NUR hier** (`--sticker-zug`, Regel 0): Am
   ganzen Zettel naehme es dem Finger das Scrollen ueber einem vollen Brett.
   Am Handy bleibt es `auto` - dort wird nicht gezogen. */
.stickergriff {
  /* **Wo nicht gezogen wird, gibt es ihn nicht** (`--sticker-griff`,
     Regel 0): Am Handy stehen die Bereiche untereinander, dort fuehrt die
     Auswahlliste am Zettel ans Ziel. *Ein Griff, an dem nichts geht, ist
     eine Zusage, die niemand einloest* (Regel 7) - und er kostete dort
     gemessen **54 px je Zettel**. */
  display: var(--sticker-griff);
  height: var(--knopf-flaeche);
  margin: calc(var(--abstand) / -2) calc(var(--abstand) / -2) 0;
  border-radius: var(--radius) var(--radius) 0 0;
  cursor: var(--sticker-ziehen);
  touch-action: var(--sticker-zug);
  background-image:
    linear-gradient(var(--linie), var(--linie)),
    linear-gradient(var(--linie), var(--linie));
  background-size: 28px 2px, 28px 2px;
  background-position: center calc(50% - 3px), center calc(50% + 3px);
  background-repeat: no-repeat;
  opacity: 0.55;
}

.sticker.wird-getragen .stickergriff { cursor: grabbing; }

/* --- Die Zielleiste (23.10.2026) -----------------------------------------

   *„Auf dem Stickerboard lassen sich die Elemente nicht mehr ziehen."*

   **Sie ist die Antwort auf eine Messung, nicht auf einen Geschmack**: Am
   Tablet stehen die Bereiche untereinander, und gemessen liegt Bereich 1
   bei y = 1004, Bereich 6 bei **y = 1584** - bei 1024 px Fensterhoehe. *Ein
   Zug dorthin geht quer durch eine scrollende Wand*, und genau deshalb war
   das Ziehen dort seit dem 28.09.2026 abgeschaltet.

   **Jetzt kommt das Ziel zum Zettel**: Sobald jemand einen Zettel anfasst,
   liegt unten eine Leiste mit allen Bereichen. Der Weg ist damit **hoechstens
   eine Bildschirmhoehe** lang, egal wie lang das Brett ist.

   **Sie gilt in allen vier Ansichten**, und das ist eine Entscheidung: Auch
   am Infoboard - einem Touchscreen aus zwei Metern - ist ein 72 px hoher
   Streifen ein kleines Ziel. *Wer lieber auf den Bereich selbst zieht, tut
   es weiterhin; die Leiste nimmt nichts weg.*

   **Ohne Skript gibt es sie nicht** (Regel 7): Sie haengt an `.zieht`, und
   diese Klasse setzt allein das Ziehskript. *Ein Bedienelement, das ohne
   Skript dasteht und nichts tut, gehoert nicht ins HTML.* */
.zielleiste {
  display: none;
}

.stickerboard.zieht .zielleiste {
  display: flex;
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  gap: calc(var(--abstand) / 2);
  padding: calc(var(--abstand) / 2);
  justify-content: center;
  flex-wrap: wrap;
  background: var(--flaeche);
  border-top: 1px solid var(--linie);
  box-shadow: 0 -8px 24px oklch(from var(--schatten) l c h / 0.25);
}

/* **Jeder Platz ist so gross wie ein Knopf** - `--knopf-flaeche` ist der
   Boden, nicht die Decke (Abschnitt 15). *Ein Ziel, das kleiner ist als ein
   Finger, ist keines.* */
.zielplatz {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0 var(--abstand);
  border-radius: var(--radius);
  border: 2px dashed var(--linie);
  color: var(--text-leise);
  font-size: var(--schrift);
}

/* **Dieselbe Rueckmeldung wie am Bereich** (`.zielt`, FAM-180) - *zwei
   Namen fuer dieselbe Auskunft laufen auseinander.* */
.zielplatz.zielt {
  border-style: solid;
  border-color: var(--akzent);
  color: var(--akzent);
  background: oklch(from var(--akzent) l c h / 0.12);
}

.stickerbereich {
  min-width: 0;
  min-height: calc(var(--schaltflaeche) * 2);
  padding: calc(var(--abstand) * 0.5);
  border-radius: var(--radius);
  outline: 2px dashed oklch(from var(--glanz) l c h / 0.35);
  outline-offset: -2px;
}

/* **Das Band *Noch nicht einsortiert* laeuft ueber die ganze Breite** - es
   ist **keine siebte Zelle, sondern eine Lage** (FAM-176). Es steht oben,
   weil dort jeder neue Zettel landet.

   **Und es traegt einen durchgehenden Umriss statt eines gestrichelten**:
   *Ein Bereich ist ein Platz, den jemand gewaehlt hat; dies ist der Rest.* */
.stickerbereich-ohne {
  grid-column: 1 / -1;
  outline-style: solid;
  outline-color: oklch(from var(--glanz) l c h / 0.22);
}

/* **Die Nummer steht auf einem Schild aus Packpapier** - woertlich die
   Loesung des Projektbretts fuer denselben Fall (`.brettband-name`): *Auf
   Filz ist Text nicht zu lesen.*

   **Gemessen, nicht befuerchtet** (28.09.2026): Die erste Fassung schrieb
   die Nummer in `--text-leise` (oklch 0.48) auf den Filz (oklch 0.56) -
   **1,2 : 1**, also unsichtbar. *Auf dem Bildschirmfoto sah der Bereich
   dabei aus wie ein leerer Kasten, nicht wie einer ohne Beschriftung.*

   **Ein Schild ist kein Name** (die Entscheidung des Nutzers: *ein festes
   Raster ohne Namen*) - es traegt eine **Nummer**, damit man sagen kann, wo
   ein Zettel haengt. Ein Name braeuchte eine Tabelle, eine Pflegemaske und
   ein Recht, und das ist die Projektzeile (FAM-176). */
.stickerbereich-kopf {
  display: inline-block;
  margin: 0 0 calc(var(--abstand) * 0.5);
  padding: 2px 10px;
  border-radius: 3px;
  background: var(--papier-kraft);
  box-shadow: 0 1px 2px oklch(from var(--schatten) l c h / 0.3);
  font: 400 0.85em/1.5 var(--schrift-hand);
  color: var(--papier-tinte);
}

/* **Das Band *Noch nicht einsortiert* traegt blasseres Papier und Kursiv** -
   woertlich `.brettband-ohne > .brettband-name`: *Es ist keine Zelle,
   sondern der Rest.* */
.stickerbereich-ohne > .stickerbereich-kopf {
  background-color: var(--papier-kraft-blass);
  font-style: italic;
}

/* **Das Ziel zeigt sich, solange ein Zettel darueber haengt** - dieselbe
   Klasse, dieselbe Anmutung und dieselbe Begruendung wie am Projektbrett
   (FAM-180, `.brettspalte.zielt`): Auf dem Filz ist Weiss die Farbe, die
   sich abhebt, *ohne eine fuenfte Bedeutung einzufuehren.*

   **`.zielt` steht nur waehrend des Ziehens da** und wird beim Loslassen
   weggenommen - ohne Skript gibt es die Klasse nirgends. */
.stickerbereich.zielt {
  outline-style: solid;
  outline-color: var(--glanz);
  background-color: oklch(from var(--glanz) l c h / 0.14);
}

/* --- Die Wand zeigt ihre Einteilung nur waehrend des Zuges ----------------
   (29.09.2026, auf Wunsch: *„Auf dem Stickerboard moechte ich die Bereiche
   nur sehen, wenn ich einen Sticker auf dem Board bewege."*)

   **Ohne Zug ist die Wand eine Wand** - Filz mit Zetteln darauf, wie am
   Korkbrett im Flur. Die sechs gestrichelten Kaesten und ihre Schilder
   waren **eine Hilfslinie, die immer dastand**.

   **`outline` und nicht `border`** (schon seit dem 28.09.): Ein Umriss
   nimmt keinen Platz, also springt beim Einblenden **nichts**. Gemessen an
   allen sechs Bereichen: dieselbe Hoehe mit und ohne `.zieht`.

   **Die Auskunft geht nicht verloren**, und das ist die Bedingung dafuer,
   dass man sie ausblenden darf: Wo **nicht** gezogen wird (Handy, Tablet),
   traegt jeder Zettel seine Bereichswahl (`--sticker-bereich-anzeige`), und
   dort steht die Nummer im Klartext. */
.stickerbereich { outline-color: transparent; }
.stickerbereich > .stickerbereich-kopf { opacity: 0; }

.stickerboard.zieht .stickerbereich {
  outline-color: oklch(from var(--glanz) l c h / 0.35);
}
.stickerboard.zieht .stickerbereich-ohne {
  outline-color: oklch(from var(--glanz) l c h / 0.22);
}
.stickerboard.zieht .stickerbereich > .stickerbereich-kopf { opacity: 1; }

/* **Sie kommen und gehen weich** - ein Umriss, der im Moment des Anfassens
   aufblitzt, sieht aus wie ein Fehler. Wer keine Bewegung will, bekommt
   keine (dieselbe Abfrage wie an den Textmarkern). */
.stickerbereich,
.stickerbereich > .stickerbereich-kopf {
  transition: outline-color 0.15s ease, opacity 0.15s ease;
}
@media (prefers-reduced-motion: reduce) {
  .stickerbereich,
  .stickerbereich > .stickerbereich-kopf { transition: none; }
}

/* --- Der Zettel haengt am Zeiger -----------------------------------------
   *„Beim Projektboard kann ich sehen wie ich die Karte irgendwo hin
   schiebe."* - Dort zeichnet **der Browser** eine Kopie (HTML5-Drag); mit
   Pointer-Ereignissen blieb der Zettel liegen und wurde nur blass.

   **Die Zahlen kommen aus dem Skript, die Gestaltung steht hier**
   (Regel 10): `--zug-x`/`--zug-y` sind die Mausbewegung, alles andere -
   dass er sich hebt, gerade stellt und groesser wird - ist eine
   Entscheidung dieses Blattes.

   **Er stellt sich gerade** (`--sticker-neigung` faellt weg): Ein Zettel in
   der Hand haengt nicht schief an der Wand. */
.sticker.wird-getragen {
  transform: translate(var(--zug-x, 0), var(--zug-y, 0)) scale(1.04);
  transition: none;
  pointer-events: none;
  z-index: 3;
  cursor: grabbing;
  box-shadow: 0 10px 22px oklch(from var(--schatten) l c h / 0.35);

  /* **Und er ist nicht mehr blass.** Vom 28. auf den 29.09.2026 **umgekehrt**,
     und der Grund ist der Zug selbst: `opacity: 0.35` hiess *„der liegt noch
     da, aber er ist in der Hand"* - eine Auskunft, die man **braucht**,
     solange der Zettel sich nicht bewegt. **Jetzt bewegt er sich**, und ein
     blasser Zettel am Zeiger ist einer, den man beim Zielen nicht lesen kann.

     *Eine Begruendung kann ablaufen, ohne falsch zu werden* - sie nannte die
     Lage, in der sie galt. */
  opacity: 1;
}

/* **Der Zeiger sagt es vorher** - und er haengt an `data-setzweg`, also an
   der Bedingung, die die Maske setzt: Wer nicht verschieben darf, bekommt
   das Attribut gar nicht erst, *und damit auch keinen Zeiger, der etwas
   verspricht, das der Server abweist* (Regel 7, woertlich FAM-180).

   **Nur wo es Bereiche nebeneinander gibt** - am Handy stehen sie
   untereinander, dort ist das Ziehen abgeschaltet (siehe
   `sticker-ziehen.js`). */
.sticker[data-setzweg] {
  cursor: var(--sticker-ziehen);

  /* **Und der Finger zieht, statt zu scrollen** (23.10.2026, auf
     Rueckmeldung: *„Schon erst recht nicht, wenn ich es mit einem Finger
     auf dem Tablet versuche."*).

     **Ohne diese Zeile ist das Ziehen auf JEDEM Beruehrungsbildschirm
     unmoeglich** - auch am Infoboard, wo es eingeschaltet war: Der Browser
     wertet die erste Fingerbewegung als Scrollen, schickt `pointercancel`
     und nimmt dem Skript den Zettel aus der Hand. *Gemessen am 23.10.2026:
     `touch-action: auto` in allen vier Ansichten.*

     **Nur wo gezogen wird** (`--sticker-zug`, Regel 0): Am Handy bleibt
     `auto`, sonst liesse sich dort an einem vollen Brett nicht mehr
     scrollen - und gezogen wird am Handy ohnehin nicht. */
}

/* --- Der Zettel ----------------------------------------------------------

   **Die Papierfarbe kommt aus dem FARBTON, den der Blueprint mitgibt** -
   demselben, den der Schreiber traegt (`app/personenfarben.py`). *Gewaehlt
   beim Anheften, sonst die Farbe des Schreibers* - was davon gilt,
   entscheidet der Server, nicht das Blatt.

   **Seit dem 22.09.2026 sind es keine sechs Nummern mehr, sondern ein
   Grad** - und damit traegt ein Zettel jede Farbe, die sich sein Mensch
   ausgesucht hat, ohne dass hier eine Zeile dazukommt.

   **`--papier` als Vorgabe ist kein toter Code**: Ohne sie waere ein Zettel
   ohne Papierklasse durchsichtig - und das saehe nicht kaputt aus, sondern
   nach einem weissen Zettel auf weissem Grund. */
.sticker {
  --papier: var(--flaeche);
  background-color: var(--papier);

  /* **Papier hat keine runden Ecken** (24.09.2026) - woertlich die
     Entscheidung der Projektkarte, als sie an die Filzwand kam: Bis dahin
     trug der Zettel einen unregelmaessigen Radius, *das war die Anmutung
     eines gerissenen Zettels; hier ist es geschnittenes Papier.*

     **Er ueberschreibt die unregelmaessige Rundung weiter unten in diesem
     Abschnitt nicht** - sie ist weggefallen und nicht ueberdeckt: *Eine
     Regel, die eine andere aufhebt, ist zwei Stellen fuer eine Kante.* */
  border-radius: 0;

  /* **Der Schimmer auf dem Papier** - dieselbe Rezeptur wie an der
     Projektkarte, nur eine Spur flacher (172deg dort, 168deg hier): Ein
     Zettel liegt anders im Licht als eine Karteikarte. Die Farben stehen in
     Abschnitt 1. */
  background-image: linear-gradient(168deg,
                                    oklch(from var(--glanz) l c h / 0.4),
                                    transparent 42%,
                                    oklch(from var(--schatten) l c h / 0.06));

  /* **Platz fuer die Nadel** - sie sitzt oben mittig auf dem Zettel. Ohne
     ihn laege sie auf der ersten Textzeile, *und das saehe nicht nach einer
     Nadel aus, sondern nach einem Fleck.* */
  padding-top: calc(var(--abstand) + 14px);

  /* **Die Drehung wird gerechnet, nicht gewuerfelt** - aus der Nummer des
     Zettels, die die Maske als `--nr` mitgibt.

     **Ein Zufall je Aufruf liesse das ganze Board bei jedem Neuladen
     springen**, und niemand wuesste warum: Wer einen Zettel abnimmt, faende
     danach alle anderen anders liegen. Aus der **Position** zu rechnen
     (`:nth-child`) waere derselbe Fehler - die neuesten haengen oben, also
     rueckt jeder neue Zettel alle anderen weiter.

     **`mod` und nicht Python** - die Karte sagt *„alles im Blatt“*, und es
     geht wirklich: Die Maske gibt die Nummer weiter, das Blatt rechnet.
     Gemessen in Chrome 153: `calc(mod(7, 5) * 10px)` ergibt 20px, also
     laeuft die Rechnung und nicht nur `CSS.supports`.

     **Der Rueckfall ist harmlos**: Ein Browser ohne `mod()` verwirft die
     ganze Deklaration - dann liegt jeder Zettel gerade. Nuechtern, nicht
     kaputt (dieselbe Bauart wie `field-sizing` in FAM-168).

     **5 und nicht 6, obwohl es sechs Farben gibt.** Waere es dieselbe Zahl,
     traege jeder Zettel derselben Farbe denselben Winkel - fuenf und sechs
     sind teilerfremd, also wiederholt sich das Muster erst nach dreissig.

     **`- 2` dreht in beide Richtungen**: `mod(...)` gibt 0 bis 4, das ergibt
     -2 bis +2 mal `--sticker-neigung`. Ein Board, auf dem alle Zettel in
     dieselbe Richtung haengen, sieht nicht nach Zufall aus, sondern nach
     einem Fehler. */
  rotate: calc((mod(var(--nr, 2), 5) - 2) * var(--sticker-neigung));

  /* **Ein Zettel an einer Wand wirft einen Schatten** (24.09.2026) -
     dieselben drei Lagen wie die Projektkarte, und die Schattenfarbe steht
     in Abschnitt 1 (`--schatten`).

     > **Bis hierher waren es zwei flache Lagen** mit einer ausgeschriebenen
     > `oklch(26% 0.03 150 / ...)`. Die Zahl war dieselbe wie `--text`, nur
     > abgeschrieben - *eine Farbe, die zweimal dasteht, ist zweimal zu
     > pflegen* (Regel 10). Mit der Wand ist sie durch die Variable
     > ersetzt. */
  box-shadow:
    0 1px 1px oklch(from var(--schatten) l c h / 0.2),
    0 8px 12px -6px oklch(from var(--schatten) l c h / 0.38),
    0 22px 26px -18px oklch(from var(--schatten) l c h / 0.4);
}

/* --- Die Stecknadel ------------------------------------------------------

   **Oben mittig, mit Schlagschatten nach rechts unten** - eine Nadel steht
   aus der Wand heraus, also wirft sie einen.

   **Dieselben fuenf Farben wie am Projektbrett** und **dieselbe Rechnung**
   (`app/brettwand.py`): *Zwei Bretter mit einer Wand haben eine Nadel.*

   **Welche, kommt aus Python** - und das ist der Unterschied zur Drehung
   daneben, die das Blatt rechnet (FAM-169): Aus einer Zahl **eine von fuenf
   Variablen auszuwaehlen** kann CSS nicht. `:nth-child` waere die
   **Position**, und die neuesten Zettel haengen oben - *jeder neue Zettel
   taeuschte damit alle anderen um.* */
.sticker::before {
  content: "";
  position: absolute;
  top: 7px;
  left: 50%;
  width: 14px;
  height: 14px;
  margin-left: -7px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%,
                              oklch(from var(--glanz) l c h / 0.9) 0 10%,
                              var(--nadel, var(--nadel-rot)) 42%,
                              var(--nadel-d, var(--nadel-rot-d)) 100%);
  box-shadow: 2px 5px 3px -1px oklch(from var(--schatten) l c h / 0.42);
}

/* --- Filzstift auf Papier -------------------------------------------------

   **Dieselbe Schrift wie am Projektbrett**, und sie steht an **einer**
   Stelle (`--schrift-hand`, Abschnitt 1). **Keine von einer fremden
   Adresse** (Regel 4): ein Stapel **Systemschriften**, der nichts laedt und
   nichts wiegt - genau die Bedingung, unter der der alte Einwand dieses
   Abschnitts eine Handschrift zugelassen haette.

   **Sie haengt an `.sticker-text` und nicht an `.sticker`**: So erbt das
   `<textarea>` sie mit, das seit FAM-168 dieselbe Klasse traegt - *zwei
   Schriften fuer denselben Text liefen auseinander, und man saehe es erst,
   wenn ein Zettel beim Anklicken springt.*

   **Und sie steht in diesem Abschnitt**, nicht in Abschnitt 11: Dort ist
   der Zettel bewusst nuechtern. *Wer die Handschrift zurueckdreht, nimmt
   diesen Block heraus - eine Zeile, wie der ganze Abschnitt.*

   **Die Zeilenhoehe ist gemessen**, am `<textarea>` - dem einzigen Element
   hier, dessen `overflow` nicht `visible` ist (*ein Ueberstehen ist nur
   dort ein Abschneiden*, der Fund desselben Tages):

   | Schrift | Zeilenhoehe | Felder, die abschneiden | groesster Rest |
   |---|---|---|---|
   | Systemschrift | 1.4 | **0 von 8** | 0 px |
   | Handschrift | 1.4 | **8 von 8** | 4 px |
   | Handschrift | 1.55 | 8 von 8 | 2 px |
   | **Handschrift** | **1.75** | **0 von 8** | **0 px** |

   **1.55 stand hier zuerst und war geraten** - die Messung hat es
   widerlegt. Es ist **dieselbe Zahl, die das Projektbrett am selben Tag
   gebraucht hat**, und das ist kein Zufall: dieselbe Schrift. */
.sticker-text {
  font-family: var(--schrift-hand);
  line-height: 1.75;
}

/* **Fuenf Faelle, fuenf Regeln** - und `NADELN` in `app/brettwand.py` sagt,
   wie viele es sind. Dass beides zusammenbleibt, misst
   `test_jede_nadelfarbe_hat_ihre_regel`: *Eine sechste Farbe ohne Regel
   waere ein Zettel mit roter Nadel, und niemandem fiele auf, warum.* */
.sticker[data-nadel="0"] { --nadel: var(--nadel-rot);   --nadel-d: var(--nadel-rot-d); }
.sticker[data-nadel="1"] { --nadel: var(--nadel-blau);  --nadel-d: var(--nadel-blau-d); }
.sticker[data-nadel="2"] { --nadel: var(--nadel-gelb);  --nadel-d: var(--nadel-gelb-d); }
.sticker[data-nadel="3"] { --nadel: var(--nadel-gruen); --nadel-d: var(--nadel-gruen-d); }
.sticker[data-nadel="4"] { --nadel: var(--nadel-weiss); --nadel-d: var(--nadel-weiss-d); }

/* **Woher ein Zettel kommt** (FAM-170) - klein und leise ueber dem Text.

   **Kein eigener Farbton.** `--text-leise` steht schon unter jedem Zettel
   (die Fusszeile), und ihr Kontrast auf jedem Papier ist gerechnet: 4,71 bis
   4,91 : 1 (Abschnitt 1, FAM-169). Eine zweite Farbe hier brauchte eine
   zweite Rechnung - und *eine Farbe, die niemand nachgerechnet hat, ist die,
   die am Infoboard verschwindet.*

   **Der Verweis erbt sie**, statt die Verweisfarbe zu nehmen: Ein blaues Wort
   auf einem gelben Zettel ist an einem echten Brett noch niemandem
   begegnet. */
.sticker-quelle {
  font-size: 0.85em;
  color: var(--text-leise);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  /* **Kein eigener Block** - das Wort steht VOR dem Text und fliesst mit ihm.
     Gemessen kostete eine eigene Zeile am Infoboard 47,2 px je Zettel, also
     **zwei Zettel weniger** auf einem Bildschirm. Woertlich die Bauart des
     Kalendereintrags (FAM-87). */
  white-space: nowrap;
}

/* **Dieselbe Umstellung wie bei der Personenfarbe** (22.09.2026): Die
   Rezeptur steht dort, wo der Ton liegt. Auf `:root` waere sie leer, und der
   Zettel bliebe weiss - *ohne dass irgendwo etwas kaputt aussieht.* */
.sticker.gefaerbt {
  --papierfarbe: oklch(var(--papier-helligkeit) var(--papier-buntheit) var(--papier-ton));
  --papier: var(--papierfarbe);
}

/* --- Der Name traegt die Farbe nicht mehr, das Papier traegt sie ---------

   **Das ist eine gemessene Entscheidung, keine Nachlaessigkeit.** Bis zu
   dieser Karte stand der Name in seiner Personenfarbe (FAM-123, FAM-167) -
   auf **weissem** Grund, und dort misst er 5,06 bis 5,82 : 1 (FAM-162).

   **Auf farbigem Papier faellt er unter 4,5 : 1**, gerechnet: 4,02 bis 4,66
   je nach Paarung, am schlechtesten *Tuerkis auf Lila*. Das Papier heller zu
   machen loest es nicht - erst bei 97% kaeme der Name auf 4,57, und dann ist
   kein Papier mehr zu sehen.

   **Die Farbe ist damit nicht weg, sie ist gewandert** - und sie steht
   weiterhin genau einmal da. Zweimal dieselbe Farbe uebereinander ist keine
   zusaetzliche Auskunft, sondern nur weniger Kontrast.

   **Und die Aussage traegt ohnehin das Wort** (FAM-65): Der Name steht unter
   dem Text, lesbar fuer den, der rot-gruen-blind ist, und aus zwei Metern am
   Infoboard. */
.sticker-wer { color: var(--text); }

/* ===========================================================================
   13. Der Gastzugang (FAM-34)

   **Die einzige Stelle, an der jemand ohne Anmeldung etwas sieht.** Zwei
   Masken haengen daran: die Gastseite selbst (sie erbt nicht von base.html,
   wie die Anmeldeseite) und der Block in der Umfragemaske, ueber den die
   Familie einlaedt.

   **Keine festen Masse**, wo es eine Merkmalsvariable gibt (Regel 0), und
   **kein @media** - die Haltepunkte stehen an einer Stelle.
   =========================================================================== */

/* --- Die Gastseite -------------------------------------------------------

   Sie benutzt `.anmeldekarte`, weil sie genauso gebaut ist: eine zentrierte
   Karte ohne Menue. **Nur breiter** - dort steht eine Anmeldemaske mit zwei
   Feldern, hier eine Umfrage mit Antworten, Ergebnis und Namen.

   `--maske-breite` statt einer eigenen Zahl: Am Handy `100%`, ab Tablet
   `46ch` - eine Lesebreite, die mit der Schrift der Ansicht waechst. */
.gastkarte { max-width: var(--maske-breite); }

/* Der Satz, der den Gast begruesst - und der, der die freundliche Seite
   traegt. Beide sind eine Aussage und keine Ueberschrift. */
.gasthinweis {
  margin: 0 0 var(--abstand);
  font-size: var(--schrift-gross);
  line-height: 1.3;
}

/* Die Frage selbst. `--schrift-gross` waere hier zu viel: Darueber steht
   schon die Begruessung in dieser Groesse, und zwei gleich grosse Saetze
   untereinander sagen nicht, welcher der Titel ist. */
.gastfrage {
  margin: var(--abstand) 0;
  font-size: calc(var(--schrift) * 1.25);
  line-height: 1.3;
}

/* --- Wer von aussen mitmachen darf (in der Umfragemaske) -----------------

   Ein eigener Block unter dem Ergebnis, abgesetzt durch eine Linie: Er
   gehoert zur Frage, ist aber etwas anderes als das Abstimmen. */
.gastzugaenge {
  margin-top: calc(var(--abstand) * 1.5);
  padding-top: var(--abstand);
  border-top: 1px solid var(--linie);
}

.gastliste {
  list-style: none;
  margin: 0 0 var(--abstand);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
}

.gastzeile {
  padding: calc(var(--abstand) * 0.75);
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
}

/* **Ein abgestellter Link ist gedaempft - aber das Wort traegt die Aussage**
   (FAM-65). Wer rot-gruen-blind ist, sieht eine blasse Zeile nicht, und am
   Infoboard aus zwei Metern verschwimmt sie; daneben steht deshalb
   „gilt nicht mehr“ als Text.

   **0.65 und nicht weniger**: Der Name muss lesbar bleiben - die Zeile ist
   genau die Auskunft, die beim Widerrufen gebraucht wird (wem sage ich
   Bescheid?). */
.gastzeile-tot { opacity: 0.65; }

.gastkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: calc(var(--abstand) * 0.5);
  margin: 0;
}

.gastname { font-weight: 600; }

.gastzustand {
  color: var(--text-leise);
  font-size: 0.92em;
}

/* **Die Adresse muss umbrechen duerfen.** Sie ist rund 50 Zeichen lang und
   hat keine Leerzeichen - ohne diese Zeile sprengt sie die Karte und die
   Seite scrollt seitwaerts (Regel 2).

   `anywhere` und nicht `break-word`: Letzteres bricht erst, wenn das Wort
   allein in einer Zeile steht, und dann steht es trotzdem ueber.

   **Eine Schreibmaschinenschrift**, weil man sie abliest und kopiert - und
   `user-select: all`, damit ein Tipp darauf die ganze Adresse markiert. Das
   ist der Ersatz fuer den Verweis, den es hier bewusst nicht gibt. */
.gastlink {
  margin: calc(var(--abstand) * 0.5) 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9em;
  color: var(--text-leise);
  overflow-wrap: anywhere;
  user-select: all;
}

.gastform { margin: 0; }

/* Das Formular zum Anlegen. Es steht unter der Liste, denn das Nachsehen ist
   der haeufige Vorgang und das Einladen der seltene - anders als beim
   Einkauf, wo das Eintragen oben steht (FAM-70).

   **Keine Breitenregel fuer das Feld darin**, und das ist eine Entscheidung,
   auf die mich eine Wache gestossen hat: `.gastneu .feld { flex: 1 1 14ch }`
   stand hier und loeste `test_die_breitenregel_gilt_fuer_jede_maske` aus -
   woertlich die Lehre aus FAM-85 und FAM-108: *Eine Regel hinter einer
   fremden Klasse hilft der naechsten Maske nicht.*

   **Sie zu tarnen waere die falsche Abhilfe** (`> label` statt `.feld` haette
   dasselbe getan und die Wache umgangen). Das Feld steht jetzt **ueber** dem
   Knopf, wie in jeder anderen Maske - und es gibt keinen Grund, warum
   ausgerechnet dieses eine Formular nebeneinander stehen muesste. */
.gastneu { margin-top: var(--abstand); }

/* ===========================================================================
   14. Das Projektbrett (FAM-175)
   ===========================================================================
   *„Groessere Vorhaben mit mehreren Schritten, als Kanban-Brett.“* Karte 2
   des Epics FAM-173 - die erste Maske des Moduls.

   **Zwei Masken haengen daran**: die Liste der Vorhaben (`/projekte`) und das
   Brett eines Vorhabens (`/projekte/<n>`).

   **Kein `@media` hier** (Regel 0): Ob die vier Spalten neben- oder
   untereinander stehen, entscheidet `--board-spalten` aus Abschnitt 2 der
   sieben Wertesaetze - eine Media Query kennt die Ansichtsauswahl nicht, und
   *wer am Desktop „Handy“ waehlt, bekaeme trotzdem das Gitter.*

   **Und keine Zeile JavaScript.** Verschoben wird mit einem Formular, das
   Aendern steckt in einem `<details>` (wie der Erledigt-Block, FAM-65, und
   die Leihzeile, FAM-136).
   =========================================================================== */

/* --- Eintragen, und zwar ganz oben ---------------------------------------

   **Eine Regel fuer beide Masken**: Das Feld fuer ein neues Vorhaben und das
   fuer eine neue Karte sind dieselbe Sache - ein Griff, kein Vorgang. Zwei
   Regeln liefen auseinander, und man saehe es erst, wenn eines davon am Handy
   umbricht und das andere nicht.

   **`wrap`, wie beim Sticker Board** (FAM-167) und bei Verliehen (FAM-136):
   Eine Zeile, die eine Aktion abschneidet, ist bei keiner Scrollposition
   erreichbar (FAM-50, FAM-84).

   Die `6px` sind die Zeilenluft aus `.sticker-neu` - **kein neuer Wert**,
   sondern derselbe wie bei der Eintragezeile daneben. */
.projekt-neu,
.brett-neu {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  margin-bottom: var(--abstand);
}

/* **`min-width` an dem Feld, das eine Eingabe traegt** (FAM-64) - und
   **nicht `0`**: Das erlaubte dem Feld, auf nichts zu schrumpfen, statt
   umzubrechen (der Fund von FAM-145).

   **Der Innenabstand wird mitgerechnet** - `ch` misst den Text, nicht das
   Feld (der Fund vom 18.09.2026 am Mengenfeld des Einkaufs). */
.projekt-neu input[type="text"],
.brett-neu input[type="text"] {
  flex: 1 1 var(--aufgabe-titel-breite);
  min-width: calc(12ch + 2 * var(--abstand));
}

/* --- Die Vorhaben --------------------------------------------------------

   **Eine Liste von Verweisen, kein Gitter von Kacheln.** Eine Familie hat
   eine Handvoll Vorhaben; Kacheln braeuchten eine Zahl oder ein Bild darin,
   und beides gibt es hier nicht (Regel 5). */
.projektliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
}

/* **Ein Eintrag ist seit dem 29.09.2026 eine Zeile mit Griffen**, nicht nur
   ein Name (auf Wunsch: *„Familienprojekte soll man später noch bearbeiten
   können."*): der Verweis aufs Brett, die Marke *zuerst*, der
   Standard-Knopf, das Ändern und das Löschen.

   **`flex-wrap`, keine Ansichtsvariable**: Ob die Griffe danebenpassen,
   entscheidet der Platz und nicht die Ansicht - dieselbe Überlegung wie bei
   der Eingabemaske des Einkaufs (Einkauf 2a §5) und beim Wagen neben dem
   Zettel (§9). */
.projekteintrag {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
}

/* **Die ganze Zeile ist die Bedienflaeche**, nicht nur das Wort - dieselbe
   Entscheidung wie bei der Esserzeile (FAM-146) und der Ankreuzzeile
   (FAM-63): Wer mit dem Finger zielt, trifft eine Zeile, kein Wort.

   `overflow-wrap: anywhere` faengt das eine lange Wort ohne Leerzeichen; ohne
   die Zeile spraenge es am Handy die Seite (Regel 2). */
.projektname {
  display: flex;
  align-items: center;
  min-height: var(--schaltflaeche);
  padding: 0 var(--abstand);
  border-radius: var(--radius);
  background: var(--flaeche);
  box-shadow: 0 0 0 1px var(--linie) inset;
  color: var(--text);
  text-decoration: none;
  overflow-wrap: anywhere;
}

/* **Der Name nimmt, was übrig bleibt** - die Griffe stehen rechts daneben.
   **Kein `min-width: 0`**: `overflow-wrap: anywhere` darüber macht den
   Mindestinhalt ohnehin klein, und die Zeile wäre die Falle von FAM-145
   (ein Feld, das auf nichts schrumpft, statt umzubrechen). */
.projekteintrag > .projektname { flex: 1 1 12ch; }

/* Ein Formular ist hier ein Griff, kein Block. */
.projektstandard,
.projektloeschen { margin: 0; }

/* **Das Ändern liegt hinter einem Aufklapper** - eine Liste von zehn
   Vorhaben mit je einem offenen Formular ist keine Liste mehr.

   **Der Griff sieht aus wie ein Knopf und ist ein `<summary>`** - also weder
   `button` noch `.schaltflaeche`, und damit traegt ihn Abschnitt 15 nicht.
   *Genau die Falle vom 27.09.2026*: Dort standen an `.haupt` die Zutaten
   ohne das Rezept, und der Knopf mass 14,2 px statt 44. */
.projektaendern { margin: 0; }

.projektaendern > summary {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--knopf-flaeche);
  padding: 0 var(--abstand);
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;
  font: inherit;
  color: var(--text);
}

/* **Das Zeichen wird selbst gesetzt** - `display: flex` an einem `<summary>`
   loescht den `::marker` (die Falle, fuenfmal). Hier steht bewusst keines:
   Das Wort *Ändern* sagt es, und ein Dreieck daneben waere ein zweites
   Versprechen. */
.projektaendern > summary::-webkit-details-marker { display: none; }
.projektaendern > summary::marker { content: ""; }
.projektaendern > summary:hover { background: var(--flaeche); }

/* **Die Maske steht unter der ganzen Zeile**, nicht neben dem Griff:
   `flex-basis: 100%` bricht sie um - sonst quetschte sie sich in die
   Restbreite und waere am Handy ein paar Zeichen breit. */
.projektaendern[open] { flex-basis: 100%; }
.projektaendern > .maske { margin-top: calc(var(--abstand) * 0.5); }

/* --- Die Filzwand (24.09.2026, auf Auftrag) --------------------------------

   **Drei Huellen, und jede hat eine Aufgabe**: der Raum dahinter, das
   Aluminium, der Filz. *Ein einziges Element koennte das nicht* - Rahmen und
   Flaeche brauchen je eigene Schatten, und der Filz scrollt in sich.

   **Jede Farbe kommt aus Abschnitt 1** (Regel 10); hier steht keine. Die
   Alphastufen lesen `--schatten` und `--glanz` mit der relativen
   Farbsyntax - *ein Browser, der sie nicht kennt, verwirft die Deklaration,
   dann fehlt ein Schatten. Nuechtern, nicht kaputt* (wie `mod()` bei der
   Neigung, FAM-169). */
.brettwand-raum {
  padding: var(--brett-raum-abstand);
  background: var(--brett-raum);
  border-radius: var(--radius);
  margin-bottom: var(--abstand);
}

.brettwand-rahmen {
  position: relative;
  padding: var(--brett-rahmen);
  border-radius: 7px;
  background: linear-gradient(180deg, var(--brett-alu-1),
                              var(--brett-alu-2) 35%,
                              var(--brett-alu-3) 65%, var(--brett-alu-4));
  box-shadow:
    inset 0 1px 0 oklch(from var(--glanz) l c h / 0.55),
    inset 0 -2px 0 oklch(from var(--schatten) l c h / 0.25),
    inset 1px 0 0 oklch(from var(--glanz) l c h / 0.25),
    inset -1px 0 0 oklch(from var(--schatten) l c h / 0.18),
    0 2px 3px oklch(from var(--schatten) l c h / 0.25),
    0 18px 30px -14px oklch(from var(--schatten) l c h / 0.55);
}

/* **Vier Eckstuecke** - die Schrauben, die den Rahmen halten.

   **Als leere `<span>` und nicht ueber `::before`/`::after`**: Vier Ecken
   brauchen vier Elemente, und die beiden Pseudoelemente des Rahmens reichen
   nur fuer zwei. *Eine Ecke an `.brettwand-raum` saesse ausserhalb des
   Rahmens.* */
.brettecke {
  position: absolute;
  width: 26px;
  height: 26px;
  z-index: 2;
  background: linear-gradient(135deg, var(--brett-ecke-hell),
                              var(--brett-ecke-dunkel));
  box-shadow:
    inset 0 1px 0 oklch(from var(--glanz) l c h / 0.25),
    1px 1px 2px oklch(from var(--schatten) l c h / 0.35);
}

/* **Aussen 7px wie der Rahmen, innen 12px, sonst 3px** - so sitzt die Ecke
   buendig in der Rundung und laeuft nach innen weich aus. */
.brettecke-lo { top: 0; left: 0; border-radius: 7px 3px 12px 3px; }
.brettecke-ro { top: 0; right: 0; border-radius: 3px 7px 3px 12px; }
.brettecke-lu { bottom: 0; left: 0; border-radius: 3px 12px 3px 7px; }
.brettecke-ru { bottom: 0; right: 0; border-radius: 12px 3px 7px 3px; }

/* **Der Filz.** Zwei Punktraster uebereinander, leicht versetzt - das ist
   das Korn. Gerechnet, nicht als Bild: *ein Bild waere eine Datei mehr und
   auf jedem Bildschirm eine andere Groesse.*

   **`overflow: auto`** - am Handy scrollt die Wand **waagerecht** (Regel 2:
   *breite Inhalte scrollen in ihrem eigenen Kasten*). Die Wand ist der
   Kasten. */
.brettwand {
  overflow: auto;
  padding: var(--brett-innen-y) var(--brett-innen-x);
  background:
    radial-gradient(var(--brett-filz-korn-d) 0.7px, transparent 1.1px)
      0 0 / 4px 4px,
    radial-gradient(var(--brett-filz-korn-h) 0.7px, transparent 1.2px)
      2px 1px / 5px 5px,
    var(--brett-filz);
  box-shadow:
    inset 0 4px 10px oklch(from var(--schatten) l c h / 0.45),
    inset 0 1px 2px oklch(from var(--schatten) l c h / 0.4),
    inset 0 0 0 1px oklch(from var(--schatten) l c h / 0.3);
}

/* --- Die Spaltenkoepfe, einmal oben ---------------------------------------

   **An einer Moderationswand haengen die Spaltenschilder oben an der Wand**,
   nicht in jeder Zeile noch einmal. Sie bleiben beim Scrollen stehen.

   **Dasselbe Gitter wie `.brett`** - sonst staende der Kopf nicht ueber
   seiner Spalte, und *das saehe man erst, wenn eine Spalte breiter wird.* */
.brettkoepfe {
  position: sticky;
  top: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: var(--board-spalten);
  gap: var(--brett-spaltenabstand);
  padding: 4px 0 6px;
}

.brettkopf {
  position: relative;
  padding: 7px 16px 5px 26px;
  background: var(--papier-weiss);
  background-image: linear-gradient(175deg,
                                    oklch(from var(--glanz) l c h / 0.5),
                                    transparent 50%,
                                    oklch(from var(--schatten) l c h / 0.05));
  box-shadow:
    0 1px 1px oklch(from var(--schatten) l c h / 0.2),
    0 7px 12px -6px oklch(from var(--schatten) l c h / 0.45);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
}

/* **Leicht schief, abwechselnd** - ein Schild, das jemand angeheftet hat,
   haengt nicht im Lot. */
.brettkopf:nth-child(odd)  { transform: rotate(-0.4deg); }
.brettkopf:nth-child(even) { transform: rotate(0.5deg); }

/* **Die Nadel des Kopfes** - links, dunkel, mittig senkrecht. */
.brettkopf::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 8px;
  width: 12px;
  height: 12px;
  margin-top: -6px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%,
                              oklch(from var(--glanz) l c h / 0.9) 0 10%,
                              var(--nadel-dunkel) 42%,
                              var(--nadel-dunkel-d) 100%);
  box-shadow: 2px 4px 3px -1px oklch(from var(--schatten) l c h / 0.42);
}

/* **`line-height: 1.75` ist gemessen, nicht Geschmack**: `overflow: hidden`
   schneidet sonst die Unterlaenge ab - *das g in „Eingeplant“*.

   **Der Auftrag nennt 1.45, und das reicht nicht** - gemessen am
   24.09.2026 in Chrome, Kopfgroesse 24px:

       1.45  ->  3,20 px abgeschnitten
       1.60  ->  1,61 px
       1.75  ->  0,00 px   <- der erste Wert, der traegt
       1.90  ->  gar nichts mehr, aber 45,9 px Kasten

   **Der Grund ist die Schrift**: Der Auftrag rechnet mit *Kalam*, hier
   steht die **Systemschrift** (`--schrift-hand`, Regel 4) - und *Segoe
   Print* hat groessere Unterlaengen. *Eine Zahl aus einer fremden Messung
   ist eine Entscheidung, keine Vorlage* (FAM-86). */
.brettkopf-name {
  font: 700 var(--brettkopf-titel)/1.75 var(--schrift-hand);
  color: var(--papier-tinte);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  text-transform: none;
  letter-spacing: 0;
}

.brettkopf .brettzahl {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-leise);
  flex: 0 0 auto;
}

/* --- Das Brett -----------------------------------------------------------

   **Vier Spalten oder eine Liste** - das entscheidet `--board-spalten`, und
   sonst nichts. `align-items: start` ist hier kein Schmuck: Ohne die Zeile
   waere jede Spalte so hoch wie die vollste, **und eine leere Spalte truege
   einen Rahmen um nichts** (der Fund aus dem Ansichtsdurchgang zu FAM-167,
   dort an `.stickerboard`). */
.brett {
  display: grid;
  grid-template-columns: var(--board-spalten);
  gap: var(--brett-spaltenabstand);
  /* **`stretch` und nicht `start`** (24.09.2026): An einer Wand ist eine
     leere Zelle **Ablageflaeche** - sie soll so hoch sein wie ihr Band,
     damit man eine Karte auch neben die vorhandenen haengen kann. *Bis zur
     Filzwand war das Gegenteil richtig: Dort trug eine leere Spalte einen
     Rahmen um nichts (FAM-167) - den gibt es jetzt nicht mehr.* */
  align-items: stretch;
  margin-bottom: 0;
}

/* **`min-width: 0` an einer Gitterspalte**, und hier ist es richtig: Eine
   Spalte traegt Text, kein Eingabefeld - sie **darf** schmaler werden als ihr
   laengstes Wort, denn der Titel bricht um. *Das ist der Unterschied zum
   Wochenplan* (FAM-145), wo dieselbe Zeile ein Feld auf 2,9 Zeichen
   zusammendrueckte. Ohne sie sprengte ein langes Wort das ganze Gitter. */
/* **Die Zelle ist Ablageflaeche, kein Kasten** (24.09.2026): kein
   Hintergrund, kein Rahmen, nur Platz. Der gestrichelte Umriss erscheint
   erst, wenn eine Karte darueber haengt.

   **`align-items: flex-start`** - eine Karte ist so breit wie ihr Inhalt
   (`fit-content`), und sie soll links stehen bleiben statt sich zu
   dehnen. */
.brettspalte {
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 28px;
  padding: 22px 8px 34px;
  min-height: 120px;
  border-radius: 10px;
  outline: 2px dashed transparent;
  outline-offset: -3px;
  transition: background-color 0.15s;
}

/* **Die Ueberschrift traegt den Ton ihrer Spalte** - als Linie darunter,
   nicht als Flaeche: Auf einer farbigen Flaeche muesste der Text noch einmal
   nachgerechnet werden, eine Linie beruehrt keinen Text (FAM-162).

   **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65): Wer
   rot-gruen-blind ist, liest *In Arbeit*; die Linie ist die Zugabe. */
.brettspalte-kopf {
  display: flex;
  align-items: baseline;
  gap: 0 calc(var(--abstand) * 0.5);
  margin: 0 0 calc(var(--abstand) * 0.5);
  padding-bottom: calc(var(--abstand) * 0.25);
  border-bottom: 3px solid var(--spaltenfarbe);
  font-size: var(--schrift);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-leise);
}

/* **Die drei Toene kommen aus `app/board.py`** - dort steht, was eine Spalte
   *bedeutet* (`TON_JE_SPALTE`), hier, welche Farbe das ist. Woertlich Audits
   Aufteilung (AUD-59): *„Welcher Zustand ‚laeuft' heisst und welcher ‚ist
   fertig', ist Fachlichkeit, keine Gestaltung."*

   **Kein Rot.** Es gehoert dem Blocker (FAM-179), und zwei Bedeutungen fuer
   eine Farbe kosten bei jeder Karte Zeit (FAM-122). */
.spaltenton-neutral { --spaltenfarbe: var(--text-leise); }
.spaltenton-blau    { --spaltenfarbe: var(--laeuft); }
.spaltenton-gruen   { --spaltenfarbe: var(--erfolg); }

/* **Die Zahl ist gezaehlt** (Regel 5) und steht rechts in der Zeile.
   `tabular-nums`, damit sie zwischen zwei Spalten nicht springt. */
/* **Das Wort darf schrumpfen, die Zahl nicht** (FAM-176).

   Der Kopf steht auf `nowrap` - und am **Tablet** ist die Spalte 167 px
   breit, waehrend *ABGESCHLOSSEN* bei 17 px in Grossbuchstaben mit
   `letter-spacing` fast so viel braucht. Gemessen stand die Zahl deshalb
   **2,2 px ueber den Rand ihrer Spalte**; bei zwei Ziffern waeren es zwoelf.

   **Die Ellipse statt eines Umbruchs**, und das ist eine Entscheidung: Ein
   umbrechender Kopf machte **eine** Spalte zweizeilig, und danach laegen die
   farbigen Striche der vier Spalten auf verschiedenen Hoehen - *das sieht
   kaputt aus, waehrend eine gekuerzte Ueberschrift nur kuerzer ist.*
   Design.md Regel 6 sieht die Kurzform an engen Stellen ausdruecklich vor;
   das volle Wort steht weiterhin im Vorlesebaum. */
.brettspaltenname {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.brettzahl {
  margin-left: auto;
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  color: var(--spaltenfarbe);
}

/* **`display: contents`** (24.09.2026): Die Liste ist fuer das
   Vorleseprogramm da, nicht fuer die Gestaltung - die Karten sollen direkte
   Kinder der Zelle sein und ihren Abstand von dort bekommen. *Eine zweite
   Flex-Ebene dazwischen haette zwei Stellen, an denen der Abstand
   entsteht.* */
.brettkarten {
  display: contents;
  list-style: none;
}

/* --- Die Karte -----------------------------------------------------------

   **Pinnwand statt Tabelle** (AUD-59, und die Karte verlangt es): Papier,
   eine leichte Drehung, ein kurzer Schatten - dieselbe Sprache, die das
   Sticker Board seit FAM-169 spricht.

   **Die Drehung wird gerechnet, nicht gewuerfelt** - aus der Nummer der
   Karte, die die Vorlage als `--nr` mitgibt. *Ein Zufall je Aufruf liesse das
   Brett bei jedem Neuladen springen*, und aus der **Position** zu rechnen
   (`:nth-child`) waere derselbe Fehler: Eine Karte, die eine Spalte
   weiterwandert, drehte damit alle anderen mit.

   **`--brettkarte-neigung` und nicht `--sticker-neigung`**, obwohl beide
   dieselben vier Werte tragen. *Uebernommene Merkmalsvariablen sind eine
   Entscheidung, keine Vorlage* (FAM-93, FAM-136, FAM-145) - und hier waere
   es eine Kopplung ueber eine Modulgrenze: Wer eines Tages die Zettel gerade
   haengen lassen will, richtet damit das Projektbrett mit aus. **Dieselbe
   Lage wie `--monat-spalten` neben `--kalender-spalten`** (FAM-163), die
   sich in drei von vier Ansichten decken und trotzdem getrennt sind.

   **Der Rueckfall ist harmlos**: Ein Browser ohne `mod()` verwirft die
   Deklaration - dann liegt jede Karte gerade. Nuechtern, nicht kaputt. */
.brettkarte {
  position: relative;
  box-sizing: border-box;
  /* **So breit wie ihr Inhalt** - Papier, das jemand zugeschnitten hat. */
  width: fit-content;
  min-width: var(--brettkarte-min);
  max-width: 100%;
  padding: 26px 18px 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;

  /* **Papier hat keine runden Ecken.** Bis zur Filzwand trug die Karte
     einen unregelmaessigen Radius (FAM-175) - das war die Anmutung eines
     gerissenen Zettels; hier ist es geschnittenes Papier. */
  border-radius: 0;

  background-color: var(--papier-weiss);
  /* **Die Papierfarbe der ersten Person** - dieselbe Rezeptur wie am Zettel
     (FAM-169), nur heller und leiser. **Sie steht hier und nicht auf
     `:root`**, denn `--person-ton` liegt an der Karte: *Ein `var()` wird
     dort substituiert, wo es DEKLARIERT ist* (der Fund vom 22.09.2026). */
  background-image: linear-gradient(172deg,
                                    oklch(from var(--glanz) l c h / 0.4),
                                    transparent 40%,
                                    oklch(from var(--schatten) l c h / 0.06));

  --brettkarte-schatten:
    0 1px 1px oklch(from var(--schatten) l c h / 0.2),
    0 8px 12px -6px oklch(from var(--schatten) l c h / 0.38),
    0 22px 26px -18px oklch(from var(--schatten) l c h / 0.4);
  box-shadow: var(--brettkarte-schatten);

  /* **Die Kippung kommt aus Python** (`--kippen`, ein Faktor zwischen -1 und
     +1), **wie schief es wird aus dem Blatt** (`--brettkarte-neigung`,
     Regel 0). *Gerechnet, nicht gewuerfelt* - dieselbe Karte haengt immer
     gleich schief (FAM-169). */
  rotate: calc(var(--kippen, 0) * var(--brettkarte-neigung));
  transition: box-shadow 0.15s;
}

.brettkarte[style*="--person-ton"] {
  background-color: oklch(90% 0.075 var(--person-ton));
}

/* **Angefasst hebt sie sich** - ein Blatt, das man greift, loest sich von
   der Wand. */
.brettkarte:hover {
  box-shadow:
    0 1px 1px oklch(from var(--schatten) l c h / 0.2),
    0 14px 18px -8px oklch(from var(--schatten) l c h / 0.42),
    0 30px 34px -20px oklch(from var(--schatten) l c h / 0.45);
}

/* --- Die Stecknadel ------------------------------------------------------

   **Oben mittig, mit Schlagschatten nach rechts unten** - eine Nadel steht
   aus der Wand heraus, also wirft sie einen.

   **Fuenf Farben, gewechselt ueber `data-nadel`** aus Python (FAM-169:
   *gerechnet, nicht gewuerfelt*). Die Farben stehen in Abschnitt 1. */
.brettkarte::before {
  content: "";
  position: absolute;
  top: 8px;
  left: 50%;
  width: 15px;
  height: 15px;
  margin-left: -7px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%,
                              oklch(from var(--glanz) l c h / 0.9) 0 10%,
                              var(--nadel, var(--nadel-rot)) 42%,
                              var(--nadel-d, var(--nadel-rot-d)) 100%);
  box-shadow: 2px 5px 3px -1px oklch(from var(--schatten) l c h / 0.42);
}

.brettkarte[data-nadel="0"] { --nadel: var(--nadel-rot);   --nadel-d: var(--nadel-rot-d); }
.brettkarte[data-nadel="1"] { --nadel: var(--nadel-blau);  --nadel-d: var(--nadel-blau-d); }
.brettkarte[data-nadel="2"] { --nadel: var(--nadel-gelb);  --nadel-d: var(--nadel-gelb-d); }
.brettkarte[data-nadel="3"] { --nadel: var(--nadel-gruen); --nadel-d: var(--nadel-gruen-d); }
.brettkarte[data-nadel="4"] { --nadel: var(--nadel-weiss); --nadel-d: var(--nadel-weiss-d); }

/* **Eine Karte, an der ein roter Zettel haengt** (FAM-179).

   Woertlich die Bauart von Audit (`.bkarte.blockiert`, AUD-55): *Man soll es
   der Karte ansehen, ohne den Zettel gelesen zu haben.*

   **Ein Ring nach innen, kein Rahmen** - `border` kostete 2 px Aussenmass,
   und die Karte spraenge beim Anhaengen eines Zettels um zwei Pixel. Der
   Ring folgt dem unregelmaessigen Radius von selbst.

   **Und er ist die Zugabe, nicht die Auskunft** (FAM-65, Regel 5): Wer
   rot-gruen-blind ist, liest das Wort **Haengt** am Zettel darunter. *Ein
   roter Rand allein saegte einem Vorleseprogramm gar nichts.*

   **`--fehler` und keine zweite rote Angabe** (Regel 10: jede Farbe steht an
   genau einer Stelle). Gerechnet gegen `--flaeche`: **6,53 : 1** - ueber den
   3 : 1, die eine Linie braucht. */
.brettkarte.blockiert {
  box-shadow: var(--brettkarte-schatten), inset 0 0 0 2px var(--fehler);
}

/* --- Der Titel, und dahinter das Aendern ---------------------------------

   **`display: flex` an einem `<summary>` loescht den `::marker`** - zum
   fuenften Mal gemessen (FAM-70, FAM-136, FAM-146, 21.09. und 23.09.2026).
   `list-style: none` steht deshalb **ausdruecklich** da, statt sich auf
   denselben Nebeneffekt zu verlassen, und das Zeichen wird selbst gesetzt.

   **Und es wechselt** (▾ zu ▴), nicht nur die Farbe: Wer rot-gruen-blind
   ist, sieht sie nicht (FAM-65). Die Zeichen stehen **direkt** im Blatt und
   nicht als CSS-Escape - *ein Escape, das man nicht braucht, kann auch nicht
   kaputtgehen* (FAM-109, FAM-136). */
.brettkarte-aendern > summary {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
  min-height: var(--schaltflaeche);
  list-style: none;
  cursor: pointer;
}

.brettkarte-aendern > summary::-webkit-details-marker { display: none; }

.brettkarte-aendern > summary::after {
  content: "▾";
  margin-left: auto;
  color: var(--text-leise);
}

.brettkarte-aendern[open] > summary::after { content: "▴"; }

/* **Der Titel ist die Karte** - er darf umbrechen, und in einer schmalen
   Gitterspalte tut er das auch. **Keine Ellipse**: Auf einem Brett steht,
   was zu tun ist, nicht sein Anfang. */
/* **Filzstift auf Papier.** Die Schrift steht an einer Stelle
   (`--schrift-hand`, Abschnitt 1) und kommt **nicht** von einer fremden
   Adresse (Regel 4).

   **Drei Groessen nach Titellaenge** - kurze duerfen groesser sein, lange
   muessen kleiner werden: *Eine Karte ist so breit wie ihr Inhalt, und ein
   langer Titel in grosser Schrift sprengt die Spalte.* Welche Klasse eine
   Karte traegt, rechnet Python (`kartentitelgroesse`). */
.brettkarte-titel {
  font: 700 var(--brettkarte-titel)/1.12 var(--schrift-hand);
  color: var(--papier-tinte);
  text-wrap: pretty;
  overflow-wrap: anywhere;
}

.brettkarte.titel-kurz .brettkarte-titel {
  font-size: calc(var(--brettkarte-titel) + 3px);
}

.brettkarte.titel-lang .brettkarte-titel {
  font-size: calc(var(--brettkarte-titel) - 3px);
}

/* **Worum es geht** (29.09.2026, auf Wunsch) - die Beschreibung der Karte.

   **Sie steht nur da, wo Platz ist**: `--brettkarte-text-anzeige` ist
   überall `none` und wird erst im Vollbild auf `block` gesetzt (Regel 10).
   *Auf einer 154 px breiten Karte wären drei Zeilen Fließtext kein
   Gewinn, sondern ein Wortsalat.*

   **Drei Zeilen, dann eine Ellipse** - und das ist hier richtig, anders
   als beim Titel darüber: *Auf einem Brett steht, was zu tun ist; die
   Beschreibung sagt, worum es geht, und davon reicht der Anfang.* Wer
   alles lesen will, klappt die Karte auf.

   **Die Variable trägt `none` oder `-webkit-box`** – und das ist kein
   Schönheitsfehler, sondern die einzige Bauart, die trägt: Die Ellipse
   braucht `display: -webkit-box`, also **kann** neben der Variablen keine
   zweite `display`-Zeile stehen. *Zwei Deklarationen derselben
   Eigenschaft in einer Regel sind keine Wahl – die spätere gewinnt immer,
   und die Variable wäre wirkungslos.* */
.brettkarte-text {
  display: var(--brettkarte-text-anzeige);
  margin-top: calc(var(--abstand) * 0.5);
  font: var(--brettkarte-textgroesse)/1.35 var(--schrift-hand);
  color: var(--papier-tinte);
  opacity: .78;
  text-wrap: pretty;
  overflow-wrap: anywhere;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}

.brettkarte-werkzeug {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  padding-top: calc(var(--abstand) * 0.5);
}

/* **Das Feld nimmt die ganze Zeile** (`flex-basis: 100%`), die Knoepfe
   stehen darunter: In einer Gitterspalte reicht der Platz fuer beides
   nebeneinander nirgends, und *ein Knopf, der aus der Karte haengt, ist bei
   keiner Scrollposition erreichbar* (FAM-50, FAM-84).

   **`min-width: 0`, und hier ist es richtig:** Ohne die Zeile kann das
   Formular nicht unter seinen Mindestinhalt schrumpfen - gemessen stand es
   am Tablet **75 px ueber den Rand seiner Spalte** (der Fund aus dem
   Ansichtsdurchgang). *Das ist der Unterschied zum Eingabefeld darin, das
   seine eigene Breite behaelt* (FAM-64, FAM-145). */
.brettkarte-felder {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  flex: 1 1 100%;
  min-width: 0;
}

/* **6ch und nicht 12ch wie in der Eintragezeile** - gemessen: Die schmalste
   Karte des Projekts steht am **Tablet** und hat innen **127 px**; 6ch plus
   zweimal `--abstand` sind dort 96 px und passen. Zwoelf waeren 153 px und
   sprengten die Spalte. */
.brettkarte-felder input[type="text"] {
  flex: 1 1 100%;
  min-width: calc(6ch + 2 * var(--abstand));
}

/* **Ein `<select>` ist so breit wie sein laengster Eintrag** - und der ist
   hier ein Zeilenname, den eine Familie selbst tippt.

   **Gemessen im Ansichtsdurchgang zu FAM-176**: Das Auswahlfeld der Zeile
   mass am Tablet **226 px in einer 167 px breiten Spalte**, am Infoboard
   320 in 436 - es stand also ueber den Rand seiner Spalte hinaus, und die
   Seite scrollte seitwaerts (Regel 2).

   *Das ist der Fund von FAM-175 in neuer Verkleidung* (dort war es ein
   `<input>` neben einem Wortknopf), und die Abhilfe ist dieselbe wie bei
   jedem Flex-Element, das sonst seine intrinsische Breite durchsetzt:
   **`min-width: 0` und ein Deckel.** Der Name wird dann mit einer Ellipse
   gekuerzt; *welche Zeile gemeint ist, steht ausserdem am Band darueber.* */
.brettkarte-felder select {
  flex: 1 1 100%;
  min-width: 0;
  max-width: 100%;
  text-overflow: ellipsis;
}

/* **Dieselbe Vorsorge fuer das Datumsfeld** (FAM-178). Ein
   `<input type="date">` bringt eine eigene Breite mit - Kalendersymbol,
   Trenner, vier Ziffern fuer das Jahr -, und sie haengt nicht am Inhalt,
   sondern am Browser. *Der Fund von FAM-176 in neuer Verkleidung: Was seine
   Breite selbst mitbringt, steht in einer 127-px-Spalte ueber ihren Rand.* */
.brettkarte-felder input[type="date"] {
  flex: 1 1 100%;
  min-width: 0;
  max-width: 100%;
}

.brettkarte-weg { margin: 0; }

/* **Zeichenknoepfe ohne Kasten** - dieselbe Bauart wie am Zettel
   (`.sticker-wegknopf`, 18.09.2026). Das Wort steht in `.nurvorlesen` und im
   `title`, also im Vorlesebaum und beim Verweilen.

   **Und sie sind `--schaltflaeche` breit**, obwohl nur ein Zeichen darin
   steht: *Was man trifft, ist groesser als das, was man sieht* - und das ist
   hier richtig herum (Design.md, 18.09.2026). */
.brettkarte-knopf,
.brettkarte-wegknopf {
  min-width: var(--knopf-flaeche);
  padding: 0;
}

.brettkarte-wegknopf,
.projekt-wegknopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--knopf-flaeche);
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--fehler);
}

/* --- Die Griffe ----------------------------------------------------------

   **Zwei Knoepfe, nebeneinander** - links und rechts, wie am Brett. Sie
   tragen nur ihr Zeichen: Zwei Knoepfe mit *„Nach Eingeplant“* und *„Nach In
   Arbeit"* passten in keine Gitterspalte, und `Design.md` erlaubt den
   Zeichenknopf genau dort, *wo der Platz das Knappste ist*. Das Wort steht
   im `title` und im `aria-label` und nennt **das Ziel**, nicht die Richtung.

   **`wrap`**, damit am Handy nichts abgeschnitten wird (FAM-50, FAM-84). */
.brettkarte-griffe {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--abstand) * 0.5);
  margin-top: calc(var(--abstand) * 0.5);
}

.brettgriff {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--akzent-dunkel);
}

/* --- Das Ziehen (FAM-180) -------------------------------------------------

   **Drei Regeln, und keine davon verschiebt etwas** - das Verschieben macht
   der Server (`projektbrett-ziehen.js`). Hier steht nur, was ein Mensch
   waehrenddessen sieht.

   **Der Zeiger sagt es vorher.** `grab` haengt an `[draggable]` und damit an
   der Bedingung, die die Maske setzt: Wer nicht verschieben darf, bekommt das
   Attribut gar nicht erst - *und damit auch keinen Zeiger, der etwas
   verspricht, das der Server abweist* (Regel 7).

   **Angefasst wird sie blass, nicht bunt.** Eine Farbe waere eine zweite
   Bedeutung neben Rot (Blocker, FAM-179) und Gelb (Frist, FAM-178) -
   *zwei Bedeutungen fuer eine Farbe kosten bei jeder Karte Zeit* (FAM-122).
   `opacity` laesst den roten Ring einer haengenden Karte stehen, ein
   ersetzter `box-shadow` naehme ihn weg (siehe `.brettkarte.blockiert`). */
.brettkarte[draggable] { cursor: grab; }

.brettkarte.wird-getragen {
  opacity: 0.35;
  cursor: grabbing;
}

/* **Das Ziel zeigt sich, solange die Karte darueber haengt.**

   **`--akzent` und keine vierte Farbe** (Regel 10: jede Farbe steht an genau
   einer Stelle): Das Brett traegt bereits die drei Spaltentoene, Rot und
   Gelb. Der Akzent ist die Farbe dessen, *was gerade etwas tut* - dieselbe
   Rolle wie an einem betonten Knopf.

   **Ein Ring nach innen, kein Rahmen** - woertlich die Loesung von FAM-179:
   `border` kostete Aussenmass, und die Spalte spraenge beim Darueberziehen um
   zwei Pixel zur Seite. *Ein Ziel, das wackelt, sieht aus, als weiche es
   aus.*

   **`.zielt` steht nur waehrend des Ziehens da** und wird beim Loslassen
   weggenommen - ohne Skript gibt es die Klasse nirgends. */
/* **Das Ziel zeigt sich als gestrichelter Umriss** (24.09.2026, vorher ein
   Ring in `--akzent`): Auf dem Filz ist Weiss die Farbe, die sich abhebt,
   ohne eine fuenfte Bedeutung einzufuehren - *der Akzent heisst in dieser
   Anwendung „hier kannst du etwas tun“, und das waere an einer Zelle eine
   Aussage ueber ihren Inhalt.*

   **Umriss statt Rahmen, und `outline-offset` nach innen** - er kostet kein
   Aussenmass: *Ein Ziel, das beim Darueberziehen wackelt, sieht aus, als
   weiche es aus* (FAM-179, FAM-180). */
.brettspalte.zielt {
  outline-color: var(--glanz);
  background-color: oklch(from var(--glanz) l c h / 0.14);
}

/* --- Das Vorhaben selbst -------------------------------------------------

   **Unten und zugeklappt**: Wer das Brett oeffnet, will die Karten sehen -
   das Umbenennen ist der seltene Vorgang (FAM-70, FAM-136). */
.projekt-werkzeug { margin-top: var(--abstand); }

.projekt-werkzeug > summary {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
  min-height: var(--schaltflaeche);
  list-style: none;
  cursor: pointer;
  color: var(--text-leise);
}

.projekt-werkzeug > summary::-webkit-details-marker { display: none; }

.projekt-werkzeug > summary::after {
  content: "▾";
  color: var(--text-leise);
}

.projekt-werkzeug[open] > summary::after { content: "▴"; }

.projekt-felder {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
}

.projekt-umbenennen {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  flex: 1 1 var(--aufgabe-titel-breite);
}

.projekt-umbenennen input[type="text"] {
  flex: 1 1 var(--aufgabe-titel-breite);
  min-width: calc(12ch + 2 * var(--abstand));
}

.projekt-weg { margin: 0; }

/* --- Die Zeilen des Bretts (FAM-176) -------------------------------------

   **Ein Band je Zeile**, und wo sein Name steht, entscheidet
   `--board-zeilen-richtung` aus Abschnitt 2 (Regel 0).

   **Er steht in allen vier Ansichten DARUEBER** (`column`) - und das ist
   gemessen, nicht entschieden. Daneben (`row`) kostet er am **Tablet**:

   | Tablet | Spalte | Zeichen je Titelzeile | Zeilen | seitwaerts |
   |---|---|---|---|---|
   | Name daneben | 123,9 px | **7,3** | 3 | **49 px** |
   | Name darueber | **167 px** | **12,4** | 2 | 0 |

   *49 px Seitwaerts-Scroll verbietet Regel 2*, und 7,3 Zeichen sind die
   Lage, die FAM-163 fuer das Monatsgitter am Tablet abgelehnt hat. Am
   Desktop kostet der Name darueber **gar keine Hoehe** (beide Male 791 px),
   am Infoboard 142 px - und dort scrollt das Brett bei fuenf Karten
   ohnehin.

   **Die Variable bleibt trotzdem**, obwohl sie siebenmal dasselbe sagt -
   wie `--marke-anzeige` und `--essensplan-spalten` (FAM-145): Sie haelt die
   Entscheidung fest und ist die eine Stelle, an der ein Name neben dem
   Gitter wieder entstehen koennte. *Wer ihn dorthin stellt, misst neu.*

   **Und eine Liste von Spalten darf den Namen NIE daneben stellen** - er
   waere ein Name ohne Bezug. Gehalten von
   `test_ein_name_neben_einer_liste_waere_ein_name_ohne_bezug`, derselben
   Bauart wie bei der Woche (FAM-84) und beim Monat (FAM-163). */
/* **Die Zeile haengt an einer Schnur** (24.09.2026) - der Kasten ist weg,
   der Hintergrund auch. Was das Band traegt, ist ein rot-weisses Band quer
   ueber die Wand, und der Name haengt als Kraftpapier-Streifen daran.

   **`--board-zeilen-richtung` faellt hier weg**: Der Name steht jetzt
   **immer** an der Schnur, also ueber dem Band - in allen vier Ansichten.
   Die Variable bleibt in Abschnitt 2 stehen, wie `--essensplan-spalten`
   (FAM-145): *Wer ihn eines Tages daneben stellt, misst neu.* */
.brettband {
  position: relative;
  padding-top: 44px;
  margin-bottom: var(--abstand);
}

/* **Die Schnur** - sie laeuft ueber den Rand der Zelle hinaus (`-8px`),
   damit sie an der Wand befestigt aussieht und nicht im Band endet. */
.brettband::before {
  content: "";
  position: absolute;
  top: 24px;
  left: -8px;
  right: -8px;
  height: 3px;
  border-radius: 2px;
  background: repeating-linear-gradient(115deg,
                                        var(--schnur-rot) 0 5px,
                                        var(--schnur-weiss) 5px 8px);
  box-shadow: 0 3px 3px -1px oklch(from var(--schatten) l c h / 0.4);
}

/* **Der Name traegt das Band, nicht nur eine Zeile Text.**

   Er steht **darueber** und nimmt die ganze Breite - in allen vier
   Ansichten, siehe die Messung oben. Die Rechnung mit `--board-zeilen-
   richtung` bleibt trotzdem stehen: Stuende er eines Tages daneben, waere
   das hier die Stelle, an der er es tut.

   **`flex-shrink: 0` haelt ihn davon ab, unter sein Wort zu schrumpfen** -
   der Fund von FAM-64. */
/* **Der Kraftpapier-Streifen an der Schnur.** */
.brettband-name {
  position: absolute;
  top: 9px;
  left: 12px;
  z-index: 2;
  margin: 0;
  padding: 5px 16px 3px 26px;
  background: var(--papier-kraft);
  background-image: linear-gradient(175deg,
                                    oklch(from var(--glanz) l c h / 0.25),
                                    transparent 60%);
  transform: rotate(-1.2deg);
  box-shadow:
    0 1px 1px oklch(from var(--schatten) l c h / 0.25),
    0 6px 10px -5px oklch(from var(--schatten) l c h / 0.5);
  /* **1.45, und auch das ist gemessen** - dieselbe Falle wie am Spaltenkopf:
     Bei 1.2 schneidet `overflow: hidden` 1,46 px Unterlaenge ab. Gemessen am
     24.09.2026: 1.35 -> 0,46 px, **1.45 -> nichts mehr**. */
  font: 700 20px/1.45 var(--schrift-hand);
  color: var(--papier-kraft-tinte);
  white-space: nowrap;
  text-transform: none;
  letter-spacing: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: calc(100% - 24px);
}

/* Die Nadel des Streifens: links, gelb. */
.brettband-name::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 8px;
  width: 11px;
  height: 11px;
  margin-top: -5px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%,
                              oklch(from var(--glanz) l c h / 0.9) 0 10%,
                              var(--nadel-gelb) 42%, var(--nadel-gelb-d) 100%);
  box-shadow: 1px 3px 2px -1px oklch(from var(--schatten) l c h / 0.42);
}

/* **Das Band *noch nicht einsortiert* ist keine Zeile** (FAM-174, FAM-176) -
   es traegt deshalb eine leisere Linie statt derselben Auszeichnung wie eine
   echte. *Die Aussage traegt trotzdem das Wort* (FAM-65); die Linie ist die
   Zugabe. */
.brettband-ohne > .brettband-name {
  background-color: var(--papier-kraft-blass);
  font-style: italic;
}

/* Das Gitter eines Bandes steht unter seiner Schnur. */
.brettband > .brett { min-width: 0; margin-bottom: 0; }

/* --- Die Pflegemaske der Zeilen ------------------------------------------

   Dieselbe Bauart wie die Liste der Vorhaben: eine Zeile je Eintrag, die
   ganze Zeile ist die Flaeche. */
.zeilenliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
}

/* **`wrap`** - eine Zeile, die eine Aktion abschneidet, ist bei keiner
   Scrollposition erreichbar (FAM-50, FAM-84). */
.zeileneintrag {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  min-height: var(--schaltflaeche);
  padding: calc(var(--abstand) * 0.5) var(--abstand);
  border-radius: var(--radius);
  background: var(--flaeche);
  box-shadow: 0 0 0 1px var(--linie) inset;
}

.zeile-umbenennen {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  flex: 1 1 var(--aufgabe-titel-breite);
  min-width: 0;
}

/* Der Innenabstand wird mitgerechnet - `ch` misst den Text, nicht das Feld
   (der Fund vom 18.09.2026 am Mengenfeld des Einkaufs). */
.zeile-umbenennen input[type="text"] {
  flex: 1 1 auto;
  min-width: calc(8ch + 2 * var(--abstand));
}

.zeile-knopf {
  min-width: var(--knopf-flaeche);
  padding: 0;
}

.zeile-griffe {
  display: flex;
  gap: calc(var(--abstand) * 0.5);
}

.zeile-weg { margin: 0; }

.zeile-wegknopf {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--knopf-flaeche);
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--fehler);
}

/* **Der Name ohne Recht zum Pflegen** - dann steht er als Text da, nicht als
   Feld (Regel 7: kein Bedienelement, das nichts tut). */
.zeilenname {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* **Die gezaehlte Wirkung, sichtbar** (Regel 5) - wer entscheiden soll, ob
   er eine Zeile wegnimmt, soll nicht erst darauf zeigen muessen. */
.zeilenzahl {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--text-leise);
}

/* --- Wer und worum (FAM-177) ---------------------------------------------

   **Die Eintragezeile bleibt eine Zeile.** Verantwortliche, Schlagwoerter
   und Gruppe stecken hinter einem `<details>` - dieselbe Entscheidung wie
   bei der Artikelzeile (FAM-70), der Leihzeile (FAM-136) und der Karte
   selbst: *Der haeufige Vorgang ist das Anlegen, der seltene das
   Ausschmuecken.*

   **`display: flex` an einem `<summary>` loescht den `::marker`** - zum
   sechsten Mal gemessen (FAM-70, FAM-136, FAM-146, 21.09., 23.09.2026 und
   hier). Das Zeichen wird selbst gesetzt und **wechselt**; `list-style:
   none` steht ausdruecklich daneben, statt sich auf denselben Nebeneffekt
   zu verlassen (FAM-107). */
.brett-mehr {
  flex: 1 1 100%;
  min-width: 0;
}

.brett-mehr > summary {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
  min-height: var(--schaltflaeche);
  list-style: none;
  cursor: pointer;
  color: var(--text-leise);
  font-size: var(--schrift);
}

.brett-mehr > summary::-webkit-details-marker { display: none; }

.brett-mehr > summary::after {
  content: "\25be";
  color: var(--akzent);
}

.brett-mehr[open] > summary::after { content: "\25b4"; }

.brett-mehr-felder {
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
  padding-bottom: calc(var(--abstand) * 0.5);
}

/* **Ein Buendel je Gruppe** - die Ueberschrift steht darueber, klein und
   leise: *Sie ordnet, sie behauptet nichts.*

   **Ohne Gruppe steht keine da** (Regel 5, Regel 7) - das entscheidet die
   Maske, nicht diese Regel. */
/* **Die Gruppe ist ein ovales Kaertchen** (24.09.2026) - kein
   Zwischentitel mehr: An einer Wand steckt ueber einer Gruppe von Karten ein
   kleines Schild, und es ist rund geschnitten.

   **`margin-bottom: -8px`** zieht es an die Karten darunter heran: Es
   gehoert zu ihnen, nicht zur Zelle. */
.brettgruppe {
  position: relative;
  margin: 0 0 -8px;
  padding: 12px 30px 9px;
  border-radius: 50%;
  background: var(--papier-weiss);
  transform: rotate(-2deg);
  box-shadow:
    0 1px 1px oklch(from var(--schatten) l c h / 0.2),
    0 7px 12px -6px oklch(from var(--schatten) l c h / 0.45);
  font: 700 19px/1.1 var(--schrift-hand);
  color: var(--papier-gruppe-tinte);
  text-transform: none;
  letter-spacing: 0;
}

/* Die Nadel der Gruppe: oben mittig, blau. */
.brettgruppe::before {
  content: "";
  position: absolute;
  top: 3px;
  left: 50%;
  width: 10px;
  height: 10px;
  margin-left: -5px;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%,
                              oklch(from var(--glanz) l c h / 0.9) 0 10%,
                              var(--nadel-blau) 42%, var(--nadel-blau-d) 100%);
  box-shadow: 1px 3px 2px -1px oklch(from var(--schatten) l c h / 0.42);
}

/* **Die Marken einer Karte** - Namensschild und Schlagwoerter.

   **Sie duerfen umbrechen** (`wrap`): In einer Gitterspalte ist wenig
   Platz, und *ein Schild, das den Titel abschneidet, hat die Karte
   unlesbar gemacht* - deshalb steht es **unter** dem Titel und nicht
   daneben. */
.brettkarte-marken {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--abstand) * 0.25);
  margin-top: calc(var(--abstand) * 0.25);
}

/* **Das Namensschild** - klein, mit Rand, in der Farbe seines Menschen.

   **Die Farbe steht genau einmal da** (FAM-169): hier, nicht zusaetzlich am
   Rand der Karte. Und **sie faerbt den Rahmen, nicht die Flaeche**: Auf
   `--person-helligkeit` (52 %) ist kein Text lesbar - *ein Rand beruehrt
   keinen Text* (woertlich der Streifen am Termin, FAM-162).

   **Ohne `gefaerbt` bleibt es leise** - bei zwei Menschen und bei der
   Familie waere jede Farbe erfunden (Regel 5). */
/* **Ein Klebepunkt je Mensch** (24.09.2026) - bis zur Filzwand stand hier
   ein Schild mit allen Namen (FAM-177). *Ein Schild sagt, dass zwei es
   machen; zwei Punkte zeigen es.*

   **Die Farbe entsteht hier**, weil `--person-ton` am Punkt liegt (der Fund
   vom 22.09.2026: *ein `var()` wird dort substituiert, wo es deklariert
   ist*).

   **Buntheit 0,13 statt 0,11** - auf dem Punkt steht weisse Schrift, und die
   braucht einen dunkleren Grund. Die Zahl ist gemessen, nicht geraten
   (`docs/Design-Messungen.md`). */
.brettpunkte {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.brettpunkt {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: oklch(52% 0.13 var(--person-ton));
  background-image: radial-gradient(circle at 35% 30%,
                                    oklch(from var(--glanz) l c h / 0.28),
                                    transparent 60%);
  color: var(--aufkleber-blocker-text);
  font-size: 11.5px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 1px oklch(from var(--schatten) l c h / 0.3);
  flex: 0 0 auto;
}

.brettschild.gefaerbt {
  border-color: var(--personenfarbe);
  color: var(--personenfarbe);
}

/* **Ein Schlagwort** - dieselbe Groesse wie das Schild, aber ohne Rand:
   *Zwei Sorten Marke sollen sich unterscheiden lassen, ohne dass man sie
   liest.* */
/* **Kein Chip mehr, nur leiser Text** (24.09.2026) - mit Mittelpunkt
   getrennt. *An einer Wand schreibt niemand ein Schlagwort in einen
   Rahmen.* */
.brettwort {
  background: none;
  border: 0;
  padding: 0;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--papier-wort);
}

.brettwort + .brettwort::before { content: "· "; }

/* --- Der gelbe Zettel (FAM-178) ------------------------------------------

   **Woertlich die Vorlage des Nutzers:** Auf dem Foto seiner Wohnzimmerwand
   liegt rechts oben der Vorrat - vier gelbe Quadrate -, und eines klebt schon
   an einer Karte in *In Arbeit*. *„Eine Frist ist wie ein kleiner Gelber
   Zettel."*

   **Die Farbe kommt aus der Papier-Rezeptur** (FAM-169), nicht aus einer
   eigenen Angabe: `--papier-helligkeit` und `--papier-buntheit` stehen in
   Abschnitt 1, hier steht nur der **Ton**. *Eine zweite Stelle, an der ein
   Papier eine Farbe bekommt, liefe auseinander* - und man saehe es erst,
   wenn ein Zettel am Board anders gelb ist als einer am Brett.

   **Ton 90 ist Gelb** - dieselbe Zahl, die `app/personenfarben.py` unter
   diesem Namen fuehrt (`PALETTE[2]`). *Eine erfundene Zahl waere dieselbe
   Sorte Behauptung wie eine erfundene Farbe* (Regel 5).

   **Die Rezeptur steht HIER und nicht auf `:root`** - dort gibt es kein
   `--papier-ton`, die Deklaration waere leer, und **leer wird vererbt**: Der
   Zettel bliebe weiss, *ohne dass irgendwo etwas kaputt aussieht* (der Fund
   vom 22.09.2026; bewacht von
   `test_keine_rezeptur_steht_dort_wo_ihr_ton_fehlt`).

   **Der Kontrast ist gerechnet, nicht geschaetzt** (FAM-162, FAM-169), und
   die Rechnung hat ihre Gegenprobe an einem Paar, dessen Wert man kennt:
   `--text-leise` auf dem Papier ergibt **4,81 : 1**, `--text` **12,56 : 1** -
   beide ueber den 4,5, die die Karte verlangt. */
/* **Die Frist ist ein Aufkleber** (24.09.2026) - er ragt oben rechts ueber
   den Rand der Karte, wie ein Klebezettel, den jemand halb daneben geklebt
   hat.

   **Absolut positioniert, und das ist hier richtig**: Er ist klein, einzeilig
   und ueberdeckt nichts - anders als der Blocker, dessen Text drei Zeilen
   lang werden kann (siehe dort).

   **Was draengt, ist nicht rot** (FAM-178, FAM-122): Rot gehoert dem
   Blocker. Der Aufkleber wird waermer und sein Rand dunkler. */
.brettfrist {
  position: absolute;
  top: -11px;
  right: -14px;
  z-index: 2;
  padding: 4px 9px 3px;
  border-radius: 4px;
  transform: rotate(7deg);
  background: var(--aufkleber-frist);
  color: var(--aufkleber-frist-text);
  border: 1.5px solid var(--aufkleber-frist-rand);
  box-shadow:
    0 1px 1px oklch(from var(--schatten) l c h / 0.25),
    0 3px 5px -2px oklch(from var(--schatten) l c h / 0.35);
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
}

.brettfrist.draengt {
  background: var(--aufkleber-frist-draengt);
  border-color: var(--aufkleber-frist-draengt-rand);
}

/* **Was draengt, hebt sich ab - und ist NICHT rot.**

   Rot gehoert dem **Blocker** (Karte 6), und *zwei Bedeutungen fuer eine
   Farbe kosten bei jeder Karte Zeit* (FAM-122). Der Zettel bleibt gelb; was
   sich aendert, ist die **Schrift**: dunkler und fetter.

   **Und das Wort traegt die Aussage** (FAM-65): Da steht *„Heute“* oder
   *„seit gestern“* - wer rot-gruen-blind ist, liest es genauso. Die Schrift
   ist die Zugabe, nicht die Auskunft. */
.brettfrist.draengt {
  color: var(--text);
  font-weight: 600;
}

/* --- Der rote Zettel (FAM-179) --------------------------------------------

   **Woertlich die Vorlage:** Auf dem Foto des Nutzers liegen rechts oben vier
   **rote** Quadrate im Vorrat, und eines klebt an einer Karte unter
   *Eingeplant* - gleich neben dem gelben. *„Ein Blocker ist ein roter
   Zettel."*

   **Dieselbe Papier-Rezeptur wie der gelbe** (FAM-169, FAM-178) - hier steht
   nur der **Ton**. *Zwei Zettel an derselben Karte, die aus zwei
   verschiedenen Rechnungen entstehen, laufen auseinander, und man saehe es
   erst, wenn einer anders hell ist als der andere.*

   **Ton 25 ist die Zahl, die `--fehler` traegt** (Abschnitt 1) - keine
   zweite rote Angabe (Regel 10: *jede Farbe steht an genau einer Stelle*).
   Woertlich die Bauart der Frist, die den Ton 90 aus `personenfarben.py`
   nimmt: *Eine erfundene Zahl waere dieselbe Sorte Behauptung wie eine
   erfundene Farbe* (Regel 5).

   **Und die SCHRIFT ist `--fehler`**, nicht `--text-leise` wie am gelben:
   Genau hier - und sonst nirgends auf einer Karte - steht etwas in Rot.
   *Rot heisst nur dieses eine* (FAM-122); deshalb traegt die ueberfaellige
   Frist es ausdruecklich nicht (FAM-178).

   **Die Rezeptur steht HIER und nicht auf `:root`** - dort gibt es kein
   `--papier-ton`, die Deklaration waere leer, und **leer wird vererbt** (der
   Fund vom 22.09.2026; bewacht von
   `test_keine_rezeptur_steht_dort_wo_ihr_ton_fehlt`).

   **Der Kontrast ist gerechnet**, und die Rechnung hat ihre Gegenprobe an
   einem Paar, dessen Wert man kennt (FAM-162): `--fehler` auf dem Papier
   ergibt **5,05 : 1**, `--text` **11,94 : 1** - beide ueber den 4,5, die die
   Karte verlangt.

   **Er darf umbrechen** (`anywhere`) wie die Marken daneben: *Ein Grund, der
   den Titel abschneidet, hat die Karte unlesbar gemacht.* Wie viel vom Grund
   ueberhaupt dasteht, entscheidet `GRUND_ANRISS` im Blueprint - nicht diese
   Regel. */
/* **Der Blocker ist ein Aufkleber IM FLUSS** (24.09.2026), und das ist die
   wichtigste Entscheidung dieser Gestaltung.

   **Absolut positioniert waere er bequem und falsch**: Sein Text kann drei
   Zeilen lang werden (*„warten auf den Termin beim Jugendamt“*), und dann
   ueberdeckt er, was unter ihm liegt - die naechste Karte oder das naechste
   Band. **Im Fluss waechst die Karte mit**; er ragt nur ueber negative
   Raender hinaus.

   **Das Wort traegt die Aussage, nicht die Farbe** (FAM-65): Da steht
   **Haengt:** und der Grund, ganz. */
.brettblocker {
  position: relative;
  z-index: 2;
  align-self: flex-start;
  /* **Links heraus, unten nicht** - und das ist eine gemessene Abweichung
     vom Auftrag (*„ragt links unten heraus“*): **Unter dem Zettel stehen
     die Griffe** (FAM-175), die es in der Vorlage 1b nicht gibt. Mit
     `-31px` lag er darueber - gemessen 16,9 px Ueberlappung, und *ein
     verdecktes Bedienelement ist schlimmer als ein Zettel, der nicht
     heraussteht* (Regel 7). */
  margin: 2px -6px 2px -30px;
  padding: 5px 10px 4px;
  background: var(--aufkleber-blocker);
  background-image: linear-gradient(180deg,
                                    oklch(from var(--glanz) l c h / 0.18),
                                    transparent 60%);
  color: var(--aufkleber-blocker-text);
  transform: rotate(-2.5deg);
  box-shadow:
    0 1px 1px oklch(from var(--schatten) l c h / 0.25),
    0 4px 6px -2px oklch(from var(--schatten) l c h / 0.35);
  font-size: 12.5px;
  line-height: 1.3;
  text-wrap: pretty;
  max-width: calc(100% + 36px);
  overflow-wrap: anywhere;
}

/* --- Die Zettel im Aufklapper - mit Geschichte ----------------------------

   **Aufgehobene stehen mit da**, und das ist der ganze Zweck der eigenen
   Tabelle (AUD-55): *Dass eine Karte dreimal an derselben Sache hing, ist
   die Auskunft, wegen der ein Brett existiert.* Am Brett **draussen** stehen
   sie nicht - dort gilt, was **gerade** ist.

   **Eine Liste ohne Aufzaehlungszeichen** - die Zeilen tragen ihren eigenen
   Bau, und ein Punkt davor verschoebe den Knopf. */
.blockerliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.25);
  width: 100%;
}

/* **`min-width: 0` an der Zeile**, damit der Grund schrumpfen darf.

   *Ohne sie schrumpft ein Flex-Element nicht unter seinen Mindestinhalt* -
   und dann steht die Zeile ueber den Rand ihrer Spalte hinaus, **ohne dass
   die Karte kaputt aussaehe** (der Fund von FAM-175, gemessen 75 px am
   Tablet). */
.blockerzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.25);
  min-width: 0;
  font-size: var(--schrift);
  color: var(--text-leise);
}

.blockergrund {
  flex: 1 1 8ch;
  min-width: 0;
  color: var(--fehler);
  overflow-wrap: anywhere;
}

.blockerzeit { flex: 0 0 auto; }

/* **Durchgestrichen, nicht weg.**

   **Der Strich ist die Zugabe, nicht die Auskunft** (FAM-65): Ein
   Vorleseprogramm meldet `line-through` nicht. Was es meldet, steht als
   **Wort** daneben - *hing bis* statt *haengt seit*, und die Maske setzt es,
   nicht diese Regel. */
.blockerzeile.aufgehoben > .blockergrund {
  color: var(--text-leise);
  text-decoration: line-through;
}

.blockerweg { flex: 0 0 auto; margin: 0; }

/* **Das Feld darf schrumpfen, der Knopf nicht** - derselbe Bau wie
   `.brettkarte-felder` daneben, und aus demselben gemessenen Grund
   (FAM-175). */
.blockerneu { margin-top: calc(var(--abstand) * 0.25); }

/* **Die Filterleiste** - eine ANZEIGE, keine Grenze (FAM-155, FAM-164).

   Dieselbe Bauart wie die Filterleiste der Freundesliste: Verweise, keine
   Knoepfe - *ein Filter aendert nichts, er zeigt etwas anderes.*

   **Jeder Ausschnitt ist eine Bedienflaeche** (`--schaltflaeche`), auch
   wenn nur ein Wort darin steht: *Was man trifft, ist groesser als das, was
   man sieht* (Design.md, 18.09.2026). */
/* **Die Filterleiste steht UEBER dem Rahmen, auf `--grund`** (24.09.2026) -
   und nicht an der Wand: *Ein Filter ist Bedienung, keine Wand. Man haengt
   nicht die Brille an die Pinnwand, durch die man sie ansieht.*

   Das ist die Entscheidung zwischen zwei Saetzen des Auftrags (4.1 gegen
   4.12); der zweite ist der genauere. */
/* **Die Aufgaben benutzen dieselbe Leiste** (18.10.2026, auf Wunsch:
   *„Bei Aufgaben möchte ich oben einen Filter auf ‚Meine Aufgaben'
   setzen."*).

   **Eine Zeile, keine Kopie** - wörtlich die Überlegung hinter
   `_abo.html` (FAM-116), `_brettwand.html` (24.09.2026) und dem
   Farbwähler (29.09.2026): *Das nächste Modul braucht eine Zeile, keine
   Kopie.* Ein zweiter Block mit denselben Werten liefe auseinander, und
   zwar an der unauffälligsten Stelle - jemand ändert die Pillenform am
   Brett und wundert sich, warum sie bei den Aufgaben eckig bleibt. */
.aufgabenfilter,
.brettfilter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
  margin-bottom: calc(var(--abstand) * 0.5);
}

/* **Pillen** (24.09.2026) - rund statt eckig, und mit einem Rand, wo sie
   nicht gewaehlt sind: Die Leiste steht jetzt ueber einer Wand und braucht
   eine eigene Kante, damit sie nicht auf dem Grund verschwindet.

   **34px, nicht `--schaltflaeche`** - das ist die eine Abweichung, die der
   Auftrag nennt, und sie ist dieselbe Sorte wie die vier Pixel flacheren
   Knoepfe der Frischen Ebene: *eine Entscheidung, kein Versehen.* Am Handy
   und am Infoboard bleibt `--schaltflaeche` die Untergrenze, damit ein
   Kind nicht danebentippt (Regel 10). */
.aufgabenfilterwort,
.brettfilterwort {
  display: inline-flex;
  align-items: center;
  min-height: max(34px, var(--schaltflaeche));
  padding: 0 calc(var(--abstand) * 0.75);
  border-radius: 999px;
  border: 1.5px solid var(--linie);
  font-size: var(--schrift);
  color: var(--text-leise);
  text-decoration: none;
  overflow-wrap: anywhere;
}

/* **Der gewaehlte Ausschnitt traegt `aria-current`**, und das Blatt liest
   genau dieses Merkmal - **keine zweite Klasse daneben**: Sonst gaebe es
   zwei Stellen, die sagen, wo man steht, *und eine davon liefe auseinander*
   (FAM-155). */
.aufgabenfilterwort[aria-current],
.brettfilterwort[aria-current] {
  background: var(--text);
  border-color: var(--text);
  color: var(--flaeche);
  font-weight: 600;
}

/* ===========================================================================
   15. Buttons „Textmarker“ (Design 2a, A5 - 27.09.2026)
   ===========================================================================
   *„Buttons sind Woerter ohne Kasten. Unter jedem Wort liegt ein Strich wie
   von einem Textmarker. Beim Ueberfahren malt der Marker das ganze Wort aus,
   und das Wort kippt leicht."*

   ## Warum das eine eigene Ebene ist

   **Wie die Frische Ebene (11) und die Filzwand (12/14)**: Sie steht hinter
   allem und gewinnt damit gegen die Abschnitte davor - *genau der
   Mechanismus, an dem FAM-107 gestolpert ist*, hier mit Absicht benutzt.
   **Was sie ueberschreibt, bleibt lesbar stehen** (Abschnitt 8 und 11), und
   wer sie zuruecknimmt, nimmt diesen Block heraus.

   ## Drei Regeln statt 125 Handgriffe

   **Gemessen**: 125 Knoepfe in 43 von 88 Vorlagen. Sie tragen aber schon
   heute ihre **Bedeutung** als Klasse - und genau die verlangt der Auftrag:

   | heute | A5 | Marker |
   |---|---|---|
   | `.schaltflaeche` (54x) | Neben | Hellblau |
   | `.betont` (49x) | Bestaetigen | Hellgruen |
   | `.gefahr` (5x) | Gefaehrlich | Hellrot, roter Text |

   *Eine Umstellung im Markup waere 125 Gelegenheiten, eine zu vergessen* -
   und die vergessene saehe aus wie ein Knopf, nur eben wie der alte.

   > **`.btn--haupt` fehlt hier bewusst.** Das ist der **eine grosse Knopf je
   > Seite** (32px), und er gehoert zum Seitenkopf aus A4 - *eine
   > Hauptaktion, die es noch nirgends gibt, waere ein Stil ohne Knopf.*

   ## Was `all: unset` mitnimmt

   Der Auftrag setzt es, und er sagt auch warum: **Schrift und Fokusanzeige
   muessen danach ausdruecklich wieder gesetzt werden.** Hier kommt ein
   Dritter dazu, den das Projekt teuer gelernt hat: **die Trefferflaeche**.
   `min-height: 44px` steht deshalb im Grundstil - *ein Knopf, den ein Kind
   am Touchscreen nicht trifft, ist keiner.*

   > **`--schaltflaeche` gilt weiter, sie bekommt nur einen Boden.** Der
   > Auftrag nennt **44px**; gemessen sind das mehr als am Handy (36) und am
   > Desktop (36) und weniger als am Tablet (48) und am Infoboard (72).
   > `--knopf-flaeche` ist deshalb `max()` aus beidem - **44 ist der Boden,
   > nicht die Decke**, und die Ansicht sagt weiterhin, wie gross eine
   > Bedienflaeche ist (Regel 0, Regel 10).
   =========================================================================== */

/* --- Die Marker-Farben (Regel 10: jede genau einmal) --------------------- */

:root {
  --marker-haupt:     oklch(90% 0.14 95);
  --marker-neben:     oklch(88% 0.06 265);
  --marker-bestaetigen: oklch(88% 0.1 150);
  --marker-gefahr:    oklch(88% 0.08 30);
  --btn-gesperrt:     oklch(58% 0.01 270);
}

/* **Die Trefferflaeche, an einer Stelle gerechnet.** 44 px sind der Boden
   aus dem Auftrag, die Ansicht sagt den Rest (Regel 10). Wer sie liest: der
   Grundstil darunter, der Stern, der Zeichenknopf, der Brettgriff und der
   Wegknopf einer Projektkarte - **vier Stellen, die vorher jede fuer sich
   `var(--schaltflaeche)` gerechnet haben** und am Handy bei 36 px landeten.

   > **Am 05.10.2026 sind sieben dazugekommen, und das war ein Fund**: das
   > Kalenderblatt (*Mein Tag*), das ✕ der Benachrichtigungszeile, der
   > Abhak-Knopf des Einkaufs, der Wegknopf eines Zettels, die beiden Griffe
   > einer Projektkarte, der Zeilengriff und der Rufknopf eines Kontaktwegs.
   >
   > **Der Durchgang vom 27.09.2026 hat sie uebersehen** - er hat die Knoepfe
   > **mit der Klasse `.schaltflaeche`** gemessen, und keiner der sieben
   > traegt sie. Gemessen am 05.10.2026 ueber **acht Masken x zwei Ansichten**
   > und *jedes* Bedienelement: `button`, `a[href]`, `summary`, Felder.
   >
   > **Und bei vier davon stand der Boden schon an der Hoehe und fehlte an
   > der Breite** - `min-height: var(--knopf-flaeche)` neben
   > `min-width: var(--schaltflaeche)`. *Eine Zusage auf einer Achse ist
   > keine Zusage*, und der Knopf sah dabei richtig aus: Die Hoehe trug der
   > Nachbar in derselben Zeile.

   **Sie steht am `body` und nicht an `:root`, und das ist kein Geschmack.**
   Ein Custom Property wird dort substituiert, wo es *deklariert* ist - auf
   `:root` traegt `--schaltflaeche` den Wert der **Automatik**, denn die
   Uebersteuerung steht eine Ebene tiefer (`body[data-ansicht]`, Regel 0).

   **Gemessen mit der falschen Fassung**: 44 px am Infoboard statt 72, und
   48 am Tablet - die Zahl folgte der Fensterbreite und nicht der Wahl. *Wer
   am Desktop „Infoboard“ waehlt, bekaeme die Knopfgroesse des Desktops, und
   die Maske saehe dabei richtig aus.* */
body { --knopf-flaeche: max(44px, var(--schaltflaeche)); }

/* --- Der Grundstil ------------------------------------------------------- */

/* **Die Varianten stehen hier mit dabei, und das ist kein Schmuck.**

   Abschnitt 8 und Abschnitt 11 setzen an `.betont` die **Kurzform**
   `background` - sie nimmt `background-image` mit weg, und ihre Selektoren
   wiegen (0,2,0) bzw. (0,1,1). Ein Grundstil, der nur `button` anspricht
   (0,0,1), verliert dagegen **unabhaengig davon, wie weit hinten er steht**.

   Genau das ist im Browser gemessen worden: 44 px hoch, fett, in der
   Handschrift - und **kein Marker**, dafuer die blaue Kontur von vorletzter
   Woche. *Die Suite war dabei gruen, denn im Blatt stand alles da.*

   Aufgezaehlt werden deshalb **dieselben** Selektoren wie drueben; bei
   gleichem Gewicht gewinnt die spaetere Regel, und das ist diese. */
button, .schaltflaeche, .haupt,
button.betont, .schaltflaeche.betont,
button.gefahr, .schaltflaeche.gefahr {
  all: unset;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  white-space: nowrap;
  flex: none;
  box-sizing: border-box;

  min-height: var(--knopf-flaeche);
  padding: 0 4px;

  font-family: var(--schrift-hand);
  font-weight: 700;
  font-size: 17px;
  line-height: 1.2;
  color: var(--btn-text, var(--text));

  /* **Der Marker ist ein Verlauf, kein Rahmen** - zwei gleiche Farbstopps
     ergeben eine Flaeche, und `background-size` macht daraus einen Strich,
     der beim Hover hochwaechst. *Ein `border-bottom` koennte das nicht: Er
     liegt unter dem Text, nicht dahinter.* */
  background-image: linear-gradient(var(--marker, var(--marker-neben)),
                                    var(--marker, var(--marker-neben)));
  background-repeat: no-repeat;
  background-position: 0 78%;
  background-size: 100% 6px;

  transition: background-size .25s ease, transform .2s;
}

/* **Dieselbe Rechnung beim Verweilen**: `.schaltflaeche.betont:hover` wiegt
   drueben (0,3,0) und faerbt die Flaeche. */
button:hover, .schaltflaeche:hover, .haupt:hover,
button.betont:hover, .schaltflaeche.betont:hover,
button.gefahr:hover, .schaltflaeche.gefahr:hover {
  background-size: 100% 70%;
  transform: rotate(-1.5deg);
  /* **Die alte Ebene faerbte beim Hover die Flaeche** (Abschnitt 11) - das
     nimmt der Marker jetzt. */
  background-color: transparent;
  color: var(--btn-text, var(--text));
}

button:active, .schaltflaeche:active, .haupt:active { transform: rotate(-1.5deg) scale(.96); }

/* **Die Fokusanzeige muss nach `all: unset` neu gesetzt werden** - sonst
   gibt es sie nicht mehr, *und niemandem faellt es auf*, weil mit der Maus
   ohnehin nichts erscheint (die Lehre aus der Frischen Ebene). */
button:focus-visible, .schaltflaeche:focus-visible, .haupt:focus-visible {
  outline: 2px solid var(--akzent);
  outline-offset: 4px;
  border-radius: 2px;
  box-shadow: none;
}

/* --- Die vier Bedeutungen ------------------------------------------------ */

button.betont, .schaltflaeche.betont {
  --marker: var(--marker-bestaetigen);
  background-color: transparent;
  color: var(--text);
}

button.gefahr, .schaltflaeche.gefahr {
  --marker: var(--marker-gefahr);
  --btn-text: var(--fehler);
  background-color: transparent;
  color: var(--fehler);
}

/* **Gesperrt traegt keinen Marker** - *ein Strich unter einem Wort sagt „das
   kannst du nehmen"*, und genau das stimmt hier nicht. */
button:disabled, .schaltflaeche:disabled, .schaltflaeche[aria-disabled="true"] {
  background-image: none;
  color: var(--btn-gesperrt);
  text-decoration: line-through;
  cursor: not-allowed;
  transform: none;
}

/* --- Ein Zeichen ist kein Wort ------------------------------------------- */

/* **Unter einem Stern liegt kein Textmarker.** Der Auftrag beschreibt
   Woerter (*„Buttons sind Woerter ohne Kasten“*) und sagt dazu ausdruecklich:
   *„Buttons nur mit Icon gibt es nicht.“* - **Gemessen gibt es sie doch**:
   der Stern an einer Aufgabe, die Sonne fuer *Mein Tag*, das Kreuz an der
   Benachrichtigungszeile und der Zeichenknopf (Haken, Papierkorb).

   Sie umzubenennen waere eine **Aenderung der Bedienung**, nicht des
   Aussehens, und gehoert damit in eine eigene Karte (*vorgebaute Oberflaeche
   ist ungepruefte Oberflaeche*, FAM-73). Bis dahin gilt, was seit dem
   18.09.2026 im Blatt steht: **ein Zeichenknopf soll ein Zeichen bleiben** -
   und ein Strich unter einem Haken sieht aus wie ein Fehler.

   *Das ist dieselbe Ueberlegung wie bei `:disabled` darueber: Der Marker
   sagt „hier steht ein Wort, das du nehmen kannst“.* */
/* **Woran man einen erkennt, steht schon im Markup**: `knopfzeichen()`
   legt das Wort in `.nurvorlesen` - in den Vorlesebaum, nicht auf das Bild
   (`_symbole.html`, 18.09.2026). Der Stern, die Sonne und das Kaestchen
   machen es genauso.

   **Die erste Fassung zaehlte vier Klassennamen auf**, und die Messung fand
   binnen einer Minute zwei weitere. *Eine Liste erratener Namen faengt den
   nicht, der einen anderen nimmt* (FAM-146) - `:has()` fragt stattdessen
   nach der Eigenschaft und faengt damit auch den naechsten.

   `:not(:has(.knopfwort))` ist die Gegenrichtung: Ein Knopf, der sein Wort
   **zeigt**, behaelt seinen Marker, auch wenn daneben noch etwas fuer das
   Vorleseprogramm steht. */
/* **Und ein Zeichen-VERWEIS ist genauso einer** (21.10.2026).

   Bis heute nannten beide Regeln nur `button` - es gab keinen Zeichenknopf,
   der ein `<a>` war. Mit den beiden Pfeilen des Kalenders gibt es ihn, *und
   er bekäme sonst einen Textmarker unter seinen Pfeil.*

   **Gefragt wird weiter nach der Eigenschaft, nicht nach einem Namen** -
   die Zeile darüber sagt es selbst, sie zählte nur ein Element zu wenig
   auf. */
button:has(.nurvorlesen):not(:has(.knopfwort)),
.schaltflaeche:has(.nurvorlesen):not(:has(.knopfwort)),
button.haken, .wegklicken {
  background-image: none;
}

/* **Und ein Zeichenknopf bekommt den Boden auch in der Breite** (05.10.2026).
   *Ein Wortknopf ist so breit wie sein Wort; ein Zeichenknopf hat keines* -
   ihm bleibt nur, was hier steht.

   ## Warum die Zeile hier steht und nicht nur an den sieben Knoepfen

   **Gemessen am Projektbrett: `.betont.brettkarte-knopf` war 24,6 px breit**,
   obwohl `.brettkarte-knopf` `min-width: var(--knopf-flaeche)` setzt.
   Der Grund ist `all: unset` oben: Es steht in einer Liste, die auch
   `button.betont` (0,1,1) aufzaehlt - **und das schlaegt eine einzelne
   Klasse (0,1,0), unabhaengig von der Reihenfolge im Blatt.** Jeder
   Zeichenknopf, der `.betont` traegt, verliert damit lautlos seine Breite.

   Dieser Selektor wiegt (0,2,1) und schlaegt `button.betont`. **Gefragt wird
   wieder nach der Eigenschaft, nicht nach einem Namen** - so faengt die
   Zeile auch den naechsten Knopf, den noch niemand gebaut hat.

   > **Die sieben Regeln daneben bleiben trotzdem stehen.** Sie lesen
   > dieselbe Variable, und `--knopf-flaeche` ist weiterhin die **eine**
   > Stelle, an der die Zahl entschieden wird - dasselbe Verhaeltnis wie bei
   > den vierzig `min-height`-Lesern im Blatt. */
button:has(.nurvorlesen):not(:has(.knopfwort)),
.schaltflaeche:has(.nurvorlesen):not(:has(.knopfwort)) {
  min-width: var(--knopf-flaeche);
}

/* --- Kein Umbruch, lieber kleiner ---------------------------------------- */

/* **Der Text eines Knopfes bricht nie um** (A5). Fehlt der Platz, wird die
   Schrift kleiner - dafuer misst `cqi` die **Karte**, nicht den Bildschirm.

   **Ohne `container-type` ist `cqi` wirkungslos** und faellt still auf den
   Hoechstwert zurueck: *Der Knopf waere wieder zu breit, und nichts saehe
   kaputt aus.* Deshalb steht es an der Karte selbst. */
.karte, .leihzeile, .brettkarte, .sticker, .aufgabe {
  container-type: inline-size;
}

.karte button, .karte .schaltflaeche,
.leihzeile button, .leihzeile .schaltflaeche,
.brettkarte button, .brettkarte .schaltflaeche,
.sticker button, .sticker .schaltflaeche,
.aufgabe button, .aufgabe .schaltflaeche {
  font-size: clamp(13px, 5.5cqi, 17px);
}

/* **Wer keine Bewegung will, bekommt keine** (A5, und es ist die einzige
   Stelle im Blatt mit dieser Abfrage). */
@media (prefers-reduced-motion: reduce) {
  button, .schaltflaeche { transition: none; }
  button:hover, .schaltflaeche:hover,
  button:active, .schaltflaeche:active { transform: none; }
}

/* ===========================================================================
   16. Die App-Hülle und der Seitenkopf (Design 2a, A3/A4 - 27.09.2026)
   ===========================================================================
   *„Tablet und Desktop: feste Seitenleiste links, 216px breit, weiß.
   Handy: Kopfzeile 56px, ‚Menü‘ öffnet eine Schublade von links."*

   ## Eine Hülle, zwei Lagen - und kein `@media`

   Die Kopfzeile **ist** die Seitenleiste; sie steht nur einmal oben und
   einmal links. Entschieden wird das über zehn Merkmalsvariablen (Regel 0),
   nicht über eine Abfrage im Blatt: *Eine Media Query kennt die
   Ansichtsauswahl nicht* - wer am Desktop „Handy" wählt, bekäme sonst
   trotzdem die Seitenleiste.

   **Der Name `kopfzeile` bleibt.** Am Handy ist sie eine, ab Tablet dieselbe
   Hülle hochkant - *zwei Namen für dieselbe Frage laufen auseinander*
   (FAM-52, FAM-117; wörtlich die Entscheidung der Filzwand zu
   `--board-spalten`).

   ## Das Infoboard bleibt, wie es war

   Der Auftrag nimmt es ausdrücklich aus (*„nicht Teil dieses Designs, wird
   separat neu gestaltet"*). Es trägt deshalb dieselben Werte wie das Handy.

   ## Wie die Tafel offen stehen bleibt

   `<details>` versteckt seinen Inhalt nicht mit `display`, sondern seit
   Chrome 131 mit `content-visibility: hidden` am Pseudo-Element
   `::details-content`. **Gemessen** (27.09.2026, in einem leeren Rahmen, weil
   das eigene Blatt die Messung sonst färbt):

   | | Höhe des geschlossenen `<details>` |
   |---|---|
   | ohne Zutun | 18 px (nur der Griff) |
   | `> nav { display: block }` | **18 px - wirkungslos** |
   | `::details-content { content-visibility: visible }` | **58 px** |
   | wirklich offen | 58 px |

   **Der Preis ist ausgesprochen**: Ein Browser, der `::details-content`
   nicht kennt, zeigt die Tafel nicht offen - **dann steht dort der
   Hamburger von gestern**, und der funktioniert vollständig (Regel 7). *Das
   ist eine Verschlechterung des Aussehens, kein Verlust eines Weges.*

   > **Und die Messung selbst hatte zwei Anläufe.** `getBoundingClientRect`
   > und `checkVisibility` sagen beide **nichts** über ein geschlossenes
   > `<details>`: Das erste liefert echte Rechtecke (die Falle aus
   > `11-fallstricke.md`), das zweite meldet `true`, weil es nur
   > `content-visibility: auto` prüft. Gemessen wird die **Höhe des
   > `<details>` selbst** - sie wächst nur, wenn der Inhalt wirklich da ist.
   =========================================================================== */

/* --- Die Hülle -------------------------------------------------------- */

/* **`inset` wirkt nur bei `fixed`** - bei `relative` sind `top: 0` und
   `left: 0` lauter Null-Verschiebungen, `bottom` gilt neben `top` nicht, und
   `right: auto` ist keine Angabe. Also steht die Zeile einmal da statt
   zweimal; dieselbe Überlegung wie bei `max()` in der Trefferfläche: *eine
   Rechnung, eine Stelle.*

   **Und `relative` ist kein Schmuck, sondern die zweite Hälfte einer
   Zusage**: `.menuetafel` steht auf `position: absolute; top: 100%` und
   braucht einen positionierten Vorfahren (Abschnitt 8). Hier stand
   zuerst `static` - **dann rechnete die Schublade am Handy gegen das
   Fenster und fiel unter dessen Unterkante.** */
.kopfzeile {
  position: var(--huelle-lage);
  inset: 0 auto 0 0;
  width: var(--huelle-breite);
  flex-direction: var(--huelle-richtung);
  z-index: 30;
}

/* **Der Inhalt macht der Leiste Platz.** Zwei Variablen und nicht eine, und
   das ist Absicht: Die eine beantwortet *„wie breit ist die Hülle"*, die
   andere *„wie viel Platz lässt der Inhalt"*. Am Handy ist die erste `auto`
   und die zweite `0px` - ein `padding-left: auto` gäbe es nicht. */
body { padding-left: var(--rumpf-einzug); }

/* **Keine Hülle, kein Einzug** (29.09.2026, Vollbild am Projektbrett).

   **Gemessen am Tablet: 467 px Inhalt mit Hülle und 467 px ohne** - das
   Vollbild nahm die Leiste weg und liess ihre 240 px stehen. *Es sah aus
   wie ein breiter Rand und war ein Platz für etwas, das nicht mehr da ist.*

   **Gefragt wird nach der Tatsache, nicht nach einem Merker**: `:has()`
   sieht, dass die Hülle fehlt - ein zweites Attribut am `<body>` wäre eine
   zweite Wahrheit darüber, wie die Seite gerade aussieht, und die läuft
   auseinander. *Ein Browser ohne `:has()` behält den Einzug: nüchtern,
   nicht kaputt.* */
body:not(:has(> .kopfzeile)) { padding-left: 0; }

/* --- Das Brett im Vollbild (29.09.2026, auf Wunsch) ----------------------

   *„Wenn man bei Projekte auf Vollbild drückt, dann sollte das Board den
   Bildschirm fast vollkommen einnehmen und die Karten auf dem Board dürfen
   dementsprechend größer werden und können mehr Informationen anzeigen."*

   **Gemessen vorher** (Desktop, 1280 × 812): Das Vollbild gab **Breite**
   dazu – Inhalt 960 → 1200, Wand 885 → 1060 –, aber **die Karte blieb
   154 × 186 px und ihr Titel 23 px**. Der Seitenkopf kostete unverändert
   112 px Höhe. *Es nahm die Hülle weg und sonst nichts.*

   **Erkannt wird der Zustand an der Tatsache**, nicht an einem zweiten
   Merker: Die Hülle fehlt, und der Inhalt trägt eine Brettwand. *Ein
   `data-vollbild` am `<body>` wäre eine zweite Wahrheit darüber, wie die
   Seite gerade aussieht – und die läuft auseinander.*

   **Und es sind Werte, keine zweite Bauart** (Regel 0): Dieselben
   Merkmalsvariablen, andere Zahlen. Wer am Desktop *Handy* wählt, bekommt
   im Vollbild die Handy-Maße mit demselben Aufschlag. */
body:not(:has(> .kopfzeile)):has(.brettwand-raum) {
  /* **Die Karten wachsen – und der Titel mit ihnen**: Eine breitere Karte
     mit derselben Schrift sieht aus wie eine leere.

     **Je Ansicht ein eigener Wert** (Regel 10), keine Rechnung.

     > **Der erste Versuch rechnete** – `calc(var(--kachel-min) * 0.85)`
     > und `calc(var(--schrift) + 10px)`. Gemessen war das Ergebnis am
     > Handy **kleiner als vorher** (121 statt 154 px Karte, 22 statt
     > 23 px Titel) und am Infoboard **gleich** (34 px).
     >
     > *Eine Formel, die für eine Ansicht passt, ist für die anderen drei
     > geraten* – und am Handy scrollt das Brett ohnehin seitwärts, dort
     > gibt das Vollbild nur Höhe. */
  --brettkarte-min: var(--brettkarte-min-voll);
  --brettkarte-titel: var(--brettkarte-titel-voll);

  /* **Jetzt hat die Beschreibung Platz** – siehe `.brettkarte-text`. */
  --brettkarte-text-anzeige: -webkit-box;

  /* **Der Rand wird schmaler, nicht null.** Ganz ohne stünde die Wand an
     der Bildschirmkante, und *ein Brett ohne Rahmen ist kein Brett* – die
     Filzwand lebt von ihrem Aluminium (24.09.2026). */
  --brett-raum-abstand: calc(var(--abstand) * 0.25);
  --seitentitel: var(--schrift-gross);
}

/* **Und der Inhalt gibt seinen Seitenrand frei** (29.09.2026).

   *Der Rand ist dafür da, dass Text nicht an der Kante klebt; eine
   Filzwand klebt nicht.*

   > **Der erste Versuch stand am `.rumpf` und hat das Gegenteil
   > bewirkt**: Dort gab es gar kein seitliches Polster, meine Zeile hat
   > **10 px hinzugefügt** – gemessen rückte der Inhalt von x = 40 auf
   > x = 50. *Wer einen Rand wegnehmen will, misst zuerst, wo er
   > herkommt.*
   >
   > Gemessen liegt er an zwei Stellen: `.hauptbereich` trägt **40 px**,
   > der Brettraum selbst noch **22,5 px** Außenabstand. */
body:not(:has(> .kopfzeile)) .hauptbereich:has(.brettwand-raum) {
  padding-inline: calc(var(--abstand) * 0.5);
}
body:not(:has(> .kopfzeile)) .brettwand-raum {
  margin-inline: 0;
  /* **Und der Deckel fällt** (29.09.2026).

     `.brettwand-raum` trägt `max-width: 1120px` – **gemessen war das die
     Grenze, nicht der Rand**: Bei 1280 px Fenster blieben 140 px leer,
     obwohl der Inhalt daneben 1260 breit war. *Ein Deckel, der im
     Vollbild stehen bleibt, ist genau das Gegenteil von „fast vollkommen
     einnehmen".*

     **Er bleibt überall sonst**: Eine Filzwand, die auf einem 2560 px
     breiten Bildschirm über die ganze Breite läuft, hat Spalten, die
     niemand mehr überblickt. Im Vollbild hat jemand ausdrücklich danach
     gefragt. */
  max-width: none;
}

/* **Wohin Marke und Menü gehören, steht weiterhin in Abschnitt 8** - dort
   stand es seit FAM-107, und diese Ebene liefert nur die **Werte**
   (`--marke-reihenfolge`, `--huelle-mindestbreite`). *Ein zweites `order`
   hier wäre genau die Falle, gegen die jene Wache gebaut wurde* - und sie
   hat beim ersten Versuch sofort gesprochen. */
.kopfzeile { justify-content: var(--huelle-verteilung); align-items: stretch; }

/* In der Leiste ist die Tafel kein Aufklapper mehr - **sie schwebt nicht**,
   sie ist die Leiste. */
.menuetafel {
  border-width: var(--menuetafel-rahmen);
  box-shadow: var(--menuetafel-schatten);
}

/* Der Hamburger verschwindet, sobald die Tafel ohnehin offen steht - *ein
   Knopf, der nichts mehr aufklappt, ist einer, der eine Fehlermeldung
   erzeugt* (Regel 7). */
.menueknopf { display: var(--menueknopf-anzeige); }

/* --- Die Tafel: Schublade oder Leiste ------------------------------------ */

.menue { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
/* **Die Variable beantwortet nur den geschlossenen Zustand**, und das ist
   der ganze Unterschied zwischen Schublade und Leiste: *steht die Tafel
   auch dann da, wenn das `<details>` zu ist?*

   **Offen heisst offen, in jeder Ansicht.** Ohne die zweite Zeile verbirgt
   `content-visibility: hidden` den Inhalt auch im geöffneten Zustand -
   **am Handy ging die Schublade dann gar nicht mehr auf**, und zu sehen war
   nur die Abdunklung. Gemeldet hat es der Nutzer, gemessen ist es oben. */
.menue::details-content { content-visibility: var(--menuetafel-inhalt); }
.menue[open]::details-content { content-visibility: visible; }

.menuetafel {
  position: var(--menuetafel-lage);
  /* **Auch hier `dvh`** (05.11.2026) - diese Zeile kommt später im Blatt und
     gewinnt, eine Korrektur nur oben wäre wirkungslos gewesen. */
  max-height: var(--menuetafel-hoehe, calc(100vh - var(--kopfhoehe)));
  max-height: var(--menuetafel-hoehe, calc(100svh - var(--kopfhoehe)));
}

/* --- Die Abdunklung hinter der Schublade --------------------------------- */

/* **Ein Tipp daneben schließt sie**, und das geht ohne eine Zeile
   JavaScript: Die Abdunklung ist das `::after` des **Griffs** - wer sie
   antippt, tippt den Griff an, und `<details>` klappt zu.

   *Ein eigenes Element dafür bräuchte ein Skript, das es zuklappt - und
   ohne das Skript stünde eine Fläche da, die nichts tut* (Regel 7). */
.menue[open] > .menueknopf::after {
  content: "";
  display: var(--abdunklung-anzeige);
  position: fixed;
  inset: var(--kopfhoehe) 0 0 0;
  z-index: -1;
  background: var(--abdunklung);
}

/* --- Der Seitenkopf (A4) -------------------------------------------------- */

/* **Links der Titel, rechts die Knöpfe - in einer Zeile, ohne Umbruch.**
   Am Handy untereinander; das ist die einzige Ansicht, in der der Titel die
   ganze Breite braucht. */
/* **Zwei Zeilen, und die erste trägt drei Zonen** (29.09.2026, auf
   Auftrag: *„Bitte nimm den Titel, Unterzeile und Werkzeugleiste von
   Einkauf als Konzept für alle Module und baue sie um."*).

   | | |
   |---|---|
   | Zeile 1 | leer · **Titel** (mittig) · **Werkzeugleiste** (rechts) |
   | Zeile 2 | die **Unterzeile**, über die ganze Breite |

   ## Warum ein Raster und keine Flexzeile

   Der Titel soll in der Mitte **der Seite** stehen, nicht in der Mitte
   dessen, was neben den Knöpfen übrig bleibt. Das leisten zwei gleich
   breite Außenspalten (`1fr … 1fr`) – eine Flexzeile kann es nicht, weil
   die linke Seite dort leer ist und keine Breite hat.

   > **Hier stand bis zum 29.09.2026 eine Flexzeile** (Design 2a, A4):
   > Titelgruppe links, Knopfgruppe rechts. Sie hat gut getragen, solange
   > der Titel links stand. *Die Mitte ist die Entscheidung des Auftrags,
   > und sie kostet das Raster.*

   ## Die Werkzeugleiste steht jetzt IM Titel

   Bis heute war sie ein Geschwister der Titelgruppe und wurde im Einkauf
   per Raster nach oben gezogen – **und dann sprang der Fokus**: erst das
   Fragezeichen, dann die Unterzeile unten links, dann der Knopf oben
   rechts. *Eine Reihenfolge, die man sieht, und eine, die eine Tastatur
   geht, sind zwei Wahrheiten.* */
.seitenkopf {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0 14px;
  margin-bottom: calc(var(--abstand) / 2);
}

.titelzeile {
  display: grid;
  /* **Die Außenspalten fordern ihren Inhalt, die Mitte darf schrumpfen.**

     `minmax(auto, 1fr)` heißt: mindestens so breit wie der Inhalt, sonst
     gleich viel wie die andere Seite. **Solange Platz ist, sind beide
     gleich breit - und damit steht der Titel in der Mitte der Seite.**
     Wird es eng, bekommt die Knopfspalte, was sie braucht, und der Titel
     weicht: *Ein Knopf, der nicht mehr erreichbar ist, ist schlimmer als
     ein gekürzter Titel* (FAM-50, FAM-84).

     Gemessen am Projektbrett/Handy: **0 | 221 | 127** statt
     38,8 | 270,9 | 38,8 - der Titel bricht in zwei Zeilen, und beide
     Knöpfe stehen frei. */
  grid-template-columns: minmax(auto, 1fr) minmax(0, auto) minmax(auto, 1fr);
  align-items: center;
  gap: 0.4em;
}

/* Titel und Fragezeichen sind **eine** Zelle - sonst belegen beide
   dieselbe Spalte und landen in **zwei Zeilen** (gemessen am 29.09.2026:
   Kopf 98 px statt 67). Regel 9: Der Weg zur Hilfe wandert mit dem Titel. */
.titelgruppe {
  grid-column: 2;
  justify-self: center;
  display: flex;
  /* **Die Ausrichtung kommt aus einem Merkmal** (09.10.2026): Am Handy
     steht der Kasten im Kopfstreifen und ist so hoch wie dieser - dort
     gehoert er in die Mitte. Ab Tablet ist er so hoch wie sein Inhalt, und
     dann ist die Grundlinie richtig: Das Fragezeichen sitzt auf derselben
     Linie wie der Titel und nicht in der Mitte eines Kastens. */
  align-items: var(--titelgruppe-ausrichtung);
  gap: 0.4em;
  /* **Ohne diese Zeile schrumpft sie nicht** - ein Flex-Container geht
     nicht unter seinen Mindestinhalt, und die Rasterspalte darüber nützt
     dann nichts. Gemessen am Projektbrett: *„Garten neu machen"* blieb am
     Handy einzeilig, und die Knöpfe lagen darüber. */
  min-width: 0;
}

/* --- Der Titel steht am Handy IM Kopfstreifen (09.10.2026, auf Wunsch) ---

   *„Kannst auf dem Handy den Titel vom Modul oben mittig zwischen Menü und
   Family Board platzieren."* – und, ein Satz später: *„Family Board auf dem
   Handy viel kleiner machen und den Titel auf dem Handy kleiner machen."*
   **Der zweite Satz ist die Rechnung zum ersten:** Drei Dinge in einen
   56 px hohen Streifen gehen nur, wenn zwei davon schrumpfen –
   `--marke-schrift` 18 → 12, `--seitentitel` 28 → 17 (Regel 10).

   ## Warum `fixed` und nicht `absolute`

   Abschnitt 2 sagt über die alte Fassung: *„Zentriert mit `margin: 0 auto`,
   nicht mit `position: absolute`"* – ein absolut gesetzter Titel liefe bei
   einem langen Namen **unter** Menü und Marke. **Das Argument gilt und ist
   hier beantwortet, nicht übergangen:** Der Kasten ist auf
   `--titelgruppe-breite` **begrenzt** und erreicht die Nachbarn gar nicht;
   was nicht hineinpasst, kürzt `.seitentitel` mit Ellipse (Abschnitt 3).

   **`absolute` wäre hier technisch nicht einmal möglich.** Der Titel ist
   seit dem 29.09.2026 **kein Kind der Kopfzeile mehr**, sondern steht im
   Seitenkopf – und `absolute` rechnet gegen den nächsten *positionierten
   Vorfahren*, und das ist nicht `.kopfzeile`. **Es bleibt genau ein `<h1>`
   auf der Seite** (bewacht in `test_ansichten.py`), also wird der Titel
   **bewegt und nicht kopiert**.

   `fixed` ist am Handy dasselbe wie `absolute` im Kopf: Der Streifen steht
   oben am Fenster, und die Seite scrollt nicht (Regel 2).

   ## Und er darf nichts verdecken

   Der Kasten liegt über dem Menüknopf: Dessen Box trägt `flex: 1 1 0` und
   reicht bis in die Fenstermitte, auch wenn das Wort *Menü* links aufhört.
   Ohne `pointer-events: none` wäre die Mitte des Streifens eine **tote
   Zone über dem einzigen Weg ins Menü** – wörtlich die Frage aus FAM-50 und
   FAM-84: *Gibt es ein Bedienelement, das nirgends erreichbar ist?* Das
   Fragezeichen nimmt sich seine Klicks zurück; es ist ein Weg, kein Text
   (Regel 9).

   ## Ab Tablet ändert sich nichts

   `static`, `auto`, `auto`, `baseline` – dann gilt wieder das Raster
   darüber. **Und das ist der Grund für vier Merkmale statt eines:** `top`
   und `inset-inline` sind in der statischen Lage wirkungslos, `width`,
   `height` und `align-items` **nicht**. *Eine Zeile, die nur in einer
   Lage gelten soll, gehört in ein Merkmal* (Regel 0). */
.titelgruppe {
  position: var(--titelgruppe-lage);
  top: 0;
  /* **Beide Ränder auf 0, den Rest macht `margin-inline: auto`** – das
     zentriert den gedeckelten Kasten im Streifen. In der statischen Lage
     tun die Ränder nichts, und `auto` ist dort dasselbe wie das
     `justify-self: center` darüber. */
  inset-inline: 0;
  margin-inline: auto;
  /* **Eine Breite, keine Hoechstbreite** - und das ist der Zuruf einer
     Wache: Wer mit Auto-Raendern zentriert, gibt eine Breite, sonst
     schrumpft der Kasten auf seinen Inhalt und sieht dabei zentriert aus
     (`test_wer_mit_auto_raendern_zentriert_gibt_eine_breite`). **Am Handy
     steht der Titel damit immer gleich weit mittig** - unabhaengig davon,
     wie lang er ist; was nicht hineinpasst, kuerzt `.seitentitel`.

     **Ab Tablet ist der Wert `auto`**, also die Vorgabe: Dort entscheidet
     das Raster, und die Ansicht laesst die Breite ausdruecklich offen. */
  width: var(--titelgruppe-breite);
  height: var(--titelgruppe-hoehe);
  justify-content: center;
  /* Über der Kopfzeile, die `z-index: 30` trägt (Abschnitt 16). */
  z-index: 31;
  pointer-events: none;
}
.titelgruppe .hilfezeichen { pointer-events: auto; }

/* **Und der Titel bleibt in seinem Kasten** (09.10.2026, gemessen).

   Die Breite oben deckelt die **Gruppe**, nicht den Text darin: Der Titel
   ist ein Flexkind mit `min-width: 0`, und der Seitenkopf gibt ihm
   ausdruecklich `overflow: visible` – *seine Schrift laeuft also aus ihrem
   eigenen Kasten heraus, ohne dass die Gruppe breiter wird.*

   **Im Normalfall bricht er um** (`white-space: normal`, Abschnitt 17), und
   dann gibt es nichts zu kuerzen: Gemessen steht *„Was nicht geklappt
   hat"* zweizeilig in 34 px und damit im 56 px hohen Streifen. **Der
   Einkauf ist die Ausnahme** – er setzt `nowrap`, damit *„Einkauf
   Baumarkt"* eine Zeile bleibt. Gemessen passt das (132,4 px von 150), und
   es passt nur, solange der Listenname kurz ist: Ab **198 px** Titelbreite
   lief die Schrift unter *Family Board* (dessen Text beginnt bei x = 286,6).

   **Gekuerzt wird der Titel, nicht die Gruppe.** Ein `overflow: hidden` am
   Kasten darum waere der kuerzere Weg und der falsche: Das Fragezeichen
   endet gemessen **genau an der rechten Kante** (262,5), und damit waere
   sein Fokusring abgeschnitten – *„Fokus ist immer sichtbar"* (Design 2a,
   A9).

   `text-overflow` steht ohne Merkmal da: Es wirkt nur, wo `overflow` nicht
   `visible` ist, und ist in den anderen drei Ansichten damit wirkungslos. */
.titelgruppe .seitentitel {
  overflow: var(--titelgruppe-ueberlauf);
  text-overflow: ellipsis;
}

/* **Die Leiste steht rechts neben dem Titel - solange sie dort hinpasst.**

   **Gemessen am 29.09.2026**: Der Terminplaner hat vier Aktionen. In der
   rechten Spalte standen sie am Handy **untereinander** (y = 120 bis 311)
   und lagen dabei **unsichtbar über dem Inhalt**, weil die Leiste
   `height: 0` trug. *Eine Werkzeugleiste, die eine Aktion verdeckt, ist
   keine* (FAM-84).

   **Die Grenze ist die Zahl der Aktionen, nicht die Ansicht**: Ob es eng
   wird, hängt an der Maske und nicht am Gerät - eine Merkmalsvariable
   (Regel 0) wäre hier die falsche Antwort. Drei Elemente und mehr bekommen
   deshalb eine **eigene Zeile über die ganze Breite**; dort stehen sie
   nebeneinander wie vorher.

   *Gezählt werden Elemente, nicht Knöpfe* - die Trenner zählen mit, und
   das ist gewollt: Sie kosten Platz wie alles andere. */
/* **`:not(:has(.kopfaktion))` steht seit dem 21.10.2026 davor**, und es ist
   keine Verzierung: Gibt es eine Hauptaktion neben dem Titel, gehoert die
   Leiste ohnehin in die zweite Zeile - *aber dann ueber die ganze Breite und
   nicht in Spalte 1.*

   > **Ohne diese Ausnahme gewinnt diese Regel**, und zwar an der
   > **Spezifitaet**: Sie wiegt (0,4,0), die Regel der Kopfaktion weiter
   > unten nur (0,3,0) - *unabhaengig von der Reihenfolge im Blatt.*
   > Gemessen am Handy stand die Leiste danach in einer **159,8 px**
   > schmalen Spalte, und der dritte Knopf brach um. *Die Falle steht im
   > Index, und sie hat hier zum dritten Mal zugeschlagen.* */
.titelzeile:has(.werkzeugleiste > :nth-child(3)):not(:has(.kopfaktion)) {
  grid-template-columns: minmax(0, 1fr);
}
.titelzeile:has(.werkzeugleiste > :nth-child(3)):not(:has(.kopfaktion)) .titelgruppe {
  grid-column: 1;
}
.titelzeile:has(.werkzeugleiste > :nth-child(3)):not(:has(.kopfaktion)) .werkzeugleiste {
  grid-column: 1;
  justify-self: stretch;
  /* **Gestreckt beginnt sie wieder links** (29.09.2026).

     Die Grundregel richtet den Inhalt seit heute **rechts** aus, damit das
     Abo beim Umbruch bündig bleibt. Über die **ganze Breite** wäre das die
     falsche Antwort: Dort ist Platz für alle Aktionen, und Regel 1 sagt
     *„die Hauptaktion zuerst"* - die steht links. */
  justify-content: flex-start;
}

.werkzeugleiste {
  grid-column: 3;
  justify-self: end;
  /* **Hier noch einmal, und das ist kein Versehen** (29.09.2026, auf
     Rueckmeldung: *„Abo ist wieder weiss hinterlegt"*).

     Die Frische Ebene (Abschnitt 11) setzt `.inhalt, .werkzeugleiste,
     .chat-neu { background: var(--flaeche) }` - **gleiche Spezifitaet, aber
     spaeter im Blatt** als die Grundregel der Leiste. Wer `transparent` nur
     dort einträgt, wird davon ueberschrieben.

     *Eine alte Regel weiter unten im Blatt gewinnt gegen die neue* - die
     Falle steht im Index, und sie hat hier zugeschlagen, weil ich die drei
     Zeilen beim Zusammenfuehren nach **oben** verschoben habe. */
  background: transparent;
  /* **Ihr Platzbedarf zählt** - deshalb steht hier **kein** `min-width: 0`.

     > **Genau das war der Fehler, drei Runden lang** (29.09.2026): Mit
     > `min-width: 0` meldet die Leiste dem Raster einen Bedarf von **null**,
     > und `minmax(auto, 1fr)` an der Spalte greift ins Leere. Gemessen am
     > Projektbrett/Handy: Spalten **38,8 | 270,9 | 38,8**, die Leiste
     > **127 px breit** - und *„Alle Vorhaben"* lag über dem Titel.
     >
     > Mit ihrem echten Bedarf: **0 | 221 | 127**, keine Überlappung, und
     > der Titel bricht in zwei Zeilen. */
  /* **Die Leiste behält ihre Höhe**, und das ist eine Entscheidung gegen
     zwölf gesparte Pixel.

     > **Hier stand `height: 0`** (29.09.2026, für den Einkauf gebaut): Die
     > Trefferfläche ist 44 px, der Titel daneben niedriger - ohne die
     > eigene Höhe wäre der Kopf schmaler. **Gemessen hat es zwei Knöpfe
     > unerreichbar gemacht**: Am Projektbrett rückte der Inhalt darunter
     > hoch, und ein `<select>` lag über *„Alle Vorhaben"* und *„Zeilen"* -
     > `elementFromPoint` traf in der Mitte beider das Auswahlfeld.
     >
     > *Eine Werkzeugleiste, die eine Aktion abschneidet, ist keine*
     > (FAM-84) - und eine, die sie verdeckt, erst recht. **Zwölf Pixel
     > sind kein Bedienelement wert.**

     `align-self: center` bleibt: In einer Zeile mit dem Titel stehen sie
     damit auf gleicher Mitte. */
  align-self: center;

  /* **Und beim Umbruch bleibt das Letzte rechts** (29.09.2026, auf Wunsch:
     *„soll Abo an der gleichen Stelle stehen wie bei Einkauf"*).

     Die Leiste darf umbrechen (Grundregel oben), und `justify-content`
     stand auf `normal` - die zweite Zeile begann also **links**. Der
     Einkauf hat genau ein Element und bricht nie; die Masken mit zwei
     (*Tag eintragen* + Abo, *Neue Frage* + Abo) brachen am Handy, und das
     Abo stand danach **61 bis 83 px vor dem rechten Rand** statt bündig.

     *Die Leiste selbst war dabei richtig plaziert* (`justify-self: end`,
     gemessen 0 px zum Rand) - **es war ihr Inhalt.** */
  justify-content: flex-end;
}

.unterzeile { grid-column: 1 / -1; }

.seitentitel {
  /* **Century Gothic 500** (29.09.2026, auf Wunsch) - nicht mehr Segoe UI
     Bold und schon gar nicht Young Serif. Die Serifenschrift bleibt den
     Karten; der Seitentitel steht gross und soll ruhig stehen.

     **Dieselben zwei Zeilen wie an `h1, h2`**, und das ist Absicht: Der
     Seitentitel ist ein `h1` und erbt sie ohnehin - er nennt sie noch
     einmal, weil er daneben seine **Groesse** setzt und *eine Regel, die
     die halbe Schrift setzt, laedt dazu ein, die andere Haelfte zu
     vergessen.* */
  font-family: var(--schrift-seitentitel);
  font-weight: 500;
  letter-spacing: -.01em;
  font-size: var(--seitentitel);
  line-height: 1;
  margin: 0;

  /* **Er bricht um, statt abgeschnitten zu werden.** In der Kopfzeile stand
     er zwischen Menue und Marke und musste mit Ellipse schrumpfen; im
     Inhalt ist der Platz da. Gemessen ohne diese Zeilen: *„Kinderzimm"* -
     **527 px Inhalt in 241 px**, ohne Ellipse, einfach abgeschnitten. */
  overflow: visible;
  text-overflow: clip;
  white-space: normal;
  overflow-wrap: break-word;
}

/* **Die Unterzeile darf umbrechen** (A4) - sie ist der Satz, der sagt, was
   gerade los ist („3 Dinge sind gerade unterwegs."). */
.unterzeile { flex: 1 1 100%; margin: 0; font-size: 14px; color: var(--text-leise); }

/* --- Der Inhaltsbereich (A4) ---------------------------------------------- */

/* **`container-type` steht am Hauptbereich, nicht am Inhalt**, und das ist
   kein Geschmack: Der Seitenkopf liegt **neben** dem scrollenden Inhalt
   (Regel 2 - nur der Inhalt scrollt), und seine Knöpfe rechnen mit `cqi`.
   Ein Container nur um den Inhalt ließe sie ohne Bezug - **und `cqi` fällt
   dann still auf den Höchstwert zurück**, ohne dass etwas kaputt aussieht.

   Der waagerechte Innenabstand steht deshalb ebenfalls hier: So fluchten
   Titel, Knöpfe und Inhalt, statt zweimal denselben Wert zu tragen. */
.hauptbereich {
  container-type: inline-size;
  padding: 24px clamp(16px, 4cqi, 40px) 0;
}
.inhalt {
  overflow-x: hidden;
  padding: 0 0 48px;
  /* **Der Balken hat keinen eigenen Grund** (12.10.2026, auf einen Kommentar
     im Canvas: *„Die Scrollbar soll im Hintergrund auch transparent sein und
     nicht weiß"*).

     Aufgefallen ist es, seit sieben Masken ihren weißen Grund abgelegt
     haben: Die Bahn des Balkens blieb weiß und stand als heller Streifen
     neben einer Seite, die keinen mehr hat.

     **`scrollbar-color` nimmt zwei Werte** – es gibt keine Schreibweise für
     *nur die Bahn*. Der Griff bekommt deshalb `--text-leise`, eine Marke,
     die es schon gibt: *eine neue Farbe für einen Scrollbalken wäre eine
     Zahl mehr in Abschnitt 1.* `--linie` wäre die leisere Wahl und auf
     `--grund` gemessen fast unsichtbar – **ein Bedienelement, das man sucht,
     ist keines.**

     **Hier und nicht je Maske**: Eine durchsichtige Bahn zeigt, was dahinter
     liegt – auf den weißen Masken also Weiß. *Sie ist überall richtig,
     anders als der Grund selbst, der seitengenau zurückgenommen wird.* */
  scrollbar-color: var(--text-leise) transparent;
}
/* **`width: 100%` gehört dazu, und zwar für beide Hälften.**

   In einem Flexkasten schlagen **Auto-Ränder in der Querachse das
   Strecken**: `.inhalt` trägt am Sticker Board `flex-direction: column`,
   und die Wand schrumpfte damit auf ihren Inhalt - gemessen **240 px statt
   343** am Handy, *und sie sah dabei zentriert aus, also nach Absicht.*

   Mit `width: 100%` bleibt kein freier Platz, die Auto-Ränder werden null,
   und im Block ändert sich nichts: `min(100%, 1120px)`, und ist die Spalte
   breiter, zentrieren die Auto-Ränder wie bisher.

   **`.seitenkopf` hatte diese Zeile schon** - er maß heute Vormittag
   **262 px breit und mittig in einem 358 px breiten Bereich**, *und das sah
   nach Absicht aus*. Die Abhilfe stand also im Blatt - **nur für die eine
   Hälfte des Selektors.** */
.seitenkopf, .inhalt > * {
  width: 100%;
  max-width: 1120px;
  margin-inline: auto;
}

/* --- Die Menuezeilen der Leiste (A3) ------------------------------------- */

/* *„Menüeinträge: 14px, padding 7px 12px, Radius 3px, Abstand 1px. Aktiver
   Eintrag: Hintergrund Tinte, Text weiß, bold."*

   **Das Infoboard behält seine Maße** - der Auftrag nimmt es aus, und dort
   wird aus zwei Metern getippt. */
.menueliste { gap: 1px; }
.menuetafel a,
.menuegruppe > summary,
.ansichtsliste button,
.abmelden button {
  font-size: var(--menuepunkt-schrift);
  border-radius: var(--menuepunkt-radius);
  padding: 7px 12px;
}
.menuetafel a:hover,
.menuegruppe > summary:hover,
.ansichtsliste button:hover,
.abmelden button:hover { background: var(--navi-hover); }

.menuetafel a.aktiv,
.ansichtsliste button.aktiv {
  background: var(--text);
  color: var(--auf-dunkel);
  font-weight: 700;
}

/* **Das Logo ist handgeschrieben** (Einkauf 2a, Abschnitt 1) - damit
   traegt Family Board ueberall dort die Handschrift, wo es sich selbst
   nennt. **Die Groesse kommt aus einem Merkmal**: 22 px in der Leiste,
   18 am Handy - und am Infoboard wieder `--schrift-gross`, denn der
   Auftrag nimmt es aus. */
.marke {
  font-family: var(--schrift-hand);
  font-weight: 700;
  font-size: var(--marke-schrift);
}

/* **Die Zahl am Punkt** (A3) - *„Kreis mit 20px Durchmesser, Blau, weiße
   Zahl 12px bold, rechtsbündig."*

   Sie trägt `aria-hidden`: Was sie sagt, steht bereits im Band darüber, und
   *eine Zahl ohne Wort ist für ein Vorleseprogramm eine Zahl ohne Wort.* */
.badge {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  border-radius: 999px;
  background: var(--akzent);
  color: var(--auf-dunkel);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
}
.menuetafel a.aktiv .badge { background: var(--auf-dunkel); color: var(--text); }

/* ===========================================================================
   17. Eingabemaske, Karten, Archiv - und die Ausleihkarte
       (Design 2a, A6/A7/A8 und Teil B - 27.09.2026)
   =========================================================================== */

/* --- A6: die Eingabemaske ------------------------------------------------ */

/* *„Weiß, gestrichelter Rand, Radius 4px. Felder ohne Rahmen, nur unten
   eine Linie."* - **eine Maske sieht aus wie ein Formular auf Papier**, und
   der gestrichelte Rand sagt: *hier wird etwas ausgefüllt, es steht noch
   nicht fest.*

   **Sie liegt hinter dem Haupt-Knopf und ist anfangs zu.** Der Griff ist
   ein `<summary>` im Knopfkleid - **beide Wörter stehen im Dokument**, und
   das Blatt zeigt eines davon (die Lehre aus FAM-109: *ein Vorleseprogramm
   liest verlässlich das Dokument, nicht jedes generierte `content`*). */
.neumaske > summary { list-style: none; width: fit-content; }
.neumaske > summary::-webkit-details-marker { display: none; }
.neumaske > summary .wenn-auf,
.ahnmaske > summary .wenn-auf,
.erledigte > summary .klappwort .wenn-auf { display: none; }
.neumaske[open] > summary .wenn-zu,
.ahnmaske[open] > summary .wenn-zu,
.erledigte[open] > summary .klappwort .wenn-zu { display: none; }
.neumaske[open] > summary .wenn-auf,
.ahnmaske[open] > summary .wenn-auf,
.erledigte[open] > summary .klappwort .wenn-auf { display: inline; }

/* **Der Haupt-Knopf** (A5): ein Wort, gelb unterstrichen, groß. Er trägt
   `--marker-haupt` - die einzige Stelle, an der diese Farbe steht. */
/* **Der Haupt-Knopf ist ein `<summary>`**, und ein `<summary>` ist weder
   `button` noch `.schaltflaeche`: Ohne `.haupt` in den Selektorlisten
   darueber bekaeme er nur die **Zutaten** (Marker-Farbe, Streifenmass) und
   nicht das **Rezept** - gemessen 14,2 px hoch, ohne Marker, *und auf einem
   Bildschirmfoto sah es aus wie ein Knopf, weil daneben echte standen.*

   `list-style: none` steht ausdruecklich da (die Lehre aus FAM-70): `all:
   unset` nimmt es nicht zuverlaessig mit, und ein Dreieck vor dem Wort
   waere hier ein zweites Zeichen. */
.haupt {
  --marker: var(--marker-haupt);
  list-style: none;
  cursor: pointer;
  /* **Der Boden ist 24 px, nicht 13** - gemessen: `3.6cqi` ergibt am Handy
     **14,2 px**, und A4 nennt dort ausdruecklich **24**. Der Deckel bleibt
     bei 32 (Desktop). *Ein Haupt-Knopf, der kleiner ist als der Text
     daneben, ist keiner.* */
  font-size: clamp(24px, 3.6cqi, 32px);
  font-weight: 800;
  letter-spacing: -.02em;
  background-position: 0 70%;
  background-size: 100% 55%;
}
.haupt:hover { background-size: 100% 100%; }
.haupt::-webkit-details-marker { display: none; }

.eingabemaske {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: var(--abstand);
  padding: 16px 18px;
  border: 1.5px dashed var(--maskenrand);
  border-radius: 4px;
  background: var(--flaeche);
}

/* Der Kopf der Maske: Label-Stil links, Zusatzangabe rechts, 2px Linie. */
.maskenkopf, .kartenkopf {
  display: flex;
  justify-content: space-between;
  gap: var(--abstand);
  padding-bottom: 6px;
  border-bottom: 2px solid var(--text);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .12em;
}

/* *„Felder im Raster `repeat(auto-fit, minmax(170px, 1fr))`, `align-items:
   end`"* - so stehen die Linien der Felder auf einer Höhe, auch wenn ein
   Label zweizeilig wird. */
.maskenfelder {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  align-items: end;
  gap: 14px;
}
/* **Die Gruppen unter dem Feldraster** (24.10.2026, auf Rückmeldung:
   *„es sieht alles völlig durcheinander aus"*).

   **Gemessen war es `align-items: end` am Raster darüber**: Es richtet
   seine Zellen an der Unterkante aus, damit die Linien der Felder auf
   einer Höhe stehen (A6) - und eine Datumsgruppe ist **viermal so hoch wie
   ein Feld**. Daneben klebte jedes Feld unten in seiner Zeile, und darüber
   stand Luft.

   **Hier richtet nichts aneinander aus**: Zwei Gruppen stehen
   nebeneinander, jede so hoch, wie sie ist. Dasselbe Raster wie oben, nur
   mit `start` - *eine Gruppe hat keine Feldlinie, an der man sie
   ausrichten könnte.* */
.maskengruppen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  align-items: start;
  gap: 14px;
}

/* **`.maskenfeld` und nicht `.feld`** - und das ist keine Namenslaune:
   `.feld` traegt seit FAM-108/109 die **Breitenregel jeder Maske**, und eine
   Wache verbietet, sie an eine einzelne Maske zu binden. *Der Feldkasten
   einer Eingabemaske ist ein anderer Baustein* - er stellt Beschriftung und
   Feld untereinander, statt eine Breite zu verteilen. */
.maskenfeld {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin: 0;
}
.maskenfelder label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
}
/* **`input` und nicht `.feld input`** - eine Klasse schlägt keinen
   Attributselektor (`input[type="text"]` ist (0,1,1)), und genau daran ist
   die Pflichtfeld-Markierung am 25.09.2026 gescheitert. */
.maskenfelder input,
.maskenfelder select,
.maskenfelder textarea {
  border: 0;
  border-bottom: 1.5px solid var(--text);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  font-size: 16px;
  padding: 8px 0;
  /* **Ein `<select>` wird so breit wie seine laengste Option**, und diese
     Regel hat das bis zum 02.10.2026 nicht begrenzt.

     Gemessen im Stammbaum (FAM-200): Die Auswahlliste *„mit wem?"* mass
     **419 px** in einem 375 px breiten Rahmen, die Richtung daneben
     **563 px** - beide standen ueber dem Rand und waren am Handy
     unbedienbar (Regel 2).

     **Aufgefallen ist es nicht an dieser Maske, sondern am ersten Modul mit
     langen Optionen**: *„Friederike Charlotte Schmidt-Hohenwaldeck"* sind 40
     Zeichen. Die acht Masken davor hatten kurze Namen in ihren Listen - *die
     Luecke war immer da, es fehlte nur der Fall.*

     **`max-width` und `min-width` zusammen**: Das eine deckelt die bevorzugte
     Breite, das andere erlaubt dem Feld zu schrumpfen (`min-width: auto` ist
     bei einem Flex-Element der Mindestinhalt, und *genau den fragt die
     Zeile*). Dieselbe Paarung wie in `.maske .feld > select`, wo sie seit
     FAM-109 steht - **hier hat sie gefehlt.** */
  min-width: 0;
  max-width: 100%;
}
/* **Beim Fokus bleibt die Unterlinie sichtbar** (A6) - sie wird nur
   kräftiger. *Eine Linie, die beim Hineintippen verschwindet, sieht aus
   wie ein Fehler.* */
/* **Der Umriss bleibt** - und das ist eine Abweichung vom Auftrag, die
   gemessen ist: Eine kraeftigere Unterlinie allein ist eine Fokusanzeige,
   aber eine schwache, und A9 verlangt *„Fokus ist immer sichtbar"*. Die
   Wache aus FAM-109 hat es sofort gemeldet - **sie verlangt eine eigene
   Kontur, wenn jemand `outline: none` setzt**, und hier ist die einfachere
   Antwort, den Umriss stehen zu lassen. */
.maskenfelder input:focus-visible,
.maskenfelder select:focus-visible,
.maskenfelder textarea:focus-visible {
  box-shadow: none;
  border-bottom-color: var(--akzent);
  border-bottom-width: 2.5px;
}
/* Und was fehlt, bleibt rot - die Zusage vom 25.09.2026. */
.maskenfelder input.fehlt,
.maskenfelder select.fehlt,
.maskenfelder textarea.fehlt {
  box-shadow: none;
  border-bottom-color: var(--fehler);
  border-bottom-width: 2.5px;
}

/* --- A7: das Kartenraster und die Grundkarte ------------------------------ */

/* *„`repeat(auto-fill, minmax(clamp(280px, 38cqi, 420px), 1fr))`"* - am
   Desktop sind Karten dadurch etwa 30 % breiter, am Handy mindestens 280. */
.leihliste, .kartenraster {
  display: grid;
  grid-template-columns:
      repeat(auto-fill, minmax(clamp(280px, 38cqi, 420px), 1fr));
  gap: 18px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.karte {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 2px 2px 4px 4px;
  background: var(--flaeche);
  box-shadow: var(--kartenschatten);
}

/* --- A8: das Archiv ------------------------------------------------------- */

.erledigte > summary {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--abstand);
  cursor: pointer;
  list-style: none;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--linie-abschnitt);
}
.erledigte > summary::-webkit-details-marker { display: none; }
.erledigte > summary .klappwort { font-size: 13px; color: var(--text-leise); }

/* --- Die Abschnitte ------------------------------------------------------- */

/* *„Abschnittstitel: Titel-Schrift 22px, Anzahl daneben 13px, 1px Linie
   darunter."* */
.leihgruppe > h2, .erledigte > summary > h2 {
  font-family: var(--schrift-seitentitel);
  font-weight: 500;
  font-size: 22px;
  /* **Nicht in Grossbuchstaben** - der Label-Stil gehoert den kleinen
     Beschriftungen (A2), ein Abschnittstitel traegt die Titelschrift. Die
     alte Regel daneben setzt `text-transform: uppercase`; sie gilt weiter
     fuer alles, was sie sonst trifft. */
  text-transform: none;
  letter-spacing: normal;
  margin: 0 0 6px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--linie-abschnitt);
}
.erledigte > summary > h2 { margin: 0; padding: 0; border: 0; }
.leihgruppe > h2 .anzahl, .erledigte > summary > h2 .anzahl {
  font-family: var(--schrift-hand);
  font-size: 13px;
  color: var(--text-leise);
}

/* *„Gibt es gar keinen Eintrag, steht dort ein kurzer Satz in der
   Titel-Schrift, 22px."* */
.leerer-abschnitt {
  font-family: var(--schrift-titel);
  font-weight: 400;
  font-size: 22px;
  color: var(--text-leise);
}

/* --- Teil B: die Ausleihkarte --------------------------------------------- */

.kartenzeilen {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 14px;
  margin: 0;
}
.kartenzeilen > dt,
.kartenzeilen > dd {
  margin: 0;
  padding: 7px 0 5px;
  border-bottom: 1px solid var(--linie-huelle);
}
.kartenzeilen > dt {
  align-self: end;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-leise);
}
.kartenzeilen > dd { font-size: 14px; }
.kartentitel {
  font-family: var(--schrift-titel);
  font-size: 20px;
  line-height: 1.15;
}

.kartenfuss {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 56px;
}

.kartenhinweis { margin: 0; font-size: 13px; color: var(--warnung-text); }

/* **Der Stempel** (B2) - *„Rand 2.5px und Text in der Stempelfarbe, Radius
   4px, `rotate(-7deg)`."*

   **Die Farbe kommt aus einer Variablen**, die die vier Fälle setzen -
   *eine Farbe je Fall im Regelblock wäre viermal dieselbe Regel* (Regel 10). */
.stempel {
  flex: none;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  padding: 4px 10px;
  border: 2.5px solid var(--stempelfarbe, var(--akzent));
  border-radius: 4px;
  rotate: -7deg;
  color: var(--stempelfarbe, var(--akzent));
  font-weight: 700;
  text-transform: uppercase;
  text-align: center;
}
.stempelzahl { font-size: 26px; line-height: 1; }
.stempelwort { font-size: 12px; }
.stempel.drueber { --stempelfarbe: var(--warnung); }
.stempel.ohnefrist { --stempelfarbe: var(--text-leise); }
.stempel.zurueckgekommen { --stempelfarbe: var(--erfolg); }
.stempel.zurueckgekommen .stempelzahl { font-size: 15px; }

/* **Der Stempel-Knopf** (B3) - *„Diesen Knopf gibt es nur im Modul
   Verliehen."* Er trägt deshalb keinen Marker und steht nicht in
   Abschnitt 15. */
.stempelknopf {
  align-self: flex-start;
  min-height: var(--knopf-flaeche);
  padding: 0 14px;
  border: 2.5px solid var(--akzent);
  border-radius: 4px;
  background-image: none;
  color: var(--akzent);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  rotate: -2deg;
}
.stempelknopf:hover { background-image: none; transform: rotate(-1deg); }

/* Die Änderungszeile der Karte bleibt ein Aufklapper (FAM-136). */
.ausleihkarte .leihe-aendern > summary { font-size: 13px; }

/* ===========================================================================
   18. Der Einkaufszettel (Einkauf 2a, §4 bis §9 - 27.09.2026)
   ===========================================================================
   *„Die Einkaufsliste ist ein Einkaufszettel, also ein linierter
   Spiralblock."*
   =========================================================================== */

/* --- Die Listen-Auswahl (§4) --------------------------------------------- */

/* **Ein Menü, keine Reiterleiste.** Bis heute stand hier `.listenleiste` -
   sie wächst mit jeder Liste in die Breite, das Menü nicht. */
.listenwahl { display: inline-block; position: relative; }

/* **Der Griff ist ein `<summary>`, also weder `button` noch
   `.schaltflaeche`** - Abschnitt 15 traegt ihn nicht, und die Untergrenze
   von 44 px gilt fuer ihn deshalb nur, wenn sie hier steht.

   **Gemessen am 29.09.2026: 21 px** - *die Zutaten ohne das Rezept*, zum
   dritten Mal nach `.haupt` (14,2 px) und *„+ Neu verleihen"*. Aufgefallen
   ist es am Projektmenue des Bretts, das denselben Baustein benutzt; **die
   Zeile hilft dem Einkauf genauso**, denn es ist dieselbe Regel. */
.listenwahl > summary {
  display: inline-flex;
  align-items: center;
  min-height: var(--knopf-flaeche);
  gap: 4px;
  cursor: pointer;
  list-style: none;
  font-weight: 700;
}
.listenwahl > summary::-webkit-details-marker { display: none; }

.listenmenue {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 25;
  min-width: 240px;
  padding: 6px;
  border-radius: 4px;
  background: var(--flaeche);
  box-shadow: 0 1px 1px oklch(30% 0.05 270 / .1),
              0 14px 30px -12px oklch(30% 0.05 270 / .45);
}
.listenmenue a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand);
  min-height: var(--knopf-flaeche);
  padding: 0 10px;
  border-radius: 3px;
  color: var(--text);
  font-size: 16px;
  text-decoration: none;
}
.listenmenue a:hover { background: var(--navi-hover); }
.listenmenue a.aktiv { font-weight: 700; }
.listenmenue a .anzahl { font-size: 13px; color: var(--text-leise); }
.listenmenue hr {
  margin: 6px 4px;
  border: 0;
  border-top: 1px solid var(--linie);
}
/* Die beiden Verwalten-Wege stehen leiser und flacher - sie sind kein
   Listenwechsel. */
.listenmenue hr ~ a { min-height: 40px; font-size: 14px; }

/* --- Die Anordnung (§9) --------------------------------------------------- */

/* **Zettel links, Wagen rechts - und am Handy untereinander.** Kein
   `@media`: `flex-wrap` entscheidet an der Breite, die wirklich da ist. */
.einkaufsraum {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  align-items: flex-start;
}
/* **Der freie Platz gehört dem Zettel** (28.09.2026, auf Rückmeldung:
   *„der Zettel wurde deswegen verkleinert … bitte den Platz ausnutzen"*).

   Vorher wuchsen **beide**, im Verhältnis 1,6 zu 1 – bei 984 px Inhalt waren
   das **578 px Zettel neben 378 px Wagen**. *Der Wagen ist eine Liste aus
   kurzen Zeilen; er wird nicht besser, wenn er breiter wird.* Der Zettel
   schon: Dort steht „7 Eistee (Pfirsich)" neben einem Kästchen und einem
   Aufklappzeichen.

   **Zwei Zeilen zusammen**, und die zweite ist die, die man vergisst:

   | | |
   |---|---|
   | `flex-grow: 0` am Wagen | Er nimmt keinen freien Platz mehr mit – er ist eine Liste aus kurzen Zeilen und wird nicht besser, wenn er breiter wird |
   | **Die Basis des Zettels ist seine Höchstbreite** | Sonst passt der Wagen rechnerisch daneben, *bevor* der Zettel seine Breite erreicht hat: **Umgebrochen wird nach der Basis, geschrumpft erst danach.** Mit 420 px Basis blieben bei 1280 px Fenster **676 statt 720** |

   Gemessen, und zwar gegen den Zustand **ohne** den Wagen (die Gegenprobe
   nach FAM-86):

   | Fenster | Inhalt | Zettel | ohne Wagen | Wagen |
   |---|---|---|---|---|
   | 375 | 343 | 343 | 343 | darunter |
   | 834 | 551 | 551 | 551 | darunter |
   | 1280 | 984 | **720** | 720 | darunter |
   | 1400 | 1104 | **720** | 720 | daneben |
   | 1920 | 1840 | **720** | 720 | daneben |

   **In allen fünf Fällen genau so groß wie ohne den Wagen** – und wo er
   daneben steht, entscheidet weiterhin `flex-wrap`, also die Breite, die
   wirklich da ist (§9).

   **`max-width` bleibt**, und es ist kein Rest: Eine Zeile über eine
   Armlänge liest niemand gern, und die Zahl gilt mit und ohne Wagen gleich.
   *Dass der Zettel ohne den Wagen nicht breiter würde, ist der Grund, warum
   „genau so groß" überhaupt erreichbar ist.* */
.zettel { flex: 1 1 720px; min-width: 0; max-width: 720px; }
.wagen  { flex: 0 1 280px; min-width: 0; }

/* --- Das Papier (§6) ------------------------------------------------------ */

.zettel { filter: var(--zettel-schatten); }

/* **Das Blatt ist oben abgerissen** (Aenderungen, §3) - *„Abgerissener
   oberer Rand statt Spiralrand."*

   **Zwei Dinge auf einem Streifen**, und sie tun Verschiedenes:

   | | |
   |---|---|
   | `clip-path` | die **ausgefranste Oberkante** - viele Punkte, x in Schritten von 0,6-2 %, y zwischen 2 und 11 px |
   | `mask-image` | die **aufgerissenen Loecher** - Halbkreise, aus der Kante ausgeschnitten |

   **Der Punktzug steht so im Auftrag** und ist nicht gerechnet: Er ist
   einmal ausgewuerfelt und dann **festgeschrieben** - dieselbe Ueberlegung
   wie bei der Drehung eines Zettels (FAM-169), nur andersherum: Dort war
   ein Zufall je Aufruf der Fehler, hier waere er es auch. *Ein Riss, der
   bei jedem Neuladen anders aussieht, ist keine Kante, sondern ein Flackern.*

   **`-webkit-` steht dabei** - der Auftrag verlangt es ausdruecklich, und
   ohne den Praefix fehlen die Loecher in aelteren WebKit-Browsern. *Die
   Kante bliebe trotzdem; es ist eine Verschlechterung, kein Verlust.*

   **Kein Radius mehr und keine gestrichelte Linie darunter** - eine
   Abrisskante braucht keinen zweiten Strich, der sagt, dass hier
   abgerissen wurde. */
.zettelspirale {
  height: 18px;
  background-color: var(--flaeche);
  clip-path: polygon(0% 100%,0.00% 6.4px,1.39% 3.0px,2.33% 6.0px,3.64% 2.7px,5.47% 7.3px,6.64% 4.6px,8.33% 7.9px,9.04% 6.5px,9.97% 8.0px,10.79% 7.0px,12.07% 3.6px,12.74% 10.5px,14.70% 3.1px,15.92% 10.6px,16.79% 5.8px,18.16% 8.0px,19.15% 3.5px,21.13% 6.0px,22.06% 6.9px,23.12% 3.4px,24.80% 5.2px,25.89% 2.7px,27.79% 4.1px,29.63% 6.2px,30.56% 7.4px,31.72% 8.8px,33.23% 3.2px,34.15% 6.1px,34.87% 2.4px,36.05% 8.1px,37.56% 10.6px,38.61% 5.9px,39.91% 5.9px,41.68% 6.1px,42.89% 4.4px,43.83% 8.7px,44.61% 4.4px,46.28% 9.7px,48.24% 10.3px,50.04% 8.7px,51.54% 7.0px,52.17% 4.9px,53.27% 3.9px,54.43% 8.2px,56.06% 4.4px,58.00% 2.1px,59.10% 7.3px,60.32% 2.8px,62.30% 6.1px,64.13% 7.3px,65.38% 3.1px,67.09% 10.1px,68.06% 2.2px,68.88% 8.4px,70.46% 7.9px,72.27% 4.1px,74.04% 3.5px,74.69% 2.7px,75.32% 6.8px,77.08% 9.6px,77.89% 6.4px,78.56% 4.5px,79.84% 2.6px,80.94% 4.1px,81.95% 9.1px,82.75% 3.5px,83.35% 7.6px,84.07% 5.6px,84.82% 6.3px,86.00% 2.1px,87.66% 6.1px,89.29% 5.0px,91.20% 8.5px,92.69% 4.4px,93.79% 2.7px,95.63% 2.7px,96.32% 3.7px,97.35% 2.3px,98.79% 6.0px,99.92% 6.9px,100% 5px,100% 100%);
  -webkit-mask-image: radial-gradient(circle at 14px 0, transparent 5.5px, #000 6.5px);
  mask-image: radial-gradient(circle at 14px 0, transparent 5.5px, #000 6.5px);
  -webkit-mask-size: 28px 100%;
  mask-size: 28px 100%;
  -webkit-mask-repeat: repeat-x;
  mask-repeat: repeat-x;
}

/* **Der rote Randstrich ist weg** (Aenderungen, §3) - *„entfaellt
   komplett."*

   **`position: relative; z-index: 0`** macht das Blatt zum Bezugsrahmen des
   Kaffeeflecks (§5): Ohne den eigenen Stapelkontext laege ein `z-index: -1`
   **hinter dem Grund der Seite** statt hinter dem Text des Blattes, und der
   Fleck waere unsichtbar. */
.zettelblatt {
  position: relative;
  z-index: 0;
  padding: 0 16px 22px 8px;
  border-radius: 0 0 4px 4px;
  background-color: var(--flaeche);
  container-type: inline-size;
  /* **Der Fleck endet am Papierrand** (28.09.2026, gemessen).

     Er ist **260 px** groß; ein Zettel mit vier Zeilen ist **232** hoch -
     gemessen ragte er **39 px unter das Blatt**, und ein Kaffeefleck
     *neben* dem Papier ist keiner.

     **`clip`, nicht `hidden`**: Es erzeugt keinen Scrollkontext, und genau
     das will Regel 2 hier nicht - der Zettel scrollt mit der Seite, nicht
     in sich. */
  overflow: clip;
}

.zettelkopf {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  margin: 0;
  padding: 10px 0 6px 60px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
}

.zettelliste { list-style: none; margin: 0; padding: 0; }

/* **Jede Zeile bringt ihre Linie mit** (§6) - *die Linien sind kein
   durchgehender Hintergrund.* Ein liniertes Papier läuft auseinander,
   sobald ein Artikel zwei Zeilen braucht oder das Ändern aufgeklappt ist;
   dann stünde der Text **zwischen** den Linien statt darauf.

   `margin-right: -16px` zieht die Linie bis an den Rand des Blattes. */
.zettelzeile, .zettelabteilung, .zettelleer {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: var(--knopf-flaeche);
  margin: 0 -16px 0 0;
  padding-right: 16px;
  border-bottom: 1px solid var(--zettel-linie);
}

/* Die Abteilung ist eine Zeile des Zettels, kein eigener Block. */
.zettelabteilung {
  padding-left: 60px;
  color: var(--akzent);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
}
.zettelleer { padding-left: 60px; color: var(--text-leise); }

/* **Das Kästchen ist leicht schief** (§6) - von Hand gemalt sieht kein
   Kasten wie der nächste aus. Die Trefferfläche ist der ganze Knopf. */
.kaestchen {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  padding: 0;
  background-image: none;
}
.kaestchenform {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  /* **Grau statt Tinte** (Aenderungen, §4) - die schiefe Form und die
     Groesse bleiben. */
  border: 2px solid var(--zettel-kasten);
  border-radius: 6px 3px 7px 4px;
  rotate: -4deg;
  background: var(--flaeche);
  color: var(--akzent);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
}
.kaestchen.gesetzt .kaestchenform {
  border-color: var(--akzent);
  background: var(--akzent);
  color: var(--auf-dunkel);
}

/* Der Text ist der zweite Knopf - er öffnet das Ändern. */
.artikel-aendern { flex: 1 1 auto; min-width: 0; }
.artikel-aendern > summary {
  display: flex;
  align-items: center;
  min-height: var(--knopf-flaeche);
  cursor: pointer;
  list-style: none;
}
.artikel-aendern > summary::-webkit-details-marker { display: none; }
.zettelwort { font-size: 19px; line-height: 1.3; }
.zettelwort .menge, .wagenwort .menge { color: var(--text-leise); }

/* Die Änderungsmaske steht unter der Zeile, eingerückt wie der Text. */
.artikelmaske { margin: 10px 0 10px 60px; padding: 12px 14px; }
.maskenaktionen {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: nowrap;
}

/* --- Der Stiftstrich beim Abhaken (§7) ------------------------------------ */

/* *„Ein blauer Stiftstrich läuft über den Text ... Nach 1,3 Sekunden wandert
   der Artikel nach ‚Im Wagen‘, und **erst dann** geht der POST."*

   ## Was hier sichtbar ist und was nicht

   **Ohne Skript passiert nichts davon** (Regel 7): Das Kästchen ist ein
   Sende-Knopf, der Artikel wandert sofort. Die Klasse `wird-eingepackt`
   setzt `einkauf-abhaken.js`, und ohne es gibt es sie nie.

   ## Und die Zusage von FAM-79 bleibt

   *„Nichts wird vorab umgestellt"* - was hier 1,3 Sekunden lang zu sehen
   ist, ist **nicht der Endzustand**, sondern *„wird gleich eingepackt"*,
   **mit einem Weg zurück**. Nach dem Absenden kommt die Wahrheit weiterhin
   vom Server. *Der Unterschied ist der Rückweg* - ohne ihn wäre es genau
   die Beschleunigung, die FAM-79 verboten hat. */

/* Der Haken erscheint sofort - er ist die erste Hälfte der Antwort. */
.wird-eingepackt .kaestchenform {
  border-color: var(--akzent);
  color: var(--akzent);
}
.wird-eingepackt .kaestchenform::after { content: "\2713"; }

/* **Der Strich ist ein `::after` am Wort**, kein `text-decoration`: Er soll
   **laufen**, und eine Textdekoration lässt sich nicht animieren. */
.zettelwort { position: relative; }
.zettelwort::after {
  content: "";
  position: absolute;
  left: -6px;
  top: 52%;
  width: 0;
  /* **Halb so dick** (Aenderungen, §7) - 1,5 px statt 3, Radius 1 statt 2.
     *Alles andere bleibt gleich*: Farbe, Lage, Drehung, Animation. */
  height: 1.5px;
  border-radius: 1px;
  rotate: -1.5deg;
  background: var(--akzent);
  transition: width .35s ease-out;
}
.wird-eingepackt .zettelwort::after { width: calc(100% + 12px); }

/* Danach wird der Text blasser - **mit Verzögerung**, damit der Strich
   zuerst zu sehen ist und nicht beides zugleich passiert. */
.wird-eingepackt .zettelwort {
  opacity: .55;
  transition: opacity .3s ease-out .4s;
}

/* **Wer keine Bewegung will, bekommt keine** (§7) - *„Der Artikel wandert
   trotzdem nach 1,3 s."* Die Wartezeit ist kein Schmuck, sondern der
   Rückweg; sie bleibt. */
@media (prefers-reduced-motion: reduce) {
  .zettelwort::after { transition: none; }
  .wird-eingepackt .zettelwort { transition: none; }
}

/* --- Der Grund hinter dem Zettel (28.09.2026, auf Wunsch) ---------------- */

/* *„Bitte den Hintergrund hinter den Einkaufszettel nicht weiß machen
   sondern transparent halten."*

   **Weiß war nicht der Zettel, sondern `.inhalt`** – seit der Frischen Ebene
   steht dort `background: var(--flaeche)` (*„Der Inhalt steht auf Weiss,
   nicht auf dem getoenten Grund"*, Abschnitt 11). Gemessen ist diese Fläche
   am Desktop **984 px breit**, der Zettel darin **578**: eine weiße Platte,
   die rechts neben dem Papier weiterläuft.

   **Seitengenau, nicht überall.** Der Wunsch nennt den Einkaufszettel; für
   jede andere Maske gilt die Entscheidung der Frischen Ebene weiter. Die
   Bauart gibt es seit dem 24.09.2026 schon einmal – `.inhalt:has(.stickerboard)`.

   **`transparent` und keine zweite Farbe**: So scheint der Grund des Rumpfes
   durch (`--grund`), und es steht keine Zahl daneben, die mit ihm
   auseinanderlaufen kann (Regel 10). */
.inhalt:has(.einkaufsraum) { background: transparent; }

/* **Und in der Aufgabenmaske genauso** (28.09.2026, auf Wunsch: *„Auch hier
   soll der Hintergrund transparent sein."*) - **die dritte Maske mit
   derselben Bauart**, nach dem Sticker Board und dem Einkaufszettel.

   `transparent` und keine zweite Farbe: So scheint `--grund` durch, und es
   steht keine Zahl daneben, die mit ihm auseinanderlaufen kann (Regel 10). */
.inhalt:has(.arbeitsflaeche) { background: transparent; }

/* **Und hinter dem Chat genauso** (11.10.2026, auf einen Kommentar im Canvas:
   *„Der Hintergrund hinter dem Chat soll transparent sein."*) - **die vierte
   Maske mit derselben Bauart**, nach dem Sticker Board, dem Einkaufszettel
   und der Aufgabenmaske.

   **Die Beitraege bleiben, wie sie waren**: weiss mit Rand, der eigene
   getoent. *Erst dadurch werden sie zu Karten auf einem Grund* - auf der
   weissen Platte lagen sie auf ihrer eigenen Farbe.

   **Gerechnet, bevor sie weggenommen wurde** (Gegenprobe Schwarz auf Weiss
   21,0): Die leise Zeile (*„Heute, 09:22"*) traegt auf `--grund` mit **5,92**
   statt 6,55 auf Weiss - beide ueber den 4,5 von WCAG AA. Auf dem eigenen
   Beitrag sind es 5,79. *Woertlich die Messung, die FAM-232 fuer dieselbe
   Farbe gemacht hat.*

   **Das Eingabefeld bleibt weiss** (`.chat-neu`): Es ist die Flaeche, in die
   man schreibt - *ein durchsichtiges Eingabefeld sieht aus wie keines.* */
/* **Das Eingabefeld steht ausserhalb des Scrollbereichs** (11.10.2026, auf
   einen Kommentar im Canvas: *„Die Texteingabe muss aus dem Scrollbereich
   raus, die Posts duerfen nicht unter dem Eingabefeld durchscrollen"*).

   **Es ist umgezogen, nicht umgebaut**: Das Formular liegt jetzt im Block
   `unterbau` von `base.html`, also NEBEN `.inhalt` statt darin. Damit gibt
   es nichts mehr, was darunter durchscrollen koennte - *der Scrollbereich
   endet ueber dem Feld.*

   **Der erste Anlauf ging anders und ist gemessen gescheitert**: `.inhalt`
   zur Flex-Spalte, `.chatbereich` zum Scrollkasten - **der Verlauf fiel auf
   0 px zusammen.** Der Bereich traegt seit FAM-124 `display: contents`, hat
   also keinen Kasten, an dem `flex` oder `min-height` greifen. Ihn zu einem
   zu machen haette drei weitere Stellen gekostet: den Anker `#unten`,
   `scrollkasten()` in `chat.js` (es beginnt bei `bereich.parentElement` und
   haette den Bereich selbst uebersprungen) und das Wiederherstellen der
   Leseposition beim Austauschen, das danach an einem abgeloesten Knoten
   gearbeitet haette. *Der Umzug war das Billigere.*

   **Die Flaeche bleibt weiss** (`.chat-neu`): *ein durchsichtiges
   Eingabefeld sieht aus wie keines.* */
/* **Das Bild in der Sprechblase** (11.10.2026, auf Wunsch: *„Ich moechte
   beim Chat auch Bilder posten koennen."*).

   **Gedeckelt wird die HOEHE, nicht die Breite** (`--chatbild-hoehe`, Regel
   10): Die Blase ist ohnehin so breit wie der Chat erlaubt, und ein hohes
   Hochformat waere ohne Deckel eine Bildschirmseite hoch. `object-fit:
   contain` laesst das Verhaeltnis stehen - *eine Fliese verzieht man, ein
   Foto nicht* (dieselbe Entscheidung wie beim Wetterbild, 10.10.2026).

   **`display: block` am Bild**: Ein `<img>` ist inline und sitzt auf der
   Grundlinie - darunter blieben sonst ein paar Pixel Luft, die aussehen wie
   ein schiefer Rand.

   **Die Rundung ist die des Kastens** (`--radius-klein`, seit heute 2 px) -
   *ein Bild mit einer anderen Ecke als seine Blase sieht aus wie
   hineingeklebt.* */
.chat-bild {
  display: block;
  margin-top: 4px;
}

.chat-bild img {
  display: block;
  width: auto;
  max-width: 100%;
  max-height: var(--chatbild-hoehe);
  object-fit: contain;
  border-radius: var(--radius-klein);
}

/* **Das gewaehlte Bild, bevor es hinausgeht** (11.10.2026, auf Wunsch).

   **Eine eigene Zeile ueber dem Feld** (`flex-basis: 100%`), und das Bild
   darin ist klein: Es beantwortet *„habe ich das richtige erwischt?"*, und
   dafuer reicht die Hoehe eines Knopfes mal zwei. Eine grosse Vorschau
   schoebe das Eingabefeld aus dem Bild, und darunter steht am Handy die
   Tastatur.

   **`:has()` statt einer zweiten Klasse am Formular**: Der Kasten ist
   `hidden`, solange nichts gewaehlt ist - *das Blatt fragt denselben
   Zustand, den das Markup ohnehin traegt*, und niemand muss daran denken,
   eine Klasse mitzupflegen. */
.chat-vorschau {
  flex: 0 0 100%;
  display: flex;
  align-items: center;
  gap: var(--abstand);
  margin-bottom: 4px;
}

.chat-vorschau img {
  display: block;
  width: auto;
  max-width: 100%;
  max-height: calc(var(--knopf-flaeche) * 2);
  object-fit: contain;
  border-radius: var(--radius-klein);
}

/* **Das Wegnehmen ist ein Zeichenknopf** - Abschnitt 15 gibt ihm deshalb
   keinen Textmarker, und sein Wort steht im `aria-label`. */
.chat-vorschau-weg {
  flex: 0 0 auto;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
}

/* **Das Zeichen, mit dem man ein Bild aussucht** (11.10.2026).

   Es ist ein `<label>` und kein Knopf - **Abschnitt 15 greift dort nicht**
   (ein Textmarker haengt an `button` und `.schaltflaeche`), und das ist
   richtig: *Ein Zeichenknopf traegt ohnehin keinen Marker.*

   **Die Trefferflaeche kommt aus `--knopf-flaeche`**, der einen Stelle:
   44 px sind der Boden, nicht die Decke. Ein Label ohne eigene Hoehe waere
   so hoch wie sein Zeichen - gemessen 15 px, also ein Drittel. */
/* **Der Fokus liegt am Feld und muss am Zeichen zu sehen sein** (A9:
   *„Fokus ist immer sichtbar“*).

   Das Dateifeld steht in `.nurvorlesen` - gemessen 0 x 10 px mit
   `clip-path: inset(50%)`. Es ist damit **mit der Tastatur erreichbar und
   unsichtbar**, und ohne diese Regel waere es ein Bedienelement, das den
   Fokus bekommt, ohne dass man es sieht. `:focus-within` hebt ihn auf das
   Label, das man sieht. */
.chat-bildwahl:focus-within {
  outline: 2px solid var(--akzent);
  outline-offset: 2px;
}

.chat-bildwahl {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  border-radius: var(--radius-klein);
  color: var(--text-leise);
  cursor: pointer;
}

.inhalt:has(.chatverlauf) { background: transparent; }

/* **Der Eingabebereich liegt auf dem Grund, und darüber steht ein Strich**
   (13.10.2026, auf einen Kommentar im Canvas: *„Hintergrund transparent und
   über dem Eingabebereich ein kleiner waagerechter Strich zur optischen
   Abtrennung"*).

   **Warum die Regel HIER steht und nicht oben bei `.chat-neu`:** Die
   Frische Ebene setzt `.inhalt, .werkzeugleiste, .chat-neu { background:
   var(--flaeche) }` – **gleiche Spezifität, aber später im Blatt**. Wer
   `transparent` nur oben einträgt, wird davon überschrieben. *Dieselbe
   Falle wie bei der Werkzeugleiste am 29.09.2026* (*„Abo ist wieder weiß
   hinterlegt"*), und dort steht die Begründung ausführlich.

   **Der alte Strich war da und unsichtbar.** `border-top: 1px solid
   var(--linie)` – gerechnet steht `--linie` auf `--grund` bei **1,22 : 1**,
   und WCAG verlangt für eine Abgrenzung **3,0**. *Ein Strich, den niemand
   sieht, trennt nichts* – und genau deshalb kam die Bitte, einen zu bauen,
   den es längst gab. Die Gegenprobe der Rechnung ist Schwarz auf Weiß mit
   21,00.

   **Er trägt jetzt `--text-leise`** (gerechnet **5,92 : 1**) – eine Marke,
   die es schon gibt: *Eine neue Farbe für einen Trennstrich wäre eine Zahl
   mehr in Abschnitt 1.*

   **Kurz und mittig statt über die ganze Breite**: *„ein kleiner
   waagerechter Strich"*. Seine Länge ist **vier Abstandseinheiten** – damit
   wächst er mit der Ansicht, statt als feste Zahl am Infoboard zu einem
   Strichlein zu schrumpfen. */
.chat-neu {
  position: relative;
  background: transparent;
  border-top: 0;
}

.chat-neu::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50% 0;
  width: calc(var(--abstand) * 4);
  height: 2px;
  border-radius: 2px;
  background: var(--text-leise);
}

/* **Auch die Startseite steht auf dem getoenten Grund** (11.10.2026, auf
   einen Kommentar im Canvas: *,,Hintergrund transparent"*).

   **Die sechste Maske mit dieser Bauart**, nach dem Sticker Board
   (24.09.), dem Einkaufszettel (27.09.), der Aufgabenmaske (28.09.), dem
   Chat (11.10.) und den Familienregeln. Fuer jede andere Maske gilt die
   Entscheidung der Frischen Ebene weiter.

   **Erkannt am Inhalt, nicht an einer Klasse am Rumpf**: Die Kachelliste
   steht nur hier - *eine Klasse am Koerper waere eine zweite Stelle, an der
   sich eine Maske benennt* (die Lehre vom Einkauf, 28.09.2026).

   **,,transparent" und keine zweite Farbe**, wie bei den fuenf davor: So
   scheint --grund durch, und es steht keine Zahl daneben, die mit ihm
   auseinanderlaufen kann. **Die Kacheln bleiben weiss** - erst dadurch sind
   sie ueberhaupt Karten auf einem Grund. */
.inhalt:has(.kachelliste) { background: transparent; }

/* **Der Stundenplan liegt auf dem Grund** (12.10.2026, auf einen Kommentar
   im Canvas: *„Hintergrund bitte transparent"*, am Tablet-Abzug).

   **Die siebte Maske mit dieser Bauart**, nach dem Sticker Board (24.09.),
   dem Einkaufszettel (27.09.), der Aufgabenmaske (28.09.), dem Chat, den
   Familienregeln und der Startseite (11.10.). Für jede andere Maske gilt
   die Entscheidung der Frischen Ebene weiter – *seitengenau zurückgenommen,
   nicht allgemein.*

   **`.planwoche`, nicht `.plangitter`** – der Selektor ist aus dem Blatt
   gelesen und nicht aus dem Markup geraten: `.plangitter` ist das
   **Formular**, das Gitter heißt `.planwoche` (`11-fallstricke.md`, und
   genau diese Verwechslung steht dort schon).

   **Sie trägt beide Bauarten**: ab Desktop das Gitter, darunter die Liste
   je Tag – `display: var(--plan-anzeige)` an **einem** Markup. Die Regel
   gilt damit in allen vier Ansichten und in beiden Modi, und das ist
   richtig: *Es ist eine Maske, nicht zwei.*

   **Die Zellen bleiben, wie sie sind** – sie tragen `--flaeche` und einen
   Innenrahmen. Erst dadurch sind sie überhaupt Fächer auf einem Grund; auf
   der weißen Platte lagen sie auf ihrer eigenen Farbe. */
.inhalt:has(.planwoche) { background: transparent; }

/* --- Im Wagen (§8) -------------------------------------------------------- */

.wagen > h2 { margin: 0; padding-bottom: 6px; border-bottom: 2px solid var(--text); }
.wagen > h2 .anzahl {
  font-family: var(--schrift-hand);
  font-size: 13px;
  font-weight: 400;
  color: var(--text-leise);
}
.wagenliste { list-style: none; margin: 0; padding: 0; }
.wagenzeile {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: var(--knopf-flaeche);
  border-bottom: 1px solid var(--linie-huelle);
}
/* **Durchgestrichen und gedämpft** (§8) - *es ist erledigt, aber es steht
   noch da.* Der Strich ist blau wie der Stift. */
.wagenwort {
  color: var(--text-leise);
  text-decoration: line-through 2px var(--akzent);
}


/* ===========================================================================
   19. Der Einkauf nach den Änderungen zu 2a (28.09.2026)

   *„Ergänzung zu `Anweisung Claude - Einkauf 2a.md`. Diese Datei beschreibt
   nur, was sich geändert hat."*

   **Nur dieses Modul.** Der Auftrag sagt es ausdrücklich: *„Die Schriftregel
   aus 2a, Abschnitt 1 (h1 in Segoe UI Bold für alle Module) bleibt für die
   anderen Module bestehen."*

   ## Woran das Blatt den Einkauf erkennt

   **An seinem Inhalt, nicht an einer Klasse am `<body>`.** Der Auftrag
   schreibt `.einkauf h1`; eine solche Klasse gibt es hier nicht, und sie
   wäre eine **zweite Stelle**, an der eine Maske sich benennt (die erste
   ist ihr Blueprint). Stattdessen fragt das Blatt nach dem Einkaufsraum:

       .hauptbereich:has(.einkaufsraum)

   Das ist **derselbe Mechanismus**, der seit dem 28.09.2026 den Grund hinter
   dem Zettel durchsichtig macht (`.inhalt:has(.einkaufsraum)`), nur eine
   Ebene höher - der Seitenkopf ist ein Geschwister des Inhalts, kein Kind.

   > **Der Preis steht dabei:** Ein Browser ohne `:has()` zeigt den Einkauf
   > mit dem Kopf aller anderen Masken. *Eine Verschlechterung des
   > Aussehens, kein Verlust eines Weges* (Regel 7).
   =========================================================================== */

/* --- Die Schriften (§1 und §6) -------------------------------------------- */

/* **Hier stand die Schrift des Einkaufs, und sie ist weggefallen**
   (29.09.2026, auf Wunsch: *„Bitte genau die Größe und Art der Überschrift
   in Einkauf in alle anderen Module übernehmen."*).

   Century Gothic 500 steht jetzt an `h1, h2` und an `.seitentitel` – für
   **alle** Module. *Eine Regel, die nur noch dasselbe sagt wie die
   Grundregel, ist keine Ausnahme mehr, sondern eine zweite Stelle.*

   **Was bleibt, ist der Umbruch darunter** – der ist wirklich ein Merkmal
   dieses Titels und nicht der Schrift. */

/* Der Seitentitel bricht im Einkauf **nicht** um: Er ist zwei Wörter lang
   („Einkauf Baumarkt"), und in der mittleren Spalte des Rasters würde ein
   Umbruch die beiden Nachbarn nach oben ziehen. */
.hauptbereich:has(.einkaufsraum) .seitentitel {
  letter-spacing: -.01em;
  white-space: nowrap;
  text-align: center;
}

/* --- Die Frage steht halb so gross ---------------------------------------
   (29.09.2026, auf einen Kommentar im Design-Canvas: *„Bitte die
   Ueberschrift der Frage nur bei 10-Fragen in der halben Groesse."*)

   **Der Titel dieser Maske ist die Frage selbst** - *„Wann machen wir das
   Grillfest?"* statt eines Modulnamens wie *„Umfragen"* oder *„Einkauf"*.
   Ein Satz in Titelgroesse fuellt am Handy drei Zeilen, und der Seitenkopf
   schiebt die Antworten unter den Bildschirmrand.

   **`calc(… / 2)` und keine zweite Zahlenreihe** (Regel 0): So folgt die
   Frage allen vier Ansichten - *vier hingeschriebene Haelften waeren vier
   Stellen, die beim naechsten Entwurf einzeln nachzuziehen waeren.*

   **Und nur hier**: `.hauptbereich:has(.frageraum)`, wie beim Einkauf. Der
   Modulname *„Umfragen"* auf der Liste daneben bleibt gross. */
.hauptbereich:has(.frageraum) .seitentitel {
  font-size: calc(var(--seitentitel) / 2);
}

/* **Der Listenname steht leichter und leiser** (§1) - er ist die Auskunft
   *welche* Liste, nicht der Titel der Maske.

   **Die Farbe steht schon da**: Der Auftrag nennt `oklch(48% 0.02 270)`,
   und das ist Zeichen fuer Zeichen `--text-leise` aus Abschnitt 1. *Eine
   zweite Farbe mit demselben Wert waere die zweite Stelle* (Regel 10). */
.titelliste {
  font-weight: 400;
  color: var(--text-leise);
}

/* **Hat die Liste eine Farbe, trägt der Name sie** (29.09.2026, auf Wunsch:
   *„Wenn ich der Liste Baumarkt die Farbe orange zugeordnet habe, dann soll
   in der Überschrift in Schwarz Einkauf stehen und in Orange Baumarkt
   dahinter."*).

   **Dieselbe Rezeptur wie bei den Menschen** (`.gefaerbt`, Abschnitt 1) -
   aus Python kommt eine **Zahl**, die Farbe entsteht im Blatt. *Zwei
   Rezepturen für denselben Farbkreis liefen auseinander.*

   **Ohne Farbe bleibt es bei der Zeile darüber**: gedämpft, und das ist der
   Normalfall - eine Liste bekommt eine Farbe, sie hat nicht schon eine. */
.titelliste.gefaerbt { color: var(--personenfarbe); }

/* **Der Aufklapper steht in der Zeile, seine Tafel darunter** (29.09.2026).

   **Gemessen beim Zeigen**: In der Flexzeile bekam das Farbraster die
   Restbreite - achtzehn Knöpfe standen als **eine schmale Säule**
   untereinander, und die Zeile war zerrissen. *Dieselbe Familie wie der
   Zettel am Sticker Board: ein Raster in einer zu schmalen Spalte.*

   `flex-basis: 100%` gibt ihm die **ganze Zeilenbreite**; die Zeile bricht
   ohnehin um (`--aufgabe-umbruch`). Zugeklappt kostet das nichts - dann ist
   der Aufklapper nur sein Griff. */
.listenfarbe { flex: 1 1 100%; }

/* **Und die Zeile bricht um, solange die Wahl offen ist.**

   `--aufgabe-umbruch` ist ab Tablet `nowrap` - ohne diese Regel blieb das
   Raster **in** der Zeile, und Name, Stand und Knöpfe standen mittig
   daneben (`align-items: center` an einer plötzlich 400 px hohen Zeile).

   **Nur bei `[open]`**: Zugeklappt ändert sich nichts, und das ist der
   Normalfall. */
.listenzeile:has(.listenfarbe[open]) {
  flex-wrap: wrap;
  align-items: flex-start;
}

/* Der Fleck am Aufklapper in der Listenverwaltung - ohne Farbe bleibt er
   leer und zeigt nur seinen Rand. */
.listenfarbe > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: var(--knopf-flaeche);
  cursor: pointer;
  list-style: none;
}
.listenfarbe > summary::-webkit-details-marker { display: none; }
.listenfarbe .farbfleck { border: 1px solid var(--linie); }

/* --- Der Seitenkopf als Raster (§2) --------------------------------------- */

/* **Drei Spalten, damit der Titel in der Mitte der Seite steht** - nicht in
   der Mitte dessen, was links und rechts übrig bleibt.

   | Spalte | Inhalt | Ausrichtung |
   |---|---|---|
   | links | „Liste [Einkauf ▾] · 6 Dinge fehlen noch" | rechtsbündig |
   | Mitte | der Titel | zentriert |
   | rechts | „Abo" und „+ Was fehlt?" | linksbündig |

   **Am Handy eine Spalte**, und die Reihenfolge ist eine andere: erst der
   Titel, dann die Listenzeile, dann die Knöpfe.

   ## Warum `order` und nicht `grid-column`

   **Eine Variable statt zwei.** Bei drei Spalten und `grid-auto-flow: row`
   füllt der Browser die erste Zeile in der Reihenfolge der `order`-Werte;
   am Handy stapelt dieselbe Reihenfolge untereinander. Es genügt deshalb,
   **den Titel** zu verschieben - die Unterzeile trägt fest `1`, die
   Knöpfe fest `3`:

   | | Titel | Ergebnis |
   |---|---|---|
   | Handy | **0** | Titel · Zeile · Knöpfe |
   | ab Tablet | **2** | Zeile · Titel · Knöpfe |

   *Mit `grid-column` bräuchte es je Ansicht drei Angaben, und am Handy
   müssten alle drei wieder zurückgenommen werden.* */
/* **Der Titel steht oben, darunter eine Zeile** (28.09.2026, auf Wunsch):

   *„Abo soll immer rechts in der Reihe unter dem Titel stehen … Das
   Aufklappmenü soll unter der Überschrift stehen, genauso wie die Schrift
   ‚2 Dinge fehlen noch'."*

   | | |
   |---|---|
   | Zeile 1 | der Titel, über beide Spalten und zentriert |
   | Zeile 2 links | „Supermarkt ▾ · 2 Dinge fehlen noch" |
   | Zeile 2 rechts | das Abo-Aufklappmenü |

   ## Das nimmt das Drei-Spalten-Raster von §2 zurück

   Dort standen Listenzeile, Titel und Knöpfe **nebeneinander**, und der
   Titel musste zwischen zwei gleich breiten Außenspalten mittig gehalten
   werden. **Das kostete vier Merkmalsvariablen**, weil es am Handy und am
   Tablet nicht trägt - gemessen bekamen die Außenspalten dort 73 px für
   Knöpfe, die 199 brauchen.

   **Jetzt ist es überall dieselbe Anordnung**, und die vier Variablen sind
   weggefallen: *Was in allen vier Ansichten gleich aussieht, braucht keinen
   Wertesatz je Ansicht* (Regel 0 verlangt Variablen für Unterschiede, nicht
   für Gleiches).

   **Der Titel ist damit in der Mitte der Seite** - ohne Rechnung, weil er
   die ganze Breite hat. */
/* --- Das Abo als Aufklappmenü (28.09.2026, auf Wunsch) ------------------- */

/* **Seit dem 29.09.2026 gilt dieser Block für JEDE Maske mit Abo** – Chat,
   Umfragen, Verliehen, Geburtstage und den Einkauf. *„Ich will dass alle
   Masken nach dem gleichen Prinzip (im HTML und CSS) funktionieren und
   Einkauf soll die Vorlage sein."*

   Er steht weiterhin in diesem Abschnitt, und das ist Absicht: **Eine
   Regel, die den Abschnitt wechselt, wechselt ihre Lage im Blatt** – und
   die Lage entscheidet bei gleichem Gewicht (die Falle der Frischen
   Ebene). *Hier ist sie gemessen; anderswo wäre sie eine Vermutung.*

   *„Abo … soll ein Aufklappmenü sein. Wenn es nicht aufgeklappt ist, soll
   es eine kleine Schrift geben wie Supermarkt."*

   **Der Griff sieht aus wie das Listen-Menü daneben** - dieselbe Größe,
   dieselbe Schrift, derselbe ▾. *Zwei Aufklapper in einer Zeile, die
   verschieden aussehen, sehen aus wie zwei verschiedene Dinge.*

   **Und er trägt keinen Textmarker** (*„Abo bitte im Hintergrund
   transparent"*): Ein `<summary>` ist weder `button` noch
   `.schaltflaeche`, Abschnitt 15 greift dort also gar nicht. Die Zeile
   steht hier trotzdem - *eine Zusage, die sich auf eine Abwesenheit
   verlässt, hält nur, solange niemand eine Klasse ergänzt.* */
.abomenue { position: relative; }

/* **Der Griff ist eine Menuezeile** (11.10.2026, auf einen Kommentar im
   Canvas: *„Abo bitte auf allen Masken aus dem Inhaltsbereich in das Menue
   bringen"*).

   Hier stand bis dahin die Form fuer den **Seitenkopf**: `inline-flex`,
   fett, 4 px Abstand, die Trefferflaeche von 44 px. *Die Form ist nicht
   geblieben, weil der Ort nicht geblieben ist* - die Werte kommen jetzt
   Zeile fuer Zeile von den Menuepunkten daneben (`.menuetafel a`), damit
   die Zeile sich nicht als etwas anderes ausgibt.

   **Das Gewicht ist 600, und das ist gemessen statt ueberlegt**: Hier
   stand zuerst nichts, also 400 - mit der Begruendung, ein fetter
   Menuepunkt sehe aus wie der gewaehlte. **Die Menuepunkte daneben stehen
   gemessen auf 600**, und damit war meine Begruendung widerlegt, bevor sie
   trug: Nicht der fette Punkt faellt auf, sondern der duenne. */
.abomenue > summary {
  display: flex;
  align-items: center;
  gap: 0.6em;
  min-height: var(--schaltflaeche);
  width: 100%;
  padding: calc(var(--abstand) / 4) calc(var(--abstand) / 2);
  border-radius: var(--radius);
  cursor: pointer;
  list-style: none;
  background: none;
  color: var(--text);
  font-size: var(--menuepunkt-schrift);
  font-weight: 600;
}
.abomenue > summary::-webkit-details-marker { display: none; }

/* **Das Zeichen sitzt am rechten Rand der Zeile**, nicht am Wort - sonst
   haengt es mitten in der Zeile, und bei zwei Spalten am Handy steht es an
   einer anderen Stelle als bei fuenf am Infoboard. */
.abopfeil { margin-left: auto; }

/* **Die Zeile ist eine Rasterzelle wie jede andere** - keine Sonderregel.

   Hier stand am 11.10.2026 ein `grid-column: 1 / -1`, damit sie immer die
   ganze Breite nimmt: *Das Abo ist der zwanzigste Eintrag, und bei zwei
   Spalten landet es rechts, bei fuenf woanders.* **Zurueckgenommen am
   selben Tag**, und der Grund ist eine Messung, die ich nicht machen
   konnte: Der Abzug beantwortet Menuebreiten nicht - er liefert fuer alle
   vier Wertesaetze dieselben Zahlen.

   **Was eine Sonderregel braucht, braucht auch eine Messung.** Ohne sie
   verhaelt sich die Zeile wie ihre neunzehn Nachbarn - *was fuer die gilt,
   gilt dann auch fuer sie*, und das ist in einem Menue die bessere Zusage
   als eine Breite, die ich nur vermuten kann.

   **Wer sie eines Tages ueber die ganze Breite will, misst vorher** - im
   Browser an der laufenden Anwendung, nicht im Abzug. */

/* **Im Menue klappt es auf, es schwebt nicht** (11.10.2026).

   Hier stand ein absolut gesetztes Fenster, und das war die Form des
   **Seitenkopfs**: Dort durfte das Aufklappen den Titel nicht zur Seite
   schieben. **Im Menue ist das Verschieben richtig** - ein Menuepunkt, der
   aufgeht, schiebt die Punkte darunter, so wie *Einstellungen* daneben.

   **Gemessen war das Fenster unbrauchbar**: Es ragte **99,6 px rechts** aus
   dem Menue und **455 px unten** heraus - die Abo-Zeile ist die letzte des
   Menues, ein Fenster darunter faellt also aus der Tafel. *Ein Fenster, das
   man nicht sieht, ist kein Fenster.*

   **Kein Schatten mehr**: Er machte aus der Gruppe ein schwebendes Blatt.
   Im Fluss ist sie ein eingerueckter Kasten, und der Rand reicht. */
.abomenue > .abogruppe {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 6px;
  margin-top: 6px;
  padding: 10px 12px;
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
}

/* --- Der Kaffeefleck (§5) ------------------------------------------------- */

/* **Reine Deko**, und sie sagt es an drei Stellen: `aria-hidden` im Markup,
   `pointer-events: none` hier, und **hinter** dem Text (`z-index: -1`).

   *Der Bezugsrahmen dafür ist `.zettelblatt`* - es trägt seit §3
   `position: relative; z-index: 0`. Ohne den eigenen Stapelkontext läge der
   Fleck hinter dem **Grund der Seite** und wäre unsichtbar. */
.kaffeefleck {
  position: absolute;
  right: 14px;
  /* **Oben rechts** (28.09.2026, auf Wunsch: *„der Kaffeekreis sollte oben
     rechts auf dem Einkaufszettel sein"*).

     Der Auftrag zeichnet ihn unten (`bottom: 10px`); **ein Wunsch von heute
     überstimmt eine Zeichnung von heute Vormittag** – und oben steht er auf
     dem Papier statt am Rand, weil ein kurzer Zettel unten früh endet. */
  top: 10px;
  z-index: -1;
  pointer-events: none;
}

/* **Eine leere Zeile ist Papier, keine Auskunft** (28.09.2026) - sie bringt
   ihre Linie mit und sonst nichts. `min-height` steht schon an
   `.zettelzeile`; hier fehlt nur der Inhalt. */
.zettelluft { margin-bottom: 0; }

/* --- Die Eingabemaske steht in einer Zeile (28.09.2026, auf Wunsch) ------- */

/* *„Wenn die Eingabemaske Neuer Eintrag aufgeht, sollten die Textfelder und
   Auf den Zettel nebeneinander stehen."*

   **Der Knopf stand darunter** - zwei Felder, dann eine eigene Zeile für
   eine einzige Schaltfläche. *Ein Eintrag ist ein Handgriff: tippen,
   tippen, drücken.*

   **`flex-wrap`, keine Ansichtsvariable** (Regel 0 ist damit nicht
   berührt): Ob der Knopf danebenpasst, entscheidet der **Platz** und nicht
   das Gerät - dieselbe Bauart wie `.einkaufsraum` (§9), wo der Wagen neben
   den Zettel rutscht, sobald die Breite reicht. *Wer am Desktop „Handy"
   wählt, bekommt hier dasselbe wie auf einem Handy, weil es an beiden
   Stellen die Breite ist, die entscheidet.*

   **Der Kopf bleibt über allem** (`100%`) - er sagt, worum es geht, und ist
   keine Spalte. */
.artikel-neu {
  display: flex;
  /* **`row` muss dastehen** - `.eingabemaske` ist eine **Spalte**, und eine
     geerbte Richtung sieht man einer Regel nicht an.

     **Gemessen ohne diese Zeile**: Das Feldraster war **203 px breit und
     354 px hoch** - `flex: 1 1 354px` teilt in einer Spalte die **Höhe**.
     *Dieselbe Falle wie bei der Marke in der Seitenleiste, dem Feld „An
     wen?" und dem Namensfeld des Einkaufs* - **zum fünften Mal an zwei
     Tagen**, und diesmal in der Maske, die ich gerade umgebaut habe. */
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 14px;
  /* **Aus der alten Regel von FAM-50 uebernommen**, die hier aufgegangen
     ist - sie setzte den Abstand nach unten, und den braucht die Maske
     weiterhin. */
  margin-bottom: var(--abstand);
}
.artikel-neu > .maskenkopf { flex: 1 1 100%; }

/* **Die Basis ist die Breite, die zwei Felder brauchen** - `.maskenfelder`
   stellt sie mit `minmax(170px, 1fr)` und 14 px Abstand nebeneinander, also
   354. *Keine erfundene Zahl* (Regel 5): Sie steht schon da, nur als Summe.

   Passt sie nicht, schrumpfen die Felder (`min-width: 0`) und der Knopf
   bricht um - genau der Fall am Handy. */
.artikel-neu > .maskenfelder { flex: 1 1 354px; min-width: 0; }
.artikel-neu > .maskenaktionen { flex: 0 0 auto; }


/* ===========================================================================
   20. Das Gesetzblatt (FAM-184, Epic FAM-182)

   Die Familienregeln stehen untereinander wie Paragraphen: eine Nummer, eine
   Überschrift daneben, darunter der Text.

   ## Die Nummer steht neben der Überschrift, nicht darüber

   **Gemessen, nicht entschieden**: Über dem Titel kostet sie in allen vier
   Ansichten eine ganze Zeile je Regel - bei zwölf Regeln am Handy sind das
   zwölf Zeilen, die nichts sagen. Daneben steht sie in derselben Zeile und
   ist trotzdem das Erste, was man liest.

   ## Der Text behält seine Zeilenumbrüche

   `white-space: pre-line` - **im Blatt, nicht als `<br>` aus der Eingabe.**
   Aus einem Regeltext wird so nie HTML (FAM-57), und wer die Darstellung
   ändert, ändert sie an einer Stelle.

   ## Keine neue Merkmalsvariable

   Alles kommt aus den vorhandenen Sätzen - `--abstand`, `--radius-karte`,
   `--kartenschatten`, `--schrift-titel`. *Eine Variable, die in allen vier
   Ansichten denselben Wert trüge, wäre eine Zahl mit einem Namen* (Regel 10).
   =========================================================================== */

/* --- Die Sammlung --------------------------------------------------------- */

/* **Eine Liste, kein Gitter.** Ein Gesetzblatt liest man von oben nach unten;
   nebeneinander stehende Paragraphen hätten keine Reihenfolge mehr - und die
   ist der Kern des Wunsches. */
.gesetzblatt {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* --- Ein Paragraph -------------------------------------------------------- */

/* **Dieselbe Karte wie überall** (A7): Fläche, Radius, Kartenschatten. Das
   Papier kommt mit FAM-187 - *eine vorgebaute Gestaltung ist eine ungeprüfte*
   (FAM-73). */
.paragraf {
  padding: var(--abstand);
  border-radius: var(--radius-karte);
  background: var(--flaeche);
  box-shadow: var(--kartenschatten);
}

.paragrafkopf {
  display: flex;
  align-items: baseline;
  gap: calc(var(--abstand) * 0.6);
  margin: 0 0 calc(var(--abstand) * 0.5);
}

/* **Die Nummer trägt die Titelschrift** - Young Serif, wie jeder Titel auf
   Papier (Design 2a A2). `tabular-nums` hält die Spalte gerade, sobald aus
   `§ 9` ein `§ 10` wird. */
.paragrafnummer {
  flex: 0 0 auto;
  font-family: var(--schrift-titel);
  font-weight: 400;
  color: var(--akzent);
  font-variant-numeric: tabular-nums;
}

/* **`min-width: 0`, damit eine lange Überschrift umbricht statt zu schieben.**
   Ohne sie meldet ein Flex-Kind seinen Mindestinhalt an, und die Karte wird
   breiter als die Spalte (die Falle steht im Index). */
.paragraftitel {
  min-width: 0;
  overflow-wrap: break-word;
}

.paragraftext {
  margin: 0;
  white-space: pre-line;
  overflow-wrap: break-word;
}

/* --- Für wen sie gilt (FAM-186) ------------------------------------------- */

/* **Bei „für alle" steht hier nichts** – die Vorlage zeichnet den Absatz
   dann gar nicht erst. *Der Normalfall braucht keine Marke; eine, die an
   jeder zweiten Regel steht, sieht niemand mehr.* */
.paragrafgeltung {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.3);
  margin: calc(var(--abstand) * 0.5) 0 0;
}

/* **Der Name, nicht nur die Farbe** (FAM-65): Wer rot-grün-blind ist, liest
   *Mia*; die Farbe ist die Zugabe.

   **Ohne `--person-ton` bleibt es grau** – das ist der Fall „zwei und mehr",
   in dem jede Farbe erfunden wäre (Regel 5, FAM-162). Der Fallback steht
   hier und nicht in der Vorlage: *Eine Vorlage, die sich eine Farbe
   ausdenkt, ist die zweite Stelle.* */
.paragrafschild {
  padding: 2px 10px;
  border-radius: 999px;
  font-size: 0.85em;
  background: oklch(52% 0.13 var(--person-ton, 0) /
                    var(--schild-saettigung, 0));
  color: var(--text);
  box-shadow: inset 0 0 0 1px var(--linie);
}

/* **Trägt sie einen Ton, trägt sie auch Farbe** – ein einziger Schalter
   statt einer zweiten Regel mit demselben Selektor. */
.paragrafschild[style*="--person-ton"] {
  --schild-saettigung: 1;
  color: var(--aufkleber-blocker-text);
  box-shadow: 0 1px 1px oklch(from var(--schatten) l c h / 0.3);
}

/* --- Die beiden Griffe (FAM-185) ------------------------------------------ */

/* **Sie stehen ganz rechts**, abgesetzt von *Ändern* und *Wegnehmen*:
   `margin-inline-start: auto` in einer Flex-Zeile.

   **Und sie stehen am Ende der Zeile, nicht am Anfang** – *der häufige
   Vorgang ist das Ändern, der seltene das Schieben*, und was man selten
   braucht, steht nicht zuerst (FAM-70). */
.paragrafgriffe {
  display: flex;
  gap: calc(var(--abstand) * 0.25);
  margin-inline-start: auto;
  order: 1;
}

/* Ein Formular je Richtung – **kein gemeinsames mit zwei Knöpfen**: Dann
   entschiede der zuletzt geklickte Knopf, und ein Formular, das je nach
   Knopf etwas anderes tut, ist eines, bei dem man beim Lesen raten muss. */
.paragrafgriff { margin: 0; }

/* **44 px sind der Boden, auch in der Breite** (FAM-62, FAM-84).
   `.schaltflaeche` setzt nur `min-height`; bei einem einzelnen Zeichen misst
   der Knopf **16 px breit** - gemessen am Handy, und das ist ein Drittel
   dessen, was eine Fingerkuppe braucht. *Eine Höhe allein ist keine
   Trefferfläche.* */
.paragrafgriff > .schaltflaeche {
  min-width: var(--knopf-flaeche);
  justify-content: center;
}

/* --- Ändern und wegnehmen ------------------------------------------------- */

/* **Ein Kind sieht diesen Kasten gar nicht** - die Vorlage fragt vorher
   `darf()`. *Was die Maske verbirgt, ist Bequemlichkeit*; der Server prüft
   es noch einmal (Regel 7). */
.paragrafwerkzeug {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: calc(var(--abstand) * 0.5);
  margin-top: var(--abstand);
}

/* **Geändert wird an Ort und Stelle** (FAM-70) - ein `<details>`, und der
   Browser klappt es von sich aus auf (Regel 7).

   `flex: 1 1 100%`: Ist es offen, gehört ihm die ganze Breite - sonst stünde
   die Maske neben dem Löschknopf und wäre in jeder Ansicht zu schmal. */
/* **Die ganze Breite bekommt es erst, wenn es offen ist.**

   Zugeklappt ist es ein Wort - `flex: 1 1 100%` schob dann *Wegnehmen* und
   die Griffe in eine zweite Zeile, gemessen am Handy. *Ein Aufklapper, der
   zugeklappt eine Zeile für sich beansprucht, kostet sie an jeder Regel.* */
.paragrafaendern {
  flex: 0 0 auto;
  min-width: 0;
}
.paragrafaendern[open] { flex: 1 1 100%; }

/* **`min-height` und nicht `height`** - die Bedienfläche ist der Boden, nicht
   die Decke (28.09.2026). Ein `<summary>` ist weder `button` noch
   `.schaltflaeche`; ohne diese Zeile misst es 14 px (der Fund von Einkauf 2a). */
.paragrafaendern > summary {
  display: inline-flex;
  align-items: center;
  min-height: var(--knopf-flaeche);
  cursor: pointer;
  list-style: none;
  color: var(--akzent);
}
.paragrafaendern > summary::-webkit-details-marker { display: none; }

.paragrafaendern > .maske {
  margin-top: calc(var(--abstand) * 0.5);
  max-width: var(--maske-breite);
}

/* **Ein Formular, kein Verweis** - *ein Link, der etwas ändert, ließe sich
   von einem fremden Bild aus aufrufen* (FAM-53). */
.paragrafweg {
  margin: 0;
  flex: 0 0 auto;
}


/* ===========================================================================
   21. Das alte Papier (FAM-187, Epic FAM-182)

   *„Diese Regeln sollen dann präsentiert werden auf einem DIN-A4-Papier, das
   so leicht by the way schattiert hat. Einfach, dass es aussieht wie ein
   alter Gesetzestext."*

   ## Die Breite ist ein A4-Bogen, die Höhe nicht

   **794 px sind 210 mm bei 96 dpi** – die echte Breite eines A4-Blattes, und
   deshalb keine erfundene Zahl (Regel 5). **94 px sind 25 mm**, der Rand
   eines gesetzten Textes.

   **Die Höhe wächst mit dem Text.** Ein A4-Blatt ist 297 mm hoch, die Regeln
   einer Familie sind es nicht – und *ein Papier, das Text abschneidet, ist
   keine Darstellung, sondern ein Verlust.* Echte Seiten (Blatt 1, Blatt 2)
   wären etwas anderes und bräuchten eine Entscheidung, was mit einem
   Paragraphen an der Blattkante geschieht; die trifft diese Karte nicht.

   ## Am Handy ist das Blatt die Seite

   **375 px minus zwei A4-Ränder sind kein Blatt mehr.** Breite und Rand sind
   deshalb Merkmalsvariablen (Regel 0, Regel 10): `100%` und 18 px am Handy,
   `794px` und 94 px am Desktop.

   ## Der Untergrund ist der Grundton

   `.inhalt` wird **seitengenau** durchsichtig – über `:has(.gesetzblatt)`,
   wie am Einkaufszettel und am Sticker Board (28.09.2026). *Auf Weiß läge
   ein weißes Blatt nicht, es wäre die Seite.*
   =========================================================================== */

/* --- Der Untergrund ------------------------------------------------------- */

/* **Woran das Blatt die Familienregeln erkennt: an ihrem Inhalt**, nicht an
   einer Klasse am `<body>` – *sie wäre eine zweite Stelle, an der eine Maske
   sich benennt* (Einkauf, 28.09.2026).

   > **Der Preis steht dabei:** Ein Browser ohne `:has()` zeigt die Regeln auf
   > weißem Grund. *Eine Verschlechterung des Aussehens, kein Verlust eines
   > Weges* (Regel 7). */
.inhalt:has(.gesetzblatt) {
  background: transparent;
  box-shadow: none;
  /* **Die Reißleine gegen ein sehr langes Blatt** (17.10.2026).

     Ein geneigtes Blatt wird seitlich breiter, **und zwar mit seiner
     Höhe**: `b·cos α + h·sin α`. Gerechnet für alle vier Ansichten – bis
     zu 3000 px Blatthöhe bleibt jede innerhalb ihres Randes, *und am
     Tablet erst, seit die Neigung dort 0,6° statt 1,2° ist* (bei 1,2°
     ragte es ab 2000 px hinaus).

     **Eine Familie mit sechzig Regeln kommt trotzdem darüber hinaus.**
     Dann wird die Papierecke beschnitten statt die Seite seitwärts
     scrollbar zu machen – *Regel 2 gilt, und ein paar Pixel Blattkante
     sind der kleinere Verlust.*

     > **`clip` steht hier und `hidden` kommt heraus**, und das ist kein
     > Versehen: Neben einem scrollenden `overflow-y` rechnet der Browser
     > `clip` selbst zu `hidden`. **Gemessen, nicht vermutet** – ein
     > Testelement mit `overflow-x: clip; overflow-y: auto` gibt dieselbe
     > Antwort, also liegt es nicht an dieser Regel. *Hier stand, `clip`
     > verhindere einen zweiten Scrollbereich; das tut es an dieser Stelle
     > nicht, weil `.inhalt` ohnehin der Scrollbereich ist* (siehe
     > `11-fallstricke.md`: gescrollt wird in `.inhalt`).
     >
     > Es bleibt stehen, weil es die **Absicht** benennt: beschneiden, nicht
     > scrollen. Gemessen greift es auch – `scrollWidth > clientWidth` ist
     > wahr, und eine Leiste erscheint trotzdem nicht. */
  overflow-x: clip;
}

/* --- Das Blatt ------------------------------------------------------------

   **Ein abgerissenes Blatt aus einem Notizblock** (17.10.2026, auf Wunsch
   mit Bild): *„Bitte für das Modul regeln, den Hintergrund wie auf dem Bild
   machen. Damit wird die Schrift natürlich auch wenig schräg und wichtig
   ist auch der kleine Klebestreifen."*

   **Hier lag bis heute ein Pergament** (08.10.2026, ebenfalls auf Wunsch
   mit Bild) – braun, gekörnt, fleckig, mit einer Rolle oben und unten. Es
   ist vollständig weg, samt der elf Verlaufslagen und der beiden Wülste.
   *Eine Entscheidung, die der Nutzer trifft, hält, bis er sie umdreht* –
   und er hat sie zweimal getroffen: erst zum Pergament, jetzt zum
   Notizblock.

   **Die Liste IST das Blatt**, unverändert – kein zusätzlicher Kasten
   darum: *Ein `<div class="blatt">` um ein `<ol>` wäre ein Element, das
   nichts tut außer eine Fläche zu tragen.*

   ## Vier Dinge machen das Bild aus

   | | |
   |---|---|
   | **Die Linien** | ein `repeating-linear-gradient` im Takt der Textzeile – *sie wachsen mit der Schrift jeder Ansicht, ein Bild täte das nicht* (Regel 4, wörtlich die Filzwand und der Spiralrand) |
   | **Die Abrisskante** | halbrunde Kerben am oberen Rand, **übermalt statt ausgeschnitten** – siehe unten |
   | **Die Neigung** | `--blatt-neigung` (Regel 10): Das Blatt liegt schief, und die Schrift darauf mit ihm. *Genau darum hat der Wunsch sie erwähnt* |
   | **Der Klebestreifen** | `::after`, quer über die obere linke Ecke, und er **ragt hinaus** |

   ## Warum die Kerben übermalt und nicht maskiert sind

   Ein `mask-image` wäre die saubere Lösung und hat einen Haken, der erst
   am Bildschirm auffällt: **Maskiert wird nach dem Filtern, aber vor nichts
   anderem** – ein `box-shadow` bleibt rechteckig und stünde als gerade
   Kante über den Kerben, ein `filter: drop-shadow` würde von derselben
   Maske gleich mit weggeschnitten.

   Deshalb liegt oben ein Streifen in der **Farbe des Untergrunds**
   (`--grund`), aus dem die Kerben als Kreise herausgezeichnet sind. Das
   geht hier, und zwar nachprüfbar: `.inhalt:has(.gesetzblatt)` ist
   durchsichtig (siehe oben), darunter liegt der Seitengrund, und der ist
   **eine flache Farbe**. *Ein Trick, der eine Bedingung hat, gehört mit
   seiner Bedingung aufgeschrieben.*

   **Der Schatten ist dafür nach unten gezogen** (`0 14px 26px -10px`):
   Mit genug Versatz und negativer Streuung reicht er oben nicht über die
   Blattkante hinaus – *sonst sähe man ihn zwischen den Kerben stehen.*

   ## Keine neue Farbe

   Regel 10, jede genau einmal. Das Papier ist `--flaeche`, die Linien sind
   `--linie`, der Streifen oben ist `--grund`, der Schatten `--schatten`.
   **Der Klebestreifen leitet sich aus `--marker-bestaetigen` ab** – dem
   einzigen blassen Grün des Blattes, und der Farbton 150 ist genau das
   Salbei der Vorlage. *Die Kopplung steht hier, damit sie auffällt, wenn
   jemand den Marker umfärbt:* Dann wird auch der Streifen anders, und das
   ist kein Fehler, sondern der Preis dafür, keine zwölfte Farbe anzulegen.
   =========================================================================== */

.gesetzblatt {
  /* **Der Takt des Papiers** - eine oertliche Rechnung, kein Merkmal aus
     Regel 10: Sie leitet sich aus `--schrift` ab und waere in vier
     Wertesaetzen viermal dieselbe Multiplikation. *Was sich rechnen laesst,
     wird nicht gepflegt* (wie die Laufzeit des Geraetecookies aus
     `FRIST_TAGE`, FAM-196).

     **1,9 ist weit fuer Fliesstext und richtig fuer liniertes Papier**: Auf
     einem Block stehen die Zeilen auseinander, sonst passt keine Handschrift
     dazwischen. */
  --zeilenmass: calc(var(--schrift) * 1.45);
  --blatt-schub: calc(var(--blatt-kante) * 1.8);
  /* **Die Schrift sitzt auf den Linien**, weil beide dieselbe Zahl lesen. */
  line-height: var(--zeilenmass);

  width: var(--blatt-breite);
  max-width: calc(100% - var(--blatt-schub) * 2);
  margin-inline: auto;
  /* **Platz fuer den Klebestreifen** (17.10.2026, auf Rueckmeldung: *„Bei
     den Regeln ist der Klebestreifen oben abgeschnitten"*).

     Er haengt ueber der Blattkante und ist **gedreht**, und genau das ist
     die Rechnung, die hier fehlte: Ein Kasten von 8,4 x 1,5 Kanten, um 24
     Grad gekippt, ragt oben um

         |b/2 * sin a| + |h/2 * cos a| - h/2 = 1,643 Kanten

     hoeher als ungedreht; dazu die 0,2 Kanten, die er ohnehin ueber dem
     Blatt sitzt. **Macht 1,85 Kanten** - gemessen 25,8 px am Handy, 36,9
     am Tablet und Desktop, 59,0 am Infoboard.

     **Der Ueberstand PLUS ein gewoehnlicher Abstand** - und die zweite
     Haelfte ist gemessen, nicht geschmueckt: Mit glatten 2 Kanten blieben
     **0,9 px** zwischen Streifen und Oberkante der Scrollbahn, und *ein
     Streifen, der die Kante beruehrt, sieht abgeschnitten aus, auch wenn
     er es nicht ist.* Mit `+ var(--abstand)` sind es rund zehn.

     *Dieselbe Falle wie am 29.09.2026 beim Blatt selbst*: Ein Ueberstehen
     ist nur dort ein Abschneiden, wo `overflow` nicht `visible` ist - und
     der Inhaltsbereich scrollt (Regel 2). */
  margin-top: calc(var(--blatt-kante) * 1.85 + var(--abstand));
  padding: var(--blatt-rand);
  /* **Oben ist die Abrisskante, unten nicht** – ein Blatt wird oben aus dem
     Block gerissen. Der Text braucht ihre Höhe zusätzlich, sonst beginnt er
     am Handy zwischen den Kerben: 18 px Rand gegen 14 px Kante.

     *Zwei Angaben zum Innenabstand und nicht eine gerechnete*: Die Zeile
     darüber ist die Zusage, die
     `test_das_blatt_liest_die_variablen_statt_eigener_zahlen` liest –
     **Breite und Rand kommen aus den Variablen**. Diese hier legt nur die
     Kante oben drauf. */
  padding-top: calc(var(--blatt-rand) + var(--blatt-kante));

  /* **Das Blatt liegt schief** – und die Schrift darauf mit ihm, weil sie
     darin steht und nicht daneben. */
  rotate: var(--blatt-neigung);

  /* **Die Linien laufen im Takt der Textzeile** (`--zeilenmass`, gleich
     darunter am Paragraphen): Was der Text an Höhe braucht, ist der
     Abstand der Linien – *zwei Zahlen dafür liefen auseinander, und dann
     schwebte die Schrift über dem Papier.*

     **Die erste Linie liegt unter der ersten Zeile**, nicht über ihr:
     Deshalb beginnt der Verlauf transparent und endet mit dem Strich.

     Darüber zwei sehr schwache Lagen, die der Fläche das Glatte nehmen –
     ein Hauch Schattierung oben links und eine am unteren Rand, beide aus
     `--schatten`. *Ohne sie ist es eine weiße Fläche und kein Papier.* */
  background:
    radial-gradient(ellipse 60% 24% at 22% 4%,
                    oklch(from var(--schatten) l c h / 0.022),
                    transparent 70%),
    radial-gradient(ellipse 80% 16% at 50% 100%,
                    oklch(from var(--schatten) l c h / 0.03),
                    transparent 72%),
    repeating-linear-gradient(to bottom,
                    transparent 0 calc(var(--zeilenmass) - 1px),
                    var(--linie) calc(var(--zeilenmass) - 1px) var(--zeilenmass)),
    var(--flaeche);
  /* **Die Linien fangen unter der Abrisskante an**, nicht am Blattrand –
     sonst liefe die erste quer durch die Kerben. */
  background-position: 0 0, 0 0,
    0 calc(var(--blatt-rand) + var(--blatt-kante)), 0 0;

  /* **Nach unten gezogen** – die Begründung steht im Kopf. */
  box-shadow: 0 1px 2px oklch(from var(--schatten) l c h / 0.10),
              0 14px 26px -10px oklch(from var(--schatten) l c h / 0.22);
  /* Kein Radius: **Papier hat Ecken.** */
  border-radius: 0;
  gap: 0;
  /* **Der Bezugsrahmen für die Kante und den Klebestreifen.** */
  position: relative;
  /* **Kein `overflow: hidden` mehr**: Der Klebestreifen ragt über die Ecke
     hinaus, und genau so liegt er auf der Vorlage. *Das Pergament brauchte
     es, weil seine Rollen seitlich überstanden; die Kerben tun das nicht.* */
}

/* --- Die Abrisskante ------------------------------------------------------ */

/* **Halbrunde Kerben, wie aus einem Spiralblock gerissen.**

   Ein Streifen in der Farbe des Untergrunds, aus dem die Kerben
   herausgezeichnet sind – die Bedingung dafür steht im Kopf dieses
   Abschnitts.

   `background-size` setzt den Takt: **eine Kerbe je 2,4 Kantenhöhen**, also
   wachsen Größe und Abstand gemeinsam mit der Ansicht. *Eine feste Zahl
   hier hätte am Infoboard dreimal so viele Kerben wie am Handy.* */
.gesetzblatt::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--blatt-kante);
  /* **Zwei Lagen je Kerbe, und die untere ist die eigentliche Arbeit.**

     Gemessen am Abzug: Der Untergrund ist `rgb(244,244,238)`, das Papier
     `rgb(255,255,255)` - **vier Prozent Unterschied**, und beim ersten
     Anlauf war die Abrisskante damit fast unsichtbar. *Auf der Vorlage
     liest man die Kerben nicht an ihrer Farbe, sondern an der
     **Schnittkante**: Papier wirft an jeder Rundung einen kleinen
     Schatten.*

     Deshalb liegt unter dem Loch ein etwas größerer, weicher Saum aus
     `--schatten`; das Loch darüber deckt seine Mitte wieder ab. Was
     bleibt, ist ein dünner Ring - die Kante. */
  background:
    radial-gradient(circle calc(var(--blatt-kante) * 0.8) at 50% 0,
                    var(--grund) 99%, transparent 100%),
    radial-gradient(circle calc(var(--blatt-kante) * 0.98) at 50% 0,
                    oklch(from var(--schatten) l c h / 0.22) 82%,
                    transparent 100%);
  background-size: calc(var(--blatt-kante) * 2) 100%;
  background-repeat: repeat-x;
  /* **Über dem Papier, unter dem Klebestreifen.** */
  z-index: 1;
  pointer-events: none;
}

/* --- Der Klebestreifen ---------------------------------------------------- */

/* **Quer über die obere linke Ecke, und er ragt hinaus** – wörtlich die
   Vorlage. *Ein Klebestreifen, der genau auf dem Blatt endet, klebt nichts
   fest.*

   **Durchscheinend, mit feinen Streifen**: Washi-Band ist Papier auf
   Papier – man sieht, was darunter liegt, und man sieht die Faser. Beides
   kostet eine Zeile: `opacity` und ein zweiter, sehr schwacher
   `repeating-linear-gradient` quer zur Laufrichtung.

   **Die Enden sind nicht ganz gerade**: Ein winziger `clip-path` nimmt
   oben und unten je eine Ecke weg – *abgerissenes Band ist nie rechteckig.*

   **Die Maße hängen an `--blatt-kante`** und nicht an eigenen Zahlen: Der
   Streifen wächst mit dem Blatt, über alle vier Ansichten. */
.gesetzblatt::after {
  content: "";
  position: absolute;
  top: calc(var(--blatt-kante) * -0.2);
  left: calc(var(--blatt-kante) * -1.7);
  width: calc(var(--blatt-kante) * 8.4);
  height: calc(var(--blatt-kante) * 1.5);
  rotate: -24deg;
  background:
    repeating-linear-gradient(to right,
                    oklch(from var(--marker-bestaetigen) 0.99 c h / 0.22) 0 1px,
                    transparent 1px 4px),
    linear-gradient(to bottom,
                    oklch(from var(--marker-bestaetigen) 0.93 0.05 h / 0.88),
                    oklch(from var(--marker-bestaetigen) 0.85 0.075 h / 0.94) 55%,
                    oklch(from var(--marker-bestaetigen) 0.89 0.06 h / 0.9));
  clip-path: polygon(
    4% 3%, 22% 0%, 46% 2%, 71% 0%, 93% 2%,
    96% 12%, 100% 26%, 96% 40%, 100% 54%, 96% 68%, 100% 82%, 96% 93%,
    90% 99%, 64% 100%, 39% 97%, 16% 100%, 5% 97%,
    0% 84%, 4% 70%, 0% 56%, 4% 42%, 0% 28%, 4% 14%
  );
  box-shadow: 0 1px 2px oklch(from var(--schatten) l c h / 0.12);
  z-index: 2;
  pointer-events: none;
}

/* --- Ein Paragraph auf dem Papier ----------------------------------------- */

/* **Keine Karte mehr** – Fläche, Radius und Schatten fallen weg: Sie stehen
   jetzt am Blatt, und *zwei Papiere übereinander sind eines zu viel.*

   Getrennt wird durch **Abstand**, wie in einem gesetzten Text. */
.gesetzblatt > .paragraf {
  padding: 0;
  background: none;
  box-shadow: none;
  border-radius: 0;
}
.gesetzblatt > .paragraf + .paragraf {
  margin-top: var(--zeilenmass);
}

/* **Jeder senkrechte Abstand im Blatt ist ein Vielfaches der Zeile**
   (17.10.2026, auf einen Kommentar im Canvas: *„Der Text muss zwischen den
   Linien, quasi als ob man ihn auf den Linien geschrieben hat"*).

   **Das ist die eigentliche Arbeit an dieser Zusage**, nicht der
   Zeilenabstand: Steht **ein einziger** Abstand daneben, läuft die Schrift
   ab dem zweiten Absatz von den Strichen weg – *und nach acht Zeilen steht
   sie wieder dazwischen.* Betroffen sind drei:

   | | |
   |---|---|
   | zwischen zwei Paragraphen | `--zeilenmass` statt `--abstand` (oben) |
   | unter der Überschrift | **keiner** – sie füllt selbst genau eine Zeile |
   | über den Namensschildern | **keiner**, aus demselben Grund |

   **Und der Kopf bekommt die Zeilenhöhe ausdrücklich**: Er ist ein
   Flex-Kasten, und ein Flex-Kasten erbt die Zeilenhöhe zwar, *richtet seine
   Kinder aber an der Grundlinie aus* – ohne diese Zeile bestimmt das
   größte Kind die Höhe, und das ist bei einer Regel mit Schild nicht
   dasselbe wie bei einer ohne. */
.gesetzblatt .paragrafkopf {
  margin-bottom: 0;
  line-height: var(--zeilenmass);
}

.gesetzblatt .paragrafgeltung {
  margin-top: 0;
  /* **Die Schildzeile ist genau eine Zeile hoch** - gemessen war sie es
     nicht, und das war der Grund, warum die Schrift ab dem zweiten
     Paragraphen von den Linien weglief: Ein Schild traegt `padding: 2px
     10px`, also 4 px mehr als eine Textzeile. **Nach zwei Regeln sind das
     8 px** - gemessen genau der Abstand, den die Zeilen danach daneben
     lagen.

     *Ein Takt haelt nur, wenn JEDER Kasten darin ein Vielfaches ist.* */
  height: var(--zeilenmass);
  align-items: center;
}

/* **Und das Schild selbst traegt keine Hoehe bei** - es waechst nicht aus
   der Zeile heraus, sondern sitzt darin. */
.gesetzblatt .paragrafschild {
  padding-block: 0;
  line-height: calc(var(--zeilenmass) - 4px);
}

/* **Die Serifenschrift trägt den ganzen Paragraphen**, nicht nur die Nummer
   – *das ist es, was „alter Gesetzestext" heißt.* `--schrift-titel` liegt
   seit dem 27.09.2026 selbst ausgeliefert bei uns (Young Serif); **nichts
   von Google** (Regel 4, `09-compliance.md`). */
.gesetzblatt .paragrafkopf,
.gesetzblatt .paragraftext {
  font-family: var(--schrift-titel);
}

/* **Die Überschrift ist fett, der Text nicht** – *eine Serifenschrift hebt
   sich durch ihre Form ab, und wo beides dieselbe Schrift ist, trägt das
   Gewicht* (Einkauf 2a, Abschnitt 1). */
.gesetzblatt .paragraftitel { font-weight: 700; }

/* **Die Nummer wird dunkler, sobald sie auf dem Pergament steht**
   (08.10.2026) – *eine Farbe, die auf Weiß trägt, trägt auf Pergament
   nicht.*

   **Gemessen, nicht vermutet** (WCAG AA = 4,5), an drei Stellen des Blattes:
   in der hellen Mitte, über dem äußeren Ring und zusätzlich über dem
   dunkelsten Fleck.

   - `--akzent`:        4,26 · 3,67 · **3,16** – auf Weiß waren es 6,27
   - `--akzent-dunkel`: 6,55 · 5,64 · **4,86**
   - `--text` daneben: 12,05 · 10,38 · 8,94 – *der Fließtext war nie in
     Gefahr, die Nummer schon*

   > **Die erste Messung war falsch und sah schlimmer aus**: `getComputedStyle`
   > gibt eine in `oklch` gesetzte Farbe heute als `oklch(...)` zurück, nicht
   > als `rgb(...)`; die Ziffern daraus ergaben für **alles** 2,34. Gerechnet
   > wird jetzt über einen 1×1-Canvas, und die Gegenprobe steht daneben:
   > **Schwarz auf Weiß = 21,0.**

   **Nur hier**, nicht an `--akzent` selbst: Überall sonst steht die Farbe auf
   `--flaeche` und trägt dort (6,27). */
.gesetzblatt .paragrafnummer { color: var(--akzent-dunkel); }

/* **Der Text sitzt auf den Linien** (17.10.2026) – er liest dieselbe Zahl,
   aus der der Abstand der Linien entsteht.

   > **Hier stand `line-height: 1.6`**, mit der Begründung *„der
   > Zeilenabstand eines gesetzten Absatzes"* – richtig für ein Blatt ohne
   > Linien, und auf liniertem Papier genau der Fehler: **Die Schrift liefe
   > mit jeder Zeile weiter von den Strichen weg**, und nach acht Zeilen
   > stünde sie zwischen ihnen. *Eine Zahl, die zu einer anderen passen
   > muss, darf nicht zweimal dastehen.* */
.gesetzblatt .paragraftext { line-height: var(--zeilenmass); }


/* ===========================================================================
   22. Die Stundenpläne (FAM-191, Epic FAM-189)

   Die Verwaltungsmaske: je Mensch ein Abschnitt, darin seine Pläne als
   Karten.

   ## Das Kartenraster gibt es schon

   `.planliste` benutzt dieselbe Rechnung wie `.leihliste` und
   `.kartenraster` (Design 2a A7) – *zwei Rasterrechnungen für dieselbe Sache
   liefen auseinander.* Sie steht hier trotzdem als eigener Selektor, weil die
   Klasse anders heißt; **die Zahlen kommen aus der einen Regel.**

   ## Ein abgelaufener Plan ist abgesetzt, nicht weg

   *Er beantwortet keine Frage mehr, aber ihn still zu löschen wäre ein
   Verlust, den niemand bemerkt* (FAM-190). **Abgesetzt heißt blasser** – wie
   der erledigte Block bei den Aufgaben (FAM-65).

   **Und das Wort bleibt schwarz.** Wer rot-grün-blind ist oder vorlesen
   lässt, liest *vorbei* – die Blässe ist die Zugabe (FAM-65).
   =========================================================================== */

/* --- Je Mensch ein Abschnitt ---------------------------------------------- */

.planmensch + .planmensch { margin-top: calc(var(--abstand) * 1.5); }

/* --- Die Karten ----------------------------------------------------------- */

/* **Dieselbe Rechnung wie `.leihliste`** (A7) – *eine zweite Zahl wäre eine
   zweite Stelle.* */
.planliste {
  display: grid;
  grid-template-columns:
      repeat(auto-fill, minmax(clamp(280px, 38cqi, 420px), 1fr));
  gap: 18px;
  margin: var(--abstand) 0 0;
  padding: 0;
  list-style: none;
}

.plankarte {
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
  padding: 14px 16px;
  border-radius: 2px 2px 4px 4px;
  background: var(--flaeche);
  box-shadow: var(--kartenschatten);
}

/* **Abgesetzt, nicht versteckt** – und `opacity` trifft die ganze Karte:
   *Ein Plan, der vorbei ist, soll leiser sein, nicht unlesbar.* Gemessen
   gegen `--grund` bleibt der Text über 4,5 : 1. */
.plankarte.abgelaufen { opacity: 0.62; }

.plantitel {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: calc(var(--abstand) * 0.4);
  margin: 0;
}

.planzeitraum {
  margin: 0;
  color: var(--text-leise);
  font-size: 0.9em;
}

/* --- Der Stempel ---------------------------------------------------------- */

/* **Das Wort trägt die Aussage, die Farbe ist die Zugabe** (FAM-65) – wörtlich
   die Regel der Ausleihkarte (Design 2a B1).

   **Und die drei Farben sind die von dort**: Grün für *gilt gerade*, Blau für
   *was kommt*, Grau für *vorbei*. **Rot bleibt dem Löschen** – *zwei
   Bedeutungen für eine Farbe kosten bei jeder Karte Zeit* (FAM-122). */
.planstempel {
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 0.75em;
  font-weight: 700;
  white-space: nowrap;
  box-shadow: inset 0 0 0 1px var(--linie);
}
.planstempel.gilt { background: var(--marker-bestaetigen); }
.planstempel.kommt { background: var(--marker-neben); }
.planstempel.vorbei { background: var(--grund); color: var(--text-leise); }

/* --- Ändern und wegnehmen ------------------------------------------------- */

.planwerkzeug {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: calc(var(--abstand) * 0.5);
  margin-top: auto;
}

/* **Die ganze Breite erst, wenn es offen ist** – der Fund von FAM-185:
   *Ein Aufklapper, der zugeklappt eine Zeile für sich beansprucht, kostet sie
   an jeder Karte.* */
.planaendern {
  flex: 0 0 auto;
  min-width: 0;
}
.planaendern[open] { flex: 1 1 100%; }

/* **`min-height` und nicht `height`** – ein `<summary>` ist weder `button`
   noch `.schaltflaeche`; ohne diese Zeile misst es 14 px (Einkauf 2a). */
.planaendern > summary {
  display: inline-flex;
  align-items: center;
  min-height: var(--knopf-flaeche);
  cursor: pointer;
  list-style: none;
  color: var(--akzent);
}
.planaendern > summary::-webkit-details-marker { display: none; }

.planaendern > .maske {
  margin-top: calc(var(--abstand) * 0.5);
  max-width: var(--maske-breite);
}

.planweg { margin: 0; flex: 0 0 auto; }


/* ===========================================================================
   23. Das Wochengitter des Stundenplans (FAM-192, Epic FAM-189)

   *„Man soll den Stundenplan in der Woche sehen"* - **Stunden als Zeilen,
   Tage als Spalten**, so wie er im Ranzen haengt.

   ## Ein Markup, zwei Anordnungen

   **Die Vorlage ist tageweise geordnet** - ein Kasten je Tag, darin seine
   Zellen. Das ist die Ordnung, die das Handy braucht (*„was habe ich
   Montag?"*), und deshalb steht dort **kein `<table>`**: Eine Tabelle ist
   stundenweise geordnet, und *zwei Fassungen desselben Gitters im selben
   Dokument liefen auseinander* (FAM-84, FAM-168, FAM-175).

   **Zum Gitter wird es hier**, mit `display: contents`: Die Tagesspalte
   loest sich auf, ihre Kinder werden Kinder des Gitters, und
   `grid-auto-flow: column` fuellt mit ihnen **eine Spalte**.

   | | Handy | Tablet | Desktop | Infoboard |
   |---|---|---|---|---|
   | `--plan-anzeige` | `block` | `block` | `grid` | `grid` |
   | `--plan-tag-anzeige` | `block` | `block` | `contents` | `contents` |
   | `--planzeiten-anzeige` | `none` | `none` | `contents` | `contents` |
   | `--planzelle-zeit-anzeige` | `block` | `block` | `none` | `none` |
   | `--plankopf-lage` | `static` | `static` | `sticky` | `sticky` |

   > **Die Tablet-Spalte stand hier bis zum 07.10.2026 auf `grid`** (FAM-237).
   > Sie war der Stand der ersten Fassung; **gemessen** blieben dem Fach am
   > Tablet dann 54,5 px und 13 von 27 Faechern standen gekuerzt da, also
   > wurde es die Liste - und diese Tabelle ist nicht nachgezogen worden.
   >
   > **Gemerkt hat es niemand, weil keine Wache sie gelesen hat.** Die vier
   > Wachen ueber Regel 10 halten `docs/Design.md` gegen die **Werte** im
   > Blatt, nicht gegen Tabellen in Kommentaren. Seitdem tut es
   > `test_jede_wertetabelle_im_blatt_stimmt`.
   >
   > *Eine Begruendung kann ablaufen, ohne falsch zu werden* - diese ist
   > falsch geworden, und sie war die gefaehrlichere Sorte: Wer sie liest,
   > schliesst daraus, ein breites Geraet bekomme das Gitter schon.

   **Dazu kommt ein fuenfter Fall, der in keiner Spalte steht**: das Handy im
   Querformat (FAM-235). Dort gelten die Handy-Werte - und die fuenf Zeilen
   darueber auf den Desktop-Werten. Die Bedingung steht in Abschnitt 2.

   **Die Zahl der Zeilen kommt aus dem Server** (`--plan-zeilen` im `style`):
   `grid-auto-flow: column` fuellt eine Spalte vollstaendig, bevor es die
   naechste beginnt - eine Zeile zu viel, und der naechste Tag beginnt mitten
   in der vorigen Spalte. **Und acht ist nicht fest**: FAM-193 laesst das
   Raster aendern.

   ## Die Pause ist ein Abstand, keine Zeile

   `.vorpause` traegt Luft und einen gestrichelten Strich - **an jeder Zelle
   dieser Zeile**, nicht an einem Kasten, der die Spalten ueberspannt. Mit
   `grid-auto-flow: column` gibt es keinen solchen Kasten, und er waere auch
   falsch: *Weil jede Spalte dieselben Zellen an derselben Zeile hat, steht
   der Strich von selbst durch.*

   **Wie viele Minuten es sind, steht in der Zeitspalte** - und am Handy, wo
   die es nicht gibt, an der Zelle selbst (`data-pause`). *Eine grosse Pause
   ohne Strich davor sieht aus wie ein Tippfehler.*

   ## Kein neues Mass und keine neue Farbe

   Die Zelle liest `--knopf-flaeche` (Design 2a A5), die Linien `--linie`,
   die Flaeche `--flaeche`. **Der heutige Tag bekommt den Akzent** - und das
   **Wort** steht in `.nurvorlesen` (FAM-65, FAM-163): *Wer rot-gruen-blind
   ist, verliert nichts.*
   =========================================================================== */

/* --- Wessen Plan ---------------------------------------------------------- */

/* **Die Namen stehen in der Unterzeile, neben der Woche** - dort, wo beim
   Einkauf das Listen-Menue steht (29.09.2026). *Zwei Angaben, eine Zeile.*

   **Ein Verweis je Name, kein Aufklapper**: Eine Familie hat drei Kinder,
   keine dreissig - und ein Tipp ist billiger als zwei. */
.planwer {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 10px;
  margin-left: 10px;
}

.planwer > [aria-current] { color: var(--text); }

/* --- Das Gitter ----------------------------------------------------------- */

.planwoche {
  display: var(--plan-anzeige);
  grid-template-columns: var(--plan-spalten);
  /* **Eine Kopfzeile und dann die Stunden.** Die Zahl kommt aus dem Server;
     `8` ist nur der Rueckfall, wenn das Attribut fehlt. */
  grid-template-rows: auto repeat(var(--plan-zeilen, 8), auto);
  grid-auto-flow: column;
  /* **Luft zwischen den Spalten, keine zwischen den Zeilen** - senkrecht
     trennt die Pause, und wo keine ist, gehoeren zwei Stunden zusammen
     (die Doppelstunde). */
  column-gap: 6px;
  row-gap: 0;
  margin-top: var(--abstand);
}

/* --- Die Spalte der Uhrzeiten --------------------------------------------- */

/* **Am Handy gibt es sie nicht** - dort traegt jede Zelle ihre Stunde selbst
   (`data-zeit`). *Zwei Fassungen, nie zwei sichtbar.* */
.planzeiten { display: var(--planzeiten-anzeige); }

/* **Der Kopf bleibt stehen** (FAM-236). Gemessen bei 812 x 375: Dem
   Inhaltsbereich bleiben **160,2 px** (Kopfzeile 56, Seitenkopf 97,8), das
   Gitter ist 430 px hoch - man sieht **3 von 8 Stunden** und muss scrollen.
   Unten angekommen waren **0 von 5** Tagesnamen im Bild, mit `sticky` sind
   es **5 von 5**.

   **Nur im Gitter**, und deshalb eine Merkmalsvariable und kein `@media`
   (Regel 0): In der Liste je Tag ist der Tagesname eine **Ueberschrift**
   ueber seinem Tag - dort soll sich nichts aendern. `--plankopf-lage` traegt
   `sticky` in genau den Saetzen, in denen `--plan-anzeige` auf `grid` steht.

   **`z-index` ausdruecklich**: Bei zwei positionierten Elementen ohne ihn
   entscheidet die Dokumentreihenfolge, und der Kopf steht **vor** den Zellen
   (`11-fallstricke.md`). Die Flaeche darunter deckt schon - `--grund` bzw.
   `--marker-neben` am heutigen Tag. */
.planecke,
.plantagname {
  position: var(--plankopf-lage);
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: var(--schaltflaeche);
  margin: 0;
  padding: 4px 6px;
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--grund);
  font-family: var(--schrift-seitentitel);
  font-size: 0.95em;
  font-weight: 700;
  color: var(--text-leise);
  text-align: center;
}

/* **Ist der Tag frei, rückt der Wochentag nach links** (12.10.2026, auf
   Wunsch: *„der Wochentag soll dann damit Platz ist linksbündig werden"*).

   **Gefragt wird `[data-frei]`, nicht eine zweite Klasse**: Das Attribut
   trägt den Namen und sagt damit zugleich, *dass* es einen gibt – *eine
   Klasse daneben wäre eine zweite Stelle für dieselbe Tatsache.*

   `justify-content` statt `text-align`: Der Kopf ist ein Flex-Kasten
   (die beiden Schreibweisen des Wochentags liegen nebeneinander), und
   dort richtet `text-align` nichts aus. */
.plantagname[data-frei] {
  justify-content: flex-start;
  gap: 8px;
}

/* **Der Stempel: kein Rahmen, nur Text, leicht schräg** (12.10.2026, auf
   Wunsch) – anders als der Ausfall-Stempel in der Zelle, der einen Rand
   hat. *Zwei Stempel mit demselben Kleid wären zwei Aussagen in einer
   Schrift; der eine streicht eine Stunde, der andere benennt einen Tag.*

   **Er trägt `--akzent-dunkel` und nicht `--fehler`**: Ferien sind keine
   Störung. *Rot heißt in diesem Blatt „hier stimmt etwas nicht"* (Regel 10,
   die Ausleihkarte) – ein Feiertag ist eine gute Nachricht.

   **Keine neue Zahl in Regel 10.** Die Größe ist `0.85em` des Kopfes, die
   Neigung dieselbe wie beim Ausfall-Stempel. */
/* **Das Datum der Woche, neben ihrem Wort** (12.10.2026, auf Wunsch).
   Leise, denn *„Diese Woche"* ist die Hauptsache – das Datum beantwortet
   die Nachfrage, nicht die Frage. */
.wochendatum {
  margin-left: 0.6ch;
  color: var(--text-leise);
  white-space: nowrap;
}

.plantagstempel {
  rotate: -3deg;
  font-family: var(--schrift-hand);
  font-size: 0.85em;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--akzent-dunkel);
  /* **Er darf nicht umbrechen und nicht drängeln**: *„Weihnachtsferien"* ist
     lang, und der Wochentag ist die Hauptsache. Wo es eng wird, weicht der
     Stempel – mit Ellipse, nicht mit einem Umbruch, der die Kopfzeile
     doppelt so hoch machte und das ganze Gitter verschöbe. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* **Der heutige Tag hebt sich ab** - und das Wort dazu steht im Markup. */
.plantagname[aria-current="date"] {
  background: var(--marker-neben);
  color: var(--text);
}

.planzeit {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
  min-height: var(--knopf-flaeche);
  padding: 4px 8px 4px 0;
  font-family: var(--schrift-seitentitel);
  font-size: 0.8em;
  line-height: 1.2;
  color: var(--text-leise);
  white-space: nowrap;
}

.planzeit > strong { font-size: 1.1em; color: var(--text); }

/* --- Die Tagesspalte ------------------------------------------------------ */

/* **`contents` im Gitter, `block` am Handy** - das ist die ganze Umschaltung. */
.plantag { display: var(--plan-tag-anzeige); }

/* Am Handy braucht ein Tag Luft zum naechsten; im Gitter ist `.plantag`
   `display: contents` und hat gar keinen Kasten, an dem ein Rand haengen
   koennte - die Zeile ist dort also wirkungslos und nicht falsch. */
.plantag + .plantag { margin-top: var(--abstand); }

/* **Der kurze Tagesname im Gitter, der lange am Handy.** Zwei Woerter fuer
   einen Tag, und immer nur eines sichtbar - dieselbe Bauart wie der Griff der
   Eingabemaske (`.wenn-zu` / `.wenn-auf`). Gemessen: *„Donnerstag"* braucht
   im Gitter mehr als eine Spalte breit ist. */
.plantag-lang { display: var(--planzelle-zeit-anzeige); }
.plantag-kurz { display: var(--planzeiten-anzeige); }

/* --- Die Zelle ------------------------------------------------------------ */

.planzelle {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  /* **Ein Rasterkind hat `min-width: auto`, also seinen Mindestinhalt** - und
     das ueberlaeuft die Spur, obwohl `--plan-spalten` sie mit `minmax(0, 1fr)`
     deckelt. **Gemessen 78 px Seitwaerts-Scroll am Desktop** (Regel 2), und
     die Zeile, die es entschied, stand nicht am Raster, sondern am Kind.

     *Das ist nicht die Falle vom 29.09.2026, sondern ihre Gegenrichtung:*
     Dort machte `min-width: 0` ein `minmax(auto, 1fr)` wirkungslos; hier ist
     die Spur schon auf `0` gedeckelt, und genau dann gehoert es hierher. */
  min-width: 0;
  min-height: var(--knopf-flaeche);
  padding: 4px 6px;
  background: var(--flaeche);
  box-shadow: inset 0 0 0 1px var(--linie);
}

/* **Die Stunde am Handy** - aus dem Attribut, das die Zelle ohnehin traegt. */
.planzelle::before {
  content: attr(data-zeit);
  display: var(--planzelle-zeit-anzeige);
  flex: 1 1 100%;
  font-family: var(--schrift-seitentitel);
  font-size: 0.8em;
  color: var(--text-leise);
}

/* **Die Pause**: Luft und ein gestrichelter Strich, an jeder Zelle der Zeile
   und an ihrer Uhrzeit. */
.planzelle.vorpause,
.planzeit.vorpause { margin-bottom: 14px; }

.planzelle.vorpause { border-bottom: 2px dashed var(--linie); }

/* Wie lang sie ist - in der Zeitspalte immer, an der Zelle nur am Handy. */
.planzeit.vorpause::after,
.planzelle.vorpause::after {
  content: attr(data-pause);
  font-family: var(--schrift-seitentitel);
  font-size: 0.7em;
  color: var(--text-leise);
}

.planzeit.vorpause::after { align-self: flex-end; }

.planzelle.vorpause::after {
  display: var(--planzelle-zeit-anzeige);
  flex: 1 1 100%;
}

/* --- Das Fach ------------------------------------------------------------- */

/* **Die Handschrift bleibt** - anders als im Monatsgitter (FAM-163): Dort
   stehen sieben Spalten und ein Titel wie *„Britta Urlaub"*, hier fuenf und
   ein Wort wie *„Mathe"*. **Gemessen, nicht angenommen** - die Zahlen stehen
   in `docs/Design-Messungen.md`.

   **Und wo es nicht passt, steht die Kurzform** (Regel 6): Der volle Name
   bleibt im `title`. */
.planfach {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-weight: 700;
}

/* --- Raum und Kuerzel: der Fuss der Zelle (FAM-232) ----------------------- */

/* *„Es soll dann auf dem Stundenplan leicht grau unten links und rechts
   angezeigt werden, in jeder Stunde."* - woertlich der Wunsch vom 06.10.2026.

   ## Eine Zeile, keine zweite Spalte

   `flex: 1 1 100%` setzt den Fuss **unter** das Fach - dieselbe Zeile, mit der
   schon die Uhrzeit am Handy eine eigene Reihe bekommt (`.planzelle::before`).
   *Links und rechts* ist dann `space-between`, und es braucht keine
   Rasterspalten: **Die Zelle ist ohnehin ein Flex-Kasten mit Umbruch.**

   **Das gilt in allen vier Ansichten**, und das ist der Fund der Messung: Die
   Karte fragt, wo die beiden Angaben am Handy stehen, weil es dort **kein
   Gitter** gibt (FAM-192) - die Zelle ist dort aber **so breit wie die
   Seite**, also ist *unten links und rechts* genau dort am billigsten. **Im
   Gitter ist die Spalte das Enge**, und dafuer weicht der Raum (siehe unten).

   ## Keine neue Farbe und keine neue Zahl

   `--text-leise` traegt das Leise schon - die Uhrzeit und die Pausenangabe
   derselben Zelle stehen darin (Regel 10: jede Farbe genau einmal). **Und
   `0.8em` ist dieselbe Groesse wie die Uhrzeit**: *Zwei verschiedene kleine
   Schriften in einer Zelle sehen aus wie ein Versehen.*

   **Gerechnet, nicht behauptet** (die Karte verlangt es): `--text-leise` auf
   `--flaeche` ergibt **6,55 : 1**, auf dem Grund der leeren Zelle
   **5,92 : 1** - beides ueber den 4,5 von WCAG AA. Die Gegenprobe der
   Messung (Weiss auf Schwarz = 21,00) steht in `docs/Design-Messungen.md`,
   *weil eine Kontrastrechnung ohne sie zweimal gamma-dekodiert und 14 : 1
   fuer mittelhelle Farben meldet* (27.09.2026). */
.planfuss {
  flex: 1 1 100%;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px;
  min-width: 0;
  font-size: 0.8em;
  color: var(--text-leise);
}

/* **Der Raum weicht, das Kuerzel nicht.** Im Gitter hat die Spalte rund
   hundertsechzig Pixel; *„Chemie-Fachraum C 014"* passt dort nicht, drei
   Buchstaben immer. **Also bekommt der Raum die Ellipse und das Kuerzel seinen
   Platz** - umgekehrt stuende rechts *„MU…"*, und ein abgeschnittenes Kuerzel
   ist keines (Regel 6). */
.planraum {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.planlehrer { flex: 0 0 auto; }

/* **Die volle Zelle ist das Papier, die leere ist leiser** - und nicht
   umgekehrt.

   **Die erste Fassung hat die vollen Zellen gruen hinterlegt**, und das war
   der Marker *Bestaetigen* aus Abschnitt 15: **eine zweite Bedeutung fuer
   eine Farbe**, und *die kostet bei jeder Karte Zeit* (FAM-122, FAM-179).
   Gesehen auf dem Bild, nicht im Code. */
.planzelle:not(.belegt) { background: var(--grund); }

/* --- Eintragen und herausnehmen ------------------------------------------- */

/* **Das Formular um das ganze Gitter** (FAM-233) - *ein Knopf fuer den ganzen
   Plan.*

   **Es traegt keine Gestaltung**, nur seinen eigenen Rand weg: Der Browser
   gibt einem `<form>` einen Aussenabstand, und der schoebe das Gitter gegen
   die Ueberschrift. *Alles andere steht am Gitter darin.* */
.plangitter { margin: 0; }

/* **Das Band über dem Gitter: was in der Hand liegt** (FAM-234).

   *„Nach dem Kopieren ist sichtbar, was in der Hand liegt – sonst fügt man
   blind ein."* **Mit Wort, nicht nur mit Farbe** (FAM-65): Fach, Raum und
   Kürzel stehen darin, dazu der Weg zum Loslassen.

   **Keine neue Farbe** (Regel 10): `--marker-neben` trägt das Hellblau der
   Knöpfe aus Abschnitt 15 schon. */
.planhand {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--abstand);
  margin: 0 0 var(--abstand);
  padding: 6px var(--abstand);
  border-radius: var(--radius);
  background: var(--marker-neben);
}

.planhand-fach { font-weight: 700; }

/* **Und die Zelle, aus der sie stammt, ist markiert** - derselbe Ton, damit
   das Band und die Quelle sichtbar zusammengehoeren. */
.planzelle.in-der-hand {
  box-shadow: inset 0 0 0 2px var(--akzent);
}

.planeintrag { display: flex; flex-wrap: wrap; align-items: center; gap: 4px;
               margin: 0; min-width: 0; flex: 1 1 100%; }

/* **Die Trefferflaeche ist auch eine Breite** - der Grundstil setzt nur
   `min-height` (der Fund von FAM-185): Ein Knopf mit einem Zeichen darauf mass
   **29,3 px breit** und blieb damit unter dem Boden von 44 (Design 2a A5).

   **Und der Selektor traegt zwei Klassen, weil er zwei Klassen schlagen
   muss.** `.planweg > button` ist (0,1,1) und hat **nicht** gegriffen:
   Abschnitt 15 setzt `all: unset` in einer Liste, in der auch
   `.schaltflaeche.gefahr` steht - **(0,2,0)**, und *bei einer Selektorliste
   zaehlt der spezifischste Selektor, der passt.* Ein `all: unset` von
   weiter oben schlaegt damit eine spaetere, scheinbar genauere Regel;
   gemessen blieb `min-width` auf `auto`, **und die Regel stand sichtbar im
   Blatt.** Dieselbe Familie wie der Abo-Knopf und die Kurzform
   `background` an `.schaltflaeche.betont`. */
.planeintrag > .schaltflaeche,
.planweg > .schaltflaeche { min-width: var(--knopf-flaeche); }

/* **Das Feld nimmt die Zeile, der Knopf die naechste** - in einer Spalte von
   neunzig Pixeln stehen beide nicht nebeneinander, und *ein Feld, in dem man
   drei Zeichen sieht, ist keines* (der Fund des Essensplans, FAM-145). */
/* **`input.planfeld` und nicht `.planfeld`** - die Falle vom 25.09.2026, zum
   zweiten Mal, und diesmal hat sie zwei Tage lang gehalten: Die Feldregel in
   Abschnitt 7 lautet `select, input[type="text"], …` - Element **und**
   Attribut, also **(0,1,1)**. Eine blosse Klasse ist (0,1,0) und verliert,
   *ganz gleich, wie weit unten im Blatt sie steht.*

   **Gemessen am 06.10.2026**: `padding: 2px 6px` stand hier seit FAM-192 und
   war **vollstaendig wirkungslos** - gerechnet kam `0 10px` heraus, also
   `var(--abstand)`. Am Infoboard sind das **64 px Innenabstand**, und das
   Kuerzelfeld (drei Zeichen) zeigte damit **kein einziges**. */
input.planfeld {
  flex: 1 1 100%;
  min-width: 0;
  min-height: var(--schaltflaeche);
  padding: 2px 6px;
  border: 0;
  border-bottom: 1.5px solid var(--linie);
  background: transparent;
  font: inherit;
}

/* **Raum und Kuerzel teilen eine Zeile** (FAM-232) - das Fach nimmt die ganze
   Breite darueber.

   **Warum sie nicht einzeln `flex: 1 1 100%` tragen**: Drei Felder
   untereinander machen aus einer Zelle im Bearbeitungsmodus vier Zeilen, und
   bei acht Stunden ist das ein Gitter, das niemand mehr ueberblickt. *Ein
   Kuerzel braucht drei Zeichen - ein Fach bis zu sechzig.*

   **Das ist nicht die Zeile, die der Essensplan abgelehnt hat** (FAM-145):
   Dort stand ein Fachfeld neben einem Knopf und mass **2,9 Zeichen**. Hier
   nimmt das Kuerzel nur, was es braucht, und der Raum bekommt den Rest. */
.planfelder {
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

input.planfeld-raum { flex: 1 1 auto; }

/* **Und beide tragen `input` davor** - *eine Klasse schlaegt die
   Nachbarregel nicht, sobald die ein Element nennt*: Mit `input.planfeld`
   (0,1,1) eine Regel weiter oben waere `.planfeld-lehrer` (0,1,0)
   wirkungslos geworden, **und das Kuerzelfeld mass gemessen 163,5 px statt
   43** - es erbte `flex: 1 1 100%`. *Wer die Spezifitaet einer Regel hebt,
   hebt sie gegen ihre eigenen Nachbarn mit.*

   **`ch` misst den Text, der Innenabstand kommt dazu** (der Fund vom
   20.09.2026): `input.planfeld` traegt `padding: 2px 6px`, also zwoelf Pixel,
   dazu zwei fuer die Unterlinie. *Ohne den Zuschlag sieht man vom dritten
   Zeichen die Haelfte.*

   **Und `3ch` sind keine drei Grossbuchstaben.** `ch` ist die Breite der
   **Null**; gemessen am 06.10.2026 misst *„MUE"* in dieser Schrift
   **28,9 px** bei `1ch = 8,8 px`, also **1,1 ch je Buchstabe**. Mit `3ch`
   waren **alle neun** Kuerzel im Abzug abgeschnitten - *ein Feld, in dem man
   drei Zeichen sieht, ist keines, und eines fuer drei Zeichen, das zwei
   zeigt, auch nicht.*

   > Die 3,3 sind damit **gemessen und nicht gewaehlt**: 3 × 1,1. */
input.planfeld-lehrer { flex: 0 0 calc(3.3ch + 14px); }

/* **Das ✕ steht rechts** - unter dem Kuerzel, nicht unter dem Raum.

   **Und es gehoert in diesen Selektor, nicht in einen eigenen.** Hier stand
   `.planeintrag > button { margin-left: auto }` – **(0,1,1), und damit
   wirkungslos**: Abschnitt 15 setzt `all: unset` in einer Liste, in der auch
   `.schaltflaeche.gefahr` steht, und das ist **(0,2,0)**. *Gemessen stand das
   ✕ am linken Rand, und die Regel stand sichtbar im Blatt* – dieselbe Falle
   wie bei `min-width` eine Regel weiter oben (FAM-185). */
.planeintrag > .schaltflaeche { margin-left: auto; }


/* ===========================================================================
   23b. Eine Stunde fällt aus (30.09.2026, auf Kommentar aus dem Canvas)

   *„Ausfall wird dadurch angezeigt, dass ein dicker roter Strich quer durch
   die Zelle geht."*

   ## Waagerecht, nicht diagonal

   **Quer heißt hier durchgestrichen** – der Strich, den man über ein Wort
   zieht, das nicht mehr gilt. Eine Diagonale wäre im Gitter die Linie, die
   eine *leere* Zelle markiert; *zwei Bedeutungen für einen Strich kosten bei
   jeder Karte Zeit* (FAM-122).

   ## Keine neue Farbe

   `--fehler` trägt sie schon (Regel 10: jede genau einmal). **Rot heißt in
   dieser Anwendung: hier stimmt etwas nicht** – der Löschknopf, das
   Pflichtfeld, der Blocker am Projektbrett. *Ein Ausfall ist derselbe
   Gedanke.*

   ## Der Knopf liegt über der Zelle und ist durchsichtig

   `position: absolute; inset: 0` – er trägt kein eigenes Bild, seine
   Trefferfläche ist die Zelle. **Der Fokusring bleibt**: *Ein Bedienelement,
   das man mit der Tastatur nicht findet, gibt es für eine Tastatur nicht.*
   =========================================================================== */

/* **Die Zelle ist ein eigener Stapel** (12.10.2026, auf einen Kommentar im
   Canvas: *„Beim Scrollen dürfen die Schulstunden nicht unter den
   Wochentagen durchlaufen"*).

   **`position: relative` allein genügt nicht**: Mit `z-index: auto` macht es
   *keinen* Stapelkontext auf. Die `z-index` der Kinder gelten dann gegen die
   **ganze Seite** – und der Ausfallstrich trägt **2**, der klebende
   Wochentag (`.plantagname`) nur **1**. Beim Scrollen lief der rote Strich
   damit **über** den Kopf hinweg.

   **`isolation: isolate` sperrt die beiden ein**, ohne ihr Verhältnis
   zueinander zu ändern: Der Strich bleibt über dem Knopf (darum ging es),
   und beide bleiben unter dem Kopf. *Eine Zahl am Kopf hochzusetzen hätte
   denselben Streit nur verschoben – bis zum nächsten Element mit einer
   höheren.* */
.planzelle { position: relative; isolation: isolate; }

.planausfall { margin: 0; }

.ausfallknopf {
  position: absolute;
  inset: 0;
  width: 100%;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  /* **Über dem Fach, nicht darunter** – gemessen am 30.09.2026:
     `elementFromPoint` in der Mitte einer belegten Zelle meldete
     `planfach`, und *die Mitte wäre damit tot gewesen*.

     **Nicht `pointer-events: none` am Fach**: Das kostet den Tooltip mit
     dem vollen Namen (Regel 6). Der Knopf trägt ihn selbst – im `title`
     und im `aria-label`. */
  z-index: 1;
}

/* **Ein eigenes Element, kein Pseudo-Element der Zelle** (30.09.2026, auf
   Rückmeldung: *„Ich kann die 1., 3., 5. Stunde absagen, aber bei der 2., 4.
   und 6. funktioniert es nicht."*).

   Der Strich hing am `::after` der Zelle – **und das trägt seit FAM-190 den
   Pausentext** (`content: attr(data-pause)`). Beide Regeln standen im Blatt,
   meine später: Sie gewann beim `content`, **nicht beim `display`**. Jene
   setzt `display: var(--planzelle-zeit-anzeige)`, und das ist ab Desktop
   `none` – *was die spätere Regel nicht nennt, bleibt stehen.*

   **Getroffen hat es genau die Zellen mit Pause davor** – im Standardraster
   die 2., 4. und 6. Stunde. *Der Ausfall wurde gespeichert, er war nur nicht
   zu sehen.*

   **`::before` war auch nicht frei**: Es trägt seit FAM-192 die Uhrzeit am
   Handy. Beide Pseudo-Elemente der Zelle sind vergeben, ein drittes gibt es
   nicht.

   **Und es hängt nicht am Knopf**, sondern an der Zelle: Wer den Plan nicht
   pflegen darf, sieht keinen Knopf – **den Strich muss er trotzdem sehen.**

   Der Stempel liegt über dem Fach, nicht dahinter – er soll es
   überstempeln. `pointer-events: none`, damit der Knopf darunter erreichbar
   bleibt: *Ein Stempel, der den Tipp abfängt, macht den Ausfall
   unumkehrbar.* */
/* **Ein Stempel mit dem Wort „Ausfall"** (12.10.2026, auf Wunsch: *„den
   roten Strich austauschen durch einen Stempel mit dem Text Ausfall, der
   Stempel soll so aussehen, als ob die Farbe nicht überall richtig am
   Stempel war"*).

   **Damit hat der Ausfall zum ersten Mal ein Wort.** Vorher war er ein
   Strich – und *ein Zeichen allein trägt die Aussage nicht* (FAM-65). Das
   Wort steht deshalb auch im Vorlesebaum, ohne `aria-hidden`: Wer den Plan
   nicht pflegen darf, bekommt keinen Knopf und hatte damit **nirgends**
   eine Auskunft über den Ausfall.

   **Die ungleiche Farbe ist eine Maske, keine Farbe.** `mask-image` nimmt
   dem Stempel Tinte weg – Rand und Schrift zugleich, denn eine Maske gilt
   für das ganze Element. Die Stufen darin sind **Deckkraft**, kein Ton:
   Die Farbe kommt nach wie vor aus `--fehler` und sonst nirgendher.

   **Eine Ebene, nicht zwei.** Zwei Verläufe übereinander bräuchten
   `mask-composite: intersect`, um sich gegenseitig abzuziehen – und das
   kann Safari erst seit 18.4. *Ein Effekt, der auf dem Handy der Familie
   nicht ankommt, ist keiner.* Die eine Ebene liegt schräg und hat
   **unregelmäßige** Stufen: Ein gleichmäßiges Muster sähe aus wie ein
   Strichcode, nicht wie ein Stempel.

   **In Prozent und nicht in Pixeln** – so wächst das Lückenmuster mit dem
   Stempel, und der ist je Ansicht verschieden groß (`0.8em`, wie die
   Uhrzeit und das Kürzel derselben Zelle).

   **Die Dicke bleibt `--ausfall-strich`** (Regel 10, 3 / 4 / 4 / 6): Der
   Rand des Stempels ist die Tinte, die vorher der Strich war. *Die Marke
   umzubenennen hieße, sie an sieben Stellen und in der Maßtabelle
   anzufassen – für einen Namen, der dasselbe meint.* */
.ausfallstempel {
  position: absolute;
  /* **Mittig im Fach, leicht schief aufgesetzt** – ein Stempel trifft nie
     genau gerade. */
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  rotate: -9deg;
  pointer-events: none;
  /* **Über dem Knopf, der selbst `z-index: 1` trägt** – sonst läge der
     Stempel darunter und wäre an einer belegten Zelle unsichtbar. */
  z-index: 2;

  padding: 0.15em 0.5em;
  border: var(--ausfall-strich) solid var(--fehler);
  border-radius: calc(var(--ausfall-strich) * 0.8);
  color: var(--fehler);
  font-family: var(--schrift-seitentitel);
  font-size: 0.8em;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;

  mask-image: linear-gradient(
    104deg,
    rgb(0 0 0 / 1) 0 8%,
    rgb(0 0 0 / 0.25) 11%,
    rgb(0 0 0 / 1) 15% 30%,
    rgb(0 0 0 / 0.12) 33%,
    rgb(0 0 0 / 1) 37% 51%,
    rgb(0 0 0 / 0.45) 55%,
    rgb(0 0 0 / 1) 59% 72%,
    rgb(0 0 0 / 0.2) 76%,
    rgb(0 0 0 / 1) 80% 100%
  );
}

/* **Das Fach bleibt lesbar, nur gedämpft** – *ein Ausfall sagt, dass die
   Stunde nicht stattfindet, nicht dass sie nie im Plan stand.*

   **Raum und Kürzel dämpfen mit** (FAM-232): Sie gehören zu dieser Stunde,
   und *ein Raum in voller Stärke unter einem durchgestrichenen Fach sieht aus
   wie eine Auskunft, die noch gilt.* */
.planzelle.faellt-aus .planfach,
.planzelle.faellt-aus .planfuss { opacity: 0.55; }

/* ===========================================================================
   24. Die Zeiten eines Stundenplans (FAM-193, Epic FAM-189)

   **Eine Liste, kein Gitter** - Nummer, Beginn, Ende, und je Zeile ein
   Wegnehmen.

   ## Die Pause steht dazwischen und ist kein Feld

   `.vorpause` traegt dieselbe Rechnung wie im Wochengitter (Abschnitt 23):
   Luft, ein gestrichelter Strich und die Zahl aus `data-pause`. **Es gibt
   nichts einzutragen** - *wer die Pause aendern will, verschiebt die Stunde
   danach.*

   ## Kein neues Mass

   Die Zeile liest `--knopf-flaeche`, die Linien `--linie`, die Flaeche
   `--flaeche`. **Und `input[type="time"]` steht seit FAM-83 in der
   Positivliste fuer `font: inherit`** - *sie ist viermal vergessen worden;
   hier ist sie es nicht.*
   =========================================================================== */

.zeitenliste {
  display: flex;
  flex-direction: column;
  gap: 0;
  margin: var(--abstand) 0 0;
  padding: 0;
  list-style: none;
  max-width: 46ch;
}

.zeitzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
  min-height: var(--knopf-flaeche);
  padding: 6px 10px;
  background: var(--flaeche);
  box-shadow: inset 0 0 0 1px var(--linie);
}

/* **Dieselbe Pausendarstellung wie im Gitter** - *zwei Bauarten fuer
   dieselbe Auskunft liefen auseinander.* */
.zeitzeile.vorpause {
  margin-bottom: 14px;
  border-bottom: 2px dashed var(--linie);
}

.zeitzeile.vorpause::after {
  content: attr(data-pause);
  flex: 1 1 100%;
  font-family: var(--schrift-seitentitel);
  font-size: 0.75em;
  color: var(--text-leise);
}

.zeitnummer {
  flex: 0 0 auto;
  min-width: 2.5ch;
  font-family: var(--schrift-seitentitel);
  font-weight: 700;
}

.zeitform {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0;
  min-width: 0;
  flex: 1 1 auto;
}

.zeitweg { margin: 0; flex: 0 0 auto; }

/* **Die Trefferflaeche ist auch eine Breite**, und der Selektor traegt zwei
   Klassen, weil er zwei schlagen muss: Abschnitt 15 setzt `all: unset` in
   einer Liste, in der `.schaltflaeche.gefahr` steht - **(0,2,0)**, und *bei
   einer Selektorliste zaehlt der spezifischste Selektor, der passt* (der
   Fund von FAM-192). */
.zeitform > .schaltflaeche,
.zeitweg > .schaltflaeche { min-width: var(--knopf-flaeche); }

.zeitform input[type="time"] {
  min-height: var(--schaltflaeche);
  min-width: 0;
  padding: 2px 6px;
  border: 0;
  border-bottom: 1.5px solid var(--linie);
  background: transparent;
}

/* **Die Maske zum Anhaengen steht unten**, nicht hinter einem Haupt-Knopf:
   *Sie ist der Grund, aus dem man diese Seite aufruft* - anders als bei den
   Plaenen, wo die Liste die Hauptsache ist (FAM-191). */
.zeitneu { margin-top: calc(var(--abstand) * 1.5); }

/* **Die Vorlage steht unter dem Anhängen** (FAM-238) und trägt denselben
   Abstand - *zwei Masken untereinander, gleich weit auseinander.* Ein eigener
   Wert wäre eine Zahl ohne Entscheidung (Regel 5).

   **Kein `.betont` an ihrem Knopf** (und deshalb hier auch keine Farbe): Das
   Anhängen ist die Hauptsache dieser Maske, das Übernehmen die Abkürzung
   dazu. *Zwei betonte Knöpfe auf einer Seite betonen keinen.* */
.zeitvorlage { margin-top: calc(var(--abstand) * 1.5); }


/* ===========================================================================
   Der Stammbaum (FAM-199, FAM-200)

   **Zwei Masken, ein Baustein.** Die Liste ist die Arbeitsansicht, der Baum
   zeigt dasselbe anders - und `stammbaum/_mensch.html` zeichnet in beiden
   denselben Kasten.

   **Die vier Merkmale stehen in Regel 10** (`--baum-*`): Am Handy stehen die
   Kaesten untereinander, ab Tablet steht das Wort der Generation links neben
   ihnen, ab Desktop ist die Reihe mittig.
   ======================================================================== */

/* Der Baum scrollt in SEINEM Kasten, nicht die Seite (Regel 2). */
.baumraum {
  overflow-x: auto;
  padding-bottom: var(--abstand);
}

.baum {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  /* **So breit wie seine breiteste Reihe** (25.10.2026, auf Rueckmeldung:
     *„Die Ahnentafel muss natuerlich viel breiter werden unten"*).

     Dieselbe Angabe wie an `.baumkaesten` darunter, und das ist Absicht:
     Waere der Baum nur so breit wie sein Kasten, stuenden die unteren
     Reihen rechts darueber hinaus - **und das SVG mit den Linien hoert an
     seiner Kante auf.** `min-width` haelt ihn dort, wo er hineinpasst, auf
     voller Breite, damit eine kurze Reihe mittig stehen kann. */
  width: var(--baum-kaesten-breite);
  min-width: 100%;
}

.baumreihe {
  /* **Ueber dem SVG.** Ohne diese beiden Zeilen liefe eine Linie quer
     ueber die Karten, denn das SVG steht im Dokument vor ihnen. */
  position: relative;
  z-index: 1;
  display: var(--baum-reihe-anzeige);
  /* **Eine Spalte, nicht zwei** (24.10.2026): Hier stand
     `var(--baum-wort-breite) minmax(0, 1fr)` - eine Spalte fuer das Wort
     der Generation, eine fuer die Kaesten. **Das Wort steht seitdem im
     Vorlesebaum**, und eine leere Spalte von 9 bis 14 Zeichen nimmt dem
     Baum genau die Breite, um die es im Wunsch geht. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--abstand);
  /* **`start`, nicht `baseline`** (02.10.2026): Ein Rasterelement an seiner
     Grundlinie waechst nicht in die Zeilenhoehe hinein, sondern laeuft
     darueber hinaus - gemessen lag der Partner unter dem Wort *Kinder*. */
  align-items: start;
}

/* Das Wort der Generation. **Es steht seit dem 24.10.2026 nur noch im
   Vorlesebaum** (`.nurvorlesen` in der Vorlage) - die Regel bleibt, weil
   sie greift, sobald jemand es wieder sichtbar macht, und weil `h2` sonst
   die Grundgroesse einer Ueberschrift traegt. */
.baumwort {
  margin: 0;
  font-family: var(--schrift-seitentitel);
  font-size: var(--schrift);
  font-weight: 700;
  color: var(--text-leise);
}

.baumkaesten {
  position: relative;
  display: flex;
  /* **Eine Generation steht in EINER Reihe** (25.10.2026, auf
     Rueckmeldung: *„nicht immer nur zwei nebeneinander"*).

     **Hier stand `wrap` und daneben `max-width: 100%`** - zusammen hiess
     das: *so breit wie die Karten, aber nie breiter als der Kasten*, und
     damit brach eine Generation aus acht Tafeln in vier Zeilen um. **Jede
     dieser Zeilen sah aus wie eine eigene Generation**, und die Klammern
     liefen quer darueber.

     Eine Ahnentafel **verdoppelt sich nach unten** - sie wird irgendwann
     breiter als jeder Bildschirm. Sie scrollt deshalb in ihrem eigenen
     Kasten (`.baumraum`, Regel 2), *wie die Filzwand am Handy und das
     Wochengitter des Stundenplans.*

     **Am Handy bleibt es beim Umbruch**: Dort ist eine Tafel so breit wie
     der Bildschirm, und nebeneinander waere jede einzeln eine
     Bildschirmbreite. */
  flex-wrap: var(--baum-kaesten-umbruch);
  /* **So breit wie seine Karten, nicht wie die Seite.** Erst dadurch liegt
     die aeusserste Tafel buendig am Rand - und die Schiene darunter kann
     ihre Enden mit *einer halben Kartenbreite* angeben, ohne dass irgendwo
     eine gemessene Zahl steht. */
  width: var(--baum-kaesten-breite);
  margin-inline: var(--baum-kaesten-rand);
  gap: var(--abstand);
  /* Traegt, wenn eine Reihe umbricht - sonst ist nichts zu verteilen. */
  justify-content: var(--baum-ausrichtung);
}

/* Ein Mensch. **Papier an einer Wand**, wie der Zettel am Brett - aber ohne
   Nadel und ohne Kippung: *Ein Stammbaum ist kein Moderationsbrett, und eine
   Reihe gekippter Kaesten liest sich schlechter als eine gerade.* */
.ahnkasten {
  /* **Die Breite steht als `width`, nicht als `flex-basis`.** In einer
     Zeile waere die Basis die Breite, in einer Spalte die **Hoehe** - und
     die Tafel ist am Handy eine Spalte. Hier stand einmal
     `flex: 0 0 var(--baum-kasten-breite)`, und aus `20ch` wurden 190 px
     Hoehe (*dieselbe Falle zum fuenften Mal*, docs/11-fallstricke.md).
     **Eine Angabe, die in beiden Richtungen dasselbe bedeutet, kann sie
     gar nicht erst stellen.** */
  flex: 0 0 auto;
  width: var(--baum-kasten-breite);
  max-width: 100%;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 2px;
  /* **Kompakter als eine Karte** (24.10.2026, nach der Vorlage): Hier
     stand `* 0.6`, das Polster einer Karte. In der Vorlage ist ein Kasten
     zwei Zeilen hoch und sonst nichts - *ein Baum ist eine Uebersicht,
     und jedes Polster kostet eine Generation Hoehe.* */
  padding: calc(var(--abstand) * 0.3);
  /* **Alles mittig** - wie in der Vorlage, und es ist eine Angabe statt
     dreier: Name, Jahre und Marke richten sich danach. */
  align-items: center;
  text-align: center;
  /* **Jeder braucht seine Karte** (02.10.2026, auf Rueckmeldung). Hier
     stand `box-shadow: var(--schatten)`, und das ist **ungueltig**:
     `--schatten` ist eine Farbe, kein Schatten. Die Zeile fiel weg, und
     sichtbar war nur der Mittelpunkt mit seinem `outline`. */
  /* **Die Karte traegt seit dem 24.10.2026 die TAFEL** (auf Wunsch:
     *„Verheiratete oder Beziehung sollen in einem Kasten stehen"*) - hier
     stand Rahmen, Grund und Schatten, und ein Paar waren damit **zwei
     Kaesten mit einem Strich dazwischen**.

     *Ein Paar ist eine Sache, kein Paar Sachen* - und daran haengt die
     zweite Haelfte des Wunsches: **Eine Linie nach oben endet an einem
     Kasten**, und dieser eine Kasten ist die Beziehung. Vorher endete sie
     an der Mitte zwischen zweien, also an einer Stelle, an der nichts
     stand. */
}

/* **Der Mittelpunkt hebt sich ab, und zwar nicht nur farbig** (FAM-65): Er
   traegt keinen Verweis, und das ist der Unterschied, den eine Tastatur
   merkt. */
.ahnkasten.mittelpunkt {
  outline: 2px solid var(--akzent);
  outline-offset: 1px;
}

/* --- Der Baum nach der Vorlage (24.10.2026) ------------------------------

   *„Der Baum sieht fürchterlich aus. Bitte ein wenig mehr in diese Richtung
   bringen."* - dazu ein Bild eines klassischen Stammbaums: kompakte
   Kaesten, **Name zentriert und mehrzeilig**, darunter die Lebensdaten,
   duenner Rahmen, helle Fuellung.

   **Was aus der Vorlage NICHT kommt**: die Geschlechtszeichen. Der `Ahn`
   hat kein Geschlecht (FAM-199) - *eine erfundene Angabe ist schlimmer als
   eine fehlende* (Regel 5). */
.ahnkasten .ahnname,
.ahnzeile .ahnname {
  font-weight: 700;
  /* **Die Trefferflaeche steht an einer Stelle** (`--knopf-flaeche`, 27.09.2026)
     - ein Name im Baum ist der einzige Weg weiter, also ist er ein
     Bedienelement. */
  min-height: var(--knopf-flaeche);
  display: flex;
  align-items: center;
}

/* **Im Baum steht er mittig, dunkel und mehrzeilig** - in der Liste nicht:
   Dort ist er eine Zeile unter vielen. *Zwei Orte, zwei Rollen* - dieselbe
   Trennung wie `kompakt` am Termin (FAM-163).

   **Er bleibt ein Verweis** (FAM-200: *„Der Name ist ein Verweis, und zwar
   auf den Baum um ihn"* - am Handy die ganze Bedienung); er **sieht** nur
   nicht mehr wie einer aus. In der Vorlage steht er dunkel und ruhig, und
   *ein Baum aus dreissig blauen Verweisen ist ein Linkverzeichnis.* Dass er
   einer ist, sagen der Zeiger, das Ueberfahren und die Trefferflaeche - und
   die bleibt (`--knopf-flaeche`). */
.ahnkasten .ahnname {
  justify-content: center;
  color: var(--text);
  text-decoration: none;
  line-height: 1.25;
}

.ahnkasten .ahnname:hover,
.ahnkasten .ahnname:focus-visible { color: var(--akzent-dunkel); }

.ahnjahre {
  font-size: calc(var(--schrift) * 0.9);
  color: var(--text-leise);
}

/* Das Portrait (FAM-201). **Klein, und der Name steht darunter** - *ein Bild
   allein traegt die Aussage nicht* (FAM-65).

   **`object-fit: cover`**: Ein Portrait ist selten quadratisch, und ein
   verzerrtes Gesicht ist schlimmer als ein beschnittenes. */
.ahnfoto {
  width: 64px;
  height: 64px;
  border-radius: var(--radius);
  object-fit: cover;
  align-self: center;
}

/* **Das Bild einer Beziehung** (24.10.2026, auf Wunsch: *„Man soll auch ein
   Foto von der Person oder der Beziehung in den Baum bringen koennen"*).

   **Ueber beiden Haelften, nicht in einer** - *ein Hochzeitsbild gehoert
   keinem der zwei allein*: `flex-basis: 100%` an einer Tafel mit
   `flex-wrap`, und am Handy, wo die Tafel eine Spalte ist, steht es
   ohnehin oben.

   **Eine feste Hoehe, keine Zeile in Regel 10** - wie `.ahnfoto` daneben
   (64px seit FAM-201): *Eine Merkmalsvariable mit viermal demselben Wert
   waere eine Zeile, die nichts entscheidet.* 88 statt 64, weil ein Paarbild
   die ganze Tafelbreite hat und bei 64 ein Streifen bliebe.

   **`object-fit: cover`**, aus demselben Grund wie beim Portrait: Ein
   verzerrtes Gesicht ist schlimmer als ein beschnittenes. */
.paarfoto {
  /* **Die Breite steht als `width`, nicht als `flex-basis`** - woertlich
     die Lehre von `.ahnkasten` daneben, und sie ist hier zum **sechsten**
     Mal zugeschlagen (docs/11-fallstricke.md): Mit `flex: 0 0 100%` war das
     Bild am Handy, wo die Tafel eine **Spalte** ist, gemessen **547,6 px**
     hoch statt 88 - *in einer Spalte ist die Basis die Hoehe.*

     **Den Umbruch in der Zeile erzwingt die Breite genauso**: Mit
     `flex-basis: auto` nimmt das Element seine `width`, und bei
     `flex-wrap: wrap` passt daneben nichts mehr. */
  flex: 0 0 auto;
  width: 100%;
  height: 88px;
  object-fit: cover;
  /* **Die Trennlinie zu den Namen** - dieselbe Farbe wie der Rahmen der
     Tafel, nicht `--linie`: *Zwei Striche in einem Kasten, und der eine ist
     heller als der andere, sehen aus wie ein Versehen.* */
  border-bottom: 1px solid var(--akzent);
}

/* Das Wort steht da, nicht nur eine Farbe (FAM-65). */
.ahnmarke {
  /* **Mittig im Kasten**, seit alles mittig steht (24.10.2026) - ein
     `flex-start` zoege die Marke an die linke Kante unter einen
     zentrierten Namen. */
  align-self: center;
  padding: 1px 6px;
  border-radius: 999px;
  /* **`--akzent-sanft` gab es nie** (gefunden am 24.10.2026): Der Name
     stand genau hier und in keinem `:root` - *eine unbekannte Variable
     macht die Eigenschaft ungueltig*, die Marke hatte also **keinen
     Grund**, und niemandem ist es aufgefallen.

     **Weiss und nicht blass**, und das ist der Fund des Durchgangs: Mit
     `--akzent-blass` trug sie dieselbe Farbe wie die Tafel unter ihr -
     *auf blass liegt blass*, also war die Pille wieder unsichtbar, nur
     diesmal mit einer gueltigen Zeile. Gemessen 4 von 6 Kaesten. */
  background: var(--flaeche);
  color: var(--akzent-dunkel);
  font-size: calc(var(--schrift) * 0.85);
}

/* ------------------------------------------------------------ die Liste */

/* **Eine Tafel traegt ein Paar** (02.10.2026) - die Vorlage entscheidet das:
   Auf `docs/standard-familienatammbaum.jpg` stehen auf einem Schild *zwei*
   Namen, nicht einer.

   **Ein Rahmen um beide, kein dritter Kasten**: Die beiden Kaesten darin
   behalten ihr Papier, die Tafel setzt nur die Klammer. */
.ahntafel {
  /* **Ein Paar steht nebeneinander** (02.10.2026, nach der Vorlage) - mit
     einem Strich dazwischen, und die Senkrechte zu den Kindern faellt aus
     seiner **Mitte**. Am Handy untereinander: Dort ist eine Karte so breit
     wie die Seite. */
  flex: 0 0 auto;
  /* **Am Handy ist die Tafel seitenbreit**, sonst so breit wie ihre Karten.
     Ohne diese Zeile schrumpft sie am Handy auf ihren Text, und gemessen
     standen dort fuenf verschieden breite Karten untereinander (324, 311,
     215, 169, 109 px). */
  width: var(--baum-tafel-breite);
  max-width: 100%;
  position: relative;
  display: flex;
  flex-direction: var(--baum-tafel-richtung);
  /* **Damit das Paarbild eine eigene Zeile bekommt** (24.10.2026): Es traegt
     `flex-basis: 100%`, und ohne Umbruch stuende es **neben** den beiden
     Haelften statt ueber ihnen. */
  flex-wrap: wrap;
  /* **Der Rand schneidet das Bild zu** - sonst steht seine quadratische Ecke
     ueber der runden der Tafel. */
  overflow: hidden;
  /* **Beide Haelften sind gleich hoch** (24.10.2026). Hier stand
     »verschieden hoch, und keine waechst mit« - richtig, solange jede
     ihren **eigenen** Rahmen hatte. Seit sie zwei Haelften **eines**
     Kastens sind, endete der Trenner dazwischen auf halber Strecke, sobald
     einer ein Portrait trug und der andere nicht: gemessen 236,4 px Tafel
     und ein Strich, der bei 154 aufhoerte. *Ein Strich, der mitten im
     Kasten aufhoert, sieht aus wie ein Fehler.* */
  align-items: stretch;
  /* **Keine Luecke mehr zwischen den beiden** (24.10.2026): Sie stehen in
     **einem** Kasten, und zwischen ihnen steht ein Strich statt einer
     Luft. */
  gap: 0;
  /* **Hell gefuellt mit duennem Rahmen in der Akzentfarbe** (24.10.2026,
     nach der Vorlage) - und es ist dieselbe Farbe, die die Marke im Kasten
     traegt: *keine neue Farbe fuer eine neue Form* (Regel 10).

     **Der Schatten faellt weg** (hier stand `--kartenschatten`): In der
     Vorlage liegt der Baum flach auf dem Papier - *ein Relief an dreissig
     Kaesten ist Unruhe, und die Linien dazwischen sind flach gezeichnet.* */
  border: 1px solid var(--akzent);
  border-radius: var(--radius);
  background: var(--akzent-blass);
}

/* **Der Name des Kastens** (24.10.2026, auf Wunsch: *„Der Name des Kastens
   sollte dann eine Überschrift bekommen wie Björn & Britta Wildenhain"*).

   **Über beiden Hälften, über die ganze Breite** - dieselbe Bauart wie das
   Paarbild darüber: `width: 100%` an einer Tafel mit `flex-wrap`, **nicht**
   `flex-basis` (in der Spalte am Handy wäre das die Höhe - die Falle zum
   sechsten Mal).

   **Nur bei einem Paar**: Bei einem Alleinstehenden gibt es keine - sie
   stünde genau über seinem eigenen Namen, und *dasselbe zweimal
   übereinander ist eine Zeile zu viel.* Das entscheidet
   `stammbaum.tafelnamen`, nicht dieses Blatt.

   **Keine neue Farbe und keine neue Zahl**: der Rand der Tafel, die
   Textfarbe daneben, `0.95em`. */
.ahntafelname {
  flex: 0 0 auto;
  width: 100%;
  /* **Die Ueberschrift liest die Breite ab, sie setzt sie nicht**
     (25.10.2026). Ohne diese Zeile ist eine Tafel so breit wie ihr
     laengster Name in **einer** Zeile - gemessen stand
     *„Anna Friderike Marie Doll & Friedr. Andreas Christoph Koehlitz"*
     ungebrochen da und machte den Kasten rund dreimal so breit wie seine
     beiden Haelften. **Danach passten zwei Tafeln in eine Reihe, und
     genau das war der Befund.**

     `contain: inline-size` heisst: *Die Breite dieses Kastens haengt
     nicht von seinem Inhalt ab* - er traegt damit **nichts** zur
     Mindestbreite der Tafel bei, nimmt seine 100 % von ihr und bricht
     darin um. Dieselbe Browsergrundlage wie `:has()` und `cqi`, die im
     Blatt ohnehin stehen. */
  contain: inline-size;
  margin: 0;
  padding: calc(var(--abstand) * 0.3);
  border-bottom: 1px solid var(--akzent);
  font-weight: 700;
  font-size: 0.95em;
  text-align: center;
  line-height: 1.2;
}

/* **Der Strich zwischen zweien** - er ersetzt den gezeichneten Paarstrich
   aus `stammbaum.js`, und das ist der Punkt: *Was das Blatt zeichnen kann,
   zeichnet kein Skript* (Regel 7). Er liegt **zwischen** den Haelften, also
   am Nachfolger - eine Linie vor der ersten waere ein Strich gegen nichts
   (woertlich die Menuebereiche, 17.10.2026).

   **Waagerecht, wo die Tafel eine Spalte ist** (`--baum-paar-trenner`,
   Regel 0): Am Handy stehen die beiden untereinander, und ein senkrechter
   Strich traefe dort nichts. */
/* **Die beiden Hälften teilen sich ihre Tafel** (25.10.2026, auf
   Rückmeldung: *„Der Kasten im Stammbaum hat scheinbar rechts ein wenig zu
   viel Platz bekommen, er wirkt nicht symetrisch"*).

   **Gemessen am Desktop** (1280 px, Blatt geladen): Die Tafel war
   **510,4 px** breit – so breit wie ihre Überschrift *„Bjoern & Britta
   Wildenhain"* (508,4) –, die beiden Hälften je **187** (`16ch`). Macht
   374 belegt und **136 px, die rechts liegen blieben**: Ein Flexkasten
   verteilt übrigen Platz nicht, er lässt ihn am Ende stehen.

   **Die Überschrift bestimmt die Breite, und das soll sie** - *ein Name,
   der umbricht, damit der Kasten schmal bleibt, ist der falsche Tausch.*
   Also wächst mit, was darin steht.

   **`flex-grow` und keine Breite**: `width` bleibt der **Boden**
   (`--baum-kasten-breite`, Regel 10) - zwei Hälften mit `50%` wären eine
   zweite Zahl neben ihr, und bei einer Tafel mit **einem** Menschen die
   falsche. */
.ahntafel > .ahnkasten {
  flex-grow: 1; /* die Haelften teilen den Platz ihrer Tafel */
}

.ahntafel > .ahnkasten + .ahnkasten {
  border-top: var(--baum-paar-trenner);
  border-left: var(--baum-paar-strich);
}

/* ------------------------------------------------- die Linien zwischen
   zwei Generationen (02.10.2026, auf Rueckmeldung: *„Linien zu den
   Eltern"*).

   **Drei Stuecke, keine gemessene Zahl**: ein Stummel nach unten an der
   oberen Reihe, eine Schiene in der Mitte des Abstands, ein Stummel nach
   oben an der unteren.

   **Die Schiene braucht keine Breitenrechnung.** Jede Tafel zeichnet ihr
   eigenes Stueck - ihre Breite plus die halbe Luecke nach beiden Seiten -,
   und die Stuecke der Nachbarn stossen in der Mitte der Luecke aneinander.
   Die erste faengt in ihrer Mitte an, die letzte hoert dort auf; *bei einer
   einzigen Tafel bleibt davon nichts uebrig, und das ist richtig so.*

   **Sie haengt an der Tafel, nicht an der Karte**: Ein Paar teilt sich eine
   Linie zu seinen Kindern - so steht es auf der Vorlage
   (`docs/standard-familienatammbaum.jpg`). */

/* **Die Linien zeichnet ein Skript** (02.10.2026, auf Auftrag: *„Benutze
   gerne JavaScript um die Optik hinzubekommen"*) -
   `app/static/js/stammbaum.js`.

   **Hier standen drei Anlaeufe mit reinem CSS**, und alle drei sind am
   selben Punkt gescheitert: *Eine Linie zwischen zwei Kaesten braucht die
   Lage beider.* Ein Blatt kennt sie nicht; es kann nur raten (*„eine halbe
   Kartenbreite vom Rand"*), und jede dieser Annahmen faellt um, sobald eine
   Tafel zwei Karten traegt, ein Name laenger ist oder eine Reihe umbricht.

   **Was hier bleibt, ist die Entscheidung** - ob ueberhaupt gezeichnet wird
   (`--baum-linie-anzeige`, Regel 0) und wie die Linie aussieht. *Im Skript
   steht keine Farbe und keine Media Query.* */
/* **Halb durchsichtig, was nicht dazugehört** (25.10.2026, auf Wunsch:
   *„erst wenn man einen Namen anklickt sollen die Elemente, die nicht zur
   Reihe gehören halb transparent werden"*).

   **Eine Regel für alles im Baum** - Tafel, Kasten und Linie: `.blass`
   steht am Element, und wer es setzt, entscheidet der **Server**
   (`baum.html`) bzw. das Skript für die Linien. *Drei Regeln mit derselben
   Zahl liefen auseinander.*

   **0.5 ist „halb transparent" und keine erfundene Zahl** - es steht
   wörtlich im Wunsch. Eine Merkmalsvariable dafür wäre eine Zeile in
   Regel 10, die in allen vier Ansichten dasselbe sagt.

   **Blass heißt nicht weg**: Der Name bleibt ein Verweis, die
   Trefferfläche bleibt dieselbe - *wer blass ist, ist nicht fort, sondern
   gerade nicht gemeint.* Und **Blässe liegt nie auf Blässe**: Eine Tafel
   wird nur blass, wenn keine ihrer Hälften dazugehört (`baum.html`) -
   zwei halbe Deckkräfte übereinander ergäben eine viertel. */
.baum .blass {
  opacity: 0.5;
}

.baumlinien {
  position: absolute;
  /* **Nicht `inset: 0`**: Die Groesse setzt das Skript, denn der Baum ist
     breiter als sein Kasten, sobald er scrollt (Regel 2). */
  top: 0;
  left: 0;
  /* Die Linien liegen UNTER den Karten (die haben ihre eigene Flaeche) und
     fangen keinen Zeiger ab. */
  z-index: 0;
  pointer-events: none;
  stroke: var(--text-leise);
  stroke-width: 2;
}

/* Was schon verbunden ist, in der Maske. */
/* **Die drei Fragen** (24.10.2026, auf Wunsch: *„Wer sind die Eltern, wer
   ist der Partner, wer sind die Kinder"*) - **und seit dem 25.10.2026
   klappt jede zu** (*„mach die vielen Felder lieber als multi anhak
   dropdowns"*).

   **Der Griff ist eine `.klappgruppe`** (`_klappgruppe.html`, 20.10.2026),
   kein eigener Aufklapper: Rahmen, Zeichen, Polster und die
   Zusammenfassung daneben stehen damit an **einer** Stelle, und Abschnitt
   17 trägt sie schon. *Das nächste Modul braucht eine Zeile, keine Kopie*
   (FAM-116).

   > **Hier stand ein `<fieldset>` mit einer `<legend>`** - mit der
   > Begründung, nur eine Legende gebe einer Kästchengruppe einen Namen,
   > den ein Vorleseprogramm mitliest. **Der Baustein löst dasselbe
   > anders**: `role="group"` und `aria-labelledby` auf den Griff. *Die
   > Zusage ist dieselbe, die Stelle ist eine weniger.*

   **Die Namen stehen nebeneinander und brechen um** - bei dreißig Menschen
   im Baum sind das mehrere Zeilen, und *eine Liste untereinander wäre bei
   dreißig eine Seite.* `.klappgruppeinhalt` ist eine **Spalte**, also
   braucht der Inhalt seinen eigenen Flexkasten. */
.verbindungsnamen {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--abstand);
}

.verbindungsnamen .ankreuz {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  /* **Die ganze Zeile ist die Trefferfläche**, nicht nur das Kästchen -
     ein `<label>` um beides, und 44 px sind der Boden (Abschnitt 15). */
  min-height: var(--knopf-flaeche);
}

/* **Die Verbindungen als Tabelle** (24.10.2026, auf Wunsch: *„einfach und
   knapp, vielleicht in einer Tabelle der Person"*).

   Hier stand eine Liste mit **einer Zeile je Verbindung** und einem
   Wortknopf *Lösen* daneben - bei zwei Kindern und zwei Eltern vier Zeilen,
   und *die Knöpfe waren mehr Fläche als die Namen.*

   **Je Art eine Zeile**, und die Namen stehen nebeneinander. */
.ahnkantentabelle {
  margin-top: var(--abstand);
  border-collapse: collapse;
  /* **So breit wie die Maske**, damit die zweite Spalte den Rest bekommt. */
  width: 100%;
}

.ahnkantentabelle th {
  /* **Die Art steht links und schmal** - sie ist die Beschriftung, nicht
     die Auskunft. */
  width: 1%;
  white-space: nowrap;
  padding: 4px calc(var(--abstand) * 0.6) 4px 0;
  text-align: left;
  vertical-align: baseline;
  font-weight: 400;
  color: var(--text-leise);
  font-size: calc(var(--schrift) * 0.9);
}

.ahnkantentabelle td {
  padding: 4px 0;
  vertical-align: baseline;
}

/* **Keine Linien zwischen den Zeilen** - drei Zeilen brauchen kein Gitter,
   und *ein Rahmen um drei Namen ist mehr Gestaltung als Auskunft.* */
.ahnkante {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  /* Trennt zwei Namen derselben Art, ohne ein Komma zu erfinden. */
  margin-inline-end: calc(var(--abstand) * 0.8);
}

/* **Das Kreuz ist leise, bis man es anfasst** - es steht viermal da, wo
   vorher viermal das Wort *Lösen* stand. */
.ahnkantentabelle .zeichenknopf {
  color: var(--text-leise);
  border-color: transparent;
}
.ahnkantentabelle .zeichenknopf:hover,
.ahnkantentabelle .zeichenknopf:focus-visible { color: var(--fehler); }

/* **Eng, weil die Liste lang ist** (25.10.2026, auf einen Kommentar im
   Canvas: *„Die Abstaende zwischen den Personen verkleinern."*).

   **Gemessen am Handy** (375er Rahmen, Blatt geladen): Eine Zeile wog
   56 px bei 10 px Abstand - **66 px je Mensch**, und bei vierzig
   Vorfahren sind das 2640. Jetzt traegt die Zeile ihre Hoehe allein aus
   dem Griff (44 px, Abschnitt 15), der Abstand ist **0,4 statt 1,0**
   `--abstand`.

   **Die Trefferflaeche bleibt**, und das ist die Grenze dieser Aenderung:
   Gespart wird am Polster, nicht am Griff - *ein Name, den man nicht mehr
   trifft, ist keine kuerzere Liste, sondern eine unbedienbare.* */
/* **Die Suche steht über der Liste** (25.10.2026, auf Wunsch: *„nur durch
   scrollen ist es nicht praktikabel"*).

   **Das Feld nimmt die Zeile, die Knöpfe stehen daneben** - und bricht die
   Zeile um, rutschen sie darunter. `min-width: 0` gehört dazu: Ohne sie
   schrumpft ein Flex-Element nicht unter seinen Mindestinhalt und hängt
   aus der Spalte (`11-fallstricke.md`). */
/* **Das kleine Wort neben dem Datum** (25.10.2026, auf Wunsch: *„gestern /
   heute, morgen sollte wenn ein wenig kleiner geschrieben werden"*).

   **0.85em und keine feste Zahl**: Es steht in Kopfzeilen, in Listen und auf
   Zetteln, und ueberall soll es ein wenig kleiner sein als das, was daneben
   steht - *eine Zahl in Pixeln waere in der einen Zeile klein und in der
   anderen gross.*

   **Und leise, nicht bunt**: Das Datum traegt die Auskunft, das Wort ordnet
   sie ein. `--text-leise` ist derselbe Ton, den auch die Lebensdaten im Baum
   und der Stand einer Klappgruppe tragen. */
/* **Das `+` an einem Tag** (25.10.2026, auf Wunsch: *„würde ich gerne
   unten an einem Tag ein + Termin Button haben, um direkt in den Tag einen
   Termin zu schreiben"*).

   **Leise, bis man ihn sucht**: Er steht an **jedem** der sieben Tage, und
   *sieben kräftige Knöpfe nebeneinander sind eine Werkzeugleiste, kein
   Kalender.* Beim Überfahren und beim Fokus trägt er den Akzent.

   **Die Trefferfläche bleibt 44 px** (Abschnitt 15) - das Zeichen ist
   klein, der Knopf ist es nicht. */
.tagneu {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: var(--knopf-flaeche);
  min-width: var(--knopf-flaeche);

  /* **Ein Knopf, kein Balken** (05.11.2026, gemeldet vom Gerät: *„Das
     Pluszeichen stimmt nicht mehr."*).

     Ein `<a>` auf Blockebene ist so breit wie sein Tag - gemessen am Handy
     **379 × 44 px**, und das Zeichen schwebte in der Mitte einer leeren
     Fläche. *Ein Zeichen in der Mitte eines leeren Kastens sieht aus wie
     ein Platzhalter, nicht wie ein Knopf.*

     **Die Trefferfläche bleibt 44 px** (Abschnitt 15) - geändert ist die
     Breite, nicht der Boden. Und er steht **am Ende der Zeile**: *Was
     wechselt, gehört ans Ende* (17.10.2026) - ein Tag mit Terminen und
     einer ohne haben den Knopf damit an derselben Stelle. */
  width: var(--knopf-flaeche);
  margin-left: auto;
  color: var(--text-leise);
  text-decoration: none;
  font-size: 1.2em;
  line-height: 1;
}

.tagneu:hover,
.tagneu:focus-visible { color: var(--akzent-dunkel); }

/* **Im Monat steht er neben der Zahl** und darf dort nicht die halbe Zelle
   nehmen: Die Tageszeile ist ein Raster aus sieben Spalten, und *ein
   quadratischer 44er Knopf in einer 50-px-Spalte liesse der Zahl nichts.*
   Die Trefferflaeche bleibt in der **Hoehe**, die Breite gibt der Inhalt. */
.monatneu {
  min-width: 0;
  padding-inline: 2px;
}

.tagnaehe {
  font-size: 0.85em;
  color: var(--text-leise);
}

.ahnsuche {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.6);
  margin-bottom: var(--abstand);
}

.ahnsuche input {
  flex: 1 1 14ch;
  min-width: 0;
  /* **44 px sind der Boden, auch fuer ein Feld** (Abschnitt 15): Gemessen
     war es 30 px hoch neben einem 44 px hohen Knopf - *ein Suchfeld, das
     man mit dem Finger verfehlt, loest das Problem nicht, das es loesen
     soll.* */
  min-height: var(--knopf-flaeche);
}

.ahnliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.4);
}

.ahnzeile {
  /* Senkrecht kein Polster - die 44 px des Griffs sind die Hoehe. */
  padding: 0 calc(var(--abstand) * 0.6);
  border-radius: var(--radius-karte);
  background: var(--flaeche);
  box-shadow: var(--schatten);
}

/* **Die offene Zeile bekommt ihre Luft zurueck** - sonst klebte das
   Formular an der Unterkante der Karte. */
.ahnzeile:has(.ahnmaske[open]) {
  padding-bottom: calc(var(--abstand) * 0.6);
}

/* **Der Kopf einer Zeile ist der Griff ihres Aufklappers** (25.10.2026,
   auf einen Kommentar im Canvas: *„die Buttons löschen und Ändern auslösen,
   wenn man auf den Namen klickt"*).

   **`list-style: none` steht ausdrücklich da** - ein `<summary>` trägt sonst
   das Dreieck des Browsers, und das ist auf jedem Gerät ein anderes
   (dieselbe Entscheidung wie an der Klappgruppe und am Menü). */
.ahnkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: calc(var(--abstand) * 0.6);
}

summary.ahnkopf {
  cursor: pointer;
  list-style: none;
  /* **Die ganze Zeile ist die Trefferfläche** - 44 px sind der Boden
     (Abschnitt 15), und der Name bringt sie ohnehin mit. */
  min-height: var(--knopf-flaeche);
  align-items: center;
}
summary.ahnkopf::-webkit-details-marker { display: none; }

/* **Das Wort steht rechts und leise** - es sagt, was der Tipp tut, und
   nimmt dem Namen nicht die Aufmerksamkeit. `margin-inline-start: auto`
   schiebt es ans Ende der Zeile; bricht sie um, steht es in der nächsten.

   **Kein Textmarker** (Abschnitt 15): Das ist kein Wortknopf, sondern die
   Beschriftung eines Griffs - *ein Strich unter einem Namen sähe aus wie
   ein Verweis.* */
.ahnkopfwort {
  margin-inline-start: auto;
  color: var(--text-leise);
  font-size: 0.9em;
}

/* **Der Weg in den Baum steht in der offenen Maske** - er hing bis zum
   25.10.2026 am Namen, und der ist jetzt der Griff. */
.ahnbaumweg {
  margin: 0 0 var(--abstand);
}

/* **Ein Verstorbener ist abgesetzt, nicht weggelassen** - woertlich die
   Loesung des abgelaufenen Stundenplans (FAM-190): *Ein stilles Weglassen
   waere ein Verlust, den niemand bemerkt.* */
.ahnzeile.verstorben .ahnname {
  color: var(--text-leise);
}

/* Die drei Zahlen eines Datums stehen in einer Zeile, solange Platz ist. */
.datumsfelder {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--abstand) * 0.6);
  align-items: flex-end;
}

/* **Hier stand `.datumsfelder .feld { flex: 1 1 10ch }`, und die Wache hat es
   sofort gemeldet** (`test_die_breitenregel_gilt_fuer_jede_maske`): *Eine
   Regel hinter einer fremden Klasse hilft der naechsten Maske nicht* -
   woertlich der Fund von FAM-108, wo die Terminmaske 194 von 343 px breit
   blieb, weil die Regel an `.umfragemaske` hing.
   Die Breite der drei Felder kommt deshalb aus `--feld-eingabe-basis` wie
   ueberall, und dieser Kasten entscheidet nur, dass sie **umbrechen**
   duerfen. */

.ahndaten {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 2px calc(var(--abstand) * 0.8);
  margin: var(--abstand) 0 0;
}

.ahndaten dt {
  font-weight: 700;
  color: var(--text-leise);
}

.ahndaten dd {
  margin: 0;
}

.ahnnotiz {
  margin-top: var(--abstand);
}


/* ===========================================================================
   Module an- und abschalten (FAM-203)
   ---------------------------------------------------------------------------
   **Dieselbe Bauart wie die Zeilen des Projektbretts** (`.zeilenliste`,
   FAM-176): eine Liste benannter Zeilen mit einer Aktion rechts. *Zwei
   Bauarten fuer dieselbe Sache laufen auseinander* (FAM-52, FAM-117) - und
   diese hier braucht deshalb **keine neue Merkmalsvariable**: Abstand,
   Radius, Flaeche und Mindesthoehe kommen aus den vorhandenen (Regel 0).

   **Gebaut nach dem Ansichtsdurchgang**, nicht davor: Bis dahin gab es den
   Block nicht, und der Browser hat seine Voreinstellung gesetzt -
   Aufzaehlungspunkte und 40 px Einzug. *Eine fehlende Regel greift die
   Browservoreinstellung* (`11-fallstricke.md`), und die Suite sieht davon
   nichts.
   =========================================================================== */
.modulliste {
  list-style: none;
  margin: var(--abstand) 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
}

/* **`wrap`** - eine Zeile, die ihre Aktion abschneidet, ist bei keiner
   Scrollposition erreichbar (FAM-50, FAM-84). */
.modulzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  min-height: var(--schaltflaeche);
  padding: calc(var(--abstand) * 0.5) var(--abstand);
  border-radius: var(--radius);
  background: var(--flaeche);
  box-shadow: 0 0 0 1px var(--linie) inset;
}

/* Der Name nimmt den Platz, der uebrig ist - **`min-width: 0`**, sonst
   schiebt ein langes Wort den Knopf aus der Zeile. */
.modulwort {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 700;
}

/* **Das Wort, nicht nur eine Farbe** (FAM-65). Es steht vor dem Knopf, weil
   es den **Zustand** nennt und der Knopf das **Ziel**. */
.modulzustand {
  flex: 0 0 auto;
  color: var(--text-leise);
}

/* Ein abgeschaltetes Modul steht leiser da - **die Zugabe zum Wort**, nicht
   sein Ersatz. */
.modulzeile.aus .modulwort {
  color: var(--text-leise);
  font-weight: 400;
}

.modulzeile form {
  flex: 0 0 auto;
  margin: 0;
}


/* ===========================================================================
   Geschenkewuensche (Epic FAM-206)
   ---------------------------------------------------------------------------
   **Dieselbe Bauart wie die Zeilen des Projektbretts** (`.zeilenliste`,
   FAM-176) und die Modulliste (FAM-203): eine Liste benannter Zeilen mit
   einer Aktion rechts.

   **Keine neue Merkmalsvariable** (Regel 0): Abstand, Radius, Flaeche und
   Mindesthoehe kommen aus den vorhandenen.
   =========================================================================== */
.wunschliste,
.ereignisliste,
.einladungsliste,
.gastlinkliste {
  list-style: none;
  margin: var(--abstand) 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
}

/* **`wrap`** - eine Zeile, die ihre Aktion abschneidet, ist bei keiner
   Scrollposition erreichbar (FAM-50, FAM-84). */
.wunschzeile,
.ereigniszeile,
.einladungszeile,
.gastlinkzeile {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px var(--abstand);
  min-height: var(--schaltflaeche);
  padding: calc(var(--abstand) * 0.5) var(--abstand);
  border-radius: var(--radius);
  background: var(--flaeche);
  box-shadow: 0 0 0 1px var(--linie) inset;
}

/* Der Text nimmt den Platz, der uebrig ist - **`min-width: 0`**, sonst
   schiebt ein langer Wunsch die Knoepfe aus der Zeile. */
.wunschwort,
.ereigniswort {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px calc(var(--abstand) * 0.6);
}

.wunschtitel,
.ereignistitel {
  font-weight: 700;
}

/* Wessen Wunsch das ist - **in der Liste der anderen** (FAM-212). */
.wunschname,
.einladungsname,
.gastlinkname {
  font-weight: 700;
  color: var(--text-leise);
}

.wunschmarken {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* Kategorie, Preis, Prio - **klein und leise**, wie die Marken am
   Projektbrett. */
.wunschmarke {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--grund);
  box-shadow: 0 0 0 1px var(--linie) inset;
  font-size: 0.86em;
  color: var(--text-leise);
}

/* **Die Rangfolge ist die Schriftstaerke, nicht eine Farbe** (FAM-65):
   *unbedingt* steht fett da, *vielleicht* leise. Eine dritte Farbe im Blatt
   waere eine Bedeutung mehr, die jemand lernen muss. */
.wunschmarke.prio-unbedingt { font-weight: 700; color: var(--text); }
.wunschmarke.prio-vielleicht { opacity: 0.75; }

/* **Wer es genommen hat** - und das Wort steht da, nicht nur ein Strich. */
.wunschmarke.geschenkt {
  background: var(--flaeche);
  color: var(--text);
}

/* **Gestrichen heisst gestrichen** - und es trifft nur den Titel, nicht die
   Marke daneben: *„geschenkt von Oma" durchzustreichen hiesse, auch diese
   Auskunft zurueckzunehmen.* */
.wunschzeile.gestrichen .wunschtitel {
  text-decoration: line-through;
  color: var(--text-leise);
}

.wunschlink {
  font-size: 0.9em;
}

.wunschgriffe,
.ereignisgriffe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.4);
  flex: 0 0 auto;
}

.wunschgriffe form,
.ereignisgriffe form,
.einladungszeile form,
.gastlinkzeile form {
  margin: 0;
}

/* Eine Zeile, die gerade geaendert wird, nimmt die ganze Breite. */
.wunschzeile.offen,
.ereigniszeile.offen {
  display: block;
}

/* **`.gastlinkwort` ist am 04.10.2026 zu `.gastlinkfrist` geworden** - aus
   dem Wort *„gilt nicht mehr"* ist ein Satz geworden, der auch am lebenden
   Link steht: *gilt noch 12 Tage (bis 26.12.2026)*. Dieselbe Marke, mehr
   Auskunft. */
.ereigniszustand,
.gastlinkfrist {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--grund);
  box-shadow: 0 0 0 1px var(--linie) inset;
  font-size: 0.86em;
}

/* **Sie darf nicht mitten im Wort brechen** - „gilt noch 12 / Tage" waere
   an einer Marke schlimmer als eine zweite Zeile darunter. */
.gastlinkfrist { white-space: nowrap; }

/* **Das laufende Ereignis hebt sich ab** - dieselbe Farbe wie die Spalte
   *In Arbeit* am Projektbrett (`--laeuft`, FAM-175), **und daneben steht
   das Wort**. */
.ereigniszeile.zustand-laeuft .ereigniszustand {
  background: var(--laeuft);
}

.ereigniszeile.zustand-vorbei {
  opacity: 0.8;
}

.ereigniszeitraum {
  color: var(--text-leise);
  font-size: 0.9em;
}

/* Die vier Bloecke einer Ereignismaske. */
.ereignisblock {
  margin-top: calc(var(--abstand) * 1.5);
}

.ereignisblock > h2 {
  margin-bottom: calc(var(--abstand) * 0.5);
}

/* Die Adresse eines Gastlinks - **sie darf umbrechen**: 43 Zufallszeichen
   passen in keine Handybreite, und *ein Kasten, der seitwaerts scrollt, ist
   einer, aus dem man nicht kopiert*. */
.gastlinkadresse {
  flex: 1 1 100%;
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: 0.8em;
  color: var(--text-leise);
}

.gastlinkzeile.abgestellt {
  opacity: 0.7;
}

/* Die beiden Formulare unter den Bloecken stehen wie eine Eingabemaske,
   nur ohne eigenen Kasten. */
.einladen,
.gastlink-neu,
.zuordnen,
.zuordnen-alle {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: calc(var(--abstand) * 0.6);
  margin-top: var(--abstand);
}

/* **Das Bild am Wunsch** (03.10.2026, auf Zuruf) - der Fall, den FAM-120
   angekuendigt und FAM-207 vertagt hat.

   **Keine neue Merkmalsvariable** (Regel 0): Die Vorschau ist so hoch wie
   ein Knopf - *ein Bild neben einem Wunsch ist so gross wie das, was
   danebensteht.* */
.wunschbilder {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 4px;
  vertical-align: middle;
}

/* **Ein Quadrat, keine freie Breite** - und das ist gemessen (03.10.2026).
   Mit `width: auto` reserviert ein Bild mit `loading="lazy"` **gar keinen
   Platz**, solange es nicht geladen ist: gemessen 0 x 36 px, und die Zeile
   springt, sobald es ankommt.

   `object-fit: cover` schneidet dafuer zu - *bei einer Vorschau von 36 px
   ist das Bild ohnehin ein Hinweis und kein Bild.* */
.wunschbild {
  display: block;
  height: var(--schaltflaeche);
  width: var(--schaltflaeche);
  border-radius: calc(var(--radius) * 0.5);
  object-fit: cover;
  box-shadow: 0 0 0 1px var(--linie);
}


/* --- Meldungen auf dem Handy (FAM-221) ----------------------------------- */

/* **Dieselbe Bauart wie die Gastlinkliste** - eine Zeile je Geraet, der
   Zustand als Marke daneben. *Das naechste Modul braucht eine Zeile, keine
   Kopie* (FAM-116). */
.pushliste { list-style: none; margin: 0; padding: 0; }

.pushzeile {
  display: flex;
  flex-wrap: var(--aufgabe-umbruch);
  align-items: center;
  gap: 6px var(--abstand);
  padding: calc(var(--abstand) * 0.5) 0;
  border-bottom: 1px solid var(--linie);
}

/* **Ein abgemeldetes Geraet steht gedaempft da, nicht in Rot.** Rot gehoert
   dem Loeschen (FAM-179) - *zwei Bedeutungen fuer eine Farbe kosten bei jeder
   Karte Zeit* (FAM-122). */
.pushzeile.abgestellt { color: var(--text-leise); }

.pushname { font-weight: 700; }

.pushzeit { color: var(--text-leise); font-size: 0.9em; }

/* **Nicht mitten im Wort brechen** - dieselbe Entscheidung wie bei der
   Gastlinkfrist (04.10.2026). */
.pushwort {
  padding: 2px 10px;
  border-radius: 999px;
  background: var(--grund);
  box-shadow: 0 0 0 1px var(--linie) inset;
  font-size: 0.86em;
  white-space: nowrap;
}

/* **Der Platz, den das Skript fuellt** (Regel 7). Er traegt keinen Rahmen und
   keine Hoehe: *Ein leerer Kasten, der aussieht wie ein Knopf, ist schlimmer
   als gar keiner* - und er bleibt leer, wo Push nicht geht. */
.pushplatz { margin-bottom: var(--abstand); }

/* Das Abmelden steht am Ende der Zeile, wie das Wegnehmen ueberall. */
.pushzeile form { margin-left: auto; }

/* ===========================================================================
   Das Wetter - sieben Kinderbilder (FAM-230, umgebaut am 10.10.2026)
   ===========================================================================

   *„Jeder Tag soll angezeigt werden, wie eine schoene Kinderzeichnung mit
   einer lustigen Sonne, wenn Sonnenschein angesagt ist, oder mit Regen, so
   in blau, Regentropfen gezeichnet auf dem Kinderbild. Diese sieben Bilder
   sollen untereinander stehen, und oben links in der Ecke vom Bild steht
   dann der Wochentag mit Datum."*

   **Hier stand bis dahin eine grosse Karte und darunter ein Gitter aus
   sieben Kaesten.** Beides ist weg; auf Rueckfrage: *„Die alte Anzeige wird
   oben links im Kinderbild angezeigt."*

   **Drei Merkmale sind weggefallen** - `--wetter-zeichen`,
   `--wetter-richtung` und `--wetterwoche-zeichen`. Sie massen die
   Strichzeichen und die Richtung der Karte; *eine Regel ohne Leser ist
   keine Reserve.* Dazu ein neues: `--wetterbild-hoehe`.

   **`--wetterwoche-spalten` bleibt und traegt jetzt `1fr`** - in allen vier
   Ansichten, auf Rueckfrage ausdruecklich auch am Desktop und am Infoboard.
   *Die Variable bleibt, weil die Frage bleibt*: wie viele nebeneinander.
   =========================================================================== */

.wetterraum { container-type: inline-size; }

.wetterwoche {
  display: grid;
  grid-template-columns: var(--wetterwoche-spalten);
  gap: var(--abstand);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* **Ein Bild ist eine Kachel mit fester Hoehe**, keine mit festem
   Seitenverhaeltnis: Der Inhalt ist am Handy 343 px breit und am Infoboard
   1840 - ein 16:9-Bild waere dort 1035 px hoch, und man saehe eines statt
   sieben. Die Hoehe steht je Ansicht in Regel 10, die Zeichnung fuellt sie
   (`slice`).

   **Und wo Platz ist, steht der Zettel DANEBEN** (10.10.2026, auf einen
   Kommentar im Canvas: *„Die Bilder sollen zentriert sein und wenn genug
   Platz ist soll der Kasten mit den Temperatur Informationen links neben
   dem Bild stehen."*).

   **`row-reverse`, kein `order`**: Im Markup steht das Bild zuerst, weil es
   im Normalfall der Grund ist, auf dem der Zettel liegt - *und ein
   absolutes Element, das VOR seinem Grund im Dokument steht, wird von ihm
   uebermalt.* In der Zeile dreht `row-reverse` die beiden um, ohne dass das
   Dokument sich aendert.

   **Zentriert mit `width: 100%` davor** - und das ist die Falle aus
   `11-fallstricke.md` ein viertes Mal, nur unter anderem Namen: Ein
   `justify-self: center` am Rasterkind schaltet das **Strecken** ab wie ein
   Auto-Rand, und gemessen war das Bild danach **0 px breit** (die Kachel
   schrumpfte von 770 auf 274, die Breite des Zettels). *Wer zentriert,
   setzt eine Breite* - hier `width: 100%` gegen die Rasterspalte, gedeckelt
   von `max-width`. */
.wettertag {
  position: relative;
  display: var(--wettertag-anzeige);
  flex-direction: row-reverse;
  width: 100%;
  margin-inline: auto;
  /* **Kein Grund** (12.10.2026, auf einen Kommentar im Canvas: *„Den
     hintergrund transparent machen"*, am Desktop-Zettel).

     **Der Zettel selbst war schon transparent** (10.10.2026) - zu sehen war
     der Grund der Kachel DARUNTER. Am Handy und am Tablet faellt das nicht
     auf, weil der Zettel dort auf dem Bild liegt und das Bild die Kachel
     fuellt; **ab Desktop steht er daneben**, in einer eigenen Spalte
     (`--wetterecke-spalte`) - und die war weiss.

     **Gerechnet, nicht angenommen** (die Lehre vom 10.10.): Auf `--grund`
     traegt `--text` **16,04 : 1**, `--text-leise` **5,92** und
     `--wetterecke-leise` **7,03** - alle drei ueber den 4,5 von WCAG AA.
     Die Gegenprobe der Rechnung ist Schwarz auf Weiss mit **21,00**, und
     sie trifft die 17,74 wieder, die oben schon dastanden. */
  background: transparent;
  height: var(--wetterbild-hoehe);
  /* **Eine Kachel ist nicht beliebig breit** (10.10.2026, gemessen).

     Ohne Deckel ist sie so breit wie der Inhalt: am Handy 343, am Desktop
     **960**, am Infoboard **1840** - bei 190 bzw. 280 px Hoehe ein
     Verhaeltnis von **5,1** und **6,6 zu eins**. Das Motiv behaelt darin
     sein Verhaeltnis (1,67) und steht als kleine Insel in einer breiten
     Bahn Himmel.

     **Mit dem Deckel liegen alle vier nah beieinander**: 2,3 am Handy,
     2,5 am Tablet, 2,7 am Desktop und am Infoboard. */
  max-width: var(--wetterbild-breite);
  border-radius: var(--radius-karte);
  overflow: hidden;
  box-shadow: var(--kartenschatten);
}

/* **Heute traegt einen Rahmen** - und das Wort *Heute* steht ohnehin in der
   Ecke: *ein Zeichen allein traegt die Aussage nicht* (FAM-65).

   **Innen statt aussen** (`inset`): Ein gewoehnlicher Rahmen laege unter dem
   Bild, das die Kachel randlos fuellt. */
.wettertag.wettertag-heute {
  box-shadow: var(--kartenschatten), inset 0 0 0 3px var(--akzent);
}

/* **Zwei Ebenen, und nur eine darf sich dehnen** (10.10.2026, gemessen).

   Der Grund (Himmel und Wiese) fuellt die Kachel und wird dabei gedehnt;
   das Motiv darueber behaelt sein Verhaeltnis und steht mittig. **Gemessen
   war der Unterschied ein Fetzen**: Mit einem einzigen `slice`-Bild zoomte
   die Zeichnung am Desktop (960 x 190) auf das **4,8fache**. */
/* **In der Zeile nimmt das Bild, was der Zettel uebrig laesst** - `flex: 1`
   teilt dort die BREITE (in einer Spalte waere es die Hoehe,
   `11-fallstricke.md`, fuenfmal). `min-width: 0` daneben: Ohne das schrumpft
   ein Flexkind nicht unter seinen Mindestinhalt. */
.wetterbild {
  position: relative;
  width: 100%;
  height: 100%;
  flex: 1 1 auto;
  min-width: 0;
}
.wetterbild-grund,
.wetterbild-baum,
.wetterbild-motiv,
.wetterbild-wind {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* **Die Sonnenzeiten in der Unterzeile** (11.10.2026, auf Wunsch: *„klein
   mit Symbol"*). **Klein heisst hier 0.85em**, nicht eine eigene Zahl je
   Ansicht: Die Unterzeile traegt ihre Groesse schon, und *eine zweite Zahl
   fuer dieselbe Zeile liefe auseinander* (Regel 10 bleibt unberuehrt).

   **Zeichen und Uhrzeit gehoeren zusammen** - `inline-flex` und ein kleiner
   Abstand, damit kein Umbruch zwischen Sonne und Zeit faellt. */
.wettersonne {
  display: inline-flex;
  align-items: center;
  gap: 0.25em;
  font-size: 0.85em;
}

/* --- Die Ecke oben links -------------------------------------------------

   **Sie liegt im Bild, nicht darueber** - der Zettel, den ein Kind in die
   Ecke seiner Zeichnung schreibt.

   **Ohne Grund** (10.10.2026, auf einen Kommentar im Canvas: *„Hintergrund
   bitte transparent machen"*). Hier stand bis dahin ein Grund aus
   `--flaeche` zu 86 %, mit der Begruendung *„ueber einem blauen Himmel ist
   dunkler Text ohne Grund nicht zu lesen"* - **und das war eine Annahme,
   keine Messung.** Gerechnet steht `--text` auf dem Himmel bei **13,66 : 1**
   und auf dem trueben bei **11,99**; auf der Flaeche waren es 17,74.

   **Durchgefallen ist nur die leise Schrift**, und knapp: 5,04 auf dem
   Himmel - aber **4,42 auf dem trueben**, und WCAG AA verlangt 4,5. *Vier
   der sieben Bilder sind trueb* (Regen, Gewitter, Schnee, Nebel). Deshalb
   gibt es `--wetterecke-leise`: dasselbe Blau bei 44 % statt 48 %, gerechnet
   **5,99** und **5,26**.

   **`--text-leise` selbst bleibt, wie es ist.** Es steht in der ganzen
   Anwendung auf `--flaeche`, und dort traegt es mit 6,55. *Eine Farbe fuer
   ein Modul dunkler zu machen heisst, sie fuer jedes andere zu aendern.*

   **Der Baum beginnt deshalb unter dem Zettel** (y = 74 von 120): Ohne
   Grund steht der Text unmittelbar auf dem Bild, und dunkler Text auf
   dunkelgruenem Laub waere genau der Fall, den die alte Begruendung
   meinte. */
.wetterecke {
  position: var(--wetterecke-lage);
  top: 0;
  left: 0;
  flex: 0 0 var(--wetterecke-spalte);
  margin: calc(var(--abstand) * 0.6);
  padding: calc(var(--abstand) * 0.4) calc(var(--abstand) * 0.7);
  border-radius: var(--radius);
  background: transparent;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.wetterecke p { margin: 0; }

.wetterecketag { font-weight: 700; }

.wetterecke-werte {
  display: flex;
  align-items: baseline;
  gap: 0.4em;
}

/* Der Wert fuer **jetzt** - die alte Karte, auf einen Wert geschrumpft. */
.wettergrad {
  font-size: var(--wetter-grad);
  font-weight: 700;
  line-height: 1;
}
.wetterjetzt { color: var(--wetterecke-leise); font-size: 0.9em; }

.wetterecke-wort { font-size: var(--wetter-wort); }

/* **Der Hoechstwert steht vorn und fett, der Tiefstwert leise dahinter** -
   *die Frage lautet „wie warm wird es", nicht „wie kalt war die Nacht".* */
.wettertaggrade { font-weight: 700; }
.wettertagmin { font-weight: 400; color: var(--wetterecke-leise); }
.wettertaggrade.wetter-ohne,
.wettertaggrade .wetter-ohne { color: var(--wetterecke-leise); font-weight: 400; }

/* **Der Wind steht unter dem Wort, nicht dahinter** (10.10.2026, auf einen
   Kommentar im Canvas: *„Wind soll viel kleiner und wind unter Regen bzw.
   Gewitter"*). Eine eigene Zeile und eine eigene Groesse - **eng gesetzt**
   (1.2 statt 1.5), denn jede Zeile im Zettel drueckt den Baum tiefer. */
.wetterwind {
  color: var(--wetterecke-leise);
  font-size: var(--wetter-wind-wort);
  line-height: 1.2;
}

.wetteralter { margin: 0; color: var(--text-leise); font-size: 0.85em; }

.wetterquelle { margin-top: var(--abstand); color: var(--text-leise); font-size: 0.85em; }

/* --- Die Zeichnungen -----------------------------------------------------

   **Die Farben stehen in Abschnitt 1**, die Zeichnung traegt nur Klassen
   (Regel 10): *Eine Farbe im Markup waere ein zweiter Ort fuer die
   Farbrichtung.*

   **Ein Praesentationsattribut nimmt kein `var()`** - `fill="var(--x)"` im
   SVG bliebe wirkungslos, und zwar lautlos. Deshalb steht hier jede
   Fuellung und jeder Strich. */
.wetterbild-himmel { fill: var(--wetterbild-himmel); }
.wetterbild-himmel.wetterbild-himmel-trueb { fill: var(--wetterbild-himmel-trueb); }
.wetterbild-wiese { fill: var(--wetterbild-wiese); }

.wetterbild-sonne {
  fill: var(--wetterbild-sonne);
  stroke: var(--wetterbild-sonne-rand);
  stroke-width: 3;
}
.wetterbild-strahl path {
  fill: none;
  stroke: var(--wetterbild-sonne-rand);
  stroke-width: 5;
  stroke-linecap: round;
}
/* Zwei Augen und ein Lachen - **mit der Tinte des Textes**, nicht schwarz. */
.wetterbild-gesicht path {
  fill: none;
  stroke: var(--wetterbild-stift);
  stroke-width: 3.5;
  stroke-linecap: round;
}

.wetterbild-wolke {
  fill: var(--wetterbild-wolke);
  stroke: var(--wetterbild-stift);
  stroke-width: 3;
  stroke-linejoin: round;
}
.wetterbild-wolke.wetterbild-wolke-trueb { fill: var(--wetterbild-wolke-trueb); }

/* **Regentropfen, in blau** - woertlich der Wunsch. */
.wetterbild-tropfen path {
  fill: var(--wetterbild-tropfen);
  stroke: var(--wetterbild-tropfen);
  stroke-width: 2;
  stroke-linejoin: round;
}

.wetterbild-blitz {
  fill: var(--wetterbild-blitz);
  stroke: var(--wetterbild-sonne-rand);
  stroke-width: 2.5;
  stroke-linejoin: round;
}

.wetterbild-flocke path {
  fill: none;
  stroke: var(--wetterbild-schnee);
  stroke-width: 3;
  stroke-linecap: round;
}

/* **Der Wind weht ueber dem Motiv** (10.10.2026, auf Wunsch) - drei Striche
   mit einem Haken, fast weiss und halb durchscheinend: *Eine Boee ist eine
   Flaeche, kein Gegenstand.* */
/* --- Der Baum ------------------------------------------------------------

   **Eine Variable je Jahreszeit, nicht eine Regel je Teil** (10.10.2026):
   `--laub` steht am Baum, und Krone wie fliegende Blaetter lesen sie. *Zwei
   Regeln fuer dieselbe Farbe liefen auseinander, sobald jemand den Herbst
   waermer macht.*

   **Der Winter hat keine** - dort gibt es keine Krone, nur Stamm und Aeste. */
.wetterbild-baum               { --laub: var(--wetterbild-laub-sommer); }
.wetterbild-baum-fruehling     { --laub: var(--wetterbild-laub-fruehling); }
.wetterbild-baum-herbst        { --laub: var(--wetterbild-laub-herbst); }

.wetterbild-stamm,
.wetterbild-ast path {
  fill: none;
  stroke: var(--wetterbild-stamm);
  stroke-linecap: round;
}
.wetterbild-stamm   { stroke-width: 4; }
.wetterbild-ast path { stroke-width: 3; }

/* **Der Stamm ist eine Flaeche, kein Strich** (10.10.2026, nachgebessert:
   *„natuerlicher wirken lassen"*). Ein Strich hat ueberall dieselbe Dicke;
   ein Baum ist unten breiter als oben - und am Fuss laeuft er auseinander. */
.wetterbild-stammflaeche { fill: var(--wetterbild-stamm); }

.wetterbild-laub {
  fill: var(--laub);
  stroke: var(--wetterbild-stift);
  stroke-width: 3;
  stroke-linejoin: round;
}

.wetterbild-bluete circle { fill: var(--wetterbild-bluete); }

/* **Ein fliegendes Blatt hat keinen Umriss** - es ist einen Finger gross,
   und ein Strich von 3 waere das halbe Blatt. */
.wetterbild-blatt path { fill: var(--laub); stroke: none; }

/* **Was am Boden steht, rueckt hoch, wo der Zettel daneben steht**
   (10.10.2026). Baum und Wind weichen dem Eckkasten aus - *gibt es ihn im
   Bild nicht, ist das Ausweichen gegenstandslos*, und der Baum klebte an
   der Unterkante unter einem leeren Himmel.

   **Positiv gezaehlt, negativ gerechnet** (`calc(... * -1)`): Die Wache
   `test_jede_masszahl_steht_in_der_doku` liest jede Zahl im Blatt gegen
   Regel 10 - und dort stehen **Zahlen**, keine Vorzeichen.

   **Ein CSS-`transform` am `<g>`, kein zweiter Satz Pfade**: Er rechnet in
   Benutzereinheiten (`transform-box: view-box`), also in denselben, in
   denen die Zeichnung steht. *Zwei Zeichnungen fuer dieselbe Sache liefen
   auseinander.* Er steht an einem EIGENEN `<g>`, weil das innere schon ein
   `rotate` traegt - **ein CSS-Transform ersetzt das Attribut, es ergaenzt
   es nicht.** */
.wetterbild-hub { transform: translateY(calc(var(--wetterbild-hub) * -1)); }

/* **Der Baum waechst und rueckt nach rechts, wo Platz ist** (10.10.2026, auf
   einen Kommentar im Canvas: *„Beim [Baum] groesser und natuerlicher wirken
   lassen und nach rechts verschieben neben den Kasten mit den
   Informationen"*).

   **Wo Platz ist - und das ist gemessen.** Zwischen dem rechten Rand des
   Zettels und dem linken Rand des Motivs liegen am Handy **6,6 Einheiten**,
   am Tablet 21,6; ab Desktop steht der Zettel daneben, und frei ist alles
   bis zum Motiv (165). *Nach rechts kann der Baum nur dort, wo rechts etwas
   ist.*

   **Der Ursprung ist sein Fuss** (36, 120): Ein Baum waechst nach oben, er
   schwebt nicht. */
/* **Der Baum darf ueber sein Feld hinaus** (`overflow: visible`): Sein Layer
   ist `meet`-gepasst und damit schmaler als die Kachel - gewachsen und nach
   rechts gerueckt landet ein fliegendes Blatt gemessen bei x = 231 von 200,
   und die Vorgabe `hidden` schnitte es ab. */
.wetterbild-baum { overflow: visible; }

.wetterbild-baum .wetterbild-hub {
  transform-origin: 36px 120px;
  transform: translate(var(--baum-schub), calc(var(--baum-heb) * -1))
             scale(var(--baum-wuchs));
}

.wetterbild-wind path {
  fill: none;
  stroke: var(--wetterbild-wind);
  stroke-width: 4;
  stroke-linecap: round;
  opacity: 0.9;
}

.wetterbild-schliere path {
  fill: none;
  stroke: var(--wetterbild-wolke);
  stroke-width: 6;
  stroke-linecap: round;
  opacity: 0.85;
}

/* ---------------------------------------------------------------------------
   Die Kalenderwahl (FAM-242)

   **Ein Knopf je Kalender, untereinander** - kein Raster und keine Spalten:
   Ein Konto hat drei bis fuenf Kalender, und *eine Liste von fuenf Namen
   braucht keine Anordnung.* Damit gibt es hier auch **keine neue
   Merkmalsvariable** (Regel 10): Was sich je Ansicht aendert, ist allein
   `--abstand`, und den gibt es laengst.
   --------------------------------------------------------------------------- */

.kalenderliste {
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
  margin: 0 0 var(--abstand);
  padding: 0;
  list-style: none;
}

/* **Der Name und das Wort daneben in einer Zeile** - und `wrap`, damit am
   Handy nichts aus dem Rand laeuft (Regel 2). */
.kalenderzeile {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
  flex-wrap: wrap;
}

/* **Ein Kalendername ist kein Knopfwort, und das ist gemessen** (08.10.2026):
   *„Familie Wildenhain - Termine und Feste"* ergab am Handy einen **356,2 px
   breiten Knopf**, der **13,2 px uebersteht**, und die Seite scrollte **13 px
   seitwaerts** - was Regel 2 verbietet.

   **Der Name kommt aus Outlook**, nicht von uns: Er ist so lang, wie jemand
   ihn getippt hat, und *eine Kuerzung waere eine Entscheidung ueber einen
   Namen, den wir nicht kennen.* Also bricht er um.

   `max-width: 100%` dazu, denn **ein Flex-Element schrumpft nicht unter
   seinen Mindestinhalt** (`11-fallstricke.md`) - ohne die Zeile haengt es
   trotz `normal` aus der Zeile. */
.kalenderzeile button {
  white-space: normal;
  max-width: 100%;
  text-align: left;
}

/* **Was gilt, steht als Wort da** - *ein Symbol allein traegt die Aussage
   nicht* (FAM-65), und die Betonung des Knopfes ist die Zugabe. */
.kalendergilt { color: var(--text-leise); font-size: 0.9em; }

/* **Zwei Knoepfe, und einer davon ist ein Formular** (FAM-242).

   **`.maskenknoepfe` traegt gar keine Regel** - gemessen am 08.10.2026: Die
   Klasse steht dreimal in den Vorlagen und **nullmal** in diesem Blatt. Bei
   *einem* Knopf faellt das nicht auf; ein `<form>` daneben ist aber ein
   **Block** und nimmt die ganze Zeile - gemessen **zwei Reihen**, auch bei
   1920 px, wo zwei kurze Knoepfe nebeneinander gehoeren.

   **Eine eigene Klasse statt eines Umbaus an der fremden**: Die beiden
   anderen Masken tragen dort je einen Knopf, und *wer eine Klasse umbaut,
   die nichts tut, aendert drei Masken fuer eine.* */
.kopplungsknoepfe {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
}

/* Der Abschnitt auf der Familienseite (FAM-229) - abgesetzt wie das Wappen
   darueber, damit er nicht wie eine Zeile der Mitgliederliste aussieht. */
.familienabschnitt {
  margin-top: calc(var(--abstand) * 1.5);
  padding-top: var(--abstand);
  border-top: 1px solid var(--linie);
}


/* ===========================================================================
   Mein Tag - eine Zusammenfassung aus den Modulen (17.10.2026, auf Wunsch)
   ===========================================================================

   *„Die Maske Mein Tag soll eine Zusammenfassung aus den Modulen sein, die
   den aktuellen Tag beschreiben."*

   ## Keine einzige neue Merkmalsvariable

   **Und das ist eine Entscheidung, keine Sparsamkeit.** Jede Variable aus
   Regel 10 steht siebenmal im Blatt und einmal in der Maßtabelle; wer eine
   anlegt, legt acht Stellen an. Diese Maske braucht keine: Sie ist eine
   Spalte aus Karten, und *Abstand, Radius, Schrift und Fläche gibt es
   dafür längst*.

   **Was sie wirklich an Maßen braucht, bringen die Blöcke selbst mit** -
   eine Terminzeile sieht hier aus wie im Kalender, eine Wetterkachel wie
   auf der Wetterseite, eine Aufgabenzeile wie in der Liste. *Das ist der
   Sinn davon, dass jedes Modul seine eigene Teilvorlage mitbringt.*

   ## Der Grund ist durchsichtig

   Wie am Einkaufszettel, an der Arbeitsfläche, am Chat, an der
   Kachelübersicht und am Stundenplan: **Die Karten sollen auf dem
   Seitengrund liegen, nicht auf einer weißen Platte** - *ein Schatten auf
   einer weißen Platte, die genauso weiß ist wie die Karte, ist keiner.*
   Seitengenau zurückgenommen, nicht allgemein. */
.inhalt:has(.tagesliste) { background: transparent; }

.tagesliste {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  /* **Mehr Luft zur Aussenkante** (18.10.2026, auf Wunsch). Gemessen
     standen die Karten am Handy **16 px** vom Rand - das ist das Polster
     des Hauptbereichs, und es gilt fuer jede Maske. Hier kommt **ein
     Abstand** dazu: `--abstand` ist die Zahl, die diese Ansicht fuer
     „ein Abstand" haelt (10/20/20/32), also 26 px am Handy.

     *Eine eigene Zahl waere eine erfundene* (Regel 5), und das Polster
     des Hauptbereichs zu erhoehen traefe sechzehn andere Masken. */
  padding-inline: var(--abstand);
}

/* **Eine Karte je Modul** - mit derselben Rezeptur wie die Grundkarte und
   der Chatbeitrag: `--flaeche`, `--radius-karte`, `--kartenschatten`.
   *Eine zweite Rezeptur für dieselbe Erhebung liefe auseinander*
   (Regel 10). */
.tagesblock {
  padding: var(--abstand);
  border-radius: var(--radius-karte);
  background: var(--flaeche);
  box-shadow: var(--kartenschatten);
}

/* **Die Überschrift ist der Weg ins Modul**, und sie sieht aus wie eine
   Überschrift und nicht wie ein Knopf: Der Textmarker aus Abschnitt 15
   gehört den Schaltflächen (*„Navigationseinträge bleiben Menüzeilen"*,
   Auftrag vom 27.09.2026) - und eine Karte voller Marker wäre ein
   Jahrmarkt.

   **Segoe UI Bold wie jedes `h2`** (Einkauf 2a, Abschnitt 1): *Eine
   Grotesk hebt sich durch ihr Gewicht ab, eine Serifenschrift durch ihre
   Form.* */
.tagesblocktitel {
  /* **Ein halber Abstand nach unten** (18.10.2026, auf Wunsch: *„den
     Abstand der Ueberschriften der Kaesten zum Inhalt kleiner machen"*).

     Bis dahin stand hier `margin: 0`, und den Abstand brachte die
     **Trefferflaeche** des Verweises mit: 44 px fuer eine Zeile von 15 px
     - gemessen **29 px Luft**, und die sah aus wie ein Abstand, den
     jemand gesetzt hat.

     **Die Trefferflaeche ist mit dem naechsten Wunsch weggefallen**: Seit
     die **ganze Karte** der Weg ins Modul ist, braucht die Ueberschrift
     keine eigene mehr. Was bleibt, ist ein halber Abstand - *der Kopf
     gehoert zum Kasten, nicht in die Mitte zwischen zwei Dingen.* */
  margin: 0 0 calc(var(--abstand) * 0.5);
  font-family: var(--schrift-seitentitel);
  font-size: calc(var(--schrift) * 1.25);
  font-weight: 700;
}

/* **Die Trefferfläche ist `--knopf-flaeche`, und das ist gemessen**
   (17.10.2026).

   Beim ersten Anlauf war der Verweis **18 px** hoch - die Höhe seiner
   Textzeile. Zum Vergleich im selben Abzug: eine **Menüzeile misst 36**,
   und `--knopf-flaeche` ist `max(44px, var(--schaltflaeche))`. *Die
   einzige Tür in ein Modul war damit halb so groß wie die Menüzeile
   daneben und nicht einmal halb so groß wie der Boden, den dieses Projekt
   jedem Knopf gibt.*

   **`inline-flex`, nicht `block`**: Ein `inline`-Element nimmt keine Höhe
   an, und über die ganze Breite wäre die Fläche auch dort anzutippen, wo
   gar kein Wort steht.

   **Der Preis steht dabei**: Am Handy wächst jeder Block um rund 20 px,
   bei sechs Blöcken um 120 - das ist knapp ein Siebtel des Bildschirms.
   *Eine Überschrift, die man dreimal treffen muss, kostet mehr.* */
.tagesblocktitel a {
  display: inline-flex;
  align-items: center;
  color: inherit;
  text-decoration: none;
}

/* **Die ganze Karte ist der Weg ins Modul** (18.10.2026, auf Wunsch:
   *„Wenn ich einen Kasten anklicke, will ich zum Modul kommen und nicht
   nur bei der Ueberschrift."*).

   **Ohne eine Zeile JavaScript und ohne zweites Markup**: Der Verweis in
   der Ueberschrift legt sich als `::after` ueber die ganze Karte. Das ist
   der uebliche Weg dafuer, und er hat drei Eigenschaften, die hier
   zaehlen:

   | | |
   |---|---|
   | **Ein Verweis, nicht zwei** | Die Karte in ein `<a>` zu wickeln ginge nicht - *in einem Verweis darf kein zweiter stehen*, und in einer Karte stehen welche (ein Termin, eine Aufgabe) |
   | **Das Vorleseprogramm hoert einen Verweis** | mit dem Text der Ueberschrift, nicht mit dem ganzen Kasteninhalt |
   | **Die Trefferflaeche ist die Karte** | damit ist die Regel von 44 px nicht nur erfuellt, sondern weit uebertroffen - *und die Ueberschrift braucht keine eigene mehr* |

   **Was darin steht, bleibt erreichbar**: Jeder Verweis und jedes
   Bedienelement in der Karte liegt mit `position: relative` darueber.
   *Eine Karte, die ihre eigenen Verweise verdeckt, ist schlimmer als eine,
   die nur am Kopf klickbar ist.* */
.tagesblock { position: relative; }
.tagesblocktitel a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.tagesblock a:not(.tagesblocktitel a),
.tagesblock button,
.tagesblock summary,
.tagesblock input,
.tagesblock select,
.tagesblock label { position: relative; }

/* **Unterstrichen erst beim Anfassen** - *dass es ein Weg ist, sagt der
   Zeiger; dass es eine Überschrift ist, sagt die Schrift.* Beim Fokus
   bleibt der Umriss, den Regel A9 verlangt. */
.tagesblocktitel a:hover,
.tagesblocktitel a:focus-visible {
  text-decoration: underline;
}

/* Eine einzelne Auskunft - der Einkaufszettel, der Chat, ein freier
   Schultag. */
.tageszeile {
  margin: 0;
}

/* ---------------------------------------------------------------------------
   Das Essen - zwei Zeilen, Zeit und Gericht
   --------------------------------------------------------------------------- */
.tagesessen {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.4);
}

.tagesessenzeile {
  display: flex;
  align-items: baseline;
  gap: 0.6ch;
  /* **Am Handy darf es umbrechen** - *„Spaghetti Bolognese mit Salat" und
     „für alle" passen dort nicht nebeneinander*, und eine abgeschnittene
     Mahlzeit ist keine Auskunft (Regel 6 gilt für Kurzformen, nicht für
     Beschneiden). */
  flex-wrap: wrap;
}

/* **Das Wort steht vorn und ist leise** - *Mittag* und *Abend* sind die
   Beschriftung, das Gericht ist die Auskunft. Die Breite hält beide Zeilen
   untereinander bündig. */
.tagesessenzeit {
  flex: 0 0 auto;
  min-width: 7ch;
  color: var(--text-leise);
}

.tagesessenname {
  font-weight: 600;
}

/* **Für wie viele** - leise, hinter dem Gericht, mit dem vollen Namen im
   `title` (FAM-146). */
.tagesesser {
  color: var(--text-leise);
  font-size: 0.85em;
}

/* ---------------------------------------------------------------------------
   Der Stundenplan - die Stunden von heute, untereinander
   --------------------------------------------------------------------------- */
.tagesstunden {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.4);
}

/* **Der Bezug für den Ausfallstempel, und ein eigener Stapel** - wörtlich
   `.planzelle` im Gitter (12.10.2026): `position: relative` allein macht
   mit `z-index: auto` **keinen** Stapelkontext, und ohne `isolation`
   schlüge die `2` des Stempels alles, was auf der Seite darunter klebt.
   *Das war der Fund jenes Tages, und er gilt hier genauso.* */
.tagesstunde {
  position: relative;
  isolation: isolate;
  display: flex;
  align-items: baseline;
  gap: 0.6ch;
  flex-wrap: wrap;
}

/* **Die Uhrzeit steht vorn** und hält alle Fächer bündig untereinander -
   dieselbe Stelle wie das Wort beim Essen darüber. */
.tagesstundenzeit {
  flex: 0 0 auto;
  min-width: 7ch;
  color: var(--text-leise);
  font-variant-numeric: tabular-nums;
}

.tagesstundenfach {
  font-weight: 600;
}

/* **Raum und Kürzel, leicht grau** - dieselbe Farbe und dieselbe Größe wie
   im Gitter (FAM-232): *Zwei verschiedene kleine Schriften in einer Zeile
   sehen aus wie ein Versehen.* */
.tagesstundenfuss {
  color: var(--text-leise);
  font-size: 0.8em;
}

/* **Die ausgefallene Stunde bleibt lesbar, nur gedämpft** - wörtlich die
   Entscheidung im Gitter: *Ein Ausfall sagt, dass die Stunde nicht
   stattfindet, nicht dass sie nie im Plan stand.* */
.tagesstunde.faellt-aus .tagesstundenfach,
.tagesstunde.faellt-aus .tagesstundenzeit,
.tagesstunde.faellt-aus .tagesstundenfuss {
  opacity: 0.55;
}


/* --- Die Aufgaben in Mein Tag (17.10.2026) --------------------------------

   *„Aufgaben sollen hier nicht bearbeitet werden können und sollen weder den
   Stern noch das Kalender Symbol anzeigen."*

   **Dieselbe Bauart wie die Stunden und das Essen darüber** – eine Zeile aus
   Angaben, die umbricht, statt etwas abzuschneiden. Was in der Aufgabenliste
   ein Raster aus Spalten ist (`.aufgabe`), ist hier bewusst keines: *Vier
   Spalten brauchen eine Breite, die eine Karte in einer Spalte nicht hat.* */
.tagesaufgaben {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.4);
}

.tagesaufgabe {
  display: flex;
  align-items: baseline;
  gap: 0.6ch;
  flex-wrap: wrap;
}

/* **Der Titel nimmt die Zeile, die Angaben hängen sich an** – und er bricht
   um, statt die Marken hinauszuschieben.

   **`--knopf-flaeche` wie bei den Blocküberschriften** (gemessen am selben
   Tag): Ohne sie ist der Verweis **18 px** hoch – *und seit die Knöpfe aus
   diesem Block heraus sind, ist er der einzige Weg in die Aufgabe.* Ein
   Ziel, das man treffen muss, um überhaupt etwas tun zu können, darf nicht
   kleiner sein als der Boden, den dieses Projekt jedem Knopf gibt.

   **Und er sieht aus wie ein Verweis** – gemessen am Abzug: Im Block
   darüber trägt *„Grillfest bei Schulzes"* Farbe und Unterstrich
   (`_termin.html`), hier stand derselbe Weg als schlichter Text. *Zwei
   Bauarten für dasselbe in einer Maske sind eine zu viel* – und seit die
   Knöpfe heraus sind, ist dies der einzige Weg in die Aufgabe. Die
   Überschrift des Blocks bleibt davon unberührt: **eine Überschrift ist
   keine Zeile im Inhalt.** */
.tagesaufgabewas {
  flex: 1 1 14ch;
  display: inline-flex;
  align-items: center;
  min-height: var(--knopf-flaeche);
  font-weight: 600;
}

/* **Die Zeichen sagen etwas, sie tun nichts** – Schritte, Notiz,
   Wiederholung, Anhang, Erinnerung, *wichtig*. Alle in einer Größe und
   einer Farbe: *Zwei verschiedene kleine Schriften in einer Zeile sehen aus
   wie ein Versehen* (FAM-232). */
.tagesaufgabezeichen {
  flex: 0 0 auto;
  color: var(--text-leise);
  font-size: 0.8em;
}

/* **Das Wort statt des Sterns** – es trägt dieselbe Aussage und ist kein
   Knopf. Der warme Akzent ist derselbe, den auch sonst *„für dich ist etwas
   dran"* trägt (FAM-115). */
.tagesaufgabezeichen.wichtig {
  color: var(--warm);
  font-weight: 700;
}

/* **Die Personenfarbe** – die Rezeptur steht bei `.gefaerbt` (Abschnitt 1),
   hier wird sie nur gelesen. */
.tagesaufgabewer {
  flex: 0 0 auto;
  color: var(--text-leise);
  font-size: 0.85em;
}

.tagesaufgabewer.gefaerbt,
.tagesaufgabewer.gefaerbt .leise {
  color: var(--personenfarbe);
  font-weight: 600;
}

.tagesaufgabewann {
  flex: 0 0 auto;
  color: var(--text-leise);
  font-size: 0.85em;
}

/* **Das Wort trägt die Aussage, nicht die Farbe** (FAM-65) – fett und rot,
   und im `title` steht „überfällig" ausgeschrieben. Dieselbe Rezeptur wie
   `.aufgabe-wann.ueberfaellig` und `.leihe-frist.ueberfaellig`. */
.tagesaufgabewann.ueberfaellig {
  color: var(--fehler);
  font-weight: 600;
}

/* ===========================================================================
   Ein neuer Stand ist da (17.10.2026, auf Wunsch)
   ===========================================================================

   *„Wenn die Anwendung geändert wurde, soll dem User angezeigt werden, dass
   er mit einem Button die neue Version laden kann."*

   **Ein drittes Band, und das ist eine Entscheidung** – nach der
   Meldungsleiste (Regel 3) und der Benachrichtigungszeile (FAM-115). Die
   Begründung von damals trägt hier genauso: *Eine Meldung blendet sich nach
   vier Sekunden aus, eine Benachrichtigung darf das nicht* – und dies darf
   es erst recht nicht, denn ohne diesen Satz erfährt niemand, warum die
   Anwendung sich seltsam verhält.

   **Es liegt über beiden anderen.** Die sagen, was in der Familie los ist;
   dies sagt etwas über die Anwendung selbst.

   **Der Akzent und nicht das Warm der Benachrichtigung**: *Zwei Bedeutungen
   für eine Farbe kosten bei jeder Karte Zeit* (FAM-122, wörtlich die
   Begründung gegen Rot an der Ausleihkarte). Warm heißt in Family Board
   seit FAM-115 *„für dich ist etwas passiert"*; hier ist nichts für
   jemanden passiert, hier ist die Anwendung eine andere geworden.

   **Keine neue Merkmalsvariable** – dieselbe Rechnung wie bei *Mein Tag*
   darüber: Abstand, Radius und Farben gibt es alle schon. */
.neuerstand {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) / 2);
  /* **Dieselbe Einrückung wie die Benachrichtigungszeile** – die beiden
     stehen untereinander, und eine Kante, die um ein paar Pixel springt,
     sieht aus wie ein Fehler. */
  margin: calc(var(--abstand) / 2) var(--abstand) 0;
  padding: calc(var(--abstand) / 2) var(--abstand);
  border: 1px solid var(--akzent);
  border-radius: var(--radius);
  background: var(--akzent-blass);
  /* **Am Handy darf der Knopf unter den Satz** – *ein Knopf, der nicht
     erreichbar ist, ist schlimmer als einer, der eine Zeile tiefer steht*
     (29.09.2026, der Seitenkopf). */
  flex-wrap: wrap;
}

.neuerstandwort {
  /* **Nimmt den Platz, den der Knopf übrig lässt** – und bricht um, statt
     den Knopf aus dem Band zu schieben. */
  flex: 1 1 12ch;
  margin: 0;
}


/* ===========================================================================
   Jemanden zu einem Termin einladen (17.10.2026, auf Wunsch)
   ===========================================================================

   *„dass ich jemanden mit einladen kann zu einem Termin und das quasi über
   eine E-Mail."*

   **Keine neue Merkmalsvariable und keine neue Farbe** – dieselbe Rechnung
   wie bei *Mein Tag*: Abstand, Linie und die leise Textfarbe gibt es alle
   schon (Regel 10). */

/* **Ein eigener Block unter der Maske**, abgesetzt durch eine Linie – *was
   darüber steht, ändert den Termin; was hier steht, schickt etwas hinaus.* */
.einladungen {
  margin-top: calc(var(--abstand) * 1.5);
  padding-top: var(--abstand);
  border-top: 1px solid var(--linie);
}

.einladungneu {
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
  /* **Dieselbe Breite wie die Maske darüber** (`--maske-breite`, Regel 10):
     Zwei Formulare untereinander mit zwei Breiten sehen aus wie zwei
     Masken. */
  max-width: var(--maske-breite);
}

/* --- Wen ihr schon eingeladen habt --------------------------------------- */

.einladungsliste {
  margin: var(--abstand) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.4);
}

/* **Eine Zeile, die umbricht** – wie die Stunden und das Essen in *Mein
   Tag*: *Eine Adresse ist lang, und ein Raster aus Spalten hätte am Handy
   keine Breite dafür.* */
.einladung {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.6ch;
}

.einladungwer {
  font-weight: 600;
}

.einladungadresse {
  color: var(--text-leise);
  font-size: 0.85em;
  /* **Eine Adresse bricht überall** – sonst schiebt
     `ganz.lange.adresse@beispiel.de` die Zeile über den Rand (Regel 2). */
  overflow-wrap: anywhere;
}

/* **Das Wort trägt die Aussage, nicht die Farbe** (FAM-65): *verschickt*
   und *unterwegs* stehen als Wörter da, und das `title` sagt wann. */
.einladungstand {
  flex: 0 0 auto;
  color: var(--text-leise);
  font-size: 0.8em;
}

/* **Was noch in der Warteschlange liegt, trägt den warmen Akzent** –
   dieselbe Farbe, die in Family Board *„hier ist etwas im Gange"* heißt
   (FAM-115). */
.einladungstand.wartet {
  color: var(--warm);
  font-weight: 700;
}

.einladungweg {
  flex: 0 0 auto;
  margin-left: auto;
}

/* ===========================================================================
   Der Ort steht unter dem Titel (17.10.2026, auf Wunsch)
   ===========================================================================

   *„Außerdem sollte der Ort für einen Termin direkt unter dem Titel
   stehen."*

   Er stand bis dahin in der Zeile mit den Namen, hinter einem Mittelpunkt.
   **Jetzt ist er eine eigene Zeile** – leise wie die Namen darunter, aber
   davor.

   **Nicht gedämpfter als die Namenszeile**: Ein Ort ist eine Auskunft, die
   jemand sucht; wer ihn weiter zurücknimmt, hat ihn nur verschoben. */
.terminort {
  color: var(--text-leise);
  overflow-wrap: anywhere;
}


/* ===========================================================================
   Drei Bereiche, nur durch einen Strich getrennt (17.10.2026, auf Wunsch)
   ===========================================================================

   *„Das Menü muss in Bereiche eingeteilt werden, die aber nur mit Strichen
   voneinander getrennt sind. Diese Bereiche auch für die Übersicht
   übernehmen."*

   **Zwei Regeln, kein neues Merkmal.** Der Strich ist `--linie`, der
   Abstand `--abstand` – beides gibt es (Regel 10). Die Listen selbst
   behalten ihr Raster (`--menueliste-spalten`), also sieht ein Bereich am
   Handy aus wie zwei Spalten und am Infoboard wie fünf, genau wie vorher.

   **Im Menü zieht ihn seit dem Nachmittag der Bereich** (`.menuebereich
   + .menuebereich`, weiter oben): Die Listen sind dort keine Geschwister
   mehr, sondern stecken je in einem `<details>` - *eine Regel, deren
   Geschwisterbeziehung jemand aufgetrennt hat, greift lautlos nicht
   mehr.* Hier bleibt die **Kachelliste**, und die ist unverändert.

   **Der Strich sitzt am Nachfolger, nicht am Vorgänger** (`+`): So trägt
   ihn nur, was wirklich einen Vorgänger hat – *eine Linie über der ersten
   Liste wäre ein Strich gegen nichts*, und sie erschiene genau dann, wenn
   eine Familie den ersten Bereich ganz abgeschaltet hat. */
.kachelliste + .kachelliste {
  margin-top: var(--abstand);
  padding-top: var(--abstand);
  border-top: 1px solid var(--linie);
}


/* ===========================================================================
   Das Installierband (17.10.2026, auf Wunsch)
   ===========================================================================

   *„Kannst du die Installation der App auf dem Handy, Tablet nicht
   automatisieren und nicht nur eine Anleitung anbieten?"*

   **Es steht über dem Inhalt**, wie die Benachrichtigungszeile (FAM-115) -
   was von selbst kommt, steht oben und nicht mitten im Text. Und es sieht
   aus wie ein Hinweis, nicht wie eine Meldung: Eine `.meldung` blendet sich
   nach vier Sekunden aus (Regel 3), dieses Band wartet auf eine Antwort.

   **Keine neue Farbe und keine neue Zahl** - Fläche, Linie, Radius und
   Abstand kommen aus Regel 10 und Abschnitt 1. */
.installierband {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--abstand);
  margin-bottom: var(--abstand);
  padding: calc(var(--abstand) * 0.6) var(--abstand);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
  box-shadow: var(--schatten-karte, none);
}

/* **Der Satz nimmt den Platz, die Knöpfe hängen sich an** - und bricht um,
   statt sie hinauszuschieben. */
.installierbandwort {
  flex: 1 1 14ch;
}

/* **Das Kreuz trägt keinen Textmarker** (Abschnitt 15): Es ist ein
   Zeichenknopf, und ein Strich unter einem Kreuz sieht aus wie ein Fehler.
   **Die Trefferfläche bleibt `--knopf-flaeche`** - der Boden, den dieses
   Projekt jedem Ziel gibt. */
.installierbandweg {
  all: unset;
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: var(--knopf-flaeche);
  min-height: var(--knopf-flaeche);
  cursor: pointer;
  color: var(--text-leise);
}

.installierbandweg:focus-visible {
  outline: 2px solid var(--akzent);
  outline-offset: 2px;
}


/* ===========================================================================
   Das Symbol auf der Kachel (17.10.2026, auf Wunsch)
   ===========================================================================

   *„In der Übersicht bitte die Symbole aus dem Menü übernehmen."*

   **Es steht im Titel, links vom Wort** - wie im Menüpunkt daneben, und mit
   demselben Abstand. **Keine neue Zahl**: Die Größe kommt aus der
   Schriftgröße des Titels, der Abstand aus `--abstand`. */
.kachel h3 {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
}

/* **Der Verweis trägt das Zeichen mit**, denn er füllt die ganze Kachel
   (11.10.2026) - also muss er selbst die Zeile sein, nicht nur das Wort
   darin. */
.kachel h3 > a {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
}

/* **Das Wort darf umbrechen, das Zeichen nicht schrumpfen** - *„Sticker
   Board" und „Geschenkewünsche" sind zwei Zeilen am Handy*, und ein
   gestauchtes Symbol daneben sähe aus wie ein Fehler. */
.kachel .kachelwort {
  min-width: 0;
}

.kachel h3 .symbol {
  flex: none;
}


/* **Die Spanne neben dem Wort der Woche** (17.10.2026, auf Wunsch).

   **Keine neue Zahl und keine neue Farbe**: `--text-leise` und `0.9em`
   stehen so schon an jeder zweiten Stelle - *das Wort ist die Auskunft,
   die Spanne die Ergaenzung.* */
.kalenderspanne {
  color: var(--text-leise);
  font-size: 0.9em;
  white-space: nowrap;
}


/* **Ein Feld, das die ganze Zeile nimmt** (17.10.2026, auf Wunsch: *„dass
   die Termin-Titel-Box über die gesamte Breite des Bildschirms geht"*).

   **Keine eigene Breite, sondern die drei Merkmale aus FAM-109** – dieselben,
   die ab Tablet ohnehin gelten: Beschriftung darüber, Feld über die volle
   Zeile. *Eine Zahl, die nur hier stünde, kennte die Ansichtsauswahl nicht*
   (Regel 0).

   **Sie stehen am Feld selbst**, und genau dort werden sie auch gelesen –
   `flex-direction: var(--feld-richtung)` steht in der Regel zu `.maske
   .feld`. *Ein Custom Property wird dort substituiert, wo es deklariert
   ist*; auf `:root` gesetzt bliebe es für dieses eine Feld wirkungslos (die
   Falle steht im Index). */
/* **Was „nimmt freien Text auf" heisst** (17.10.2026, auf Wunsch: *„Die
   Felder, die Text aufnehmen, sollten versuchen die Breite
   auszunutzen."*).

   **Eine Positivliste, und das ist hier die richtige Richtung** - anders
   als bei der Breitenregel (FAM-108), wo
   `input:not([type="checkbox"]):not([type="radio"])` steht. Der
   Unterschied ist, was ein Fehler kostet:

   | | |
   |---|---|
   | **Negativliste** | Ein neuer Typ wird **gedehnt**. Ein Datumsfeld ueber die ganze Breite ist kein Datumsfeld mehr |
   | **Positivliste** | Ein neuer Typ wird **nicht** gedehnt - das sieht man, und es ist nichts kaputt |

   **Was bewusst NICHT dazugehoert**: `date`, `time`, `number`, `select`,
   `color`, `checkbox`, `radio`. Sie tragen keinen freien Text, sondern
   einen Wert mit eigener Laenge - *ein Tagesfeld von 290 px waere kein
   Tagesfeld mehr, und drei davon nebeneinander passten nicht mehr in eine
   Zeile* (die Geburtstagsmaske).

   **Gehalten wird die Liste von einer Wache**
   (`test_jedes_textfeld_nutzt_die_breite`): Sie liest, welche Typen die
   Masken **wirklich** benutzen - *eine Positivliste, die jemand pflegen
   muss, wird irgendwann nicht gepflegt; eine, die sich beschwert, schon*
   (FAM-85). */
.feld.feldbreit,
.feld:has(> :where(textarea, input[type="text"], input[type="email"],
                   input[type="url"], input[type="tel"],
                   input[type="search"], input[type="password"])),
div.feldgruppe:has(> :where(textarea, input[type="text"],
                           input[type="email"], input[type="url"],
                           input[type="tel"], input[type="search"],
                           input[type="password"])) {
  /* **Fuer die Masken mit `.maske`**: dieselben drei Merkmale, die ab
     Tablet ohnehin gelten. */
  --feld-richtung: column;
  --feld-beschriftung-breite: auto;
  --feld-eingabe-basis: auto;

  /* **Und fuer die Eingabemaske, wo gar keine Feldregel greift.**

     **Gemessen am 17.10.2026**, und das war der eigentliche Fund: In
     `.eingabemaske` (A6, 29.09.2026) traegt ein `.feld` **keine einzige
     Regel ausser `box-sizing`** - das Label ist `inline`, das Feld ein
     `block`, und das `<input>` steht mit seiner **Eigenbreite** daneben:
     **164 px von 290.**

     Der Grund ist der Selektor: Die Feldregeln heissen seit FAM-108
     `.maske .feld`, und die Eingabemaske heisst nicht `.maske`. *Eine
     Regel hinter einer fremden Klasse hilft der naechsten Maske nicht* -
     woertlich die Lehre, die `test_die_breitenregel_gilt_fuer_jede_maske`
     seit FAM-85 bewacht, nur eine Maskenart weiter.

     **Deshalb steht hier alles, was das Feld braucht**, und nicht nur die
     drei Merkmale: Diese Klasse soll in **beiden** Maskenarten dasselbe
     tun. */
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.feld.feldbreit > label,
.feld:has(> :where(textarea, input[type="text"], input[type="email"],
                   input[type="url"], input[type="tel"],
                   input[type="search"], input[type="password"])) > label,
div.feldgruppe:has(> :where(textarea, input[type="text"],
                           input[type="email"], input[type="url"],
                           input[type="tel"], input[type="search"],
                           input[type="password"])) > label {
  display: flex;
  align-items: center;
  flex: none;
  font-weight: 600;
}

/* **Und die Eingabe selbst nimmt die Zeile.**

   **Nur die Textfelder**, nicht jedes Feld im selben Kasten: In der
   Terminmaske steht *Wann* als Gruppe aus Datum und zwei Uhrzeiten
   nebeneinander - *die drei gehoeren in eine Zeile, und drei volle Breiten
   waeren drei Zeilen.* */
.feld.feldbreit > input,
.feld.feldbreit > select,
.feld.feldbreit > textarea,
.feld > :where(textarea, input[type="text"], input[type="email"],
               input[type="url"], input[type="tel"], input[type="search"],
               input[type="password"]),
div.feldgruppe > :where(textarea, input[type="text"], input[type="email"],
                        input[type="url"], input[type="tel"],
                        input[type="search"], input[type="password"]) {
  width: 100%;
}


/* ===========================================================================
   Ein Termin in zwei Zeilen – nur in *Mein Tag* (18.10.2026, auf Wunsch)
   ===========================================================================

   *„Termin nimmt in dieser Ansicht zu viel Platz ein. Bitte den Titel des
   Termins rechtsbündig machen und den Ort auch rechtsbündig und wer daran
   beteiligt ist, linksbündig in der zweiten Zeile."*

   **Oben** Uhrzeit links, Titel rechts; **unten** wer links, Ort rechts.
   Vorher standen Uhrzeit, Titel, Ort und Namen untereinander – vier Zeilen
   für einen Termin.

   **Wird es eng, bricht die Zeile um, statt zu überlaufen** (`flex-wrap`):
   Ein langer Titel rutscht dann unter die Uhrzeit und bleibt rechts. Der
   Titel behält seine Mindesthöhe – er ist die Bedienfläche (Regel 10). */
.terminzeile.zweizeilig .terminkopf,
.terminzeile.zweizeilig .terminfuss {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 12px;
}
.terminzeile.zweizeilig .terminname {
  margin-left: auto;
  min-width: 0;
  text-align: right;
}
.terminzeile.zweizeilig .terminname a { justify-content: flex-end; }
.terminzeile.zweizeilig .terminwer { min-width: 0; }
.terminzeile.zweizeilig .terminort {
  margin-left: auto;
  text-align: right;
}


/* ===========================================================================
   Das Infoboard – der Bildschirm im Wohnzimmer (FAM-261)
   ===========================================================================

   **Ein Raster, das aus der Vorlage kommt**: `grid-template-areas` steht
   am Element (`app/infoboard.py::raster()`), die Flächen heißen nach dem
   Platz (`b1` … `b6`). Hier steht nur, was für **jede** Vorlage gilt.

   **Der ganze Bildschirm, kein Scrollen**: `100vh` und gleich hohe Zeilen.
   Was nicht in einen Bereich passt, wird dort abgeschnitten, nicht
   gescrollt – *am Bildschirm im Wohnzimmer scrollt niemand*. Die Holer
   begrenzen deshalb selbst, wie viel sie liefern („und 7 weitere“).

   **Nur lesen**: Verweise der Module führen am Bildschirm nirgends hin –
   wer auf einen Termin tippt, käme sonst auf die Anmeldeseite. */
.infoboardseite {
  margin: 0;
  min-height: 100vh;
  background: var(--grund);
  display: flex;
  flex-direction: column;
}
.infoboardkopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand);
  padding: var(--abstand) calc(2 * var(--abstand)) 0;
}
.infoboarddatum { font-size: 1.4em; color: var(--text-leise); }
.infoboardanzeige {
  flex: 1;
  display: grid;
  grid-auto-columns: 1fr;
  grid-auto-rows: 1fr;
  gap: var(--abstand);
  padding: var(--abstand) calc(2 * var(--abstand)) calc(2 * var(--abstand));
  min-height: 0;
}
.infoboardbereich {
  /* **Eine Spalte, kein Block** (18.10.2026): Nur so kann der Inhalt die
     Hoehe fuellen, die sein Bereich hat - *ein Wetterbild mit `height:
     100%` in einem Block ist 0 px hoch*, gemessen 512 x 0. Und nur so
     kann ein Chat unten stehen, statt oben anzufangen. */
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  padding: var(--abstand);
  border-radius: var(--radius);
  background: var(--flaeche);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.infoboardbereichtitel { margin: 0 0 calc(var(--abstand) / 2); font-size: 1.2em; }
.infoboardunterkopf { margin: calc(var(--abstand) / 2) 0 0; font-size: 1em; color: var(--text-leise); }
.infoboardliste { list-style: none; margin: 0; padding: 0; }
.infoboardliste li { padding: 4px 0; border-bottom: 1px solid var(--linie); overflow-wrap: anywhere; }
.infoboardliste li:last-child { border-bottom: 0; }
.infoboardleise, .infoboardleer { color: var(--text-leise); }
.infoboardmarke {
  display: inline-block;
  padding: 0 6px;
  border-radius: var(--radius);
  background: var(--akzent-blass);
  color: var(--akzent-dunkel);
  font-size: 0.82em;
}
.infoboardwer { font-weight: 600; margin-right: 0.4em; }
.infoboardanzeige a { color: inherit; text-decoration: none; pointer-events: none; }

/* **Das Wetter nimmt im Bereich die Fassung vom Handy** (18.10.2026, auf
   Wunsch: *„Bei Wetter ist es unangenehm zusammengeschoben, bitte nicht so
   hoch und in der Breite eher die Sicht aus dem Handy nehmen."*).

   **Vier Merkmale, kein zweites Markup** - dieselbe Teilvorlage wie in
   *Mein Tag*, nur anders angeordnet. Das ist derselbe Griff wie bei der
   Aufgabenliste neben der Maske (`.listenspalte`, 28.09.2026): *Keine der
   vier Ansichten passt, also ist es keine Frage der Ansicht, sondern eine
   des Kastens.*

   **Warum ueberhaupt**: Am Infoboard steht der Zettel **neben** dem Bild
   (`--wettertag-anzeige: flex`), und das ist fuer eine Seite gedacht, die
   1920 px breit ist - nicht fuer einen Bereich von 880. Gemessen war die
   Kachel dort unten **abgeschnitten**: Baum und Wiese liefen aus dem
   Bereich heraus.

   **Keine neue Zahl, sondern der Bereich selbst**: Die Kachel bekommt die
   Hoehe ihres Bereichs (`100%`), nicht die 280 px, die fuer eine ganze
   Seite gedacht sind. *Eine Zahl, die ich hier hinschriebe, waere eine
   erfundene* (Regel 5) - der Bereich weiss besser, wie hoch er ist, denn
   seine Hoehe kommt aus der Aufteilung. */
.infoboardwetter {
  --wettertag-anzeige: block;
  --wetterecke-lage: absolute;
  --wetterbild-breite: 100%;
  --wetterbild-hoehe: 100%;
  flex: 1 1 auto;
  min-height: 0;
}
.infoboardwetter > * { height: 100%; }

/* **Die Schrift wird kleiner, je mehr Bereiche das Board hat**
   (18.10.2026, auf Wunsch). Das Maß kommt aus der Vorlage
   (`infoboard.schriftmass()`), der Wert aus der Ansicht.

   **`--schrift-voll` ist der Umweg, den es braucht**: Ein Custom Property
   kann sich nicht selbst als Quelle nehmen (`--schrift: calc(var(--schrift)
   * 0.8)` ist zirkulär und fällt ersatzlos aus). Der Kasten merkt sich
   den Wert der Ansicht, der Bereich rechnet daraus seinen eigenen - und
   **alles, was `var(--schrift)` liest, rechnet ab hier mit dem kleineren**,
   ohne dass eine einzige Modulvorlage es wissen muss.

   **Die Trefferfläche bleibt, wo sie ist**: `--knopf-flaeche` hängt an
   `--schaltflaeche`, nicht an der Schrift. *Ein Haken, den man nicht
   trifft, ist kein Haken.* */
.infoboardanzeige { --schrift-voll: var(--schrift); }
.infoboardbereich {
  --schrift: calc(var(--schrift-voll) * var(--infoboard-schriftmass, 1));
  font-size: var(--schrift);
}

/* **Und der Zettel ist am Bildschirm kein Bedienelement** (gemessen):
   `.zettelzeile` trägt in der Maske `min-height: var(--knopf-flaeche)` -
   am Infoboard **72 px**, damit ein Finger die Zeile trifft und den
   Artikel in den Wagen legt. **Am Bildschirm legt niemand etwas in den
   Wagen.**

   Gemessen hat das allein **sieben von sieben statt zwei von sieben**
   Artikeln sichtbar gemacht: Die Zeile fällt von 72 auf 32 px. *Die
   Schrift war nur die halbe Ursache; die andere war eine Trefferfläche,
   die hier niemand braucht.* */
.infoboardzettel .zettelzeile,
.infoboardzettel .zettelabteilung,
.infoboardzettel .zettelleer { min-height: 0; }
.infoboardzettel .zettelwort { font-size: inherit; }

/* **Der Chat am Bildschirm ist der Chat** - die Blasen kommen aus
   Abschnitt 13, hier steht nur, was ein Bereich daran aendert: Er hat
   keinen Platz fuer Abstaende, die eine ganze Seite vertraegt, und
   **gescrollt wird nicht** (am Bildschirm scrollt niemand). */
.infoboardchat {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: calc(var(--abstand) * 0.5);
  margin: 0;
  padding: 0;
  list-style: none;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}
/* **Was nicht passt, faellt oben weg - nicht unten** (18.10.2026,
   gemessen): Sechs Blasen brauchen 864 px, ein Bereich hat 424. *Ein Chat
   wird von unten gelesen*; abgeschnitten gehoert das Aelteste, nicht das,
   was gerade geschrieben wurde. `justify-content: flex-end` drueckt die
   Liste nach unten, `overflow: hidden` schneidet oben ab. */
.infoboardchat .chatbeitrag { margin: 0; flex: 0 0 auto; }

/* **Der Zettel am Bildschirm ist ein Zettel** - Spirale, roter Randstrich,
   Linien an der Zeile (Einkauf 2a). Was ein Bereich daran aendert: Er gibt
   ihm **keine feste Breite** (in der Maske sind es 720 px) und keinen
   Aussenabstand. */
.infoboardzettel {
  flex: 1 1 auto;
  max-width: none;
  width: 100%;
  margin: 0;
  min-height: 0;
  overflow: hidden;
}
/* **Das Blatt hoert am Bereich auf** (gemessen): Der Zettel der Maske ist
   fuer eine Seite gebaut und war im Bereich **355 px zu hoch**. Hier
   traegt er deshalb keinen eigenen Mindestplatz - *er ist so hoch wie
   sein Bereich, und was darunter laege, steht gar nicht erst da.* */
.infoboardzettel .zettelblatt {
  min-height: 0;
  height: 100%;
  overflow: hidden;
}

/* **Abhaken am Bildschirm** (18.10.2026, auf Wunsch: *„Aufgabe soll man
   abhaken können, es könnte ein Touchscreen sein."*).

   **Die ganze Zeile ist der Knopf** - am Touchscreen zielt niemand auf ein
   Kästchen von 20 px. Die Trefferfläche kommt aus `--knopf-flaeche`
   (`max(44px, var(--schaltflaeche))`, Abschnitt 15): 44 px sind der Boden,
   am Infoboard sind es 72.

   **Kein Textmarker** (Abschnitt 15): Das ist kein Wort mit einem Strich
   darunter, sondern eine Zeile mit einem Kästchen davor - *wie in der
   Aufgabenliste, nur groß.* Und kein `:hover`: Vor einem Bildschirm an der
   Wand liegt keine Maus. */
.infoboardhaken { margin: 0; }
.infoboardhakenknopf {
  all: unset;
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.5);
  width: 100%;
  min-height: var(--knopf-flaeche);
  cursor: pointer;
}
.infoboardkaestchen {
  flex: none;
  width: 0.9em;
  height: 0.9em;
  border: 2px solid var(--text-leise);
  border-radius: var(--radius-klein);
}
.infoboardhakenwort { min-width: 0; }
/* **Angefasst heisst angefasst** - die einzige Rueckmeldung, die ein
   Bildschirm geben kann, ist die, die beim Tippen entsteht. */
.infoboardhakenknopf:active .infoboardkaestchen {
  background: var(--akzent);
  border-color: var(--akzent);
}

/* Das Raster waehlen: zwei Auswahllisten nebeneinander (FAM-264). */
.infoboardraster {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--abstand);
  border: 0;
  padding: 0;
}

/* **Die Bereiche stehen im gewaehlten Raster** (FAM-264, 19.10.2026, auf
   Rueckmeldung am Geraet: *„die Boxen so platzieren, wie man die
   Bereichsaufteilung gewaehlt hat"*).

   **Das `grid-template-areas` steht am Element, nicht hier** - es ist ein
   **Datum** dieser Familie und keine Gestaltung (Regel 0), und es kommt aus
   derselben Stelle wie der Bildschirm (`infoboard.board_raster`): *Die
   Vorschau zeigt, was der Bildschirm zeigt* (FAM-261).

   **Gleich hohe Zeilen** (`grid-auto-rows: 1fr`) wie am Bildschirm - so
   sieht die Maske aus wie das, was sie einstellt. */
.infoboardbereiche {
  /* **Am Handy untereinander** (Regel 0): Drei Auswahllisten nebeneinander
     messen bei 375 px rund 111 px - *darin steht kein Modulname.* Mit
     `display: block` sind die `grid-area` an den Boxen gegenstandslos, und
     die Reihenfolge bleibt die des Rasters. */
  display: var(--infoboardboxen-anzeige);
  grid-auto-columns: 1fr;
  grid-auto-rows: 1fr;
  gap: var(--abstand);
  border: 0;
  padding: 0;
}

/* **Die Legende steht ueber dem Raster, nicht darin** - ein `<legend>` in
   einem Raster waere eine Zelle, und die erste Box rutschte ein Feld
   weiter. */
.infoboardbereiche > legend { grid-column: 1 / -1; }

/* Eine Box: das Wort, die Auswahl, das Haekchen. */
.infoboardfeld {
  /* **Der Abstand gilt in beiden Faellen** - `gap` wirkt nur im Raster,
     also traegt die Box ihn selbst, wo sie untereinander steht. */
  margin-bottom: var(--abstand);
  display: flex;
  flex-direction: column;
  gap: calc(var(--abstand) * 0.5);
  padding: var(--abstand);
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  background: var(--flaeche);
}


/* Das Haekchen steht leise unter der Auswahl. */
.infoboardbreit {
  display: flex;
  align-items: center;
  gap: calc(var(--abstand) * 0.4);
  color: var(--text-leise);
  font-size: 0.9em;
}
.infoboardadresse code { overflow-wrap: anywhere; }

/* ============================================================================
   Abschnitt 17: DIE AUFKLAPPER (21.10.2026, auf einen Kommentar im Canvas)

   *„Das Auf und Zuklappsymbol muss viel deutlicher sein. Bitte einen leichten
   Grauen Rand um alles ziehen. Gilt hier für alles zum Aufklappen."*

   ## Was gemessen war

   Am **Handy** (375 px, Blatt geladen, 1299 Regeln): `--feldgruppe-rahmen`
   steht dort auf **0** (FAM-109: am Handy ist der Platz knapp, Feldgruppen
   tragen keinen Rahmen) – die Klappgruppe hatte also **gar keinen Rand**,
   und ihr Zeichen war **12 px**, so groß wie die Grundschrift.

   **Und es war zwölfmal dieselbe Entscheidung**: Zwölf Aufklapper setzen ihr
   ▾/▴ einzeln, verstreut über das ganze Blatt. **Zehn davon haben gar keinen
   Rand** – gemessen am Blatt, nicht geraten.

   ## Warum eine Ebene und keine zwölf Änderungen

   Wie die Frische Ebene (11), die Filzwand (12/14) und die Textmarker (15):
   **Wer sie herausnimmt, bekommt die Aufklapper von gestern zurück.** Die
   zwölf eigenen Regeln bleiben unberührt, sie bestimmen weiter *welches*
   Zeichen dasteht und wo – hier steht nur, **wie deutlich** es ist.

   ## Was NICHT dazugehört, und warum

   | | |
   |---|---|
   | **Das Menü** | `.menue` (der Hamburger), `.menuebereich` (die drei Bereiche vom 17.10.2026) und `.menuegruppe` – *„Navigationseinträge bleiben Menüzeilen"* (Auftrag Design 2a, Abschnitt 8). `.menuegruppe` trägt dort schon einen `border-top` als Trenner; ein Kasten drumherum wäre eine zweite Sprache im Menü |
   | **Die Feldhilfe** | Das Fragezeichen hinter einer Beschriftung ist **kein Kasten**, sondern ein Zeichen im Fließtext (28.09.2026) |
   | **Die Punktemenüs** | `.chatmenue`, `.abomenue`, `.listenwahl`, `.listenfarbe` – sie öffnen ein **Menü**, keinen Block, und tragen deshalb gar kein Dreieck |

   *Die Grenze ist nicht geraten: Wer ein Dreieck trägt, klappt einen Block
   auf; wer ein Menü öffnet, trägt keines.*
   ============================================================================ */

/* **Das Zeichen, an einer Stelle statt an zwölfen.**

   `1.5em` und nicht eine feste Zahl: Es wächst mit der Ansicht mit, ohne eine
   Zeile in Regel 10 – gemessen **18 px** am Handy (vorher 12), 25,5 am
   Tablet, 24 am Desktop, 36 am Infoboard.

   **`line-height: 1` gehört dazu**: Ohne sie macht ein 1,5-faches Zeichen die
   Kopfzeile höher, und der Kopf hat seine Höhe aus `--knopf-flaeche`.

   **Die Farbe bleibt, wo sie steht.** Fünf der zwölf setzen `--text-leise`,
   einer (`.brett-mehr`) den Akzent – *eine Ebene, die das überschreibt, nimmt
   eine Entscheidung mit, die niemand zurückgenommen hat.* In 18 px ist auch
   ein leises Dreieck deutlich. */
.klappgruppe > summary::after,
.erledigte > summary::after,
.artikel-aendern > summary::after,
.terminfilter > summary::after,
.esserwahl > summary::after,
.leihe-aendern > summary::after,
.farbfrei > summary::after,
.kontakt-aendern > summary::after,
.brettkarte-aendern > summary::after,
.projekt-werkzeug > summary::after,
.brett-mehr > summary::after,
.ahnmaske > summary::after,
.importanleitung > summary::after,
.menuegruppe > summary::after {
  font-size: 1.5em;
  line-height: 1;
}

/* **Der Aufklapper einer Ahnenzeile setzt sein Zeichen selbst** - er ist
   seit dem 25.10.2026 der Kopf der Zeile, und die Zeile trägt ihren Rahmen
   schon (`.ahnzeile`): *Ein Kasten im Kasten wäre ein Käfig um jeden
   Menschen.* Deshalb steht er oben beim Zeichen und **nicht** in der
   Rahmenliste darunter. */
.ahnmaske > summary::after {
  content: "▾";
  color: var(--text-leise);
}
.ahnmaske[open] > summary::after { content: "▴"; }

/* **Der leichte graue Rand** – `--linie`, dieselbe Farbe wie jede andere
   Trennlinie im Blatt, und `--radius` wie jeder andere Kasten. *Keine neue
   Farbe, keine neue Zahl.*

   **1 px steht hier als Zahl, und das ist kein Verstoß gegen Regel 0**: Ein
   Rand ist in allen vier Ansichten einer – wie `--radius-klein`, das überall
   2 trägt. Eine Merkmalsvariable mit viermal demselben Wert wäre eine Zeile
   in Regel 10, die nichts entscheidet.

   **`--feldgruppe-rahmen` wäre der falsche Griff gewesen**: Er steht am Handy
   auf 0, *und genau das war der Befund.* Ihn dort auf 1 zu setzen, träfe
   jede Feldgruppe in jeder Maske – FAM-109 hat ihn aus gemessenem Grund auf 0
   gestellt. */
.klappgruppe,
.erledigte,
.artikel-aendern,
.terminfilter,
.esserwahl,
.leihe-aendern,
.farbfrei,
.kontakt-aendern,
.brettkarte-aendern,
.projekt-werkzeug,
.importanleitung,
.brett-mehr {
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  /* **Ein Rand ohne Polster lässt den Text am Rand kleben.** `--abstand / 2`
     ist der halbe Normalabstand und keine erfundene Zahl. */
  padding: calc(var(--abstand) / 2);
}

/* **Die Klappgruppe behält ihr größeres Polster, wo sie eines hat** – ab
   Tablet trägt `--feldgruppe-innen` 20 px, und das war eine Entscheidung
   (FAM-109). `max()` nimmt den größeren der beiden, wie `--knopf-flaeche`
   seit dem 27.09.2026: *44 px sind der Boden, nicht die Decke.* */
.klappgruppe {
  padding: max(var(--feldgruppe-innen), calc(var(--abstand) / 2));
}

/* --- Die Hauptaktion neben dem Titel (21.10.2026) -------------------------

   *„Termin eintragen kann in die Zeile darüber aber rechtsbündig wechseln,
   ich habe Platz geschaffen."*

   **Dieselbe Zelle wie die Werkzeugleiste** - Spalte 3, rechtsbündig, und
   **ohne `min-width: 0`**: Ihr Platzbedarf zählt, sonst meldet sie dem
   Raster eine Breite von null und landet über dem Titel. *Das war der
   Fehler, der am 29.09.2026 drei Runden gekostet hat.* */
.kopfaktion {
  grid-column: 3;
  justify-self: end;
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 4px;
}

/* **Und dann gehört die Leiste in die zweite Zeile** - beide in Spalte 3
   wären zwei Kästen in einer Zelle. Dieselbe Bauart wie die
   Drei-Elemente-Regel darüber: ganze Breite, linksbündig, *denn Regel 1
   sagt „die Hauptaktion zuerst"* - und die steht jetzt eine Zeile höher. */
.titelzeile:has(.kopfaktion) {
  grid-template-columns: minmax(auto, 1fr) minmax(0, auto) minmax(auto, 1fr);
}
.titelzeile:has(.kopfaktion) .werkzeugleiste {
  grid-column: 1 / -1;
  justify-self: stretch;
  justify-content: flex-start;
}

/* **Und steht darunter nichts, nimmt sie die ganze Zeile** - rechtsbündig
   bleibt sie trotzdem (`flex-end` oben).

   Der Fall ist die offene Terminmaske: Dort ruht die Navigation
   (21.10.2026), die Zeile darunter ist also leer, **und in einer
   Rasterspalte von einem Drittel brechen zwei Knöpfe um**. Gemessen am
   Handy: *Eintragen* bei y = 80, *× Schließen* bei y = 128 - zwei Zeilen
   für zwei Knöpfe, obwohl 333 von 343 px frei lagen.

   **Keine Media Query und kein neues Merkmal** (Regel 0): Gefragt wird
   nach dem, worum es geht - *liegt unter mir eine Leiste?* Am Handy spart
   das 48 px Kopfhöhe, in den anderen drei Ansichten standen die zwei
   Knöpfe schon in einer Zeile, dort ändert es nichts. */
.titelzeile:not(:has(.werkzeugleiste)) .kopfaktion {
  grid-column: 1 / -1;
  justify-self: stretch;
}

/* **Und am Handy traegt die Zeile keinen Trenner** (21.10.2026).

   *„Diese Zeile in die gleiche Zeile bringen wie Termin eintragen"* - und
   gemessen fehlten dafuer **7,4 px**: Die vier Knoepfe wiegen 334,4 px plus
   vier Luecken, die Gruppe ist 343 px breit. **Der Trenner wiegt 9** (1 px
   Strich, 2x4 px Rand), mit ihm faellt eine Luecke weg: **337,4 px**, und
   die Zeile ist eine.

   **Es ist eine Frage der Ansicht, keine der Maske** (Regel 0) - deshalb
   ein Merkmal und kein `@media`: Wer am Desktop *Handy* waehlt, bekommt
   dieselbe Entscheidung.

   > **Was er kostet, ist ausgesprochen**: Regel 1 teilt Zusammengehoerendes
   > mit einem Trenner ab. *Am Handy teilt der Umbruch* - und ein Strich, der
   > eine Zeile kostet, ist teurer als der Strich wert ist. */
.kopfaktion .trenner {
  display: var(--kopftrenner-anzeige);
}

/* **Der zweite Teil eines Knopfwortes** (21.10.2026) - *„+ Termin"* am
   Handy, *„+ Termin eintragen"* ab Tablet.

   **Das ist Regel 6, nicht eine Abkuerzung**: *„Wo der volle Name nicht
   passt, steht die Kurzform ... Der volle Name bleibt im
   `title`-Attribut."* Gemessen brauchte die Zeile am Handy **345,4 px** bei
   **343 px** Platz - *2,4 px*, und mit dem Zusatz faellt sie auf 271,8.

   **Ein allgemeines Merkmal, kein Sonderfall der Terminmaske**: Jeder
   Knopf, dessen Wort am Handy zu lang ist, kann seinen hinteren Teil so
   wegnehmen. *Eine Regel fuer einen Knopf waere eine, die der naechste
   nicht findet.* */
.knopfzusatz {
  display: var(--knopfzusatz-anzeige);
}

/* ---------------------------------------------------------------------------
   Die Schnittstelle (FAM-272)

   **Keine neue Zahl und keine neue Farbe.** Abstand, Radius, Linie und
   Flaeche kommen aus Regel 10 und Abschnitt 1; die einzige nackte Zahl ist
   ein `1px`-Rand, und die gilt in allen vier Ansichten (dieselbe Begruendung
   wie bei den Aufklappern in Abschnitt 17 und bei `--radius-klein`).

   **Und keine eigene Feldregel**: Die Maske benutzt `.maskenfeld` und
   `.feldbreit` wie jede andere Eingabemaske (A6, und der Fund vom
   24.10.2026: *eine Regel hinter einer fremden Klasse hilft der naechsten
   Maske nicht*).
   --------------------------------------------------------------------------- */

/* **Der Kasten, in dem das Kennzeichen steht** - genau einmal, nach dem
   Anlegen. Er ist betont, weil man ihn nicht uebersehen darf: *Wer ihn
   wegklickt, bekommt ihn nie wieder.* */
.neuerschluessel {
  border: 1px solid var(--akzent);
  border-radius: var(--radius-karte);
  background: var(--akzent-blass);
  padding: var(--abstand);
  margin-block: var(--abstand);
}

/* **Umbrechend und zum Markieren**: Ein Kennzeichen ist 52 Zeichen lang und
   passt am Handy in keine Zeile. `user-select: all` nimmt dem Kopieren den
   Teil, an dem es schiefgeht - *wer die Haelfte markiert, bekommt einen
   Schluessel, der nicht funktioniert, und sucht den Fehler im Programm.* */
.kennzeichen {
  word-break: break-all;
  user-select: all;
  font-family: var(--schrift-fest, monospace);
  background: var(--flaeche);
  border: 1px solid var(--linie);
  border-radius: var(--radius-klein);
  padding: calc(var(--abstand) / 2);
}

.kennzeichenhinweis {
  color: var(--text-leise);
  font-size: 0.9em;
}

/* **Das Schreibrecht steht abgesetzt** - mit seiner Warnung und dem Passwort
   daneben, nicht als dritter Haken zwischen den Feldern. *Eine
   Sonderentscheidung, die aussieht wie eine Einstellung, wird eine.*

   Der Browser gibt einem `<fieldset>` Rahmen und eingerueckte Legende; die
   Legende steht hier wie jede Beschriftung (A6). */
.schreibrecht {
  border: 1px solid var(--linie);
  border-radius: var(--radius);
  padding: var(--abstand);
  margin-block: var(--abstand);
}

.schreibrecht > legend {
  padding-inline: calc(var(--abstand) / 2);
  font-weight: 600;
}

/* **Die Liste der Schluessel** - eine Karte je Zeile, wie bei den Geraeten,
   aber mit eigenen Klassen: *Eine Regel hinter einer fremden Klasse hilft der
   naechsten Maske nicht.* */
.schluesselliste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
}

.schluesselzeile {
  border: 1px solid var(--linie);
  border-radius: var(--radius-karte);
  background: var(--flaeche);
  padding: var(--abstand);
}

/* **Ein zurueckgenommener bleibt stehen und wird blass** - die Zeile ist die
   Auskunft, wem man einmal Zugang gegeben hat (FAM-34). *Blass heisst nicht
   weg*, und das Wort daneben sagt es ohnehin (FAM-65). */
.schluessel-weg {
  opacity: 0.5;
}

.schluesselkopf {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: calc(var(--abstand) / 2);
}

.schluesselname {
  font-weight: 700;
}

.schluesselstand {
  color: var(--text-leise);
  font-size: 0.9em;
}

/* **Die Angaben umbrechen, statt zu scrollen** (Regel 2): Am Handy stehen
   sie untereinander, ab Tablet nebeneinander - ohne eine eigene
   Merkmalsvariable, denn das entscheidet hier der Platz und nicht die
   Ansicht. */
.schluesselzeilen {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--abstand) / 2) var(--abstand);
  color: var(--text-leise);
  font-size: 0.9em;
  margin-block: calc(var(--abstand) / 2);
}


/* ============================================================================
   Abschnitt 18: DIE DURCHSICHTSLISTE DES IMPORTS (FAM-278, 08.11.2026)

   *„Beim Importieren wäre es mir wichtig, wenn ich vorher eine Auswahlliste
   noch bekomme."*

   ## Keine neue Merkmalsvariable, keine neue Farbe

   Abstand, Radius, Linie und die Trefferfläche kommen aus Abschnitt 1 und
   Regel 10; die Lage einer Zeile trägt **kein eigenes Grün und kein eigenes
   Rot**, sondern den Akzent und `--text-leise`. *Vier Farben für vier Lagen
   wären vier Bedeutungen, die man erst lernen muss* – und das Wort steht
   ohnehin daneben (FAM-65: das Wort trägt die Aussage, nicht die Farbe).

   ## Die Filterleiste sieht aus wie die der Freundesliste – und ist eine andere

   Sie benutzt `.freundefilter` für die Leiste (Abschnitt 8) und **eine eigene
   Klasse für die Wahl**. Der Grund ist nicht Geschmack, sondern eine Wache:

   **Der Filter muss aus `<button>` bestehen**, denn er trägt die
   hochgeladene Datei mit – *ein Verweis ließe sie fallen* (FAM-278). Und
   sobald ein `<button>` die Klasse trägt, verlangt
   `test_die_trefferflaeche_wird_an_einer_stelle_gerechnet` **`--knopf-flaeche`**
   statt `--schaltflaeche`: 44 px sind der Boden (Abschnitt 15).

   > **Gemessen hat es die Wache, nicht das Nachdenken.** Der erste Entwurf
   > benutzte `.freundefilter-wahl` mit vier zurücknehmenden Regeln – und
   > machte damit **die Leiste der Freundesliste mit** zu einer Knopfleiste:
   > Die Wache meldete *beide* Selektoren. *Eine Klasse, die an einem
   > `<button>` hängt, ändert die Zusage für jede Stelle, an der sie schon
   > steht.*

   **Ein `<button>` bekommt in Abschnitt 15 `all: unset`** – der Chip wird
   deshalb hier ganz gezeichnet, statt eine fremde Regel zu überschreiben.
   Zurückgenommen wird nur, was Abschnitt 15 **danach** noch setzt: der
   **Textmarker** als Hintergrundverlauf, die 17 px fett in der Handschrift
   und die Drehung beim Überfahren. *Ein Chip, der sich dreht, ist ein Knopf;
   der gewählte Ausschnitt soll stehen.*

   **Die Gewichte sind nachgerechnet, nicht geraten**: `button` ist (0,0,1),
   `.importfilter-wahl` ist (0,1,0) und gewinnt damit **unabhängig davon, wie
   weit hinten es steht**; `button:hover` ist (0,1,1), `.importfilter-wahl:hover`
   (0,2,0) (die Lehre aus Abschnitt 15 und FAM-107).

   **Und es gibt hier keine Fokusregel.** Abschnitt 15 gibt jedem `<button>`
   im Fokus einen `outline` **und nimmt den Schatten weg** – also eine Linie
   und nicht zwei (`test_ein_feld_im_fokus_bekommt_eine_linie_und_nicht_zwei`).
   *Der Chip-Rahmen weicht beim Tabben dem Umriss; eine eigene Regel hätte
   beides gezeigt.*
   ============================================================================ */

.importfilter-wahl {
  display: inline-flex;
  align-items: center;
  min-height: var(--knopf-flaeche);
  padding: 0 var(--abstand);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px var(--linie);
  background-image: none;
  background-color: transparent;
  color: inherit;
  cursor: pointer;
  font-family: inherit;
  font-size: inherit;
  font-weight: inherit;
  white-space: nowrap;
}

.importfilter-wahl.aktiv {
  background-color: var(--akzent-blass);
  box-shadow: inset 0 0 0 2px var(--akzent);
  font-weight: 600;
}

.importfilter-wahl:hover {
  transform: none;
  box-shadow: inset 0 0 0 2px var(--akzent);
}

/* --- Wem die neuen Einträge gehören ------------------------------------- */

/* **Eine Zeile für sich**, nicht in der Filterleiste: Die eine beantwortet
   *„was will ich sehen"*, die andere *„wem gehört das nachher"* – und zwei
   Fragen in einer Leiste sähen aus wie eine Auswahl, in der man nur eines
   wählen kann (wörtlich der Schlagwortfilter, FAM-157). */
.importgehoert {
  margin-bottom: var(--abstand);
}

/* --- Die Liste ---------------------------------------------------------- */

.importliste {
  list-style: none;
  margin: 0 0 var(--abstand);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--abstand);
}

/* **Eine Karte je Mensch**, wie die Grundkarte aus A7 (27.09.2026): Eine
   Zeile mit vier Angaben darunter ist keine Zeile mehr. */
.importzeile {
  background: var(--flaeche);
  border-radius: var(--radius-karte);
  box-shadow: inset 0 0 0 1px var(--linie);
  padding: var(--abstand);
}

/* **Der Haken und der Name sind EIN Label** – die ganze Zeile ist die
   Trefferfläche. *Ein Kästchen von 13 px ist am Handy kein Ziel*; 44 px sind
   der Boden (Abschnitt 15). */
.importhaken {
  display: flex;
  align-items: center;
  gap: var(--abstand);
  min-height: var(--knopf-flaeche);
  cursor: pointer;
}

/* **Ohne Haken kein Zeiger** – *ein Zeiger, der auf nichts Klickbarem steht,
   behauptet etwas Falsches* (Regel 7). */
.importhaken-ohne {
  cursor: default;
}

.importname {
  font-weight: 600;
}

/* **Die Lage steht als Wort da, leise** – sie ist die Auskunft, nicht die
   Hauptsache: Der Name ist die Hauptsache. */
.importlage {
  display: block;
  color: var(--text-leise);
  font-size: 0.9em;
}

/* **Die Kontaktwege und die Änderungen rücken ein** – sie gehören zum Namen
   darüber, und ohne Einzug sähe die Karte aus wie eine Liste gleichwertiger
   Zeilen. */
.importwege,
.importaenderungen {
  list-style: none;
  margin: calc(var(--abstand) / 2) 0 0;
  padding-inline-start: calc(var(--abstand) * 2);
  color: var(--text-leise);
  font-size: 0.9em;
}

.importfeld {
  font-weight: 600;
}

/* **Der Gedankenstrich für „bei uns steht nichts"** – und er ist leise:
   *Eine Lücke ist keine Aussage* (Regel 5). */
.importleer {
  color: var(--text-leise);
}

.importdublette {
  margin: calc(var(--abstand) / 2) 0 0;
  padding-inline-start: calc(var(--abstand) * 2);
  font-size: 0.9em;
}

/* **Was schon dasteht, steht gedämpft da** – es ist die Zeile, bei der
   nichts passiert, und *ein Wort, das immer gleich laut ist, wird überlesen*
   (FAM-94). */
.importzeile-schon_da {
  opacity: 0.65;
}

/* --- Die Anleitung je Portal (FAM-279, 08.11.2026) ---------------------- */

/* **Ein Aufklapper wie jeder andere** – Rand, Polster und Zeichengröße
   kommen aus Abschnitt 17, wo `.importanleitung` in **beiden** Listen steht.
   Hier steht nur, **welches** Zeichen – so wie jeder der zwölf es für sich
   sagt.

   **Zugeklappt**, denn wer die Datei schon hat, braucht den Weg dorthin
   nicht: *Eine Anleitung, die immer offen steht, schiebt das Feld nach
   unten, um das es geht.* */
.importanleitung > summary::after {
  content: "▾";
  color: var(--text-leise);
}

.importanleitung[open] > summary::after { content: "▴"; }

/* **Die Überschriften darin sind klein** – sie gliedern vier Wege, sie
   kündigen keinen Abschnitt der Seite an. *Vier Seitentitel in einem
   Aufklapper wären lauter als die Maske darunter.* */
.importanleitung h3 {
  font-size: 1em;
  margin: var(--abstand) 0 calc(var(--abstand) / 4);
}

.importanleitung > summary + h3 {
  margin-top: calc(var(--abstand) / 2);
}

.importanleitung p {
  margin: 0;
}

.importanleitung {
  margin-bottom: var(--abstand);
}

/* **Der Satz unter dem Knopf, und er steht auch am Handy da** (11.11.2026) -
   kein `.feldhinweis`, den `--feldhinweis-anzeige` dort ausblendet
   (FAM-109), denn **ausgerechnet das Handy ist das Geraet, auf dem es den
   Knopf ueberhaupt gibt.** Woertlich die Rechnung von `.filterpreis`.

   **Gefunden hat es eine Messung, nicht das Nachdenken**: Mit
   `.feldhinweis` war der Satz gemessen **0 px** hoch - im Dokument, im
   Vorlesebaum, und fuer das Auge nicht da.

   **Der Fehlersatz daneben traegt `.hinweiskasten` und bleibt davon
   unberuehrt**: *Was erklaert, ist leise; was schiefging, ist es nicht.* */
.kontakteholen-hinweis {
  margin: calc(var(--abstand) / 2) 0 0;
  color: var(--text-leise);
  font-size: 0.92em;
  max-width: 62ch;
}

/* **Der Griff ist eine Bedienfläche** – und das war der Fund des
   Ansichtsdurchgangs: Gemessen war er **20 px** hoch, so hoch wie seine
   Textzeile. *Ein Aufklapper, den man am Handy mit dem Daumen verfehlt, ist
   zugeklappt und bleibt es* – 44 px sind der Boden (Abschnitt 15).

   **Und das eigene Dreieck braucht die Flex-Zeile**: Ohne sie steht es
   direkt hinter dem Text statt am rechten Rand, und das Dreieck des
   Browsers steht zusätzlich davor. *Zwei Zeichen für eine Handlung sehen
   aus wie zwei Handlungen.* */
.importanleitung > summary {
  display: flex;
  align-items: center;
  gap: var(--abstand);
  min-height: var(--knopf-flaeche);
  cursor: pointer;
  list-style: none;
  font-weight: 600;
}

.importanleitung > summary::-webkit-details-marker { display: none; }
.importanleitung > summary::after { margin-left: auto; }
