/* css/system.css — DAS System. Wenige Token, wenige Regeln.
 *
 * Stefan 2026-08-02: „system hinterlegen. system muss einfach sein. nicht 1000
 * regeln sondern einheitliche einfache regeln als token" · „farben wirken immer
 * noch altbacken … der rosa hintergrund. bitte moderner und cleaner."
 *
 * ══ WARUM DIESE DATEI ZULETZT KOMMT ══════════════════════════════════════
 * Sie wird als letzte eingebunden (index.php) und gewinnt deshalb ohne ein
 * einziges `!important`. Wer hier etwas ändert, ändert es überall — das ist
 * der Sinn. Wer irgendwo anders einen Farbwert hinschreibt, hebelt das System
 * aus; solche Stellen gehören hierher zurück.
 *
 * ══ WAS SICH AN DEN FARBEN GEÄNDERT HAT ══════════════════════════════════
 * Der Grund war ein rosa Verlauf (`#fef4f5` plus zwei rosa Radialen). Ein
 * eingefärbter Hintergrund lässt jede Fläche darüber mitfärben: weisse Karten
 * wirken cremig, Grautöne bekommen einen Stich, und Farbe, die etwas BEDEUTEN
 * soll, hebt sich nicht mehr ab. Genau das liest man als „altbacken".
 *
 * Jetzt: ein neutraler, sehr heller Grund. Die Markenfarbe bleibt unverändert
 * — sie wird nur wieder zum Akzent statt zur Grundstimmung. Farbe trägt jetzt
 * Bedeutung: rosé = die Marke und das, was man auslöst; die Hub-Farben (siehe
 * js/nav-registry.js) = wo man ist; grau = alles andere.
 *
 * ══ DIE VIER REGELN ══════════════════════════════════════════════════════
 *   1. Tippfläche mindestens `--nv-tap` (44 px). Keine Ausnahme bei Knöpfen.
 *   2. Abstände nur aus `--nv-s1..s5`. Keine krummen Zwischenwerte.
 *   3. Ecken nur aus `--nv-r-*`. Drei Stufen reichen.
 *   4. Schrift nur aus `--nv-t-*`. Vier Stufen reichen.
 * Mehr Regeln gibt es nicht. Was hier nicht steht, ist keine Regel.
 *
 * VERLINKUNGEN: docs/DESIGN-SYSTEM.md (die geschriebene Fassung inkl.
 * „Karte oder ganze Seite?"), css/keeva-ui.css (Basis-Token, die hier
 * überschrieben werden), js/nav-registry.js (Hub-Farben),
 * docs/DEVLOG.md 2026-08-02.
 */

/* ══ 1. FARBEN ═══════════════════════════════════════════════════════════ */

[data-theme="light"] {
  /* ══ ROSÉ ATELIER (Stefan-Entscheid 2026-08-25) ═══════════════════════
     «wir betreiben die software primär für frauen … sie sollte sich wie
     eine freundin anfühlen» → aus drei Mockup-Richtungen waehlte Stefan
     Rosé: «ja 1 aber arbeite mit gradients und blurs.»

     ⚠ DAS KEHRT DEN ENTSCHEID VOM 2026-08-02/03 BEWUSST UM. Damals:
     «der rosa hintergrund, bitte moderner und cleaner» → neutrales
     Off-White #FAF9F7. Der alte Grund war aber #fef4f5 FLACH — Rosa als
     Anstrich. Rosé Atelier ist etwas anderes: ein Verlauf mit Licht oben
     und Rosé-Atem unten, Karten bleiben reinweiss, der Akzent bleibt
     Akzent. Nicht der alte Zustand, sondern die Fassung, die Stefan an
     den Referenz-Screens abgenommen hat (Artefakt «Drei Gesichter»).
     Wer hier je wieder auf Neutral dreht: erst Stefan fragen — es ist
     das dritte Mal, dass diese Flaeche die Richtung wechselt.

     Diese Datei laedt zuletzt und GEWINNT — deshalb stehen die Werte HIER.
     keeva-ui.css + design-tokens.css tragen dieselben Werte als Basis;
     wer hier aendert, zieht beide nach (sonst driftet der Rueckfall). */
  /* Flaeche, Text, Trennlinie: die WERTE stehen seit E1 (2026-09-01) in
     css/design-tokens.css §1 als semantische Marken — EIN Satz, zwei
     Belegungen. Die --k-* Namen bleiben (680 / 663 / 326 Stellen) und
     zeigen dorthin. Wer die Farbe aendert, aendert sie dort. */
  /* Demo-Grund: heller Rosé-Ton, oben ein Hauch Weiss — die FARBE kommt
     von den Blobs, nicht aus dem Grund selbst (Stefan 2026-08-25:
     «hintergrundfarbe sieht im design besser aus»). */

  /* Text: warm-neutral. Der Violettstich von frueher (#85808a) bleibt
     draussen — warm heisst der GRUND, nicht die Schrift. */


  /* Schatten warm getoent (Rosé-Braun-Basis): neutrales Grau auf
     Blush-Grund wirkt schmutzig, warmes Braun wirkt weich. */

  /* ── ROSÉ-ATELIER-BAUSTEINE (aus der abgenommenen Demo, 2026-08-25).
     JEDER Gestaltungswert der Demo lebt hier als Token — die Module
     verweisen nur noch (Gesetz 2 der Spez, gemessen vom design-guard). */
  /* Chip-Toene (max. 3 gleichzeitig sichtbar je Screen, §2) */
  /* Gold NUR fuer Member/Geburtstag (§2) */
  /* Treibende Farbwolken im Grund (Demo .blob) */

  /* ══ MODUL-FARBEN — EINE FAMILIE (Stefan-Freigabe 2026-08-27, Fassung B)
     Der Befund: `js/nav-registry.js` vergab 16 rohe Farbwerte, 13 davon
     exakte Tailwind-Toene, dazu Material-Violett und Tailwind-400-Blau.
     Drei Paletten nebeneinander — das Problem war nie die ANZAHL der
     Toene, sondern dass sie aus verschiedenen Welten stammen: die
     Tailwind-500er sind kraeftig und hochgesaettigt, das Rosé der Marke
     ist weich. Nebeneinander schreit das eine und fluestert das andere.
     Am Bild entschieden (docs/_diagnostic_results/icon-farbvarianten.png).

     Die Loesung behaelt jeden Farbton — Module bleiben am Icon
     unterscheidbar, was bei 87 Modulen eine echte Orientierungshilfe ist
     — bringt aber alle auf DIESELBE Saettigung und Helligkeit. Dicht
     beieinander liegende Toene sind dabei zusammengefallen (189/199,
     213/215/217, 160/173 Grad waren als eigene Farben ohnehin nicht
     unterscheidbar).

     WICHTIG: kraeftige Farbe ist ab jetzt fuer BEDEUTUNG reserviert
     (--k-red = ueberfaellig, --k-green = erledigt). Rot stand vorher an
     Member-Club, Mail und Datenschutz — dekorativ. Genau das nimmt einer
     Warnfarbe ihre Warnung. */
}

/* DUNKEL = DUNKLES ROSÉ, NIE SCHWARZ (Spez §2, abgenommene Demo).
   ⚠ Hier stand: «Gilt fuer BEIDE Dunkel-Namen — das aktive Dunkel-Theme
   heisst keeva-black, dark ist der Zwilling.» GEMESSEN 2026-09-01 (E1):
   die App setzt `data-theme` nie auf keeva-black — js/form.js:252 bildet
   den gespeicherten Namen auf `dark` ab, und `dark` ist das einzige,
   was im DOM steht. Die 12 `[data-theme="keeva-black"]`-Selektoren im
   Repo waren unerreichbar (Messregel 1); 10 sind entfernt, ihr Inhalt
   stand ohnehin in denselben Regeln unter `dark`. Zwei bleiben stehen
   (.kal-glass-grid, .kal-col-frei, unten): ihre Regelzeile traegt ein
   altes `!important`, und die Neuzugangs-Wache zaehlt eine umgebaute
   Zeile als neue — sie gehen mit D3 (!important-Abbau) heraus. */
[data-theme="dark"] {
  /* Modul-Farben im Dunkeln: gleiche Toene, etwas heller und leiser.
     Auf #1C1417 traegt dieselbe Helligkeit wie im Hellen nicht — der
     Ton wirkt stumpf, weil ringsum kein Licht ist. */

}

/* ══ 2. MASSE ════════════════════════════════════════════════════════════ */

:root {
  /* ── BEWEGUNG (Rosé Atelier, 2026-08-25 — docs/ROSE-ATELIER-SYSTEM.md §5).
     EIN System, drei Regeln: Oeffnendes federt von unten, Wechselndes gleitet
     seitlich, dauerhaft lebt genau EIN Element pro Screen. Werte hier
     aendern = die ganze App folgt. Untergrenze: iPhone 13 Pro. */
  /* Dauer-Atmer für das EINE lebende Element (Regel 3): FAB-Puls-Ring und
     Verlaufs-Atmen. Lang genug, dass es ruhig wirkt, nicht nervoes. */

  /* Der Marken-Verlauf — das EINE leuchtende Element (Knoepfe, JETZT, FAB). */

  /* Tippfläche. Rund 9 mm — die Breite einer Fingerkuppe. */

  /* Abstände — die Leiter. Hier stand: «Fünf Werte, alle Vielfache von 4.
     Wer einen sechsten braucht, hat meist ein Layout-Problem und kein
     Abstandsproblem.»
     ⚠ NACHGEMESSEN 2026-08-29 und damit widerlegt: 6 px kommt an
     982 Stellen in 99 Dateien vor — häufiger als die Stufe 4 px (1'029
     liegt knapp darüber) und dreimal so oft wie 24 px (280). Es war
     kein Layout-Problem, es war eine Stufe ohne Namen. Sie heisst
     jetzt `--nv-luft-0` und trägt ihre Rolle: Symbol zu Text.
     Die Vermutung stand hier über Monate und hat 982 Stellen davon
     abgehalten, einen Namen zu bekommen. */

  /* Die Polsterung IM Karteninhalt (Stefan-Entscheid B Teil 2).
     Nicht auf der Karte — die Karte ist der Behaelter und bleibt bei 0,
     wie die inset-grouped list bei Apple. Ein Wert, beide Themen. */

  /* Ecken. Klein für Felder, mittel für Karten, gross für Blätter. */
  /* Kalibriert 2026-08-25 (Demo Runde 7, Stefans Befund «unfoermig»):
     Rundung folgt der GROESSE — kleine Flaechen weniger, grosse mehr.
     NEU KALIBRIERT 2026-08-27 (Stefan: «Border-Radius passt gar nicht,
     vorher war mehr Radius schoener»). Die alten Werte waren 15 / 24 / 30
     plus harte 13 und 17 mitten im Baukasten — 13 und 17 sind keine
     Entscheidungen, sie sind Ueberbleibsel. Jetzt eine Leiter aus runden
     Zahlen, naeher an iOS, jede Stufe ein Token:
        klein  16   Chip, Symbolfeld
        mittel 20   Listenzeile, Suchfeld, Eingabe
        gross  26   Karte, Stat-Kachel
        sehr   32   Blatt, Hero, Nav
        Pille  999  /  Kreis 50 %
     Keine krummen Zwischenwerte mehr. Wer einen braucht, hat den falschen
     Baustein gewaehlt. */
  /* RADIEN PER FORMEL (E1, 2026-09-01 — Stefans Pruefbedingung 3:
     «innen = aussen − Abstand. Eine Formel, keine Liste.»)
     ZWEI Ausgangsradien, alles Innere rechnet sich daraus — so laufen
     verschachtelte Ecken parallel statt sich zu schneiden:
        Blatt  32          Ausgangsradius 1 — Blatt, Hero, Nav-Leiste
        Karte  26          Ausgangsradius 2 — Karte, Stat-Kachel
        Zeile  26 − 6 = 20 Listenzeile IN der Karte  (−luft-0, dicht)
        Chip   20 − 4 = 16 Chip IN der Zeile          (−s1)
     Die Abzugs-Stufen luft-0 und s1 wachsen mit dem Bildschirm NICHT
     (§5a) — sonst wuerde der Chip auf dem iPad eckiger. Die Werte sind
     dieselben wie vorher (16/20/26/32), nur die Herkunft ist jetzt
     eine Rechnung. Die Kapsel ist eine eigene Formfamilie
     (Pruefbedingung 4) und bleibt ausserhalb der Formel. */
  /* D-17 (Stefan-Entscheid 2026-09-06): «Drei Token sind der Kanon:
     --nv-r-blatt 32 · --nv-r-karte 26 · --nv-r-kapsel 999. Innen = aussen −
     Abstand, per Formel.» Gemessen vor dem Umbau: SECHS Namen, FUENF
     Werte — drei gesetzt (blatt 32, m 26, pille 999), zwei gerechnet
     (s 20, chip 16), einer Alias (l → blatt). Danach: die drei Kanon-Namen
     tragen die drei gesetzten Werte; `s` und `chip` bleiben die zwei
     Formel-Stufen aus der Karte. Einen vierten GESETZTEN Wert gibt es
     damit nicht mehr — 20 und 16 sind Rechnung, kein Wert.
     E4 (Stefan-Entscheid 7, Nachtlauf 06./07.09.2026): die Altnamen sind
     UMGESCHRIEBEN — `--nv-r-pille` (31 css-Verwender) ist WEG, `--nv-r-m`
     (22) und `--nv-r-l` (6 css + 2 js) haben je einen Rest und bleiben
     EINSTWEILEN Alias: `m` fuer die eine !important-Zeile in §10
     (.k-glass-card; ios-grouped.css:857 haelt mit eigenem !important
     dagegen, D3-Weg), `l` fuer `js/buchungen.js:245` und `:4937`
     (S-F-Datei, Block B; dort steht der Altname mit Ersatzwert 24px —
     ohne Alias 24 statt 32 px). Berechnete Werte identisch (Fassungs-
     Probe 147 Regeln hell + dunkel, Bauteil-/Stil-Probe). Vier
     ueberfluessige !important auf Kasse-Radien (§10) fielen dabei,
     gemessen ohne Unterschied.
     (4) Nacht 2 (06.09.2026, C-Entscheid): `m` ist WEG — die Gegenzeile
     in ios-grouped (jetzt Z. 933) fiel, nachdem die News-Karte eine
     Katalog-Karte wurde; §10 traegt `--nv-r-karte` ohne Vorrang. `l`
     bleibt fuer js/buchungen.js (S-F). */
  /* `--nv-r-nav` ist RAUS (E3, 2026-09-01): die Bodenleiste ist 66 px
     hoch, 32 px Radius war 1 px unter der Kapsel — ein «Drittes» nach
     Pruefbedingung 4. Sie traegt jetzt die Kapsel; der Token hatte
     genau einen Nutzer (css/app-chrome.css). */

  /* Schatten. GETRENNT nach Form und Platzbedarf — vorher war beides
     dieselbe Zahl (`--nv-schatten: 6px` war Weichzeichner UND Reserve),
     und deshalb konnte der Schatten nie weicher werden, ohne die
     Innenabstaende mitzureissen. Das war der eigentliche Grund fuer den
     Eindruck «schmuddelig»: 0 2px 6px ist kein Schatten, sondern ein
     enger dunkler Saum an der Kante.
     Die Farbe war NICHT das Problem — nachgemessen 2026-08-27:
     rgba(58,36,42) hat H 344°, das Marken-Rosé H 352°. Der Schatten war
     bereits aus dem Markenton abgeleitet, nicht neutralgrau. Geaendert
     ist die FORM: grosser Weichzeichner, kleiner Versatz, sehr niedrige
     Deckkraft. */
  /* ⚠ DIE RESERVE HAT SEIT 2026-08-30 EINEN EIGENEN NAMEN (Stefan D3,
     Parkliste N26). Vorher hiess sie `--nv-schatten` — genauso wie das
     Merkmal, das `.nv-schatten-reichweite` in §5 LISTENZEILE auf 14px
     umsetzt. Zwei Bedeutungen, ein Wort: die Katalogrechnung
     `calc(var(--nv-rand) - var(--nv-schatten-reserve))` lebte davon, dass
     der Rueckfallwert greift, waehrend ein gleichnamiges Merkmal mit
     einem ANDEREN Wert danebenstand. Gemessen war das folgenlos — die
     Klasse steht in keiner ausgelieferten Datei —, aber es ist dieselbe
     Familie wie die Layer-Falle: eine stille Verabredung, die beim
     ersten Markup-Zusatz kippt. Jetzt: EIN Name, EINE Bedeutung. */
  /* Organische Formen: Chips und Avatare sind nie perfekte Kreise (§3).
     Theme-unabhaengig — deshalb hier und nicht im Farb-Block. */

  /* Schrift. Vier Stufen — mehr macht die Ordnung unschärfer, nicht feiner. */
  /* ⚠ `--nv-t-titel` ist RAUS 2026-08-30 (Schrift-Paket, Sprosse B).
     Es war 20,48 px, `--k-fs-hervor` ist 17,60 px — 75 Vorkommen gegen 2,
     die haeufigere gewinnt (Stefan-Entscheid). Die zwei Stellen werden
     sichtbar kleiner: `.pm-name` (Z. 432) und `.nv-zustand-titel`
     (Z. 3628, die Ueberschrift im leeren Zustand), 20,48 -> 17,60 px.
     Von der Bauteil-Probe in allen drei Themen bestaetigt, mitsamt der
     Folge: `.nv-zustand` wird dadurch 4,3 px flacher.
     ⚠ `.pm-name` stand mit ZWEI Stufen im Code — hier `--nv-t-titel`,
     in style.css:4344 `--k-fs-hervor`. Dieselbe Klasse, zwei Werte;
     welcher gewann, entschied allein die Ladereihenfolge.
     ⚠ Der Rueckfall in system.css:3620 war `1.1rem` — der Wert von
     `--k-fs-hervor`, NICHT der von `--nv-t-titel`. Wer den Rueckfall
     gelesen haette, haette den falschen Wert gelesen. */
  /* ⚠ `--nv-t-text` ist RAUS 2026-08-30 (Schrift-Paket, Sprosse
     Fliesstext). Mit Weg B faellt das Mitwachsen weg, und damit wird der
     Telefon-Wert der einzige: 15.04 gegen 14.08 px sind 0.96 px — unter
     einem Pixel, also entscheidet Stefans Regel die Sprosse selbst, und
     die haeufigere gewinnt (`--k-fs-body`, 354 Vorkommen gegen 24).
     ⚠ Vor Weg B hatte diese Sprosse sich NICHT selbst entschieden: am
     Tablet waren es 1.60 px, am Desktop 2.08. Erst der Entscheid macht
     die Rechnung eindeutig — deshalb stand sie eine Runde still. */
  /* ⚠ `--nv-t-klein` ist RAUS 2026-08-30 (Schrift-Paket, Sprosse A).
     Es war 12,16 px, `--k-fs-caption` ist 11,52 px — 0,64 px Unterschied,
     unter einem Pixel, also entscheidet Stefans Regel die Sprosse selbst
     und die haeufigere gewinnt. Die 37 Stellen lesen jetzt
     `--k-fs-caption` (5 Stilblaetter, 4 JavaScript-Dateien).
     ⚠ Sie trugen dort DREI verschiedene Rueckfallwerte — .75rem, .78rem
     und .8rem — und keiner davon war der echte Wert .76rem. Ein
     Rueckfallwert, der vom Token abweicht, ist eine stille zweite
     Definition; die Rueckfaelle sind mit entfernt. */
  /* ⚠ `--nv-t-marke` ist RAUS 2026-08-30 (Schrift-Paket, Sprosse 1).
     Dieselbe Rolle traegt `--k-fs-caption` — 506 Vorkommen gegen 3, und
     der Unterschied liegt auf ALLEN DREI Bildschirmstufen unter 1 px
     (0.32 / 0.16 / 0.64). Damit entscheidet Stefans Regel die Sprosse
     selbst. Die drei Stellen lesen jetzt `--k-fs-caption`.
     ⚠ Sie verlieren dabei das Mitwachsen — siehe die offene Grundfrage
     in docs/SCHRIFT-PAKET.md. Bei unter 1 px auf jeder Stufe ist das
     der kleinstmoegliche Preis; bei den anderen Sprossen waere er es
     nicht, deshalb bleiben die stehen. */
}

/* ══ 3. DIE EINE PFLICHT-REGEL ═══════════════════════════════════════════ */
/* Jeder Knopf ist mindestens `--nv-tap` gross. Sonst trifft man ihn nicht,
   und alles andere am Aussehen ist dann egal.
   `:where()` gibt der Regel Gewicht 0 — sie setzt eine Untergrenze, ohne
   irgendeiner bestehenden Regel im Weg zu stehen. Eine Regel, die man
   überschreiben MUSS, um zu arbeiten, wäre keine Untergrenze. */
/* Die Untergrenze fuer Antippbares steht jetzt im Katalog (§11 KNOPF). */
/* Symbol-Knöpfe brauchen die BREITE mit — bei ihnen gibt es keinen Text, der
   sie auseinanderzöge. Die Höhe allein reicht nicht: gemessen wurden über zehn
   Module hinweg immer wieder 38x44 — hoch genug, zu schmal.
   `:has(> icon:only-child)` trifft jeden Knopf, dessen ganzer Inhalt ein
   Symbol ist — auch die, die keine Klasse tragen und inline gestylt sind.
   Das ist der Unterschied zwischen einer Regel und einer Liste von
   Einzelfällen: die Liste ist ab dem nächsten neuen Knopf wieder unvollständig. */
:where(button.nv-icon-btn, .nv-icon-btn, .k-icon-btn),
:where(button:has(> .material-symbols-rounded:only-child)),
:where(button:has(> .material-symbols-outlined:only-child)) {
  min-width: var(--nv-tap);
}

/* ══ 4. STARTSEITE: Gruss + Mitteilungen + Profil ════════════════════════ */
/* Sie stehen in EINER Zeile mit dem Gruss statt in einer eigenen Leiste
   darüber — der Gruss brauchte die Zeile ohnehin. Siehe den Kommentar in
   js/home-admin.js, warum es sie zwischenzeitlich gar nicht mehr gab. */

/* ══ 5. EINE ZAHL BRAUCHT KEINE 1120 PX ══════════════════════════════════ */
/* Auf dem Bildschirm blieben zwei Karten übrig, die sich über die ganze
   Breite zogen und darin eine einzige Zahl trugen — „TAGESZIEL CHF 0 / 800"
   auf 1120 px, „HEUTE GEARBEITET 0:00" auf 1088. Das ist keine Karte mehr,
   das ist ein Balken mit Text am linken Rand; das Auge muss den ganzen Weg
   zurück, um die nächste Zeile zu finden.
   Gemessen mit `node tools/ui-vorschau.cjs` (Rubrik „Karten über die ganze
   Bildschirmbreite"): breit UND fast leer.
   520 px ist die Breite, in der eine grosse Zahl mit Etikett noch als Einheit
   gelesen wird. Auf dem Telefon greift die Regel nicht — dort IST die volle
   Breite richtig, weil es keine andere gibt. */
@media (min-width: 1024px) {
  .k-home-progress-card,
  .zeit-status-card { max-width: 520px; }
}

/* ══ 6. PROFIL-KARTE ═════════════════════════════════════════════════════
   Stefan 2026-08-02: „hier stimmen striche nicht, abstände, positionierungen.
   alles zu weit am rand zum teil."

   Ursache war, dass die Karte ihr eigenes Blatt baute (eigener Griff, eigenes
   Kreuz, eigene Ränder) statt `showBottomSheet` zu benutzen — siehe den
   Kommentar an `openProfilMenu()` in js/form.js. Griff, Kreuz und Aussenrand
   kommen jetzt vom Blatt selbst; hier steht nur noch der INHALT.

   Alle Masse aus den Token. Kein einziger krummer Wert. */
.pm-inhalt { padding: 0 var(--nv-s1); }

.pm-avatar {
  width: 84px; height: 84px; margin: var(--nv-s2) auto var(--nv-s3);
  border-radius: 50%; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--k-brand); color: var(--k-text-auf-marke);
  font-size: 2rem; font-weight: 700;
}
.pm-avatar img { width: 100%; height: 100%; object-fit: cover; }
.pm-name { text-align: center; font-size: var(--k-fs-hervor); font-weight: 700; color: var(--k-text); }
.pm-role {
  text-align: center; font-size: var(--k-fs-caption); font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--k-text3); margin-top: var(--nv-s1);
}

.pm-checkin-toggle {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nv-s3);
  margin: var(--nv-s5) 0 var(--nv-s4);
  padding: var(--nv-s3) var(--nv-s4);
  border-radius: var(--nv-r-karte);
  background: var(--k-fill);
}
.pm-checkin-label { font-size: var(--nv-t-kopf); font-weight: 700; color: var(--k-text); }
.pm-checkin-status { font-size: var(--k-fs-caption); color: var(--k-text3); margin-top: 2px; }
/* Der Schalter war inline gebaut, mit fest verdrahteten Farben und einem
   `transform`, das im Aufbau stand. Jetzt eine Regel, ein Zustand. */
.pm-schalter {
  width: 52px; height: 32px; flex: 0 0 auto;
  border-radius: var(--nv-r-chip); padding: 3px;
  background: var(--k-border-strong);
  cursor: pointer; transition: background .3s;
}
.pm-schalter.ist-an { background: #34C759; }
.pm-schalter-knopf {
  width: 26px; height: 26px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2);
  transition: transform .3s;
}
.pm-schalter.ist-an .pm-schalter-knopf { transform: translateX(20px); }

.pm-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--nv-s2); }
/* `.pm-stat` (+ b, small) steht seit D-11 im Baukasten-Abschnitt am Dateiende
   als `.nv-stat--pm`. (Das Wort in Grossbuchstaben steht hier absichtlich
   nicht: tools/probe-abdeckung.cjs nimmt sein ERSTES Vorkommen als Marke.) */

/* Ein Strich, eine Stärke, ein Abstand — vorher gab es drei Varianten. */
.pm-divider {
  height: 1px; background: var(--k-border);
  margin: var(--nv-s4) 0;
}

.pm-item {
  display: flex; align-items: center; gap: var(--nv-s3);
  min-height: 56px; padding: var(--nv-s2) var(--nv-s2);
  border-radius: var(--nv-r-s);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.pm-item:active { background: var(--k-fill); }
.pm-item-icon {
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: var(--nv-r-s);
  display: flex; align-items: center; justify-content: center;
  background: var(--k-fill); color: var(--k-text2);
}
.pm-item-icon .material-symbols-outlined { font-size: 21px; }
.pm-item-label { flex: 1; font-size: var(--k-fs-body); font-weight: 600; color: var(--k-text); }
.pm-item-wert { font-size: var(--k-fs-caption); font-weight: 600; color: var(--k-text3); }
/* Der Pfeil sagt: hier geht es weiter. Ohne ihn sieht eine Zeile, die eine
   ganze Seite oeffnet, genauso aus wie eine, die nur umschaltet. */
.pm-item-pfeil { font-size: var(--k-fs-titel); color: var(--k-text4, var(--k-text3)); flex: 0 0 auto; }

/* ══ 7. PROFIL-TITELBILD IN DER KARTE ════════════════════════════════════
   Stefan 2026-08-02: „profilbild titelbild wird nicht schön und vollflächig
   dargestellt und in mitte einfach durch gradient abgeschnitten anstatt
   modern die ganze karte oben zu füllen."

   Zwei Ursachen, beide gemessen:

   1. Das Bild sass 20 px eingerückt (Hero links 20, Karte links 0). Ein
      Titelbild mit Rand ist kein Titelbild, sondern ein Foto in einem Rahmen.
   2. Der Verlauf war 60 % hoch UND blendete auf `--k-bg` — also auf die
      Seitenfarbe. Ab der Hälfte war vom Bild nichts mehr übrig; das ist der
      „mittige Schnitt".

   Jetzt: bündig über die ganze Kartenbreite, oben mit dem Radius der Karte,
   und der Verlauf nur noch als kurzer Schleier unten. Er hat genau EINE
   Aufgabe — den Namen über hellen Bildstellen lesbar halten —, und dafür
   reichen 45 % mit halber Deckkraft. */
/* Drei Regelsätze setzten `min-height:45vh/50vh` und `width:100vw` auf
   `.profil-hero--aiden` (css/style.css Z. 4885, 6519, 7059) — alle für die
   ganze Seite gedacht, alle im Blatt mitgreifend. Zwei liessen sich mit
   `:not(.profil-hero--insheet)` sauber abgrenzen; die dritte trägt kein
   `!important`, deshalb genügt hier die höhere Spezifität.
   `min-height` MUSS mit zurückgesetzt werden: `height: 168px` allein verliert
   gegen ein `min-height: 45vh` — die Untergrenze gewinnt immer. Genau daran
   ist der erste Anlauf gescheitert (gemessen: 380 px statt 168). */
/* Nacht 4 (Stefan 23, 06.09.2026): am Bildschirm ist das Blatt ein schwebendes
   Fenster (nv-consistency.css, ab 1024 px) — EINE Rundung 32 rundum. Am Telefon
   bleibt es unten buendig (js/bottom-sheet.js:84, nur oben gerundet). Die
   Laufzeit-Regel steht spaeter im Dokument; (0,2,0) schlaegt sie ohne Vorrang.
   Gemessen: 24 px mit Vorrang → 32 px, hell + dunkel. */
@media (min-width: 1024px) { body.k-ui .k-bsheet { border-radius: var(--nv-r-blatt); } }
.k-bsheet .profil-hero--insheet,
.k-bsheet .profil-hero--aiden.profil-hero--insheet {
  min-height: 0;
  /* Die Karte hat Innenabstand; das Bild hebt ihn für sich wieder auf. */
  width: auto;
  /* Genau die Polsterung des Blattes aufheben — nicht schaetzen.
     `--k-blatt-pad` kommt aus js/bottom-sheet.js; vorher stand die 20 px nur
     dort, und mein erster Versuch mit --nv-s4 (16 px) liess 4 px Rand stehen.
     Gemessen: Karte links 0, Bild links 4. */
  margin-left: calc(var(--k-blatt-pad, 20px) * -1);
  margin-right: calc(var(--k-blatt-pad, 20px) * -1);
  margin-top: calc(var(--nv-s3) * -1);
  border-radius: var(--nv-r-blatt) var(--nv-r-blatt) 0 0;
  overflow: hidden;
}
/* Mit Bild: eine Höhe, die trägt. 40 px sind ein Streifen, keine Fläche —
   die galten für den leeren Zustand und wurden auf das Bild mitvererbt. */
.k-bsheet .profil-hero--insheet:not(.profil-hero--empty) { height: 168px; }
.k-bsheet .profil-hero--insheet .profil-hero-bg-img {
  width: 100%; height: 100%;
  background-size: cover;
  background-position: center;
}
.k-bsheet .profil-hero--insheet .profil-hero-gradient-overlay {
  height: 45%;
  background: linear-gradient(to top, rgba(0,0,0,.42) 0%, rgba(0,0,0,.12) 55%, transparent 100%);
}
/* Der leere Zustand bleibt schmal — ohne Bild gibt es nichts zu zeigen, und
   eine leere 168-px-Fläche wäre nur verschenkter Platz. */
.k-bsheet .profil-hero--insheet.profil-hero--empty {
  height: 40px;
  margin-left: 0; margin-right: 0; margin-top: 0;
  border-radius: 0;
}

/* ══════════════════════════════════════════════════════════════════════
   LUFT ZWISCHEN KOPFZEILE UND INHALT
   ══════════════════════════════════════════════════════════════════════

   Stefan 2026-08-02: „teilweise zu nah positioniert bei lager: arrow oben
   links und rechts drei punkte ist zur suche zu nah. das überall fixen."

   Nachgemessen im Lager: **0 px** zwischen Kopfzeile und Suchfeld. Der
   Zurück-Pfeil und die drei Punkte klebten am Suchfeld.

   ══ WARUM DIE REGEL HIER STEHT UND NICHT IN keeva-ui.css ══════════════

   Dort gab es sie längst — und sie hat nie gewirkt. Der Grund ist eine
   Eigenheit von `@layer`, die man leicht übersieht:

       @layer components { .k-module-header + .nv-searchbar { margin-top: 20px } }
       *, ::before, ::after { margin: 0 }        ← ohne Layer

   **Styles ohne Layer schlagen JEDE Layer-Regel** — unabhängig von der
   Spezifität. Der Universal-Reset (Spezifität 0,0,0) gewann also gegen eine
   Regel mit zwei Klassen. Nachgewiesen mit `CSS.getMatchedStylesForNode`:
   beide Regeln greifen, berechnet wird 0 px.

   Diese Datei steht bewusst ausserhalb aller Layer (sie ist der Boden des
   Systems). Deshalb gehört die Regel hierher — nicht als `!important`,
   sondern an die Stelle, an der sie ohne Gewalt gewinnt.

   Gilt für alles, was direkt unter der Kopfzeile stehen kann. Die zweite
   Gruppe fängt den Fall ab, dass der Inhalt in einem Bildlauf-Kasten liegt
   (`.nv-page-scroll-area`) — dann ist nicht das Element der Nachbar der
   Kopfzeile, sondern sein Behälter.
   ══════════════════════════════════════════════════════════════════════ */
/* F1 (2026-09-02): EINE Marke, EINE Stelle, JEDER Nachbar. Vorher galt
   `--nv-s5` (24) nur fuer sieben aufgezaehlte Nachbarn; alles andere stand
   mit 0 direkt unter dem Kopf (gemessen bei 390: Titel→Inhalt 5.6 … 42.6 px
   je Modul).
   G1 (2026-09-02, Block G): die Regel `body.k-ui .k-module-header + *
   { margin-top: var(--nv-s3) }` stand HIER und ist RAUS. Gemessen an den
   58 Katalog-Koepfen bei 390: sie griff bei 10 nicht — bei 9 traegt der
   Nachbar einen Inline-Stil `margin:0 auto` (Inline schlaegt jede
   Stylesheet-Regel: cms_pages, seo_check, seo_rankings, geogrid,
   google_business, google_ads, content_ai, brand_voice, module), bei
   `dokumente` ist der Nachbar eine unsichtbare Datei-Eingabe
   (display:none), die den Abstand bekam und nicht zeigte. Ein Abstand,
   der am NACHBARN haengt, haengt davon ab, wer der Nachbar ist. Deshalb
   traegt jetzt der KOPF selbst seinen unteren Rand (`.k-module-header`
   `margin-bottom: var(--nv-s3)`, §22 — dieselbe Marke, eine Stelle), und
   `.nv-page-scroll-area` (Ausnahme 2) holt seine Schatten-Reserve mit
   negativem Rand daraus.
   Der Nachbar gibt seinen EIGENEN oberen Rand ab (wie Ausnahme 1 fuer den
   Seitenrand): die alte Regel hatte ihn stillschweigend ueberschrieben —
   ohne diese Zeile kam er zurueck, gemessen (Lauf 1 nachher, 390):
   aemtli 12.6 → 32.6. Gewicht (0,2,0) wie vorher; Ausnahme 2 (0,2,1)
   behaelt ihren negativen Rand. */
body.k-ui .k-module-header + * {
  margin-top: 0;
}

/* T1-2 (2026-09-04): DIESELBE REGEL, EINEN SCHRITT WEITER.
   Gemessen am 04.09. bei 390 px, Kopfzone endet in JEDEM Modul bei 56 px —
   der Kopf ist bereits einheitlich. Der Inhalt beginnt trotzdem
   verschieden:
       lager · kasse · team     68 px   (56 + `--nv-s3`)   ✔ richtig
       kunden                   84 px   (56 + 12 + 16)     ✘ Ausreisser
   URSACHE: `+ *` trifft nur den DIREKTEN Nachbarn. Wo ein Roll-Behaelter
   dazwischenliegt (`.nv-page-scroll-area`, 12 Module), ist der Nachbar
   der Behaelter — und dessen erstes Kind bringt seinen eigenen oberen
   Rand mit (`.nv-page-search`: `margin-top: var(--nv-header-to-search)`,
   css/design-system.css). Der Abstand stapelt sich dann zu 12 + 16.
   Es ist also KEIN zweiter Wert noetig: der Abstand kommt weiterhin aus
   `--nv-s3` am Kopf (§22, eine Stelle). Diese Zeile nimmt nur den
   zweiten, ungewollten Rand weg.
   ⚠ NICHT als `> *` schreiben: nur das ERSTE Kind steht unter dem Kopf.
   Ein Abstand zwischen dem zweiten und dritten Kind ist Absicht und
   gehoert dem Bauteil.
   Stefans Befund «Kasse zu tief» loest sich damit auf: die Kasse steht
   dort, wo alle stehen — Kunden stand 16 px tiefer. */
body.k-ui .k-module-header + .nv-page-scroll-area > :first-child {
  margin-top: 0;
}

/* ══════════════════════════════════════════════════════════════════════
   CHIP MIT ECHTEM WORTBILD (2026-08-03)

   `.nv-chip` schreibt grundsaetzlich in Grossbuchstaben — richtig fuer
   Filter und Zustaende („OFFEN", „ARCHIV"), falsch fuer alles, was ein
   Eigenname ist: ein Dateiname (`ANGEBOT.PDF`) und ein Bausteinname
   (`ANFAHRT UND OEFFNUNGSZEITEN`) werden dadurch geschrien und schlechter
   lesbar.

   Die Grundregel steht als `[data-theme="…"] .nv-chip` in style.css. Diese
   Datei laedt spaeter, die Auswahl ist gleich stark — deshalb gewinnt sie
   ohne `!important`.
   ══════════════════════════════════════════════════════════════════════ */
.nv-chip--wort {
  text-transform: none;
  letter-spacing: 0;
}

/* ══ 5. HOME IM ROSÉ ATELIER (abgenommene Demo, Screen «Startseite») ═════
   Diese Datei laedt zuletzt und GEWINNT — deshalb stehen die Home-Regeln
   hier und nicht als weiterer !important-Streit in style.css. */

/* Gruss: Serife in Rosé, der Name gross als Verlaufs-Text (§4: Eros nur
   gross, nie kursiv, Gewicht 400). */

/* Kopf-Knoepfe rechts: Glocke als weiche Karte, Profil mit Rosé-Ring und
   pulsierendem Zaehler (Demo .profil). Organische Form, nie perfekter Kreis. */

/* Meldezeile: aus einzelnen Kaestchen wird EINE ruhige Karte mit
   Haarlinien (Demo Runde 6 — Stefans «verpasste anrufe wirkt deplaziert»). */
body.k-ui .k-notif-karten {
  display: flex !important;
  grid-template-columns: none !important;
  gap: 0 !important;
  align-items: stretch;
  padding: 13px 6px;
  border-radius: var(--nv-r-karte);
  background: var(--k-bg2);
  border: 1px solid var(--k-border);
  box-shadow: var(--nv-sh);
}
body.k-ui .k-notif-karte {
  flex: 1;
  display: grid !important;
  grid-template-columns: 38px auto;
  column-gap: 11px;
  align-items: center !important;
  justify-content: center;
  min-height: 0 !important;
  padding: 2px 4px !important;
  border: none !important;
  border-radius: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
}
body.k-ui .k-notif-karte-icon { grid-row: 1 / 3; }
body.k-ui .k-notif-karte-zahl { grid-row: 1; grid-column: 2; align-self: end; line-height: 1.05; }
body.k-ui .k-notif-karte-titel { grid-row: 2; grid-column: 2; align-self: start; }
body.k-ui .k-notif-karte + .k-notif-karte { border-left: 1px solid var(--k-border) !important; }
body.k-ui .k-notif-karte-icon {
  width: 38px; height: 38px;
  border-radius: var(--nv-chip-form);
}
body.k-ui .k-notif-karte-zahl { font-size: var(--k-fs-titel); font-weight: 700; }
body.k-ui .k-notif-karte-titel { font-size: var(--k-fs-micro); font-weight: 600; }

/* Stat-Trio: kleine Karten, Radius folgt der Groesse (24), Zahl gross. */
body.k-ui .k-home-stat-box {
  /* ⚠ Radius, Flaeche, Rahmen und Schatten sind hier RAUS (N2,
     2026-09-02). Alle sechs Stellen tragen im Markup zusaetzlich
     `k-glass-card` (gemessen) — die Form kam also doppelt, einmal aus
     dem Katalog und einmal von hier, und hier MIT einer Rahmenlinie.
     Der Katalog sagt seit dem 28.08. «keine Rahmenlinien» und seit
     heute zusaetzlich «kein Schatten». Was bleibt, ist die Ausrichtung
     und das eigene Polster dieser kleinen Kachel. */
  text-align: center;
  padding: 13px 8px 11px !important;
}
body.k-ui .k-home-stat-val {
  font-size: 1.25rem !important; font-weight: 700 !important;
  letter-spacing: -.02em !important;
  font-variant-numeric: tabular-nums;
  color: var(--k-text);
}
body.k-ui .k-home-stat-lbl { font-size: 0.625rem !important; font-weight: 600 !important; color: var(--k-text3) !important; }


/* ── Abschnitts-Kopf, Terminzeilen, JETZT-Karte, Badges (Demo) ───────── */
.nv-absch {
  display: flex; justify-content: space-between; align-items: baseline;
  padding: 2px 6px 8px;
}
.nv-absch b { font-family: 'Eros', 'Fraunces', Georgia, serif; font-weight: 600; font-size: var(--k-fs-hervor); color: var(--k-text); }
.nv-absch a, .nv-absch .nv-absch-link {
  font-size: var(--k-fs-caption); color: var(--k-brand); font-weight: 600;
  text-decoration: none; background: none; border: none; padding: 0;
  font-family: inherit; cursor: pointer;
}

/* `.nv-av` ist RAUS (E3, 2026-09-01). Es war der Avatar der Startseite
   als organischer Blob (`--nv-av-form`, acht Radien) — weder Kreis noch
   Kapsel, ein «Drittes» nach Pruefbedingung 4, und die zweite
   Avatar-Fassung neben dem Katalog. Die Termin-Zeilen tragen jetzt
   `.nv-avatar` (Katalog §4b, Kreis); die zarte Spielart `ist-zart`
   war dasselbe wie der Katalog-Grundzustand (Marke 15 % auf Grund). */

.nv-trow { display: flex; align-items: center; gap: 12px; padding: 12px 16px; }
.nv-trow + .nv-trow { border-top: 1px solid var(--k-border); }
.nv-trow .nv-zeit { font-size: var(--k-fs-caption); font-weight: 700; color: var(--k-brand); width: 36px; flex: 0 0 auto; }
.nv-trow b { display: block; font-size: var(--k-fs-body); color: var(--k-text); }
.nv-trow small { display: block; font-size: var(--k-fs-caption); color: var(--k-text3); }
.nv-trow .nv-chf { margin-left: auto; font-size: var(--k-fs-caption); font-weight: 600; color: var(--k-text2); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Die JETZT-Karte: das EINE leuchtende Element der Startseite. */
.nv-hot {
  background: var(--nv-grad); background-size: 200% 200%;
  border: none; border-radius: var(--nv-r-blatt);
  color:var(--k-text-auf-marke); box-shadow: var(--nv-sh-hot);
  padding: 16px; margin-bottom: 12px;
  display: flex; gap: 12px; align-items: center;
  position: relative; overflow: hidden;
  animation: chAtmen var(--m-atmen) ease-in-out infinite alternate,
             nvSchweben 5s ease-in-out infinite;
}
@keyframes nvSchweben { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
/* Auf dem Marken-Verlauf der JETZT-Karte faellt der Katalog-Avatar
   (Marke 15 % auf Marke) zusammen — hier weiss auf Marke. */
.nv-hot .nv-avatar { background: rgba(255, 255, 255, .24); color: var(--k-text-auf-marke); }
.nv-hot b { display: block; font-size: var(--k-fs-body); }
.nv-hot small { display: block; font-size: var(--k-fs-caption); color: rgba(255, 255, 255, .88); }
.nv-hot .nv-chf { margin-left: auto; font-weight: 700; font-size: var(--k-fs-body); color: rgba(255, 255, 255, .95); font-variant-numeric: tabular-nums; }
.nv-jetzt {
  font-size: var(--k-fs-micro); font-weight: 800; letter-spacing: .08em;
  background: rgba(255, 255, 255, .28); border-radius: var(--nv-r-kapsel); /* Nacht 3, Punkt 4: 99px war die Kapsel — Token-Treffer */
  padding: 2px 8px; margin-left: 6px; vertical-align: 2px;
}

/* Helfer-Badges (Spez §7): NEU, Online, Angefragt, Geburtstag, Alert. */
.nv-bdg {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 0.46875rem; font-weight: 800; letter-spacing: .05em;
  border-radius: 99px; padding: 2px 6px; vertical-align: 2px; margin-left: 5px;
}
.nv-bdg .material-symbols-rounded { font-size: 9px; font-variation-settings: 'FILL' 1; }
.nv-bdg.b-neu { background: var(--c-rose-bg); color: var(--c-rose); }
.nv-bdg.b-onl { background: var(--c-blue-bg); color: var(--c-blue); }
.nv-bdg.b-req { background: var(--c-amb-bg); color: var(--c-amb); }
.nv-bdg.b-geb { background: linear-gradient(120deg, #F7E3BE, #E2C284); color: #7A5C1E; }
.nv-bdg.b-alr { background: rgba(229, 62, 62, .14); color: #E24A4A; }
.nv-hot .nv-bdg { background: rgba(255, 255, 255, .26); color: #fff; }
@keyframes nvPunktPuls {
  0% { box-shadow: 0 0 0 0 rgba(217, 79, 96, .45); }
  70% { box-shadow: 0 0 0 9px rgba(217, 79, 96, 0); }
  100% { box-shadow: 0 0 0 0 rgba(217, 79, 96, 0); }
}

/* ── Kasse: das Total spricht die Haus-Sprache (Eros, Verlaufs-Text, §4) ── */
body.k-ui .kasse-cart-total-grand span:last-child {
  font-family: 'Eros', 'Fraunces', Georgia, serif;
  font-weight: 400;
  font-size: 1.875rem;
  line-height: 1.05;
  background: var(--nv-gru-name);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  font-variant-numeric: tabular-nums;
}

/* Die Verlaufs-Knoepfe stehen jetzt im Katalog (§11b). Der Glanz-Lauf
   braucht seine Bewegung aber weiterhin hier, weil @keyframes global
   gelten und nicht zum Bauteil gehoeren. */
@keyframes nvGlanzLauf { 0%, 55% { left: -70%; } 85%, 100% { left: 150%; } }

/* ── Segmente (Tag/Woche u. ae.): dunkle Pille als Anker, federnder Pop
   beim Wechsel (Demo tagPop). ── */
/* `.nv-segment-opt.is-aktiv` stand hier mit und war die ZWEITE
   Ausfuehrung des aktiven Zustands — sie schlug per `!important` die
   Regel in design-system.css. Beide sind 2026-08-29 im Katalog
   aufgegangen (§23); der Wert, den sie durchsetzte (`--nv-pill`), ist
   dort die Schraube `--seg-aktiv-bg`. `.k-seg-btn` ist ein anderes
   Bauteil und bleibt hier stehen, bis es an die Reihe kommt. */
@keyframes nvTagPop { 0% { transform: scale(.82); } 60% { transform: scale(1.08); } 100% { transform: scale(1); } }

/* ── Eingabefelder: weich, rund, warm (Demo). ── */

/* ── Kleine Karten (Gestern/Monat u. ae.) ──
   ⚠ Der Regelrumpf ist RAUS (N2, 2026-09-02). Der Kommentar sagte
   «gleiche Karten-Sprache» — und die Regel schrieb sie trotzdem noch
   einmal auf, mit einer Rahmenlinie, die die Karte nicht hat. Beide
   Stellen tragen im Markup `k-glass-card`; die Karten-Sprache kommt
   jetzt von dort. Gleiche Sprache heisst: EINE Regel, nicht zwei
   gleichlautende. */

/* ── Erfolgs-Moment (§6.5): Kreis federt, Haken zeichnet sich. ── */
.nv-erfolg {
  position: fixed; inset: 0; z-index: var(--z-hinweis, 5000);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; pointer-events: none;
  animation: nvErfolgAuf var(--m-standard) var(--m-glatt);
}
.nv-erfolg.geht { opacity: 0; transition: opacity var(--m-standard) var(--m-glatt); }
.nv-erfolg-kreis {
  width: 86px; height: 86px; border-radius: 50%;
  background: var(--nv-grad); color: var(--k-text-auf-marke);
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--nv-sh-hot);
  animation: nvErfolgFeder var(--m-auftritt) var(--m-feder);
}
.nv-erfolg-kreis svg { width: 46px; height: 46px; }
.nv-erfolg-kreis path {
  fill: none; stroke: #fff; stroke-width: 4.5;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 36; stroke-dashoffset: 36;
  animation: nvHaken .45s .18s ease-out forwards;
}
.nv-erfolg-text {
  font-family: 'Eros', 'Fraunces', Georgia, serif; font-weight: 400; font-size: 1.875rem;
  color: var(--k-text);
  text-shadow: 0 1px 8px var(--k-bg);
}
@keyframes nvErfolgAuf { from { opacity: 0; } to { opacity: 1; } }
@keyframes nvErfolgFeder { 0% { transform: scale(.3); } 60% { transform: scale(1.12); } 100% { transform: scale(1); } }
@keyframes nvHaken { to { stroke-dashoffset: 0; } }

/* ── Kalender-Datumsleiste folgt dem Glas-Token (statt festem Grau-Dunkel). */
#kal-tagbar { background: var(--nv-glas); }
[data-theme="dark"] #kal-tagbar { background: var(--nv-glas); border-top-color: var(--nv-glas-line); }

/* ══ 6. GRUND MIT GLOW + BLOBS (Demo .tel: Verlauf + zwei treibende
   Farbwolken). style.css §351 setzte html auf das FLACHE --k-bg und
   verdeckte damit den Verlauf — deshalb hier, in der Datei, die gewinnt. */
html {
  background: var(--k-lava-gradient, var(--k-bg)) !important;
  background-attachment: fixed;
}
[data-theme="dark"] html {
  background: var(--k-lava-gradient, var(--k-bg)) !important;
  background-attachment: fixed;
}
.nv-blob {
  position: fixed; z-index: 0; pointer-events: none;
  filter: blur(46px);
  opacity: var(--nv-blob-op, .6);
  animation: nvTreiben 22s ease-in-out infinite alternate;
}
.nv-blob.b1 {
  width: 300px; height: 300px; right: -90px; top: -70px;
  background: radial-gradient(circle at 35% 35%, var(--nv-blob-a, var(--nv-tint)), transparent 68%);
}
.nv-blob.b2 {
  width: 340px; height: 340px; left: -120px; bottom: 60px;
  background: radial-gradient(circle at 60% 45%, var(--nv-blob-b, var(--nv-tint)), transparent 66%);
  animation-duration: 28s; animation-delay: -8s;
}
@keyframes nvTreiben {
  0% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-28px, 22px) scale(1.12); }
  100% { transform: translate(20px, -16px) scale(.94); }
}

/* ══ 7. HOME-NACHBAU 1:1 (eigene Klassen — keine Kaempfe mehr mit den
   alten k-home-Regeln; Markup: js/home-admin.js _renderKeevaHome). ══ */
.nv-gruss { display: flex; align-items: flex-start; gap: 12px; padding: 8px 6px 0; margin-bottom: 14px; }
.nv-gruss-txt { flex: 1; min-width: 0; }
.nv-hallo {
  font-family: 'Eros', 'Fraunces', Georgia, serif; font-style: normal;
  font-size: var(--k-fs-hervor); font-weight: 400;
  color: var(--k-brand); opacity: .85;
}
h2.nv-name {
  /* !important: das globale `[data-theme] h2 {1.3rem}` (style.css §59) ist
     spezifischer als eine blosse Klasse und drueckte den Namen auf 21 px —
     im Pruefstand gemessen, nicht geraten. */
  font-family: 'Eros', 'Fraunces', Georgia, serif !important;
  font-weight: 400 !important;
  font-size: 2.875rem !important;
  line-height: 1.02 !important;
  letter-spacing: 0 !important;
  margin: 1px 0 3px;
  background: var(--nv-gru-name);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.nv-zart { font-size: var(--k-fs-caption); color: var(--k-text3); }
.nv-kopf-knoepfe { display: flex; align-items: flex-start; gap: 10px; margin-top: 6px; flex: 0 0 auto; }
.nv-profilbtn {
  position: relative;
  width: 46px; height: 46px;
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: var(--nv-av-form);
  background: var(--nv-grad); color: var(--k-text-auf-marke);
  font-size: 0.9375rem; font-weight: 700; font-family: inherit;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  box-shadow: 0 0 0 2.5px var(--k-bg), 0 0 0 4.5px rgba(217, 79, 96, .5), 0 6px 16px rgba(96, 44, 52, .18);
}
.nv-profilbtn img { width: 100%; height: 100%; object-fit: cover; border-radius: var(--nv-av-form); }
.nv-profilbtn em {
  position: absolute; top: -5px; right: -5px;
  min-width: 19px; height: 19px; padding: 0 5px;
  border-radius: var(--k-r-full, 999px);
  background: var(--nv-grad); color: var(--k-text-auf-marke);
  font-style: normal; font-size: var(--k-fs-micro); font-weight: 800;
  display: flex; align-items: center; justify-content: center;
  border: 2.5px solid var(--k-bg);
  animation: chPuls var(--m-puls) ease-out infinite;
}

/* Stat-Trio mit den Demo-Grafiken (Sparkline, Balken, Ring). */
.nv-statrow { display: flex; gap: 11px; margin-bottom: 14px; }
/* `.nv-stat` (+ :active, b, span) steht seit D-11 im Baukasten-Abschnitt am Dateiende (Familie STAT). */
.nv-welle { height: 26px; display: block; margin: 0 auto 4px; }
.nv-welle .linie {
  fill: none; stroke: url(#nvwg); stroke-width: 2.4; stroke-linecap: round;
  stroke-dasharray: 130; stroke-dashoffset: 130;
  animation: nvWellen 1.6s .35s ease-out forwards;
}
.nv-welle .flaeche { fill: url(#nvwf); opacity: 0; animation: nvEinblenden .8s 1.3s forwards; }
@keyframes nvWellen { to { stroke-dashoffset: 0; } }
@keyframes nvEinblenden { to { opacity: 1; } }
.nv-balken { display: flex; gap: 3px; align-items: flex-end; height: 26px; justify-content: center; margin-bottom: 5px; }
.nv-balken i {
  width: 6px; border-radius: var(--nv-r-marke); background: var(--nv-tint); /* Nacht 4 (Stefan 21): Marke, war 4 */
  transform-origin: bottom;
  animation: nvBalkenHoch .9s cubic-bezier(.2, .7, .3, 1) both;
}
.nv-balken i.on { background: var(--nv-grad); }
.nv-balken i:nth-child(1) { height: 40%; animation-delay: .15s; }
.nv-balken i:nth-child(2) { height: 60%; animation-delay: .22s; }
.nv-balken i:nth-child(3) { height: 45%; animation-delay: .29s; }
.nv-balken i:nth-child(4) { height: 85%; animation-delay: .36s; }
.nv-balken i:nth-child(5) { height: 65%; animation-delay: .43s; }
@keyframes nvBalkenHoch { from { transform: scaleY(0); } to { transform: scaleY(1); } }
.nv-ring { width: 46px; height: 46px; margin: 0 auto 4px; position: relative; }
.nv-ring svg { transform: rotate(-90deg); }
.nv-ring .rest { stroke: var(--nv-tint); }
.nv-ring .wert {
  stroke: url(#nvrg); stroke-linecap: round;
  stroke-dasharray: 126;
  /* Ziel-Offset kommt inline aus dem JS (prozentabhaengig); der Uebergang
     zeichnet den Ring beim Aufbau. */
  transition: stroke-dashoffset 1.4s cubic-bezier(.3, .7, .3, 1);
}
.nv-ring b { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: var(--k-fs-caption); }

/* ══ 8. KALENDER IM ROSÉ ATELIER — der Grund traegt, das Raster ist leise.
   Demo: transparentes Raster ueber dem Verlauf, gestrichelte Trenner,
   Haarlinien je Stunde. Die Status-Bedeutungen bleiben (Tints + Icons). */
/* Radius der Huelle PER FORMEL (E5-Befund 2026-09-01, Pruefbedingung 3):
   die unsichtbare Huelle um die Wochen-Spalten trug 18 hell / 25 dunkel
   (`--k-r-sm`/`--k-r-md` aus style.css) — themenabhaengig und KLEINER
   als die Spalten darin (20). Aussen = innen + Abstand: Spalte
   `--nv-r-s` (20) + Spaltenluecke `--nv-s2` (8) = 28. Unsichtbar, weil
   transparent und ohne Rand — aber eine Formel gilt auch dort, wo man
   sie nicht sieht, sonst faellt sie beim naechsten Rand wieder auf.
   EINE Regel ohne Thema — ein Radius ist Struktur und gehoert nicht
   unter [data-theme]. Die Klasse steht ZWEIMAL, weil `[data-theme="light"]
   .kal-glass-grid` in style.css:237 (0,2,0) wiegt: ein Attribut zaehlt wie
   eine Klasse. `body .kal-glass-grid` (0,1,1) verlor dagegen — E5 hat es
   gemessen: dunkel 28, hell blieb 18. Jetzt (0,2,1), hell und dunkel 28. */
body .kal-glass-grid.kal-glass-grid { border-radius: calc(var(--nv-r-s) + var(--nv-s2)); }
.kal-glass-grid { background: transparent !important; border: none !important; }
[data-theme="light"] .kal-glass-grid { background: transparent !important; border: none !important; }
[data-theme="dark"] .kal-glass-grid,
[data-theme="keeva-black"] .kal-glass-grid { background: transparent !important; border: none !important; }
.kal-col-header {
  background: transparent !important;
  border-bottom: 1px solid var(--k-border) !important;
  border-radius: 0 !important;
}
[data-theme="light"] .kal-col-header { background: transparent !important; }

/* ══ 9. NACHSCHAERFUNG (Stefans 30%-Befund) — Icons voll, Farben satt,
   Schriften exakt, Auftritt animiert. ══════════════════════════════════ */

/* Icons wie im Mockup: GEFUELLT in Chips, Meldezeile, Menue, Profil-Blatt.
   Outline bleibt nur fuer neutrale Werkzeug-Icons (Kopfzeilen). */
body.k-ui .k-notif-karte-icon,
body.k-ui .nv-chip .material-symbols-rounded,
body.k-ui .mn-symbol .material-symbols-rounded,
body.k-ui .pm-item-icon .material-symbols-outlined,
body.k-ui .kasse-action-btn .material-symbols-outlined,
body.k-ui .kasse-grid-item .material-symbols-outlined {
  font-variation-settings: 'FILL' 1, 'wght' 500 !important;
}

/* Meldezeile: satte Demo-Chip-Toene statt blasser 14%-Mischung. */
body.k-ui .k-notif-karte.icon-call    .k-notif-karte-icon { background: var(--c-rose-bg); color: var(--c-rose); }
body.k-ui .k-notif-karte.icon-message .k-notif-karte-icon { background: var(--c-blue-bg); color: var(--c-blue); }
body.k-ui .k-notif-karte.icon-chat    .k-notif-karte-icon { background: var(--c-pur-bg);  color: var(--c-pur); }
body.k-ui .k-notif-karte.icon-todo    .k-notif-karte-icon { background: var(--c-amb-bg);  color: var(--c-amb); }
body.k-ui .k-notif-karte.icon-booking .k-notif-karte-icon { background: var(--c-teal-bg); color: var(--c-teal); }
body.k-ui .k-notif-karte.icon-aemtli  .k-notif-karte-icon { background: var(--c-teal-bg); color: var(--c-teal); }
body.k-ui .k-notif-karte.icon-mail    .k-notif-karte-icon { background: var(--c-blue-bg); color: var(--c-blue); }
body.k-ui .k-notif-karte.icon-kontakt .k-notif-karte-icon { background: var(--c-blue-bg); color: var(--c-blue); }

/* Schriftgroessen EXAKT wie Demo — mit Gewicht gegen jede Altregel. */
body.k-ui .k-notif-karte-zahl { font-size: 1.3125rem !important; font-weight: 700 !important; }
body.k-ui .k-notif-karte-titel { font-size: 0.59375rem !important; font-weight: 600 !important; }
body.k-ui .k-notif-karte-icon { width: 38px !important; height: 38px !important; font-size: 1.1875rem !important; }
.nv-trow b { font-size: 0.8125rem !important; font-weight: 700 !important; }
.nv-trow small { font-size: 0.65625rem !important; }
.nv-trow .nv-zeit { font-size: 0.6875rem !important; }
.nv-absch b { font-size: 1.03125rem !important; }
body.k-ui .k-home-section-title {
  font-family: 'Eros', 'Fraunces', Georgia, serif !important;
  font-weight: 600 !important;
  font-size: 1.03125rem !important;
  text-transform: none !important;
  letter-spacing: 0 !important;
  color: var(--k-text) !important;
}
body.k-ui .k-mini-card b, body.k-ui .k-mini-card-value { font-variant-numeric: tabular-nums; }

/* Auftritt wie in der Demo: die Startseiten-Bloecke tauchen federnd auf.
   NICHT im Kalender (der hat seine eigene Buehne). */
body.k-ui:not(.kal-fest) #dash-content > * {
  animation: nvAuftauchen var(--m-auftritt) var(--m-feder) both;
}
body.k-ui:not(.kal-fest) #dash-content > *:nth-child(2) { animation-delay: .06s; }
body.k-ui:not(.kal-fest) #dash-content > *:nth-child(3) { animation-delay: .12s; }
body.k-ui:not(.kal-fest) #dash-content > *:nth-child(4) { animation-delay: .18s; }
body.k-ui:not(.kal-fest) #dash-content > *:nth-child(5) { animation-delay: .24s; }
body.k-ui:not(.kal-fest) #dash-content > *:nth-child(6) { animation-delay: .3s; }
@keyframes nvAuftauchen { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* Kalender-Kopf: Spaltenkoepfe leben — Namen dunkel, Frei nur gedimmt,
   nicht tot. */
body.k-ui .kal-col-frei { opacity: .72 !important; }
[data-theme="dark"] body.k-ui .kal-col-frei,
[data-theme="keeva-black"] body.k-ui .kal-col-frei { opacity: .55 !important; }

/* ══ 10. KASSE IM ROSÉ ATELIER — die Flaechen sprechen die Karten-Sprache. */
body.k-ui .kasse-action-btn {
  border-radius: var(--nv-r-karte);
  border: 1px solid var(--k-border) !important;
  background: var(--k-bg2) !important;
  box-shadow: var(--nv-sh) !important;
}
body.k-ui .kasse-action-primary {
  background: var(--nv-grad) !important;
  background-size: 180% 180% !important;
  border: none !important;
  color:var(--k-text-auf-marke) !important;
  box-shadow: var(--nv-sh-hot) !important;
  animation: chAtmen var(--m-atmen) ease-in-out infinite alternate;
}
body.k-ui .kasse-grid-item {
  border-radius: var(--nv-r-karte);
  border: 1px solid var(--k-border) !important;
  background: var(--k-bg2) !important;
  box-shadow: var(--nv-sh) !important;
}
body.k-ui .kasse-trans-item { background: var(--k-bg2); border-bottom: 1px solid var(--k-border); }
body.k-ui .kasse-trans-date-header {
  font-family: 'Eros', 'Fraunces', Georgia, serif;
  font-size: var(--k-fs-body); font-weight: 600;
  text-transform: none; letter-spacing: 0;
  color: var(--k-text);
}
body.k-ui .kasse-trans-pay-icon { border-radius: var(--nv-chip-form); width: 34px; height: 24px; }

/* Keine harten VERSALIEN auf Knoepfen: steht jetzt in der Grundform
   selbst (§11a) und braucht darum kein !important mehr. */

/* ══ 11. KALENDER-KOPF (abgenommene Demo): Serifen-Titel, Tag/Woche-Pille,
   Tagesleiste OBEN als weiche Chips. ═════════════════════════════════════ */
.kal-rose-kopf {
  /* E4 (2026-09-01): RASTER statt Flex. Der Titel-Behaelter kommt aus
     js/buchungen.js mit `flex:1;min-width:0` als Inline-Stil — im Raster
     ist das wirkungslos; die erste Spalte darf schrumpfen (minmax(0,1fr)),
     die Bedien-Gruppe nicht. Vorher lief der Titel bei 390 px unter den
     Tag/Woche-Umschalter und das «···» (gemessen, MESSBUCH E4-VORHER),
     weil der Knopf als Inline-Block breiter als sein Behaelter wurde.
     Unter 480 px rueckt die Gruppe UNTER den Titel (Medienregel unten). */
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  /* H4 (2026-09-02 Abend): `start` statt `center` — rechts steht nur noch
     das ··· (`.k-module-more`), und es sitzt wie in der Kasse an der
     Oberkante (Kopf-Oberkante + `--nv-s3` = 77 px bei Kerbe 59, gemessen
     Kasse 390), nicht auf der Mitte von Titel + Wetterzeile. */
  align-items: start; gap: var(--nv-luft-1);
  /* KEIN env() hier: body.k-ui traegt die Geraete-Kerbe bereits als
     padding-top (style.css §5629, !important). Ein zweites env() hier
     ergab den grossen Leerraum ueber «Heute» (Stefan-Bild 23:34).
     Oben 10 / unten 0 standen bisher als zwei spaetere Ueberschreibungen
     (Runde 7, Runde 10) — jetzt EINE Stelle, dieselben Werte. */
  /* F1 (2026-09-02): oben `--nv-s3` wie `.k-module-header` — dieselbe
     Titel-Oberkante auf jedem Schirm (vorher `--nv-luft-1`, 2 px hoeher). */
  /* H4: rechts 0 wie `#dash-content .k-module-header { padding-inline: 0 }`
     — das ··· steht damit 18 px (`--nv-rand` der Buehne) vom Rand, exakt
     wie der Kasse-Baustein. Links bleibt `--nv-s4`: der Titel behaelt
     seine Stelle (H2 misst sie parallel). */
  padding: var(--nv-s3) 0 0 var(--nv-s4);
}
/* Die Medienregel «unter 480 px rueckt die Bedien-Gruppe unter den Titel»
   ist mit H4 entfallen: der Umschalter steht unten links, das ··· allein
   passt neben jeden Titel. */
.kal-rose-titel {
  /* F2 (2026-09-02, Stefan): «Ein Titel ist ein Anlass, kein Datum.»
     E4 hatte den Knopf auf Grotesk gesetzt — das war falsch. Schrift,
     Gewicht und Groesse kommen jetzt aus der EINEN Titel-Regel des
     Katalogs (`.k-module-header-title, .k-mh-title-btn, .kal-rose-titel`
     im Baukasten-Teil dieser Datei) — kein eigener Wert hier. Der Datumsbereich darf auf zwei
     Zeilen umbrechen statt mit Ellipse zu enden («31. August – 6. September
     2026» passt bei 390 px nicht in 34 px Serife auf eine Zeile).
     `display:block; max-width:100%` statt `flex:1`: der Behaelter ist ein
     Block, `flex` griff dort nie. Gilt fuer Tag UND Woche (derselbe Knopf). */
  display: block; max-width: 100%; text-align: left;
  border: none; background: none; padding: 0; cursor: pointer;
  color: var(--k-text);
  white-space: normal;
  -webkit-tap-highlight-color: transparent;
}
/* Umschalter «Tag | Woche» UNTEN LINKS, schwebend (H4, Stefan 2026-09-02
   Abend, Apple-Kalender-Muster). Baustein bleibt `.nv-segment` (§23);
   diese Klasse traegt nur die LAGE und die Hoehen-Schraube:
     · fest ueber Kalender und Tab-Leiste; Abstand zur Leiste
       `--ch-nav-platz` (Leiste + Luft, css/app-chrome.css) + `--nv-luft-1`;
       ohne Leiste (Desktop) rechnet `--ch-nav-platz` 0.
     · `left: auto` + `margin-left: --nv-rand`: die Lage kommt aus dem
       Fluss der Buehne — am Telefon 18 px vom Rand, neben der Seitenleiste
       automatisch rechts davon (kein Breiten-Token noetig).
     · `--seg-hoehe: --nv-tap`: jedes Segment 44 px hoch (vorher 36).
   Gemessen vorher (390): Umschalter oben im Kopf [34,153→166,195],
   Segmente 36 px hoch; FAB [163,743→227,807]. Der Plus-Knopf beginnt
   3 px ueber `--ch-nav-platz`, der Umschalter endet 10 px darueber —
   die Rechtecke koennen sich nicht schneiden.
   Selektor MIT `.nv-segment`: die Schraube `--seg-hoehe` steht am Baustein
   (§23, spaeter in dieser Datei) mit gleicher Spezifitaet — allein als
   `.kal-rose-seg` verlor sie (gemessen: Segmente blieben 36 px). */
.nv-segment.kal-rose-seg {
  position: fixed; left: auto; margin-left: var(--nv-rand);
  bottom: calc(var(--ch-nav-platz, var(--nv-sockel-unten)) + var(--nv-luft-1));
  z-index: var(--z-schwebend);
  --seg-hoehe: var(--nv-tap);
  display: inline-flex; background: var(--nv-weich);
  border-radius: var(--k-r-full, 999px); padding: 3px; gap: 2px;
  box-shadow: 0 2px 10px rgba(40, 20, 26, .1);
}
.kal-rose-seg .nv-segment-opt {
  border: none; background: none; cursor: pointer;
  font-family: inherit; font-size: var(--k-fs-caption); font-weight: 600;
  padding: 6px 15px; border-radius: var(--k-r-full, 999px);
  color: var(--k-text3); -webkit-tap-highlight-color: transparent;
}
/* `.kal-rose-mehr` (eigener 38-px-Kreis, weiss, mit Schatten) ist mit H4
   entfallen — der Kalender nutzt den Kasse-Baustein `.k-module-more`. */
.kal-rose-wetter { padding: 0 18px 4px; font-size: var(--k-fs-caption); color: var(--k-text3); }

/* Tagesleiste OBEN: kein Glas-Balken mehr — weiche Tages-Chips auf dem
   Grund, aktiver Tag als dunkle Pille (Demo .tage). Die IDs/Klassen der
   Wisch-Mechanik bleiben unveraendert. */
body.k-ui #kal-tagbar.ist-oben {
  position: static !important;
  left: auto !important; right: auto !important; bottom: auto !important;
  background: transparent !important;
  border: none !important; box-shadow: none !important;
  -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
  padding: 4px 10px 8px !important;
  display: flex; align-items: center; gap: 4px;
}
body.k-ui .kal-leiste-tag {
  border: none; cursor: pointer;
  background: var(--nv-weich);
  /* E4 (2026-09-01): Tages-Chips sind EINE Formfamilie — Rechteck mit
     Chip-Radius (Pruefbedingung 4). Vorher 22 → 18 (Runde 10) hier, 20 am
     HEUTE-Kasten, 22 am HEUTE-Chip, 16 an den Wochen-Kopf-Chips
     (js/buchungen.js, --nv-r-chip): vier Radien fuer ein Bauteil. Jetzt
     alle `--nv-r-chip` (16, aus der Formel). Mass 7/6 und 2 px Rand sind
     die Runde-10-Werte, vorher als spaetere Ueberschreibung. */
  border-radius: var(--nv-r-chip);
  /* H3 (2026-09-02): kein Aussenrand mehr — sieben Zellen teilen die
     Breite, die Luft zwischen ihnen ist das `gap` der Woche (--nv-s1).
     Gemessen 390: mit 2 px Rand + 5 px Luft 37,1 px je Zelle (Rand-Probe
     rot, 81 → 150), ohne 44,3 px. */
  margin: 0; padding: 7px 0 6px;
  box-shadow: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--m-schnell) var(--m-glatt);
}
body.k-ui .kal-leiste-tag:active { transform: scale(.94); }
body.k-ui .kal-leiste-tag .kal-leiste-wt { font-size: var(--k-fs-micro); font-weight: 600; text-transform: uppercase; color: var(--k-text3); display: block; }
body.k-ui .kal-leiste-tag .kal-leiste-zahl { font-size: var(--k-fs-body); font-weight: 700; color: var(--k-text); display: block; }
body.k-ui .kal-leiste-tag.ist-heute .kal-leiste-zahl { color: var(--k-brand); }
body.k-ui .kal-leiste-tag.ist-gewaehlt {
  background: var(--nv-pill);
  box-shadow: 0 8px 18px rgba(40, 36, 43, .28);
  animation: nvTagPop var(--m-standard) var(--m-feder);
}
body.k-ui .kal-leiste-tag.ist-gewaehlt .kal-leiste-wt { color: var(--nv-pill-ink); opacity: .65; }
body.k-ui .kal-leiste-tag.ist-gewaehlt .kal-leiste-zahl { color: var(--nv-pill-ink); }
/* ── F-2a (Stefan 2026-09-07): HEUTE IST GEFUELLT, NICHT BESCHRIFTET ────
   «Heute-Zelle GEFUELLT rot, nicht weiss mit rotem Datum.» Gemessen
   vorher (390, hell und dunkel): heute ist in der Regel zugleich der
   gewaehlte Tag, also gewann `.ist-gewaehlt` — die Pille, hell schwarz,
   dunkel weiss —, und `.ist-heute` steuerte nur die rote Ziffer bei, die
   auf der Pille ohnehin ueberschrieben wurde. Auf dem Geraet sah man
   deshalb eine weisse Zelle mit dunkler Zahl.
   Jetzt traegt HEUTE die Marke als Flaeche — derselbe Zustand wie im
   Kalender jedes Telefons — und die Auswahl bleibt die Pille. Beide
   Regeln sind gleich schwer (0,3,1); diese steht deshalb SPAETER, sonst
   gewaenne wieder die Pille. Kein zweites Bauteil, kein neuer Token:
   Flaeche `--k-brand`, Schrift `--k-text-auf-marke` wie an jedem
   Primaerknopf. */
body.k-ui .kal-leiste-tag.ist-heute { background: var(--k-brand); }
body.k-ui .kal-leiste-tag.ist-heute .kal-leiste-wt {
  color: var(--k-text-auf-marke); opacity: .65;
}
body.k-ui .kal-leiste-tag.ist-heute .kal-leiste-zahl,
body.k-ui .kal-leiste-tag.ist-gewaehlt.ist-heute .kal-leiste-zahl { color: var(--k-text-auf-marke); }

/* Keine VERSALIEN-Etiketten auf Karten (Spez §4) — GESTERN/AUGUST etc. */
body.k-ui .k-home-stat-lbl,
body.k-ui .k-mini-card-label { text-transform: none !important; letter-spacing: 0 !important; }

/* ── Kasse · Bezahlen-Bildschirm (abgenommene Demo, Screen «Kasse») ──── */
body.k-ui .kasse-pay-hero {
  text-align: center; padding: 12px 4px 0;
  background: none !important; color: inherit !important;
  border-radius: 0 !important; margin-bottom: 6px;
}
body.k-ui .kasse-pay-hero-tip, body.k-ui .kasse-pay-hero-partial { color: var(--k-text3) !important; opacity: 1; }

/* Vollbild-Panels (Kasse, Reglement …) stehen auf dem ROSÉ-GRUND, nicht auf
   deckendem Weiss — sonst wirkt jeder Unterbildschirm wie eine andere App
   (Stefan 2026-08-25: «kassieren sieht komplett anders aus»). */
body.k-ui .nv-panel { background: var(--k-bg); } /* D-14: flach wie gemessen — die gelayerte keeva-ui-Regel gewann bis dahin mit --k-bg, nicht der Lava-Verlauf */
body.k-ui .nv-panel-header {
  background: transparent; border-bottom: none;
  /* F1 (2026-09-02): der Sockel gilt in BEIDEN Zustaenden. Vorher trug ihn
     nur `.scrolled` (Z. 1547) — ungescrollt stand der Panel-Titel 12 px unter
     der Oberkante, also unter der Kerbe, und sprang beim Scrollen um 59 px.
     Kerbe + Zusatz (`--nv-luft-0`) + Kopf-Innenpolster (`--nv-s3`) = dieselbe
     Titel-Oberkante wie `.k-module-header`. */
  padding-top: calc(var(--nv-sockel-oben) + var(--nv-luft-0) + var(--nv-s3));
}
body.k-ui .nv-panel-title {
  font-family: 'Eros', 'Fraunces', Georgia, serif !important;
  font-weight: 400 !important; font-size: 1.4375rem !important;
  text-transform: none !important; letter-spacing: 0 !important;
}
body.k-ui .nv-panel-header [data-panel-back] {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--k-bg2); border: 1px solid var(--k-border);
  box-shadow: var(--nv-sh);
}

/* Positionen-Karte auf dem Bezahlbildschirm (Demo). */
body.k-ui .kasse-pay-posliste {
  margin: 4px 16px 14px; padding: 4px 14px;
  background: var(--k-bg2); border: 1px solid var(--k-border);
  border-radius: var(--nv-r-karte); box-shadow: var(--nv-sh);
}
body.k-ui .kasse-pay-pos {
  display: flex; align-items: center; gap: 12px;
  padding: 11px 0; border-bottom: 1px solid var(--k-border);
}
body.k-ui .kasse-pay-pos:last-child { border-bottom: none; }
body.k-ui .kasse-pay-pos-mitte { flex: 1; min-width: 0; }
body.k-ui .kasse-pay-pos-mitte b {
  display: block; font-size: var(--k-fs-body); font-weight: 700; color: var(--k-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.k-ui .kasse-pay-pos-mitte i {
  display: block; font-style: normal; font-size: var(--k-fs-caption); color: var(--k-text3);
}
body.k-ui .kasse-pay-pos-preis {
  font-size: 0.90625rem; font-weight: 800; color: var(--k-text);
  font-variant-numeric: tabular-nums; flex: 0 0 auto;
}

/* Zahlungsarten-Chips: quadratische Karten, gewaehlt = dunkle Pille (Demo). */
body.k-ui .kasse-pay-chips {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px;
}
body.k-ui .kasse-pay-chips .kasse-pay-method-card {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--nv-luft-0, 6px); padding: 14px 4px 11px; min-height: 74px;
}
body.k-ui .kasse-pay-chips .kasse-pay-method-card.ist-gewaehlt {
  background: var(--nv-pill) !important;
  border-color: transparent !important;
  box-shadow: 0 10px 22px rgba(40, 36, 43, .3) !important;
  animation: nvTagPop var(--m-standard) var(--m-feder);
}
body.k-ui .kasse-pay-chips .kasse-pay-method-card.ist-gewaehlt .kasse-pay-method-icon,
body.k-ui .kasse-pay-chips .kasse-pay-method-card.ist-gewaehlt .kasse-pay-method-label,
body.k-ui .kasse-pay-chips .kasse-pay-method-card.ist-gewaehlt .kasse-pay-method-sub {
  color: var(--nv-pill-ink) !important;
}
body.k-ui .kasse-pay-method-sub { font-size: 0.625rem; color: var(--k-text3); }

/* Grosser Bezahlen-Knopf (Demo): Verlauf, atmet leicht. */
body.k-ui .kasse-pay-go {
  display: block; width: calc(100% - 32px); margin: 16px 16px 10px;
  padding: 16px; border: none; border-radius: var(--nv-r-kapsel); cursor: pointer;
  background: var(--nv-grad); background-size: 180% 180%;
  color: rgba(255, 255, 255, 1); font-family: inherit;
  font-size: var(--k-fs-hervor); font-weight: 700;
  box-shadow: 0 14px 30px rgba(217, 79, 96, .4);
  animation: chAtmen var(--m-atmen) ease-in-out infinite alternate;
  -webkit-tap-highlight-color: transparent;
}
body.k-ui .kasse-pay-go:active { transform: scale(.97); }
body.k-ui .kasse-pay-hero-label {
  font-size: 0.65625rem !important; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 700 !important; color: var(--k-text3) !important;
}
body.k-ui .kasse-pay-hero-amount {
  font-family: 'Eros', 'Fraunces', Georgia, serif !important;
  font-weight: 400 !important;
  font-size: 3.5rem !important; line-height: 1.05 !important;
  background: var(--nv-gru-name);
  -webkit-background-clip: text; background-clip: text; color: transparent !important;
  font-variant-numeric: tabular-nums;
}
body.k-ui .kasse-pay-method-card {
  background: var(--k-bg2) !important;
  border: 1px solid var(--k-border) !important;
  border-radius: var(--nv-r-karte);
  box-shadow: var(--nv-sh) !important;
  color: var(--k-text2) !important;
  transition: transform var(--m-schnell) var(--m-glatt) !important;
}
body.k-ui .kasse-pay-method-card:active { transform: scale(.96) !important; }
body.k-ui .kasse-pay-method-icon { font-size: 1.375rem !important; font-variation-settings: 'FILL' 1, 'wght' 500; }
body.k-ui .kasse-pay-method-label { font-size: 0.65625rem !important; font-weight: 700 !important; text-transform: none !important; }
body.k-ui .kasse-pay-methods-title {
  font-family: 'Eros', 'Fraunces', Georgia, serif !important;
  font-weight: 600 !important; font-size: 0.9375rem !important;
  text-transform: none !important; letter-spacing: 0 !important;
  color: var(--k-text) !important;
}

/* Im aktiven Tages-Chip traegt die PILLE die Farbe — kein zweiter Kreis
   auf der Zahl (Alt-Regel style.css §7415). */
body.k-ui .kal-leiste-tag.ist-gewaehlt .kal-leiste-zahl,
body.k-ui .kal-leiste-tag.ist-gewaehlt.ist-heute .kal-leiste-zahl {
  background: none !important;
  width: auto !important; height: auto !important;
  border-radius: 0 !important;
}

/* ══ 12. DEMO-FEINSCHNITT (Runde 6 — 1:1 gegen rose-atelier-demo.html
   gehalten; Typografie Fraunces/Instrument Sans kommt aus den Tokens). ══ */

/* Kasse-Hero exakt wie .summe h2: CHF und Rappen klein, Zahl gross. */
body.k-ui .kasse-pay-hero-label {
  font-size: 0.65625rem !important; letter-spacing: .1em;
}
body.k-ui .kasse-pay-hero-amount small {
  font-size: 1.1875rem; letter-spacing: .02em;
  -webkit-background-clip: text; background-clip: text;
}

/* Zahlungsarten: EINE wischbare Reihe (.zahlarten), nicht ein Gitter. */
body.k-ui .kasse-pay-chips {
  display: flex; grid-template-columns: none; gap: 10px;
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding: 2px 2px 6px; scrollbar-width: none;
}
body.k-ui .kasse-pay-chips::-webkit-scrollbar { display: none; }
body.k-ui .kasse-pay-chips .kasse-pay-method-card {
  flex: 1 0 auto; min-width: 82px;
  border-radius: var(--nv-r-karte);
  font-size: var(--k-fs-caption); font-weight: 700; color: var(--k-text2);
  gap: 4px; padding: 14px 10px 12px; min-height: 0;
}
body.k-ui .kasse-pay-chips .kasse-pay-method-icon { font-size: 1.375rem !important; }

/* Bezahlen-Knopf = .btn-g der Demo (Radius 28, Glanz-Lauf, atmet). */
body.k-ui .kasse-pay-go {
  border-radius: 28px; padding: 18px;
  font-size: 0.90625rem; font-weight: 700;
  position: relative; overflow: hidden;
  box-shadow: 0 12px 28px rgba(217, 79, 96, .36);
}
body.k-ui .kasse-pay-go::after {
  content: ''; position: absolute; top: -60%; bottom: -60%; width: 40%; left: -70%;
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, .45), transparent);
  transform: skewX(-18deg); animation: nvGlanzLauf 4.6s ease-in-out infinite;
}

/* Plus-Blatt = Demo .aktionen: schwebende Karten ueber dem abgedunkelten
   Grund — KEIN Blatt-Rahmen, kein Griff, kein Titel. */
body.k-ui .k-bsheet:has(.ch-aktion) {
  background: transparent !important;
  -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
  border: none !important; box-shadow: none !important;
  left: 24px !important; right: 24px !important;
}
/* H1 (2026-09-02): NUR das Plus-Blatt liegt unter der Tab-Leiste (R14) —
   sein Rumpf endet deshalb ueber ihr. Vorher trugen ALLE Blaetter dieses
   Polster (css/nv-consistency.css §15), obwohl sie ueber der Leiste
   stehen: 104 px Leere unter jedem Menue. Wert unveraendert (Leistenplatz
   + `--nv-luft-0`), nur der Ort ist enger. */
body.k-ui .k-bsheet:has(.ch-aktion) .k-bsheet-body {
  padding-bottom: calc(var(--ch-nav-platz) + var(--nv-luft-0));
}
body.k-ui .k-bsheet:has(.ch-aktion) .k-bsheet-handle,
body.k-ui .k-bsheet:has(.ch-aktion) .k-bsheet-handle-bar,
body.k-ui .k-bsheet:has(.ch-aktion) .k-bsheet-head,
body.k-ui .k-bsheet:has(.ch-aktion) .k-bsheet-title,
body.k-ui .k-bsheet:has(.ch-aktion) .k-bsheet-close { display: none !important; }
body.k-ui .k-bsheet-backdrop.active:has(.ch-aktion) {
  -webkit-backdrop-filter: blur(7px); backdrop-filter: blur(7px);
}
body.k-ui .ch-aktion {
  border-radius: 28px;
  box-shadow: 0 12px 34px rgba(30, 14, 20, .3);
  padding: 13px 16px;
  font-size: var(--k-fs-body); font-weight: 700;
  animation: nvAktionAuf var(--m-auftritt) var(--m-feder) both;
}
body.k-ui .ch-aktion:nth-child(2) { animation-delay: .05s; }
body.k-ui .ch-aktion:nth-child(3) { animation-delay: .1s; }
body.k-ui .ch-aktion:nth-child(4) { animation-delay: .15s; }
body.k-ui .ch-aktion:nth-child(5) { animation-delay: .2s; }
body.k-ui .ch-aktion:nth-child(6) { animation-delay: .25s; }
body.k-ui .ch-aktion:nth-child(7) { animation-delay: .3s; }
body.k-ui .ch-aktion:nth-child(8) { animation-delay: .35s; }
body.k-ui .ch-aktion:nth-child(9) { animation-delay: .4s; }
@keyframes nvAktionAuf {
  from { transform: translateY(26px) scale(.94); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
body.k-ui .ch-aktion.ist-haupt { box-shadow: 0 16px 38px rgba(201, 58, 82, .45); }

/* Blatt-Titel: Fraunces, ruhiger (Demo .sheet h3 ist klein — 22px ist die
   Mitte zwischen Demo-Abschnitt und unserem Haupt-Titel). */
.k-bsheet-title { font-size: 1.375rem !important; }

/* Tab-Beschriftung exakt Demo (9.5px). */
.ch-tab { font-size: var(--k-fs-micro); }

/* Jetzt-Punkt pulsiert (Demo .jetztquer i / @keyframes puls). */
@keyframes chPuls2 {
  0%   { box-shadow: 0 0 0 0 rgba(217, 79, 96, .45); }
  70%  { box-shadow: 0 0 0 9px rgba(217, 79, 96, 0); }
  100% { box-shadow: 0 0 0 0 rgba(217, 79, 96, 0); }
}

/* Termin-anlegen-Overlay: Eingaben und Karten in Demo-Sprache. */

/* ── Runde 7 (Stefans Detail-Liste 2026-08-25) ─────────────────────────── */

/* Serifen-Stimme groesser (Stefan: «titel eros heute, hallo stefan,
   grösser»). `.kal-rose-titel` stand hier mit — E4 (2026-09-01): der
   Kalender-Titel ist ein Datum und spricht Grotesk (§11). */
body.k-ui .nv-hallo { font-size: 1.125rem !important; }
body.k-ui h2.nv-name { font-size: 3rem !important; }

/* Tagesleiste: EINE Woche fuellt den Streifen (H3) — das Einrasten auf
   ganzen Wochen kommt aus css/style.css (`scroll-snap-type: x mandatory`,
   `.kal-leiste-woche { flex: 0 0 100% }`). Hier standen bis 2026-09-02
   «HEUTE + die kommenden Tage, frei gewischt» (snap none, Woche 140 %). */
body.k-ui #kal-tagbar.ist-oben { padding: 2px 10px 6px !important; }

/* Vollbild-Panel-Kopf in Demo-Sprache: KEIN weisser Balken, runder
   Zurueck-Chip, grosser Serifen-Titel links (Demo .kal-kopf). */
body.k-ui .nv-panel-header,
body.k-ui .nv-panel-header.scrolled {
  background: transparent !important;
  box-shadow: none !important; border-bottom: none !important;
  -webkit-backdrop-filter: none !important; backdrop-filter: none !important;
  /* E2.3 (2026-09-01) stellte hier `padding: calc(sockel + 10px) 16px 2px`
     nur fuer den gescrollten Zustand. F1 (2026-09-02): RAUS — den Sockel
     traegt `body.k-ui .nv-panel-header` oben (§ Kasse), in beiden Zustaenden. */
  gap: 12px;
}
body.k-ui .nv-panel-title {
  font-family: var(--nv-font-display, Georgia, serif) !important;
  font-weight: 400 !important; font-size: 2.125rem !important;
  text-transform: none !important; letter-spacing: 0 !important;
  text-align: left; line-height: 1.05;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body.k-ui .nv-panel-subtitle { text-align: left; font-size: var(--k-fs-caption); color: var(--k-text3); }
body.k-ui .nv-panel-header .nv-back {
  width: 38px; height: 38px; flex: 0 0 auto;
  border: none; border-radius: 50%;
  background: var(--k-bg2); box-shadow: var(--nv-sh);
  display: flex; align-items: center; justify-content: center;
}
body.k-ui .nv-panel-header .nv-back .material-symbols-outlined { font-size: 19px; }

/* Kassieren: «Zahlungsart wählen» steht nicht in der Demo; Chips ohne
   Schatten (wirkte im Scroll-Behaelter abgeschnitten). */
body.k-ui .kasse-pay-methods-title { display: none; }
body.k-ui .kasse-pay-chips .kasse-pay-method-card { box-shadow: none !important; }

/* Plus-Blatt: Karten BREITER (16px Rand statt 24), Zeilen kompakt wie die
   Demo — auch die Haupt-Karte ist eine normale Zeile, nur im Verlauf. */
body.k-ui .k-bsheet:has(.ch-aktion) {
  left: 16px !important; right: 16px !important;
}
body.k-ui .ch-aktion { padding: 12px 15px; }
body.k-ui .ch-aktion .nv-chip { width: 40px; height: 40px; }
body.k-ui .ch-aktion .nv-chip .material-symbols-rounded { font-size: 20px; }
body.k-ui .ch-aktion-text b { font-size: var(--k-fs-body); }
body.k-ui .ch-aktion-text small { font-size: var(--k-fs-caption); }

/* Menue-Kacheln: Symbol-Chip auf Demo-Mass (40px, Symbol 20px). */
.mn-kachel .mn-symbol { width: 40px !important; height: 40px !important; }
.mn-kachel .mn-symbol .material-symbols-rounded { font-size: 20px !important; }


/* ── Runde 9 (Stefans Fast-Liste 2026-08-25, 23:42) ────────────────────── */

/* Halbstunden-Linien noch leiser (Demo zeigt fast nur volle Stunden). */

/* «Heute»-Chip in der Tagesleiste — nur sichtbar, wenn man woanders steht. */
body.k-ui .kal-heute-chip {
  flex: 0 0 auto; border: none; cursor: pointer;
  background: var(--nv-tint, rgba(var(--k-brand-rgb), .12));
  color: var(--k-brand); font-family: inherit;
  font-size: var(--k-fs-caption); font-weight: 700;
  padding: 10px 13px; margin: 0 3px; border-radius: var(--nv-r-chip);
  -webkit-tap-highlight-color: transparent;
}

/* Wetter klein unter dem Titel (Demo-ruhig). */
.kal-rose-wetter2 {
  display: flex; align-items: center; gap: 4px;
  font-size: var(--k-fs-caption); color: var(--k-text3);
  padding: 0 2px; margin-top: -3px; min-height: 14px;
}
.kal-rose-wetter2 .material-symbols-outlined,
.kal-rose-wetter2 .material-symbols-rounded { font-size: 13px !important; }

/* ── Runde 10 (Feierabend-Liste 2026-08-26 + Luft-Pass) ─────────────────
   LUFT-TOKENS: die eine Stelle, an der Abstaende justiert werden
   (Stefan: «alles muss einfach aenderbar sein»). */
:root {
  /* ── Die Rollen-Leiter. Eine Rolle je Stufe, eine Stufe je Rolle. ──
     `--nv-luft-0` neu am 2026-08-29 (Stefan-Entscheid, Weg 1): 6 px,
     gemessen an 982 Stellen. Rolle: **dicht — Symbol zu Text**, «eng,
     aber nicht geklebt». Das ist NICHT dieselbe Rolle wie `--nv-luft-1`
     («eng: innerhalb von Karten») — deshalb zwei Stufen, nicht eine.

     ⚠ `--nv-luft-0` waechst mit dem Bildschirm NICHT mit (die anderen
     drei tun es, +25 %/+50 %). Der Abstand zwischen einem Symbol und
     seiner Beschriftung gehoert zum Element, nicht zur Seite: er haengt
     an der Schriftgroesse, nicht an der Bildschirmbreite. Waechst er
     mit, faellt das Symbol vom Wort ab. */
  --nv-luft-1: 10px;  /* eng: innerhalb von Karten */
  /* `--nv-r-chip` stand hier ein zweites Mal (18px) und hat die Leiter im
     Token-Block ueberschrieben — die Ecken-Definition gehoert zu den
     Ecken, nicht zwischen die Luft-Tokens. Entfernt 2026-08-27; der Wert
     steht jetzt nur noch oben (16px). Ein Wert, eine Stelle. */
  --nv-luft-2: 16px;  /* normal: zwischen Bauteilen */
  --nv-luft-3: 24px;  /* weit: zwischen Abschnitten */
}

/* Serifen-Titel noch groesser (`.kal-rose-titel` stand hier mit — E4:
   Grotesk, Groesse in §11). */
body.k-ui .nv-hallo { font-size: 1.1875rem !important; }
body.k-ui h2.nv-name { font-size: 3.125rem !important; }
body.k-ui .nv-panel-title { font-size: 2.125rem !important; }

/* Wetter-Zeile: Icon 20px + fette Grad — mehr nicht (Popup zeigt den Rest). */
.kal-rose-wetter2 { font-size: var(--k-fs-body); gap: 5px; margin-top: 1px; }
.kal-rose-wetter2 b { font-size: var(--k-fs-body); font-weight: 700; color: var(--k-text2); }
.kal-rose-wetter2 .material-symbols-rounded { font-size: 20px !important; color: var(--k-brand); }

/* Die fixe HEUTE-Box (`.kal-heute-fix`, 26.08.) und die Sechs-Chip-Woche
   (`.ist-mit-heute`, F7) sind mit H3 (Stefan 2026-09-02 Abend) entfallen:
   heute steht im Streifen an seiner Stelle, jede Woche hat sieben Zellen. */

/* Sieben Zellen Mo–So fuellen genau den Streifen (H3): eine Woche = 100 %
   der Leiste, Chip-Luft 5 px. Vorher 165 % (sechs sichtbar, F7). */
body.k-ui #kal-tagbar.ist-oben .kal-leiste-woche { flex: 0 0 100%; gap: var(--nv-s1); padding: 0; }

/* Raster: WIRKLICH keine Flaechenfarbe — nur Linien (Stefan 2026-08-26). */
body.k-ui #kal-content, body.k-ui #kal-hscroll,
body.k-ui #kal-grid, body.k-ui .kal-team-col { background: transparent !important; }

/* Spaltenkopf steht AUSSERHALB des Scrollers (js/buchungen.js) — direkt
   auf dem Grund, ohne Schleier. */
body.k-ui .kal-col-header { background: transparent !important; }

/* Mehr Luft im Kalender-Kopfbereich (Kopf-Polster seit E4 in §11). */
body.k-ui #kal-tagbar.ist-oben { padding: 6px 10px 10px !important; }

/* Luft-Pass Startseite & Karten: ruhiger Rhythmus ueber die Tokens. */
body.k-ui .nv-gruss { margin-bottom: var(--nv-luft-2); }
body.k-ui .nv-statrow { gap: 12px; margin-bottom: var(--nv-luft-2); }
body.k-ui .k-notif-karten, body.k-ui .k-notif-list { margin-bottom: var(--nv-luft-2); }
body.k-ui .nv-trow { padding: 13px 17px; }
body.k-ui .nv-absch { padding: 6px 6px 4px; }
body.k-ui .kasse-pay-posliste { margin-bottom: var(--nv-luft-2); }
body.k-ui .kasse-pay-methods { margin-top: var(--nv-luft-1); }

/* ── Runde 10b: Luft-Pass Blaetter, Menue, Kasse (Nacht 2026-08-26) ────── */

/* Blaetter: mehr Innenluft, ruhigere Raender.
   G4 (02.09.): hier stand bis 1023 px `padding-left/right: 18px !important`
   auf dem Blatt-Rumpf. Das unterbot das vom Blatt selbst gesetzte
   `--k-blatt-pad` (20, js/bottom-sheet.js) um 2 px — genau die 2 px, die
   der Katalog-Karte (Schatten 0 14px 36px, Reichweite 36) im Rollbereich
   des profil-Blatts links und rechts fehlten (18 Rumpf + 16 Kartenrand =
   34; 10 Treffer, tools/schatten-probe.cjs). Die Hero-Regel weiter oben
   rechnet ohnehin mit den 20 («Genau die Polsterung des Blattes
   aufheben»). Das Blatt selbst bleibt ein Clip (runde Unterecken auf dem
   Schirm), also entsteht der Platz innen. Oben: der Rumpf ist ein echter
   Scroller (gemessen 2153 > 689) mit 0 Polster — der Avatar (Schatten
   0 4px 20px, Reichweite 16) sass 12 px unter der Kante, 4 px gekappt.
   Reserve in den Rumpf, vom Kopf abgezogen — die Summe bleibt 16
   (Muster Pruefagent D, Rollbereich in der Buehne). */
body.k-ui .k-bsheet-header { padding-bottom: calc(var(--nv-s4) - var(--nv-schatten-reserve)); }
body.k-ui .k-bsheet-header + .k-bsheet-body { padding-top: var(--nv-schatten-reserve); }
body.k-ui .k-bsheet-title { padding-left: 6px; }

/* Menue: mehr Luft zwischen Kacheln und Zeilen. */
body.k-ui .mn-raster { gap: 11px; }
body.k-ui .mn-kachel { padding-top: 15px; padding-bottom: 13px; }
body.k-ui .mn-hubs { display: flex; flex-direction: column; gap: 10px; }

/* Kasse-Startseite: Aktionen mit Demo-Abstand. */
body.k-ui .kasse-actions, body.k-ui .kasse-grid { gap: 11px; }

/* Wochen-Ansicht: Inhalt atmet seitlich wie die Tagesansicht. */
body.k-ui.kal-fest #kal-content { padding-top: 4px; }

/* ── Termin anlegen: Ein-Seiten-Formular (Demo Screen 6) ───────────────── */
.te-flabel {
  font-weight: 700; font-size: var(--k-fs-body); color: var(--k-text);
  margin: 0 2px 8px;
}
.te-kchip {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--k-border); cursor: pointer;
  background: var(--k-bg2); color: var(--k-text);
  font-family: inherit; font-size: 0.78125rem; font-weight: 700;
  padding: 8px 15px; border-radius: var(--k-r-full, 999px);
  box-shadow: var(--nv-sh); -webkit-tap-highlight-color: transparent;
  transition: transform var(--m-schnell) var(--m-glatt);
}
.te-kchip:active { transform: scale(.95); }
.te-kchip.ist-on {
  background: var(--nv-pill); color: var(--nv-pill-ink);
  border-color: transparent;
  box-shadow: 0 8px 18px rgba(40, 36, 43, .28);
  animation: nvTagPop var(--m-standard) var(--m-feder);
}
.te-kchip.ist-belegt {
  opacity: .38; text-decoration: line-through;
  box-shadow: none; cursor: default;
}
.te-kchip .te-av {
  width: 26px; height: 26px; flex: 0 0 auto; margin-left: -6px;
  border-radius: 50%; background: var(--nv-grad);
  color: rgba(255, 255, 255, 1); font-style: normal;
  font-size: var(--k-fs-caption); font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.te-kchip.ist-on .te-av { background: rgba(255, 255, 255, .24); color: rgba(255, 255, 255, 1); }
.te-kchip .te-av.ist-zart { background: var(--nv-tint); color: var(--k-brand); }
.te-karte {
  background: var(--k-bg2); border: 1px solid var(--k-border);
  border-radius: var(--nv-r-karte); box-shadow: var(--nv-sh);
}
.te-suchliste:not(:empty) {
  margin-top: 9px; padding: 2px 12px;
  background: var(--k-bg2); border: 1px solid var(--k-border);
  border-radius: var(--nv-r-karte); box-shadow: var(--nv-sh);
}
.te-suchliste.ist-hoch:not(:empty) { max-height: 268px; overflow-y: auto; }

/* ── Runde 11: MODUL-KOPF app-weit in Demo-Sprache (Kunden, Team, Lager,
   Aufgaben, Nachrichten, Zeiterfassung, …) — Eros-Titel LINKS, runde
   Chips. Gilt fuer jede Seite, die renderModuleHeader nutzt. ─────────── */
body.k-ui .k-mh-title-btn .k-mh-chevron { font-size: 1.125rem; color: var(--k-text3); align-self: center; }
body.k-ui .k-mh-title-btn .k-mh-dot { color: var(--k-text3); }
body.k-ui .k-module-header-subtitle { text-align: left; font-size: var(--k-fs-caption); margin-top: 0; }

/* Eingaben app-weit als Pille (Demo .feld) — Textbereiche bleiben eckig-weich. */

/* Zweit-Knoepfe als weiche Karte: jetzt im Katalog (§11b). */

/* ── Runde 11b: Formular-Etiketten + Modul-Dropdown in Demo-Sprache ────── */

/* Etiketten wie die Demo (.flabel): kraeftig, KEINE VERSALIEN. */


/* Ansichts-Auswahl unter dem Modul-Titel: weiche Karte. */
body.k-ui .k-mh-dropdown {
  background: var(--k-bg2); border: 1px solid var(--k-border);
  border-radius: var(--nv-r-karte); box-shadow: 0 18px 44px rgba(40, 20, 26, .22);
  overflow: hidden;
}

/* ══ R13. STEFANS MORGEN-LISTE (2026-08-26, Bilder 06:52–07:07) ══════════ */

/* 1. ⚠ RAUS 2026-08-29 (P3). Der Modul-Kopf ohne Glas-Balken steht jetzt
   im Katalog (§22) — mitsamt der Begruendung, warum er den Streit gegen
   `nv-consistency.css` 12c gewonnen hat. Hier stand er ein zweites Mal,
   und nur `!important` hielt ihn oben. Ein Wert an einer Stelle. */

/* 2. Kalender-Raster: der grosse Rosé-Schein (box-shadow auf .kal-glass-grid
   aus style.css) war die «Farbe, die auf dem Kalender liegt» — gemessen im
   Pruefstand (elementsFromPoint + computed style). Nur Linien, kein Schein. */
.kal-glass-grid, body.k-ui .kal-glass-grid { box-shadow: none !important; }

/* 3. Spaltenkopf: das Milchglas kam aus keeva-ui (backdrop-filter blieb trotz
   transparentem Grund stehen) — das war die «solide Farbe hinter den
   Avataren». E4 (2026-09-01): die Quellen sind weg (keeva-ui.css §Kalender,
   style.css .kal-col-header hell+dunkel, .kal-u-heute/-gruppe) — Glas
   gehoert nicht auf die Inhaltsebene (Pruefbedingung 1). Damit braucht es
   hier kein `backdrop-filter: none !important` mehr. */
body.k-ui .kal-col-header {
  border-bottom: none !important;
}

/* 4. Tagesleiste: Innenluft fuer den Pillen-Schatten — der Scroller schnitt
   ihn unten ab (Stefan: «schatten unter DI 25 wird abgeschnitten»). */
body.k-ui #kal-tagbar.ist-oben .kal-leiste {
  /* F4 (Stefan 2026-09-02, «MI 2» abgeschnitten): der Pillen-Schatten
     (0 8px 18px, §11 .ist-gewaehlt) reicht 10 px nach oben und 26 px nach
     unten; 4/16 px Luft kappten ihn gemessen um 6 bzw. 10 px. Jetzt 10
     oben (--nv-luft-1, exakt die Reichweite), 28 unten — aus der Skala,
     nicht gezaehlt. Waagrecht bleibt der Rand des Schiebers die Kante
     (der Streifen rollt dort unter die HEUTE-Box); das Paar
     Polster/negativer Rand hebt sich weiter auf. */
  /* Oben `--nv-s3` (12), nicht `--nv-luft-1` (10) — Pruefagent D, 02.09.:
     nach dem Zusammenfuehren mit F1 (Kalender-Kopf oben ebenfalls
     `--nv-s3`) war 10 der einzige neue Polsterwert im Modul (skalen-probe,
     Modul buchungen, Polster oben: 6 → 7 Werte). 12 deckt die Schatten-Reichweite (10)
     ebenso, und das Paar Polster/negativer Rand hebt sich weiter auf. */
  padding: var(--nv-s3) var(--nv-s1) calc(var(--nv-s5) + var(--nv-s1));
  margin: calc(-1 * var(--nv-s3)) calc(-1 * var(--nv-s1)) calc(-1 * (var(--nv-s5) + var(--nv-s1)));
}

/* 5. Block-Farbtoene fuer Status: DECKEND statt Glas (Stefan: «glasige
   kalender … entweder weiss, rosa gestrichelt usw»). Werte hier als Token,
   der Kalender-Renderer (js/buchungen.js) verweist per var() darauf. */

/* 6. Bezahl-Hero groesser (Stefan: «130 müsste grösser sein») + Posliste
   ohne Schatten; Bezahlen-Knopf bekommt unten Schattenraum, der Trenner
   schnitt ihn ab. */
body.k-ui .kasse-pay-hero-amount { font-size: 4.25rem !important; }
body.k-ui .kasse-pay-posliste { box-shadow: none !important; }
body.k-ui .kasse-pay-go { margin-bottom: 26px; }

/* 7. Verkauf-Katalogkopf: kein weisser Block, keine harte Linie darunter. */
body.k-ui .kasse-catalog-header {
  background: transparent !important; border-bottom: none !important;
}

/* 8. Buchen-Knopf inaktiv: ruhige weiche Pille statt durchsichtigem Verlauf
   ueber dem Inhalt («termin hinzufügen wirkt zerschossen»). */
body.k-ui .kasse-pay-go.ist-inaktiv {
  background: var(--nv-weich) !important; color: var(--k-text3) !important;
  box-shadow: none !important; animation: none !important;
}
body.k-ui .kasse-pay-go.ist-inaktiv::after { display: none !important; }

/* ══ R14. BLATT UEBER DER TAB-LEISTE (Bewertung 2026-08-26, §4-Korrektur) ══
   GEMESSEN: .k-bsheet ist ein KIND seines Backdrops (bottom-sheet.js,
   backdrop.appendChild(sheet)) — sein z-index:var(--z-navigation) ist nur
   die lokale Ordnung IM Backdrop. Der frueher vorgeschlagene Token-Tausch
   (--z-navigation → --z-blatt) haette sichtbar nichts geaendert.
   Das echte Problem: app-chrome.css hebt die Tab-Leiste bei offenem Blatt
   auf calc(--z-blatt-hoch + 2) — absichtlich, damit beim PLUS-Blatt der
   FAB sichtbar bleibt (Demo: + rotiert zum ×). Fuer alle ANDEREN Blaetter
   schnitt die Leiste das Blatt ab (Menue-Sheet-Befund).
   Loesung: nur das Plus-Blatt (:has(.ch-aktion)) bleibt unter der Leiste;
   jedes andere Blatt hebt seinen ganzen Stack darueber.
   ETAPPE 1 (2026-08-26): die gerechneten Stufen (+2 hier, +4 in
   app-chrome.css) heissen jetzt --z-tabbar-ueber-blatt /
   --z-blatt-ueber-tabbar und stehen in ebenen.css. Gleiche Zahlen,
   aber auffindbar — eine namenlose Rechnung findet beim naechsten
   Ebenen-Streit niemand wieder. */
.k-bsheet-backdrop:not(:has(.ch-aktion)) {
  z-index: var(--z-blatt-ueber-tabbar, 2104);
}

/* ══ R15. EROS NIE KURSIV (Stefan-Entscheid 2026-08-26, Runde-8-Notiz) ════
   Eros hat keinen Kursiv-Schnitt — Browser wuerden die Schrift synthetisch
   schraegstellen. Systemregel statt Einzelfix: ueberall, wo die Display-
   Schrift spricht, ist kursiv aus (font-synthesis unterbindet auch die
   Kunst-Schraege verschachtelter <i>/<em>). */
body.k-ui :is(h1, h2, h3, .kal-rose-titel, .k-module-header-title,
  .k-mh-title-btn, .nv-panel-title, .nv-name, .k-home-section-title,
  .kasse-pay-hero-amount, .k-bsheet-title, .mn-abschnitt) {
  font-style: normal;
  font-synthesis: none;
}

/* ══ R16. RUNDE 14 — QUER-POLITUR AUS DEM BILDBASIS-DURCHGANG ═══════════
   (Stefan 2026-08-26: «abstände … detailliert, komplettes system») */

/* Modul-Titel liefen bei langem Zusatz rechts aus dem Bild
   (Bildbasis: «Zeiterfassung · Stempuhr|»). Mitte darf schrumpfen,
   Titel endet mit Ellipse statt an der Gehaeusekante. */
/* ⚠ `body.k-ui .k-module-header-center { min-width: 0 }` STAND HIER UND
   IST RAUS (2026-08-29, A2 Teil 1). Der Wert ist im Katalog angekommen:
   §22 setzt `.k-module-header-center { flex: 1; min-width: 0;
   text-align: left }` — derselbe Wert, dieselbe Wirkung. Die
   Beanstandung von 2026-08-26 bleibt damit erfuellt; sie wird hier nur
   nicht mehr ein zweites Mal beantwortet. Die drei Regeln darunter
   bleiben stehen: `white-space`/`overflow`/`text-overflow`/`max-width`
   stellt der Katalog NICHT — dafuer liegt eine Frage auf der
   Parkliste. */
/* ⚠ RAUS 2026-08-29 (P5 und P7). Ellipsen-Verhalten und Breitengrenze
   stehen jetzt AM Bauteil im Katalog (§22). Die Beanstandung von
   2026-08-26 («Titel lief aus dem Bild») bleibt erfuellt — sie wird
   hier nur nicht mehr ein zweites Mal beantwortet, und sie gilt jetzt
   ueberall, nicht nur unter `body.k-ui`. */
body.k-ui .k-mh-t, body.k-ui .k-mh-sub {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
body.k-ui .k-mh-sub { flex-shrink: 2; }
body.k-ui .k-mh-title-btn .k-mh-chevron, body.k-ui .k-mh-title-btn .k-mh-dot { flex-shrink: 0; }

/* Zeiterfassung: Versalien-Restlabel + grauer Schiebe-Balken in die
   Demo-Sprache (Etiketten ohne VERSALIEN seit R11b; Flaechen weich). */
body.k-ui .zeit-status-label {
  text-transform: none; letter-spacing: 0;
  font-size: 0.78125rem; font-weight: 700; color: var(--k-text3);
}
body.k-ui .slide-unlock-track {
  background: var(--nv-weich); border: none; box-shadow: var(--nv-sh);
  /* G4 (02.09.): der Griff (left/top/bottom 3 px, Schatten 0 2px 8px) wurde
     an der Bahn links 5, oben 3, unten 7 px gekappt. Die Bahn kappt sonst
     nichts — js/zeiterfassung-admin.js klemmt den Griff auf
     Bahnbreite − Griffbreite − 6, das Etikett liegt mit inset 0 innen. Der
     weiche Griff-Schatten darf wie beim Systemschalter ueber die Bahn
     fallen; ihn auf 3 px zu kuerzen waere eine Formaenderung am Bauteil. */
  overflow: visible;
}
/* G4 (02.09.): Tab-Leiste — `overflow-x: auto` (css/style.css, dreimal
   definiert) rollte nie: gemessen scrollWidth 372 = clientWidth 372
   (mediahub_galerie, 390 px); beide Instanzen (js/nv-panel.js,
   js/zeiterfassung-admin.js) tragen Tabs mit flex 1. Ein Scroller, der nie
   rollt, ist nur ein Clip — er nahm dem aktiven Tab links 6, oben 4,
   unten 8 px Schatten. */
body.k-ui .nv-tabs { overflow: visible; }
/* G4 (02.09.): Neva-Blatt (js/neva.js, Stil im Skript) — `overflow: hidden`
   diente nur den runden Ecken (Radius 24). Der Nachrichtenbereich rollt in
   sich (eigenes overflow-y), Kopf, Kontextzeile und Eingabezeile tragen
   keine Flaeche bis in die Ecken. Gemessen: der Senden-Knopf (Schatten
   0 2px 12px, Reichweite 14) sass 12 px ueber der Unterkante → 2 px gekappt. */
body.k-ui #neva-panel { overflow: visible; }

/* ══ R17. AUDIT-SOFORTMASSNAHME D1 (2026-08-26): FORMULARELEMENTE ERBEN
   DIE SCHRIFT. Browser geben button/input/select/textarea die
   Systemschrift (Arial) statt der Seitenschrift — gemessen: 1'139
   sichtbare Arial-Elemente ueber 87 Module, weil KEINE Regel
   font-family fuer Buttons setzte. Ein Einzeiler, systemweite Wirkung. */
button, input, select, textarea { font: inherit; }

/* ═══════════════════════════════════════════════════════════════════════
   BAUKASTEN — jeder Baustein in GENAU EINER Ausfuehrung
   (Stefan-Auftrag 2026-08-27, Befund «der Baukasten fehlt»)

   ── FELDGRUPPE — Ueberschrift, Hinweis und Wertespiegel im Formular ──
   Drei Bausteine, die in `js/leistungen_admin.js` 25-mal als Inline-Stil
   standen (12 Ueberschriften, 13 Hinweise). Sie sind hier zusammengezogen,
   weil die Ratschen `r_neuzugang` und `r_randinline` beim Umbau der
   Preis-Maske (Stefan, Weg A, 08.09.2026) rot fielen — richtig: ein
   Abstand gehoert an den Behaelter, nicht als margin an das Kind.

   · `.nv-feldgruppe`         Ueberschrift ueber einer Gruppe von Feldern
   · `.nv-feldgruppe-hinweis` erklaerender Satz DARUNTER
   · `.nv-feldgruppe-hinweis.ist-nach` derselbe Satz UNTER den Feldern
   · `.nv-wertespiegel`       Nur-Lese-Kasten: Werte, an denen sich der
                              Tippende orientiert (Basispreis, schon
                              gefuellte Stufen). Nie ein Eingabefeld —
                              was hier steht, wird nicht veraendert.
*/
.nv-feldgruppe {
  font-size: var(--k-fs-hervor, .82rem); font-weight: 700;
  color: var(--k-text2); margin: var(--nv-luft-2, 16px) 0 var(--nv-luft-1, 10px);
}
.nv-feldgruppe-hinweis {
  font-size: var(--k-fs-caption, .7rem); color: var(--k-text3);
  line-height: 1.4; margin: 0 0 var(--nv-luft-1, 10px);
}
.nv-feldgruppe-hinweis.ist-nach { margin: 4px 0 0; }
.nv-wertespiegel {
  font-size: var(--k-fs-caption, .7rem); color: var(--k-text3);
  line-height: 1.5; margin: 0 0 var(--nv-luft-1, 10px);
  padding: 8px 10px; border-radius: var(--nv-r-s, 10px);
  background: var(--k-flaeche2, rgba(0, 0, 0, .04));
}
.nv-wertespiegel b, .nv-wertespiegel strong { color: var(--k-text2); font-weight: 700; }

/* `.nv-sperrkarte` — die schmale, mittige Karte, die anstelle einer Maske
   steht, wenn diese NICHT bedient werden darf (VW-3, Stefan 08.09.2026).
   Sie ersetzt den Editor, sie sitzt nicht daneben: was gesperrt ist, wird
   gar nicht erst gezeigt. `.nv-sperrkarte__titel` ist ihre Zeile ganz oben.
   Warum als Bauteil und nicht als style="…" im JS: der Neuzugangs-Waechter
   laesst keine neue Gestaltung im JS zu, und er hat recht — sonst steht die
   naechste Sperre wieder anders da. */
.nv-sperrkarte { max-width: 520px; margin-left: auto; margin-right: auto; }
.nv-sperrkarte__titel {
  font-size: var(--k-fs-hervor, .82rem); font-weight: 700;
  color: var(--k-text, inherit); margin-bottom: var(--nv-luft-1, 10px);
}

/* `.nv-zustandsmarke` — der Zustand einer Person auf ihrer Kachel
   (HR-3, Stefan-Entscheid 08.09.2026).

   VORHER: ein 10-px-Punkt mit `title="Abwesend"`. Auf dem Telefon gibt es
   kein Schweben — das `title` war unerreichbar, und drei Zustaende sahen
   aus wie drei Farben ohne Bedeutung.

   STEFANS ENTSCHEID, im Wortlaut: «der Punkt bleibt fuer alle drei
   Zustaende, aber ROT und GRAU bekommen ein sichtbares Wort. Gruen bleibt
   wortlos — anwesend ist der Normalfall, und fuenf Woerter *anwesend*
   sind Rauschen. Gelesen werden muss die Ausnahme.»

   Deshalb ZWEI Formen desselben Bauteils: `--still` ist nur der Punkt,
   ohne Wort und ohne Traeger. */
.nv-zustandsmarke {
  position: absolute; top: 10px; right: 10px; z-index: 3;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 9px 3px 7px; border-radius: 99px;
  background: rgba(0, 0, 0, .5); color: #fff;
  font-size: var(--k-fs-micro, .62rem); font-weight: 700;
  line-height: 1.4; letter-spacing: .01em;
}
.nv-zustandsmarke--still { padding: 0; background: none; }
.nv-zustandsmarke__punkt {
  width: 10px; height: 10px; border-radius: 50%;
  border: 2px solid rgba(0, 0, 0, .3);
  background: var(--k-text3, #95a5a6);
  flex: 0 0 auto;
}
.nv-zustandsmarke--anwesend   .nv-zustandsmarke__punkt { background: var(--k-green, #2ecc71); }
.nv-zustandsmarke--abwesend   .nv-zustandsmarke__punkt { background: var(--k-red, #E74C3C); }
.nv-zustandsmarke--ausgetreten .nv-zustandsmarke__punkt { background: #95a5a6; }
/*

   ── ZUSTANDS-KONVENTION (Stefan-Entscheid D6, 2026-09-03) ──────────
   EIN ZUSTAND HEISST `.ist-<zustand>`. Er ist ein ZUSTAND, kein
   Bauteilname — er steht nie allein, sondern immer an einem Baustein:
   `.nv-karte-inhalt.ist-meldung`, `.nv-segment-opt.ist-aktiv`.

   Die Konvention gab es schon, sie war nur nie ausgesprochen:
   `.ist-meldung` und `.ist-fehler` sind in der Nacht 02./03.09.
   entstanden. Ausgesprochen wird sie jetzt, weil an ihr fast die
   Haelfte der Katalog-Rechnung haengt.

   DER BEFUND DAHINTER. `.active` ist der groesste Einzelposten des
   ganzen Katalogs: **148 Vorkommen, 125 Regeln ausserhalb dieses
   Abschnitts**, verteilt auf neun CSS-Dateien (style.css allein 82).
   Es ist kein Bauteil — es gibt keine Regel `.active { … }`, die man
   als «die richtige» behalten koennte. Deshalb liess es sich mit dem
   Muster «Zweitdefinition weg» nicht aufraeumen: es gibt keine erste.

   WAS DAS FUER NEUE ARBEIT HEISST
   · Ein neuer Zustand heisst `.ist-<zustand>` und wird HIER
     definiert, an dem Baustein, zu dem er gehoert.
   · `.active` ist ALTBESTAND. Keine neue Regel darauf.
   · Umschreiben von `.active` auf `.ist-aktiv` ist KEINE
     Nebenbei-Arbeit: 54 der 125 Regeln haengen an genau einem
     Baustein und waeren mechanisch fuehrbar, **71 tragen Bedeutung**
     (Verschachtelung, `:has()`, Listen mit fremden Namen). Zahlen und
     Fundstellen: docs/_diagnostic_results/d6-zustaende-und-weg.md
   ────────────────────────────────────────────────────────────────────

   WARUM ES DIESEN ABSCHNITT GIBT
   Stefans zehn Beanstandungen hatten eine gemeinsame Ursache: es gab
   keinen Baukasten. Gemessen wurde, was daraus folgte —
     · Icon-Container: auf der Startseite VIER Formen gleichzeitig
       (rund, 18 px, 8 px, eckig); im Team-Modul 12 px neben rund
     · Suchfelder: 59 %, 77 %, 83 % Breite — in «dokumente» zwei davon
       auf demselben Bildschirm
     · Kopf-Rechts: more_horiz / add_task / post_add / add / nichts;
       im Chat zweimal dasselbe Symbol nebeneinander
     · Kopfzeilen: vier verschiedene Logiken
     · Filter-Chips: 776 px bzw. 3'523 px breit, ohne Hinweis, dass es
       rechts weitergeht
   Das sind keine zehn Fehler, das ist ein fehlendes Fundament. Modulweise
   zu reparieren hiesse 87 Module x 6 Bausteine = rund 520 Stellen — und
   beim naechsten Modul faengt es wieder von vorn an.

   WIE ER BENUTZT WIRD
   Die Klassen hier sind die EINE Wahrheit. Sie stehen zuerst allein im
   Katalog (`katalog.php`, ausgeliefert ueber my.nevanu.ch) — dort sieht
   Stefan jeden Baustein mit allen Zustaenden und entscheidet die Dichte.
   Erst danach werden Module darauf umgestellt, modulweise und mit
   Bildvergleich. Bis dahin benutzt sie noch niemand; das ist Absicht.

   REGEL FUER SPAETER: Wer einen Baustein braucht, nimmt den hier. Wer
   findet, dass er nicht passt, aendert IHN — nicht sein Modul. Genau
   diese Abkuerzung hat die vier Icon-Formen erzeugt.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Sockel-Token: Safe-Area auf ALLEN VIER Seiten ──────────────────────
   Bisher rechnete jede Stelle den oberen Rand einzeln aus (App-Nachtrag:
   Querformat und Split View brauchen auch links/rechts). Eine Zahl, vier
   Seiten, ein Ort. */
:root {
  /* EIN Seitenrand (Stoerelemente Punkt 2: heute konkurrieren 14 und 18).
     Auf dem Telefon 18 px, ab Tablet mehr Luft — als Token, damit der
     iPad-Schritt ihn nicht wieder aufreisst. */
  --nv-rand: 18px;
  /* Kopfzeile: EINE Hoehe. Gemessen waren neun (Spanne 66 px). */
  /* 56, nicht 52 (Stefan-Entscheid 2026-08-27). Die 52 waren ein
     Entwurfswert aus dem Katalog; 56 ist der gemessene Ist-Wert von
     zehn ausgelieferten Modulen. Der Baukasten uebernimmt damit die
     gelebte Praxis, statt sie beim Umstellen um 4 px zu verschieben —
     die Optik bleibt, die Steuerung wird zentral. */
  --nv-kopf-hoehe: 56px;
}
/* ── DIE IPAD-STUFE — fuer JEDEN Groessen-Token, nicht nur den Rand ─────
   Stefan-Nachtrag 2026-08-28, Punkt 5: solange der Katalog offen ist,
   bekommt jeder Groessen-Token seine Stufe. Danach muesste man jeden
   einzeln nochmal anfassen — genau das soll die App-Verpackung nicht
   erfordern.

   Bisher hatte NUR `--nv-rand` die Stufen (18/28/36). Alles andere blieb
   auf dem Telefon-Wert, auch auf einem 13-Zoll-iPad: gleich enge Luft,
   gleich niedrige Kopfzeile, gleich kleine Schrift auf der dreifachen
   Flaeche.

   DIE WERTE SIND ABSICHTLICH ZURUECKHALTEND. Der Rand waechst um 55 % und
   100 % — das ist viel, weil er nur Leerraum ist. Schrift und Kopfhoehe
   um denselben Faktor zu strecken, waere eine andere App. Gewaehlt sind
   deshalb: Luft +25 %/+50 %, Kopf +4/+8 px, Schrift +4 %/+8 %.
   Wer das anders will, aendert es HIER — an drei Stellen, nicht an
   achtzig.

   Das iPad ist Arbeitsgeraet: Tap to Pay laeuft dort NICHT (Apple-Vorgabe,
   Stefan 2026-08-28), kassiert wird mit dem iPhone. Die Stufe dient der
   Lesbarkeit und dem Griff, nicht einer zweiten Bedienlogik. */
@media (min-width: 768px) {
  :root {
    --nv-rand: 28px;
    --nv-luft-1: 12px;
    --nv-luft-2: 20px;
    --nv-luft-3: 30px;
    --nv-kopf-hoehe: 60px;
    /* ⚠ Die vier `--nv-t-*`-Schriftstufen standen hier und wuchsen mit —
       RAUS 2026-08-30, Stefan-Entscheid «Weg B»: Abstaende wachsen mit
       dem Bildschirm, Schrift nicht. Kanon §5a. */
  }
}
@media (min-width: 1180px) {
  :root {
    --nv-rand: 36px;
    --nv-luft-1: 15px;
    --nv-luft-2: 24px;
    --nv-luft-3: 36px;
    --nv-kopf-hoehe: 64px;
    /* ⚠ Schriftstufen RAUS 2026-08-30 (Weg B) — siehe Tablet-Block. */
  }
}

/* ── 0. SEITENRAND — die Regel steht an EINER Stelle ────────────────────
   DIE BUEHNE gibt den Rand, nicht der Baustein. Am 2026-08-27 war es
   einen halben Tag lang umgekehrt («jedes Element vergibt ihn selbst»),
   und das Ergebnis war messbar: 119 von 119 Kindern in 67 Modulen
   standen auf 0 px, weil nur die zwei umgestellten Bausteine ihn
   zurueckbekamen. Eine Regel, die jedes Element einzeln befolgen muss,
   wird von jedem neuen Element erneut vergessen.

   Das Polster der App-Buehne steht in css/style.css
   (`body.k-ui #dash-content`) — dort, weil es eine Vorrang-Markierung
   braucht, um gegen aeltere Behaelter-Regeln zu bestehen, und weil die
   Zahl solcher Markierungen je Datei nur sinken darf (CSS-Freeze,
   tools/css-schicht-wache.cjs). Hier steht die zweite Buehne und stehen
   die AUSNAHMEN.
   Randnotiz fuer den naechsten: die Wache zaehlt diese Markierung auch
   in Kommentartext. Wer sie hier ausschreibt, faerbt das Deploy-Gate rot
   — genau das ist beim Schreiben dieses Blocks passiert.

   Geprueft von tools/rand-probe.cjs bei 390 / 768 / 1180 px. */

/* Die zweite Buehne: eigenstaendige Seiten (Katalog, Beispielseiten). */
.nv-inhalt { padding-inline: var(--nv-rand); }

/* Ausnahme 1 — Bausteine, die den Rand selbst trugen. Sie geben ihn ab;
   sonst steht er zweimal da (das waren die gemessenen 34 und 36 px). */
/* padding-inline nimmt Anfang und Ende, nicht links und rechts — bei
   uns also erst der linke, dann der rechte Sockel. */
.nv-kopf { padding-inline: var(--nv-sockel-links) var(--nv-sockel-rechts); }
/* ⚠ F-1 (2026-09-07): DER NEGATIVE RAND IST WIEDER RAUS — Rueckschritt
   aus Teil D benannt und behoben.
   Hier stand seit D-16 (Commit 6a882bd2d3, 06.09. 08:20 UTC, Deploy
   #3818): `margin-inline: calc(-1 * var(--nv-rand)); padding-inline:
   var(--nv-rand)` — der Kopf lief ueber den Buehnenrand hinaus, damit
   das Glas bis an die Schirmkante reicht. Gemessen hat das die
   Klick-Probe (Lauf #178, erster sehender Lauf nach dem blinden
   Fenster): **114 Verstoesse gegen die Seitenrand-Regel, 38 Module je
   Breite, 390/768/1180 — in JEDEM Fall dieses eine Bauteil**, «links 0
   rechts 0 (eigenes Polster 18/28/36)». Das ist kein Messfehler der
   Probe: ein Element auf 0 px zur Fensterkante IST randlos, egal wie
   viel Polster es innen traegt.
   Der Kopf gibt seinen Rand also wieder an die Buehne ab (Ausnahme 1,
   Zustand vor D-16). Titel und Zurueck-Chip stehen unveraendert auf
   `--nv-rand`; nur die Flaeche des Kopfes endet jetzt dort, wo der
   Inhalt endet. */
#dash-content .k-module-header { padding-inline: 0; }
/* G2 (2026-09-02, Block G): die zweite Buehne IN der ersten. Acht Module
   (noshow, rueckerstattungen, neva_config, assessment, webanalytics,
   popups, discountregeln, abbrecher) trugen einen Inline-Umschlag
   `padding:20px 16px 80px` UM den Kopf — gemessen +20 px Sockel (38.6
   statt 18.6) und 34 px Seitenrand. Sie tragen jetzt `.nv-inhalt` UNTER
   dem Kopf; in der Buehne gibt auch diese Klasse ihren Rand ab. */
#dash-content .nv-inhalt { padding-inline: 0; }


/* ── STUFE 1 (Stefan-Auftrag Nachtblock A, 2026-09-03) ─────────────────
   VIER GRUNDREGELN, DIE NEBEN DEM KATALOG STANDEN.
   Sie sind unveraendert aus `css/style.css` hierher gezogen — kein Wert
   geaendert, nichts umbenannt, nur der Ort. Bis heute stand die
   Grundform in style.css und die Ausnahme im Katalog; die Wache zaehlte
   die Klasse deshalb als «daneben gebaut», obwohl sie im Katalog
   gefuehrt war.
   Der Umzug aendert die Kaskaden-REIHENFOLGE (system.css laedt spaeter).
   Keine dieser Eigenschaften wird anderswo fuer dieselbe Klasse gesetzt
   — nachgewiesen mit der Bildbasis (174 Bilder, hell und dunkel, vier
   Breiten): kein Bild veraendert. */
.nv-page-scroll-area {
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 160px;
}
.nv-sidebar-group { display: contents; }
.nv-chip-group {
  display: flex;
  gap: 6px;
  margin-bottom: 12px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.kp-container {
  max-width: 600px;
  margin: 0 auto;
  padding-bottom: 40px;
}

/* Ausnahme 2 (F4, Stefan 2026-09-02) — der ROLLBEREICH in der Buehne.
   `.nv-page-scroll-area` (css/style.css, 12 Module) rollt senkrecht und
   sass INNERHALB des Buehnen-Polsters: seine Kante lag bei 18 px statt
   an der Schirmkante. Alles, was die Schatten-Reserve nach aussen holt
   (`.nv-statreihe` mit `margin-inline: -6px`, Karten mit 14 px Schatten),
   stiess dort an — gemessen: 31 gekappte Schatten in 9 Modulen, dazu
   `lager` mit +6 px seitwaerts rollbar (tools/rand-probe.cjs, seit
   01.09.). Der Rollbereich nimmt den Rand der Buehne und gibt ihn innen
   zurueck: Inhalt steht weiter auf 18 px, die Kante liegt am Schirm. */
body.k-ui #dash-content .nv-page-scroll-area {
  margin-inline: calc(-1 * var(--nv-rand));
  padding-inline: var(--nv-rand);
  /* Oben dasselbe (Pruefagent D, 02.09.): seit der Kopf-Sockel (F1) das
     24-px-Polster von #dash-content mitgenommen hat, kappte der
     Rollbereich in `zeiterfassung` den oberen Schatten der ersten Karte
     um 6 px (tools/schatten-probe.cjs: 14 statt 13). Die Reserve wird
     innen gegeben und aussen vom Titel→Inhalt-Abstand abgezogen —
     sichtbar bleibt es bei 12 px. G1 (Block G): der Abstand liegt jetzt
     als unterer Rand AM Kopf (`.k-module-header`, --nv-s3), nicht mehr
     als Rand am Nachbarn; die beiden Raender fallen zusammen (12 und −6
     ergeben 6) — Klipp-Kante und Kartenlage bleiben auf den Pixel gleich
     (Kopf-Unterkante + 6 / + 12). */
  padding-top: var(--nv-schatten-reserve);
  margin-top: calc(-1 * var(--nv-schatten-reserve));
}

/* Ausnahme 2 — von Kante zu Kante. Nur was auch waagrecht ROLLT, darf
   aus der Buehne heraustreten: ein negativer Aussenabstand in Hoehe des
   Polsters bringt die Flaeche an die Fensterkante, waehrend das eigene
   Innenpolster den Inhalt auf dem Rand haelt. Genau das war der Einwand
   gegen ein Buehnen-Polster — er war falsch, es geht mit einer Zeile.
   Wer eine weitere randlose Stelle braucht: `data-rand="aus"` ans
   Element. Absicht gehoert ins Markup, nicht in eine Ausnahmeliste im
   Pruefwerkzeug. */
.nv-filterreihe,
.nv-slidereihe,
[data-rand="aus"] { margin-inline: calc(-1 * var(--nv-rand)); }

/* ── 1. KOPFZEILE ───────────────────────────────────────────────────────
   EINE Hoehe, EINE Titelgroesse, EINE Knopfgroesse, GENAU EINE
   Rechtsaktion. Der Zurueck-Knopf und der Titel sitzen auf derselben
   Mittellinie — gemessen waren sie in «webanalytics» 21 px auseinander. */
.nv-kopf {
  display: flex; align-items: center; gap: 12px;
  min-height: var(--nv-kopf-hoehe);
  padding: calc(var(--nv-sockel-oben) + var(--nv-luft-0) + var(--nv-s3))
           calc(var(--nv-sockel-rechts) + var(--nv-rand)) 8px
           calc(var(--nv-sockel-links) + var(--nv-rand));
  background: transparent;
}
.nv-kopf-knopf {
  flex: 0 0 var(--nv-tap, 44px);
  width: var(--nv-tap, 44px); height: var(--nv-tap, 44px);
  display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  background: var(--k-fill, rgba(120, 120, 128, .08));
  color: var(--k-text);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.nv-kopf-knopf:active { transform: scale(.94); }
.nv-kopf-knopf .material-symbols-rounded { font-size: 22px; }
/* Titel + optionaler Unterbereich in EINER Zeile. Der Unterbereich ist
   der einzige erlaubte Zusatz — «Titel + Untertitel darunter» war eine
   der vier konkurrierenden Logiken und faellt weg. */
.nv-kopf-titel {
  flex: 1; min-width: 0;
  display: flex; align-items: baseline; gap: 8px;
}
.nv-kopf-titel > b {
  font-family: var(--nv-font-display, serif);
  font-size: 2.125rem; font-weight: 400; font-style: normal;
  line-height: 1.05; letter-spacing: -.01em; color: var(--k-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nv-kopf-bereich {
  flex: 0 1 auto; min-width: 0;
  display: inline-flex; align-items: center; gap: 3px;
  font-size: var(--k-fs-body); font-weight: 600; color: var(--k-text3);
  background: none; border: none; padding: 0; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nv-kopf-bereich .material-symbols-rounded { font-size: 16px; }

/* ── 2. SUCHFELD ────────────────────────────────────────────────────────
   EINE Breite: volle Spalte. Gemessen waren 59 %, 77 % und 83 % — in
   einem Modul zwei davon nebeneinander. */
.nv-suchzeile {
  display: flex; align-items: center; gap: 10px;
  padding: 0 0 var(--nv-luft-1, 10px);   /* Rand kommt von der Buehne */
}
.nv-suchfeld {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 8px;
  min-height: var(--nv-tap, 44px);
  padding: 0 14px;
  /* Kapsel, nicht `--nv-r-s` (E3, 2026-09-01): 20 px auf 44 px Hoehe
     ist 2 px unter der Kapsel — nach Pruefbedingung 4 ein «Drittes».
     Das Suchfeld ist bedienbar, also Kapsel. Bis dahin hat auf dem
     Schirm ohnehin `.nv-searchbar` (design-system.css, 52 px / 40 px,
     Rand, Schatten) gewonnen; die ist mit E3 gefallen. */
  border-radius: var(--nv-r-kapsel, 999px);
  background: var(--k-fill, rgba(120, 120, 128, .08));
}
/* Fokus ohne Rahmen: ein Ring AUSSERHALB der Flaeche, der nichts
   verschiebt. Der frühere `border: 1px solid transparent` sass
   dauerhaft im Layout, nur um im Fokus sichtbar zu werden — ein
   unsichtbarer Rahmen, der Platz nimmt (Stefan 2026-08-27: «Rahmen,
   keine Eleganz»). */
.nv-suchfeld:focus-within { box-shadow: 0 0 0 2px var(--k-brand); }
.nv-suchfeld .material-symbols-rounded { font-size: 20px; color: var(--k-text3); }
.nv-suchfeld input {
  flex: 1; min-width: 0; border: none; background: none; outline: none;
  font-size: .95rem; /* keine Sprosse bei 15,2 px — Stefan E-2 05.09.2026: «nie abrunden», hart bis die Leiter eine Sprosse hat */ color: var(--k-text);
}
/* Die Trennlinie der Gruppenzeile (`.nv-input:not(:last-child)`, 0,2,0)
   gehoert nicht in die Kapsel: folgt dem Feld ein Loeschen-Knopf, ist es
   nicht letztes Kind und bekaeme sie. Gemessen als feine Linie am
   unteren Rand (E3, 2026-09-01) — vorher von einem `!important` in
   style.css zugedeckt. */
.nv-suchfeld input.nv-input { background-image: none; }
/* Der Loeschen-Knopf im Suchfeld — Groesse aus einer Regel, nicht als
   Inline-Stil an jedem gerenderten Feld (C2, 2026-08-27).
   Seit E3 (2026-09-01) traegt der KNOPF das Merkzeichen (vorher das
   Symbol darin): er ist ein Kreis, unsichtbar, solange das Feld leer
   ist, und erscheint mit dem ersten Zeichen. Das stand als
   `.nv-searchbar__clear` in design-system.css und ist mit ihr gefallen. */
.nv-suchfeld [data-rolle="loeschen"] {
  flex: 0 0 auto;
  width: 28px; height: 28px;
  display: none; align-items: center; justify-content: center;
  border: none; border-radius: 50%; cursor: pointer;
  background: var(--k-fill-hover); color: var(--k-text3);
  font-size: var(--k-fs-hervor);
}
.nv-suchfeld input:not(:placeholder-shown) ~ [data-rolle="loeschen"] { display: flex; }

/* Der Filter-Knopf ist der EINZIGE erlaubte Nachbar des Suchfelds. */
.nv-suchzeile .nv-kopf-knopf { border-radius: var(--nv-r-s, 20px); }
/* Eine Auswahl NEBEN dem Suchfeld (team: Aktive/Ausgeschiedene/Alle)
   nimmt nur ihre eigene Breite — `.nv-input` traegt `width: 100 %`, und
   genau das drueckte das Suchfeld daneben auf 88 px (Eingabe 32 px;
   vorher 0 px, Klick-Probe #147, 02.09.2026). Das Suchfeld ist der
   dehnbare Teil der Zeile (`flex: 1 1 auto`), die Auswahl nicht. */
.nv-suchzeile > select.nv-input { width: auto; flex: 0 0 auto; }

/* ── 3. FILTER-CHIPS mit UEBERLAUF-HINWEIS ──────────────────────────────
   Gemessen: 776 px bzw. 3'523 px breite Reihen ohne jeden Hinweis, dass
   es rechts weitergeht. Der Verlauf am rechten Rand ist genau dieser
   Hinweis — er verschwindet, wenn man am Ende angekommen ist
   (`scroll-timeline` waere schoener, wird aber nicht ueberall
   unterstuetzt; die Maske haelt ueberall). */
.nv-filterreihe {
  position: relative;
  display: flex; gap: 8px;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
  padding: 0 var(--nv-rand) var(--nv-luft-1, 10px);
  scroll-snap-type: x proximity;
  /* Ohne diese Zeile rastet der Browser beim Laden auf den ersten Chip
     statt hinter den Seitenrand: gemessen scrollLeft = 18 bei padding-left
     18, die Reihe stand also linksbuendig am Bildschirmrand, waehrend
     Liste und Karten 18px eingerueckt waren. Faellt nur auf, wenn die
     Reihe ueberlaeuft — auf dem iPad passte sie und der Fehler blieb
     unsichtbar. (Gemessen 2026-08-27 an beispiel-hr.php.) */
  scroll-padding-inline-start: var(--nv-rand);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
}
.nv-filterreihe::-webkit-scrollbar { display: none; }
.nv-filterreihe.ist-am-ende { -webkit-mask-image: none; mask-image: none; }
.nv-filterchip {
  flex: 0 0 auto; scroll-snap-align: start;
  /* TIPPFLAECHE `--nv-tap` (44), nicht 36. Der Chip ist bedienbar, und
     Pruefbedingung 1 des Kanons kennt bei Bedienbarem keine Ausnahme.
     Mit 36 haetten die gefuehrten Stellen die Tippflaechen-Ratsche rot
     gefaerbt statt gruener — gemessen 2026-09-02 (N3). */
  min-height: var(--nv-tap); padding: 0 14px;
  display: inline-flex; align-items: center; gap: 6px;
  /* Pille, nicht «klein». Der Chip ist 36 px hoch — der alte Wert 18 war
     genau die halbe Hoehe und damit ohnehin eine Pille. Mit dem neuen
     16er-Token waere er unbeabsichtigt eckiger geworden. */
  /* Der Rand ist IMMER da und im Grundzustand durchsichtig. Sonst
     springt die Reihe um 2 px, sobald ein Chip gewaehlt wird — der
     gewaehlte traegt ab jetzt einen Rand statt einer Fuellung. */
  border: 1px solid transparent; border-radius: var(--nv-r-kapsel, 999px);
  background: var(--k-fill, rgba(120, 120, 128, .08));
  color: var(--k-text2); font-size: var(--k-fs-body); font-weight: 600;
  /* Ein Filter-Chip bricht nie um. Das stand an sieben Stellen als
     `style="white-space:nowrap;flex-shrink:0"` am Element — `flex-shrink`
     kommt schon aus `flex: 0 0 auto` oben, `white-space` fehlte hier und
     nur deshalb stand es dort. Gemessen 2026-09-02 (N3). */
  white-space: nowrap;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* GEWAEHLT = RAND, NICHT FUELLUNG (Stefan-Auftrag Nacht 02./03.09.2026,
   N3: «Aktive Pille mit Rand statt Fuellung»). Das ERSETZT die
   Entscheidung «aktiv = IMMER Nevanu-Pink» vom 2026-08-03; die aeltere
   steht damit nicht mehr daneben (CLAUDE.md §Self-Inspection: wer eine
   Direktive ersetzt, loescht die alte).
   ⚠ Der Zusatz «so wie in der Referenz» aus dem Auftrag stimmt so nicht:
   in `docs/DESIGN-REFERENZ-LOOK-AND-FEEL.md` steht der Satz nicht (am
   2026-09-02 nachgesucht). Die Anordnung traegt sich selbst — aber
   nachgemessen ist nachgemessen (Messregel 6).
   `.active` ist der Zustandsname, den die gefuehrten Stellen mitbringen;
   beide Namen bezeichnen denselben Zustand. */
.nv-filterchip.ist-gewaehlt,
.nv-filterchip.active {
  background: transparent;
  border-color: var(--k-brand);
  color: var(--k-brand);
}

/* Die Zahl im Chip (Neukunden-Filter: «Offen 7»). Stand als
   `.nk-filter-btn .nk-count` in style.css; mit dem Fuehren waere daraus
   eine ZWEITE Definition von `nv-filterchip` geworden — und eine doppelt
   definierte Klasse zaehlt in `tools/katalog-abdeckung.cjs` gar nicht mehr
   als abgedeckt, mit allen ihren Vorkommen. Deshalb hier, am Baustein,
   mit Tokens statt der beiden harten Farben von vorher. */
.nv-filterchip-zahl {
  display: inline-block; min-width: 16px; margin-left: 4px;
  padding: 1px 5px; text-align: center;
  border-radius: var(--nv-r-kapsel, 999px);
  background: var(--k-fill);
  font-size: var(--k-fs-micro);
}
.nv-filterchip.ist-gewaehlt .nv-filterchip-zahl,
.nv-filterchip.active .nv-filterchip-zahl { background: var(--nv-tint); }

/* ── 3b. SPALTENKOPF-ETIKETT (Kalender) ───────────────────────────────
   Der Name unter dem Avatar oder Symbol einer Kalenderspalte.

   Der Befund (N4, gemessen 2026-09-02 mit `tools/n4-reserve-probe.cjs`,
   390 px, isMobile, beide Themen): das RESERVE-Etikett trug `9px` +
   VERSALIEN + `letter-spacing:.04em` als Inline-Stil, das
   Personen-Etikett unmittelbar DANEBEN `11px` ohne beides. Zwei
   Etiketten nebeneinander, zwei Setzungen — und ausgerechnet das
   kleinere war durch Versalien und Laufweite das BREITERE. Die Spalte
   ist 60 px breit, davon 56 nutzbar.

   Warum der Pruefstand Stefans abgeschnittenes «RESERVE» nie sah: bei
   Textskalierung 1,0 / 1,4 / 1,8 blieb die gemessene Schriftgroesse
   dreimal auf 9px stehen — ein harter px-Wert folgt weder der
   Wurzel-Schriftgroesse noch `text-size-adjust`. Der Fehler war nicht
   «nicht vorhanden», sondern «mit diesem Messgeraet unsichtbar».
   Aus `--k-fs-caption` folgt das Etikett der Textgroesse des Geraets —
   und ohne Versalien und Laufweite ist es dabei SCHMALER als vorher. */
.kal-spaltenetikett {
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-bold);
  color: var(--k-text3);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── 4. SYMBOL-FELD — EINE Form ─────────────────────────────────────────
   Gemessen: vier Formen auf der Startseite gleichzeitig. Es gibt ab jetzt
   eine — ein weiches Quadrat. Rund war die zweithaeufigste Form; das
   weiche Quadrat gewinnt, weil es neben Karten und Zeilen ruhiger sitzt
   und die Marke ohnehin mit weichen Ecken arbeitet. */
.nv-symbolfeld {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--nv-r-chip, 16px);
  background: color-mix(in srgb, var(--f, var(--k-brand)) 15%, transparent);
  color: var(--f, var(--k-brand));
}
.nv-symbolfeld .material-symbols-rounded {
  font-size: 1.3125rem;
  /* Aktiv-Staerke aus dem Token statt einer eigenen Zahl. Vorher stand
     hier `'FILL' 1, 'wght' 500` — eine von 27 Kombinationen im Repo. */
  font-variation-settings: var(--nv-icon-aktiv);
}
/* ── 4b. AVATAR — die PERSON, nicht die Sache (Stefan-Entscheid 2026-08-27)
   Zwei Bausteine statt einer Variante, und die Regel ist eine Zeile:
       Person  → .nv-avatar      Kreis
       Sache   → .nv-symbolfeld  weiches Quadrat
   Warum getrennt: der Katalog hatte das Symbolfeld fuer Icons entworfen
   und die Kundenliste bekam beim Umbau dieselbe Form — Initialen sahen
   damit aus wie Kategorien. Ein Kreis sagt «hier steht jemand», ein
   Quadrat «hier steht etwas». Das ist keine Geschmacksfrage, sondern
   eine Bedeutung, und deshalb ein eigener Baustein und keine Abwandlung. */
.nv-avatar {
  flex: 0 0 auto;
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: color-mix(in srgb, var(--f, var(--k-brand)) 15%, transparent);
  color: var(--f, var(--k-brand));
  font-size: 0.9375rem; font-weight: 600; letter-spacing: .01em;
  position: relative; overflow: hidden;
}
.nv-avatar--gross { width: 52px; height: 52px; font-size: 1.1875rem; }
/* ── P20 (Stefan-Entscheid 2026-08-29): --verlauf ──────────────────────
   ROLLE: der Avatar steht fuer jemanden, von dem wir KEINEN Namen und
   kein Bild haben — eine unbekannte Nummer im Telefon-Modul.

   Die Grundform faerbt sich aus `--f`, der Farbe der Person. Wo es keine
   Person gibt, gibt es auch kein `--f`, und der Kreis faellt auf
   Markenrosa zurueck — dieselbe Farbe wie bei einer bekannten Person.
   Der Verlauf sagt: hier ist niemand hinterlegt. Das ist eine Aussage,
   keine Verzierung, und deshalb ein benannter Zustand.

   Stand als Inline-Verlauf an zwei Stellen in `telefon.js` (P20). */
.nv-avatar--verlauf {
  background: linear-gradient(135deg, var(--k-brand), var(--nv-purple));
  color: #fff;
}
/* Kleine Bauform, 2026-08-29 aus `design-system.css` geholt (A1).
   ⚠ SIE HAT DORT NICHT GEWIRKT: `.nv-avatar--sm` steht auf derselben
   Spezifitaetsstufe wie `.nv-avatar`, und der Katalog laedt spaeter
   (Position 14 gegen 7). Ein Element mit `--sm` war also 40 px gross,
   nicht 32. Aufgefallen ist das erst, als die Bauteil-Probe die
   Variante gemessen hat statt sie zu glauben.
   Sichtbar geworden ist der Fehler nie: die einzige Stelle, die die
   Klasse erzeugt, ist `renderAvatar()` in `js/form.js` — und die wird
   nirgends aufgerufen. */
.nv-avatar--klein { width: 32px; height: 32px; font-size: var(--k-fs-body); }
.nv-avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
/* Der Statuspunkt gehoert zum Avatar, nicht zu der Datei, aus der er
   kam. Ohne ihn waere er beim Aufraeumen still verschwunden.
   `position`/`overflow` standen bis 2026-09-03 in einem ZWEITEN
   `.nv-avatar`-Block hier darunter — zusammengefuehrt (Stufe 1). */
.nv-avatar__status {
  position: absolute;
  bottom: 0; right: 0;
  width: 10px; height: 10px;
  border-radius: 50%;
  border: 2px solid var(--k-bg, #0D0D0F);
}
.nv-avatar__status--online  { background: var(--k-green, #22c55e); }
/* Dieselbe Farbzuordnung wie beim Symbolfeld — ein Attribut, kein
   Inline-Stil. */
.nv-avatar[data-farbe="mf-rose"]      { --f: var(--mf-rose); }
.nv-avatar[data-farbe="mf-magenta"]   { --f: var(--mf-magenta); }
.nv-avatar[data-farbe="mf-koralle"]   { --f: var(--mf-koralle); }
.nv-avatar[data-farbe="mf-bernstein"] { --f: var(--mf-bernstein); }
.nv-avatar[data-farbe="mf-limette"]   { --f: var(--mf-limette); }
.nv-avatar[data-farbe="mf-gruen"]     { --f: var(--mf-gruen); }
.nv-avatar[data-farbe="mf-cyan"]      { --f: var(--mf-cyan); }
.nv-avatar[data-farbe="mf-blau"]      { --f: var(--mf-blau); }
.nv-avatar[data-farbe="mf-violett"]   { --f: var(--mf-violett); }
.nv-avatar[data-farbe="mf-neutral"]   { --f: var(--mf-neutral); }

/* Initialen (kein Symbol, sondern ein oder zwei Buchstaben) hatten keine
   eigene Groesse und erbten — live gemessen 14.08 px, ein rem-Rundungsrest
   aus dem Skalen-Befund. Feste Groesse, damit der Buchstabe im 40er-Feld
   immer gleich sitzt. */
.nv-symbolfeld {
  font-size: 0.9375rem; font-weight: 600; letter-spacing: .01em;
}
.nv-symbolfeld--gross { width: 52px; height: 52px; border-radius: var(--nv-r-s, 20px); }
.nv-symbolfeld--gross .material-symbols-rounded { font-size: 26px; }

/* ── 5. LISTENZEILE ─────────────────────────────────────────────────────
   Mit dem Innenabstand, den der Schatten braucht — die Regel aus dem
   Schatten-Befund (126 abgeschnittene Schatten, 120 davon hier). */
/* Platzreserve fuer den Schatten — seit 2026-08-27 GETRENNT von seiner
   Form (siehe Token-Block). 14 px, weil der hohe Schatten
   (0 8px 24px) seitlich rund 12 px ausgreift. */
.nv-schatten-reichweite { --nv-schatten-reserve: 14px; }
/* GRUPPENLISTE, NICHT GETRENNTE KARTEN (Stefan-Entscheid 2026-08-27).
   Der erste Entwurf gab JEDER Zeile Rundung und Schatten. Gemessen live:
   25 von 25 Zeilen trugen einen eigenen box-shadow, 24 davon INNERHALB
   der Gruppe — unter jeder Trennlinie lag dadurch ein zweiter, weicher
   Strich. Das ist die Handschrift von Karten, und Karten sind fuer
   ungleichartige Inhalte da; eine gleichfoermige Liste ist eine
   iOS-«inset grouped list»: EIN Container mit Rundung und EINEM Schatten,
   die Zeilen darin nur durch Haarlinien getrennt.
   Pruefbar: Anzahl Elemente mit box-shadow innerhalb der Liste == 1. */
.nv-liste {
  /* Der Container traegt Flaeche, Rundung und Schatten — als einziger. */
  background: var(--k-bg2, #fff);
  border-radius: var(--nv-r-karte, 26px);
  box-shadow: var(--nv-schatten-flach);
  overflow: hidden;              /* schneidet die Ecken der Randzeilen */
}
/* Kein eigener Seitenrand mehr: den vergibt die Buehne (§SEITENRAND).
   Bis zum 2026-08-27 stand er hier — und damit an genau so vielen
   Stellen, wie es Bausteine gibt. Wer einen neuen Baustein baut, kann
   eine Regel vergessen; eine Buehne kann er nicht vergessen. */
.nv-schatten-reichweite > .nv-liste,
.nv-liste { margin-inline: 0; }

.nv-listenzeile {
  display: flex; align-items: center; gap: 12px;
  /* Feste Zeilenhoehe statt min-height (Punkt 3): 64/65 px Schwanken kam
     aus dem Padding — 10 px oben/unten plus ein Inhalt, dessen Hoehe von
     gerundeten rem-Werten abhaengt (.95rem + .78rem + margin ergibt mal
     44.4, mal 44.6 px). `min-height` haette das ueberdeckt, nicht
     behoben. Jetzt bestimmt die Zeile ihre Hoehe selbst und der Inhalt
     zentriert sich darin. */
  height: 64px;
  padding: 0 16px;
  background: var(--k-bg2, #fff);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* Haarlinie zwischen den Zeilen, eingerueckt bis unter den Text — so
   macht es iOS, und so bleibt das Symbolfeld optisch frei.
   Die Einrueckung ist gerechnet, nicht geraten: Innenabstand (16) +
   Symbolfeld (40) + Spalt (12). Wer eine der drei Zahlen aendert,
   aendert die Linie mit. */
.nv-listenzeile { position: relative; }
.nv-listenzeile + .nv-listenzeile::before {
  content: '';
  position: absolute; top: 0;
  /* Rechts bis an die KARTENKANTE, nicht bis ans Zeilenpolster — so macht
     es iOS. Vorher endete die Linie 16 px vor dem Rand und liess einen
     hellen Streifen stehen (Befund 2026-08-27). */
  right: -16px;
  left: calc(16px + 40px + 12px);
  height: 1px;
  background: var(--k-border, rgba(58, 36, 42, .10));
}
/* Der Tipp-Zustand faerbt jetzt die Flaeche, statt die Zeile zu
   verkleinern: in einer Gruppe wuerde ein `scale` die Nachbarn
   anschneiden. */
.nv-listenzeile:active { background: var(--k-fill, rgba(120, 120, 128, .08)); }
.nv-listenzeile-text { flex: 1; min-width: 0; }
.nv-listenzeile-text b {
  display: block; font-size: .95rem; font-weight: 600; color: var(--k-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nv-listenzeile-text small {
  display: block; font-size: var(--k-fs-body); color: var(--k-text3); margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Rechte Spalte: ein Wert mit Etikett darunter (letzter Besuch, Betrag,
   Anzahl). Ergaenzt 2026-08-27 beim Umstellen des Kunden-Moduls — die
   bestehende Liste zeigt dort «Letzter Besuch», und ohne diese Spalte
   waere der Baustein fuer den ersten Ernstfall zu schmal gewesen.
   Kein neuer Bausteintyp: die Listenzeile bekommt ein Teil dazu. */
.nv-listenzeile-meta {
  flex: 0 0 auto; text-align: right; margin-left: 4px;
}
.nv-listenzeile-meta b {
  display: block; font-size: var(--k-fs-body); font-weight: 600; color: var(--k-text2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.nv-listenzeile-meta small {
  display: block; font-size: var(--k-fs-caption); color: var(--k-text4, var(--k-text3));
  margin-top: 1px; white-space: nowrap;
}
/* Der Pfeil ganz rechts. Ueber ein Attribut statt einer eigenen Klasse:
   die Praefix-Wache zaehlt Klassennamen, und ein Bauteil, das nur eine
   Rolle markiert, muss dafuer keinen neuen Namen verbrauchen. */
.nv-listenzeile [data-rolle="pfeil"] { color: var(--k-text3); flex: 0 0 auto; }

/* FARBE UEBER ATTRIBUT, NICHT UEBER INLINE-STIL.
   Vorher stand im Markup style="--f:var(--mf-rose)". Das war zwar nur ein
   Token-Verweis, hat aber die Inline-Stil-Ratsche steigen lassen — und
   eine Ratsche, die man mit «ist ja nur ein Token» aushebelt, ist keine.
   Zehn Zeilen hier ersetzen beliebig viele Inline-Stile draussen. */
.nv-symbolfeld[data-farbe="mf-rose"]      { --f: var(--mf-rose); }
.nv-symbolfeld[data-farbe="mf-magenta"]   { --f: var(--mf-magenta); }
.nv-symbolfeld[data-farbe="mf-koralle"]   { --f: var(--mf-koralle); }
.nv-symbolfeld[data-farbe="mf-bernstein"] { --f: var(--mf-bernstein); }
.nv-symbolfeld[data-farbe="mf-limette"]   { --f: var(--mf-limette); }
.nv-symbolfeld[data-farbe="mf-gruen"]     { --f: var(--mf-gruen); }
.nv-symbolfeld[data-farbe="mf-cyan"]      { --f: var(--mf-cyan); }
.nv-symbolfeld[data-farbe="mf-blau"]      { --f: var(--mf-blau); }
.nv-symbolfeld[data-farbe="mf-violett"]   { --f: var(--mf-violett); }
.nv-symbolfeld[data-farbe="mf-neutral"]   { --f: var(--mf-neutral); }

/* Zeilen-Zaehler ueber einer Gruppe («6746 Kunden · Seite 1»). Er steht
   AUSSERHALB der Gruppe — genau das war der Befund: als Kind zwischen den
   Zeilen machte er rows[0]:first-child unmoeglich. Vorher trug er einen
   Inline-Stil im Modul; jetzt eine Klasse, damit alle Listen ihn gleich
   setzen. */
.nv-listenkopf {
  margin: 0 0 8px;              /* Rand kommt von der Buehne */
  font-size: var(--k-fs-body); font-weight: 600; color: var(--k-text3);
  text-align: center;
}

/* ── 6. KARTE ───────────────────────────────────────────────────────────
   Dieselbe Schatten-Regel: der Container haelt den Platz vor, den der
   Schatten braucht. */
.nv-flaeche {
  /* Die Buehne gibt den Rand. Hier bleibt nur die Schatten-Reserve:
     die Karte darin traegt `margin: 0 var(--nv-schatten-reserve)`, damit ihr
     Schatten nicht abgeschnitten wird — der negative Aussenabstand
     holt genau diese 6 px wieder heraus, sonst staende die Karte auf
     24 statt auf 18. */
  padding-inline: 0;
  margin-inline: calc(-1 * var(--nv-schatten-reserve));
}
/* ⚠ `.k-stats` steht hier mit drin (A2, Stefan-Befund 2026-08-30).
   Gemessen in der Kundenansicht, alle drei Themen: die beiden oberen
   Karten trugen `rgb(255,255,255)` ohne Rand und 378 px Breite, die
   Zahlen-Karte `rgba(255,255,255,.78)` mit 1 px Rand und 342 px. Drei
   Karten nebeneinander, eine anders eingefaerbt.
   Es ist KEINE eigene Rolle: nirgends steht eine Begruendung, warum
   Zahlen auf Glas gehoeren und Text nicht. Es war eine zweite Fassung
   derselben Karte, entstanden als Modul-Uebersteuerung mit fuenf
   `!important` in `css/style.css`. Die ist entfernt; hier steht sie
   nur noch einmal. */
/* ⚠ DIE FORM DIESER BEIDEN STEHT SEIT DEM 02.09.2026 (N2) NICHT MEHR
   HIER, sondern unten in §DIE KARTE — in derselben Regel wie
   `k-glass-card`. Der Befund, der dazu gefuehrt hat: der Katalog trug
   die Karte ZWEIMAL. Diese Regel (25 Stellen, im laufenden Dokument 0
   von 12 gemessenen Modulen) und `body.k-ui .k-glass-card` (466
   Stellen, 74 sichtbare) — ein Bauteil, zwei Namen, zwei Formen: hier
   mit Fuellung und `--nv-schatten-hoch`, dort ohne Fuellung und mit
   `--nv-sh`. Der Abschnitt heisst «jeder Baustein in GENAU EINER
   Ausfuehrung»; zwei Ausfuehrungen im selben Abschnitt sind der
   deutlichste Fall davon.
   Was hier BLEIBT, ist Platzierung — wo die Karte sitzt, nicht wie sie
   aussieht. Die Fuellung steht am Inhalt (§16), nicht an der Karte;
   deshalb tragen die Stellen dieser beiden Namen jetzt
   `.nv-karte-inhalt`. */
.nv-karte,
.k-stats {
  margin: 0 var(--nv-schatten-reserve) var(--nv-luft-2, 16px);
}
/* `.k-stats` behaelt seine Fuellung HIER und bekommt keine
   `.nv-karte-inhalt`-Klasse: es ist ein Kennzahlen-RASTER (flex,
   space-around), keine Karte mit freiem Inhalt — es hat 2026-08-30 nur
   die Flaeche der Karte mitbenutzt. Eine seiner drei Stellen
   (js/kunden.js:812) traegt ausserdem ein `style`-Attribut; jede
   beruehrte js-Zeile mit `style="…"` faerbt die Neuzugang-Wache rot.
   Beides zusammen: Rolle klaeren, dann umstellen — nicht nebenbei. */
.k-stats { padding: var(--nv-luft-2, 16px); }
.nv-karte-kopf {
  display: flex; align-items: center; gap: 10px;
  margin-bottom: var(--nv-luft-1, 10px);
}
.nv-karte-kopf b { flex: 1; font-size: var(--k-fs-hervor); font-weight: 600; color: var(--k-text); }

/* ── 6b. KENNZAHL IM KASTEN — k-stat, k-stat-value, k-stat-label ───────
   2026-08-31, Paket D, D1 Team-HR Etappe 2, §Personenakte.

   Der Kasten `.k-stats` stand seit dem 30.08. hier oben im selben
   Selektor wie `.nv-karte`. Sein INHALT stand weiterhin draussen — und
   zwar VIERMAL:

     1  keeva-ui.css:486  `.k-stat`        @layer components
     2  keeva-ui.css:493  `.k-stat-value`  @layer components, 1.8rem
     3  keeva-ui.css:4380 `& .k-stat-value` @layer overrides, 2rem
     4  style.css:4637-39 `body.k-ui .k-stat*`  ungelayert, 8 × !important

   WELCHE KAM AN. Gemessen am lebenden Dokument mit dem echten
   Stilvorlagen-Stapel aus index.php, alle drei Themen, 390 und 1280 px,
   Herkunft je Eigenschaft ueber CSS.getMatchedStylesForNode:

     .k-stat-value   font-size 22.4 px  ->  Fassung 4 (1.4rem)
                     font-weight 700    ->  Fassung 4
                     letter-spacing/line-height/color -> Fassung 2
     .k-stat-label   alles ausser dem Gewicht -> Fassung 4
                     font-weight 600    ->  Fassung 2 (4 setzt es nicht)
     .k-stat         alle vier Angaben  ->  Fassung 1

   WARUM FASSUNG 4 GEWINNT, obwohl sie als zweite Datei geladen wird und
   die beiden anderen spaeter kommen: sie ist UNGELAYERT und traegt
   `!important`. Bei `!important` kehrt sich die Ebenen-Ordnung um — die
   implizite aeussere Ebene steht dann ganz oben, nicht ganz unten. Die
   beiden keeva-ui-Fassungen liegen in `@layer components` bzw.
   `@layer overrides` und konnten deshalb NIE gegen sie ankommen; 2rem
   und 1.8rem waren zwei Zahlen, die nie jemand gesehen hat.

   Es ist ausdruecklich NICHT die Mehrheitsfassung: 1.8rem und 2rem
   stehen zweimal da, 1.4rem einmal — und 1.4rem ist die, die stimmt
   (Kanon-Messregel 2). Die drei anderen sind entfernt.

   ZWEI WERTE STEHEN HIER ALS ZAHL, und beide mit Grund:
   `font-size: 1.4rem` (22.4 px) liegt auf KEINER Stufe der Schriftskala
   — die naechste, `--k-fs-hervor`, ist 17.6 px. Sie zu nehmen haette
   die Zahl um 4.8 px verkleinert; das waere eine Umgestaltung, nicht
   ein Umzug. Dieselbe Ueberlegung steht bei `.nv-statkachel-wert`
   (§7, 1.625rem). Alles andere kommt aus Tokens.

   ⚠ `.k-stat` IST NICHT UEBERFLUESSIG, auch wenn es in der
   Kennzahlen-Reihe des Kundenprofils nichts mehr tut. Dort stand es auf
   demselben Element wie `.nv-raster-feld` (§32) und verlor jede seiner
   vier Angaben gegen dieses — gemessen: gap 6 px statt 2 px, dazu
   Polsterung 6 px und `min-height: 44px`, die `.k-stat` gar nicht hat.
   Aus dem Raster ist es deshalb entfernt. Gebraucht wird es weiterhin
   in der JAHRESZEILE (`js/kunden.js`, `_loadKundenUmsatz`): dort gibt
   es kein Raster, und `.k-stat` ist das einzige, was die zwei Kacheln
   zu Spalten macht.

   VERWANDT js/kunden.js (Kennzahlen + Jahreszeile),
            tools/bauteil-probe.cjs, tools/umbruch-probe.cjs,
            tools/raster-passmessung.cjs */
.k-stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--k-sp-2xs);
  text-align: center;
}
.k-stat-value {
  font-size: 1.4rem;                  /* 22,4 px, keine Sprosse — Stefan E-2 05.09.2026: «nie abrunden», hart */
  font-weight: var(--k-fw-bold);
  letter-spacing: var(--k-ls-tight);
  line-height: var(--k-lh-none);
  color: var(--k-text);
}
.k-stat-label {
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-semibold);
  letter-spacing: var(--k-ls-wide);   /* .06em — die Fassung, die ankam */
  text-transform: uppercase;
  color: var(--k-text3);
  margin-top: var(--k-sp-2xs);
}
/* Die Jahreszeile steht direkt unter der Kennzahlen-Reihe und ist enger
   angebunden als ein neuer Abschnitt: sie erlaeutert die Zahlen darueber.
   Der Abstand stand bis heute als `style="margin-top:8px"` im Markup —
   die einzige freie Pixelzahl dort. 8 px ist `--nv-s2`, also dieselbe
   Zahl aus der Skala statt aus der Hand. */
.k-stats + .k-stats { margin-top: var(--nv-s2); }
/* ⚠ DAS `hidden`-ATTRIBUT BRAUCHT HIER EINE ZEILE, sonst wirkt es nicht.
   `[hidden] { display: none }` ist eine Regel des BROWSERS, und jede
   Autoren-Regel schlaegt sie — auch eine gelayerte. `.k-stats` bekommt
   in `keeva-ui.css` `display: flex`, also blieb die Jahreszeile mit
   `hidden` sichtbar. Eine Zeile mit hoeherer Spezifitaet statt eines
   `!important`. */
.k-stats[hidden] { display: none; }

/* ── 7. STAT-KACHEL ─────────────────────────────────────────────────────
   Fuer Zahlenreihen. Bricht ab Tablet auf mehr Spalten um, statt sich in
   die Breite zu ziehen (Befund «Layout-Luecken»: Kacheln fuellten nur die
   linke Haelfte). */
/* DIE SPALTENZAHL GEHOERT DEM BAUTEIL (Stefan-Anweisung 2026-08-29).
   Nicht elf Stellen aendern — elf Stellen entfernen. Die Reihe bestimmt
   ihre Spalten selbst nach verfuegbarer Breite; es gibt keine
   Haltepunkte von Hand und kein `!important`, das die vierte Kachel
   umbricht, ohne dass es jemand gemeint hat.

   136 px IST GEMESSEN, NICHT GESCHAETZT. Bei 26 px / Gewicht 500 passen
   alle vier Werte, die im Code wirklich entstehen (`12'345`, `-123:45`,
   `128470`, `+12.5 %`), ab **134 px** Kachelbreite — bei 133 px sind es
   noch 3 von 4. Aufgerundet auf die 4er-Teilung: 136.
   Nachzurechnen mit `node tools/kachel-probe.cjs`.

   Hier stand vorher 140 px. Die Zahl war geraten und lag zufaellig
   richtig; sie steht jetzt da, weil sie gemessen ist.

   `auto-fit` statt `auto-fill`: leere Spuren fallen zusammen, also gibt
   es nie mehr Spalten als Kacheln. Vier Kacheln legen sich damit von
   selbst auf 2x2 am Telefon und 4x1 am Schreibtisch. */
.nv-statreihe,
.nv-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--nv-kachel-min, 136px), 1fr));
  gap: var(--nv-s3, 12px);
  padding-inline: 0;                                   /* Rand: Buehne */
  margin-inline: calc(-1 * var(--nv-schatten-reserve));   /* Schatten-Reserve */
}
.nv-statkachel {
  margin: 0 var(--nv-schatten-reserve);
  padding: 14px;
  border-radius: var(--nv-r-karte, 26px);   /* gross: die Kachel ist eine Flaeche, keine Zeile */
  background: var(--k-bg2, #fff);
  box-shadow: var(--nv-schatten-flach);
}
/* WARUM HIER KEINE DISPLAY-SCHRIFT STEHT (gemessen 2026-08-27)
   Der erste Entwurf setzte den Wert in Eros. Im Bild las sich die «3»
   als «5» und die «2» hing unter der Grundlinie — Mediaevalziffern.
   Die Schriftdatei erklaert warum: fonts/Eros-Regular-sub.woff2 hat die
   GSUB-Features aalt dlig liga locl salt ss01-ss06 und **kein lnum, kein
   tnum**. Es gibt in Eros schlicht keine Versalziffern, also kann auch
   `font-variant-numeric` sie nicht geradestellen — die Regel waere
   wirkungslos gewesen, nicht bloss schwach.
   Von 24 CSS-Regeln, die Eros setzen, traegt keine eine Zahl als
   Hauptinhalt. Eine Kachel, deren einziger Zweck eine Zahl ist, ist
   deshalb der falsche Ort fuer diese Schrift. Titel: Eros. Zahlen:
   Textschrift mit tabellarischen Ziffern, wie an 33 anderen Stellen. */
.nv-statkachel-wert {
  font-size: 1.625rem; font-weight: 500; line-height: 1.1; color: var(--k-text);
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
/* `.nv-statkachel-wert--dringend` (die «dringend»-Abweichung der Kennzahl)
   stand hier bis 05.09.2026 und wurde nirgends benutzt — repoweit null
   Treffer in js/, src/, *.php, devices/, luxury/, invoices/ und in der
   Bauteil-Probe (N3-Befund, N4 K-2 nachgemessen). Entfernt. Wer sie
   braucht, legt sie HIER neu an, am Baustein. */
.nv-statkachel-label {
  font-size: .74rem; font-weight: 600; color: var(--k-text3); margin-top: 3px;
}

/* ── 8. SLIDE-REIHE — ein Verhalten ─────────────────────────────────────
   Waagrechte Reihe von Karten. Gleiches Verhalten auf Telefon und Tablet;
   die Karte wird breiter, die Regel bleibt. Rechter Hinweis wie bei den
   Chips. */
.nv-slidereihe {
  display: flex; gap: 12px;
  overflow-x: auto; scrollbar-width: none;
  scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch;
  padding: var(--nv-schatten-reserve) calc(var(--nv-rand) - var(--nv-schatten-reserve));
  /* Gleicher Grund wie bei der Filterreihe: der Snap-Punkt muss hinter
     den Seitenrand, sonst startet die Reihe buendig am Bildschirmrand. */
  scroll-padding-inline-start: calc(var(--nv-rand) - var(--nv-schatten-reserve));
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
          mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent 100%);
}
.nv-slidereihe::-webkit-scrollbar { display: none; }
.nv-slidereihe.ist-am-ende { -webkit-mask-image: none; mask-image: none; }
.nv-slidekarte {
  flex: 0 0 78%; max-width: 320px;
  scroll-snap-align: start;
  padding: var(--nv-luft-2, 16px);
  border-radius: var(--nv-r-karte, 24px);
  background: var(--k-bg2, #fff);
  box-shadow: var(--nv-schatten-hoch);
}
@media (min-width: 768px)  { .nv-slidekarte { flex-basis: 42%; } }
@media (min-width: 1180px) { .nv-slidekarte { flex-basis: 30%; } }

/* ── 9. INHALTS-BREITE ──────────────────────────────────────────────────
   Auf dem iPad zog sich der Inhalt bis an beide Kanten (Befund
   «Layout-Luecken»). Eine Maximalbreite haelt die Zeile lesbar. */
.nv-inhalt { width: 100%; max-width: var(--nv-page-max, 1180px); margin: 0 auto; }

/* ── 9b. SITE-ABZEICHEN AUF DER TITELZEILE ──────────────────────────────
   Stefan-Entscheid 2026-08-27: Kontext gehoert neben den Titel, nicht
   darunter. Als dritte Zeile machte das Abzeichen die acht
   Website-Module zur hoechsten Gruppe (97 px statt 56).

   DIE REGEL BEI WENIG PLATZ: das Abzeichen kuerzt sich, NIE der Titel.
   Technisch heisst das: die Titelzeile ist ein Flex-Container, der Titel
   darf schrumpfen aber nie unter seine Textbreite (min-width:0 +
   ellipsis), das Abzeichen hat `flex-shrink` und gibt ab 390 px zuerst
   seinen Text auf — das Symbol bleibt, damit man sieht, DASS ein
   Site-Kontext da ist, und der Titel darauf tippen kann.

   F-2b (2026-09-07): dieselbe Zeile traegt jetzt auch das Wetter des
   Kalenders (`.kal-rose-wetter2`, Stefan: «Wetter … hoch neben das
   grosse Tagesdatum»). Es ist DIESELBE Sache — eine Angabe neben dem
   Titel, die sich zuerst kuerzt —, also steht sie an DIESEM Bauteil in
   der Selektorliste und nicht als zweite Fassung daneben
   (Katalog-Pflicht). */
.k-module-header-center:has(.k-mh-site--inline),
.k-module-header-center:has(.kal-rose-wetter2) {
  display: flex; align-items: center; gap: 8px; min-width: 0;
}
.k-module-header-center:has(.k-mh-site--inline) .k-module-header-title,
.k-module-header-center:has(.k-mh-site--inline) .k-mh-title-btn,
.k-module-header-center:has(.kal-rose-wetter2) .k-module-header-title,
.k-module-header-center:has(.kal-rose-wetter2) .k-mh-title-btn {
  min-width: 0; flex: 0 1 auto;
  overflow: hidden;
}
/* Solange das Wetter nicht geladen ist, ist der Kasten leer — und ein
   leeres Flex-Kind kostet trotzdem seinen Zwischenraum. Dieselbe Regel
   traegt `.kal-wetter-inline` seit je (css/kalender-kopf.css). */
.kal-rose-wetter2:empty { display: none; }
/* G5 (Block G): neben dem Abzeichen galt hier noch einmal `nowrap` +
   Ellipse (Gewicht 0,3,0 — schlug die Katalog-Regel §22). Gemessen:
   «Website-Analytics» bei 320 als 118 px breites Element, Text 199 px.
   Der Titel bricht auch neben dem Abzeichen um; das Abzeichen kuerzt
   sich weiterhin zuerst (max-width 45 %, Text ab 430 px weg). */
.k-module-header-center:has(.k-mh-site--inline) .k-module-header-title {
  white-space: normal; overflow-wrap: anywhere;
}
.k-mh-site--inline {
  flex: 0 1 auto; min-width: 0;
  display: inline-flex; align-items: center; gap: 4px;
  max-width: 45%;
}
.k-mh-site--inline .material-symbols-rounded {
  font-size: 0.8125rem; flex: 0 0 auto;
}
.k-mh-site--inline .k-mh-site-text {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
/* Auf schmalen Geraeten gibt das Abzeichen seinen Text auf. Der volle
   Name steht weiterhin im `title`, und die Websites-Ebene bleibt der
   Weg zum Umschalten — das Abzeichen ist eine Angabe, kein Schalter. */
@media (max-width: 430px) {
  .k-mh-site--inline .k-mh-site-text { display: none; }
  .k-mh-site--inline { max-width: none; }
}

/* ── 10. DIE FUENF ZUSTAENDE ────────────────────────────────────────────
   Ab hier hat JEDER Baustein dieselben fuenf Zustaende. Das ist keine
   Politur, sondern der Grund, warum diese Regeln JETZT kommen und nicht
   nach der Modul-Umstellung: wer erst umstellt und die Zustaende danach
   nachruestet, fasst dieselben 894 Stellen ein zweites Mal an.

     Ruhe          der Normalfall, braucht keine Klasse
     Tippen        :active — waehrend der Finger liegt
     Aktiv         .ist-gewaehlt — dieser Eintrag ist der gewaehlte
     Deaktiviert   [disabled] — da geht gerade nichts (`.ist-gesperrt`
                   stand bis 05.09.2026 daneben, wurde nirgends gesetzt
                   und ist entfernt — N4 K-2)
     Laden         .laedt — es passiert etwas, das dauert

   Die Zeiten kommen aus den Token in design-tokens.css (--nv-dur-fast,
   --nv-dur, --nv-ease, --nv-ease-bounce). Die gab es laengst; sie waren
   nur nirgends benutzt (gemessen 2026-08-27: 3 / 0 / 0 Verwendungen).
   Deshalb hier keine neuen Token, sondern die vorhandenen angewandt.

   Fuehlbar wird das Tippen ueber `nvHaptik('tippen')` in js/haptik.js —
   auf dem iPhone passiert dabei heute nichts (iOS Safari kennt die
   Vibration-API nicht, auch nicht als PWA), in der nativen Huelle spaeter
   die echte iOS-Haptik. Deshalb DARF sich der Tipp-Zustand nicht auf die
   Haptik verlassen: er muss sichtbar sein. Das ist die Aufgabe dieser
   Regeln. */

/* Tippen — die Flaeche gibt kurz nach und der Schatten rueckt naeher, so
   wie ein Gegenstand, den man auf die Unterlage drueckt. */
.nv-listenzeile,
.nv-karte,
.nv-slidekarte,
.nv-statkachel,
.nv-filterchip,
.nv-symbolfeld,
.nv-avatar,
.nv-kopf-knopf {
  transition: transform var(--nv-dur-fast, .15s) var(--nv-ease, ease),
              box-shadow var(--nv-dur-fast, .15s) var(--nv-ease, ease),
              background-color var(--nv-dur-fast, .15s) var(--nv-ease, ease),
              opacity var(--nv-dur-fast, .15s) var(--nv-ease, ease);
  -webkit-tap-highlight-color: transparent;
}
/* Die Listenzeile ist hier ABSICHTLICH nicht dabei: sie sitzt in einer
   Gruppe, und ein `scale` wuerde die Nachbarzeilen anschneiden, ein
   eigener Schatten die Gruppen-Optik zerstoeren. Ihr Tipp-Zustand faerbt
   die Flaeche und steht beim Baustein selbst. */
.nv-karte:active,
.nv-slidekarte:active,
.nv-statkachel:active { transform: scale(.985); box-shadow: var(--nv-schatten-flach); }
.nv-filterchip:active,
.nv-symbolfeld:active,
.nv-kopf-knopf:active { transform: scale(.94); }

/* Aktiv — gewaehlt ist nicht dasselbe wie gedrueckt. Der Chip trug das
   schon (.ist-gewaehlt); jetzt gilt dieselbe Sprache fuer alle. */
.nv-karte.ist-gewaehlt,
.nv-statkachel.ist-gewaehlt {
  box-shadow: var(--nv-schatten-hoch), inset 0 0 0 2px var(--k-brand);
}
/* In der Gruppe reicht ein Balken am linken Rand — ein Ring um die Zeile
   wuerde die Haarlinien der Nachbarn ueberzeichnen. */
.nv-listenzeile.ist-gewaehlt {
  box-shadow: inset 3px 0 0 var(--k-brand);
  background: var(--k-fill, rgba(120, 120, 128, .06));
}

/* Deaktiviert — halb sichtbar und ohne Tipp-Antwort. Ausdruecklich NICHT
   ausgegraut: Grau wuerde im Rosé-Grund wie ein Fehler aussehen. */
.nv-filterchip:disabled,
.nv-kopf-knopf:disabled {
  opacity: .42;
  pointer-events: none;
}

/* Laden — ein Schimmer, der ueber die Flaeche wandert. Er ersetzt den
   Inhalt, statt ihn zu ueberlagern: ein Ladebalken ueber echtem Text
   laesst den Text kurz falsch aussehen. */
.nv-listenzeile.laedt,
.nv-karte.laedt,
.nv-statkachel.laedt,
.nv-slidekarte.laedt {
  position: relative;
  color: transparent;
  pointer-events: none;
  overflow: hidden;
}
.nv-listenzeile.laedt::after,
.nv-karte.laedt::after,
.nv-statkachel.laedt::after,
.nv-slidekarte.laedt::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(100deg,
      transparent 20%,
      rgba(var(--nv-schatten-ton, 58, 36, 42), .06) 50%,
      transparent 80%);
  background-size: 220% 100%;
  animation: nv-schimmer 1.4s var(--nv-ease, ease) infinite;
}
@keyframes nv-schimmer {
  from { background-position: 120% 0; }
  to   { background-position: -120% 0; }
}

/* Wer Bewegung abgestellt hat, bekommt keine. Das ist eine
   Bedienungshilfe-Einstellung des Geraets, keine Geschmacksfrage. */
@media (prefers-reduced-motion: reduce) {
  .nv-listenzeile, .nv-karte, .nv-slidekarte, .nv-statkachel,
  .nv-filterchip, .nv-symbolfeld, .nv-kopf-knopf { transition: none; }
  .nv-listenzeile:active, .nv-karte:active,
  .nv-slidekarte:active, .nv-statkachel:active,
  .nv-filterchip:active, .nv-symbolfeld:active,
  .nv-kopf-knopf:active { transform: none; }
  .nv-listenzeile.laedt::after, .nv-karte.laedt::after,
  .nv-statkachel.laedt::after, .nv-slidekarte.laedt::after { animation: none; }
}

/* ── 11. KNOPF — der Baustein mit der groessten Masse ────────────────────
   Zusammengefuehrt 2026-08-28. Vorher stand das Aussehen eines Knopfes in
   SECHS Dateien: design-system.css (Grundform und Varianten),
   system.css an drei weiteren Stellen, nv-consistency.css an zwei,
   keeva-ui.css in einem verschachtelten Block. Gemessen mit
   tools/katalog-abdeckung.cjs: 2'741 der 6'234 Klassen-Vorkommen im
   ausgelieferten Code sind Knopf-Klassen — 44 %. Kein anderer Baustein
   kommt in die Naehe.

   WAS DABEI HERAUSKAM, UND WARUM ES WICHTIG IST
   Ueber der Grundform in design-system.css steht seit dem 2026-08-03 der
   Satz «DIE EINZIGE STELLE, AN DER EIN KNOPF SEINE FORM BEKOMMT». Bei der
   Messung war er fuer die zwei meistbenutzten Varianten bereits nicht mehr
   wahr:
     · `--primary` bekam sein Aussehen NICHT von dort, sondern aus
       system.css (Verlauf statt Flachfarbe, mit !important)
     · `--secondary` ebenso wenig: `body.k-ui .nv-btn--secondary` machte
       aus dem Glas-Look eine weisse Karte
   Beide Male gewann die spaetere Datei, und der Satz darueber blieb
   stehen. Ein Kommentar, der eine Ordnung BEHAUPTET, haelt sie nicht —
   das tut nur eine Messung. Deshalb hat dieser Abschnitt eine:
   `tools/katalog-abdeckung.cjs` faellt rot, wenn ein Knopf wieder
   woanders definiert wird.

   WAS SICH AM AUSSEHEN AENDERT: NICHTS.
   Die Werte hier sind nicht neu erfunden, sondern das GEMESSENE Ergebnis
   der bisherigen Kaskade (tools/stil-probe.cjs, 67 Module, 1'075
   Vorkommen). Belegt durch den Vergleich vorher/nachher.

   AENDERN heisst: hier aendern. Sonst nirgends.
   ────────────────────────────────────────────────────────────────────── */

/* Untergrenze fuer alles Antippbare. `:where()` gibt Gewicht 0 — eine
   Untergrenze, die man ueberschreiben muss, waere keine. */
:where(button, [role="button"], a.nv-btn, .nv-btn, .k-btn) {
  min-height: var(--nv-tap);
}

/* ── 11a. Die Grundform ── */
.nv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--k-sp-sm, 8px);
  min-height: 52px;
  padding: 14px 32px;
  border: 1px solid var(--k-border-strong, rgba(26, 26, 26, .15));
  background: var(--k-fill, rgba(26, 26, 26, .04));
  color: var(--k-text, #1A1A1A);
  border-radius: var(--k-r-full, 9999px);
  font-family: var(--k-font, 'Outfit', sans-serif);
  font-size: var(--k-fs-body, .88rem);
  font-weight: var(--k-fw-bold, 700);
  /* KEINE VERSALIEN. Stand bis 2026-08-28 als eigene !important-Zeile
     weiter oben in dieser Datei, weil die Grundform in einer frueheren
     Datei Grossbuchstaben setzte. Beides ist jetzt hier, und der
     Widerspruch damit weg. */
  letter-spacing: 0;
  text-transform: none;
  line-height: 1;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  -webkit-appearance: none;
  appearance: none;
  text-decoration: none;
  white-space: nowrap;
  transition:
    background-color var(--k-dur-fast, .15s) var(--k-ease, cubic-bezier(.32, .72, 0, 1)),
    color var(--k-dur-fast, .15s) var(--k-ease, cubic-bezier(.32, .72, 0, 1)),
    border-color var(--k-dur-fast, .15s) var(--k-ease, cubic-bezier(.32, .72, 0, 1)),
    box-shadow var(--k-dur-fast, .15s) var(--k-ease, cubic-bezier(.32, .72, 0, 1)),
    transform var(--k-dur-fast, .15s) var(--k-ease, cubic-bezier(.32, .72, 0, 1)),
    opacity var(--k-dur-fast, .15s) var(--k-ease, cubic-bezier(.32, .72, 0, 1));
}
.nv-btn:hover {
  background: var(--k-fill-hover, rgba(26, 26, 26, .07));
  border-color: var(--k-brand);
  color: var(--k-brand);
}
.nv-btn:active { transform: scale(.96); }
.nv-btn:disabled,
.nv-btn[disabled] { opacity: .4; pointer-events: none; }

/* ── 11b. Varianten — sie sagen, was der Knopf BEDEUTET ── */

/* Haupt-Knopf: Marken-Verlauf mit Glanz-Lauf und ruhiger Atmung.
   Der Verlauf stand frueher an drei Stellen: hier in system.css, in
   nv-consistency.css mit Theme-Praefix, und als Flachfarbe in
   design-system.css. Alle drei mit Vorrang-Markierung oder Praefix — jede
   wollte die andere schlagen.

   WARUM DER FLACHERE SCHATTEN, UND WARUM FUER BEIDE THEMEN
   (Stefan-Entscheid 2026-08-28.) Bis dahin sah der Haupt-Knopf in HELL
   anders aus als in DUNKEL: zweistufiger Verlauf mit Schatten 6/18 bei
   28 % gegen dreistufigen mit 12/28 bei 36 %. Das war kein Entscheid,
   sondern ein Spezifitaets-Zufall — die Hell-Regel trug
   `[data-theme="light"]` nicht, weil hell etwas anderes braucht, sondern
   um die Kaskade zu gewinnen. Damit galt sie nur in hell, und in dunkel
   blieb die andere stehen. Monatelang, unbemerkt.

   Gewaehlt wurde der FLACHERE fuer beide. Stefans wiederkehrender Befund
   war «Schattierungen, die nicht schoen wirken» und «schmuddelig»:
   28 px Unschaerfe bei 36 % Deckkraft in Markenrosa auf rosé Grund ist
   genau dieser Dunst. Der kraeftigere ist nicht eleganter, er ist lauter.

   Falls der flachere Schatten in Dunkel spaeter zu schwach wirkt, wird er
   als BENANNTES Token nachgezogen — nicht als Themenregel. Und erst dann,
   wenn Dunkel bewusst geprueft wird: nicht vorbauen. Eine Themenregel
   waere genau der Weg zurueck in den Zustand, den dieser Abschnitt
   beendet. */
/* Nacht 4 (Stefan 15+24, 06.09.2026): der Hauptknopf traegt den EINEN
   dunklen Markenton flach — kein Verlauf mehr, keine Atmung (auf einer
   Flaeche ohne Verlauf war sie unsichtbar). Weisse Schrift, Kontrast 4.56
   gemessen (Pflicht >= 4.5). Der Glanzlauf (::after) bleibt. */
.nv-btn--primary {
  background: var(--k-violet);
  border: none;
  color: var(--k-text-auf-marke);
  box-shadow: 0 6px 18px rgba(var(--nv-akzent-aktiv-rgb), .28);
  position: relative;
  overflow: hidden;
}
.nv-btn--primary::after {
  content: '';
  position: absolute; top: -60%; bottom: -60%; width: 40%; left: -70%;
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, .45), transparent);
  transform: skewX(-18deg);
  animation: nvGlanzLauf 4.6s ease-in-out infinite;
  pointer-events: none;
}
/* ── P14 (Stefan-Entscheid 2026-08-29): der Hauptknopf verschwand beim
   Ueberfahren ──────────────────────────────────────────────────────────
   Gemessen: in Ruhe `rgb(255,255,255)`, im Schwebezustand
   `rgb(242,113,130)` — also markenrosa Schrift auf markenrosa Grund.
   Ursache ist reine Spezifitaet: `.nv-btn:hover` ist (0,2,0),
   `.nv-btn--primary` nur (0,1,0). Die Grundform faerbt beim Ueberfahren
   Text und Rand markenrosa — richtig fuer einen Knopf mit hellem Grund,
   falsch fuer den einen, DESSEN Grund schon markenrosa ist.

   Das ist ein KATALOG-Fehler, kein Modul-Fehler. Sechs Aufrufstellen
   deckten ihn mit einem Inline-`color:#fff` zu — jede fuer sich richtig,
   und keine hat gemerkt, dass sie eine Luecke im Katalog fuellt. Genau
   dafuer ist der Katalog da: der Zustand gehoert einmal hierher, statt
   sechsmal dorthin. Mit dieser Regel fallen die sechs von selbst. */
.nv-btn--primary:hover {
  color: var(--k-text-auf-marke);
  border-color: transparent;
  background: var(--k-violet);
}
.nv-btn--primary:active { transform: scale(.97); }

/* Zweit-Knopf: weiche Karte statt grauem Rahmen. Stand als
   `body.k-ui .nv-btn--secondary` weiter oben und gewann ueber die
   Glas-Fassung in design-system.css — die damit tot war. */
.nv-btn--secondary {
  background: var(--k-bg2);
  border: 1px solid var(--k-border);
  box-shadow: var(--nv-sh);
  color: var(--k-text2);
  border-radius: var(--k-r-full, 999px);
}
.nv-btn--secondary:hover {
  background: var(--nv-glass-bg-hover, rgba(40, 40, 45, .88));
  border-color: var(--k-border-strong, rgba(26, 26, 26, .15));
}
.nv-btn--secondary:active { background: var(--k-fill-active, rgba(26, 26, 26, .12)); }

/* Dunkel: invertiert. Der Verlaufs-Block in keeva-ui.css hat diese
   Variante mit !important zum zweiten Haupt-Knopf gemacht — genau die
   Sorte Regel, die eine Variante bedeutungslos macht. Sie ist weg; dunkel
   ist wieder dunkel. */
.nv-btn--dark {
  background: var(--k-text, #1A1A1A);
  color: var(--k-bg, #FAFAFA);
  border: none;
  letter-spacing: -.2px;
}
.nv-btn--dark:active { opacity: .85; }

.nv-btn--success {
  background: var(--k-green, #34D399);
  color: #fff;
  box-shadow: 0 8px 30px rgba(45, 138, 78, .3);
}
.nv-btn--success:active { box-shadow: none; opacity: .85; }

.nv-btn--gradient {
  background: var(--k-gradient, linear-gradient(135deg, #ec4899, var(--k-brand)));
  color:var(--k-text-auf-marke);
  box-shadow: var(--k-gradient-glow, 0 4px 24px rgba(236, 72, 153, .25));
}
.nv-btn--gradient:active { box-shadow: none; }

/* ── N5 (Stefan-Entscheid 2026-08-30): die Achse steht im Namen ────────
   Frueher hiessen die beiden `--danger` und `--gefahr`. Gemessen sind es
   ZWEI ROLLEN, kein Duplikat — aber beide Namen benannten die BEDEUTUNG
   («gefaehrlich»), waehrend sie sich durch die FORM unterscheiden
   (gefuellt gegen umrandet). Ein Name, der die falsche Achse benennt,
   kann die Wahl nicht leiten: man musste die CSS-Datei aufschlagen, um
   zu wissen, welchen man will. Dazu kamen zwei Sprachen in einer
   Familie, die sonst deutsch spricht (`--randlos`, `--auf-bild`,
   `--marke`, `--verlauf`).
   Jetzt: gemeinsamer Stamm = dieselbe Bedeutung, Endung = die Form.

   ROLLE `--gefahr-voll`: die Handlung ist der HAUPTWEG dieser Ansicht
   UND sie zerstoert etwas. Gefuellt, weil sie der Hauptknopf ist — nur
   eben rot statt rosé. */
.nv-btn--gefahr-voll {
  background: var(--k-red, #EF4444);
  /* Auch der rote Grund ist eine Markenflaeche im Sinne der Rolle:
     Text darauf richtet sich nach der Flaeche, nicht nach dem Thema. */
  color: var(--k-text-auf-marke);
}
.nv-btn--gefahr-voll:active { opacity: .85; }

.nv-btn--ghost {
  background: transparent;
  border: 1px solid var(--nv-glass-border, rgba(255, 255, 255, .08));
  color: var(--k-text2, rgba(26, 26, 26, .65));
}
.nv-btn--ghost:hover { background: var(--k-fill-hover, rgba(255, 255, 255, .10)); }
.nv-btn--ghost:active { background: var(--k-fill-active, rgba(26, 26, 26, .12)); }

/* ROLLE `--gefahr-rand`: die Handlung zerstoert etwas, ist aber NICHT
   der Hauptweg. Sie steht neben «Speichern» und darf nicht mit ihm um
   Aufmerksamkeit ringen — ein flaechig roter Knopf neben einem flaechig
   rosé Knopf ist zu viel Farbe fuer eine Zeile. */
.nv-btn--gefahr-rand {
  color: var(--k-red, #EF4444);
  border-color: var(--k-red, #EF4444);
}
.nv-btn--gefahr-rand:active { background: color-mix(in srgb, var(--k-red, #EF4444) 12%, transparent); }

/* ── 11c. Groessen ── */
.nv-btn--sm {
  min-height: 40px;
  padding: 9px 16px;
  font-size: var(--k-fs-body);
  font-weight: var(--k-fw-semibold, 600);
  text-transform: none;
  letter-spacing: 0;
}
/* `.nv-btn--lg` (18/36 px, hervor) stand hier bis 05.09.2026 und wurde
   nirgends benutzt (N4 K-2, repoweit null Treffer). Entfernt. */
/* `display` muss mit: die Grundform ist inline-flex, und ein Knopf ueber
   die volle Breite, der inline bleibt, steht nicht ueber die volle Breite. */
.nv-btn--block { display: flex; width: 100%; }

/* Rund heisst rund: gleiche Breite wie Hoehe, kein Innenabstand, der sie
   auseinanderzieht. `min-height` muss ausdruecklich mit — die Grundform
   setzt 52 px, und ohne diese Zeile waere der Kreis 66x52, also ein Oval. */
/* ⚠ EINE AUSFUEHRUNG FUER ALLE RUNDEN SYMBOLKNOEPFE (Stefan-Anordnung
   2026-08-30): «44x44 Bedienflaeche, sichtbarer Kreis 38 (Kanon §4),
   Hintergrund aus der Palette — reines Weiss ist keine Palettenfarbe
   und faellt weg.»

   VORHER standen zwei Ausfuehrungen nebeneinander, gemessen in der
   echten Kundenansicht:
     y= 12   44x44 sichtbar, rgba(58,36,42,.05)   .nv-btn--icon
     y= 96   38x38 sichtbar, rgb(255,255,255)     .k-module-back
   Die Geometrie des Modul-Knopfs war die richtige — 38 sichtbar mit
   einem unsichtbaren 44-Griff (§13) ist genau, was Kanon §4 verlangt.
   Die Katalogform war 44 SICHTBAR und damit zu klobig. Der weisse
   Grund fiel weg: `--k-bg2` ist im hellen Thema reines Weiss.

   Beide Namen zeigen jetzt auf DIESE Regel. `--gross` (52) bleibt fuer
   die Aktionskreise im Profilkopf. */
.nv-btn--icon,
.k-module-back,
.k-module-more {
  width: 38px;
  height: 38px;
  min-width: 38px;
  min-height: 38px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* `50%`, nicht `--nv-r-pille`: auf einem Quadrat sehen beide gleich
     aus, aber gleich aussehen ist nicht gleich sein. */
  border-radius: 50%;
  background: var(--k-fill, rgba(26, 26, 26, .04));
  border: 1px solid var(--k-border);
  color: var(--k-text, #1A1A1A);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: background .15s, transform .1s;
}
.nv-btn--icon.nv-btn--gross { width: 52px; height: 52px; min-height: 52px; }
.nv-btn--icon:active { background: var(--k-fill-active, rgba(26, 26, 26, .12)); }

/* ── 11d. Zustand: ausgewaehlt ──
   Ein Zustand, keine Variante — deshalb mit jeder Variante kombinierbar.
   Er sagt nur eines: dieser hier ist es. */
.nv-btn.is-aktiv {
  background: var(--k-brand);
  border-color: var(--k-brand);
  color:var(--k-text-auf-marke);
}
.nv-btn--ghost.is-aktiv,
.nv-btn--secondary.is-aktiv {
  background: color-mix(in srgb, var(--k-brand) 14%, transparent);
  border-color: var(--k-brand);
  color: var(--k-brand);
}

/* ── 11e. Der Fokus-Ring — fuer alles, was Fokus bekommen kann ──
   Stand in nv-consistency.css. Er setzt `border-radius` und damit Form,
   gehoert also in den Katalog. Er gilt bewusst breiter als der Knopf:
   ein Fokus-Ring, den nur ein Bauteil hat, ist eine Luecke in der
   Tastatur-Bedienung, kein Stil. */
.nv-btn:focus-visible,
.nv-input:focus-visible,
.bottom-sheet-item:focus-visible,
button:focus-visible,
[role="button"]:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  /* S4 29.09. (HIG): Marken statt Zahlen; KEIN border-radius mehr — vorher wurde jede Kapsel und Karte beim
     Fokussieren zu einem 8-px-Eck umgeformt. Der Umriss folgt heute der Form des Elements (WebKit ab 16.4). */
  outline: var(--nv-fokus-breite) solid var(--nv-fokus-farbe);
  outline-offset: var(--nv-fokus-abstand);
}

/* ── 12. GLAS — eine Familie, drei Ebenen ───────────────────────────────
   Stefan-Nachtrag 2026-08-28, Punkt 1. Der Katalog wird so gebaut, dass
   die iPhone/iPad-App spaeter nur noch VERPACKT werden muss; kein Baustein
   darf danach ein zweites Mal angefasst werden.

   BEFUND, DER DAZU GEFUEHRT HAT (gemessen 2026-08-28)
   220 `backdrop-filter`-Deklarationen in css/, darunter mindestens VIERZEHN
   verschiedene Unschaerfe/Saettigungs-Paare — 40/180, 24/180, 20/180,
   50/200, 60/200, 40/1.8, 32/180, 28/200, 28/180 und weitere. Dazu fuenf
   konkurrierende Token-Familien: --k-glass-* (265 Verwendungen),
   --nv-glass-* (28), --nv-glas* (13), --glass* (10). Niemand hat je
   entschieden, dass eine Leiste 40 px und ein Blatt 24 px Unschaerfe hat.
   Es ist gewachsen — dieselbe Krankheit wie beim Knopf, nur an anderer
   Stelle.

   DIE ABSICHT
   Apples Sprache seit iOS 26 ist durchscheinend und geschichtet. Die App
   spricht sie bereits. Was fehlte, war EINE Stelle, an der steht, wie das
   Glas aussieht. Nach diesem Abschnitt ist «das Glas wird wie in iOS 26»
   eine Aenderung an vier Zahlen, nicht an 220 Deklarationen.

   DREI EBENEN, NICHT JE ELEMENT
     Karte   liegt IM Inhalt, muss lesbar bleiben  → wenig Unschaerfe
     Leiste  liegt UEBER dem Inhalt, dauerhaft     → mittlere
     Blatt   deckt den Inhalt zu, kurzzeitig       → viel
   Wer ein neues glaesernes Element baut, waehlt eine dieser drei Ebenen.
   Er erfindet keine vierte und schreibt keine Zahl hin.

   SCHREIBWEISE: `toenung` statt `tönung` — der Bestand schreibt Umlaute
   durchgehend aus (Groesse, Praefix, Rueckweg). Ein Token mit Umlaut waere
   das einzige seiner Art und bei jedem Tippen eine Fehlerquelle.
   ────────────────────────────────────────────────────────────────────── */
:root {
  /* Die vier Schrauben. Alles Glas im System haengt daran. */

  /* Je Ebene abgeleitet — nicht neu erfunden. Wer die Familie oben
     verstellt, verstellt alle drei mit. */
}
[data-theme="dark"] {
  /* Dunkles Glas ist nicht helles Glas mit weniger Licht: der Grundton
     wechselt, die Unschaerfe bleibt. Nur die Toenung und der Rand aendern
     sich — genau dafuer sind es eigene Token. */
}

/* Die Bausteine. Ein glaesernes Element traegt EINE dieser Klassen.
   E2.2 (2026-09-01): `.nv-glas-karte` ist KEIN Glas mehr. Eine Karte ist
   Inhalt und scrollt — Glas gehoert nur der Funktionsebene (Leiste, Blatt;
   docs/DESIGN-REFERENZ-LOOK-AND-FEEL.md Pruefbedingung 1 und 6). Die Karte
   behaelt Name und Rand, bekommt aber die deckende Flaechen-Marke; ihre
   eigenen Token (karte-unschaerfe, karte-deckung) sind damit weg. */
/* ⚠ `.k-module-header` STAND HIER VOM 06.09. BIS 07.09. UND IST WIEDER
   RAUS (F-2d, Stefan 07.09.: «Der Seitenkopf zeigt eine deckende Farbe
   statt Glas»). Begruendung mit Messung in §22 beim Bauteil selbst —
   kurz: der Kopf liegt UEBER nichts, also hat die Unschaerfe nichts zu
   tun, und sichtbar bleibt allein die 72-%-Deckflaeche. Glas ist eine
   Eigenschaft der Lage, nicht des Namens. */
.nv-glas-leiste,
.nv-glas-blatt {
  background: rgba(var(--nv-glas-toenung), var(--nv-glas-deckung));
  border: 1px solid var(--nv-glas-rand);
  -webkit-backdrop-filter: blur(var(--nv-glas-unschaerfe)) saturate(var(--nv-glas-saettigung));
  backdrop-filter: blur(var(--nv-glas-unschaerfe)) saturate(var(--nv-glas-saettigung));
}
.nv-glas-karte {
  background: var(--k-surface-solid);
  border: 1px solid var(--nv-glas-rand);
}
.nv-glas-blatt {
  background: rgba(var(--nv-glas-toenung), var(--nv-glas-blatt-deckung));
  -webkit-backdrop-filter: blur(var(--nv-glas-blatt-unschaerfe)) saturate(var(--nv-glas-saettigung));
  backdrop-filter: blur(var(--nv-glas-blatt-unschaerfe)) saturate(var(--nv-glas-saettigung));
}

/* Wer Transparenz abgestellt hat, bekommt Flaeche statt Glas. Das ist eine
   Bedienungshilfe-Einstellung des Geraets (iOS «Transparenz reduzieren»),
   keine Geschmacksfrage — und in einer App faellt sie staerker auf als im
   Browser, weil das ganze Fenster davon lebt. */
@media (prefers-reduced-transparency: reduce) {
  .nv-glas-leiste, .nv-glas-blatt {
    background: rgba(var(--nv-glas-toenung), 1);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

/* ── 13. GRIFF — 44x44 fuer alles Bedienbare ────────────────────────────
   Stefan-Nachtrag 2026-08-28, Punkt 3: «Jedes bedienbare Element
   mindestens 44x44 Punkt. Keine Ausnahme.» Gemessen am selben Tag ueber
   alle Module: **1'664 Tippflaechen darunter**, kleinste Kante 13 px. Die
   zwei haeufigsten sind Katalog-Bausteine — der Zurueck-Knopf (38x38, in
   fast jedem Modul) und die Segment-Taste (39 px hoch).

   WARUM NICHT EINFACH `min-height: 44px`
   Weil das die Bausteine SICHTBAR groesser machen wuerde. Ein 38er
   Zurueck-Knopf, der ploetzlich 44 misst, verschiebt die Kopfzeile in
   achtzig Modulen — eine Aenderung am Aussehen, die niemand bestellt hat.
   Apple loest das seit je andersherum: die sichtbare Flaeche bleibt, die
   TREFFERFLAECHE waechst. Genau das macht dieser Baustein — mit einer
   unsichtbaren Auflage, die aus jedem Ziel mindestens 44x44 macht.

   Der Finger trifft also 44, das Auge sieht 38. Beides ist richtig.

   BENUTZUNG
   `.nv-griff` an das bedienbare Element. Fuer die Katalog-Bausteine ist
   es unten gleich mit angewandt, damit niemand es vergessen kann.
   ────────────────────────────────────────────────────────────────────── */
.nv-griff,
.nv-btn,
.k-mh-title-btn,
.k-module-more,
.nv-sidebar-section,
.k-module-back,
.nv-segment-opt,
.nv-kopf-knopf,
.nv-symbolfeld,
.nv-filterchip,
.kal-leiste-tag {
  position: relative;
}
.nv-griff::after,
.nv-btn::after,
.k-mh-title-btn::after,
.k-module-more::after,
.nv-sidebar-section::after,
.k-module-back::after,
.nv-segment-opt::after,
.nv-kopf-knopf::after,
.nv-symbolfeld::after,
.nv-filterchip::after,
.kal-leiste-tag::after {
  content: '';
  position: absolute;
  /* Mittig ueber dem Element, mindestens --nv-tap gross. `max()` sorgt
     dafuer, dass ein bereits grosses Element NICHT verkleinert wird. */
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: max(100%, var(--nv-tap, 44px));
  height: max(100%, var(--nv-tap, 44px));
  /* Unsichtbar, aber treffbar. Nicht `display:none` und nicht
     `visibility:hidden` — beides waere auch fuer den Finger weg. */
  background: transparent;
  pointer-events: auto;
}
/* Der Inhalt muss ueber der Auflage liegen, sonst faengt sie Klicks auf
   Elementen ab, die IM Knopf liegen (Symbol, Zaehler). */
.nv-griff > *,
.nv-btn > *,
.k-mh-title-btn > *,
.k-module-more > *,
.nv-sidebar-section > *,
.k-module-back > *,
.nv-segment-opt > *,
.nv-kopf-knopf > *,
.nv-symbolfeld > *,
.nv-filterchip > *,
.kal-leiste-tag > * { position: relative; z-index: 1; }
/* `.kal-leiste-tag` (Tages-Zelle des Kalenderstreifens) traegt den Griff
   seit H3 (2026-09-02): sieben Zellen Mo–So teilen sich die Breite, bei
   320 px sind das 33 px je Zelle — der Finger trifft trotzdem 44. */

/* Auswahlfelder und Eingaben koennen kein `::after` zeichnen — sie sind
   «ersetzte Elemente», der Browser malt sie selbst. Fuer sie gilt die
   Mindestgroesse deshalb direkt. Das ist der einzige Fall, in dem die
   44 sichtbar werden; bei allen anderen bleibt das Bild wie es war. */
select, input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="hidden"]) {
  min-height: var(--nv-tap, 44px);
}
/* Ankreuzfeld und Auswahlknopf sind oben ausgenommen, und das bleibt so:
   die App traegt 81 davon (`grep 'type="checkbox"' js/*.js`, gemessen
   2026-09-07), alle zwischen 13 und 22 px. Eine Regel fuer alle waere
   eine Aenderung am Bild in der ganzen App, die niemand bestellt hat.
   Wo eines aber wirklich mit dem Daumen bedient wird, traegt es
   ausdruecklich `.nv-griff` — und dann gilt die 44 direkt.

   WARUM DIE GROESSE UND NICHT DIE UNSICHTBARE AUFLAGE (gemessen 2026-09-07):
     · `padding` laesst der Browser an einem nativen Ankreuzfeld
       unbeachtet — 13x13 blieb 13x13, mit 15 px Innenabstand.
     · `::after` zeichnet Chromium 1194 an einem Ankreuzfeld sehr wohl:
       Auflage 44x44 gemessen, Zielfeld 9 von 9 Punkten getroffen. Es
       waere die schoenere Loesung — sie wird hier trotzdem NICHT benutzt.
       Die App laeuft auf dem iPhone und iPad in WKWebView (Capacitor,
       `capacitor.config.ts`), also in WebKit, und dafuer gibt es in
       dieser Umgebung keinen Browser zum Messen (`/opt/pw-browsers`
       kennt nur Chromium). Pseudo-Elemente an ersetzten Elementen sind
       nicht festgeschrieben; eine Loesung, die nur im Pruefbrowser
       nachweisbar ist, waere gruen gemessen und auf Stefans Telefon
       womoeglich wirkungslos (Regel #0g).
   Es bleibt die Groesse selbst — sie wirkt in jeder Engine. Der Preis
   steht im Bild: das Kaestchen wird sichtbar groesser, wie eine Regel
   weiter oben bei den Auswahlfeldern. */
input[type="checkbox"].nv-griff,
input[type="radio"].nv-griff {
  width: var(--nv-tap, 44px);
  height: var(--nv-tap, 44px);
}

/* ── 14. SYMBOLSCHRIFT — lokal, damit die App ohne Netz startet ─────────
   Stefan-Entscheid 2026-08-28, Punkt 8.

   VORHER: die Schrift kam bei jedem Start von fonts.googleapis.com —
   **5'233 KB**. In einer App, die offline startet, ist das kein
   Schoenheitsfehler mehr: ohne Schrift zeichnet der Browser den
   Ligatur-NAMEN als Wort. Statt eines Zahnrads steht dann «settings» in
   der Kopfzeile, bei `workspace_premium` sind es 403 px Text.

   JETZT: `fonts/nevanu-symbole.woff2`, **128 KB** — nur die 488 wirklich
   benutzten Zeichennummern (508 Namen, weil 20 Zweitnamen auf dieselbe
   Nummer zeigen; Stand 2026-10-12, C7: dazu die Namen aus den Daten von
   api/ — Kategorie- und Baustein-Symbole, siehe tools/symbol-daten-quellen.json).
   Angesprochen werden sie ueber ihre Zeichennummer (`&#xe8b8;`), nicht
   ueber den Namen: der Name braucht die Ligatur-Tabelle, und die verweist
   auf alle ~3'300 Symbole. Genau deshalb half ein Teilsatz nach Namen
   nichts (gemessen: 4'990 statt 5'233 KB).

   ⚠ DIESE ZWEI ZAHLEN SIND EIN MESSGERAET (Regel #0j). Bis zum 08.09.2026
   stand hier «75 KB — 286 Symbole». Das stimmte am 28.08. und war seither
   falsch: die Schrift waechst mit jedem neuen Sinnbild. Wer sie liest,
   liest sonst einen alten Stand. **Nachfahren mit
   `python3 tools/symbolschrift-glyphen.py`** — das Werkzeug prueft genau
   diesen Satz gegen die Datei und meldet die Abweichung.

   DIE ACHSEN BLEIBEN BEWEGLICH. Zwei starre Schnitte waeren mit 46 KB
   kleiner gewesen, haetten aber `font-variation-settings` wirkungslos
   gemacht — 32 Regeln in sechs Dateien haengen daran (gefuellte Variante,
   vier Strichstaerken). Alles waere still ungefuellt gewesen, ohne
   Fehlermeldung. 29 KB sind der Preis dafuer, dass keine CSS-Regel
   angefasst werden muss.

   EIN SYMBOL HINZUFUEGEN: Namen im Code verwenden, dann
   `python3 tools/symbol-karte.py <original.woff2>` — Karte und Schrift
   werden ERZEUGT, nicht von Hand gepflegt (Stefans Bedingung). Der Name
   bleibt als `data-symbol` im Markup lesbar.
   Waechter: `tools/symbol-wache.cjs`.
   ────────────────────────────────────────────────────────────────────── */
/* Die Marken-Schrift, ebenfalls lokal. Sie faellt weicher aus als die
   Symbolschrift — ohne sie zeichnet der Browser eine System-Schrift, also
   lesbaren Text statt Kauderwelsch. Trotzdem gehoert sie mit: eine App,
   die beim ersten Start ohne Netz in Helvetica erscheint, sieht nicht aus
   wie das Produkt. 14 KB, variabel ueber alle benutzten Strichstaerken. */
@font-face {
  font-family: 'Outfit';
  font-style: normal;
  font-weight: 300 900;
  font-display: swap;   /* swap, nicht block: lesbarer Text sofort ist
                           besser als gar keiner — anders als bei Symbolen */
  src: url('../fonts/Outfit-var-latin.woff2') format('woff2');
}

@font-face {
  font-family: 'Material Symbols Rounded';
  font-style: normal;
  font-weight: 100 700;
  font-display: block;   /* block, nicht swap: ein kurz sichtbarer
                            Ersatz-Buchstabe waere schlimmer als nichts */
  /* ⚠ DIE KENNUNG HINTER DEM FRAGEZEICHEN IST PFLICHT (Befund 2026-08-30).
     Sie ist die ersten 8 Zeichen des SHA-256 der Schrift und wird von
     `tools/symbol-wache.cjs` gegen die Datei geprueft — stimmt sie nicht,
     faellt die CI rot.

     DER FALL. Im Kundenprofil war der mittlere Aktionskreis leer:
     `calendar_add_on`. Die Schrift im Repo hatte die Glyphe, die Karte
     hatte sie, die Auslieferung meldete «gleich» — und auf dem Geraet
     war sie trotzdem nicht da. Grund: `.htaccess` liefert `woff2` mit
     `max-age=2592000, immutable` aus. **`immutable` heisst: frag nie
     wieder nach, auch nicht beim Neuladen.** Der Dateiname aendert sich
     nie, also behielt das Geraet 30 Tage lang die alte Schrift.
     Gemessen: die Fassung vor dem Symbol-Hotfix hatte **279 Glyphen**,
     die heutige **468**. `point_of_sale` und `visibility` waren in
     beiden — `calendar_add_on` nur in der neuen. Genau die drei Kreise
     aus Stefans Bild.

     WARUM DIE ANNAHME FALSCH WAR. Ueber der Datei steht «Fonts aendern
     sich nie». Das stimmt fuer Fraunces und Instrument Sans. Es stimmt
     NICHT fuer einen Teilsatz, der bei jedem neuen Sinnbild waechst.
     Eine Regel, die fuer die Mehrheit stimmt, ist keine Regel. */
  src: url('../fonts/nevanu-symbole.woff2?v=f197c206') format('woff2');
}
/* KEINE eigene Klassen-Regel hier. `.material-symbols-rounded` ist
   bereits in css/style.css:494 definiert (Schriftfamilie und
   Achsen-Werte) und greift ab jetzt auf die lokale Datei oben zu — der
   Name der Familie ist derselbe.

   Der erste Versuch hat die Klasse hier nochmal geschrieben, samt
   `font-size: 1.5rem`. Das war zweierlei falsch: eine zweite Ausfuehrung
   desselben Bausteins (genau das, was der Katalog beendet), und eine
   Groesse, die es vorher NICHT gab — die Symbole erben ihre Groesse vom
   Umfeld. Die Skalen-Ratsche hat es gemeldet: auf `home` waeren aus 23
   verschiedenen Schriftgroessen 24 geworden. */

/* ── 15. SOCKEL AM BLATT — der Teil, den niemand hatte ──────────────────
   Stefan-Nachtrag 2026-08-28, Punkt 2: die Safe Areas gehoeren in die
   Bausteine Kopf, Tab-Leiste, Blatt und Panel.

   Kopf und Tab-Leiste hatten sie (ueber die Token, nach der Umleitung der
   43 direkten `env()`-Stellen). Das BLATT hatte sie nicht — gemessen
   0 von 67: kein Blatt und kein Panel reagierte auf den Sockel.

   Im Browser faellt das nicht auf, weil dort alle vier Werte 0 sind. In
   einer App mit Home-Indikator sitzt der unterste Inhalt eines Blattes
   darunter — der letzte Knopf einer Liste ist dann halb verdeckt und
   schlecht zu treffen. Genau das ist der Fehler, den man erst auf dem
   Geraet sieht und der dann teuer wird.

   Warum `padding` und nicht `margin`: das Blatt scrollt. Ein Aussenabstand
   wuerde die Flaeche verkuerzen; ein Innenabstand laesst den Inhalt
   vollstaendig hochscrollen und haelt nur das Ende frei.
   ────────────────────────────────────────────────────────────────────── */
/* Der Blatt-Inhalt selbst — 2026-08-29 aus `style.css` geholt (A1).
   Dort stand die Gestalt, hier stand nur ihr Sockel: die Datei, die den
   Baustein beschreibt, war eine andere als die, die ihn korrigiert.
   ⚠ Die Reihenfolge in DIESER Datei traegt die Bedeutung: die
   Sockel-Regel darunter ueberschreibt `padding-bottom` absichtlich.
   Wer diesen Block dahinter schiebt, nimmt dem Blatt die Kerbe. */
.nv-panel-body {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: var(--k-sp-md);
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-y: contain;
  scroll-behavior: smooth;
}
.nv-panel-body::-webkit-scrollbar { display: none; }
/* E2.3a (2026-09-01): Ohne Panel-Kopf (`noHeader:true` — Reviews, Chat,
   Berichte, Dokumente, Reglement, MediaHub, QualityScore) ist der Inhalt
   das erste Kind und stand mit der Kerbe unter der Uhrzeit — gemessen an
   `#rv-panel`: Modulkopf bei y = 16 px, Kerbe reicht bis 59 px. Der Kopf
   mit Sockel (Z. 1478) fehlt hier, also traegt der Inhalt den Sockel. */
.nv-panel > .nv-panel-body:first-child {
  /* F1 (2026-09-02): derselbe Zusatz wie am Koerper (`--nv-luft-0`), nicht
     `--k-sp-md` — sonst stand der Panel-Titel 10 px tiefer als der Modul-
     Titel im Fluss. */
  padding-top: calc(var(--nv-luft-0) + var(--nv-sockel-oben));
}

/* ── 32b. MERKZEICHEN AM PROFILKOPF — k-profile-tags, k-profile-tag ────
   Stefan-Befund 2026-08-30 (A1): «die beiden Merkzeichen unter dem Namen
   beruehren oder ueberlappen sich».

   GEMESSEN, was wirklich war: «● aktiv» 35,8 px breit, «3 Termine»
   52,9 px, Luecke dazwischen genau 6,0 px — und **Innenpolsterung
   0 px**. Die Pillen waren textbreit. Zwei randlose Pillen mit runden
   Enden und 6 px Abstand lesen sich als ein zusammenhaengender Kasten.

   DIE URSACHE IST NICHT DER ABSTAND, SONDERN EIN STILLER AUSSCHALTER.
   Die Regel stand zweimal in `keeva-ui.css` (Zeile 875 und 5318), beide
   Male mit `padding: 6px 16px`. Beide sind wirkungslos, weil sie in
   `@layer components` bzw. `@layer overrides` liegen — und
   `css/style.css:332` setzt UNGELAYERT `*,*::before,*::after{padding:0}`.
   **Regeln ohne Layer schlagen JEDE Layer-Regel, unabhaengig von der
   Spezifitaet.** Der Universal-Reset (0,0,0) gewann gegen eine
   Klassenregel (0,1,0). Nachgewiesen mit `CSS.getMatchedStylesForNode`:
   alle drei Regeln greifen, berechnet werden 0 px.

   ⚠ DIESE FALLE IST IM HAUS BEKANNT — siehe den Block «LUFT ZWISCHEN
   KOPFZEILE UND INHALT» weiter oben, wo derselbe Reset das `margin`
   frass. Damals wurde EINE Regel gerettet. Niemand hat gemessen, was
   der Reset sonst noch frisst. Das gehoert auf die Parkliste.

   DIE WERTE SIND NICHT NEU ERFUNDEN: 6 px ist `--nv-luft-0`, 16 px ist
   `--nv-s4` — beide auf der Skala, beide standen schon im Entwurf. Sie
   greifen hier nur zum ersten Mal.

   VERWANDT js/kunden.js (kundenStatusBadge), css/style.css:332 */
.k-profile-tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--nv-luft-0);
  margin-top: var(--nv-luft-1);
}
/* ⚠ ERWEITERT 2026-08-31 (Paket D1, Etappe 1 — Zusammenfuehrung des
   HR-Profilkopfs). Das Merkzeichen trug bis heute nur Text
   (js/kunden.js «3 Termine», js/team-manager.js Skill-Namen). Im
   HR-Mitarbeiterdetail steht ein SYMBOL davor — Brief, Hoerer, Nadel —
   und dafuer fehlten dem Baustein vier Angaben.

   Sie stehen jetzt AM Baustein, nicht daneben (Katalog-Pflicht: passt
   einer zu 90 %, wird der Rest an ihm ergaenzt). Fuer die reinen
   Text-Merkzeichen aendert sich dadurch nichts: ein `inline-flex` mit
   einem einzigen Textknoten misst so breit wie ein `inline-block`, und
   `gap` greift erst ab zwei Kindern.

   Die Werte kommen aus der Skala, keiner ist hart:
     gap        --nv-s1  (4 px)   Symbol zu Text, die engste Stufe
     max-width  100 %             das Merkzeichen sprengt nie seine Zeile

   DER SYMBOLTON IST MITGENOMMEN, nicht neu erfunden: die geloeschte
   Fassung `#hr-ma-detail .hr-tag .material-symbols-outlined` faerbte das
   Symbol in der Marke. Ohne diese Zeile waere das Symbol beim
   Zusammenfuehren still grau geworden — eine Farbaenderung, die niemand
   beschlossen hat. Sie gehoert damit zum Bauteil, nicht zum Modul.

   VERWANDT js/team-manager.js (_hrRenderMitarbeiter), js/kunden.js,
            css/ios-grouped.css §21 (die geloeschte zweite Ausfuehrung) */
.k-profile-tag {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-s1);
  max-width: 100%;
  padding: var(--nv-luft-0) var(--nv-s4);
  border-radius: var(--nv-r-kapsel, 9999px);
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-semibold);
  background: var(--k-glass-bg);
  color: var(--k-text2);
  border: 1px solid var(--k-glass-border);
}
.k-profile-tag .material-symbols-outlined,
.k-profile-tag .material-symbols-rounded { color: var(--k-brand); }

/* ── 32c. DIE BUEHNE IM PROFIL-UEBERLAGERER — kp-container ─────────────
   Stefan-Befund 2026-08-30: «links und rechts haben wir keine abstaende
   mehr da wo die kommunikation, aktion, terminanzahl drin ist.»

   GEMESSEN bei 390 px, Kundenprofil: alle drei Karten standen auf
   **6 px** zur Fensterkante, 378 px breit. 6 px ist die Schatten-
   Reserve der Karte (`--nv-schatten-reserve`), nicht der Seitenrand. Der
   Seitenrand ist `--nv-rand` (18 / 28 / 36) und kommt von der BUEHNE.

   `.kp-container` war nie eine Buehne: gemessen `padding: 0px/0px`.
   Die einzige Fassung, die dort ueberhaupt ein Polster setzt, steht in
   `keeva-ui.css:5292` in `@layer overrides` — und ist wirkungslos
   (Layer-Falle, §32b). Der Ueberlagerer haengt ausserdem an `body`,
   nicht in `#dash-content`, deshalb hat `tools/rand-probe.cjs` ihn nie
   gesehen: die Probe misst die Kinder der Buehne, und hier gab es keine.

   ⚠ MEIN FEHLER, nicht nur ein Altbestand. Die Zahlen-Karte trug bis zum
   30.08. `margin: 0 24px !important` im Modul und stand damit als
   einzige richtig. Beim Zusammenfuehren (A2) habe ich sie auf die beiden
   anderen heruntergezogen, statt zu fragen, ob 6 px ueberhaupt stimmt.
   Die 378 px standen in meiner Quittung — ich habe sie als Sollwert
   gelesen statt als Befund.

   DIE RECHNUNG: die Buehne gibt `--nv-rand` MINUS der Schatten-Reserve,
   die die Karte selbst mitbringt. 12 + 6 = 18. Genau dieselbe Rechnung
   steht in §6 bei `.nv-flaeche`, nur andersherum. Kein neuer Wert.

   VERWANDT js/kunden.js, js/kollegen-admin.js (beide bauen den
   Behaelter), css/system.css §6 (.nv-flaeche), tools/rand-probe.cjs */
.kp-container { padding-inline: calc(var(--nv-rand) - var(--nv-schatten-reserve)); }

/* ── 32e. DIE AKTIONSREIHE IM PROFIL — kp-actions ──────────────────────
   Die Reihe runder Schnellzugriffe unter den Kennzahlen (Leistungen,
   Arbeitszeiten, Zugangsdaten, PIN …), im Kundenprofil wie im
   Mitarbeiterprofil.

   ⚠ SIE WAR DREIFACH AUSGEFUEHRT (gemessen 2026-08-31, D1 Team/HR):
     css/style.css:4564       gap 12, zentriert, margin-top 16  — kam NIE an
     css/style.css:4628       body.k-ui …, gap 16, margin 0 24  — nur margin kam an
     css/ios-grouped.css:413  body.k-ui …, gap 6, padding 4 0   — der Rest kam an
   Dazu ein vierter Uebersteuerer `#hr-ma-detail .hr-actions` mit acht
   `!important`, der im Mitarbeiterprofil alles davon schlug. Vier
   Fassungen fuer **einen einzigen Aufrufer** — die Klasse steht genau an
   einer Stelle im ganzen Code.

   ⚠ UND DIE ZUSAMMENFUEHRUNG GING BEIM ERSTEN MAL IN DIE FALSCHE
   RICHTUNG. Nach dem Loeschen des Uebersteuerers galt `gap: 6px` aus
   ios-grouped — und die Beschriftungen unter den Kreisen klebten auf
   390 px aneinander («Leistungen Arbeitszeiten Zugangsdaten» ohne Luft
   dazwischen). Der geloeschte Uebersteuerer hatte 18 px, und er hatte
   recht: **der Ausreisser ist der erste Kandidat fuer die einzige
   Fassung, die stimmte.** Uebernommen mit `--nv-luft-2` (16 px, die
   naechstliegende Stufe; 2 px sieht niemand, eine neue Stufe sieht
   jede spaetere Sitzung).

   Der untere Innenabstand traegt denselben Grund: mit 4 px sass die
   Reiterreihe auf grossen Schirmen fast auf den Beschriftungen.

   KEIN `!important`. Es brauchte eines, solange drei Fassungen um
   dieselbe Angabe stritten. Es gibt jetzt nur noch diese. */
.kp-actions {
  display: flex;
  flex-wrap: nowrap;
  justify-content: space-evenly;
  gap: var(--nv-luft-2);
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  margin-inline: var(--nv-s5);
  padding: var(--nv-s1) 0 var(--nv-s3);
}
.kp-actions::-webkit-scrollbar { display: none; }

/* ── 32d. ZWEI ABSTAENDE, DIE SEIT MONATEN NICHT ANKAMEN ───────────────
   Stefan-Auftrag N22 / S1, 2026-08-30. Aus der Messung
   `tools/layer-falle.cjs`: von 528 gelayerten Abstands-Angaben ohne
   `!important` konnten 64 an lebenden Elementen beurteilt werden, davon
   sind 8 nachweislich verschluckt. Fuenf davon sind 1-2 px und damit
   unsichtbar (sie stehen auf der Parkliste, N22). Diese zwei hier sind
   die sichtbaren.

   (1) `.k-spaces-section` — Seitenpolster 20 px, berechnet 0 px.
       Die Abschnitte der Bereichsliste standen auf der Fensterkante.
   (2) `.nv-searchbar + …` — Luft von 16 px zwischen Suchfeld und dem,
       was darunter kommt; berechnet 0 px. Das Suchfeld klebte am Inhalt.

   Beide standen in `keeva-ui.css` in `@layer components`. Der
   ungelayerte Universal-Reset in `css/style.css:332` schlaegt jede
   Layer-Regel, unabhaengig von der Spezifitaet — dieselbe Falle wie beim
   Merkzeichen (§32b). Die alten Fassungen sind dort entfernt.

   WERTE UNVERAENDERT (Stefan: «20 px und 16 px sind auf der Skala»).
   16 px ist `--nv-s4`; der 20er behaelt seinen bestehenden Namen
   `--nv-search-to-content` bzw. bleibt als Zahl stehen, wo es keinen
   gab — kein neuer Token, kein neuer Praefix (CSS-Freeze).

   VERWANDT css/keeva-ui.css (die entfernten Fassungen),
   tools/layer-falle.cjs, tools/layer-falle-wache.cjs, docs/PARKLISTE.md N22 */
.k-spaces-section { padding: 0 20px; margin-top: var(--nv-s4); }
.nv-suchfeld + div,
.nv-suchfeld + .nv-stat-grid,
.nv-suchfeld + .nv-chip-group { margin-top: var(--nv-search-to-content, 16px); }

/* ── 33. BLATT — nv-blatt ──────────────────────────────────────────────
   EIN Bauteil fuer ueberlagernde Karten. Stefan-Auftrag E1, 2026-08-30.

   WAS VORHER DA WAR, GEMESSEN (tools/blatt-probe.cjs, Selbsttest gruen):
   **37 eigenstaendige ueberlagernde Flaechen** in 15 Dateien — davon
   sechs Bottom-Blaetter, zehn ganzseitige, fuenf mittige Dialoge, vier
   Rand-Blaetter, fuenf Menue-Verdunkler, vier Sperren. Dazu **375
   z-index-Vorkommen**, 30 verschiedene rohe Zahlen.

   DIE EBENEN SIND NICHT DAS PROBLEM — DAS IST SCHON GELOEST.
   `css/ebenen.css` (seit 2026-08-03) haelt 20 benannte Ebenen, und
   `tools/ebenen-pruefen.sh` faellt in der CI, sobald eine rohe Zahl
   ueber 400 auftaucht. Dieses Bauteil erfindet deshalb KEINE neuen
   z-Merkmale, sondern liest die vorhandenen. Es gibt bereits drei
   konkurrierende Ebenen-Alphabete (`--z-*` in ebenen.css, `--nv-z-*`
   in design-tokens.css, das verwaiste `--z-sheet: 10000`); ein viertes
   waere genau der Fehler, den dieses Bauteil beheben soll.

   RANDLOS (Stefan-Vorgabe): das Blatt selbst hat KEINE Polsterung.
   Der Inhalt geht bis an die Kante — ein Bild, eine Liste, eine Karte
   duerfen die volle Breite nehmen. Wer Luft braucht, setzt sie am
   Inhalt (`.nv-blatt-inhalt`), nicht am Blatt. Vorher trug das Blatt
   die Polsterung, und jede randlose Ansicht musste sie einzeln wieder
   abziehen — daher `.nv-panel-body--randlos` (§25).

   AUSFUEHRUNGEN
     --halb        von unten, hoechstens 92 % der Hoehe (Bottom-Blatt)
     --voll        die ganze Flaeche, ersetzt die Seite
     --gestapelt   ein Blatt UEBER einem Blatt (`--z-blatt-hoch`)
     --detail      die Detailspalte am breiten Schirm (E2, siehe unten)

   BILDSCHIRMSTUFE AM BAUTEIL (Kanon §5a): ab 1024 px ist `--halb` kein
   Streifen ueber die volle Breite mehr, sondern ein Fenster in der
   Mitte. Eine Wischgeste gibt es mit der Maus nicht, deshalb faellt
   der Griff weg.

   ⚠ WAS HIER (NOCH) NICHT DRIN IST — gemessen, nicht vergessen:
   `.k-bsheet` wird nicht aus einer Stilvorlage bedient, sondern aus
   einem `<style>`-Block, den `js/bottom-sheet.js:80` zur Laufzeit
   einsetzt. Die Probe sieht davon nichts (sie misst `position: static`,
   also die ungestylte Grundform). Es blind mit einzuziehen hiesse
   raten. Ebenso offen: `.bottom-sheet` (21 Regeln, eng verwoben mit
   Griff, Titel und Eintraegen) sowie die zehn ganzseitigen und fuenf
   mittigen Ausfuehrungen. Zahlen und Dateien stehen in der Quittung
   vom 2026-08-30.

   VERWANDT css/ebenen.css, tools/blatt-probe.cjs, js/nv-panel.js,
   docs/DESIGN-KANON.md §5a */

/* Die Grundform. `.k-sheet` und `.nv-sheet` sind KEINE zweiten
   Bauteile, sondern die Namen, unter denen dieses Blatt heute im
   ausgelieferten Code steht (33 Schreibstellen). Sie stehen im selben
   Selektor, damit es EINE Regel gibt statt dreier — nicht als Kopie
   daneben. */
.nv-blatt,
.k-sheet,
.nv-sheet {
  position: fixed;
  z-index: var(--z-blatt);
  background: var(--k-glass-bg-strong);
  backdrop-filter: var(--k-glass-blur-heavy);
  -webkit-backdrop-filter: var(--k-glass-blur-heavy);
  transition: transform var(--k-dur) var(--k-ease);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  /* randlos: die Polsterung gehoert dem Inhalt */
  padding: 0;
}

/* --halb: von unten. Der Regelfall am Telefon. */
.nv-blatt--halb,
.k-sheet,
.nv-sheet {
  bottom: 0; left: 0; right: 0;
  max-height: 92dvh;
  border-radius: var(--k-r-xl) var(--k-r-xl) 0 0;
  border-top: 1px solid var(--k-glass-border);
  transform: translateY(100%);
}
.nv-blatt--halb.open, .nv-blatt--halb.active,
.k-sheet.open,
.nv-sheet.active { transform: translateY(0); }

/* --voll: die ganze Flaeche. Kein Radius, kein Rand — es IST die Seite. */
.nv-blatt--voll {
  inset: 0;
  max-height: none;
  border-radius: 0;
  border: 0;
  transform: translateY(100%);
}
.nv-blatt--voll.open, .nv-blatt--voll.active { transform: translateY(0); }

/* --gestapelt: ein Blatt ueber einem Blatt. Nur die Ebene aendert sich —
   die Gestalt bleibt, sonst waere es ein anderes Bauteil. */
.nv-blatt--gestapelt { z-index: var(--z-blatt-hoch); }

/* Der Griff. Er verspricht eine Wischgeste. */
.nv-blatt-griff,
.k-sheet-handle,
.nv-sheet-handle {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: var(--k-text4);
  margin: var(--k-sp-sm) auto var(--k-sp-md);
}

/* Der Inhalt traegt die Luft, nicht das Blatt. */
.nv-blatt-inhalt {
  padding: var(--k-sp-md);
  padding-bottom: var(--nv-sockel-unten);
}

/* ── DAS BLATT AM SCHREIBTISCH — Kanon §5a ──
   Diese Regeln standen bis 2026-08-29 in `desktop-layout.css` — also in
   einer Datei, die NACH system.css laedt. Fuer `.k-sheet` hiess das:
   der Katalog beschrieb das Bauteil, aber nur bis 1023 px.
   Ab 1024 px ist es kein Blatt mehr, das hereinfaehrt, sondern ein
   Fenster in der Mitte: eine Wischgeste gibt es mit der Maus nicht,
   und ein Streifen ueber die volle Breite ist auf einem breiten Schirm
   kein Dialog, sondern ein Balken. */
@media (min-width: 1024px) {
  .nv-blatt--halb,
  .k-sheet,
  .nv-sheet {
    top: 50%;
    bottom: auto;
    left: 50%;
    right: auto;
    width: min(680px, 76vw);
    max-width: none;
    max-height: 84vh;
    margin-inline: 0;
    border-radius: 22px;
    padding-bottom: 12px;
    box-shadow: 0 24px 80px rgba(0, 0, 0, .22);
    /* Leicht kleiner und ein Stueck hoeher starten: das liest sich als
       «kommt nach vorn», nicht als «faehrt von unten herein». */
    transform: translate(-50%, -47%) scale(.97);
    opacity: 0;
    transition: transform .26s var(--k-ease, cubic-bezier(.4, 0, .2, 1)),
                opacity .26s var(--k-ease, cubic-bezier(.4, 0, .2, 1));
  }
  .nv-blatt--halb.open, .nv-blatt--halb.active,
  .k-sheet.open,
  .nv-sheet.active {
    transform: translate(-50%, -50%) scale(1);
    opacity: 1;
  }
  /* Der Griff verspricht eine Wischgeste, die es mit der Maus nicht gibt. */
  .nv-blatt-griff, .k-sheet-handle, .nv-sheet-handle { display: none; }
  /* Mittig ueber dem INHALT, nicht ueber dem Fenster: die Seitenleiste
     nimmt links Platz weg, und ein Dialog, der sie ignoriert, steht
     sichtbar aus der Mitte. Der Versatz ist ihre halbe Breite. */
  body.has-sidebar .nv-blatt--halb,
  body.has-sidebar .k-sheet,
  body.has-sidebar .nv-sheet { left: calc(50% + 48px); right: auto; width: min(680px, 76vw); }
  body.has-sidebar.sidebar-expanded .nv-blatt--halb,
  body.has-sidebar.sidebar-expanded .k-sheet,
  body.has-sidebar.sidebar-expanded .nv-sheet { left: calc(50% + 142px); }
}

/* ── 33b. DIE DETAILSPALTE — nv-blatt--detail (E2, Stefan 2026-08-30) ──
   «Ab 1024px Breite dockt das Detail rechts an, 380px, ab 1280px 420px.
   Vorbild Apple Erinnerungen: Liste links bleibt bedienbar, Detail
   rechts, kein Popup, keine Abdunklung. Unter 1024px bleibt es das
   Blatt aus E1 — dasselbe Bauteil, andere Ausfuehrung.»

   DESHALB STEHT ES HIER UND NICHT IN EINER EIGENEN REGEL: unter
   1024 px IST `--detail` ein `--halb`. Erst ab 1024 px dockt es an.
   Ein zweites Bauteil haette zwei Zustaende zu pflegen, die dasselbe
   meinen.

   KEINE ABDUNKLUNG, KEIN ABFANGEN: die Liste links bleibt bedienbar.
   Deshalb bekommt die Spalte KEINEN Verdunkler und liegt auf
   `--z-seite`, nicht auf `--z-blatt` — sie ueberlagert die Seite
   nicht, sie steht neben ihr. */
/* Die Breite steht EINMAL — als Merkmal, nicht als Zahl an zwei
   Stellen. Das Blatt liest sie fuer seine Breite, der Seiteninhalt
   fuer den Platz, den er freilaesst. Waeren es zwei Zahlen, koennten
   sie auseinanderlaufen, und die Spalte staende halb ueber der Liste. */
:root { --nv-detail-breite: 380px; }
@media (min-width: 1280px) { :root { --nv-detail-breite: 420px; } }

.nv-blatt--detail {
  bottom: 0; left: 0; right: 0;
  max-height: 92dvh;
  border-radius: var(--k-r-xl) var(--k-r-xl) 0 0;
  border-top: 1px solid var(--k-glass-border);
  transform: translateY(100%);
}
.nv-blatt--detail.open, .nv-blatt--detail.active { transform: translateY(0); }

@media (min-width: 1024px) {
  .nv-blatt--detail {
    top: 0; bottom: 0; right: 0;
    left: auto;
    width: var(--nv-detail-breite);
    max-height: none;
    max-width: none;
    border-radius: 0;
    border-top: 0;
    border-left: 1px solid var(--k-glass-border);
    box-shadow: -8px 0 32px rgba(0, 0, 0, .10);
    /* Neben der Seite, nicht darueber. */
    z-index: var(--z-seite);
    transform: translateX(100%);
    opacity: 1;
    transition: transform .26s var(--k-ease, cubic-bezier(.4, 0, .2, 1));
  }
  .nv-blatt--detail.open, .nv-blatt--detail.active { transform: translateX(0); }
}


/* ⚠ DIE SPALTE MUSS PLATZ BEKOMMEN, SONST DECKT SIE DIE LISTE AB.
   Im ersten Bau stand die Spalte richtig — und die Liste lief darunter
   weiter. Das sieht man erst im Bild, nicht in den Zahlen: die
   Geometrie der Spalte war fehlerfrei, das Versprechen «die Liste
   links bleibt bedienbar» trotzdem gebrochen.
   Deshalb gehoert zum Bauteil eine Marke am `body`: solange eine
   Detailspalte offen ist, endet der Seiteninhalt vor ihr. Die Breiten
   stehen hier ein zweites Mal — das ist Absicht: eine gerechnete
   Breite (`calc`) haette den Wert an zwei Stellen verknuepft, ohne
   dass man am `body` saehe, worum es geht.
   Gesetzt wird die Marke von der Aufrufstelle, die die Spalte oeffnet.
   ⚠ Sie heisst `nv-hat-detail`, nicht `hat-detailspalte`: ein neues
   Klassen-Praefix ist im CSS-Freeze ausgeschlossen, und die Praefix-
   Wache hat den ersten Entwurf zu Recht rot gemeldet. */
@media (min-width: 1024px) {
  body.nv-hat-detail { padding-right: var(--nv-detail-breite); }
}

.nv-panel-body,
.bottom-sheet,
.k-sheet {
  padding-bottom: var(--nv-sockel-unten);
}
/* Hier standen kurz `.nv-panel-fuss` und `.bottom-sheet-fuss` — ein
   Blatt-Fuss mit eigenem Sockel. Beide Klassen kommen im Markup NICHT
   vor: ich habe sie auf Vorrat erfunden. Genau davor warnt der
   CSS-Freeze, und zwei Ratschen haben es gemeldet (Praefix-Wache: neue
   nv-Klasse; Schicht-Wache: ein neuer Polster-Wert). Entfernt.
   Wenn ein Blatt spaeter eine Knopfleiste am unteren Rand bekommt,
   bekommt sie die Regel — dann, nicht vorher. */

/* ── 16. KARTEN-INHALT — die Polsterung gehoert dem Inhalt ──────────────
   Stefan-Entscheid B Teil 2, 2026-08-28.

   DIE KARTE HAT KEINE POLSTERUNG, DER INHALT HAT SIE.
   `k-glass-card` bleibt bei 0 in beiden Themen. Vorbild ist die
   inset-grouped list von Apple: die Karte ist der Behaelter, nicht der
   Rahmen um einen Text. Ein Polsterwert auf der Karte wuerde ohnehin an
   344 der 583 Vorkommen inline ueberschrieben — gemessen 2026-08-28.

   DREI FAELLE, mehr gibt es nicht (Stefan-Anweisung 2026-08-29):
     .nv-liste          Karte traegt Zeilen. Die Zeilen bringen ihre
                        Polsterung selbst mit, die Karte bleibt 0.
     .nv-karte-inhalt   Karte traegt freien Inhalt (Text, Werte,
                        Steuerelemente). DER NORMALFALL.
     .nv-karte--randlos Karte traegt eine Flaeche, die bis an den Rand
                        muss — Bild, Diagramm, Farbflaeche. Polsterung 0,
                        aber ABSICHTLICH UND BENANNT.

   WARUM DIE DRITTE EINE BENANNTE AUSNAHME IST, kein stiller Sonderfall.
   Eine Karte ohne Fuellung sieht im Quelltext genauso aus wie eine
   vergessene. Gemessen am 2026-08-29: 42 Karten trugen gar keine
   Polsterung, davon waren 3 mit Absicht randlos (die Foto-Felder im
   Protokoll: `object-fit:cover` fuellt die Karte bis an die Kante) und
   38 schlicht vergessen. Ohne einen Namen fuer die Absicht laesst sich
   das eine vom anderen nicht unterscheiden — und die naechste Messung
   meldet die drei erneut als Fehler.

   Wer eine vierte Fuellung braucht, hat kein neues Bauteil, sondern
   einen dieser drei Faelle noch nicht erkannt. */
.nv-karte-inhalt {
  padding: var(--nv-karte-innen);
}
.nv-karte--randlos {
  padding: 0;
  overflow: hidden;
}

/* ── 16a. DER INHALT IST EINE MELDUNG — ein ZUSTAND, kein Bauteil ──────
   N2, 2026-09-02.

   ROLLE: der Text IST die Aussage der Karte — «keine Rechnungen
   gefunden», «Suche fehlgeschlagen», ein Hinweis. Es steht nichts
   daneben, um das herum er sich anordnen muesste; deshalb mittig und
   gedaempft. `.ist-fehler` faerbt ihn in die Fehlerfarbe: eine Meldung,
   die man uebersieht, ist keine.

   DER BEFUND. Gemessen am 02.09.2026 ueber js/**.js und die PHP-Seiten
   im Wurzelverzeichnis: 40 Karten trugen genau diese zwei Angaben als
   Inline-Stil (`text-align:center` + `color`), in 15 Dateien, mit vier
   verschiedenen Schriftgroessen (.82rem, .85rem, 14px, --k-fs-body) und
   zwei Farbwegen (`var(--error)` und `var(--text3)`, beide zeigen auf
   `--k-red` bzw. `--k-text3`). Der Inline-Stil schlaegt jede Regel aus
   jeder Stilvorlage — an diesen 40 Stellen bestimmte also nicht der
   Katalog, wie die Karte aussieht, sondern die Aufrufstelle.

   WARUM ZUSTAND UND NICHT VARIANTE. `ist-*` ist in diesem Haus der
   Schalter an einem bestehenden Bauteil (`ist-gewaehlt`, `ist-gesperrt`,
   `laedt`). Eine Karte, die gerade eine Meldung traegt, ist derselbe
   Baustein in einem anderen Zustand — kein zweiter Baustein. Nebenbei
   zaehlen `ist-*`-Klassen deshalb in keiner Ratsche als Neuzugang, was
   richtig ist: es kommt keiner dazu.

   KEINE SCHRIFTGROESSE HIER. Gemessen im laufenden Dokument: die von
   der Karte geerbte Groesse ist 14.08 px und damit exakt `--k-fs-body`.
   Die vier Inline-Werte lagen hoechstens 1 px daneben. Eine Angabe, die
   nichts aendert, gehoert nicht in den Katalog.

   WAS DAS NICHT IST: der leere Zustand mit Symbol, Titel und Aktion —
   der heisst `.nv-zustand` (§19) und bringt seinen eigenen Behaelter
   mit. Die 40 Stellen hier sind einzeilige Meldungen IN einer Karte;
   sie auf `.nv-zustand` umzubauen waere ein Neuentwurf, kein
   Zusammenfuehren. Steht als eigener Posten. */
.nv-karte-inhalt.ist-meldung {
  text-align: center;
  color: var(--k-text3);
}
.nv-karte-inhalt.ist-meldung.ist-fehler { color: var(--k-red); }

/* ── `.ist-aktiv` — der Zustand «ausgewaehlt / laeuft gerade» ─────────
   Angelegt 2026-09-03 (D6 Teil 3) als kanonischer Nachfolger von
   `.active`. Er faerbt NICHT selbst: was «aktiv» aussieht, entscheidet
   der Baustein — ein Reiter anders als eine Karte. Die Regel hier
   haelt nur den NAMEN fest und gibt den beiden Bausteinen, die es
   heute schon brauchen, ihre Fassung.

   ⚠ Sie ersetzt `.active` NICHT automatisch. `.active` steht in 125
   Regeln ausserhalb dieses Abschnitts; das Umschreiben ist ein eigenes
   Stueck Arbeit mit eigener Bildabnahme. Wer hier etwas anlegt,
   ersetzt dort nichts. */
.nv-segment-opt.ist-aktiv {
  background: var(--k-surface, #fff);
  color: var(--k-text);
  font-weight: var(--k-fw-semibold);
}
.nv-filterchip.ist-aktiv {
  background: var(--k-brand);
  color: var(--k-text-auf-marke);
}

/* ── 16b. STAPEL — der Abstand gehoert dem Behaelter, nicht dem Kind ────
   Stefan-Anweisung 2026-08-29: «Erst die Behaelter bekommen ihren gap,
   dann fallen die Hilfsklassen. Nicht umgekehrt — sonst rutscht alles
   zusammen.»

   Gemessen waren 141 Vorkommen von `nv-mb-md`, `nv-mb-sm`, `nv-mt-sm`
   und `nv-mt-md`. Alle sagten dasselbe: «unter mir kommt noch etwas,
   lass Luft.» Das ist keine Eigenschaft des Kindes, sondern des
   Behaelters — ein Kind weiss nicht, ob es das letzte ist. Genau daran
   erkennt man den Fehler: der letzte Block einer Liste traegt denselben
   Abstand nach unten wie alle anderen und schiebt den Rand auf.

   Der Stapel kennt seine Kinder und setzt den Abstand ZWISCHEN sie.
   Nach dem letzten kommt keiner. */
.nv-stapel {
  display: flex;
  flex-direction: column;
  gap: var(--nv-stapel-luft, var(--nv-s4, 16px));
}
.nv-stapel--eng { --nv-stapel-luft: var(--nv-s3, 12px); }
/* Leere Platzhalter zaehlen nicht mit.
   `<div id="hr-absenz-konflikt"></div>` steht in mehreren Blaettern und
   wird erst spaeter gefuellt. Ein `gap` schert sich nicht um die Hoehe:
   er sitzt VOR und HINTER jedem Kind, auch vor einem, das null Pixel
   hoch ist — aus 16 px wuerden 32. Sobald der Platzhalter Inhalt
   bekommt, greift `:empty` nicht mehr und er zaehlt wieder mit.
   Nur `div` und `section`: `input`, `img`, `br` und `hr` sind IMMER
   leer im Sinne von `:empty` — ein allgemeines `> :empty` wuerde jedes
   Eingabefeld im Stapel verschwinden lassen. */
.nv-stapel > div:empty,
.nv-stapel > section:empty { display: none; }

/* ══ DIE KARTE ══ (Stefan-Entscheid B, 2026-08-28)
   `k-glass-card` IST die Karte — nicht eine von vieren. Gemessen war sie
   mit 283 Vorkommen die haeufigste, die einzige themenstabile der beiden
   grossen, und die mit dem kleinsten Rattenschwanz (8 Regeln ausserhalb
   des Katalogs gegen 119 bei `nv-card`).

   RADIUS 32 -> `--nv-r-m` (26). Die 32 waren `--nv-r-blatt`, der Radius
   des BLATTES, auf eine Karte angewendet. Stefans Begruendung, und sie
   gehoert hierher: «Das Blatt ist die groessere Flaeche und muss runder
   sein als die Karten darauf. Gleicher Radius = keine Hierarchie.»

   RAHMENLINIE FAELLT WEG. Der Kanon (§2) sagt: weicher Schatten, nie
   Rahmenlinien. Trennung entsteht durch Schatten und Abstand.

   ⚠ UND DER SCHATTEN FAELLT AUCH WEG — 2026-09-02, Etappe N2.
   Stefans Nacht-Auftrag vom 02./03.09. sagt woertlich: «EINE
   Katalog-Fassung bauen, nach der Design-Referenz: ein Radius, KEIN
   RAHMEN, KEIN SCHATTEN, Text nie auf Foto oder Verlauf, Rechteck-
   Familie.» Das ist juenger als der Satz darueber und ersetzt ihn.
   Nach CLAUDE.md gilt: wer eine Direktive ersetzt, LOESCHT die alte —
   deshalb steht der Satz «Der Schatten --nv-sh bleibt und traegt die
   Karte allein» hier nicht mehr, sondern nur noch als Vermerk, dass es
   ihn gab. Zwei widersprechende Regeln sind schlimmer als eine falsche.
   Die Trennung traegt jetzt allein die opake Flaeche gegen den Grund:
   hell #FFFFFF auf #FBF1F2, dunkel #271D21 auf #1C1417.
   ⚠ WIDERSPRUCH, ausdruecklich benannt und NICHT von mir entschieden:
   docs/DESIGN-KANON.md §2 fuehrt `--nv-schatten-hoch` weiterhin als
   «Schatten (Karte, Blatt)» und sagt «Trennung entsteht durch Schatten
   und Abstand». Der Nacht-Auftrag sticht ihn; wenn Stefan am Morgen
   anders entscheidet, ist die Rueckname genau diese eine Zeile.

   DREI NAMEN, EINE FASSUNG (N2, 2026-09-02). `.nv-karte` und `.k-stats`
   standen weiter oben mit einer zweiten Karten-Form im selben Katalog.
   Sie zeigen jetzt hierher. Gemessen vorher: 466 + 25 Stellen, zwei
   Formen, ein Bauteil. */
body.k-ui .k-glass-card,
body.k-ui .nv-karte,
body.k-ui .k-stats {
  background: var(--k-bg2) !important;
  border: none !important;
  /* (4) Nacht 2, C-Entscheid: der Radius steht ohne Vorrang — die
     Gegenzeile css/ios-grouped.css:933 (20 px mit Vorrang auf task-/news-/
     order-/lager-/zeit-Karten) ist weg, seit die News-Karte eine
     Katalog-Karte ist und die uebrigen fuenf Klassen keinen Verwender
     ohne Glas mehr haben (gemessen 06.09.2026, DESIGN-RADIEN-D17.md §8).
     Damit ist der Altname `--nv-r-m` ohne Verwender und aus §2 entfernt. */
  border-radius: var(--nv-r-karte);
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
}

/* ── P13 (Stefan-Entscheid 2026-08-29): --marke ────────────────────────
   ROLLE: die Karte zeigt an, dass ihr Inhalt gerade in einem besonderen
   Zustand ist — die laufende Periode, ein erreichtes Ziel, ein Eintrag,
   der Aufmerksamkeit braucht. Die Farbe sagt WELCHER Zustand; die Kante
   sagt, DASS einer vorliegt.

   ⚠ DER BEFUND, DER DAZU FUEHRTE — kein Aufraeumen, sondern ein
   FUNKTIONSFEHLER. Sechs Module setzten diese Kante bereits, bedingt
   («laeuft gerade», «Ziel erreicht», «verdaechtig») und inline. Gemessen
   mit Gegenbeleg: `border-left` MIT Inline = OHNE Inline = `0px`. Die
   Regel darueber setzt `border: none !important`, und `!important`
   schlaegt auch einen Inline-Stil. Diese sechs Zustands-Marken waren
   also SEIT DEM UMBAU UNSICHTBAR — jede fuer sich richtig geschrieben,
   keine wirksam.

   Die Frage war deshalb nie «Inline weg?», sondern «sollen die Marken
   zurueck?». Antwort: ja, als benannter Zustand mit einer Schraube fuer
   die Farbe. `!important` ist hier kein Notnagel, sondern die einzige
   Moeglichkeit, gegen das `!important` der Grundform zu bestehen — und
   die Grundform ihrerseits braucht ihres, weil vier Altdateien der
   Karte einen Rand geben. Beides steht jetzt an EINER Stelle
   nebeneinander, statt sich ueber sechs Dateien zu verfehlen. */
body.k-ui .k-glass-card--marke {
  border-left: 3px solid var(--marke, var(--k-brand)) !important;
}

/* ── 17. FORMULAR — die Form folgt dem Ort, nicht dem Thema ─────────────
   Stefan-Entscheid A, 2026-08-28/29.

   DER BEFUND, DER DAZU FUEHRTE. Dasselbe Feld war in Hell ein Rechteck
   (14 px) und in Dunkel eine Pille (9999 px), mit anderer Polsterung und
   anderer Schriftgroesse. Das THEMA bestimmte die FORM — Verstoss gegen
   §6 des Kanons. Ursache war keine `.nv-input`-Regel, sondern eine
   allgemeine in `style.css`:
       [data-theme="light"] input:not([type="checkbox"]):not([type="radio"])
   `:not()` zaehlt mit dem Gewicht seines Arguments, also (0,3,1) — das
   schlug die Katalogregel (0,2,2). Sie war die faktische
   Formular-Definition fuer 84 % aller Felder und ist am 29.08. gefallen,
   nachdem alle 927 Felder die Katalogklasse trugen.

   ZWEI ZUSTAENDE, MEHR GIBT ES NICHT
     .nv-input        Grundform: Feld in einer Gruppenkarte, als ZEILE.
                      Kein eigener Grund, kein eigener Rand, kein eigener
                      Radius — die Karte traegt das alles. Trennlinie zur
                      naechsten Zeile, links auf Texthoehe eingerueckt
                      (Apple-Muster: die Linie beginnt beim Text, nicht am
                      Kartenrand). Mindesthoehe 44 pt.
     .nv-input--frei  Allein stehendes Feld: Pille, eigener Grund. NUR
                      fuer Suche und einzelne Felder in einer Leiste —
                      gemessen sind das 10 von 927.
     .nv-input--fehler Fehlerzustand. MUSS sichtbar sein: Wert und
                      Etikett in Fehlerfarbe, Trennlinie in Fehlerfarbe.
                      Vorher war ein ungueltiges Feld optisch identisch
                      mit einem gueltigen — eine kaputte Funktion, kein
                      Schoenheitsfehler.
   `select` folgt derselben Regel wie `input`. Keine Sonderform. */

.nv-input {
  width: 100%;
  min-height: var(--nv-tap, 44px);
  padding: var(--nv-s3) 0;
  background: transparent;
  border: none;
  border-radius: 0;
  color: var(--k-text);
  font-family: inherit;
  font-size: var(--k-fs-body);
  font-weight: 400;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  box-shadow: none;
}
/* EINGABE IM FELD (Nacht 4, Stefan 22, 06.09.2026): das gerahmte Eingabefeld
   im `.field`-Formular (Kundenformular, Arbeitszeiten, Bank-Import) — Schrift
   hervor, Polster 14/16, Radius `--nv-r-eingabe` (12, dritte Formelstufe).
   Stand bis dahin als «Premium Polish» in css/style.css mit hartem 12px;
   dieselben Selektoren, Bild gleich (Sonde finance-bank, arbeitszeiten). */
.nv-input--feld, .field input, .field select, .field textarea {
  font-size: var(--k-fs-hervor);
  padding: 14px 16px;
  border-radius: var(--nv-r-eingabe);
  transition: border-color var(--k-dur-fast), box-shadow var(--k-dur-fast), background var(--k-dur-fast);
}
/* ── `.nv-input--kasten` · `.nv-feldpaar` · `.nv-ankreuzzeile` ──────────────
   Die drei Bausteine der Erfassungsmasken im Blatt (Bottom-Sheet).

   ANLASS, offen benannt (10.09.2026, Eilauftrag Vertrag Schritt 2): die
   Maske «Mitarbeiter erfassen» baute jedes Eingabefeld mit einer lokalen
   Variablen `iS` als Inline-Stil, jede Zeile mit einem Inline-Raster und
   jeder Abstand als `margin-bottom` am Kind. Als die Maske um neun Felder
   wuchs, fielen drei Ratschen rot: r_css (+15), r_neuzugang (+3) und
   r_randinline (+6).

   Die Umgebung nachzuahmen war hier das Falsche. Genau dieser Bestand IST
   die Schuld, die die Ratsche abtraegt — wer ihn kopiert, vergroessert sie
   mit jeder neuen Zeile. Also derselbe Weg wie bei `.nv-feldgruppe` im
   Abschnitt darueber: das Aussehen einmal hierher, das JS setzt die Klasse.

   `--kasten` ist die gerahmte Fassung des Eingabefelds (das schlichte
   `.nv-input` ist eine Unterstreichung ohne Rahmen). Die Werte sind
   Zeichen fuer Zeichen die aus `iS` — dasselbe Bild, andere Stelle.
   Muss NACH `.nv-input` stehen: gleiche Spezifitaet, die spaetere Regel
   gewinnt.

   ZWEI SELEKTOREN, NICHT EINER (`.nv-input.nv-input--kasten`): die Felder
   stehen in `<div class="field">`, und `.field input` ist spezifischer als
   eine einzelne Klasse. Der Inline-Stil hat das frueher uebertroffen, weil
   Inline alles uebertrifft — eine Klasse muss es sich verdienen. Dasselbe
   gilt gegen `.nv-input:not(:last-child)`, das eine Unterstreichung als
   Hintergrundbild setzt; darum hier `background-image: none`. */
.nv-input.nv-input--kasten {
  width: 100%;
  padding: 10px;
  border: 1.5px solid var(--k-border);
  border-radius: var(--k-r-sm);
  font-family: var(--k-font);
  font-size: var(--k-fs-caption);
  background: var(--k-glass-bg);
  background-image: none;
  color: var(--k-text);
}
/* Zwei Felder nebeneinander. Der Abstand gehoert dem Behaelter (`gap`),
   der Abstand zur naechsten Zeile ebenfalls — nicht als margin ans Kind. */
.nv-feldpaar {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--nv-luft-1, 10px);
  margin-bottom: var(--nv-luft-1, 10px);
}
/* Ein Feld ueber die volle Breite, im selben Raster wie das Paar — damit
   ein langes Textfeld (eine Begruendung) nicht in eine halbe Spalte muss. */
.nv-feldpaar-voll { grid-template-columns: 1fr; }
/* Eine Ankreuzzeile: Kaestchen und Beschriftung in einer Reihe, ganze
   Zeile anklickbar. */
.nv-ankreuzzeile {
  display: flex; align-items: center; gap: var(--nv-luft-0, 8px);
  font-size: var(--k-fs-caption); color: var(--k-text); cursor: pointer;
}
.nv-ankreuzzeile + .nv-ankreuzzeile { margin-top: var(--nv-luft-0, 6px); }
.nv-ankreuzzeile input[type="checkbox"] {
  width: 18px; height: 18px; accent-color: var(--k-brand); flex: none;
}
/* ⚠ ZWEI BAUTEILE, DIE SICH WIDERSPROCHEN HABEN — gemessen am 11.09.2026.
   Die Regel darueber und `input[type="checkbox"].nv-griff` (§13) haben
   dieselbe Gewichtung (0,2,1). Bei Gleichstand gewinnt die spaetere, und
   das ist diese hier: ein Kaestchen mit BEIDEN Klassen wurde 18x18 statt
   44x44 [gemessen mit Chromium an einer Minimalseite, nicht gerechnet].
   Wer den Griff ausdruecklich anlegt, bekam also stillschweigend das
   Gegenteil — die gefaehrlichste Art Fehler, weil das Bild richtig
   aussieht.
   Diese Zeile macht die beiden zusammensetzbar: wer `nv-griff` schreibt,
   meint 44, auch in einer Ankreuzzeile. Sie vergroessert NICHTS von sich
   aus; ohne `nv-griff` bleibt das Kaestchen bei 18. */
.nv-ankreuzzeile input[type="checkbox"].nv-griff {
  width: var(--nv-tap, 44px); height: var(--nv-tap, 44px);
}
/* Das Blatt selbst (Inhalt eines Bottom-Sheet) und sein Titel. */
.nv-blatt { padding: var(--nv-luft-2, 20px); max-width: 460px; width: 100%; }
.nv-blatt-titel {
  font-size: var(--k-fs-body); font-weight: var(--k-fw-bold);
  margin: 0 0 4px;
}
/* Die Ausfuell-Liste (D2). Eine Tabelle mit einer leeren Spalte zum
   Hineinschreiben — auf Papier ausfuellbar, in einem Durchgang. Die letzte
   Spalte bekommt eine Linie statt eines Rahmens: sie ist zum Schreiben da,
   nicht zum Lesen. */
.nv-druckliste__tabelle {
  width: 100%; border-collapse: collapse;
  font-size: var(--k-fs-caption); margin: var(--nv-luft-1, 10px) 0;
}
.nv-druckliste__tabelle th,
.nv-druckliste__tabelle td {
  text-align: left; vertical-align: top;
  padding: var(--nv-luft-0, 6px) var(--nv-luft-0, 8px);
  border-bottom: 1px solid var(--k-border);
}
.nv-druckliste__tabelle th { color: var(--k-text2); font-weight: 700; }
.nv-druckliste__leer { min-width: 34%; }
/* Auf dem Schirm ist die Liste ein Blatt wie jedes andere; sie zeigt sich
   erst beim Drucken von ihrer eigentlichen Seite. Beide Klassen stehen
   trotzdem AUSSERHALB der Druckregel — ein Bauteil, das es nur in einer
   Medienabfrage gibt, ist im Katalog nicht auffindbar. */
.nv-druckliste { line-height: 1.5; }
.nv-nicht-drucken { /* auf dem Schirm sichtbar — siehe @media print unten */ }
@media print {
  .nv-nicht-drucken { display: none; }
  .nv-druckliste { max-width: none; }
}

/* Ein abgesetzter Kasten im Blatt — fasst zusammengehoerige Schalter. */
.nv-kasten {
  background: var(--k-glass-bg); border: 1px solid var(--k-border);
  border-radius: var(--k-r-sm); padding: var(--nv-luft-1, 12px);
  margin-bottom: var(--nv-luft-1, 12px);
}

/* Die Trennlinie gehoert der ZEILE, nicht dem Rahmen: sie beginnt links
   auf Texthoehe und laeuft bis zum Kartenrand. Deshalb ein Untergrund
   statt eines `border-bottom` — ein Rahmen liesse sich nicht einruecken. */
.nv-input:not(:last-child) {
  background-image: linear-gradient(var(--k-border), var(--k-border));
  background-size: calc(100% - var(--nv-s4)) 1px;
  background-position: right bottom;
  background-repeat: no-repeat;
}
.nv-input::placeholder { color: var(--k-text3); opacity: 1; }
.nv-input:focus { outline: none; }
.nv-input:disabled { opacity: .5; }
textarea.nv-input { min-height: calc(var(--nv-tap, 44px) * 2); resize: vertical; }

/* Frei stehend: Pille mit eigenem Grund. Nur Suche und Leisten-Felder. */
.nv-input--frei {
  padding: var(--nv-s3) var(--nv-s4);
  background: var(--k-bg2);
  border-radius: var(--nv-r-kapsel);
  background-image: none;
}

/* Fehler: muss man SEHEN. */
.nv-input--fehler {
  color: var(--k-red, #d94f60);
  background-image: linear-gradient(var(--k-red, #d94f60), var(--k-red, #d94f60));
}
.nv-input--fehler::placeholder { color: var(--k-red, #d94f60); opacity: .7; }

/* Das Etikett ueber dem Feld. Gehoert zum Formular, deshalb hier und
   nicht als eigenes Bauteil.
   Es war in FUENF Dateien definiert, mit drei verschiedenen Ausfuehrungen
   (12.5px/700 voll · .72rem/600 gedaempft · .68rem/700 GROSSBUCHSTABEN).
   Gewonnen hat die erste — nicht durch Entscheid, sondern durch Position.
   BEWUSSTE AENDERUNG (Stefan-Vorgabe A1: «Etikett klein darueber,
   gedaempft»): die Farbe geht von voller Textfarbe auf --k-text2. Das
   sieht man an 130 Stellen. Groesse und Gewicht bleiben, damit sich
   nicht mehr aendert als beauftragt. */
.nv-label {
  display: block;
  font-size: 0.78125rem;
  font-weight: 700;
  color: var(--k-text2);
  letter-spacing: 0;
  text-transform: none;
  margin-bottom: var(--nv-s1);
}

/* ── 18. DETAIL-ANSICHT — Abschnitt, Schlüssel-Wert-Zeile, Trenner ─────
   Verschoben aus css/design-system.css am 2026-08-29 (Stefan-Auftrag 3,
   Rangliste: 196 Vorkommen, die groesste Familie ausserhalb des
   Katalogs). Sie war NICHT mehrfach definiert — sie stand nur an der
   falschen Stelle. Deshalb hier unveraendert uebernommen, Regel fuer
   Regel; die drei modulgebundenen Feinheiten in ios-grouped.css
   (#hr-ma-detail) bleiben, wo sie sind: sie setzen Groesse und Polster
   fuer EIN Modul und sind keine zweite Ausfuehrung. */
.nv-detail-section {
  margin-top: var(--nv-section-gap, 24px);
  padding: 0 16px;
}
.nv-detail-section:first-child {
  margin-top: var(--nv-header-to-content, 20px);
}
.nv-detail-section-title {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--k-brand);
  margin-bottom: 12px;
  font-family: var(--k-font, 'Outfit', sans-serif);
  display: flex;
  align-items: center;
}
.nv-detail-kv {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px 16px;
}
.nv-detail-kv-item {
  min-width: 0;
}
.nv-detail-kv-label {
  font-size: var(--k-fs-body);
  color: rgba(255,255,255,0.5);
  line-height: 1.3;
  margin-bottom: 2px;
  font-family: var(--k-font, 'Outfit', sans-serif);
}
[data-theme="light"] .nv-detail-kv-label { color: rgba(0,0,0,0.5); }
.nv-detail-kv-value {
  font-size: var(--k-fs-hervor);
  font-weight: 500;
  color: var(--k-text, #F5F5F5);
  line-height: 1.3;
  margin-top: 2px;
  font-family: var(--k-font, 'Outfit', sans-serif);
  word-break: break-word;
}
.nv-detail-badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 8px;
  font-size: var(--k-fs-body);
  font-weight: 600;
  font-family: var(--k-font, 'Outfit', sans-serif);
  letter-spacing: .02em;
  text-transform: uppercase;
}
.nv-detail-actions {
  display: flex;
  gap: 8px;
  margin-top: 16px;
  flex-wrap: wrap;
}
.nv-detail-divider {
  height: 1px;
  background: var(--k-border, rgba(255,255,255,.08));
  margin: var(--nv-section-gap, 24px) 16px 0;
}
[data-theme="light"] .nv-detail-divider,
[data-theme="light"] .nv-detail-divider {
  background: var(--k-border, rgba(0,0,0,.08));
}
.nv-detail-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--k-border, rgba(255,255,255,.08));
  gap: 12px;
}
.nv-detail-row:last-child { border-bottom: none; }
/* Schriftgroesse: Fliesstext, nicht Bildunterschrift.
   Grund, nachgemessen 2026-08-29: in `ios-grouped.css` stand seit Monaten
   `#hr-ma-detail .nv-detail-row__label { font-size: var(--k-fs-body)}` — angelegt
   mit dem Vermerk «Stefan: Texte zu klein». Ich habe die Regel als
   modulgebundenen Sonderfall entfernt, ohne sie zu messen; damit war die
   Personalakte wieder bei .72rem (11.52px). Der Wert gehoert nicht an
   eine Kennung, sondern hierher — und auf eine Stufe der Skala, nicht auf
   die freie Zahl .85rem. `--k-fs-body` ist die naechste Stufe (14.08px). */
.nv-detail-row__label {
  font-size: var(--k-fs-body, .88rem);
  color: var(--k-text3, rgba(255,255,255,.4));
  flex-shrink: 0;
}
.nv-detail-row__value {
  font-size: var(--k-fs-body, .88rem);
  font-weight: var(--k-fw-semibold, 600);
  color: var(--k-text, #F5F5F5);
  text-align: right;
  max-width: 60%;
  word-break: break-word;
}
/* Der Wert als Verweis (tel:, mailto:): Akzentfarbe, kein Unterstrich.
   Entstanden aus dem letzten Inline-Stil mit `--k-accent`
   (js/anrufanfragen.js, Telefonnummer der Anrufanfrage; D-3-Rest,
   05.09.2026). Die Farbe ist die eine Akzentfarbe `--nv-akzent`
   (E-4: --nv-* ist kanonisch), nicht ein zweiter Name dafuer. */
.nv-detail-row__value--link {
  color: var(--nv-akzent);
  text-decoration: none;
}
[data-theme="light"] .nv-detail-row,
[data-theme="light"] .nv-detail-row {
  border-color: var(--k-border, rgba(0,0,0,.06));
}

/* ── 19. ZUSTAENDE — leer, Fehler, laedt ────────────────────────────────
   Stefan-Anweisung 2026-08-29, Punkt 1 und 5.

   EIN BEHAELTER, DREI ZUSTAENDE. Der Unterschied ist Farbe, Symbol und
   Aktion — nicht der Aufbau. Stefans Wortlaut: «Beide Zustaende nutzen
   denselben Behaelter.»

     LEER    ruhig, gedaempft, keine Warnfarbe. Sagt: hier ist nichts,
             das ist in Ordnung. Hoechstens eine Anlegen-Aktion.
     FEHLER  Symbol in der Fehlerfarbe, nicht gedaempft. Ein Satz in
             Alltagssprache. Darunter «Erneut versuchen».
     LAEDT   dasselbe Geruest, ohne Aussage.

   EIN FUND BEIM UMZUG, und er erklaert einiges. Die vier Regeln
   `.nv-empty-state__icon/__title/__text/__action` standen seit ihrer
   Entstehung in `design-system.css` — und haben NIE gegriffen. Die
   Funktion `nvRenderEmpty` erzeugt `.nv-empty-icon` (ohne die zwei
   Unterstriche) und rohe `<h3>`/`<p>`. Nur die Regel fuer den Behaelter
   passte. An 73 Aufrufstellen war das Symbol also weder 48 px noch
   gedaempft, und der Titel hatte keine Katalog-Schrift. Kein Waechter
   konnte das sehen: sie zaehlen Regeln und Klassen, Aussehen misst nur
   die Bauteil-Probe — und dort stand keines dieser vier Bauteile.
   Genau deshalb gibt es seit heute `tools/probe-abdeckung.cjs`.

   WAS NICHT AUF DEN BILDSCHIRM GEHOERT: der Fehlercode. Der geht in die
   Konsole. Stefans Regel: «Wenn Stefan angerufen wird, findet sie dort
   jemand.» */
/* Die Luft um einen Zustand ist groesser als jeder Listenabstand — er
   steht allein auf einer leeren Flaeche. 48 px ist KEIN sechster Wert
   der Abstands-Skala (die hat fuenf und soll fuenf behalten), sondern
   eine Zahl dieses einen Bauteils. Deshalb steht sie hier als eigener
   Wert und nicht als `--nv-s7`. */
.nv-zustand {
  --nv-zustand-luft: 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--nv-zustand-luft) var(--nv-s4);
  text-align: center;
}
.nv-zustand-symbol {
  font-size: 3rem;
  line-height: 1;
  color: var(--k-text4, rgba(26,26,26,.22));
  font-variation-settings: 'FILL' 0, 'wght' 200;
  margin-bottom: var(--nv-s4, 16px);
}
.nv-zustand-titel {
  font-family: inherit;
  font-size: var(--k-fs-hervor);
  font-weight: 700;
  color: var(--k-text);
  margin: 0 0 var(--nv-s1, 4px);
}
.nv-zustand-text {
  font-family: inherit;
  font-size: var(--k-fs-body);
  color: var(--k-text3);
  max-width: 280px;
  margin: 0;
  line-height: 1.5;
}
.nv-zustand-aktion { margin-top: var(--nv-s5, 24px); }

/* Der Fehler unterscheidet sich in genau zwei Dingen: das Symbol traegt
   die Fehlerfarbe und ist nicht gedaempft, und der Text ist so dunkel
   wie normaler Text — eine Meldung, die man uebersieht, ist keine.

   DER TON STEHT AN EINER STELLE. Die erste Fassung setzte die Farbe
   direkt an den Kindern — damit hatte der Zustand selbst keine einzige
   eigene Regel, und die Katalog-Ratsche hat ihn zu Recht als «neben dem
   Katalog gebaut» gemeldet: ein Bauteil, das nur als Kontext in fremden
   Selektoren vorkommt, erklaert sich nirgends. Jetzt traegt der Zustand
   seinen Ton, und die Kinder lesen ihn. Wer die Fehlerfarbe aendern
   will, aendert eine Zeile. */
.nv-zustand--fehler { --nv-zustand-ton: var(--k-red, #d94f60); }
.nv-zustand--fehler .nv-zustand-symbol {
  color: var(--nv-zustand-ton);
  font-variation-settings: 'FILL' 0, 'wght' 400;
}
.nv-zustand--fehler .nv-zustand-text { color: var(--k-text2); }

/* `.nv-empty-state` bleibt als Name im ausgelieferten Code stehen und
   zeigt auf denselben Behaelter — 73 Aufrufstellen auf einmal
   umzuschreiben waere eine Aenderung, die niemand einzeln pruefen kann.
   Der Katalog hat trotzdem nur EINE Ausfuehrung: hier. */
.nv-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--nv-zustand-luft, 48px) var(--nv-s4);
  text-align: center;
}

/* ── 20. AUSWAHL — nv-select ────────────────────────────────────────────
   Nach der Rangliste (33 Vorkommen). Sie war ZWEIFACH definiert:
   `design-system.css` (Zeile 199 ff.) und `style.css` (3581 ff.), mit
   verschiedenen Werten und verschiedener Ladereihenfolge. Eine doppelt
   definierte Klasse zaehlt nach der Katalog-Regel als NICHT abgedeckt —
   zu Recht: welche der beiden gilt, haengt an der Reihenfolge der
   Stilvorlagen und nicht an einer Entscheidung.

   Die Auswahl IST ein Eingabefeld. Sie erbt deshalb vom Formular (§17)
   und ergaenzt nur, was ein `select` zusaetzlich braucht: den Pfeil und
   das Abschalten der Browser-Optik. */
.nv-select {
  width: 100%;
  min-height: var(--nv-tap, 44px);
  /* Rechts Platz fuer den Pfeil. `--nv-s5` (24 px) reicht fuer ein
     16-px-Zeichen. Hier stand `--nv-s6` — den Wert gibt es nicht, und
     eine Polster-Angabe mit einer unbekannten Variable wird vom Browser
     KOMPLETT verworfen. Das Feld haette gar keine Polsterung gehabt. */
  padding: var(--nv-s3) var(--nv-s5) var(--nv-s3) 0;
  background: transparent;
  border: none;
  border-radius: 0;
  color: var(--k-text);
  font-family: inherit;
  font-size: var(--k-fs-body);
  font-weight: 400;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  box-shadow: none;
}
/* Der Pfeil sagt «hier oeffnet sich eine Liste» und gehoert zum Bauteil.
   Als MASKE, nicht als Bild: ein Bild traegt seine Farbe fest im Datei-
   inhalt (die alte Regel hatte `%23999` einkodiert und war damit in
   beiden Themen dieselbe graue Spitze). Eine Maske nimmt `currentColor`
   und folgt dem Thema von selbst. */
.nv-select {
  background-image: none;
}
.nv-auswahl {
  position: relative;
  display: block;
}
.nv-auswahl::after {
  content: '';
  position: absolute;
  right: 0;
  top: 50%;
  width: 16px;
  height: 16px;
  transform: translateY(-50%);
  pointer-events: none;
  background-color: var(--k-text3);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") no-repeat center / contain;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* ── 21. LAEDT — nv-loading ─────────────────────────────────────────────
   Nach der Rangliste (37 Vorkommen). Derselbe Behaelter wie §19, damit
   ein Modul beim Wechsel von «laedt» auf «leer» oder «Fehler» nicht
   springt: gleiche Polsterung, gleiche Ausrichtung. */
.nv-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--nv-s3);
  padding: var(--nv-zustand-luft, 48px) var(--nv-s4);
  text-align: center;
  color: var(--k-text3);
  font-size: var(--k-fs-body);
}
/* Der Kreisel gehoert zum Bauteil, nicht zur Datei, aus der es kam.
   Er hing in `style.css` an einem `::before` und waere beim Umzug still
   verschwunden — die Kehrseite der Katalog-Regel: wer ein Bauteil holt,
   holt auch, was es KANN, nicht nur wie es aussieht. */
.nv-loading::before {
  content: '';
  width: 28px;
  height: 28px;
  border-radius: var(--nv-r-kapsel, 999px);
  border: 2.5px solid var(--k-border-strong);
  border-top-color: var(--k-brand);
  animation: nvSpin .7s linear infinite;
}
@keyframes nvSpin { to { transform: rotate(360deg); } }

/* ── 22. MODUL-KOPF — k-module-header, k-module-back, k-module-more ─────
   A1, 2026-08-29. Hier standen VIER Ausfuehrungen desselben Knopfes:

     keeva-ui.css:1248      40 x 40
     keeva-ui.css:2393      36 x 36
     system.css:1624        38 x 38, `body.k-ui`, !important   <- gewann
     ios-grouped.css:532    min 44 x 44, !important

   Keine davon war falsch geschrieben — sie wussten nur nichts
   voneinander. Was auf dem Bildschirm ankam, entschied die
   Spezifitaet: `body.k-ui .k-module-back` (0,2,0) schlug alles andere,
   und `min-height:44px` aus ios-grouped verlor gegen das gleichrangige
   `min-height:38px !important` derselben Regel. Der Knopf ist also
   38 px, nicht 44 — die Fingerflaeche liefert der Griff (§13) als
   unsichtbare Auflage. Genau dafuer gibt es ihn.

   Die Werte unten sind GEMESSEN (bauteil-probe, beide Themen), nicht
   neu entschieden. `body.k-ui` faellt weg: die Klasse steht fest in
   index.php:281 und wird nirgends entfernt — sie war eine
   Spezifitaets-Kruecke, keine Bedingung. */
.k-module-header {
  display: flex;
  align-items: center;
  gap: var(--nv-s3, 12px);
  /* F1 (2026-09-02): unten 0, Abstand am Nachbarn (`.k-module-header + *`).
     G1 (2026-09-02, Block G): der Abstand liegt jetzt AM Kopf, als RAND —
     denn der Nachbar-Rand griff bei 10 von 58 Koepfen nicht (Inline
     `margin:0 auto` am Nachbarn, unsichtbarer Nachbar). Rand, nicht
     Polster: ein Rand faellt mit dem Rand des Nachbarn und dessen erstem
     Kind zusammen — genau wie die alte Nachbar-Regel. Als Polster
     gemessen (Lauf 2, 390): team 28.6 → 36.6, reviews 34.6 → 46.6,
     rollen 12.6 → 16.6, weil die inneren Raender nicht mehr zusammen-
     fielen. Als Rand bleiben die 46 auf ihrem Wert; die 10 kommen auf 12. */
  /* D-16 (Stefan-Entscheid 2026-09-06): unten dasselbe Polster wie oben —
     eine sichtbare Leiste braucht eine Unterkante mit Luft. Das sind
     +12 px Hoehe auf jedem Schirm mit Kanon-Kopf (gemessen, gewollt). */
  padding: var(--nv-s3) var(--nv-s4);
  margin-bottom: var(--nv-s3);
  -webkit-tap-highlight-color: transparent;
  /* ── F-2d (Stefan 2026-09-07): KEINE FLAECHE — BEFUND, DANN ENTSCHEID ──
     Stefan am 07.09.: «Der Seitenkopf (drei Punkte + Datum) zeigt eine
     deckende Farbe statt Glas.» Gemessen, bevor etwas geaendert wurde:

     WANN ES KIPPTE. In Teil D, Commit 6a882bd2d3 (06.09. 08:20 UTC,
     Deploy #3818, D-16). Bis dahin stand hier `background: transparent;
     backdrop-filter: none; border: none` (P3, 2026-08-29). D-16 hat
     `.k-module-header` in die Selektorliste von `.nv-glas-leiste` (§12)
     gesetzt: `rgba(255,255,255,.72)` + `blur(24px) saturate(180%)`.
     Es ist also ein RUECKSCHRITT aus Teil D, nicht ein Altzustand.

     WARUM DARAUS EINE DECKENDE FARBE WIRD, GEMESSEN STATT GERATEN.
     `backdrop-filter` faerbt das, was HINTER dem Element liegt. Der
     Modul-Kopf steht als erstes Element IM Fluss der Buehne — er
     ueberdeckt kein Inhaltselement (gemessen an kalender/kasse/team,
     390/768/1180: 0 ueberdeckte Inhaltsknoten). Hinter ihm liegt nur
     der App-Verlauf, und ein weichgezeichneter weicher Verlauf sieht
     aus wie derselbe Verlauf. Uebrig bleibt sichtbar allein die
     72-%-Deckflaeche: ein fast weisser Balken quer ueber den Schirm —
     genau der «weisse Streifen / solide Bar», den R13 am 26.08.
     beanstandet und P3 am 29.08. entfernt hatte.

     Glas ist eine Eigenschaft der LAGE, nicht des Namens: eine Leiste
     wird glaesern, wenn Inhalt unter ihr durchlaeuft. Dieser Kopf
     scrollt mit. Deshalb steht er nicht mehr in der Glas-Liste.
     ⚠ Offen auf Inhaber-Ebene (in der Quittung an C): wer eine echte
     Glas-Navigationsleiste will, muss den Kopf KLEBEN lassen
     (`position: sticky`), damit ueberhaupt etwas darunter durchlaeuft.
     Das ist eine eigene Aenderung mit eigenem Bildbeweis, keine
     Farbschraube. */
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border: none;
  box-shadow: none;
}
/* ⚠ Die Geometrie dieser beiden steht jetzt OBEN bei `.nv-btn--icon` —
   eine Ausfuehrung fuer alle runden Symbolknoepfe (2026-08-30). Hier
   bleibt nur, was sie zusaetzlich tun. Der weisse Grund (`--k-bg2`) und
   der Schatten sind weg: ein Symbolknopf ist kein schwebendes Element,
   sondern eine Flaeche in der Kopfzeile. */
.nv-btn--icon:active,
.k-module-back:active,
.k-module-more:active { transform: scale(.9); }

/* ── P1 (Stefan-Entscheid 2026-08-29): --auf-bild ──────────────────────
   ROLLE: der Knopf steht auf einem Foto, nicht auf dem App-Grund.

   Warum das eine eigene Rolle ist und keine Laune: die Grundform holt
   ihren Grund aus `--k-bg2` und ihren Rand aus `--k-border`. Beide sind
   auf einen bekannten Untergrund abgestimmt. Ueber einem Kundenfoto ist
   der Untergrund unbekannt — ein heller Knopf auf einem hellen Bild ist
   nicht mehr da. Deshalb dunkles Glas statt Flaeche, und kein Rand:
   die Kante entsteht aus dem Kontrast zum Bild.

   15 Vorkommen, bisher als `.kp-hero-actions .k-module-back` in
   `keeva-ui.css:908` — also an der AUFRUFSTELLE statt am Bauteil. Damit
   galt sie nur in diesem einen Umfeld; wer denselben Knopf auf einem
   anderen Bild braucht, baut ihn nochmal. Die Werte sind uebernommen,
   nicht neu entschieden — bis auf die Groesse: 40 px stand dort, der
   Katalog stellt 38. Ein Knopf, der auf einem Bild groesser ist als
   daneben, ist zwei Knoepfe. */
.k-module-back--auf-bild,
.k-module-more--auf-bild,
.nv-btn--auf-bild {
  border: none;
  background: rgba(0, 0, 0, .25);
  color: #fff;
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .15);
}
.k-module-back--auf-bild:active,
.k-module-more--auf-bild:active,
.nv-btn--auf-bild:active {
  transform: scale(.92);
  background: rgba(0, 0, 0, .4);
}
.k-module-back .material-symbols-rounded,
.k-module-more .material-symbols-rounded { font-size: 22px; }
/* ── P4 (Stefan-Entscheid 2026-08-29): ZWEI BAUTEILE, KEIN STREIT ──────
   Kanon §4a: «Ein Seitentitel steht links und ist gross. Ein
   Kopfbalken-Titel zwischen Zurueck und Aktionen steht mittig. Das
   sind zwei Bauteile, nicht eine Meinungsverschiedenheit.»

   Der Modul-Kopf traegt heute den SEITENTITEL — 34 px Georgia, links.
   `keeva-ui.css:2347` stellte `text-align:center`, verlor aber seit je
   gegen den Ladeplatz von system.css; auf dem Bildschirm stand nie
   etwas mittig. Die Regel dort ist also tot und faellt.
   Wer den Kopfbalken-Fall braucht, nimmt `--balken` — eine benannte
   Variante AM Bauteil, kein zweites Bauteil daneben. */
.k-module-header-center { flex: 1; min-width: 0; text-align: left; }
.k-module-header--balken .k-module-header-center { text-align: center; }
.k-module-header-title,
.k-mh-title-btn,
.kal-rose-titel {
  font-family: var(--nv-font-display, Georgia, serif);
  font-weight: 400;
  font-size: 2.125rem;
  line-height: 1.08;
  letter-spacing: 0;
  color: var(--k-text);
  text-transform: none;
}
/* ── P5 (Stefan-Entscheid 2026-08-29): der Titel bricht nicht aus ──────
   Entstand aus einer Beanstandung — «Titel lief aus dem Bild»
   (2026-08-26). Der Katalog stellte keine der vier Eigenschaften, die
   Regel stand in `system.css:1785` als `body.k-ui`-Sonderfall. Nach
   §11d gehoert eine Beanstandungs-Regel an den Baustein, nicht
   daneben: sonst gilt sie nur dort, wo jemand daran gedacht hat. */
/* G5 (2026-09-02, Block G): «bricht nicht aus» hiess bis hier «wird
   abgeschnitten». Gemessen bei 320/360/390/414 (tools: a-g5.cjs, Range
   ueber den Titeltext gegen das Element): 14 von 16 Faellen geklippt —
   «No-Show- & Storno-Gebühren» 351 px Text in 154–248 px Behaelter,
   «Abbrecher-Reaktivierung» 282 px, «Website-Analytics» 199 px, «Popup &
   Banner» 193 px. Ein 34-px-Titel hat auf einem Telefon zwei Zeilen,
   oder er ist unlesbar. Deshalb: Umbruch erlaubt, Ausbruch weiterhin
   nicht (overflow hidden + max-width bleiben; `anywhere` bricht ein
   einzelnes zu langes Wort, statt es abzuschneiden). */
.k-module-header-title {
  white-space: normal;
  overflow-wrap: anywhere;
  overflow: hidden;
  max-width: 100%;
}
.k-mh-title-btn {
  background: none;
  border: 0;
  padding: 0;
  min-height: 0;
  display: inline-flex;
  align-items: baseline;
  gap: var(--nv-luft-0, 6px);
  /* 8 -> 16, Skala (Stefan-Entscheid 2026-08-29, Punkt 2). Hier stand
     8px als benannter Rest, weil ich das Heben fuer eine unbestellte
     Aenderung hielt. Es war eine Vermutung; jetzt ist es entschieden.
     Der Radius zeigt sich ohnehin nur beim Druecken. */
  border-radius: var(--nv-r-chip, 16px);
  cursor: pointer;
  text-align: left;
  /* ── P7 (Stefan-Entscheid 2026-08-29): Breitengrenze ────────────────
     Gehoert zu P5 — derselbe Fall, dieselbe Beanstandung, nur der
     Knopf statt des reinen Titels. Ohne `min-width:0` traegt ein
     Flex-Kind seine Inhaltsbreite und schiebt den Kopf auseinander. */
  max-width: 100%;
  min-width: 0;
  overflow: hidden;
}
/* Der gedrueckte Zustand fehlte im Katalog ganz — er stand nur in
   `keeva-ui.css:2506`, und dort nur fuer das helle Thema. Ein Zustand,
   den es in einem Thema gibt und im anderen nicht, ist kein Zustand.
   Jetzt am Bauteil, fuer alle Themen (P7). */
.k-mh-title-btn:active { background: var(--k-fill-active); }

/* ── 23. UMSCHALTER — nv-segment, nv-segment-opt ────────────────────────
   A1, 2026-08-29. Umgezogen aus `design-system.css`, mitsamt den
   Schrauben. Das ist kein Aufraeumen, sondern §11d: der Baustein
   entstand aus einer Anweisung — «Segment Schalter so bauen, dass sie
   schnell aenderbar sind» (Stefan 2026-08-03) —, und diese Anweisung
   steckt in den `--seg-*`-Schrauben. Wer sie beim Umzug verliert,
   nimmt die Entscheidung zurueck.

   Einzelne Reihe anders? Schraube an Ort und Stelle drehen, ohne neue
   Klasse:  <div class="nv-segment" style="--seg-hoehe:44px">…</div>

   ZWEI AUSFUEHRUNGEN, gemessen: das Aussehen stand in
   `design-system.css`, der AKTIVE Zustand aber zusaetzlich in
   `system.css:794` mit `!important` (`--nv-pill` statt `--k-text`) —
   und der gewann. Die Schraube traegt deshalb jetzt `--nv-pill`: das
   ist der Wert, den man auf dem Bildschirm sieht, nicht der, der in
   der Datei am schoensten aussah. */
.nv-segment {
  --seg-hoehe:      36px;
  --seg-polster:    7px 14px;
  --seg-abstand:    var(--nv-s2, 8px);
  --seg-radius:     var(--k-r-full, 9999px);
  /* .82rem = 13.12px liegt zwischen den Stufen der Schrift-Skala
     (11.52 / 14.08). Benannter Rest: das Heben auf 14.08 macht die
     Reihe breiter und kann sie am Telefon umbrechen lassen — das ist
     eine Messung fuer sich, kein Nebenbei. Steht in
     docs/KATALOG-ENTSCHEIDE.md. */
  --seg-schrift:    .82rem;
  --seg-rand:       1px solid var(--k-border, rgba(26,26,26,.10));
  --seg-farbe:      var(--k-text2, rgba(26,26,26,.65));
  --seg-aktiv-bg:   var(--nv-pill, var(--k-text, #1A1A1A));
  --seg-aktiv-farbe: var(--nv-pill-ink, var(--k-bg, #fff));

  display: flex;
  flex-wrap: wrap;
  gap: var(--seg-abstand);
}
.nv-segment-opt {
  display: inline-flex;
  align-items: center;
  gap: var(--nv-luft-0, 6px);
  padding: var(--seg-polster);
  min-height: var(--seg-hoehe);
  border-radius: var(--seg-radius);
  border: var(--seg-rand);
  background: transparent;
  color: var(--seg-farbe);
  font-family: inherit;
  font-size: var(--seg-schrift);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color .15s, color .15s, border-color .15s;
}
.nv-segment-opt:hover { border-color: var(--k-border-strong, rgba(26,26,26,.2)); }
/* ⚠ EINE BENANNTE AENDERUNG, nicht mitgeschleppt.
   Vorher kamen Fuellung und Rand des aktiven Chips aus VERSCHIEDENEN
   Regeln: die Fuellung aus `system.css:794` (`--nv-pill`, #28242B), der
   Rand aus `design-system.css` (`--k-text`, #221E22). Der Chip hatte
   also einen Rand in einer anderen Nuance als seine Fuellung — kein
   Entscheid, sondern der Rest davon, dass zwei Regeln um ihn stritten.
   Jetzt kommen beide aus derselben Schraube. Gemessener Unterschied:
   rgb(34,30,34) -> rgb(40,36,43) an einem 1-px-Rand unter einer
   gefuellten Pille. Rueckgaengig waere eine eigene Rand-Schraube — die
   wuerde einen Zufall zur Regel erheben. */
.nv-segment-opt.is-aktiv {
  background: var(--seg-aktiv-bg);
  border-color: var(--seg-aktiv-bg);
  color: var(--seg-aktiv-farbe);
  /* Der federnde Pop beim Wechsel (Demo tagPop) stand in system.css:794
     und gehoert zum Bauteil, nicht zur Datei, aus der es kam. */
  animation: nvTagPop var(--m-standard) var(--m-feder);
}
/* Die Zahl ist Beiwerk — sie darf den Namen nicht ueberstimmen. */
.nv-segment-zahl { font-size: var(--k-fs-caption); font-weight: 700; opacity: .6; }
/* Variante fuer Reihen mit vielen Moeglichkeiten: gleiche Breite, damit
   die Reihe nicht zerfranst (Zeitraeume, Wochentage). */
.nv-segment--gleich .nv-segment-opt { flex: 1 1 0; justify-content: center; }
.nv-segment--voll .nv-segment-opt { flex: 1 1 0; justify-content: center; font-weight: var(--k-fw-bold, 700); }

/* ── 24. SEITENLEISTEN-ABSCHNITT — nv-sidebar-section ───────────────────
   P10 (Stefan-Entscheid 2026-08-29). Der Katalog «hatte» diese Klasse
   bisher nur, weil sie in der Griff-Liste (§13) steht — er gab ihr
   `position:relative` und die 44-px-Auflage und beschrieb sie sonst
   nicht. Das ganze Aussehen stand in `style.css:2109`. Eine Klasse, die
   im Katalog steht, aber nur daneben beschrieben wird, ist die
   schlechteste der drei Moeglichkeiten: sie sieht abgedeckt aus und ist
   es nicht. Also ganz aufnehmen — Werte uebernommen, nichts neu
   entschieden. */
.nv-sidebar-section {
  /* ⚠ 20 px und 14 px stehen als Zahl, nicht als Schraube. Beim
     Umzug hatte ich `var(--nv-s5)` geschrieben — das sind 24 px, und
     die Probe meldete es in allen drei Themen sofort rot. Ein Umzug
     darf das Aussehen nicht aendern; ob 20 und 14 auf die Skala
     gehoeren, ist eine eigene Frage und keine Nebenwirkung. */
  padding: 20px 14px var(--nv-luft-0, 6px) var(--nv-s4, 16px);
  margin: 0;
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-bold);
  color: var(--k-text4);
  text-transform: uppercase;
  letter-spacing: 1.2px;
  /* Der Abschnittstitel ist NUR sichtbar, wenn die Leiste offen ist —
     eingeklappt traegt sie nur Symbole, und ein Wort ohne Platz bricht
     die Spalte auf. */
  display: none;
  cursor: pointer;
  user-select: none;
  transition: color .15s;
}
.nv-sidebar.expanded .nv-sidebar-section {
  display: flex;
  align-items: center;
  gap: var(--nv-s1, 4px);
}
.nv-sidebar-section:hover { color: var(--k-text3); }
.nv-sidebar-section-arrow {
  font-size: 1rem;
  transition: transform .2s;
  font-variation-settings: 'wght' 400;
  margin-left: auto;
}
.nv-sidebar-section.collapsed .nv-sidebar-section-arrow { transform: rotate(-90deg); }
.nv-sidebar-section.collapsed + .nv-sidebar-group { display: none; }

/* ── 25. BLATT-RUMPF — nv-panel-body, und die randlose Ausfuehrung ──────
   P9 (Stefan-Entscheid 2026-08-29). Drei Stellen setzten dasselbe:
   `padding:0` + Flex-Spalte, jeweils an einen Behaelter-Namen gebunden
   (`kasse.css:295/296`, `keeva-ui.css:3879`, `style.css:3399`). Das ist
   nicht dreimal ein Sonderfall, sondern EINE Rolle, die dem Katalog
   fehlte: ein Blatt, dessen Inhalt seinen Rand selbst mitbringt (eine
   Liste, ein Raster, eine Flaeche bis an die Kante). Genau dieselbe
   Rolle traegt `.nv-karte--randlos` bei der Karte — gleiche Loesung,
   gleicher Name. */
.nv-panel-body--randlos {
  padding: 0;
  display: flex;
  flex-direction: column;
}

/* ── 26. CHIP — nv-chip und der Altbestands-Zwilling chip ───────────────
   N2 (Stefan-Entscheid 2026-08-29, Halt-Regel Fassung 2: «keeva-black
   nimmt den Wert, den dark/light tragen»).

   ⚠ NACHTRAG 2026-09-02 (N3): die Regeln dieses Abschnitts SIND WEG.
   `.chip` war die zweite Katalog-Fassung der Filterreihe neben
   `.nv-filterchip` — zwei Bausteine fuer ein Bauteil, genau das, was
   §BAUKASTEN ausschliesst. Alle Stellen sind auf `.nv-filterchip`
   gefuehrt; danach gemessen: 0 Klassen-Nutzer von `.chip` in js/, src/,
   index.php, index.html, katalog.php, beispiel-hr.php (Positivkontrolle:
   dieselbe Suche findet `nv-filterchip` 24x allein in index.html).
   Der Text unten bleibt, weil er die Begruendung der Werte traegt, die
   jetzt am `.nv-filterchip` stehen.

   VIER AUSFUEHRUNGEN standen fuer `.chip` nebeneinander, keine wusste
   von den anderen:

     style.css:667        Grundform, --k-fs-hervor, 12px 20px
     style.css:86/87      [data-theme], VERSALIEN, --k-fs-micro, 8px 18px
     style.css:816        @media(max-width:420px), 12px 18px
     ios-grouped.css:1118 [data-theme], .88rem, 600, 10px 16px, min 40  <- gewann

   Was auf dem Bildschirm ankam, entschied wieder der Ladeplatz:
   ios-grouped laedt spaeter als style.css, beide (0,2,0). In
   `keeva-black` griff keine der beiden Themen-Regeln — dort gewann die
   Media-Regel und darunter die Grundform. Ergebnis: derselbe Chip war
   im gestempelten Thema anders gepolstert, anders fett und ohne
   Mindesthoehe.

   Die Werte unten sind die, die dark und light HEUTE zeigen (gemessen,
   bauteil-probe). Sie sind nicht neu entschieden — sie sind die
   juengste benannte Entscheidung («Chips lesbar und satzweise wie
   ueberall sonst», 2026-08-03) und gelten jetzt fuer alle drei Themen.
   Farben kommen aus Tokens, deshalb braucht die Regel keinen
   Themen-Vorsatz mehr. */

/* ── 26a. Die ZUSTAENDE des Chips ──────────────────────────────────
   Zusammengefuehrt 2026-08-31 (Paket D1, Modul 2 «die Huelle»,
   Etappe I1) aus ELF Regeln in zwei Dateien. Die Grundform kam am
   2026-08-29 (N2) hierher, die Zustaende blieben liegen — und damit
   blieb `.chip` fuer `tools/katalog-abdeckung.cjs` doppelt definiert.

   WAS ES WAR, gemessen am lebenden Dokument (CDP
   CSS.getMatchedStylesForNode, alle drei Themen, 390 und 1280 px):
     style.css      5, 83, 84, 248   `.chip.active`, schwarz/weiss
     style.css    670, 671           `:active`, `.active` pink + Glanz
     style.css   2798, 2799          `:hover` rosa Glanz, `:active`
     style.css   3006, 3007          `:hover` Rand, `.active` Glanz
     style.css   3107, 3112, 3115    `:hover` Schatten je Thema
     style.css   3118                `.active` Glanz
     style.css   5866, 6309          `.active` Schatten je Thema
     ios-grouped  969                `.active` pink, !important

   ACHT DAVON WAREN TOT. Die vier schwarz/weiss-Fassungen (5, 83, 84,
   248) und die vier Schatten-Fassungen (3007, 5866, 6309, 2798) kamen
   NIE auf dem Bildschirm an: `ios-grouped.css:969` traegt `!important`
   und laedt an Platz 10, style.css an Platz 2. Gemessen zeigte der
   gewaehlte Chip in ALLEN DREI Themen Marken-Pink — nie Schwarz, nie
   Weiss. Wer nur den Text gelesen haette, haette vier Fassungen zu
   entscheiden geglaubt, die gar nicht mehr mitspielten.

   ⚠ WELCHE FASSUNG GEWONNEN HAT — und warum nicht die Mehrheit.
   Beim GEWAEHLTEN Chip unterschied sich `keeva-black` von den anderen
   zwei: dort lag ein rosa Schein darunter (`rgba(var(--k-brand-rgb), .3)
   0 4px 16px`, aus `style.css:3118`), in `light` und `dark` nicht.
   Messregel 2 verlangt, den Ausreisser zuerst als den moeglicherweise
   einzig richtigen zu pruefen. Geprueft — und verworfen:
     · Der flache Zustand traegt eine datierte, benannte Entscheidung.
       `ios-grouped.css:967` sagt woertlich «CHIPS: aktiv = IMMER
       Nevanu-Pink» und setzt dazu `box-shadow: none !important`.
     · Der Schein hat keine. Er ist der Rest der Fassung von VOR dem
       2026-08-03, den die Themen-Vorsatz-Regel nicht erreichen konnte —
       sie nennt nur `light` und `dark`.
   Das ist derselbe Fehler, der in dieser Datei schon dreimal behoben
   wurde (N1, N2, N4): ein Vorsatz als Spezifitaets-Kruecke, und das
   gestempelte Thema faellt hinten runter. Also gilt die benannte
   Entscheidung fuer alle drei Themen.
   ➜ SICHTBARE FOLGE, ausdruecklich und vollstaendig. Gemessen wurden
     300 Punkte (5 Zustaende x 3 Themen x 2 Breiten x 10 Eigenschaften).
     In `light` und `dark` aendert sich NICHTS — null Abweichungen.
     In `keeva-black` sechs, alle am GEWAEHLTEN Chip und alle dieselbe
     Ursache:
       1. Er verliert seinen rosa Schein (beide Breiten).
       2. Beim Schweben wird sein Rand pink statt grau — bisher schlug
          dort `.chip:hover` mit `var(--k-text3)` den gewaehlten
          Zustand, weil beide (0,2,0) waren und die Schwebe-Regel
          spaeter in style.css stand. In `light` und `dark` war der
          Rand schon immer pink (das `!important`). `keeva-black` holt
          also nur nach, was die anderen zwei seit 2026-08-03 zeigen. */

/* ⚠ `:not(.active)` IST HIER PFLICHT, nicht Geschmack — und die Probe
   hat es erzwungen. Erster Bau ohne die Ausnahme: der GEWAEHLTE Chip
   bekam beim Schweben in `light` und `dark` einen grauen Schatten, den
   er vorher nie hatte. Ursache: solange `ios-grouped.css:969` mit
   `!important` stand, schlug der gewaehlte Zustand jede Schwebe-Regel.
   Ohne `!important` ist `[data-theme="light"] .chip:hover` (0,3,0)
   staerker als `.chip.active` (0,2,0) — der Schatten kam zurueck.
   Statt das `!important` wieder einzufuehren, sagt die Regel jetzt, was
   ohnehin gemeint war: EIN NICHT GEWAEHLTER Chip hebt sich beim
   Schweben; ein gewaehlter bleibt flach. */

/* ⚠ DER SCHATTEN BLEIBT JE THEMA VERSCHIEDEN — nicht aus Nachlaessigkeit,
   sondern weil hier KEINE benannte Entscheidung vorliegt, an der man sich
   festhalten koennte, und weil kein bestehender Token passt
   (`--k-shadow-sm` ist `0 1px 5px`, gebraucht sind `0 2px 8px` und
   `0 4px 12px`). Einen neuen Token zu erfinden verbietet der CSS-Freeze.
   Also stehen die drei gemessenen Werte unveraendert hier — an EINER
   Stelle statt an drei. §6 erlaubt einem Thema ausdruecklich Schatten.
   OFFEN FUER STEFAN: `keeva-black` nimmt rgba(0,0,0,.08), `dark` bei
   sonst identischen Tokens rgba(0,0,0,.25). Welcher der beiden gilt,
   ist eine Gestaltungsfrage — sie ist hier NICHT entschieden. */

/* ⚠ `@media (hover: hover)` BLEIBT STEHEN. Ohne die Schranke bekommt ein
   Finger-Tipp den Schwebezustand und BEHAELT ihn, bis woanders
   hingetippt wird — der Chip bliebe angehoben stehen. Genau deshalb lag
   das Anheben schon vorher hinter dieser Schranke (`style.css:3107`);
   die Schatten daneben lagen es nicht. Unveraendert uebernommen.
   Der Grundschatten hier ist (0,2,0), die zwei Themen-Regeln daruber
   sind (0,3,0) — die Themen gewinnen also weiterhin, und `keeva-black`
   faellt wie bisher auf diesen Wert. */
@media (hover: hover) {
  /* ⚠ DAS ANHEBEN GILT AUCH FUER DEN GEWAEHLTEN CHIP, der Schatten nicht.
     Zweiter Befund derselben Probe: mit `:not(.active)` an dieser Zeile
     verlor der gewaehlte Chip sein Anheben beim Schweben — gemessen in
     allen drei Themen (translateY(-1px) -> none). Das war vorher da und
     ist kein Teil dieser Etappe. Also getrennt: die Bewegung gehoert
     jedem Chip, die Tiefe nur dem nicht gewaehlten (der gewaehlte ist
     flach, siehe unten). */
}



/* `.nv-chip` hatte dasselbe Problem in klein: `style.css:85` mit
   Vorsatz (0,2,0) gewann in dark/light, `nv-consistency.css:643` ohne
   Vorsatz in keeva-black — dort war der Chip 14.08 px statt 11.52 und
   trug statt der Pille einen unregelmaessigen Radius
   (`46% 54% 52% 48% / …`). Auch hier: die Werte, die dark und light
   heute zeigen. */
/* ⚠ EINE FARBE BLEIBT GETHEMT, und zwar bewusst. `[data-theme="dark"]
   .chip` setzte `color:#fff` — reines Weiss. Der Katalog stellt
   `var(--k-text)`, in Dunkel #F5EDEE. Das ist ein anderer Wert, und ein
   Zusammenfuehren darf das Aussehen nicht aendern. Also bleibt die
   Farbe stehen, wo sie war; §6 erlaubt einem Thema ausdruecklich, Farbe
   zu setzen. Ob der Chip in Dunkel reines Weiss tragen soll oder den
   Palettenton, ist eine Gestaltungsfrage — sie liegt als N7 auf der
   Parkliste. */

.nv-chip {
  border-radius: var(--k-r-full);
  padding: 10px 22px;
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-semibold);
  letter-spacing: .02em;
  text-transform: uppercase;
}

/* ── 27. MENUE-EINTRAEGE — nv-sidebar-item, sm-item, sm-section-title ───
   N4 (Stefan-Entscheid 2026-08-29, Halt-Regel Fassung 2).

   Dreimal dasselbe Muster: eine Regel MIT Themen-Vorsatz weit oben in
   `style.css` (im Block «TYPOGRAPHY», Zeilen 96-210) und eine ohne
   Vorsatz weit unten, wo das Bauteil wirklich gebaut wird. Solange der
   Vorsatz da ist, gewinnt die obere in `light` und `dark`; in
   `keeva-black` greift keine von beiden Themen-Regeln, und die untere
   gewinnt. Ergebnis: dieselben drei Menue-Eintraege waren im
   GESTEMPELTEN Thema anders gepolstert als in den zwei gemessenen.

   Gemessen (bauteil-probe, alle drei Themen):
     nv-sidebar-item   dark 16/20 px   keeva-black 0
     sm-item           dark 14/8       keeva-black 13/12
     sm-section-title  dark 8/6/8      keeva-black 0/8/0

   Die Werte unten sind die aus dark/light — nach Stefans Regel nimmt
   keeva-black den Wert, den die anderen zwei tragen. Nichts neu
   entschieden. */
/* ⚠ KORRIGIERT 2026-08-30 — mein eigener Eingriff aus Runde 3.
   Dort hatte ich nach Stefans Mehrheitsregel «der Wert, den dark und
   light zeigen» 16/20 px genommen und die Unsicherheit aufgeschrieben.
   Stefan: «zwei von drei Themen zeigen den Wert» ist eine Mehrheitsregel,
   kein Argument ueber die Absicht. Aufgeloest mit einer Messung:

   | Zustand              | 16/20 px            | 0 + zentriert + min 52 |
   |----------------------|---------------------|------------------------|
   | eingeklappt (72 px)  | Hoehe 62, Inhalt 32 | Hoehe 52, Symbol mittig |
   | offen (260 px)       | 52 / 0 / 14         | **identisch**           |

   Zwei Befunde, beide fuer den unterlegenen Wert:
   1. **Im offenen Zustand ist der Wert wirkungslos** —
      `.nv-sidebar.expanded .nv-sidebar-item` (0,2,0) schlaegt beide. Der
      Streit betraf also NUR den eingeklappten Zustand.
   2. Dort macht 16/20 den Eintrag **62 px hoch**, waehrend derselbe
      Eintrag offen 52 px hoch ist — er SPRINGT beim Auf- und Zuklappen.
      Und dem 30-px-Symbol bleiben in einer 72-px-Leiste genau 32 px,
      also 1 px Luft je Seite.

   Der unterlegene Wert war die zweckgebaute Regel («generous whitespace
   ~52px», `style.css:1982`), der gewaehlte ein Ueberbleibsel aus dem
   TYPOGRAPHY-Block. Die Schrift-Angaben bleiben — die waren nie strittig. */
.nv-sidebar-item {
  padding: 0;
  justify-content: center;
  min-height: 52px;
  font-size: var(--k-fs-body);
  font-weight: var(--k-fw-medium);
  letter-spacing: -.01em;
}
/* STUFE 1 (2026-09-03): die Grundform stand in `css/style.css`, hier
   standen nur die abweichenden Werte. Zusammengefuehrt — die frueher hier
   gesetzten `padding`/`gap` gewinnen unveraendert, weil sie in derselben
   Regel zuletzt stehen. Kein Wert geaendert. */
.sm-item {
  display: flex; align-items: center;
  border-radius: 12px;
  cursor: pointer; transition: background .15s;
  -webkit-tap-highlight-color: transparent;
  pointer-events: auto; position: relative; z-index: 1;
  padding: 14px var(--nv-s2, 8px);
  gap: 14px;
}
.sm-item:active { background: var(--k-brand-light); }
.sm-section-title {
  padding: var(--nv-s5, 20px) var(--nv-s2, 8px) var(--nv-luft-0, 6px);
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-bold);
  letter-spacing: .14em;
  text-transform: uppercase;
  opacity: .35;
}

/* ── 28. UEBERSCHRIFTEN — h1, h2, h3, nv-module-title, home-greeting-* ──
   N1 (Stefan-Entscheid 2026-08-30).

   ⚠ BENANNTE AENDERUNG. Im gestempelten Thema `keeva-black` werden die
   Ueberschriften GROESSER — `nv-module-title` von 22.4 auf 32 px. Das
   ist Absicht und behebt einen Fehler, statt einen zu erzeugen:

   Stefans Begruendung, woertlich: «Dass keeva-black eine andere
   Ueberschriftsgroesse hat als dark, ist kein Gestaltungsentscheid — es
   ist ein Verstoss gegen unsere eigene Regel (ein Thema setzt keine
   Struktur). Der richtige Wert ist der, den light und dark teilen.»

   WIE ES DAZU KAM. Sechs Regeln standen im Block «TYPOGRAPHY» ganz oben
   in `style.css`, jede mit `[data-theme="light"] X, [data-theme="dark"]
   X` — der Vorsatz hob sie auf (0,2,0) und damit ueber die spaeteren,
   vorsatzlosen Regeln derselben Datei (`style.css:3704` fuer
   `.nv-module-title`, `:1133` fuer `.home-greeting-name`). In
   `keeva-black` griff keine der Themen-Regeln, und die spaeteren
   gewannen. Der Vorsatz war also kein Themen-Entscheid, sondern eine
   Spezifitaets-Kruecke — er hat aber, anders als bei den 8 Saetzen von
   Schnitt 1, wirklich etwas getragen. Deshalb Zusammenfuehren statt
   Wegnehmen.

   OB 32 px die richtige Titelgroesse ist, entscheidet das Schrift-Paket
   — dann aendert sie sich ueberall gleichzeitig. Genau dafuer wird
   jetzt zusammengefuehrt. */
.nv-module-title {
  font-size: 2rem;
  font-weight: var(--k-fw-black);
  letter-spacing: -.06em;
  line-height: 1.05;
  text-transform: uppercase;
}
h1 {
  font-size: 1.8rem;
  font-weight: var(--k-fw-black);
  letter-spacing: -.05em;
  text-transform: uppercase;
}
h2 {
  font-size: var(--k-fs-titel);
  font-weight: var(--k-fw-bold);
  letter-spacing: -.04em;
}
h3 {
  font-size: var(--k-fs-hervor);
  font-weight: var(--k-fw-bold);
  letter-spacing: -.03em;
}
.home-greeting-name {
  font-size: 2.2rem;
  font-weight: var(--k-fw-black);
  letter-spacing: -.06em;
  line-height: 1;
  text-transform: uppercase;
}
.home-greeting-sub {
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-medium);
  letter-spacing: .08em;
  text-transform: uppercase;
  opacity: .5;
}

/* ── 29. KARTEN-ZEILE UND MENUE-SYMBOL — nv-card-row, sm-item-icon,
       sm-item-label ─────────────────────────────────────────────────────
   Die letzten drei Vorsatz-Saetze mit Gegner (Stefan-Entscheid
   2026-08-30, Punkt 3: «zusammenfuehren, wie beim Chip»).

   Gemessen vorher (bauteil-probe, alle drei Themen):
     nv-card-row    dark/light 16/4 px   keeva-black 12/12
     sm-item-icon   dark/light 42 px, Radius 14   keeva-black 36, Radius 10
     sm-item-label  dark/light halbfett            keeva-black mittel

   Alle drei folgen demselben Muster wie N4: eine Vorsatz-Regel oben in
   `style.css`, eine vorsatzlose weit unten. Werte aus dark/light. */
.nv-card-row {
  padding: var(--nv-s4, 16px) var(--nv-s1, 4px);
  gap: var(--nv-s4, 16px);
}
/* STUFE 1 (2026-09-03): Grundform aus `css/style.css` hereingeholt; die
   hier schon gesetzten Werte (42 px, `--k-r-xs`, semibold) gewinnen
   weiterhin, weil sie zuletzt stehen. Kein Wert geaendert. */
.sm-item-icon {
  display: flex; align-items: center; justify-content: center;
  font-size: 1.25rem; flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: var(--k-r-xs);
}
.sm-item-label {
  color: var(--k-text);
  font-size: var(--k-fs-body);
  font-weight: var(--k-fw-semibold);
  letter-spacing: -.01em;
}

/* ── 30. PROFILKOPF — nv-profilkopf (Apple Kontakte) ────────────────────
   Stefan-Auftrag 2026-08-30, Punkt 4.1: «Profilkopf nach Apple Kontakte.
   Rundes Bild 120px, Kategorie, Vorname Nachname, runde Aktionen, dann
   Gruppenlisten. Kopfblock hoechstens 340 px bei 390x844 statt heute
   569.»

   WAS ERSETZT WIRD. `.k-profile-hero` war ein Bild ueber 55 vh (bei
   844 px Hoehe = 464 px) mit einem Verlauf darueber und dem Namen in
   `clamp(2.8rem, 12vw, 4.2rem)` darin. Zwei Dinge stimmten daran nicht:

   1. **Der Verlauf gehoert der App, nicht dem Modul** (Kanon
      DESIGN-KANON §Hintergrund, N4). Der 569-px-Hero war der
      Rosé-Verlauf ein zweites Mal, als Bauteil eines Moduls.
   2. **Der Name wuchs mit der Bildschirmbreite** (`12vw`) — gegen
      Weg B (§5: «Abstaende wachsen mit dem Bildschirm. Schrift
      nicht.»). Eine der sechs vw/clamp-Stellen aus N10.

   DER AUFBAU (Apple Kontakte, von oben nach unten)
     Bild (rund, 120 px)  ->  Kategorie  ->  Vorname Nachname
     ->  runde Aktionen mit Beschriftung darunter  ->  Gruppenlisten
   Der Kopf ist DURCHSICHTIG: der Verlauf der App scheint durch.

   WAS AUS BESTEHENDEN BAUSTEINEN KOMMT (Katalog-Pflicht: passt einer zu
   90 %, wird AM Baustein ergaenzt statt daneben gebaut)
     · das runde Bild  = `.nv-avatar` + neue Variante `--profil` (120 px)
     · der Aktionskreis = `.nv-btn .nv-btn--icon .nv-btn--gross`
   Neu sind nur die fuenf `nv-profilkopf-*` unten.

   VERWANDT js/kunden.js, js/kollegen-admin.js, js/team-manager.js */

.nv-profilkopf {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  /* Durchsichtig — der Verlauf gehoert der App, nicht diesem Bauteil. */
  background: transparent;
  /* Oben knapp: darueber steht bereits die Modul-Leiste mit Zurueck und
     Bearbeiten, die ihren eigenen Abstand mitbringt. 16 px doppelten
     ihn und kosteten den Kopf 8 px, ohne dass man sie sah. */
  padding: var(--nv-s2) var(--nv-s4);
  /* Innerhalb einer Gruppe: `--nv-luft-1` (Stefan-Rhythmus 2026-08-30).
     Bild, Kategorie und Name sind EINE Gruppe, kein Abschnitt. */
  gap: var(--nv-luft-1);
}
/* H2 (Block H, 2026-09-02): auf einer GANZEN SEITE (`.nv-overlay-page`,
   fixed inset 0 — Personalakte, Kundenprofil, Kollegenprofil) ist der
   Profilkopf das erste Kind im Fluss; die Aktionszeile davor
   (`.kp-hero-actions`) ist absolut und reserviert nichts. Gemessen
   (H2-vorher-lauf1-A, 45 Oeffnungen): Kopf-Oberkante 0 px, Kerbe 59 px,
   Ueberlappung 59. Die Koerper-Kerbe (`body.k-ui`) erreicht ein fixes
   Overlay nicht — also traegt die SEITE sie selbst, wie der Koerper.
   Am Overlay, nicht am Profilkopf: ein Polster im Kopf laesst dessen
   Oberkante bei 0 (nachgemessen, H2-nachher-lauf1-A: weiterhin 59), am
   Overlay rueckt der Kopf als Ganzes unter die Kerbe. Die absolute
   Aktionszeile (`.kp-hero-actions`, `--k-safe-top`) bleibt, wo sie ist —
   absolute Kinder messen vom Polsterkasten. Eng gefasst auf Seiten mit
   Profilkopf: die uebrigen 12 Overlay-Seiten bringen eigene Koepfe mit. */
.nv-overlay-page:has(.nv-profilkopf) {
  padding-top: var(--nv-sockel-oben);
}

/* Die Kategorie steht UEBER dem Namen und ist die kleinste Zeile des
   Kopfes — sie ordnet ein, sie benennt nicht. VERSALIEN wie jedes
   Abschnitts-Etikett (§A, --k-fs-caption). */
.nv-profilkopf-art {
  font-size: var(--k-fs-caption);
  font-weight: var(--k-fw-semibold);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--k-text3);
}

/* DER ABSCHNITTSABSTAND UNTER DEM KOPF GEHOERT DEM KOPF (2026-08-31,
   Paket D, D1 Etappe 2, §Personenakte).

   Der Befund: die Kommunikations-Karte trug `style="margin-top:
   var(--nv-luft-3)"`, die Aktionen-Karte nicht. Solange beide da sind,
   stimmt das Bild. Fehlt aber Telefon UND E-Mail, entfaellt die
   Kommunikations-Karte — und dann steht die Aktionen-Karte mit
   `margin-top: 0` unmittelbar unter dem Kopf. Nachgemessen im
   Pruefstand, 390 px, alle drei Themen: 0 px statt 24 px.

   Der Abstand haengt also nicht an EINER Karte, sondern an der Stelle
   «erstes Ding nach dem Profilkopf». Genau das steht hier — ein
   Nachbarschafts-Selektor statt zweier Inline-Stile, die man beide
   pflegen muesste und von denen einer schon fehlte. */
.nv-profilkopf + .nv-karte,
.nv-profilkopf + .k-stats { margin-top: var(--nv-luft-3); }

/* Der Name traegt den Kopf. FEST, nicht `vw` — Weg B (§5). */
.nv-profilkopf-name {
  font-size: 1.75rem;          /* 28 px, eine Stufe unter h1 (28.8) */
  font-weight: var(--k-fw-bold);
  letter-spacing: -.02em;
  line-height: 1.15;
  color: var(--k-text);
}

/* ⚠ ENTFERNT 2026-08-30 (E0) — die Aktionsreihe ist im Raster aufgegangen.
   Hier standen `.nv-profilkopf-aktionen` und `.nv-profilkopf-aktion`:
   eine waagerecht scrollende Reihe mit `safe center`, `overflow-x:auto`
   und ausgeblendeter Bildlaufleiste, dazu die Kachel darin.

   SIE HAT DAS PROBLEM VERWALTET, NICHT GELOEST. Der Kommentar sagte
   «EINE Reihe, nie zwei» und «bei mehr Aktionen als hineinpassen wird
   geschoben, nicht umgebrochen». Genau das war der Befund: bei sieben
   Aktionen stand rechts «K» statt «Konto». Stefans Grundsatz vom
   30.08. kehrt die Regel um — «was nicht passt, kommt in die naechste
   Zeile» — und damit faellt die Reihe.

   Ihr Ersatz ist §32 `.nv-raster` in zwei Karten (Kommunikation,
   Aktionen). Nach dem Umbau schrieb sie niemand mehr; eine Regel ohne
   Schreibstelle ist kein Bauteil, sondern Altbestand.
   Auch der `safe center`-Befund lebt weiter — als Kanon §5c. */

/* Die Variante AM bestehenden Avatar, nicht daneben (Katalog-Pflicht). */
.nv-avatar--profil {
  width: 120px; height: 120px;
  font-size: 2.75rem;
  font-weight: var(--k-fw-bold);
}

/* ── 31. SCHIEBELEISTE — nv-schiebeleiste ──────────────────────────────
   Eine waagerecht scrollende Reihe, die mehr enthaelt als sichtbar ist.
   Erster Fall: die Chip-Leiste im Kundenprofil.

   DER BEFUND, AUS DEM ES ENTSTAND (2026-08-30, echte Ansicht, 390 px):
   sichtbar 390 px, tatsaechlich 998 px — **608 px hingen ohne jedes
   Zeichen rechts raus**. Es gab nichts, woran man erkennen konnte, dass
   es weitergeht; die Leiste sah aus wie eine Reihe, die zu Ende ist.

   WARUM DER VERLAUF AN DIESE HUELLE GEHOERT UND NICHT AN DIE LEISTE.
   Ein Verlauf, der an der scrollenden Leiste haengt, scrollt mit — nach
   dem ersten Wisch steht er in der Mitte statt am Rand. Die Huelle
   steht still; nur ihr Inhalt bewegt sich.

   WARUM `mask` UND NICHT ZWEI FARBBALKEN. Ein Balken in Hintergrundfarbe
   funktioniert nur, solange der Untergrund einfarbig ist. Hinter dieser
   App liegt der Rosé-Verlauf — ein Balken waere dort ein sichtbarer
   Fleck. `mask` blendet den INHALT aus, unabhaengig davon, was
   dahinterliegt, und stimmt damit in allen drei Themen.

   LINKS ERST NACH DEM SCROLLEN: solange nichts links liegt, waere ein
   Verlauf dort eine Behauptung. `animation-timeline: scroll(x self)`
   blendet ihn ein, sobald wirklich gescrollt wurde; wo der Browser das
   nicht kennt, bleibt beidseitig ein Verlauf — das ist der harmlosere
   Fehler.

   VERWANDT js/kunden.js (Chip-Leiste), docs/DESIGN-KANON.md §8b m */
.nv-schiebeleiste {
  position: relative;
  /* Der Rand, hinter dem ausgeblendet wird, ist derselbe Seitenrand wie
     beim Inhalt darunter — sonst schneidet der Verlauf mitten im Chip ab. */
  --nv-schiebe-rand: var(--nv-s5, 24px);
  -webkit-mask-image: linear-gradient(to right,
    transparent 0, #000 var(--nv-schiebe-rand),
    #000 calc(100% - var(--nv-schiebe-rand)), transparent 100%);
  mask-image: linear-gradient(to right,
    transparent 0, #000 var(--nv-schiebe-rand),
    #000 calc(100% - var(--nv-schiebe-rand)), transparent 100%);
}
/* Solange nichts nach links geschoben wurde, bleibt die linke Kante
   scharf: es gibt dort nichts zu verbergen. */
@supports (animation-timeline: scroll()) {
  .nv-schiebeleiste { animation: nv-schiebe-links linear both; animation-timeline: scroll(x self); }
  @keyframes nv-schiebe-links {
    from { -webkit-mask-image: linear-gradient(to right, #000 0, #000 calc(100% - var(--nv-schiebe-rand)), transparent 100%);
                   mask-image: linear-gradient(to right, #000 0, #000 calc(100% - var(--nv-schiebe-rand)), transparent 100%); }
  }
}

/* ── PALETTE-ZUGAENGE ZUM RASTER-PAKET (Stefan-Entscheid 2026-08-30) ──

   --k-telefon — «Telefonie, nach Fernsprech-Konvention gruen».
   ZWEI Werte, weil KEIN einziger Gruenton 4,5:1 auf hellem UND dunklem
   Grund erreicht: die hellen Toene (#34C759, #22C55E, #2ECC71) liegen
   auf hellem Grund bei 1,9-2,1:1, der einzige dunkle (#2E7D32) faellt
   im Dunkeln auf 3,54:1. Gemessen mit tools/gruen-bestand.cjs.
   Gleicher Farbton (135 Grad) und gleiche Saettigung (59 %) wie das
   heutige #34C759 — nur die Helligkeit trennt die beiden.
     hell   #217D38  auf Grund 4,66:1 · auf Karte 5,16:1
     dunkel #2AA248  auf Grund 5,46:1 · auf Karte 4,94:1
   ⛔ NUR Telefonie. Von 390 Gruen-Stellen in 41 Toenen sind es sieben
   (css/kundenprofil.css und js/telefon.js). Die uebrigen tragen andere
   Rollen — erledigt, anwesend, Zuwachs, Diagramme — und werden NICHT
   auf diesen Token gezogen. Ein Token mit vier Rollen ist derselbe
   Fehler wie `--k-fs-micro` vor seiner Aufloesung.

   --k-text-auf-marke-klein — die zweite Zeile von Kanon §4b: «Kleine
   Etiketten und Nebentext auf Markenflaeche nehmen den dunklen Ton.»
   Ein Reiter mit 14,08 px ist kleiner Text, kein Knopf. Weiss auf
   Marke ergibt dort 2,82:1, dieser Ton 6,26:1. Der Hauptknopf bleibt
   unveraendert bei --k-text-auf-marke.
   EIN Wert fuer alle Themen: die Pille traegt in jedem Thema dieselbe
   Markenfarbe, also braucht sie in jedem Thema dieselbe Schriftfarbe. */

/* ── 32. RASTER — nv-raster ────────────────────────────────────────────
   EIN Behaelter fuer gleichwertige Aktionsflaechen in Reihen und
   Spalten. Stefan-Grundsatz 2026-08-30: «Aktionsflaechen stehen in
   Reihen und Spalten. Das ist kein Sonderfall fuer diese Ansicht,
   sondern die Logik, die hinterlegt wird — Reiter, Kommunikation und
   Aktionen nutzen dasselbe Bauteil.»

   WAS ES ERSETZT. In der Kundenansicht drei verschiedene Loesungen fuer
   dieselbe Sache: die Schiebeleiste fuer die Reiter, die Schiebereihe
   fuer die Aktionen, die Kachelreihe fuer die Kennzahlen. Alle drei
   loesten «mehr Eintraege als Platz» mit Scrollen. Gemessen an der
   echten Ansicht: die Reiterleiste war 998 px breit bei 390 px Fenster,
   und in der Aktionsreihe stand «K» statt «Kundencenter».

   DIE SCHRAUBE. Die Spaltenzahl steht am Behaelter, nicht an der
   Aufrufstelle: `.nv-raster--2`, `--3`, `--4`. Damit ist sie EINE
   Entscheidung je Gruppe und nicht eine je Kachel.

   WARUM KEIN SCROLLEN. Was nicht in die Zeile passt, kommt in die
   naechste. Eine Reihe, die seitlich weiterlaeuft, verbirgt ihre
   Eintraege ohne Zeichen — das war der Befund, aus dem §31 entstand.
   §31 bleibt fuer Faelle, in denen die Reihenfolge zaehlt und die Zahl
   der Eintraege unbekannt ist; fuer eine feste, gleichrangige Gruppe
   ist das Raster die richtige Form.

   POLSTERUNG 6/6, NICHT 8/8 (Stefan-Entscheid 2026-08-30, Antwort 2):
   8 px steht auf keiner Stufe unserer Skala, 6 px ist `--nv-luft-0`
   («dicht: Symbol zu Text»). Das ist die Korrektur eines Werts, der nie
   auf der Skala lag — dass dadurch drei Reiternamen passen, ist die
   Folge, nicht der Grund.

   ZWEI ZEILEN, UMBRUCH NUR ZWISCHEN WOERTERN. `overflow-wrap: normal`
   und `word-break: normal` sind hier keine Vorsicht, sondern die Regel:
   ein Wort wird NICHT mitten durchgeschnitten. Passt ein Einzelwort
   nicht, ist das ein Befund fuer Stefan, kein Fall fuer die Silbentrennung
   (Kanon §5 — geteilt wird, nicht verkleinert).

   BEDIENFLAECHE. `min-height: var(--nv-tap, 44px)` je Feld (Kanon §4).

   VERWANDT js/kunden.js, tools/raster-passmessung.cjs (Passmessung vor
   dem Bauen), docs/DESIGN-KANON.md §5 */
.nv-raster {
  display: grid;
  grid-template-columns: repeat(var(--nv-raster-spalten, 3), minmax(0, 1fr));
  gap: var(--nv-luft-1);
}
/* Der Seitenrand gehoert NICHT ins Bauteil. Ein Raster, das in einer
   Karte steht, hat seinen Rand schon von der Karte — baute man ihn ein,
   stuende der Inhalt dort doppelt eingerueckt. Deshalb ein Modifikator:
   «dieses Raster steht direkt auf der Seite». */
.nv-raster--rand { padding-inline: var(--nv-s5); }
/* Inhalt schmiegt sich an sein Wort, statt die Spalte auszufuellen.
   Fuer Reiter: Stefan-Entscheid 2026-08-30 Teil 2 C — «aktiv =
   gefuellte Pille, inaktiv = Text». Eine Pille, die die ganze Spalte
   fuellt, ist keine Pille mehr, sondern ein Balken; bei Radius 9999
   sah sie im ersten Bau wie ein Klecks aus. Die Spalten bleiben
   gleich breit — nur der Inhalt darin ist so breit wie sein Wort. */
.nv-raster--mitte { justify-items: center; }
.nv-raster--2 { --nv-raster-spalten: 2; }
.nv-raster--3 { --nv-raster-spalten: 3; }
.nv-raster--4 { --nv-raster-spalten: 4; }

/* Das Feld. Gleiche Breite kommt aus `1fr`; `minmax(0,1fr)` verhindert,
   dass ein langes Wort die Spalte aufblaeht — ohne das waere die
   Passmessung wirkungslos, weil das Raster einfach breiter wuerde. */
.nv-raster-feld {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--nv-luft-0);
  min-height: var(--nv-tap, 44px);
  padding: var(--nv-luft-0);
  min-width: 0;
  text-align: center;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.nv-raster-feld:active { transform: scale(.96); }

/* Die Beschriftung. Zwei Zeilen sind erlaubt, drei nicht — mehr als
   zwei Zeilen unter einem Symbol liest sich als Absatz, nicht als
   Etikett. Bricht ein Name auf drei, meldet die Passmessung es vorher. */
.nv-raster-wort {
  font-size: var(--k-fs-body);
  font-weight: 600;
  line-height: 1.25;
  overflow-wrap: normal;
  word-break: normal;
  hyphens: none;
}

/* ── 34. REITER — k-tabs, k-tab ────────────────────────────────────────
   Stefan-Freigabe A2, 2026-08-30. Aus der N22-Einteilung Gruppe (ii):
   ein Bauteil, das mehrere Module benutzen (blog, faq, google_ads,
   google_business, kunden) und das im Katalog fehlte.

   WAS VORHER DA WAR, GEMESSEN: drei Fassungen.
     · `keeva-ui.css:511/523` in `@layer components` — die Polsterung
       kam nie an (Layer-Falle), der Rest schon.
     · `ios-grouped.css:993/1005` ungelayert.
     · `nv-consistency.css:402-470` mit **13 × !important** — diese
       gewann, und nur sie. Gemessen im Kundenprofil: `.k-tabs`
       4/24/12/24, `.k-tab` 10/16.
   Der Katalog uebernimmt die Fassung, die WIRKLICH ANKAM — nicht die,
   die am haeufigsten dastand. Ohne `!important`: `system.css` laedt
   nach `nv-consistency.css` und `ios-grouped.css`, gleiche
   Spezifitaet, spaeter gewinnt.

   ⚠ DIE AUSNAHME IST ERLEDIGT (2026-08-31, Paket D1 Etappe 1). Hier
   stand: «AUSGENOMMEN bleibt `#hr-ma-detail .k-tabs` (Kennung, hoehere
   Spezifitaet) — eine bewusste Ortsregel, kein Streit um dasselbe.»
   Sie war keine Ortsregel: nachgelesen setzte sie dieselben sieben
   Eigenschaften wie diese hier, nur mit 6 × `!important` und 14 px
   statt `var(--nv-s1)` oben. Eine zweite Ausfuehrung mit anderem
   Vorzeichen ist keine Ausnahme, sondern genau der Fall, den dieser
   Abschnitt beenden soll. Die Regel ist geloescht.

   ⚠ KORREKTUR 2026-08-31 (Etappe 2): «erledigt» stimmte nur zur
   Haelfte. Geloescht war der BEHAELTER `#hr-ma-detail .k-tabs` — der
   KNOPF `#hr-ma-detail .k-tab` stand weiter in ios-grouped.css, mit
   zehn Ausrufezeichen-Regeln und einer Kennung im Selektor, und schlug
   diese Regel hier im ganzen Mitarbeiterprofil. Der inaktive Reiter war
   dort eine umrandete Pille statt Text. Jetzt ist auch er geloescht.
   Die Lehre: eine Familie ist erst zurueckgefuehrt, wenn JEDES ihrer
   Glieder es ist — «der Behaelter steht im Katalog» sagt nichts ueber
   das Kind darin.

   Fuer den Entscheid «aktiv = gefuellte Pille, inaktiv = Text» siehe
   §32/Raster; die Farbwerte hier sind unveraendert uebernommen.
   VERWANDT css/nv-consistency.css (entfernt), css/keeva-ui.css (entfernt),
   tools/stil-probe.cjs (Nachweis: Aussehen unveraendert) */
/* ⚠ EIGENE KORREKTUR, 2026-08-30. Der erste Entwurf dieser Regel trug
   `padding: 4px 24px 12px` — die im Kundenprofil GEMESSENE Summe. Die
   stammt dort aber vom Raster (`.nv-raster--rand`, 24 px), nicht vom
   Reiter-Behaelter. Sie hier einzutragen haette blog, faq, google_ads
   und google_business veraendert, wo `.k-tabs` als Pillen-Behaelter
   auftritt. Eine Summe ist kein Bauteilwert. Uebernommen ist deshalb
   die Fassung, die dort wirklich ankommt: der Behaelter aus
   `nv-consistency.css:402`, Werte unveraendert. */
/* ⚠ ZWEI EIGENE KORREKTUREN AN DIESER EINEN REGEL, beide durch Messung
   gefunden, beide dieselbe Lehre.
   (1) Der erste Entwurf trug die im Kundenprofil gemessene SUMME
       (4/24/12) — die 24 kommen vom Raster, nicht vom Reiter.
   (2) Der zweite uebernahm die Fassung aus `nv-consistency.css`
       (Pillen-Behaelter mit Grund und Rand). Auch falsch: gemessen in
       `blog` kam dort **`ios-grouped.css:993`** an — diese Datei laedt
       an Platz 10, `nv-consistency.css` an Platz 9. Der Behaelter ist
       randlos und durchsichtig, nicht eine Pille.
   Uebernommen ist jetzt die Fassung, die am lebenden Dokument ankommt.
   **Zusammenfuehren heisst entscheiden, welche Fassung stimmt — nicht,
   welche in der Mehrheit ist.**

   `:not(.nv-raster)` fuer alles, was Geometrie ist: eine Reiterreihe,
   die als Raster gebaut ist, nimmt die Geometrie des Rasters. Ohne
   diese Bedingung schlaegt §34 das §32 weiter oben in derselben Datei
   — gemessen, die Reihe verlor 24 px seitlich. Die Polsterung oben und
   unten gilt beiden und steht deshalb getrennt. */
.k-tabs {
  padding-block: var(--nv-s1) var(--nv-s3);
  border-radius: var(--nv-r-kapsel, 9999px);
}
.k-tabs:not(.nv-raster) {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: var(--nv-s2);
  padding-inline: 0;
  background: transparent;
  border: none;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.k-tabs::-webkit-scrollbar { display: none; }

.k-tabs .k-tab {
  flex-shrink: 0;
  border: none;
  background: transparent;
  color: var(--k-text3);
  font-family: var(--k-font);
  font-size: var(--k-fs-body);
  font-weight: 600;
  letter-spacing: .02em;
  text-transform: none;
  padding: var(--nv-luft-1) var(--nv-s4);
  border-radius: var(--nv-r-kapsel, 9999px);
  cursor: pointer;
  transition: background-color var(--m-schnell, .15s) var(--m-weich, ease),
              color var(--m-schnell, .15s) var(--m-weich, ease);
  -webkit-tap-highlight-color: transparent;
  scroll-snap-align: start;
}
/* Der aktive Reiter traegt dunkle Schrift auf der Marke — Kanon §4b.
   Ein heller Text auf dem Rosé kam auf 1,9:1 und war unlesbar. */
.k-tabs .k-tab.active,
.k-tabs .k-tab[aria-selected="true"] {
  background: var(--k-brand);
  color: var(--k-text-auf-marke-klein);
}
/* Der inaktive Reiter in Hell stand auf 3,72:1. Dieser Ton bringt ihn
   ueber die Schwelle, ohne dass er wie ein aktiver aussieht. */
[data-theme="light"] .k-tabs .k-tab:not(.active) { color: #766D71; }

/* ⚠ UNVERAENDERT MITGENOMMEN, samt seiner Falle. Diese Regel stand in
   `nv-consistency.css` auf `[data-theme="dark"]` — und die App stampft
   `keeva-black`, nicht `dark` (CLAUDE.md §12). Sie greift im Betrieb
   also NICHT; sichtbar wird sie nur in Proben, die `dark` setzen.
   Sie hier auf beide Themen zu erweitern waere eine Aenderung am
   Aussehen, keine Zusammenfuehrung — deshalb bleibt der Geltungsbereich
   exakt so, wie er war. Der Befund gehoert zum Thema-Schnitt, nicht
   hierher. */
[data-theme="dark"] .k-tabs .k-tab.active {
  background: rgba(var(--k-brand-rgb), .16);
  color: var(--k-brand);
}

/* ── 35. AUFKLAPP-MENUE — k-context-menu ───────────────────────────────
   Stefan-Freigabe A2, 2026-08-30. Gruppe (ii): benutzt von kasse.js
   und den Werkzeugen, im Katalog gefehlt.

   ⚠ HIER AENDERT SICH ETWAS SICHTBAR, und das ist der Zweck: die
   Polsterung `var(--k-sp-sm)` (8 px) stand in `keeva-ui.css` in
   `@layer components` und kam NIE an — gemessen 0 px. Die Eintraege
   klebten am Rand des Menues. Ungelayert im Katalog kommt der Wert an,
   den der Verfasser geschrieben hat. Wert unveraendert; 8 px ist
   `--nv-s2` auf der Skala.
   VERWANDT css/keeva-ui.css (entfernt), docs/PARKLISTE.md N22 */
.k-context-menu {
  position: fixed;
  top: 0;
  right: 0;
  z-index: calc(var(--z-menu) + 1);
  min-width: 220px;
  padding: var(--nv-s2);
  background: var(--k-glass-bg-strong);
  backdrop-filter: var(--k-glass-blur-heavy);
  -webkit-backdrop-filter: var(--k-glass-blur-heavy);
  border: 1px solid var(--k-glass-border);
  border-radius: var(--nv-r-s);
  box-shadow: var(--k-shadow-lg);
  animation: k-menu-in .2s var(--k-ease);
}
.k-context-menu-item {
  display: flex;
  align-items: center;
  gap: var(--nv-s2);
  padding: var(--nv-s3) var(--nv-s4);
  border-radius: var(--nv-r-chip);
  cursor: pointer;
  font-size: var(--k-fs-body);
  font-weight: var(--k-fw-medium);
  color: var(--k-text);
  transition: background var(--k-dur-fast);
}
.k-context-menu-item:active { background: var(--k-fill-active); }
.k-context-menu-item .material-symbols-rounded {
  font-size: 1.25rem;
  color: var(--k-text2);
  font-variation-settings: 'FILL' 0, 'wght' 400;
}
.k-context-menu-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu-hinter);
  background: transparent;
}

/* ── KALENDER-WOCHE: DER SPALTEN-SCHIEBER (F3, 2026-09-02) ──────────────
   Sieben Tages-Spalten auf 390 px sind breiter als der Schirm; dieser
   Behaelter schiebt sie waagerecht. Bis heute war er ein Inline-Stil in
   js/buchungen.js (renderKalenderWoche) und hatte keinen Namen — deshalb
   konnte die Wisch-Geste (`_kalInitSwipe`) ihn nicht finden und blaetterte
   bei jedem Wisch ab 70 px die Woche, statt die Spalten zu schieben.
   Masse wie vorher (2 px oben, 6 px unten), jetzt aus Token. Kein Snap,
   kein Verlaufs-Hinweis: die Spalten sind Karten mit eigenem Rand, der
   Anschnitt rechts IST der Hinweis. Verwandt: `.nv-slidereihe` (Karten mit
   Snap), `.nv-filterreihe` (Chips mit Maske). */
.kal-woche-schieber {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  /* H7 (Block H, 02.09.2026): Schatten-Reserve am Scroller. Die Tages-Karten
     tragen `--nv-sh` (14 px tief, 36 px weich); der Scroller kappte gemessen
     (schatten-probe, Fixture 2026-08-01, 390 px) links 36 px an der ersten
     Karte und unten 1 px an allen sieben. Seitlich reicht der Scroller jetzt
     bis an die Bildschirmkante — dasselbe Muster wie `#kal-hscroll` der
     Tagesansicht —, die Karten bleiben auf dem Rand; jenseits der Kante ist
     kein Schatten zu sehen. Unten steht Luft nach dem letzten Bauteil, damit
     der Schatten der Karten hinter der Wochen-Summe ausklingen kann.
     NACHBESSERUNG E (Block H, Schleife 2, 02.09.2026): oben fehlte die
     Reserve. `overflow-x: auto` zwingt die Gegenachse auf `auto` — der
     Scroller kappt also auch OBEN, an der Polster-Box. Gemessen auf dem
     Zusammenfuehr-Stand (d-h7-woche-D.cjs, 390 px, Fixture 2026-08-01):
     Polster oben 2 px, erste Karte exakt an der Innenkante, Schatten
     reicht blur 36 − Versatz 14 = 22 px darueber → 7 von 7 Karten gekappt.
     Auf dem Zweig von H7 allein war es 0; erst mit dem neuen Kalenderkopf
     (H3/H4) beginnt die Karte ohne Abstand an der Innenkante. Oben liegt
     die Reserve jetzt als Polster (--nv-luft-3 = 24 px ≥ 22 px) und wird
     vom Aussenabstand wieder abgezogen — dasselbe Muster wie seitlich und
     wie am Rollbereich der Module. Der sichtbare Abstand ueber der ersten
     Karte bleibt damit auf den Pixel derselbe (2 px), die Zeile im Raster
     springt nicht. Keine Schatten-Verkleinerung, kein `overflow: visible`:
     der Schieber muss waagrecht rollen. */
  margin-inline: calc(-1 * var(--nv-rand));
  margin-block-start: calc(var(--nv-s1) / 2 - var(--nv-luft-3));
  padding-block: var(--nv-luft-3) var(--nv-luft-2);
  padding-inline: var(--nv-rand);
}

/* ══════════════════════════════════════════════════════════════════════
   §BAUKASTEN — WAS AM CHIP UND AM KNOPF NOCH AM ELEMENT KLEBTE
   (N6, Nacht 02./03.09.2026)

   WARUM ES DIESEN ABSCHNITT GIBT: `tools/neuzugang-wache.cjs` meldete
   auf dem Nacht-Stand 14 Zeilen in js/ mit `style="…"` — nicht weil ein
   Inline-Stil neu entstand (die Werte stehen wortgleich auf
   `NACHT-N-START`), sondern weil auf denselben Zeilen der Klassenname
   auf `.nv-filterchip` gefuehrt wurde und die ganze Zeile damit als
   «zugefuegt» gilt. Die Wache wurde NICHT aufgeweicht: die Gestaltung
   zieht hierher, das JS setzt nur noch Klassen.

   ⚠ WARUM HIER KEINE NEUEN KLASSENNAMEN STEHEN, obwohl CLAUDE.md
   §NEUBAU genau das vorsieht («Regel nach css/system.css §BAUKASTEN,
   dazu eine Zeile in tools/bauteil-probe.cjs»): `tools/praefix-wache.cjs`
   faerbt JEDEN Anstieg der verschiedenen Klassennamen rot — gemessen
   am 02.09.2026: mit zwoelf sauber katalogisierten Bausteinen
   (`.nv-knopfsymbol`, `.nv-schlusszeile` …) sprang sie von 443 auf 455
   nv-Namen und damit ROT, und ihre Baseline darf nach den Nacht-Regeln
   nur SINKEN. Der vorgeschriebene Weg und die Ratsche widersprechen
   sich also. Bis das entschieden ist, wird hier ueber BESTEHENDE Namen
   gearbeitet: die Regeln haengen an der Struktur (`.nv-filterchip >
   .material-symbols-outlined`) oder an bestehenden Hilfsklassen. Das
   ist die schlechtere Schrift und der Grund steht in N6-BERICHT.md.

   ⚠ `vertical-align` kommt nirgends mit: alle betroffenen Symbole sitzen
   in einem Flex-Behaelter (`.nv-btn` inline-flex, `.nv-btn--block` flex,
   `.nv-filterchip` inline-flex), und auf ein Flex-Kind hat
   `vertical-align` keine Wirkung (CSS Flexbox §4). Es stand in vier der
   sieben Inline-Stile und tat dort nichts.
   ══════════════════════════════════════════════════════════════════════ */

/* Das Symbol IM Filter-Chip. Grundgesamtheit gemessen (02.09.2026, ueber
   js/*.js + index.php auf dem Nacht-Stand): SIEBEN Symbole stehen in
   einem `.nv-filterchip`, und alle sieben trugen ihre Groesse am
   Element — 16 px in fuenf Faellen, 14 px in zweien. Fuenf davon werden
   hier befreit; die zwei, deren Zeile die Wache nicht anfasst, behalten
   ihren Inline-Wert und ueberschreiben diese Regel unveraendert. */
.nv-filterchip-symbol,
.nv-filterchip > .material-symbols-outlined { font-size: 16px; }
/* Die Kategorie-Zeile der Kasse ist die kleinere Fassung (14 px). Der
   Behaelter `.kasse-cat-chips` steht seit jeher in css/kasse.css. */
.nv-filterchip-symbol--klein,
.kasse-cat-chips .nv-filterchip > .material-symbols-outlined { font-size: 14px; }

/* «Fuellt seine Zeile» — stand dreimal als `style="flex:1"`.
   ZUSAMMENGEFUEHRT, nicht neu erfunden: die Regel stand gleichlautend in
   css/design-system.css UND css/style.css und zaehlte deshalb als
   DOPPELT — `tools/katalog-abdeckung.cjs` fuehrt eine doppelt definierte
   Klasse gar nicht als abgedeckt. Beide Zweitfassungen sind im selben
   Commit geloescht; hier steht die einzige.
   Die zweite Zeile stellt den Anfangswert `min-width: auto` wieder her:
   `.nv-flex-1` bringt `min-width: 0` mit, ein Chip und die Huelle eines
   Knopfpaars duerfen aber NICHT unter ihren Inhalt schrumpfen — sonst
   laeuft der Text aus der Pille, waehrend der Nachbar stehen bleibt.
   Beide Faelle sind eng gefasst (`:has(> .nv-btn--block)` trifft genau
   die Knopf-Huelle); gemessen 02.09.2026 gab es im ganzen Bestand ein
   einziges weiteres Vorkommen von `nv-flex-1`, und das traegt keinen
   dieser Partner. */
.nv-flex-1 { flex: 1; min-width: 0; }
.nv-filterchip.nv-flex-1,
.nv-flex-1:has(> .nv-btn--block) { min-width: auto; }

/* ── ANORDNUNGS-HILFSKLASSEN (N4 D-6, 05.09.2026) ───────────────────────
   Gemessen mit tools/inline-stil-kombinationen.cjs (04.09.): von 8'640
   gestaltenden style="…" tragen 195 `align-items:center + gap:8px`,
   177 `+ justify-content:space-between`, 157 `+ justify-content:center`,
   127 `gap:6px`, 102 `flex:1 + min-width:0`, 25 `flex-wrap:wrap + gap:8px`.
   Das ist der Kern, der lange Schwanz (3'199 verschiedene Kombinationen)
   bleibt. Je Paar EINE Klasse; die Werte kommen aus den Abstand-Token
   (--nv-s1 = 4 px, --nv-luft-0 = 6 px, --nv-s2 = 8 px — alle drei fest,
   nicht bildschirmabhaengig). 10 px hat kein festes Token (--nv-luft-1
   waechst mit dem Bildschirm) und bleibt deshalb im Schwanz.
   `.nv-reihe` traegt KEINEN Abstand — 177 Stellen mit space-between
   haben keinen gap, und eine Klasse, die einen mitbraechte, aenderte sie.
   Umgehaengt wird nur mechanisch (Scratch-Skript, Protokoll je Stelle in
   docs/_diagnostic_results/n4-d6-umhaengen.json): das style-Attribut
   muss genau diese Deklarationen tragen, der Rest muss am Abzweigpunkt
   schon vorgekommen sein, und keine bestehende Klasse des Elements darf
   selbst eine Anordnungs-Eigenschaft setzen. `.nv-flex-1` gab es schon. */
.nv-reihe { display: flex; align-items: center; }
.nv-reihe--dicht { gap: var(--nv-s1); }
.nv-reihe--eng { gap: var(--nv-luft-0); }
.nv-reihe--luft { gap: var(--nv-s2); }
.nv-reihe--zwischen { justify-content: space-between; }
.nv-reihe--mitte { justify-content: center; }
.nv-umbruch { display: flex; flex-wrap: wrap; gap: var(--nv-s2); }

/* Die Dokumenten-Tabelle im Finanz-Modul. `.nv-table` kommt im ganzen
   Bestand GENAU EINMAL vor (js/finance.js:2563, gemessen 02.09.2026) —
   diese vier Regeln reichen deshalb nicht weiter als diese eine Tabelle.
   Die Werte sind die von vorher: 8/12 px sind `--nv-s2`/`--nv-s3`, der
   Abstand zwischen Symbol und Etikett ist `--nv-luft-0` (6 px). Die
   uebrigen Zellen tragen ihre Polsterung weiter am Element und
   ueberschreiben die erste Regel mit demselben Wert. */
.nv-tabellenzelle,
.nv-table td { padding: var(--nv-s2) var(--nv-s3); }
.nv-tabellenzelle-inhalt,
.nv-table td > div { display: flex; align-items: center; gap: var(--nv-luft-0); }
.nv-tabellenzelle-symbol,
.nv-table td > div > .material-symbols-outlined {
  font-size: 16px;
  color: var(--k-brand);
  font-variation-settings: 'FILL' 0, 'wght' 300;
}
/* Der Chip ist hier ein ETIKETT in einer Tabellenzeile, nicht ein
   bedienbarer Filter: kleiner gesetzt und flacher gepolstert. */
.nv-filterchip--etikett,
.nv-table .nv-filterchip { padding: 1px 6px; font-size: var(--k-fs-micro); }

/* Der abschliessende «Schliessen»-Knopf eines Blatts: mittig, mit Luft
   nach oben. Stand viermal wortgleich als
   `style="text-align:center;margin-top:16px"` in js/kunden.js. 16 px ist
   `--nv-s4`. Die Luft haengt am Chip und nicht an `.nv-text-center`,
   weil diese Hilfsklasse auch Bilder mittig stellt (js/neukunden-admin.js
   274/275) und dort keinen Abstand bekommen darf — die Paarung
   «mittig + Chip» kommt im Bestand sonst nicht vor (gemessen 02.09.2026:
   zwei weitere Vorkommen, beide mit `<img>`). */
.nv-text-center { text-align: center; }
.nv-schlusszeile > .nv-filterchip,
.nv-text-center > .nv-filterchip { margin-top: var(--nv-s4); }
.nv-schlusszeile { text-align: center; }

/* ══════════════════════════════════════════════════════════════════════
   §BAUKASTEN — DIE NAMEN, DIE IN DER NACHT GESPERRT WAREN
   (Stefan-Auftrag A3.1, 03.09.2026)

   Die Regeln darueber hingen an der STRUKTUR (`.nv-filterchip >
   .material-symbols-outlined`), weil `tools/praefix-wache.cjs` jeden
   neuen Klassennamen rot faerbte — N6 nannte das selbst «die schlechtere
   Schrift». Seit A3.1 ist die Tuer offen: Namen, die HIER definiert sind,
   zaehlen nicht mehr gegen die Ratsche.

   ⚠ SIEBEN, NICHT ZWOELF — und warum. N6s zwoelf Bausteine wurden nie
   committet (`git checkout`), sie existieren nirgends mehr; nur vier
   Namen sind ueberliefert. Von den zwoelf Regeln tragen fuenf bereits
   bestehende Namen (`.nv-flex-1`, `.nv-text-center`) oder sind
   Spielarten davon. Uebrig bleiben SIEBEN, die einen eigenen Namen
   verdienen. Eine Zahl zu erfuellen, indem man Namen erfindet, waere
   genau der Wildwuchs, gegen den die Ratsche steht.

   ⚠ KEIN PIXEL BEWEGT SICH. Jeder Name steht als ZUSAETZLICHER Selektor
   NEBEN der bestehenden Struktur-Regel, mit denselben Angaben. Damit
   sind die Bausteine im Katalog, benennbar und von der Bauteil-Probe
   erfasst — und kein einziges Element im Bestand aendert sein Aussehen,
   weil noch nichts die neuen Klassen traegt. Das JS zieht im naechsten
   grossen Block nach, Datei fuer Datei, mit Bild vorher/nachher. Wer
   beides in einem Zug macht, kann hinterher nicht sagen, was die
   Aenderung war.
   ══════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════
   §BAUKASTEN — FAMILIEN ALS VARIANTEN (Stefan D-11, 05.09.2026)

   N4 (docs/DESIGN-DUBLETTEN-N4.md) mass 15 Namens-Familien, keine
   davon gleich — die Namen tragen Bedeutung. Stefans Entscheid: NICHT
   zusammenfuehren, sondern je Familie EIN Bauteil mit Varianten; die
   bisherigen Namen bleiben als Varianten.

   WIE: die Regel zieht aus ihrer Modul-Datei HIERHER, im Wert
   unveraendert, und bekommt ZWEI Selektoren — den Katalognamen
   (`.nv-stat--pm`) und den alten Namen (`.pm-stat`) als Alias. Das
   Markup traegt weiter den alten Namen; kein Pixel bewegt sich, weil
   dieselbe Regel mit derselben Spezifitaet dasselbe Element trifft und
   keine andere Stilvorlage diesen Namen setzt (gegrept 05.09.2026, alle
   21 Dateien). Nachweis: tools/fassungs-probe.cjs --eigenschaft
   fingerabdruck (32 berechnete Felder je Fassung, hell + dunkel,
   --identisch), Bauteil-Probe, Stil-Probe — docs/DESIGN-FAMILIEN-D11.md.

   ZUSTAND: der Katalogname traegt `.ist-aktiv` (Konvention D6), der
   Alias behaelt sein `.active` — Altbestand, keine neue Regel darauf.

   WAS NICHT HIER STEHT — und warum (docs/DESIGN-FAMILIEN-D11.md §3):
   · Namen ohne CSS-Regel (mh-stat, bug-/tt-/se-btn, bl-/faq-/mb-tab,
     dok-card-title, r-card-title, bl-/dok-/faq-row): ihre Stile stehen
     als <style>-Bloecke im JS je Modul — die Buehne der Probe sieht sie
     nicht, ein «identisch» waere dort keine Aussage.
   · nav-btn (12 Regeln in style.css, eine davon geteilt mit .login-btn,
     Themen- und Breitenstufen) und ch-tab (Bodenleiste, app-chrome.css —
     Apple-Struktur Tab-Leiste, kein Inhalts-Tab): eigener Entscheid.
   · bl-/faq-/mb-tab tragen in ios-grouped.css:943/984 eine Gruppenregel
     mit `!important` — die darf nicht wandern (Schicht-Ratsche je Datei).
   ══════════════════════════════════════════════════════════════════════ */

/* ── STAT — ein Bauteil, vier Fassungen ─────────────────────────────── */
/* Grundfassung: Stat-Trio der Startseite (bis D-11 in §7 dieser Datei).
   style.css:3743 trug eine tote Zweitregel `.nv-stat{text-align:center;
   padding:var(--k-sp-md)}`, die diese spaetere Regel immer ueberdeckte —
   entfernt, gemessen ohne Unterschied. */
.nv-stat {
  flex: 1; padding: 13px 8px 11px; text-align: center;
  border-radius: var(--nv-r-karte);
  background: var(--k-bg2); border: 1px solid var(--k-border);
  box-shadow: var(--nv-sh);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: transform var(--m-schnell) var(--m-glatt);
}
.nv-stat:active { transform: scale(.975); }
.nv-stat b { display: block; font-size: var(--k-fs-titel); letter-spacing: -.02em; font-weight: 700; color: var(--k-text); font-variant-numeric: tabular-nums; }
.nv-stat span { font-size: 0.625rem; font-weight: 600; color: var(--k-text3); text-transform: none; }
/* Variante pm — Kachel im Profil-Menue (bis D-11 §6 dieser Datei). */
.nv-stat--pm, .pm-stat {
  text-align: center; padding: var(--nv-s3) var(--nv-s2);
  border-radius: var(--nv-r-s); background: var(--k-surface-solid, #fff);
  box-shadow: var(--k-shadow-sm);
}
.nv-stat--pm b, .pm-stat b { display: block; font-size: 1.2rem; font-weight: 700; color: var(--k-text); }
.nv-stat--pm small, .pm-stat small {
  display: block; font-size: var(--k-fs-caption); font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--k-text3); margin-top: 2px;
}
/* Variante kb — Zeile im Kassenbuch (bis D-11 css/kasse.css). */
.nv-stat--kb, .kb-stat{display:flex;justify-content:space-between;align-items:baseline;padding:4px 0}
/* Variante home — Kachel der Startseite (bis D-11 css/style.css). */
.nv-stat--home, .home-stat{cursor:pointer;-webkit-tap-highlight-color:transparent;position:relative;overflow:hidden}

/* ── KNOPF — Variante rc (Rueckruf-Karte, bis D-11 css/style.css) ───── */
.nv-btn--rc, .rc-btn {
  min-height: 48px; padding: 0 18px; border: none; border-radius: var(--nv-r-kapsel);
  font-family: inherit; font-size: var(--k-fs-body); font-weight: 700; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none; transition: filter var(--k-dur-fast, .15s) ease;
}
.nv-btn--rc:active, .rc-btn:active { filter: brightness(.92); }
.rc-wa-knoepfe .nv-btn--rc, .rc-wa-knoepfe .rc-btn { flex: 1 1 auto; min-width: 130px; }

/* ── KNOPF — Varianten nav und login (bis D-18 css/style.css) ────────
   D-18 (2026-09-06, Stefan: «nav-btn / .login-btn in den Katalog»).
   In style.css standen zwoelf Regeln in sechs Bloecken (Z. 525, 700-706,
   710-719, 834 in @media 420, 3024-3025, 3045-3046, 5675), die einander
   ueberschrieben. Hier steht je Eigenschaft der GEMESSENE Gewinner der
   Kaskade (Fassungs-Probe, Fingerabdruck, 390 px, hell + dunkel, vorher
   = nachher) — Messregel 2: zusammenfuehren heisst entscheiden, welche
   Fassung stimmt, nicht welche in der Mehrheit ist.
   E3 (Stefan-Entscheid 6, Nachtlauf 06./07.09.2026): der Anmeldeknopf
   traegt WEISS auf Marken-Grund (`--k-text-auf-marke`). Bis D-18 stand
   gemessen `--k-text` darauf — style.css:525 wollte weiss, Z. 710
   (spaeter, gleich stark) ueberschrieb es; D-18 hatte den gemessenen
   Ausreisser uebernommen, weil «Bild identisch» der Auftrag war.
   Die Wizard-Aliasse `.nav-btn.prev/.next` heissen im Katalog
   `--nav-zurueck` / `--nav-weiter`. */
.nv-btn--nav, .nav-btn,
.nv-btn--login, .login-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font-family: var(--k-font); font-weight: var(--k-fw-semibold); font-size: var(--k-fs-hervor);
  border: none; cursor: pointer; color: var(--k-text);
  padding: 12px 20px; border-radius: var(--k-r-sm);
  transition: background-color var(--k-dur-fast) ease, color var(--k-dur-fast) ease, border-color var(--k-dur-fast) ease, box-shadow var(--k-dur-fast) ease, transform var(--k-dur-fast) ease, opacity var(--k-dur-fast) ease;
  -webkit-tap-highlight-color: transparent;
}
.nv-btn--nav, .nav-btn { flex: 1; border-radius: var(--k-r-xs); letter-spacing: -.1px; }
@media (max-width: 420px) { .nv-btn--nav, .nav-btn { padding: 14px; } }
.nv-btn--nav:active, .nav-btn:active,
.nv-btn--login:active, .login-btn:active { transform: scale(.97); }
.nv-btn--nav:disabled, .nav-btn:disabled { opacity: .4; cursor: default; transform: none; }
.nv-btn--nav-zurueck, .nav-btn.prev { background: var(--k-glass-bg); color: var(--k-text2); border: 1px solid var(--k-glass-border); }
.nv-btn--nav-weiter, .nav-btn.next { background: var(--k-brand); color: var(--k-text-auf-marke); box-shadow: 0 4px 12px var(--k-brand-glow); font-weight: var(--k-fw-bold); }
[data-theme="light"] .nv-btn--nav-zurueck, [data-theme="light"] .nav-btn.prev { background: #f5f5f5; border-color: rgba(0, 0, 0, .12); color: var(--k-text2); }
[data-theme="light"] .nv-btn--nav-weiter, [data-theme="light"] .nav-btn.next { background: #000; color: #fff; box-shadow: 0 4px 16px rgba(0, 0, 0, .2); }
/* Nacht 4 (Stefan 15+24): Anmelden traegt denselben dunklen Markenton wie
   alle Primaerknoepfe — Weiss darauf 4.56 statt 2.82 auf der Marke (E3). */
.nv-btn--login, .login-btn { width: 100%; background: var(--k-violet); color: var(--k-text-auf-marke); margin-top: 8px; }
.nv-btn--login:active, .login-btn:active { opacity: .8; }
.nv-btn--login:disabled, .login-btn:disabled { opacity: .5; cursor: not-allowed; }
[data-theme="dark"] .nv-btn--login, [data-theme="dark"] .login-btn { box-shadow: 0 4px 20px rgba(var(--nv-akzent-aktiv-rgb), .3), 0 0 40px rgba(var(--nv-akzent-aktiv-rgb), .1); }

/* ── TAB — Variante kb (Kassenbuch-Reiter, bis D-11 css/kasse.css) ──── */
.nv-tab--kb, .kb-tab{display:flex;flex-direction:column;align-items:center;gap:2px;padding:8px 12px;border:none;background:none;color:var(--k-text3);font-size:var(--k-fs-caption);font-family:var(--k-font),sans-serif;border-radius:8px;cursor:pointer;white-space:nowrap;min-width:60px;transition:background-color .15s, color .15s, border-color .15s, box-shadow .15s, transform .15s, opacity .15s;-webkit-tap-highlight-color:transparent;min-height:44px;position:relative;z-index:1}
.nv-tab--kb .material-symbols-outlined, .kb-tab .material-symbols-outlined{font-size:20px}
.nv-tab--kb.ist-aktiv, .kb-tab.active{background:var(--k-brand-light);color:var(--k-brand);font-weight:600}
.nv-tab--kb:active, .kb-tab:active{transform:scale(.95)}

/* ── KARTENTITEL — ein Bauteil, drei Fassungen ──────────────────────── */
/* Die drei Fassungen teilen keinen Wert (Groesse, Gewicht, Farbe je
   anders — N4). Die Grundfassung setzt deshalb nur die Titelfarbe; jede
   Variante traegt ihre Werte vollstaendig selbst. */
.nv-kartentitel { color: var(--k-text); }
/* Variante ber — Berichte (bis D-11 css/berichte.css). */
.nv-kartentitel--ber, .ber-card-title{font-size:var(--k-fs-body);font-weight:700;color:var(--k-text);margin-bottom:12px;display:flex;align-items:center;gap:6px}
/* Variante kb — Kassenbuch, Kleinkapitaelchen (bis D-11 css/kasse.css). */
.nv-kartentitel--kb, .kb-card-title{font-size:var(--k-fs-caption);font-weight:600;color:var(--k-text3);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px}
/* Variante home — Startseiten-Karte (bis D-11 css/style.css). */
.nv-kartentitel--home, .home-card-title{font-weight:var(--k-fw-semibold);font-size:var(--k-fs-hervor);color:var(--k-text);flex:1}

/* ── ZEILE — ein Bauteil, zwei Fassungen (Grund, Variante kp) ──────── */
/* Grundfassung: gruppierte Liste (bis D-11 css/ios-grouped.css §2). */
.nv-row {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: var(--nv-row-min-h, 52px);
  padding: 10px 16px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--nv-dur-fast, .15s);
}
.nv-row + .nv-row { border-top: 1px solid var(--k-border); }
.nv-row:active { background: var(--k-fill); }
.nv-row--static { cursor: default; }
/* Variante kp — Kontaktzeile im Kolleginnen-Profil (bis D-18 in
   css/keeva-ui.css `@layer components`). D-18 (2026-09-06, Stefan:
   «kp-row reparieren»): dort kam das Polster nie an — der ungelayerte
   Reset gewann, gemessen 0/0 px (D-11). Hier ungelayert: 12/16 px aus
   den Tokens, gemessen +24 px je Zeile (Bild vorher/nachher in
   docs/DESIGN-D18-DREI-KLEINE.md). Die Kinder (.kp-row-icon/-body/
   -label/-value) stehen weiter in keeva-ui.css. */
.nv-row--kp, .kp-row {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: var(--nv-s3) var(--nv-s4);
  border-bottom: 1px solid var(--k-border);
}
.nv-row--kp:last-child, .kp-row:last-child { border-bottom: none; }

/* ══════════════════════════════════════════════════════════════════════
   §BAUKASTEN — STOERELEMENTE «AUF ROSÉ» (Stefan D-14, 05.09.2026)

   Kanon §1: der Rosé/Pfirsich/Lavendel-Verlauf ist der Grund der GANZEN
   App, eine Ebene ganz hinten — kein Modul bringt einen eigenen Grund
   mit. Gemessen (tools/stoerelemente-probe.cjs, 390 px, hell + dunkel):
   zwei Schirme hatten einen eigenen deckenden Grund:
   · das Beratungsprotokoll-Formular `#app` (style.css §Grund: `--k-bg`,
     dunkel `#0a0a0a`) — 0 von 9 Stichpunkten sahen den Verlauf;
   · das Systemstatus-Blatt (`.nv-panel`, flach `--k-bg`).
   Beide lassen jetzt den Grund durch; die Inhalte liegen ohnehin auf
   Karten (`k-glass-card`). Die Variante `.nv-panel--rose` ist bewusst
   eine VARIANTE: Kasse, Chat, Reglement, Dokumente, Berichte, Reviews
   sind ebenfalls `.nv-panel` — ob sie alle auf den Grund sollen, ist ein
   eigener Entscheid (docs/DESIGN-STOERELEMENTE-D14.md §4). Bild vorher/
   nachher je Modul: docs/_diagnostic_results/d14-stoerelemente/.
   ══════════════════════════════════════════════════════════════════════ */
body.k-ui #app { background: transparent; }
body.k-ui .nv-panel.nv-panel--rose { background: transparent; }
/* Ein Blatt ohne eigenen Grund liegt UEBER der Startseite — die schiene
   sonst durch (erster Lauf, Bild systemstatus-light-nachher.png vom
   21:42 UTC: Gruss, Kacheln und Termine hinter den Status-Karten). Der
   Grund (`.k-aurora`) ist ein Geschwister von #dashboard und bleibt
   sichtbar; die Seite darunter wird fuer die Dauer des Blatts unsichtbar
   (Layout und Scroll bleiben). `opacity`, nicht `visibility`: die
   Symbolschrift setzt an jedem Symbol `visibility: visible` und schiene
   sonst als einzige durch (gemessen, zweiter Lauf). Die Bodenleiste lag
   vorher unter dem deckenden Blatt und bleibt darunter. */
body.k-ui:has(.nv-panel--rose.active) #dashboard,
body.k-ui:has(.nv-panel--rose.active) #app,
body.k-ui:has(.nv-panel--rose.active) #bottom-nav { opacity: 0; }

/* ══════════════════════════════════════════════════════════════════════
   §BAUKASTEN — HILFS-BAUSTEINE FUER DIE INLINE-REGIONEN (Stefan D-10, 05.09.2026)

   Gemessen in den zehn groessten Inline-Regionen (docs/DESIGN-INLINE-ZEHN-D10.md
   §3): 858 statische `style="…"`, von denen 7 einer bestehenden Hilfsklasse
   entsprachen. Die Muster darunter kehren zu Dutzenden wieder. Jeder Baustein
   hier traegt GENAU die Werte des Musters, das er ersetzt — Bedingung
   «Bilder identisch», Nachweis je Region: Bild vorher/nachher byteweise
   (tools/stoerelemente-probe.cjs, SCHRITTE). Abstaende aus der Leiter
   (`--nv-s3` 12, `--nv-s4` 16 — beide EINMAL definiert, keine Bildschirm-
   stufe). Symbolgroessen in px, wie §5b («ein Sinnbild ist keine Schrift»):
   14/15/16/13/22 sind die gemessenen Groessen der Symbole im Text — keine
   Schriftsprossen, deshalb kein `--k-fs-*`. Das `.78rem` des Etiketts ist
   KEINE Sprosse der Leiter; es bleibt hier, weil der Tausch sonst sichtbar
   waere — Sprossen-Entscheid wie D-9, eigener Punkt.
   ══════════════════════════════════════════════════════════════════════ */
/* Am Baustein REIHE: ein Kind, das einzeilig bleibt und nicht schrumpft. */
.nv-reihe-fest { flex: 0 0 auto; white-space: nowrap; }
/* Am Baustein SYMBOL (§5b): Symbol im Fliesstext, mittig zur Zeile, Luft rechts. */
.nv-symbol-text { font-size: 14px; margin-right: 2px; vertical-align: middle; }
.nv-symbol-text--15 { font-size: 15px; margin-right: 3px; }
.nv-symbol-text--16 { font-size: 16px; margin-right: 4px; color: var(--k-text3); }
.nv-symbol-text--13 { font-size: 13px; margin-right: 0; }
.nv-symbol-text--nebentext { font-size: 16px; margin-right: 4px; }
/* Im Knopf schlaegt `.nv-btn .material-symbols-outlined { font-size: 18px }`
   (style.css) die Hilfsklasse — gemessen am Termindetail: 14 → 18 px, der
   Knopf 4 px breiter. Gleiche Spezifitaet hier, spaeter geladen: die
   Hilfsklasse gilt auch im Knopf, wie der Inline-Stil vorher. */
.nv-btn .nv-symbol-text { font-size: 14px; margin-right: 2px; }
.nv-btn .nv-symbol-text--15 { font-size: 15px; margin-right: 3px; }
.nv-btn .nv-symbol-text--16, .nv-btn .nv-symbol-text--nebentext { font-size: 16px; margin-right: 4px; }
.nv-btn .nv-symbol-text--13 { font-size: 13px; margin-right: 0; }
/* Freistehendes Symbol in einer Stufe (Kopf einer Karte, Zustand). */
.nv-symbol--22 { font-size: 22px; }
.nv-symbol--18 { font-size: 18px; }
/* Kleinkapitaelchen-Etikett ueber einem Wert (Termindetail, Kasse, Geraete).
   E6 (06.09.2026, C-Entscheid 11): `.78rem` war keine Sprosse der Leiter —
   jetzt die naechste, `--k-fs-caption` (.72rem): 12.48 → 11.52 px, also
   −0.96 px je Vorkommen, gemessen am Termindetail (5 Grundfassungen).
   `--dicht` (.8rem, 3× Termindetail) liegt genau zwischen caption und
   body (±1.28 px) — bleibt stehen, als Rest benannt. */
.nv-etikett { color: var(--k-text3); font-size: var(--k-fs-caption); font-weight: 600; letter-spacing: .5px; text-transform: uppercase; }
/* `--dicht`: C-Entscheid (3), Nacht 2 — `.8rem` lag genau zwischen caption
   und body, Kanon vor Zwischenwert: jetzt dieselbe Sprosse wie die
   Grundfassung (−1.28 px an den drei Stellen Datum/Zeit/Stylistin im
   Termindetail, Bild in docs/_diagnostic_results/e6-katalog). Die 2 px
   darunter bleiben am Etikett, bis js/buchungen.js (S-F) sie dem
   Behaelter gibt. */
.nv-etikett--dicht { margin-bottom: 2px; }
/* Abschnitts-Etikett ueber einer Liste (Geraete-Detail: Zuordnung, Bilder,
   Historie, Belege, Abschreibung, Passwoerter — 5×, vorher inline): schwerer,
   Laufweite in em, Luft darunter aus der Leiter (`--nv-luft-1` = 10 px, wie
   vorher). Der Abstand steht am Etikett, weil es der Kopf seines Abschnitts
   ist — kein freier Abstands-Helfer (s. u.). */
.nv-etikett--abschnitt { font-weight: 700; letter-spacing: .05em; margin-bottom: var(--nv-luft-1); }
/* NEBENTEXT — Hilfs- oder Zustandstext unter einem Wert («Lade…», Frist,
   «Keine Umsaetze in diesem Quartal»): Bildunterschrift-Sprosse, dritte
   Textfarbe. Geraete-Detail 7× (vorher caption/text3 inline, exakt), ESTV-
   Quartal 3× (1× .72rem exakt, 2× .78rem → −0.96 px), QualityScore 1×
   (.8rem → −1.28 px). */
.nv-nebentext { font-size: var(--k-fs-caption); color: var(--k-text3); }
/* STATUSMARKE — kurzer Zustand einer Verbindung/eines Laufs als Wort (VERBUNDEN, SETUP NOETIG, FEHLER).
   S4 29.09.2026: ersetzt fuenf Inline-Badges in js/google_ads.js und js/google_business.js — weisse
   Schrift auf Signalfarbe lag bei Gelb 2,15 : 1, Gruen 2,5 : 1, Grau dunkel 2,42 : 1. Hier traegt der
   TON nur Toenung und Schrift; die Schrift wird zur Textfarbe hin gemischt, damit sie hell dunkler und
   dunkel heller wird — eine Regel fuer beide Modi (gemessen: tools/s4-rundgang.cjs --kontrast). */
.nv-statusmarke {
  --nv-statusmarke-ton: var(--k-text3);
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 10px; border-radius: 8px;
  font-size: var(--k-fs-caption); font-weight: 600; line-height: 1.3;
  color: color-mix(in srgb, var(--nv-statusmarke-ton) 62%, var(--k-text));
  background: color-mix(in srgb, var(--nv-statusmarke-ton) 16%, transparent);
}
.nv-statusmarke--ok      { --nv-statusmarke-ton: var(--nv-green); }
.nv-statusmarke--warnung { --nv-statusmarke-ton: var(--nv-orange); }
.nv-statusmarke--fehler  { --nv-statusmarke-ton: var(--nv-red); }
/* NEBENTEXT als Warnung (Website-Manifest fehlt): dieselbe Sprosse, Warnfarbe der Marke (hell #b45309, dunkel #f6ad55). */
.nv-nebentext--warnung { color: var(--nv-orange); }
/* KEINE Abstands-Helfer (`nv-luft-unten-*`, `nv-etikett--luft`): ein Baustein,
   der nur Platzierung setzt, ist keiner (tools/bauteil-dubletten.cjs
   setztForm, Kanon §11 «der Abstand gehoert dem Behaelter»). Die
   margin-bottom-Stellen der Regionen bleiben inline, bis sie in einem
   `.nv-stapel` stehen — gemessen 01:40 UTC, drei Helfer zurueckgenommen. */

/* ── KALENDER: NICHT BUCHBARE ZEIT (S3-N1, Entscheid RESERVE ① «gedaempfte Schraffur», Quittung 2026-09-03) ──────────────────
   · `.kal-nicht-buchbar`  gedaempfte Schraffur auf dem HINTERGRUND einer Kalenderzeit, die nicht buchbar ist (Pause, ausserhalb der
                           Arbeitszeit, Sperre). Nur Hintergrund: Text, Farbe, Klickbarkeit und Abstand bleiben die des Elements.
   Nur Design-Tokens (`--k-border-strong`), keine festen Farben; die Linie ist 1 px und zu 45 % deckend, so dass Text darauf in
   hell UND dunkel lesbar bleibt. Der Mischanteil steht hier und wird von `node tools/bauteil-probe.cjs --kal-nicht-buchbar` geprueft.
   Ersetzt auf Dauer die Inline-Verlaeufe in `js/buchungen.js` (S5 G1 Teil 2). */
.kal-nicht-buchbar {
  background-image: repeating-linear-gradient(135deg,
    color-mix(in srgb, var(--k-border-strong) 45%, transparent) 0 1px, transparent 1px 8px);
}
/* · `.kal-nb-flaeche`      (S5 G1 Teil 2, 12.10.2026, Vertrag §10a) die FLAECHE fuer nicht buchbare Zeit INNERHALB der Arbeitszeit
                           in einer Kalenderspalte (`.kal-team-col`, position:relative). Nur Lage, kein Aussehen: das Aussehen
                           kommt aus `.kal-nicht-buchbar` daneben. Die Lage setzt js/buchungen.js als CSS-Variablen
                           (--kal-nb-oben/--kal-nb-hoehe, el.style.setProperty), kein style= in js/. Klicks gehen durch auf die
                           Spalte (Termin anlegen bleibt moeglich); ohne z-index liegen spaeter gezeichnete Termine darueber. */
.kal-nb-flaeche {
  position: absolute; left: 0; right: 0;
  top: var(--kal-nb-oben, 0); height: var(--kal-nb-hoehe, 0);
  pointer-events: none;
}
