/* Erzeugt mit kern-holen.mjs aus Markenpaket und Kern 1.15.4: Tokens, Fokus, Knöpfe, Überzeile,
   Grundlagen, Formulare. Ohne Typografie, Container und Seitenaufbau des Kerns (Eigenheit E12). Nicht von Hand ändern. */
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 400;
  font-stretch: 100%;
  font-display: swap;
  src: url(schrift/nunito-sans-a0f2b136.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 500;
  font-stretch: 100%;
  font-display: swap;
  src: url(schrift/nunito-sans-a0f2b136.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 600;
  font-stretch: 100%;
  font-display: swap;
  src: url(schrift/nunito-sans-a0f2b136.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 700;
  font-stretch: 100%;
  font-display: swap;
  src: url(schrift/nunito-sans-a0f2b136.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 800;
  font-stretch: 100%;
  font-display: swap;
  src: url(schrift/nunito-sans-a0f2b136.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* TFCZ · tokens.css, nur die Tokens. Für bestehende Seiten mit eigenem CSS-Gerüst
   (Tailwind, Next.js). Wer die Bausteine braucht, bindet marke.css ein. Nicht von Hand ändern. */

:root{
  
  --space-3xs: 0.125rem;
  --space-2xs: 0.25rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;
  --space-3xl: clamp(2.5rem, 6vw, 4rem);
  --space-4xl: clamp(3rem, 8vw, 6rem);
  --space-5xl: clamp(3.5rem, 10vw, 8rem);

  
  --r-xs: 6px;
  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 8px;
  --r-xl: 8px;
  --r-pill: 999px;

  
  --font-display: 'Bricolage Grotesque', 'Bricolage Ersatz', 'Arial Narrow', 'Helvetica Neue', Arial, sans-serif;
  --font-ui: 'Bricolage Grotesque', 'Bricolage Ersatz', 'Arial Narrow', 'Helvetica Neue', Arial, sans-serif;
  --font-data: 'Bricolage Grotesque', 'Bricolage Ersatz', 'Arial Narrow', 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;

  
  --text-2xs: 0.71875rem;
  --text-xs: 0.78125rem;
  --text-sm: 0.84375rem;
  --text-md: 0.96875rem;
  --text-lg: 1.0625rem;
  --text-xl: 1.3125rem;
  --text-2xl: clamp(1.5rem, 3.2vw, 1.875rem);
  --text-3xl: clamp(1.875rem, 4.6vw, 2.625rem);
  --text-4xl: clamp(2.375rem, 6.4vw, 3.875rem);
  --text-5xl: clamp(2.875rem, 8.8vw, 6rem);

  
  --leading-tight: 1.04;
  --leading-snug: 1.28;
  --leading-normal: 1.62;

  
  --tracking-tight: -0.035em;
  --tracking-snug: -0.015em;
  --tracking-normal: 0;
  --tracking-wide: 0.06em;

  
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;
  --fw-extrabold: 800;
  --fw-knopf: var(--fw-semibold);

  
  --t-sofort: 80ms;
  --t-fast: 150ms;
  --t-base: 240ms;
  --t-slow: 480ms;
  --t-lang: 1200ms;
  --ease: cubic-bezier(.2,.7,.2,1);
  --ease-in-out: cubic-bezier(.4,0,.2,1);
  --ease-ankunft: cubic-bezier(.3,1.3,.5,1);

  
  --surface-state: color-mix(in oklab, var(--ink) 15%, transparent);
  --treffer: color-mix(in oklab, var(--accent) 34%, transparent);
  --papier: Canvas;

  
  --accent-2: var(--accent);
  --accent-2-lift: var(--accent-lift);
  --accent-2-ink: var(--accent-ink);
  --accent-2-hover: var(--accent-hover);

  
  --wrap: 73.75rem;
  --wrap-text: 68ch;
  --gutter: clamp(1.25rem, 4vw, 3.5rem);
  --ctl-h-sm: 2.25rem;
  --ctl-h: 2.75rem;
  --ctl-h-lg: 3.25rem;
  --tap-min: 44px;
  --border-w: 1px;
  --border-w-stark: 2px;
  --icon: 1.125rem;
  --icon-sm: 0.875rem;
  --menu-w: 14rem;
  --leiste-w: 13rem;
  --panel-w: 18.75rem;
  --row-h: 2rem;
  --label-w: 7.75rem;
  --dock-w: min(38rem, 42vw);
  --textbox-h: 14rem;
  --kal-stunde: 3rem;
  --wertkante: calc(var(--space-sm) + var(--border-w));
  --section-y: clamp(3rem, 8vw, 6rem);

  
  --z-unter: -1;
  --z-ueber: 1;
  --z-klebend: 2;
  --z-klebend-zeile: 4;
  --z-klebend-kopf: 6;
  --z-klebend-ecke: 7;
  --z-griff: 8;
  --z-deckt: 9;
  --z-leiste: 20;
  --z-blatt: 30;
  --z-schwebend: 40;
  --z-zuruf: 50;
  --z-aufgeklappt: 60;
  --z-ziehen: 90;

  
  --bp-sm: 560px;
  --bp-md: 720px;
  --bp-lg: 900px;
  --bp-xl: 1024px;
}

/* ------------------------------------------------------------
   Die Marke: TFCZ
   Grundfläche #2B69A6 · Akzent #005A94 · Schrift Nunito Sans
   Rundung: TFCZ (4 / 8 / 12 / 16 / 22 px) · Modus: hell und dunkel, folgt der Einstellung des Geräts
   ------------------------------------------------------------ */
html:root{
  --font-display: 'Nunito Sans', 'Helvetica Neue', Arial, sans-serif;
  --font-ui: 'Nunito Sans', 'Helvetica Neue', Arial, sans-serif;
  --font-data: 'Nunito Sans', 'Helvetica Neue', Arial, sans-serif;
  --r-xs: 4px;
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 22px;
  --fw-knopf: 800;
  --surface-base: #2B69A6;
  --surface-raised: #2A66A2;
  --surface-overlay: #255D95;
  --border-subtle: #5287BD;
  --border-strong: #C3D8EE;
  --ink: #FFFFFF;
  --ink-muted: #EEF4FA;
  --ink-faint: #E6EFF8;
  --accent: #FFFFFF;
  --accent-lift: #FFFFFF;
  --accent-soft: #255D95;
  --accent-soft-lift: #2A66A2;
  --accent-ink: #0D273D;
  --accent-hover: #E3EEF8;
  --state-pass: #C2F2CF;
  --state-fail: #FFDDD8;
  --state-warn: #FFE2A6;
  --focus: #E9C475;
  --ton-1: #CFE3F5;
  --ton-2: #C9EBCF;
  --ton-3: #F4DFB3;
  --ton-4: #F7D2C6;
  --ton-5: #EBCFE8;
  --ton-6: #C2EDEC;
  --accent-2: #E9C475;
  --accent-2-lift: #F0D08A;
  --accent-2-hover: #F4D99B;
  --accent-2-ink: #0D273D;
  color-scheme: light;
}
@media (prefers-color-scheme: dark){
  html:root:not([data-mode="light"]){
    --surface-base: #0D273D;
    --surface-raised: #122436;
    --surface-overlay: #16304A;
    --border-subtle: #333F4A;
    --border-strong: #75828F;
    --ink: #EEF4FA;
    --ink-muted: #C3D2E0;
    --ink-faint: #A5ACB2;
    --accent: #5CA7DC;
    --accent-lift: #69B4E9;
    --accent-soft: #263747;
    --accent-soft-lift: #304252;
    --accent-ink: #0D273D;
    --accent-hover: #6FBAF0;
    --state-pass: #67B57D;
    --state-fail: #FF7F73;
    --state-warn: #E49B2C;
    --focus: #CDA857;
    --ton-1: #7FB0DD;
    --ton-2: #89B992;
    --ton-3: #C4A674;
    --ton-4: #D59B89;
    --ton-5: #C69AC2;
    --ton-6: #6FBAB9;
    --accent-2: #CDA857;
    --accent-2-lift: #DAB564;
    --accent-2-hover: #E1BB6A;
    --accent-2-ink: #0D273D;
    color-scheme: dark;
  }
}
html:root[data-mode="dark"]{
  --surface-base: #0D273D;
  --surface-raised: #122436;
  --surface-overlay: #16304A;
  --border-subtle: #333F4A;
  --border-strong: #75828F;
  --ink: #EEF4FA;
  --ink-muted: #C3D2E0;
  --ink-faint: #A5ACB2;
  --accent: #5CA7DC;
  --accent-lift: #69B4E9;
  --accent-soft: #263747;
  --accent-soft-lift: #304252;
  --accent-ink: #0D273D;
  --accent-hover: #6FBAF0;
  --state-pass: #67B57D;
  --state-fail: #FF7F73;
  --state-warn: #E49B2C;
  --focus: #CDA857;
  --ton-1: #7FB0DD;
  --ton-2: #89B992;
  --ton-3: #C4A674;
  --ton-4: #D59B89;
  --ton-5: #C69AC2;
  --ton-6: #6FBAB9;
  --accent-2: #CDA857;
  --accent-2-lift: #DAB564;
  --accent-2-hover: #E1BB6A;
  --accent-2-ink: #0D273D;
  color-scheme: dark;
}
html:root[data-mode="light"]{
  --surface-base: #2B69A6;
  --surface-raised: #2A66A2;
  --surface-overlay: #255D95;
  --border-subtle: #5287BD;
  --border-strong: #C3D8EE;
  --ink: #FFFFFF;
  --ink-muted: #EEF4FA;
  --ink-faint: #E6EFF8;
  --accent: #FFFFFF;
  --accent-lift: #FFFFFF;
  --accent-soft: #255D95;
  --accent-soft-lift: #2A66A2;
  --accent-ink: #0D273D;
  --accent-hover: #E3EEF8;
  --state-pass: #C2F2CF;
  --state-fail: #FFDDD8;
  --state-warn: #FFE2A6;
  --focus: #E9C475;
  --ton-1: #CFE3F5;
  --ton-2: #C9EBCF;
  --ton-3: #F4DFB3;
  --ton-4: #F7D2C6;
  --ton-5: #EBCFE8;
  --ton-6: #C2EDEC;
  --accent-2: #E9C475;
  --accent-2-lift: #F0D08A;
  --accent-2-hover: #F4D99B;
  --accent-2-ink: #0D273D;
  color-scheme: light;
}
[data-modus-fest="dunkel"]{
  --surface-base: #0D273D;
  --surface-raised: #122436;
  --surface-overlay: #16304A;
  --border-subtle: #333F4A;
  --border-strong: #75828F;
  --ink: #EEF4FA;
  --ink-muted: #C3D2E0;
  --ink-faint: #A5ACB2;
  --accent: #5CA7DC;
  --accent-lift: #69B4E9;
  --accent-soft: #263747;
  --accent-soft-lift: #304252;
  --accent-ink: #0D273D;
  --accent-hover: #6FBAF0;
  --state-pass: #67B57D;
  --state-fail: #FF7F73;
  --state-warn: #E49B2C;
  --focus: #CDA857;
  --ton-1: #7FB0DD;
  --ton-2: #89B992;
  --ton-3: #C4A674;
  --ton-4: #D59B89;
  --ton-5: #C69AC2;
  --ton-6: #6FBAB9;
  --accent-2: #CDA857;
  --accent-2-lift: #DAB564;
  --accent-2-hover: #E1BB6A;
  --accent-2-ink: #0D273D;
  color-scheme: dark;
}
[data-modus-fest="hell"]{
  --surface-base: #2B69A6;
  --surface-raised: #2A66A2;
  --surface-overlay: #255D95;
  --border-subtle: #5287BD;
  --border-strong: #C3D8EE;
  --ink: #FFFFFF;
  --ink-muted: #EEF4FA;
  --ink-faint: #E6EFF8;
  --accent: #FFFFFF;
  --accent-lift: #FFFFFF;
  --accent-soft: #255D95;
  --accent-soft-lift: #2A66A2;
  --accent-ink: #0D273D;
  --accent-hover: #E3EEF8;
  --state-pass: #C2F2CF;
  --state-fail: #FFDDD8;
  --state-warn: #FFE2A6;
  --focus: #E9C475;
  --ton-1: #CFE3F5;
  --ton-2: #C9EBCF;
  --ton-3: #F4DFB3;
  --ton-4: #F7D2C6;
  --ton-5: #EBCFE8;
  --ton-6: #C2EDEC;
  --accent-2: #E9C475;
  --accent-2-lift: #F0D08A;
  --accent-2-hover: #F4D99B;
  --accent-2-ink: #0D273D;
  color-scheme: light;
}

/* ---------------------------------------------------------------- Fokus */
/* Der Fokusring wird INNEN gezeichnet, nicht aussen. Aussen liegend wuchs er
   in die Nachbarn hinein und legte sich auf Beschriftungen (Vasco,
   2026-09-24: „vor allem überlappt es Text"). Innen liegend kann er das
   grundsätzlich nicht — unabhängig davon, wie eng ein Baustein gesetzt ist.
   Die Regel gilt überall gleich; kein Baustein bringt eine eigene mit. */
:focus-visible {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
  /* Kein eigener Radius: der Ring folgt der Form des Elements. Ein fester
     Wert liesse ihn an runden Dingen die Ecken abschneiden. */
}
:focus:not(:focus-visible) { outline: none; }


.ueberzeile { font-size: var(--text-2xs); font-weight: var(--fw-semibold); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--ink-muted); }
/* ------------------------------------------------------------ Knöpfe
   Rangfolge über Fülle, nicht über Grösse (Runde 3, C3): der wichtige Knopf
   ist gefüllt, der zweite trägt nur eine Kante. Der Verlauf ist die einzige
   Stelle im System, an der es überhaupt einen gibt (Runde 2). */
.btn {
  font-family: var(--font-ui);
  /* Runde 7, B2: der Knopf stand auf 13.5px/500 und war damit kleiner als der
     Fliesstext daneben. Jetzt auf dessen Augenhöhe und fester. Das Gewicht
     steht hier als Grundeinstellung, nicht in einem Zustandsselektor — es
     schiebt also nichts (siehe „Keine Pushes"). */
  font-size: var(--text-md);
  /* Eigenes Token seit 2026-10-19: eine Marke darf ihre Knöpfe kräftiger setzen als den übrigen
     halbfetten Text (TFCZ: 800, Vasco «ein bisschen mehr Dicke»). Ohne Wert der Marke halbfett. */
  font-weight: var(--fw-knopf);
  min-height: var(--ctl-h);
  /* Runde 7, G2: 16px seitlich liess den Haupt-CTA wie einen Eintrag in einer
     Werkzeugleiste aussehen. Die Höhe bleibt bei 44 — das ist die Tastfläche
     nach WCAG, für Finger und Maus dieselbe, darum kein zweiter Wert für
     Mobil. */
  padding-inline: var(--space-lg);
  border-radius: var(--r-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Symbol und Wort brauchen Luft. Ohne gap klebt das Zeichen am Text —
     gemessen am 2026-09-24 bei jedem Knopf mit Symbol (Vasco: „der icon
     klebt ja am text"). display:inline-flex allein reicht nicht, gap
     steht sonst auf „normal", und das ist null. */
  /* Runde 7, S2: 4px war die Reparatur vom 2026-09-24 („der icon klebt ja am
     text"), nicht die Entscheidung. 8px ist sie. */
  gap: var(--space-xs);
  cursor: pointer;
  /* Ein <a class="btn"> erbte sonst die Unterstreichung der Links — ein Knopf
     mit Unterstrich sieht falsch aus (Vasco, 2026-09-24). Gemessen: a.btn
     stand auf underline, button.btn auf none. */
  text-decoration: none;
  background: linear-gradient(to bottom, var(--accent-lift) 0%, var(--accent) 100%);
  color: var(--accent-ink);
  border: var(--border-w) solid var(--accent);
  /* Runde 7, A2 — damit ist Runde 4 entschieden.
     Der alte Grund für „gar keine Bewegung" war, dass der Hover den Verlauf
     durch eine flache Farbe ersetzte; Verläufe blenden nicht auf flache
     Farben, das sah aus wie ein Ruckeln. Er gilt nicht mehr: .btn blendet
     heute Verlauf auf Verlauf, und jede Fassung, die flach wird (--zwei,
     --still, --symbol, --gefahr), steht auch im Ruhezustand flach.

     Bewegt wird transform, nie Breite, Gewicht oder Abstand. Gemessen am
     2026-09-27: der Nachbar bleibt beim Drücken auf derselben Koordinate.
     Die Dauern kommen aus den Tokens; prefers-reduced-motion drosselt sie
     global in motion.css.

     Runde 8 (T3): der Farbwechsel läuft auf --t-sofort statt --t-fast. Unter
     100 ms liest sich eine Antwort als unmittelbar, darüber als Animation —
     und beim Überfahren will niemand einer Farbe beim Werden zusehen. Hier
     stand für den Druckpunkt auch der einzige Rohwert im System, 90 ms mit
     genau dieser Begründung im Kommentar. Jetzt trägt ihn ein Token. */
  transition:
    background var(--t-sofort) var(--ease),
    border-color var(--t-sofort) var(--ease),
    color var(--t-sofort) var(--ease),
    transform var(--t-sofort) var(--ease);
}
/* Der Druckpunkt. Unter 100ms, damit die Rückmeldung auf den Finger als
   Antwort gelesen wird und nicht als Verzögerung. Seit 2026-10-05 abends
   (Vasco, M1 auf der Demoseite Bewegung) drückt sich der Knopf ein, statt
   einen Pixel abzusinken: 1 px las sich auf grossen Knöpfen nicht. */
.btn:active { transform: scale(.97); }
.btn:is(:disabled, [aria-disabled="true"]):active { transform: none; }
/* Gesperrt reagiert nichts auf den Zeiger (QA vom 8. Oktober, K4). `:where()` hält die
   Spezifität gleich, damit Projekte ihre Hover-Regeln nicht anpassen müssen. */
.btn:hover:where(:not(:disabled, [aria-disabled="true"])) {
  background: linear-gradient(to bottom, var(--accent) 0%, var(--accent-hover) 100%);
  border-color: var(--accent-hover);
}

/* Die Hauptaktion (seit 2026-10-19): derselbe Knopf in der Zweitakzentfarbe, für die eine
   Handlung, für die es die Seite gibt («Mitglied werden», «Platz sichern»). Gewöhnliche Handlungen
   wie Absenden oder Speichern bleiben .btn. Ohne Zweitakzent im Theme sieht er aus wie .btn,
   die Fassung bleibt darum auch in Marken ohne Zweitfarbe richtig gesetzt. */
.btn--ziel {
  background: linear-gradient(to bottom, var(--accent-2-lift) 0%, var(--accent-2) 100%);
  color: var(--accent-2-ink);
  border-color: var(--accent-2);
}
.btn--ziel:hover:where(:not(:disabled, [aria-disabled="true"])) {
  background: linear-gradient(to bottom, var(--accent-2) 0%, var(--accent-2-hover) 100%);
  border-color: var(--accent-2-hover);
}

.btn--zwei {
  background: none;
  color: var(--accent);
}
/* Die Akzentfläche allein reicht nicht: im dunklen Modus liegt --accent-soft
   zwei RGB-Stufen neben --surface-overlay (gemessen 2026-09-28: 1.018:1 im
   Theme vasco, 1.014:1 im Theme beispiel). Ein Abbrechen-Knopf in einem
   Fenster hatte dort keinen sichtbaren Hover.

   --surface-state liegt als zweite Schicht ÜBER der Akzentfläche, statt als
   fester Mischwert daneben: so steht der Anteil weiterhin nur an einer
   Stelle im System, und der Akzentton bleibt trotzdem stehen. Eine Farbe
   kann nur eine Fläche füllen, zwei Schichten gehen nur über
   background-image — daher der Verlauf aus zwei gleichen Stopps. */
.btn--zwei:hover:where(:not(:disabled, [aria-disabled="true"])) {
  background-color: var(--accent-soft);
  background-image: linear-gradient(var(--surface-state), var(--surface-state));
  color: var(--ink);
  border-color: var(--accent);
}

/* ------------------------------------------------ Symbolknopf
   Ein Knopf, der nur ein Zeichen trägt: Schliessen, Weiter, Plus, Nochmal.
   Gemessen am 2026-09-24 stand er SECHSMAL im System unter verschiedenen
   Namen — .fenster__zu, .lichtkasten__zu, .anzahlwahl__schritt,
   .anhang__aktion, .diagramm__nochmal, .seitenwahl button — und war jedes
   Mal dasselbe: Quadrat in Bedienhöhe, Tastfläche, kein Innenabstand,
   --r-sm, eine Kante. Das macht das System nur länger (Vasco).

   Er ist eine Fassung von .btn, kein eigener Knopf: `class="btn btn--symbol"`.
   Wo er sitzt, bleibt Sache des Bausteins — die Form kommt von hier. */
.btn--symbol {
  width: var(--ctl-h-sm);
  height: var(--ctl-h-sm);
  min-width: var(--tap-min);
  min-height: var(--tap-min);
  padding: 0;
  display: grid;
  place-items: center;
  /* Leise als Normalfall: ein Zeichen ohne Wort soll nicht lauter sein als
     der Inhalt, den es bedient. Wer ihn laut braucht, nimmt .btn allein. */
  background: none;
  color: var(--ink-muted);
  border-color: var(--border-subtle);
}
.btn--symbol:hover:where(:not(:disabled, [aria-disabled="true"])) { background: var(--surface-state); color: var(--ink); border-color: var(--border-strong); }
.btn--symbol > svg { width: var(--icon-sm); height: var(--icon-sm); }

/* Drei Fassungen für die drei Anlässe, mehr braucht es nicht. */
.btn--symbol.btn--auf-bild { background: var(--surface-raised); color: var(--ink); border-color: var(--border-strong); }
.btn--symbol.btn--auf-bild > svg { width: var(--icon); height: var(--icon); }
.btn--symbol.btn--zwei { color: var(--accent); border-color: var(--accent); background: var(--surface-base); }
.btn--symbol.btn--zwei:hover:where(:not(:disabled, [aria-disabled="true"])) { background-color: var(--accent-soft);
  background-image: linear-gradient(var(--surface-state), var(--surface-state)); color: var(--ink); }

/* ------------------------------------------------ Dritter Rang: still
   Für Navigation und Abbrechen. Weder Fläche noch Kante, damit er neben
   einem gefüllten und einem umrandeten Knopf als drittes noch unterscheidbar
   ist. Mehr als drei Ränge kann niemand auseinanderhalten. */
.btn--still {
  background: none;
  color: var(--ink-muted);
  border-color: transparent;
  /* Ohne Flaeche und ohne Kante bleibt nur der Text, und der sah aus wie
     Fliesstext (Vasco, 2026-09-25: „sonst wirkt es genau gleich wie Text
     und nicht wie CTA"). Der Unterstrich ist das dritte Merkmal, das
     diesem Rang noch bleibt, und er kostet keinen Platz: text-decoration
     veraendert die Zeilenhoehe nicht. */
  text-decoration: underline;
  text-decoration-thickness: var(--border-w);
  text-underline-offset: 0.25em;
  text-decoration-color: var(--border-strong);
}
.btn--still:hover:where(:not(:disabled, [aria-disabled="true"])) {
  background: var(--surface-state);
  color: var(--ink);
  border-color: transparent;
  text-decoration-color: currentColor;
}
/* Ein stiller Knopf, der nur ein Zeichen traegt, hat keinen Text zum
   Unterstreichen. Dort traegt die Flaeche beim Ueberfahren allein. */
.btn--still.btn--symbol { text-decoration: none; }
/* Ein stiller Knopf mit Gefahrenschrift bleibt auch beim Zeigen in der Gefahrenfarbe. */
.btn.t-fail, .btn.t-fail:hover { color: var(--state-fail); }

/* ------------------------------------------------ Gefahr
   Die einzige Stelle, an der ein Knopf Chroma trägt. Das ist kein Bruch der
   Regel „Farbe nur, wenn sie etwas bedeutet", sondern ihre Anwendung:
   Löschen bedeutet etwas, und zwar dasselbe wie ein durchgefallener Messwert.
   Nur für Handlungen, die sich nicht zurücknehmen lassen. Eine Absage in
   einem Formular ist keine. */
.btn--gefahr {
  background: none;
  color: var(--state-fail);
  border-color: var(--state-fail);
}
.btn--gefahr:hover:where(:not(:disabled, [aria-disabled="true"])) { background: var(--state-fail); color: var(--surface-base); border-color: var(--state-fail); }

.btn--klein { font-size: var(--text-xs); min-height: var(--ctl-h-sm); padding-inline: var(--space-sm); }
/* Der Innenabstand steigt mit auf --space-xl. Sonst wäre --gross nach Runde 7
   seitlich genauso breit wie der Normalfall und nur noch höher — dann trägt
   die Fassung ihren Namen nicht mehr. */
.btn--gross { font-size: var(--text-lg); min-height: var(--ctl-h-lg); padding-inline: var(--space-xl); }
/* Volle Breite: unter dem schmalsten Umbruchpunkt und beim Anmelden, wo es
   genau eine Handlung gibt. Nie neben einem zweiten Knopf in derselben Zeile. */
.btn--voll { width: 100%; }

.btn[disabled] { opacity: .45; cursor: not-allowed; }
/* Während etwas läuft. Die Beschriftung bleibt stehen und wird nur ergänzt,
   damit der Knopf nicht die Breite wechselt: breite-stabil. */
.btn[aria-busy="true"] { cursor: progress; }
.btn[aria-busy="true"] > .btn__ring {
  width: var(--icon-sm);
  height: var(--icon-sm);
  border: var(--border-w-stark) solid currentColor;
  border-top-color: transparent;
  border-radius: var(--r-pill);
  animation: btn-dreht var(--t-slow) linear infinite;
}
@keyframes btn-dreht { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  /* Die Lücke im Ring ist eine Behauptung über Bewegung: sie sagt „hier
     dreht sich etwas". Steht der Ring still, behauptet sie es weiter und
     stimmt nicht mehr. Ohne Bewegung wird daraus ein geschlossener Ring —
     eine ruhige Marke dafür, dass der Knopf gerade besetzt ist. Die
     Aussage selbst trägt aria-busy, nicht die Form. */
  .btn[aria-busy="true"] > .btn__ring { animation: none; border-top-color: currentColor; }
}

/* ----------------------------------------------- Matte Akzentfläche
   SYSTEMREGEL, gemessen am 2026-09-23: --accent-soft erreicht gegen die
   Grundfläche nur 1.09–1.65:1. Ohne Kante im tiefen Ton wäre die Fläche als
   Form unsichtbar. Darum gibt es sie nur zusammen mit der Kante — deshalb
   eine Klasse und keine zwei. tools/check.mjs prüft das Verhältnis.

   SIE HIESS BIS ZUM 2026-09-28 `.feld` — und so hiess auch der Wickel um ein
   Formularfeld in bausteine/grundlagen.css. Zwei Regeln, ein Name, beide
   greifen: jedes Formularfeld im System trug dadurch eine Akzentfläche mit
   Kante, die niemand bestellt hatte. Vascos Frage dazu war „warum 2x ein
   kasten?", und sie war berechtigt. check.mjs führte `feld` sogar in der
   Liste der absichtlich geteilten Klassen — das war die falsche Antwort auf
   eine echte Kollision. Umbenannt wurde die seltenere von beiden: drei
   Stellen im Guide gegen 19 Demos mit Formularfeldern. */
.akzentflaeche {
  background: var(--accent-soft);
  border: var(--border-w) solid var(--accent);
  border-radius: var(--r-sm);
  padding: var(--space-sm) var(--space-md);
  color: var(--ink);
}

/* -------------------------------------------------------- Zustandsfarben */
/* Chroma erscheint nur hier: auf echten, gemessenen Werten. */
.ist-pass { color: var(--state-pass); }
.ist-fail { color: var(--state-fail); }
.ist-warn { color: var(--state-warn); }

/* -------------------------------------------------------------- Zeichen
   Die Grösse jedes Zeichens. Stand bis 2026-10-19 in der Gruppe Navigation; ein Projekt, das nur
   Grundstil und Formulare lädt (tfcz.ch), zeichnete den Pfeil der Eigenauswahl darum so gross wie
   das ganze Feld. Gilt für jeden Baustein, gehört also hierher.
   vertical-align: middle für ein Zeichen, das im Fliesstext steht statt in einem Flex-Behälter.
   Ohne das sitzt ein SVG mit seiner Unterkante auf der Schriftlinie und steht damit neben dem Wort
   versetzt (Vasco, 2026-10-08, G23: «immer viel zu weit unten platziert und nie mittig»). In
   Flex- und Grid-Behältern wirkt die Angabe nicht, dort richtet align-items aus. */
.symbol { width: var(--icon); height: var(--icon); flex: none; vertical-align: middle; }
.symbol--klein { width: var(--icon-sm); height: var(--icon-sm); }

/* ------------------------------------------------- Für Screenreader only */
.visually-hidden {
  position: absolute;
  width: var(--border-w); height: var(--border-w);
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Eine Tabelle ignoriert die Breite oben: sie richtet sich nach ihrem Inhalt.
   `table-layout: fixed` stand hier seit dem 2026-09-24 dagegen und hat nicht
   gereicht, weil es für die <caption> nicht gilt. Gemessen am 2026-09-28
   waren die verborgenen Wertetabellen der Diagramme bis 313 px breit; weil
   sie absolut liegen, verschoben sie nichts, machten aber das Dokument
   breiter, und die Seite rollte bei 375 px seitwärts.

   Gelöst wird das jetzt am Markup statt hier: `chart-diagramme.js` legt die
   Tabelle in einen <div class="visually-hidden">. Bei einem Block greifen
   `width` und `overflow`, und die <table> darin bleibt für ein
   Vorleseprogramm eine Tabelle. `display: block` auf der Tabelle selbst
   hätte genau das zerstört, wofür sie da ist.

   Die Regel bleibt als Netz für Tabellen, die jemand direkt so auszeichnet. */
table.visually-hidden { table-layout: fixed; }

/* ------------------------------------------------ Jeder Rollkasten zeigt es
   Unter macOS blendet der Browser Rollbalken aus, bis gerollt wird. Ein
   Kasten, der mehr enthält, als er zeigt, sieht dann aus wie ein Kasten,
   der alles zeigt. Vasco am 2026-09-23 an der Rollfläche und am 2026-09-29
   an der Suchauswahl: „man merkt nicht, dass man scrollen kann".

   EINMAL FÜR ALLE statt je Baustein: bis heute hatte nur die Rollfläche
   einen stehenden Balken, Suchauswahl, Chatverlauf, Haftszene, Bilderfolge
   und Inhaltsverzeichnis nicht. `body *` und nicht `*`: der Balken des
   Fensters bleibt dem Betriebssystem, sonst verlöre jede Seite 8 px Breite
   und jeder Umbruchpunkt sässe woanders.

   NUR ::-webkit-scrollbar. Sobald ein Element `scrollbar-width` oder
   `scrollbar-color` trägt, übergeht Chrome diese Regeln; darum setzt sie im
   System niemand mehr, ausser wer den Balken bewusst verbirgt
   (`scrollbar-width: none`, Rollwörter). Firefox zeigt ihn ohnehin. */
:where(body *)::-webkit-scrollbar { width: var(--space-xs); height: var(--space-xs); }
:where(body *)::-webkit-scrollbar-track { background: var(--surface-state); border-radius: var(--r-pill); }
:where(body *)::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--r-pill); }
:where(body *)::-webkit-scrollbar-corner { background: transparent; }

/* ------------------------------------------------ Wortwechsel am Knopf
   Ein Knopf, dessen Beschriftung sich mit seinem Zustand ändert, hält
   trotzdem seine Breite. Beide Wörter liegen im selben Rasterfeld; das
   längere bestimmt die Grösse, sichtbar ist immer genau eines.

   Dieselbe Bauart wie die zwei Zeichen im Griff des Modusschalters, und
   dieselbe Regel dahinter: ein Zustandswechsel verschiebt nie. Der Wechsel
   läuft über `[disabled]` am Knopf, nicht über eine eigene Klasse — der
   Zustand steht damit an einer Stelle statt an zweien: breite-stabil. */
.wortwechsel { display: inline-grid; place-items: center; }
.wortwechsel > * { grid-area: 1 / 1; }
button:not(:disabled) > .wortwechsel > .wortwechsel__nachher,
button:disabled > .wortwechsel > .wortwechsel__vorher { visibility: hidden; }

/* ============================================================
   BAUSTEINE · Gruppe „Grundlagen"

   Umgezogen aus dem TFCZ-Design-System und re-tokenisiert.
   Die Mechanik ist übernommen, jeder Wert kommt aus tokens/.
   Herkunft und Änderungen stehen je Baustein in components/<name>/meta.json.

   Setzt tokens/core.css, ein Theme und system/base.css voraus.
   ============================================================ */

/* ---------------------------------------------------------- Marke
   TFCZ hatte vier Kategorie-Farben (fun, pro, elite, event). Die sind
   vereinsgebunden und fallen weg. Übrig bleiben die Zustandsfarben —
   die einzige Stelle im System, an der überhaupt Chroma vorkommt. */
.marke {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  padding: var(--space-3xs) var(--space-xs);
  border-radius: var(--r-xs);
  color: var(--ink);
  background: var(--accent-soft);
  border: var(--border-w) solid var(--accent);
}
.marke::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: var(--r-pill);
  background: var(--accent);
  flex: none;
}
.marke--pass { color: var(--state-pass); border-color: var(--state-pass); background: none; }
.marke--pass::before { background: var(--state-pass); }
.marke--fail { color: var(--state-fail); border-color: var(--state-fail); background: none; }
.marke--fail::before { background: var(--state-fail); }
.marke--warn { color: var(--state-warn); border-color: var(--state-warn); background: none; }
.marke--warn::before { background: var(--state-warn); }

/* Drei Fassungen für Werkzeuge mit Einträgen (Teil CRM im Guide). Vasco am
   2026-10-02: in einer Tabelle ist jeder Wert aus einer Auswahl eine Marke,
   freier Text bleibt Text. Darum braucht es eine Marke, die nur einordnet. */

/* Ein Wert aus einer Auswahl (Branche, Art, Kanton): ruhig, ohne Punkt und
   ohne Fläche. Der Punkt hiesse „Zustand", und den meldet ein Auswahlwert
   nicht. Text in --ink, damit er neben freiem Text gleich laut liest.
   Seit dem 2026-10-03 abends auch ohne Kante und Innenabstand, also reiner
   Text (U4, Audit Vertriebscockpit: 187 Marken auf 26 Tabellenzeilen).
   Die Klasse bleibt, damit Markup, das sie trägt, nicht bricht. */
.marke--wert {
  padding: 0;
  color: var(--ink);
  background: none;
  border-color: transparent;
  font-weight: var(--fw-regular);
}
.marke--wert::before { display: none; }

/* Ein Stand mit Zeichen (Offen, In Arbeit, Wartet, Erledigt). Das Zeichen
   trägt die Farbe des Zustands, Text und Kante bleiben ruhig. Der Punkt
   entfällt: Punkt und Zeichen nebeneinander sagten zweimal dasselbe. Im
   Cockpit stand er noch daneben. */
.marke--zustand {
  gap: var(--space-3xs);
  color: var(--ink);
  background: none;
  border-color: var(--border-subtle);
}
.marke--zustand::before { display: none; }
.marke--zustand > .symbol { flex: none; color: var(--ink-muted); }
.marke--zustand[data-ton-zustand="pass"] > .symbol { color: var(--state-pass); }
.marke--zustand[data-ton-zustand="warn"] > .symbol { color: var(--state-warn); }
.marke--zustand[data-ton-zustand="fail"] > .symbol { color: var(--state-fail); }
.marke--zustand[data-ton-zustand="akzent"] > .symbol { color: var(--accent); }

/* Der Hinweis steht auf einer Zeile, die beim Zeigen und Wählen eine Fläche bekommt. Ohne eigene
   Grundfläche fiel „Neu“ in Warnfarbe auf der gewählten Zeile auf 3.47:1 (gemessen 2026-10-07). */
.marke--hinweis:is(.marke--pass, .marke--fail, .marke--warn) { background: var(--surface-base); }

/* Hinweis an einer Zeile (Neu, Geändert). Wer viele Zeilen mit Hinweis hat,
   schaltet am Wurzelelement auf den Punkt allein; die Legende unter der
   Tabelle (.marken-legende) erklärt dann, was die Punkte heissen. Der Text
   bleibt im Baum und wird vorgelesen, er hat nur keine Grösse mehr. */
:root[data-marken="punkt"] .marke--hinweis {
  gap: 0;
  padding: 0;
  font-size: 0;
  background: none;
  border: 0;
  vertical-align: middle;
}

/* ----------------------------------------------------------- Taste
   Tastenkürzel im Fliesstext. Die dicke Unterkante gibt ihr Tiefe. */
.taste {
  display: inline-block;
  min-width: var(--space-lg);
  text-align: center;
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  background: var(--surface-overlay);
  border: var(--border-w-stark) solid var(--border-strong);
  border-bottom-width: 3px;
  border-radius: var(--r-xs);
  padding: var(--space-3xs) var(--space-2xs);
}

/* ------------------------------------------------------- Initialen
   Ein Mensch in einer Zeile, wo kein Platz für den Namen ist: Tabellen,
   Listen, Kommentare. Zwei Buchstaben, mehr nicht.

   RUND IST HIER ERLAUBT, obwohl die Radienskala oben flach ausläuft und
   Bedienelemente keine Pille tragen: „ein Kopf ist rund" steht als Ausnahme
   im Kapitel Rundungen. Das hier ist kein Bedienelement, sondern ein Bild
   ohne Bild.

   Der volle Name gehört ins title-Attribut UND als unsichtbarer Text
   daneben — zwei Buchstaben allein sagen einem Vorleseprogramm nichts.
   Die Fläche trägt eine Kante, weil --accent-soft allein nur 1.09–1.65:1
   gegen die Grundfläche erreicht. */
.initialen {
  display: inline-grid;
  place-items: center;
  width: var(--space-lg);
  height: var(--space-lg);
  flex: none;
  border-radius: var(--r-pill);
  background: var(--accent-soft);
  border: var(--border-w) solid var(--accent);
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  /* Zwei Buchstaben sind keine Textauswahl wert und stören beim Markieren
     der Zeile darum herum. */
  user-select: none;
}
/* ------------------------------------------------------- Trennlinie
   Neutral. Nie in der Akzentfarbe — sie trennt, sie betont nicht. */
.trenner {
  border: 0;
  height: 1px;
  width: 100%;
  background: var(--border-subtle);
  margin-block: var(--space-2xs);
}
.trenner--hoch {
  width: 1px;
  height: 1em;
  margin-block: 0;
  /* Abstand zu den Wörtern daneben, sonst klebte die Linie am Buchstaben (J17), und so viel, dass
     man die Wörter als getrennt liest (2026-10-09). */
  margin-inline: var(--space-sm);
  display: inline-block;
  vertical-align: middle;
}
/* Mit Titel: die Überschrift steht links, die Linie füllt den Rest der Zeile. Die Hülle ist nötig,
   weil die Linie sonst nicht wüsste, wie viel Platz der Titel lässt. Die Schriftgrösse setzt die
   Hülle, nicht die Stufe der Überschrift: welche Stufe es ist, bestimmt die Seitenstruktur. */
.trenner-titel { display: flex; align-items: center; gap: var(--space-sm); }
.trenner-titel > :is(h2, h3, h4) { flex: none; margin: 0; font-size: var(--text-lg); font-weight: var(--fw-medium); }
.trenner-titel > .trenner { flex: 1; width: auto; margin-block: 0; }

/* --------------------------------------------------- Seitenverhältnis
   Rahmen mit festem Verhältnis. Das Bild füllt ihn, ohne sich zu verzerren. */
.verhaeltnis {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--surface-overlay);
}
.verhaeltnis > img { width: 100%; height: 100%; object-fit: cover; }

/* Solange kein Bild darin steht, sagt die Flaeche das auch. Vorher war sie
   einfach leer — man sah nicht, ob ein Baustein kaputt ist oder nur noch
   kein Foto hat (Vasco, 2026-09-24: „hier habe ich keine Ahnung was
   passiert, veraendert sich nichts"). Eine Regel fuer alle Bildflaechen im
   System, nicht eine pro Baustein. */
/* Die leere Fläche trägt eine gestrichelte Kante im Format des Bildes (Vasco, 2026-10-08, G14:
   «man versteht gar nicht, das Foto, das folgt, wo kommt das dann hin»). Vorher stand das Wort
   allein auf einer Fläche, die sich kaum vom Grund abhob. */
.verhaeltnis:empty,
.verhaeltnis:has(> img:not([src])),
.verhaeltnis:not(:has(> img, > picture, > svg, > video)) {
  background: none;
  border: var(--border-w) dashed var(--border-strong);
}
.verhaeltnis:empty::after,
.verhaeltnis:has(> img:not([src])) ::after,
.verhaeltnis:not(:has(> img, > picture, > svg, > video))::after {
  content: 'Foto folgt';
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  color: var(--ink-faint);
  font-size: var(--text-sm);
}
.verhaeltnis--quadrat { aspect-ratio: 1 / 1; }
.verhaeltnis--hoch { aspect-ratio: 4 / 5; }

/* ------------------------------------------------------- Platzhalter
   Solange Inhalt lädt. Er hat schon die Form des Inhalts — das verhindert,
   dass die Seite springt, wenn der Inhalt eintrifft.

   Die Welle lief bis zum 2026-09-29 mit 1.4s als roher Zahl. --t-lang ist
   die Stufe für etwas, das dauert statt zu antworten, und der Unterschied
   von 200 ms ist an einer endlos laufenden Welle nicht zu sehen. */
.platzhalter {
  display: block;
  height: var(--space-sm);
  border-radius: var(--r-xs);
  background: linear-gradient(90deg,
    var(--surface-overlay) 25%,
    color-mix(in oklab, var(--surface-overlay), var(--ink) 7%) 50%,
    var(--surface-overlay) 75%);
  background-size: 200% 100%;
  animation: platzhalterWelle var(--t-lang) ease infinite;
}
.platzhalter--rund { width: var(--space-2xl); height: var(--space-2xl); border-radius: var(--r-pill); }
/* Der Titel einer Seite, solange sie lädt (A2 im Kapitel 5.5). */
.platzhalter--titel { width: min(20rem, 60%); height: var(--space-xl); }
@keyframes platzhalterWelle {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

/* ----------------------------------------------------------- Ladering
   TFCZ trug hier die Brand-Line: oben Blau, unten Gold. Zwei Markenfarben
   gibt es hier nicht, der Ring läuft in einer Akzentfarbe. */
/* Ladering. Ein Ring, dessen Viertel in --accent umläuft. Zurück seit 2026-10-08 (Vasco, G13:
   «einen normalen Ring, der sich dreht, so ganz normales Ladezeichen»); vom 2026-09-28 bis dahin
   stand hier ein Pendel nach uiverse.io. Der alte Ring war bei 16 px kaum als Bewegung lesbar,
   darum ist dieser 1.25em gross, mit der Spur in --border-strong und dem Bogen in --accent.

   WICHTIG: bei prefers-reduced-motion steht er still, als geschlossener Ring in --ink-faint. Eine
   Ladeanzeige ohne Bewegung sagt nichts mehr, darum gehört ein Wort daneben, und die Demo zeigt
   genau das. Die Klasse .pendel gibt es nicht mehr; Projekte stellen beim nächsten Kern um. */
.ladering {
  display: inline-block;
  flex: none;
  width: 1.25em;
  height: 1.25em;
  vertical-align: middle;
  border: var(--border-w-stark) solid var(--border-strong);
  border-block-start-color: var(--accent);
  border-radius: var(--r-pill);
  animation: ladering var(--t-lang) linear infinite;
}
.ladering--klein { width: 1em; height: 1em; }
@keyframes ladering { to { rotate: 1turn; } }

/* ---------------------------------------------------------- Feld
   Beschriftung gehört zu genau einem Eingabefeld. Klick auf die
   Beschriftung setzt den Fokus — deshalb <label for>, nicht <div>.

   DIE BESCHRIFTUNG STEHT ÜBER DEM FELD und bleibt stehen.

   Umkehr vom 2026-09-27. Bis dahin lag sie IM Feld und verschwand beim
   Hineinklicken (Regel vom 2026-09-25: „Email muss ins Feld rein und bitte
   kein Titel darüber bei Feldern"). Der Grund für die Umkehr ist nicht die
   Lage, sondern was dadurch frei wird: solange die Beschriftung im Feld
   liegt, ist der Platzhalter besetzt und kann nie ein Beispiel zeigen.
   Vasco: „Dafür ist im Formular immer ein Beispiel drin, das finde ich
   jetzt doch wichtiger."

   Damit fällt auch der Preis weg, der damals bewusst gekauft wurde: wer
   ein halb ausgefülltes Formular wieder aufnahm, sah nur noch die Werte.

   WARUM column-reverse UND NICHT DAS MARKUP UMSTELLEN: das Label steht in
   jeder Demo NACH der Eingabe, weil die alte Regel :placeholder-shown und
   den Nachbarschafts-Selektor brauchte. Es zu verschieben hiesse, jede
   dieser Dateien anzufassen. Die Spalte kehrt die sichtbare Reihenfolge
   um, das Markup bleibt. Zulässig, weil ein Label keinen Fokus nimmt: die
   Tab-Ordnung ändert sich nicht, und die Zuordnung läuft über `for`, nicht
   über die Nachbarschaft.

   DER PLATZHALTER IST JETZT DER BEISPIELPLATZ. Ein Platzhalter, der nur
   die Beschriftung wiederholt, kostet ihn und gibt nichts zurück; das
   meldet check.mjs. */
.feld { display: flex; flex-direction: column; gap: var(--space-2xs); }
.feld > .hinweis { font-size: var(--text-2xs); color: var(--ink-faint); }

.feld__eingabe {
  position: relative;
  display: flex;
  flex-direction: column-reverse;
  gap: var(--space-2xs);
}
.feld__eingabe > label {
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  color: var(--ink);
}
/* Das Beispiel steht leiser als ein eingetippter Wert, sonst liest man es
   als Inhalt. `opacity: 1`, weil Firefox den Platzhalter sonst ein zweites
   Mal dimmt.

   Seit 2026-10-08 deutlich leiser (Vasco, G9 und H1: «Es wirkt immer so, als wäre schon was
   reingeschrieben worden», «im dunklen Feld kaum auffällt»). --ink-faint allein stand mit 6.7:1
   fast so stark wie ein Wert, 60 % davon (3.3:1) noch immer. 40 % ergeben gemessen rund 2.2:1:
   man sieht das Beispiel, wenn man hinschaut, und liest es nicht als Inhalt. Weil --ink-faint je
   Modus wechselt, ist es im dunklen Feld dunkel und im hellen hell. Unter 4.5:1 ist bewusst: die
   Beschriftung über dem Feld steht immer, das Beispiel ergänzt nur. Gilt für jeden Platzhalter. */
:where(input, textarea)::placeholder {
  color: color-mix(in oklab, var(--ink-faint) 40%, transparent);
  opacity: 1;
}

/* ---- Die Ausnahme: kein Platz -----------------------------------
   Die alte Fassung bleibt, aber nur noch dort, wo eine Zeile über dem Feld
   eine Zeile ist, die es nicht gibt. Vasco: „Das andere, so wie es jetzt
   ist, ist vor allem gut, wenn es kein Platz hat."

   Sie trägt jetzt einen eigenen Namen. Vorher war sie der Standard und die
   Sonderfälle blieben stillschweigend; einen Namen zu haben heisst, dass
   man sie zählen und prüfen kann.

   Hier bleibt `placeholder=" "` Pflicht: ohne Platzhalter greift
   :placeholder-shown nie und die Beschriftung läge über dem Text. */
.feld__eingabe--eng { display: block; }
.feld__eingabe--eng > label {
  position: absolute;
  inset-inline-start: var(--space-sm);
  top: 50%;
  translate: 0 -50%;
  pointer-events: none;
  font-size: var(--text-sm);
  font-weight: var(--fw-regular);
  color: var(--ink-faint);
  transition: opacity var(--t-fast) var(--ease);
}
/* Bei einem mehrzeiligen Feld steht sie oben, nicht in der Mitte eines
   vier Zeilen hohen Kastens. */
.feld__eingabe--eng > textarea + label { top: var(--space-2xs); translate: 0 0; }

/* Weg, sobald man drin ist oder etwas drinsteht. visibility statt display,
   damit die Verbindung label/for bestehen bleibt. */
.feld__eingabe--eng > :is(input, textarea):focus + label,
.feld__eingabe--eng > :is(input, textarea):not(:placeholder-shown) + label {
  opacity: 0;
  visibility: hidden;
}
@media (prefers-reduced-motion: reduce) {
  .feld__eingabe--eng > label { transition: none; }
}

.feld input, .feld textarea, .feld select {
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  width: 100%;
  min-height: var(--ctl-h);
  padding: var(--space-2xs) var(--space-sm);
  color: var(--ink);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-strong);
  border-radius: var(--r-sm);
}
.feld input:focus-visible, .feld textarea:focus-visible, .feld select:focus-visible {
  border-color: var(--accent);
}

/* ---- Leeren mit einem Griff -------------------------------------
   Ein Feld mit Inhalt zeigt einen Weg, ihn wieder loszuwerden. Bei uns
   muss der selbst gebaut sein: das Kreuz, das der Browser an
   input[type=search] haengt, ist eine Flaeche, die das Betriebssystem
   zeichnet. Abgeschaltet wird es in base.css ueber
   ::-webkit-search-cancel-button — hier stand bis zum 2026-09-28, das
   erledige `appearance: none`, und diese Regel gab es nie.

   DER PLATZ IST IMMER RESERVIERT, auch solange der Knopf unsichtbar ist.
   Sonst ruecken Text und Beispielwert in dem Moment nach links, in dem
   man das erste Zeichen tippt — genau die Bewegung, die es im System
   nirgends geben darf.

   `visibility` statt `display`: der Knopf faellt damit auch aus der
   Tab-Ordnung, solange das Feld leer ist. Wer sonst durchtabbt, landet
   auf einem Knopf, der nichts tut.

   Nur als Fassung, nicht an jedem Feld. Ein Kreuz neben jedem Vor- und
   Nachnamen ist Laerm; gemeint sind Felder, die man oft wieder leert:
   Suche, Filter, ein langer Pfad. */
.feld--leerbar .feld__eingabe { position: relative; }
.feld--leerbar :is(input, textarea) {
  padding-inline-end: calc(var(--space-sm) + var(--icon) + var(--space-2xs));
}
/* Gebaut wie `.datumsfeld__knopf`, der an derselben Stelle sitzt: absolut im
   Feld, ohne Flaeche und ohne Kante. Darum auch ohne `.btn` — die
   Knopfklasse traegt die Raenge einer Handlung, nicht ein Zeichen, das im
   Feld mitlaeuft.

   ANGEBUNDEN UNTEN, NICHT UEBER DIE GANZE HOEHE: der Wickel `.feld__eingabe`
   umfasst Beschriftung UND Eingabe, und die Beschriftung steht seit der
   Umstellung DARUEBER. Ein `inset-block: 0` streckt den Knopf darum ueber
   beide, gemessen 66 statt 44 px, und das Kreuz sitzt neben der
   Beschriftung statt im Feld. */
.feld__leeren {
  position: absolute;
  inset-inline-end: var(--border-w);
  inset-block-end: var(--border-w);
  display: grid;
  place-items: center;
  width: var(--ctl-h);
  height: calc(var(--ctl-h) - 2 * var(--border-w));
  padding: 0;
  color: var(--ink-faint);
  background: none;
  border: 0;
  border-radius: var(--r-xs);
  visibility: hidden;
  cursor: pointer;
}
/* Nur wo sich der Wert auch aendern laesst. Ein gesperrtes Feld zeigte das
   Kreuz bis zum 2026-09-29, per Tab erreichbar, und ein Klick leerte es:
   „A-2026-0184" war weg. Gesperrt kann ein Feld auch erst spaeter werden,
   waehrend des Absendens (Kapitel 4.7); darum hier und nicht nur im Skript. */
.feld--leerbar .feld__eingabe:has(:is(input, textarea):not(:placeholder-shown, :disabled, [readonly])) .feld__leeren {
  visibility: visible;
}
/* Im mehrzeiligen Feld oben angebunden; die Höhe der Oberkante setzt feld-leeren.js. */
.feld__leeren--oben { inset-block-end: auto; }
.feld__leeren { transition: color var(--t-sofort) var(--ease); }
.feld__leeren:hover { color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .platzhalter { animation: none; }
  /* Der Ring steht still und schliesst sich, damit er nicht wie ein Fehler halb offen steht; was
     dort geschieht, sagt das Wort daneben. */
  .ladering { animation: none; border-color: var(--ink-faint); }
}

/* ------------------------------------------------- Zustandsreihe
   Nur fuer den Katalog, wie .knopfschau: dasselbe Bedienelement in seinen
   Zustaenden untereinander, jeder mit einer Marke davor. Vascos Massstab
   (2026-09-25): „Es genuegt, dass ich weiss wie ein Eingabefeld aussieht,
   wie es aussieht wenns ein Fehler gibt und wenn nicht."

   Untereinander und nicht nebeneinander, damit die Meldezeilen auf einer
   Hoehe stehen und man sieht, dass der Wechsel nichts verschiebt. */
.zustaende { display: grid; gap: var(--space-md); width: 100%; }
/* Kleine Bedienelemente (Haken, Herz) in Spalten nebeneinander, sonst stehen vier Zeilen Text
   über eine ganze Bildschirmhöhe verteilt (Vasco, 2026-10-08, G18: «in zwei Spalten darstellen»).
   Die Meldezeilen stehen dann zwar nicht mehr untereinander, aber diese Bausteine haben keine. */
.zustaende--spalten { grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr)); }
/* Weit: breitere Spalten mit mehr Abstand, für Fälle mit ganzen Sätzen (Trenner, 2026-10-09). */
.zustaende--weit { grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr)); column-gap: var(--space-2xl); }
/* Ganze Seitenteile (Fussbereich, Kopf) als Fälle: jeder Fall eine eigene Fläche mit Abstand, und
   die Marke darüber in Lesegrösse. Eine Linie dazwischen reichte nicht, der zweite Fuss las sich als
   Fortsetzung des ersten (Vasco, 2026-10-08, G29 und H14). */
.zustaende--getrennt { gap: var(--space-2xl); }
.zustaende--getrennt > .zustaende__fall {
  gap: var(--space-sm);
  padding: var(--space-md);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-subtle);
  border-radius: var(--r-md);
}
.zustaende--getrennt > .zustaende__fall > .zustaende__marke {
  font-size: var(--text-md);
  font-weight: var(--fw-semibold);
  color: var(--ink);
}
/* align-content: start, sonst dehnt das Raster die Zeilen eines Falls auf die Höhe seines Nachbarn,
   und in der Haken-Demo stand der erste Fall 11 px tiefer als der zweite (H10). */
.zustaende__fall { display: grid; gap: var(--space-2xs); align-content: start; }
.zustaende__marke {
  font-size: var(--text-2xs);
  color: var(--ink-faint);
}

/* Marke in der Fassung des Cockpits (Vasco, 2026-10-08, W6): eine Marke mit selbst gewählter Farbe
   trägt ihren Ton als Schrift, Kante, Punkt und leichte Fläche. */
.marke[data-ton="1"] { --ton: var(--ton-1); }
.marke[data-ton="2"] { --ton: var(--ton-2); }
.marke[data-ton="3"] { --ton: var(--ton-3); }
.marke[data-ton="4"] { --ton: var(--ton-4); }
.marke[data-ton="5"] { --ton: var(--ton-5); }
.marke[data-ton="6"] { --ton: var(--ton-6); }
.marke[data-ton] { color: var(--ton); border-color: color-mix(in oklab, var(--ton) 55%, transparent); background: color-mix(in oklab, var(--ton) 7%, transparent); }
.marke[data-ton]::before { background: var(--ton); }
.marke--pass.marke--voll { background: color-mix(in oklab, var(--state-pass) 12%, transparent); }
/* ============================================================
   BAUSTEINE · Gruppe „Formulare"

   Umgezogen aus dem TFCZ-Design-System und re-tokenisiert.
   Die Mechanik ist übernommen, jeder Wert kommt aus tokens/.
   Herkunft und Änderungen stehen je Baustein in components/<name>/meta.json.

   Setzt tokens/core.css, ein Theme und system/base.css voraus.

   ── Zwei Leitsätze, die in dieser Gruppe alles bestimmen ──────────────

   1) `.feld` wird NICHT neu erfunden. Die Klasse steht in system/base.css
      (matte Fläche mit Kante) und in der Gruppe Grundlagen (Beschriftung über
      Eingabe, Hilfetext darunter). Diese Datei ergänzt sie nur um das, was ein
      Formular zusätzlich braucht: sichtbaren Pflichthinweis, Fehlermeldung mit
      vorgehaltenem Platz, Fehlerkante am Feld selbst.

   2) Nativ vor nachgebaut. Wo der Browser das Steuerelement schon kennt, wird
      es benutzt statt nachgezeichnet: `type="date"` statt Kalender-Popover,
      `<datalist>` statt gefilterter Liste, `type="number"` statt Plus/Minus,
      `accent-color` statt sechs Hersteller-Selektoren am Schieberegler.
      Das hat beim Umzug zwei von neun JavaScript-Abhängigkeiten gestrichen.

   Reihenfolge im Ausgabe-CSS: „grundlagen" läuft zuerst (tools/build-css.mjs),
   diese Datei danach — die Ergänzungen an `.feld` greifen also verlässlich,
   ohne dass eine Regel aus Grundlagen angefasst werden muss.
   ============================================================ */

/* ================================================================
   A · FELD-ERWEITERUNG — gilt für ALLE Bausteine dieser Gruppe
   ================================================================ */

/* Breiten. Formularfelder brauchen eine Lesebreite, sonst wird ein Namensfeld
   so breit wie der Bildschirm. Zwei Stufen reichen; alles andere ergibt sich
   aus dem Behälter. */
.feld--schmal { width: min(var(--panel-w), 100%); }
.feld--breit  { width: min(calc(var(--panel-w) + var(--space-4xl)), 100%); }

/* Kopfzeile eines Feldes: Beschriftung links, Pflichthinweis oder Messwert
   rechts. Die Beschriftung braucht hier eigene Regeln, weil Grundlagen nur
   `.feld > label` bedient und sie jetzt einen Schritt tiefer steht. */
/* DIE AUSNAHME ZUR REGEL „Beschriftung im Feld": ein zusammengesetztes
   Feld. Die Eingabegruppe traegt links ein Zeichen und rechts einen Knopf;
   eine Beschriftung, die darin schwebt, stiesse an beides. Dort steht sie
   weiter darueber, und nur dort.
   Ebenso bei Bausteinen ohne Textfeld: Regler und Initialenwahl haben
   nichts, worin eine Beschriftung liegen koennte. */
.feld__kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
}
.feld__kopf label,
.feld__kopf > span:first-child {
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  color: var(--ink);
}

/* Hilfetext. Grundlagen stylt nur `.feld > .hinweis`; in dieser Gruppe steht er
   auch neben einem Schalter oder unter einer Ablagefläche. Gleiche Werte, nur
   ohne die Bindung an `.feld` — die schärfere Regel aus Grundlagen bleibt
   dadurch unberührt. */
.hinweis { font-size: var(--text-2xs); color: var(--ink-faint); }

/* Neutrale Gruppierung: ein `<fieldset>` ohne eigenes Aussehen. Nötig, damit
   mehrere Kästchen, Auswahlknöpfe oder Codefelder als EINE Frage vorgelesen
   werden — ohne Fieldset hört man sechs namenlose Felder. */
.satz,
.wahlgruppe,
.codefelder { border: 0; margin: 0; padding: 0; min-inline-size: 0; }
.satz > legend,
.wahlgruppe > legend,
.codefelder > legend {
  padding: 0;
  margin-bottom: var(--space-2xs);
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  color: var(--ink);
}

/* Pflichthinweis. TFCZ hatte einen goldenen Stern — ein Stern ist für sich
   genommen keine Auskunft: Vorleseprogramme sprechen ihn als „Stern", und wer
   die Legende nicht gelesen hat, weiss nichts. Darum steht hier ein Wort.
   Das Feld selbst trägt zusätzlich `required`, damit der Browser prüft. */
.feld .pflicht {
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Fehlermeldung. Zwei Dinge sind hier wichtig:

   — Der Platz wird IMMER vorgehalten (`min-height`). Sonst springt beim
     Absenden das halbe Formular nach unten, und der Knopf, den man treffen
     wollte, ist nicht mehr da, wo er war.
   — Die Farbe ist nur die Zugabe. Getragen wird die Meldung vom Text und von
     `aria-describedby` am Feld; `aria-invalid` schaltet zusätzlich die Kante.
     Farbe allein wäre für farbschwache Menschen nichts.

   Die Wahlgruppe ist ein <fieldset> ohne `.feld` und trägt dieselbe Meldung.
   Mit `.feld` allein stand sie bis zum 2026-09-29 ungestaltet da, 15.5 px in
   --ink statt 11.5 px in Zustandsrot. */
:is(.feld, .wahlgruppe) .feld__fehler {
  margin: 0;
  /* Die Meldung bestimmt die Breite des Feldes NICHT. `width: 0` nimmt sie
     aus der Rechnung, mit der ein Flex- oder Grid-Kasten seine Kinder misst;
     `min-width: 100%` gibt ihr danach die volle Zeile zurück. Ohne das machte
     ein langer Satz das ganze Feld breiter, und das Feld daneben rutschte in
     die nächste Zeile — gemessen am 2026-09-28 an zwei Feldern in der
     Katalogbühne. */
  width: 0;
  min-width: 100%;
  /* Genau eine Zeilenhöhe, nicht geschätzt: gemessen am 2026-09-24 wuchs das
     Feld sonst beim Einblenden der Meldung um 3.1 px. */
  min-height: calc(var(--text-2xs) * var(--leading-normal));
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  color: var(--state-fail);
}

/* WO EIN HINWEIS STEHT, HÄLT DER DIE ZEILE FREI.
   Der vorgehaltene Platz soll verhindern, dass beim Einblenden der Meldung
   etwas springt. Steht darunter ohnehin ein Hinweis, tut das schon dieser
   — und der leere Fehlerplatz schiebt den Hinweis dann nur von seinem Feld
   weg. Gemessen am 2026-09-29 am bedingten Feld: 27 px zwischen Feld und
   Hinweis, aber nur 4 px zwischen Hinweis und dem NÄCHSTEN Eintrag. Damit
   las sich die Zeile als Teil des folgenden Elements (Vasco: „dieser Text
   ist näher am unteren Punkt wie am zugehörigen Fenster"), also genau
   verkehrt zu Kapitel 4.4.

   Die Höhe bleibt trotzdem konstant, weil sich Meldung und Hinweis
   dieselbe Zeile teilen: erscheint die Meldung, tritt der Hinweis ab. Das
   ist auch inhaltlich richtig — solange etwas falsch ist, zählt der Weg
   heraus und nicht, was ausserdem gut zu wissen wäre. Sobald die Meldung
   verschwindet, ist der Hinweis wieder da. */
.feld:has(> .hinweis) > .feld__fehler:empty { min-height: 0; }
.feld:has(> .feld__fehler:not(:empty)) > .hinweis { display: none; }

/* OHNE `.feld` davor. Die Regel galt nur innerhalb dieses einen Wickels,
   und jedes Feld daneben — die Suchauswahl, das Tokenfeld, ein Feld in
   einer Eingabegruppe — blieb bei einem Fehler unverändert. Vasco am
   2026-09-28: „nicht alle Formularfelder werden rot bei einem Fehler."
   `aria-invalid` sagt dasselbe, egal wo das Feld steht; also gilt die
   Kante auch überall.

   Die Breite wechselt mit, von --border-w auf --border-w-stark. Das
   verschiebt nichts: das Feld steht in seiner eigenen Zeile, und seine
   Höhe hält --ctl-h. */
:is(input, select, textarea)[aria-invalid="true"] {
  border-color: var(--state-fail);
  border-width: var(--border-w-stark);
}

/* Deaktiviert: dieselbe Handschrift wie beim Knopf (base.css). */
.feld :is(input, select, textarea)[disabled] { opacity: .45; cursor: not-allowed; }

/* Formularaufbau. Ein Formular ist eine Spalte; zwei kurze Felder dürfen sich
   ab genügend Breite eine Zeile teilen. Unter der Schwelle stapeln sie —
   nebeneinander stehende Felder auf dem Telefon sind unbrauchbar.

   DIE SCHWELLE MISST DAS FORMULAR, NICHT DAS FENSTER. Vorher stand hier
   `@media (min-width: 560px)`, und das war am falschen Mass gemessen:
   ein Formular in einer schmalen Spalte legte zwei Felder nebeneinander,
   sobald nur der BILDSCHIRM breit genug war — in der Seitenspalte, im
   Fenster, in der Galeriekachel. Umgekehrt stapelte ein breites Formular
   auf einem schmalen Bildschirm auch dann, wenn es Platz gehabt hätte.

   Gemeldet von Vasco am 2026-09-27 an der Vorschau r8: „Warum brechen
   Formulare nicht auf die nächste Zeile um?" Das Fenster war breit, der
   Behälter auf 390px gestellt — die Abfrage sah nur das Fenster.

   Ein Baustein kennt seinen Behälter, nicht das Gerät. Das ist die
   allgemeine Fassung und gilt über dieses Formular hinaus: wo ein
   Baustein seine eigene Breite auswertet, misst er `@container`. */
/* Zwischen zwei Feldern --space-2xs (Vasco, 2026-10-08, G21: «übertriebenes Spacing von
   Eingabefeld zu Eingabefeld»). Jedes Feld hält unter sich schon die Zeile für seine Meldung frei
   (Zustand verschiebt nie); mit --space-md dazu standen zwei Felder 36 px auseinander, bei 4 px
   zwischen Beschriftung und Feld. Jetzt 24 px, und das Feld gehört sichtbar zu seiner Beschriftung. */
.formular {
  display: flex;
  flex-direction: column;
  gap: var(--space-2xs);
  container-type: inline-size;
}
/* In einem Formular enden alle Felder links und rechts bündig, ein-, zwei- oder dreispaltig. Die
   Breite bestimmt das Formular, nicht das Feld: .feld--breit deckelte bei --panel-w, und E-Mail und
   Telefon endeten 17 px vor dem Paar Vorname/Nachname darüber (Vasco, 2026-10-19: «die müssen
   immer … links und rechts total bündig enden»; dieselbe Falle am 2026-10-12 auf der Kontaktseite).
   Kurze Werte bekommen eine schmale Spalte in einer Reihe (--rechts-schmal), kein kürzeres Feld. */
.formular > :is(.feld, .formular__reihe, .feldgruppe) { width: 100%; }
/* Die Aktion steht abgesetzt unter dem letzten Feld. Ein Kästchen hält keine Meldezeile frei wie
   ein Feld, und «Platz reservieren» stand 4 px unter «Ich habe die Kursbedingungen gelesen»
   (Vasco, 2026-10-19). */
.formular > :is([type="submit"], :has(> [type="submit"]):not(:has(input:not([type="submit"]), select, textarea))) { margin-block-start: var(--space-md); }
.formular__reihe { display: grid; gap: var(--space-2xs) var(--space-md); }
@container (min-width: 35rem) {
  .formular__reihe { grid-template-columns: 1fr 1fr; }
  /* Kurze Werte (Nummer, PLZ) bekommen eine schmale Spalte neben dem langen (Vasco, 2026-10-08, H9). */
  .formular__reihe--rechts-schmal { grid-template-columns: 3fr 1fr; }
  .formular__reihe--links-schmal { grid-template-columns: 1fr 3fr; }
}
/* Ohne Behälter-Kontext — ein `.formular__reihe`, das irgendwo ohne
   `.formular` darüber steht — greift die Abfrage nie und die Felder
   stapeln. Das ist die richtige Vorgabe: stapeln geht immer, nebeneinander
   nur mit Platz. */

/* ================================================================
   B · DIE EINZELNEN BAUSTEINE
   ================================================================ */

/* ---------------------------------------------------------- Textfeld
   Mehrzeilig. Höhe zwei Bedienhöhen, nach unten ziehbar — quer ziehen ist
   gesperrt, das bricht sonst das Raster der Zeile. */
.feld textarea {
  min-height: calc(var(--ctl-h) * 2);
  resize: vertical;
  line-height: var(--leading-snug);
}

/* ---------------------------------------------------------- Eingabegruppe
   Zeichen davor, Knopf dahinter, als ein Block. Der Rahmen wandert vom
   Eingabefeld auf die Gruppe, damit Zeichen und Knopf mit im Rahmen stehen;
   das Feld darin wird durchsichtig. */
.feld .eingabegruppe {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-3xs) var(--space-3xs) var(--space-3xs) var(--space-sm);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-strong);
  border-radius: var(--r-sm);
  color: var(--ink-faint);
}
.feld .eingabegruppe:focus-within { border-color: var(--accent); }
.feld .eingabegruppe input {
  flex: 1;
  min-width: 0;
  min-height: var(--ctl-h-sm);
  padding: var(--space-2xs) 0;
  background: none;
  border: 0;
  border-radius: 0;
  outline: 0;
}
/* Der Fokusring sitzt an der Gruppe, nicht am nackten Feld darin — sonst
   liegt ein Ring mitten in einem Rahmen. */
.feld .eingabegruppe input:focus-visible { outline: none; }
/* Das Kreuz zum Leeren steht in der Reihe, vor dem Knopf (feld-leeren.js). Unsichtbar hält es
   seinen Platz, damit der Knopf beim ersten Zeichen nicht springt (Kapitel «Zustand verschiebt
   nie»). */
.feld .eingabegruppe > .feld__leeren { position: static; flex: none; width: var(--ctl-h-sm); height: var(--ctl-h-sm); }
.feld .eingabegruppe:has(input:not(:placeholder-shown, :disabled, [readonly])) > .feld__leeren { visibility: visible; }
/* Fehler: die Kante der Gruppe wird rot, wie bei jedem Feld. Am Feld darin wäre sie unsichtbar,
   es hat keine Kante (Vasco, 2026-10-19: «wenn ich das Feld verlasse, wird das Feld nicht rot»).
   Die stärkere Kante kommt als Kontur nach innen, damit die Gruppe nicht wächst. */
.feld .eingabegruppe:has(input[aria-invalid="true"]) {
  border-color: var(--state-fail);
  outline: var(--border-w) solid var(--state-fail);
  outline-offset: calc(var(--border-w) * -2);
}
.feld .eingabegruppe:has(input:focus-visible) {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}

/* ---------------------------------------------------------- Codefelder
   Sechs Kästchen für sechs Ziffern. Tabellarische Ziffern, damit eine 1 so
   breit steht wie eine 8 und die Kästchen nicht atmen. */
/* Die Reihe steckt in einem eigenen Behälter: `display:flex` direkt am
   Fieldset stellt in Browsern die Legende um, das ist keine Wette wert. */
/* Umbrechen statt hinausragen: sechs Felder zu je --tap-min plus Luecken
   brauchen 324 px, und bei 375 px Fensterbreite bleiben nach den Raendern
   331. Gemessen am 2026-09-28 ragte das sechste Feld 23 px hinaus und liess
   die ganze Seite seitwaerts rollen. */
.codefelder__zeile { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-xs); }
/* Die Meldezeile, die feld-pruefen.js nach dem ersten Feld einsetzt, steht unter den Feldern und
   nimmt leer keinen Platz. Sonst rückte das zweite Feld beim ersten Tippen um den Abstand der Zeile
   nach rechts (Vasco, 2026-10-09, J20). */
.codefelder__zeile > .feld__fehler { order: 1; flex-basis: 100%; }
.codefelder__zeile > .feld__fehler:empty { display: none; }
.codefelder input {
  width: var(--tap-min);
  min-height: var(--ctl-h-lg);
  padding-inline: 0;
  text-align: center;
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  font-size: var(--text-xl);
  font-weight: var(--fw-bold);
  color: var(--ink);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-strong);
  border-radius: var(--r-sm);
}
.codefelder input:focus-visible { border-color: var(--accent); }
.codefelder__strich {
  width: var(--space-xs);
  height: var(--border-w-stark);
  flex: none;
  background: var(--border-strong);
}

/* ---------------------------------------------------------- Haken
   Kästchen mit Häkchen. TFCZ hatte einen runden grünen Knopf mit Federung —
   das Grün ist hier für Zustände reserviert (ein Haken ist kein Messwert), die
   Federung fällt mit dem Bewegungsbudget weg. Was bleibt: Der Kasten füllt
   sich mit dem Akzentverlauf, das Häkchen erscheint. Nichts verschiebt sich. */
/* KÄSTCHEN IN SCHRIFTGRÖSSE, ZEILEN ENG (Vasco, 2026-10-08, G18: «die dürfen nicht grösser sein
   wie die Textfont» und «hier spacest du die Texte extrem breit»). Vorher war der Kasten 24 px
   neben 13 px Schrift, und jede Zeile trug min-height 44 px: vier Wahlen standen über 180 px
   verteilt. Jetzt 1em, mittig auf der ersten Zeile, und die Tastfläche von 44 px nur dort, wo
   ein Finger bedient (pointer: coarse). Gilt gleich für .wahl. */
.haken {
  position: relative;
  display: inline-flex;
  align-items: flex-start;
  gap: var(--space-xs);
  padding-block: var(--space-3xs);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--ink);
  cursor: pointer;
}
@media (pointer: coarse) {
  .haken, .wahl { min-height: var(--tap-min); align-items: center; }
}
.haken input { position: absolute; opacity: 0; width: 0; height: 0; }
.haken__kasten {
  width: 1em;
  height: 1em;
  margin-block-start: calc((1lh - 1em) / 2);
  flex: none;
  display: grid;
  place-items: center;
  /* Vorbild ist das Kästchen der Schreibfläche, das Vasco «perfekt» nannte (2026-10-08, H12): das
     Kästchen des Browsers mit accent-color. Leicht gerundet (die Hälfte von --r-xs), leer eine
     dunkle Fläche mit heller Kante, gewählt flach in --accent mit kräftigem Haken. Vorher eckiger
     mit Verlauf, und der Haken war zu dünn. */
  background: var(--surface-overlay);
  border: var(--border-w) solid var(--border-strong);
  border-radius: calc(var(--r-xs) / 2);
}
/* Das Häkchen wird GEZOGEN, nicht eingeblendet. Bis zum 2026-09-28 waren es
   zwei Kanten eines gedrehten Kastens, die von 0 auf 1 aufblendeten; das war
   billiger zu bauen und sagte nichts. Jetzt liegt der Lucide-Pfad „check" im
   Kasten, sein Strich steckt in einer Lücke seiner eigenen Länge und wird
   von dort hereingezogen. Vorlage war uiverse.io/kyle1dev/afraid-wasp-94,
   übernommen ist davon die Technik und sonst nichts.

   Der Strich trägt --accent-ink und liegt auf dem Akzentverlauf des
   gefüllten Kastens, also auf derselben Paarung wie jede Knopfschrift. */
.haken__zug {
  width: .8em;
  height: .8em;
  fill: none;
  stroke: var(--accent-ink);
  stroke-width: 3.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* 30 ist die Länge dieses Pfades, keine Gestaltungsgrösse: gemessen mit
     getTotalLength() sind es 27.6, aufgerundet, damit auch bei anderer
     Strichstärke nichts stehen bleibt. */
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
  /* --t-slow statt --t-base: mit 240 ms sah man das Zeichnen kaum (Vasco, 2026-10-08, H7). */
  transition: stroke-dashoffset var(--t-slow) var(--ease);
}
.haken input:checked ~ .haken__kasten {
  background: var(--accent);
  border-color: var(--accent);
}
.haken input:checked ~ .haken__kasten .haken__zug { stroke-dashoffset: 0; }
.haken input:focus-visible ~ .haken__kasten {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}
.haken input:disabled ~ .haken__kasten { opacity: .45; }
.haken:has(input:disabled) { cursor: not-allowed; color: var(--ink-faint); }

/* ---------------------------------------------------------- Herz
   Gefällt mir. Ein Kästchen ohne Kästchen: die Form des Zeichens IST der
   Zustand, gestrichen für offen und gefüllt für gesetzt. Genau das meint
   „Zustand verschiebt nie" mit Formänderung, und es trägt auch ohne Farbe.

   Vorlage war uiverse.io/catraco/plastic-moth-91. Weg sind das Rosa, das
   filter: brightness(1.5) auf dem Höhepunkt und das Überschwingen auf 1.2.

   Das Herz steht im Akzent und nicht in Rot. Chroma ist im System den
   gemessenen Zuständen vorbehalten, und „gefällt mir" ist keiner. */
.herz {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: var(--tap-min);
  font-size: var(--text-sm);
  color: var(--ink);
  cursor: pointer;
  /* Wer doppelt klickt, will zweimal drücken und nicht den Text auswählen. */
  user-select: none;
  -webkit-user-select: none;
}
.herz input { position: absolute; opacity: 0; width: 0; height: 0; }
@media (pointer: coarse) { .herz--still { min-width: var(--tap-min); justify-content: center; } }
/* So breit wie das Zeichen, damit das Herz bündig mit dem Text darüber beginnt (Vasco, 2026-10-08
   Nacht, H11). Mit --ctl-h stand es 13 px eingerückt. Die Tastfläche trägt das ganze label, und
   ohne Wort (.herz--still) bekommt der Finger seine 44 px über pointer: coarse. */
.herz__bild {
  position: relative;
  width: var(--icon);
  height: var(--icon);
  flex: none;
  display: grid;
  place-items: center;
  border-radius: var(--r-sm);
  color: var(--ink-muted);
}
/* Beide Fassungen liegen im selben Rasterfeld: nichts verschiebt sich beim
   Wechsel, und die gefüllte muss nicht erst Platz finden. */
.herz__bild svg {
  grid-area: 1 / 1;
  width: var(--icon);
  height: var(--icon);
  transition: opacity var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.herz__voll { fill: currentColor; opacity: 0; }
.herz:hover .herz__bild { color: var(--ink); }
.herz input:checked ~ .herz__bild { color: var(--accent); }
.herz input:checked ~ .herz__bild .herz__leer { opacity: 0; }
.herz input:checked ~ .herz__bild .herz__voll { opacity: 1; }
.herz input:focus-visible ~ .herz__bild {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}
.herz input:disabled ~ .herz__bild { opacity: .45; }
.herz:has(input:disabled) { cursor: not-allowed; color: var(--ink-faint); }

/* Der Ring darf rund sein: Rundheit ist hier seine Aussage, er ist kein
   Bedienelement. Er springt einmal heraus und ist dann weg. */
.herz__ring {
  position: absolute;
  /* Der Ring greift über das Zeichen hinaus, seit das Bild so klein ist wie das Zeichen (H11). */
  inset: calc(-1 * var(--space-xs));
  border: var(--border-w-stark) solid var(--accent);
  border-radius: var(--r-pill);
  opacity: 0;
  pointer-events: none;
}
.herz input:checked ~ .herz__bild .herz__ring {
  animation: herz-ring var(--t-slow) var(--ease) forwards;
}
@keyframes herz-ring {
  from { transform: scale(.4); opacity: 1; }
  to   { transform: scale(1.4); opacity: 0; }
}
/* Nur das Zeichen, ohne Wort. Der lesbare Text steht dann im aria-label. */
.herz--still { gap: 0; }
/* ------------------------------------------------- Herz mit Partikeln
   Bis zum 2026-10-08 der Baustein „Partikelknopf“ in navigation.css (D8).
   Die Punkte springen aus der Mitte des Herzens und verschwinden. Sie liegen
   darüber und nehmen keine Klicks an, sonst fängt ein fliegender Punkt den
   nächsten Klick ab.
   Gross, langsam, kräftig: 4 px in --accent und in 480 ms vorbei waren zu
   wenig Unterschied und zu schnell (Vasco, 2026-09-25: „ist viel zu wenig
   kontrastreich und viel zu schnell"). Jetzt 8 px, --t-lang, und eine
   Kante in der Grundfläche, damit die Punkte über jeder Fläche stehen. */
.herz__zahl { font-variant-numeric: tabular-nums; color: var(--ink-muted); }
.herz input:checked ~ .herz__zahl { color: var(--ink); }
.herz__punkt {
  position: absolute;
  inset-inline-start: 50%;
  top: 50%;
  width: var(--space-xs);
  height: var(--space-xs);
  margin: calc(var(--space-xs) / -2);
  border-radius: var(--r-pill);
  background: var(--accent);
  box-shadow: 0 0 0 var(--border-w) var(--surface-base);
  pointer-events: none;
  animation: herz-partikel var(--t-lang) var(--ease) forwards;
}
/* Die Punkte bleiben länger gross und schrumpfen erst am Ende: so sieht
   man den Flug und nicht nur ein Aufblitzen. */
@keyframes herz-partikel {
  0%   { transform: translate(0, 0) scale(.4); opacity: 0; }
  15%  { transform: translate(calc(var(--weg-x) * .2), calc(var(--weg-y) * .2)) scale(1); opacity: 1; }
  70%  { opacity: 1; }
  100% { transform: translate(var(--weg-x), var(--weg-y)) scale(.2); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .herz__punkt { display: none; }
}

/* ---------------------------------------------------------- Wahlgruppe
   Genau eine Wahl. Seit 2026-10-08 ohne runden Knopf (Vasco, G18: «diese runde Bedienung will
   ich einfach nicht mehr»): die Wahl sieht aus wie ein Kästchen mit Haken, gleich gross wie .haken
   und mit demselben gezogenen Haken. Dass nur eine geht, sagt das Verhalten: das Feld bleibt ein
   echtes <input type="radio">, die Pfeiltasten wechseln, und ein Klick auf eine andere nimmt die
   erste heraus. Die Klasse heisst aus Rücksicht auf die Projekte weiter .wahl__kreis.

   Der gewählte Eintrag trägt zwei Merkmale, die gefüllte Fläche und den Haken, dazu die eigene
   Auszeichnung des Browsers. Niemals Farbe allein. */
.wahlgruppe__liste { display: flex; flex-direction: column; gap: 0; }
.wahl {
  position: relative;
  display: inline-flex;
  align-items: flex-start;
  gap: var(--space-xs);
  padding-block: var(--space-3xs);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  color: var(--ink);
  cursor: pointer;
}
.wahl input { position: absolute; opacity: 0; width: 0; height: 0; }
.wahl__kreis {
  width: 1em;
  height: 1em;
  margin-block-start: calc((1lh - 1em) / 2);
  flex: none;
  display: grid;
  place-items: center;
  background: var(--surface-overlay);
  border: var(--border-w) solid var(--border-strong);
  border-radius: calc(var(--r-xs) / 2);
}
/* Der Haken als Maske (Lucide check), aufgedeckt von links nach rechts: zuerst der kurze, dann
   der lange Strich, wie bei .haken__zug. Eine Maske, weil die Projekte hier kein SVG im Markup
   haben; das Datenblatt zeichnet dieselbe Klasse. */
.wahl__kreis::after {
  content: "";
  width: .8em;
  height: .8em;
  background: var(--accent-ink);
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--t-slow) var(--ease);
}
.wahl input:checked ~ .wahl__kreis {
  background: var(--accent);
  border-color: var(--accent);
}
.wahl input:checked ~ .wahl__kreis::after { clip-path: inset(0); }
.eigenauswahl[data-mehrfach] .eigenauswahl__wert { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
/* KEIN Gewichtswechsel. Gemessen am 2026-09-24: 400 -> 600 machte den Text
   5.4 px breiter, die Liste wuchs mit und schob die Nachbarspalte um
   dieselben 5.4 px zur Seite. Der Zustand ist ohnehin schon an der Form zu
   sehen — der Punkt ist gefuellt, der Ring traegt --accent. Farbe aendert
   keine Breite, Gewicht schon. (Systemregel: „Zustand verschiebt nie".) */
.wahl input:checked ~ .wahl__text { color: var(--ink); }
.wahl input:focus-visible ~ .wahl__kreis {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}
.wahl__text { min-width: 0; }
/* Ein Stand mit Zeichen in der Liste und in der Kopfzeile der Eigenauswahl,
   das Zeichen in der Farbe seines Zustands, wie bei .marke--zustand und
   .wahlchip (Teil CRM). formular-auswahl.js kopiert das Zeichen mit. */
:is(.wahl__text, .eigenauswahl__wert)[data-ton-zustand] { display: inline-flex; align-items: center; gap: var(--space-2xs); }
:is(.wahl__text, .eigenauswahl__wert)[data-ton-zustand] > .symbol { flex: none; color: var(--ink-muted); }
:is(.wahl__text, .eigenauswahl__wert)[data-ton-zustand="pass"] > .symbol { color: var(--state-pass); }
:is(.wahl__text, .eigenauswahl__wert)[data-ton-zustand="warn"] > .symbol { color: var(--state-warn); }
:is(.wahl__text, .eigenauswahl__wert)[data-ton-zustand="fail"] > .symbol { color: var(--state-fail); }
:is(.wahl__text, .eigenauswahl__wert)[data-ton-zustand="akzent"] > .symbol { color: var(--accent); }

/* ---------------------------------------------------------- Schalter
   An oder aus, wirkt sofort. TFCZ hatte eine Pille mit rundem Knopf; hier
   laufen Spur und Griff auf der Radienskala (8 und 3) — ein Schalter ist ein
   Bedienelement, und für die gilt „keine Pille". Bewegen darf sich der Griff:
   seine Lage IST der Zustand, das ist keine Zierde. */
.schalter { display: inline-flex; align-items: center; gap: var(--space-sm); vertical-align: middle; }
.schalter__spur {
  position: relative;
  width: var(--ctl-h-lg);
  height: var(--space-lg);
  flex: none;
  /* Gefüllte Spur statt leerer mit Kante, 2026-09-28. Vorher stand ein
     heller Kasten mit dunkler Kante da und darin ein dunkler Griff — aus
     zwei Metern liest sich das als leeres Feld. Gefüllt ist die Spur die
     Fläche und der helle Griff die Marke darauf, und „aus" und „an"
     unterscheiden sich in der Fläche statt nur in der Griffstellung.
     Vorlage war uiverse.io/RaspberryBee/calm-deer-81; Masse und Bewegung
     stimmten schon überein, geändert ist allein die Füllung. */
  background: var(--border-strong);
  border: var(--border-w) solid transparent;
  border-radius: var(--r-md);
}
.schalter__spur input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.schalter__griff {
  position: absolute;
  /* Mittig gerechnet statt auf einen festen Abstand gesetzt: die Spur traegt
     eine Kante, dadurch war ihr Innenraum 22 statt 24 px und der Griff sass
     1 px zu hoch (Vasco, 2026-09-24: „Schalter nicht mittig"). */
  top: 50%;
  translate: 0 -50%;
  inset-inline-start: var(--space-2xs);
  width: var(--space-md);
  height: var(--space-md);
  border-radius: var(--r-xs);
  background: var(--surface-raised);
  pointer-events: none;
  transition: transform var(--t-base) var(--ease), background-color var(--t-fast) var(--ease);
}
.schalter__spur:has(input:checked) {
  background: linear-gradient(to bottom, var(--accent-lift) 0%, var(--accent) 100%);
  border-color: var(--accent);
}
/* 52 − 16 − 8 = 28: Spurbreite minus Griff minus beide Einzüge. */
.schalter__spur:has(input:checked) .schalter__griff {
  transform: translateX(calc(var(--ctl-h-lg) - var(--space-md) - var(--space-xs)));
}
.schalter__spur:has(input:focus-visible) {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}
.schalter__text { font-size: var(--text-sm); color: var(--ink); cursor: pointer; }

/* ---------------------------------------------------------- Regler
   TFCZ zeichnete Spur und Griff mit sechs Hersteller-Selektoren nach. Das ist
   hier ersetzt durch `accent-color`: der Browser füllt die Spur selbst bis zum
   Griff, in der Akzentfarbe. Weniger CSS, und der Griff bleibt der, den das
   Betriebssystem für Finger und Tastatur vorsieht. */
/* Höhe --space-lg mit der Maus, --tap-min nur für den Finger (Vasco, 2026-10-08, G25: «zu hoher
   Abstand zum Titel»). Die Spur ist 4 px hoch und stand mittig in 44 px, also 20 px unter dem
   Titel, während der Schalter mit 4 px an seinem Namen steht. Der Griff des Browsers ist 16 bis
   20 px gross und passt in --space-lg. */
.feld input[type="range"] {
  width: 100%;
  min-height: var(--space-lg);
  padding: 0;
  background: none;
  border: 0;
  accent-color: var(--accent);
  cursor: pointer;
}
@media (pointer: coarse) { .feld input[type="range"] { min-height: var(--tap-min); } }
.regler__kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
}
.regler__kopf label { font-size: var(--text-xs); font-weight: var(--fw-medium); color: var(--ink); }
.regler__wert {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  font-size: var(--text-md);
  font-weight: var(--fw-bold);
  color: var(--ink);
}
.regler__skala {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-2xs);
  color: var(--ink-faint);
}

/* ---------------------------------------------------------- Eigenauswahl
   Eigene Auswahlliste, wenn die Einträge mehr zeigen sollen als eine Zeile
   Text. Auf- und Zuklappen macht `<details>`, die Wahl machen echte
   Auswahlknöpfe — beides ohne Skript und ohne nachgebaute Tastatursteuerung.
   Das Skript trägt nur den gewählten Text in die Kopfzeile. */
.eigenauswahl { position: relative; }
.eigenauswahl > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  min-height: var(--ctl-h);
  padding: var(--space-2xs) var(--space-sm);
  font-size: var(--text-sm);
  color: var(--ink);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-strong);
  border-radius: var(--r-sm);
  cursor: pointer;
  list-style: none;
}
.eigenauswahl > summary::-webkit-details-marker { display: none; }
.eigenauswahl[open] > summary { border-color: var(--accent); }
.eigenauswahl__pfeil { color: var(--ink-faint); transition: transform var(--t-fast) var(--ease); }
.eigenauswahl[open] .eigenauswahl__pfeil { transform: rotate(180deg); }
/* Die Liste geht UNTER dem Feld auf und deckt es nie zu. Ist unten zu
   wenig Platz, kippt sie nach oben — auch dann bleibt das Feld frei
   (Vasco, 2026-09-25: „das Feld darf niiiiiiiiemals überlappt werden.
   Dropdown muss entweder darüber oder darunter aufgehen").
   Die Richtung setzt das Skript als data-richtung, weil CSS nicht weiss,
   wie viel Luft unter dem Feld ist. Ohne Skript bleibt es bei unten. */
.eigenauswahl__liste {
  position: absolute;
  top: calc(100% + var(--space-2xs));
  inset-inline-start: 0;
  inset-inline-end: 0;
  z-index: var(--z-aufgeklappt);
  display: flex;
  flex-direction: column;
  padding: var(--space-2xs);
  /* Liegt über dem Inhalt: Fläche und Kante je eine Stufe höher als alles,
     worauf es zu liegen kommt. Seit Runde 8 (E3) tragen sie allein. */
  background: var(--surface-overlay);
  border: var(--border-w-stark) solid var(--border-strong);
  border-radius: var(--r-md);
  /* Eine lange Liste rollt in sich (Vasco, 2026-10-08, G27). Höchstens 18rem, damit möglichst
     viele Einträge auf einmal zu sehen sind, und nie mehr als die halbe Fensterhöhe. Der Rollbalken
     bekommt seine eigene Spur rechts (scrollbar-gutter) und liegt nie über dem Text. */
  max-height: min(18rem, 50dvh);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
}
.eigenauswahl[data-richtung="oben"] .eigenauswahl__liste {
  top: auto;
  bottom: calc(100% + var(--space-2xs));
}
.eigenauswahl__liste .wahl {
  width: 100%;
  min-height: var(--ctl-h-sm);
  padding: var(--space-2xs) var(--space-xs);
  border-radius: var(--r-xs);
}
/* Hervorhebung unter dem Zeiger in der abgesetzten Fläche, nicht in der matten
   Akzentfläche — die braucht auf einer Fläche eine Kante, und ein Kasten im
   Kasten macht die Liste unruhig. */
/* Relative Füllung: die Liste liegt seit Runde 8 (E3) selbst auf
   --surface-overlay, ein Hover in derselben Farbe wäre unsichtbar (Kapitel 4.2). */
.eigenauswahl__liste .wahl:hover { background: var(--surface-state); }

/* Auswahllisten ohne Punkte (Vasco, 2026-10-08: «Sowas will ich nicht mehr sehen, ist richtig
   unnötig und redundant. Es gibt allgemeine Verhalten für Listen»). Eine Liste zum Aufklappen
   zeigt die Wahl wie ein Menü (.tabmenue__eintrag): Fläche und ein Haken am Ende. Punkte bleiben
   nur bei offen stehenden Wahlgruppen, wo man alle Möglichkeiten zugleich sieht. Die
   Mehrfachauswahl behält ihre Kästchen: dort wird abgehakt, nicht gewählt. check.mjs Regel 40
   verbietet .wahl__kreis in einer Eigenauswahl ohne data-mehrfach. Der Kreis wird zusätzlich
   versteckt, weil Projekte ihn noch im Markup tragen können. */
.eigenauswahl:not([data-mehrfach]) .eigenauswahl__liste .wahl__kreis { display: none; }
.eigenauswahl:not([data-mehrfach]) .eigenauswahl__liste .wahl { align-items: center; }
.eigenauswahl:not([data-mehrfach]) .eigenauswahl__liste .wahl:has(input:checked) { background: var(--surface-state); }
.eigenauswahl:not([data-mehrfach]) .eigenauswahl__liste .wahl:has(input:checked)::after {
  content: "";
  flex: none;
  margin-inline-start: auto;
  inline-size: var(--icon);
  block-size: var(--icon);
  background: currentColor;
  /* Lucide check, als Maske: so trägt der Haken die Schriftfarbe und keine eigene. */
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Ohne Kreis zeigt die Zeile selbst den Tastatur-Fokus. */
.eigenauswahl:not([data-mehrfach]) .eigenauswahl__liste .wahl:has(input:focus-visible) {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}

/* ---------------------------------------------------------- Datumsfeld
   Eigener Kalender statt `type="date"`. Die Begruendung von gestern
   („nativ bringt Kalender, Tastatur und Landesformat mit") war nur die halbe
   Wahrheit: die aufgeklappte Flaeche zeichnet das Betriebssystem, sie ist
   nicht gestaltbar und stand als Fremdmarke mitten im System
   (Vasco, 2026-09-25: „falsche kalender design"). Das ist dieselbe Regel,
   die schon die Eigenauswahl gegen das native Auswahlfeld entschieden hat.

   Getippt wird weiter in ein echtes Textfeld, damit ein Datum auch ohne
   Kalender eingegeben werden kann. Das Blatt oeffnet immer UNTER dem Feld
   und schliesst rechts beim Knopf ab. Es braucht ein Skript, weil CSS keine
   Monatslaengen kennt: begruendet in meta.json. */
/* Heisst .datumsfeld und NICHT .kalender: unter dem Namen gibt es
   bereits einen Baustein in „Daten & Inhalt", einen ganzen Monat zum
   Ansehen. Meine Regel stand spaeter im erzeugten CSS und hat ihm
   display:flex aufgezwungen — Kopf und Tabelle standen dadurch
   nebeneinander statt untereinander, und das Raster lief aus der Karte
   (Vasco, 2026-09-25 mit Bild). Zwei Bausteine, zwei Namen. */
.datumsfeld { position: relative; display: flex; }
/* Seit die Beschriftung im Feld steht, liegt zwischen .datumsfeld und der
   Eingabe ein Wickel. Der traegt die Breite, nicht mehr die Eingabe. */
.datumsfeld > .feld__eingabe { flex: 1 1 auto; min-width: 0; }
.datumsfeld__eingabe { min-width: 0; }
/* Der Knopf sitzt IM Feld, nicht daneben: sonst haette das Datumsfeld eine
   andere Aussenform als jedes andere Feld im System. */
/* --ctl-h, nicht --ctl-h-sm: die kleinere Fassung war 36 px breit und lag
   damit unter --tap-min. Eine Mindestgroesse, die man selbst unterschreitet,
   ist keine. Gleiche Groesse wie .feld__leeren, das an derselben Stelle
   sitzt und dasselbe tut. */
/* Unten angebunden, wie `.feld__leeren` (grundlagen.css): der Wickel umfasst
   seit der Umstellung auch die Beschriftung darüber. `inset-block` streckte
   den Knopf bis zum 2026-09-29 über beide, 66 statt 44 px, und das Zeichen
   sass 12 px über der Mitte des Feldes. */
.datumsfeld__knopf {
  position: absolute;
  inset-block-end: var(--border-w);
  inset-inline-end: var(--border-w);
  width: var(--ctl-h);
  height: calc(var(--ctl-h) - 2 * var(--border-w));
  display: grid;
  place-items: center;
  color: var(--ink-faint);
  background: none;
  border: 0;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  cursor: pointer;
}
/* Der reine Farbwechsel laeuft ueber --t-sofort: unter 100 ms liest er sich
   als Antwort, darueber als Animation. Vorher sprang er in 0 ms. */
.datumsfeld__knopf { transition: color var(--t-sofort) var(--ease); }
.datumsfeld__knopf:hover { color: var(--ink); }
.datumsfeld__eingabe { padding-inline-end: var(--ctl-h); }

.datumsfeld__blatt {
  position: absolute;
  top: calc(100% + var(--space-2xs));
  inset-inline-end: 0;
  z-index: var(--z-aufgeklappt);
  width: max-content;
  padding: var(--space-sm);
  /* Liegt über dem Inhalt: Fläche und Kante je eine Stufe höher. Runde 8 (E3). */
  background: var(--surface-overlay);
  border: var(--border-w-stark) solid var(--border-strong);
  border-radius: var(--r-md);
  user-select: none;
}
.datumsfeld__blatt[hidden] { display: none; }

.datumsfeld__steuer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-bottom: var(--space-2xs);
}
.datumsfeld__monat {
  font-weight: var(--fw-semibold);
  font-size: var(--text-sm);
  color: var(--ink);
}
.datumsfeld__schritt {
  width: var(--ctl-h-sm);
  height: var(--ctl-h-sm);
  display: grid;
  place-items: center;
  color: var(--ink-muted);
  background: none;
  border: var(--border-w) solid var(--border-subtle);
  border-radius: var(--r-xs);
  cursor: pointer;
}
.datumsfeld__schritt:hover:not(:disabled) { color: var(--ink); border-color: var(--border-strong); }
.datumsfeld__schritt:disabled { opacity: .4; cursor: default; }

.datumsfeld__raster { display: grid; grid-template-columns: repeat(7, 1fr); gap: var(--space-3xs); }
.datumsfeld__wochentag {
  font-size: var(--text-2xs);
  color: var(--ink-faint);
  text-align: center;
  padding-block: var(--space-3xs);
}
.datumsfeld__tag {
  min-width: var(--ctl-h-sm);
  height: var(--ctl-h-sm);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: var(--r-xs);
  cursor: pointer;
}
/* Relative Füllung: der Kalender liegt selbst auf --surface-overlay. */
.datumsfeld__tag:hover:not(:disabled) { background: var(--surface-state); }
.datumsfeld__tag:disabled { color: var(--ink-faint); cursor: default; }
/* Heute traegt einen Ring nach innen statt einer Kante: eine Kante wuerde den
   Tag um zwei Pixel wachsen lassen und die ganze Zeile verschieben. */
.datumsfeld__tag--heute { box-shadow: inset 0 0 0 var(--border-w) var(--border-strong); }
/* Der gewaehlte Tag wechselt Flaeche und Schriftfarbe, nie das Gewicht:
   „Solche pushes duerfen nie passieren". breite-stabil */
.datumsfeld__tag[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-ink);
}
.datumsfeld__tag[aria-pressed="true"]:hover { background: var(--accent-hover); }

.datumsfeld__fuss {
  display: flex;
  justify-content: space-between;
  gap: var(--space-sm);
  margin-top: var(--space-2xs);
  padding-top: var(--space-2xs);
  border-top: var(--border-w) solid var(--border-subtle);
}

/* ---------------------------------------------------------- Suchauswahl
   Tippen filtert die Liste. Das macht `<datalist>` — nativ, mit Tastatur, mit
   Vorlesetexten, und eigene Angaben bleiben erlaubt. Die nachgebaute Liste von
   TFCZ hatte keine Tastatursteuerung und brauchte Skript. */
.feld input[list]::-webkit-calendar-picker-indicator { opacity: .7; cursor: pointer; }
.feld input[list]::-webkit-calendar-picker-indicator:hover { opacity: 1; }

/* ---------------------------------------------------------- Fragebogen
   Fragen Schritt für Schritt. Die Bühne hält ihre Höhe, damit Knöpfe beim
   Weiterklicken dort bleiben, wo der Zeiger schon steht. */
/* Schmaler und enger (Vasco, 2026-10-09, J24: «übertrieben breit, das White Spacing ist zu gross»). */
.fragebogen {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  width: min(var(--panel-w) + var(--space-2xl), 100%);
  padding: var(--space-md);
  background: var(--surface-raised);
  border-radius: var(--r-md);
}
.fragebogen__balken {
  height: var(--space-xs);
  border-radius: var(--r-xs);
  background: var(--surface-overlay);
  overflow: hidden;
}
.fragebogen__balken > span {
  display: block;
  height: 100%;
  background: var(--accent);
  transform-origin: left;
  transition: transform var(--t-base) var(--ease);
}
/* ALLE SCHRITTE LIEGEN IN DERSELBEN ZELLE, der aktive ist sichtbar. Die Bühne
   ist damit so hoch wie ihr höchster Schritt, auf jedem Schritt gleich, und
   „Weiter" bleibt dort, wo der Zeiger steht (Kapitel 3.5).
   Bis zum 2026-09-29 stand hier eine Mindesthöhe aus zwei Tokens mit `vw`
   darin: sie hing an der Fensterbreite und nicht am Inhalt, und gemessen
   sprang das Formular trotzdem zwischen 348 und 332 px. Vasco: „das Fenster
   wird höher, das Spacing unten ist viel zu hoch". `visibility` statt
   `display`: der verborgene Schritt behält seine Masse, nimmt aber weder
   Fokus noch Vorleseprogramm mit. */
.fragebogen__buehne { display: grid; }
.fragebogen__buehne > * { grid-area: 1 / 1; }
.fragebogen__schritt { display: flex; flex-direction: column; gap: var(--space-sm); visibility: hidden; }
.fragebogen__schritt[data-aktiv] { visibility: visible; }
.fragebogen__zahl {
  margin: 0;
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--ink-muted);
}
.fragebogen__nav { display: flex; align-items: center; gap: var(--space-xs); }
.fragebogen__luecke { flex: 1; }
.fragebogen__fertig {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  padding-block: var(--space-xs);
  text-align: center;
}
/* Auch der Abschluss liegt in der Zelle und trägt zur Höhe bei, darum wird
   er nicht entfernt, sondern nur unsichtbar. `[hidden]` allein hiesse
   display: none, und das Formular schrumpfte beim letzten Schritt. */
.fragebogen__fertig[hidden] { display: flex; visibility: hidden; }
.fragebogen__weiterweg { display: flex; flex-wrap: wrap; gap: var(--space-xs); justify-content: center; }
.fragebogen__haken { width: var(--space-xl); height: var(--space-xl); color: var(--state-pass); }

/* Auswahl in Kachelform, für „mehrere möglich". Echte Kästchen darunter: die
   Gruppe ist damit ohne Skript bedienbar und wird korrekt vorgelesen.
   Gewählt heisst: matte Fläche MIT Kante im tiefen Ton plus volles Gewicht. */
.kachelwahl { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.kachelwahl__feld {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: var(--ctl-h-sm);
  padding: var(--space-2xs) var(--space-sm);
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  color: var(--ink);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-strong);
  border-radius: var(--r-xs);
  cursor: pointer;
}
.kachelwahl__feld input { position: absolute; opacity: 0; width: 0; height: 0; }
/* KEIN Gewichtswechsel — die Kachel umschliesst ihren Text, fett wuerde sie
   breiter machen und die Nachbarkacheln zur Seite schieben (Vasco,
   2026-09-24). Der Zustand steht schon an Flaeche und Kante. */
.kachelwahl__feld:has(input:checked) {
  background: var(--accent-soft);
  border-color: var(--accent);
}
.kachelwahl__feld:has(input:focus-visible) {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}

/* ---------------------------------------------------------- Dateiablage
   Hineinziehen oder anklicken. TFCZ nahm dafür ein `<div role="button">` mit
   eigenem Tastenabfang. Hier ist es eine Beschriftung zu einem echten
   `<input type="file">`: Klick, Leertaste, Eingabetaste und Vorlesetext
   kommen vom Browser, das Skript behandelt nur noch das Fallenlassen und die
   Liste. */
.dateiablage {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  width: min(calc(var(--panel-w) + var(--space-4xl)), 100%);
}
/* Sichtbar versteckt, aber erreichbar: `clip-path` nimmt das Feld aus dem
   Bild, ohne es aus der Tabulatorfolge zu nehmen. */
.dateiablage__eingabe {
  position: absolute;
  width: var(--border-w);
  height: var(--border-w);
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.dateiablage__zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2xs);
  padding: var(--space-lg) var(--space-md);
  text-align: center;
  font-size: var(--text-sm);
  color: var(--ink-muted);
  border: var(--border-w-stark) dashed var(--border-strong);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
.dateiablage__zone > b { color: var(--ink); font-weight: var(--fw-bold); }
.dateiablage__zone .symbol { width: var(--space-lg); height: var(--space-lg); color: var(--accent); }
.dateiablage__zone:hover,
.dateiablage.ist-ueber .dateiablage__zone {
  border-color: var(--accent);
  /* --surface-state über der Akzentfläche, wie .btn--zwei in base.css: eine
     Ablage steht oft in einem Fenster, und dort ist die reine Akzentfläche
     im dunklen Modus von --surface-overlay nicht zu unterscheiden. */
  background-color: var(--accent-soft);
  background-image: linear-gradient(var(--surface-state), var(--surface-state));
}
.dateiablage:has(.dateiablage__eingabe:focus-visible) .dateiablage__zone {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}
/* Die schmale Fassung für Werkzeuge: eine Zeile statt einer Fläche.
   Vasco, 2026-10-03, zur Ablage im Vertriebscockpit: „braucht immer zu viel
   Platz". Zeichen, Aufforderung und Hinweis stehen nebeneinander; die
   abgelegten Dateien gehen in die Liste des Eintrags (.anhang-liste), und
   die Erfolgszeile hier verschwindet nach 2.5 s (formular-dateiablage.js).
   Fehler bleiben stehen, bis man sie gelesen hat. */
.dateiablage--kompakt { width: 100%; }
.dateiablage--kompakt .dateiablage__zone {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-start;
  align-items: center;
  gap: var(--space-2xs) var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  text-align: start;
}
.dateiablage--kompakt .dateiablage__zone > b { font-weight: var(--fw-medium); }
.dateiablage--kompakt .dateiablage__zone .symbol { width: var(--icon); height: var(--icon); }
.dateiablage--kompakt .dateiablage__zone span { font-size: var(--text-xs); color: var(--ink-muted); }
.dateiablage--kompakt .dateiablage__liste:empty { display: none; }
.dateiablage__liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-xs); }
.dateiablage__liste li {
  --p: 0;
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-raised);
  border: var(--border-w) solid var(--border-subtle);
  border-radius: var(--r-sm);
}
/* Registriert, damit der Anteil INTERPOLIERT statt bei jedem Schritt zu
   springen. Ohne das ist --p für den Browser ein Text, und ein Text lässt
   sich nicht animieren. */
@property --p { syntax: '<number>'; inherits: true; initial-value: 0; }

/* Die Vorschau IST der Fortschritt. Ein zweiter Streifen darunter sagte
   dasselbe ein zweites Mal; der Platz trägt jetzt die Auskunft, welche Datei
   das überhaupt ist. Zwei Ebenen: der gedämpfte Geist liegt unten, die volle
   Fassung wird von unten so weit aufgedeckt, wie übertragen ist. */
.dateiablage__kachel {
  position: relative;
  flex: none;
  width: var(--ctl-h-sm);
  height: var(--ctl-h-sm);
  border-radius: var(--r-xs);
  overflow: hidden;
  background: var(--surface-overlay);
}
.dateiablage__kachel > * {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: grid;
  place-items: center;
}
.dateiablage__kachel .symbol { width: var(--icon); height: var(--icon); }
.dateiablage__geist { filter: grayscale(1) brightness(0.4); color: var(--border-strong); }
.dateiablage__echt {
  clip-path: inset(calc((1 - var(--p)) * 100%) 0 0 0);
  color: var(--accent);
  transition: clip-path var(--t-base) var(--ease);
}
/* Ohne Bild bleibt der Geist die Fläche; das aufgedeckte Zeichen braucht
   keinen zweiten Untergrund darüber. */
.dateiablage__echt:not(img) { background: none; }

.dateiablage__text { min-width: 0; flex: 1; }
.dateiablage__name {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  overflow-wrap: anywhere;
}
.dateiablage__mass { font-size: var(--text-2xs); color: var(--ink-muted); font-variant-numeric: tabular-nums; }

/* Der Zustand steht in der Zeile, nicht auf der Kachel: dort verdeckte er
   genau die Ecke, an der der Fortschritt abzulesen ist. Beide Zeichen liegen
   übereinander, damit die Zeile beim Wechsel nicht breiter wird. */
.dateiablage__zustand { position: relative; flex: none; width: var(--icon); height: var(--icon); }
.dateiablage__zustand > span {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-base) var(--ease);
}
.dateiablage__zustand .symbol { width: var(--icon); height: var(--icon); }
.dateiablage__liste li.ist-fertig .dateiablage__zustand .ist-gut,
.dateiablage__liste li.ist-fehler .dateiablage__zustand .ist-schlecht { opacity: 1; transform: none; }
.dateiablage__liste li.ist-fertig .dateiablage__zustand .ist-gut { color: var(--state-pass); }
.dateiablage__liste li.ist-fehler .dateiablage__zustand .ist-schlecht { color: var(--state-fail); }

.dateiablage__liste li.ist-fehler { border-color: var(--state-fail); }
/* Gewicht raus, aus demselben Grund: die Zeile umschliesst ihren Inhalt. */
.dateiablage__liste li.ist-fehler .dateiablage__mass { color: var(--state-fail); }
/* Abgewiesen heisst: nichts ist übertragen. Die Kachel bleibt der Geist. */
.dateiablage__liste li.ist-fehler .dateiablage__echt { clip-path: inset(100% 0 0 0); }

/* ---------------------------------------------------------- Anzahlwahl
   Anzahl Personen. TFCZ hatte zwei eigene Knöpfe für Plus und Minus; hier ist
   die Wahrheit ein `type="number"` mit Grenzen — damit zählen Pfeiltasten,
   Mausrad und die Tastatur des Telefons von selbst, und es gibt eine
   Beschriftung. Der Stapel darüber zeigt nur, was im Feld steht.

   Runde Köpfe sind zulässig: das ist eine Bildmaske, kein Bedienelement —
   ein Kopf ist rund. */
.anzahlwahl {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-md);
  width: min(calc(var(--panel-w) + var(--space-4xl)), 100%);
  padding: var(--space-sm) var(--space-md);
  background: var(--surface-raised);
  border-radius: var(--r-md);
}
.anzahlwahl > .hinweis { flex-basis: 100%; }
.anzahlwahl__stapel { display: flex; min-width: var(--space-xl); }
.anzahlwahl__kopf {
  width: var(--space-xl);
  height: var(--space-xl);
  margin-inline-start: calc(var(--space-xs) * -1);
  display: grid;
  place-items: center;
  font-size: var(--text-2xs);
  font-weight: var(--fw-bold);
  background: var(--accent);
  color: var(--accent-ink);
  border: var(--border-w-stark) solid var(--surface-raised);
  border-radius: var(--r-pill);
}
.anzahlwahl__kopf:first-child { margin-inline-start: 0; }
.anzahlwahl__zahl { font-size: var(--text-sm); color: var(--ink); font-variant-numeric: tabular-nums; }
/* EIN RAHMEN, drei Fächer: Minus, Zahl, Plus. Bis zum 2026-09-29 standen
   drei einzelne Kästen nebeneinander, die Zahl klebte links in ihrem
   (Vasco: „gar keine schöne UI, der Zähler ist nicht mittig"). Gebaut wie
   die Eingabegruppe: die Kante trägt der Wickel, die Teile darin keine. */
.anzahlwahl__feld {
  display: inline-grid;
  grid-template-columns: var(--ctl-h) minmax(var(--ctl-h), auto) var(--ctl-h);
  align-items: center;
  min-height: var(--ctl-h);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-strong);
  border-radius: var(--r-sm);
  overflow: clip;
}
.anzahlwahl__feld label { font-size: var(--text-xs); font-weight: var(--fw-medium); color: var(--ink); }
.anzahlwahl__feld .anzahlwahl__schritt {
  align-self: stretch;
  width: auto;
  height: auto;
  border: 0;
  border-radius: 0;
  background: none;
}
.anzahlwahl__feld .anzahlwahl__schritt:hover:not([disabled]) { background: var(--surface-state); }
.anzahlwahl__feld input {
  grid-row: 1;
  grid-column: 2;
  width: 100%;
  min-width: 0;
  height: 100%;
  padding: 0;
  text-align: center;
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-md);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: 0;
  outline: 0;
}
/* Mit Skript zeigt die rollende Zahl den Wert und das Feld darunter nur den
   Cursor. Beim Tippen tritt die Rollzahl ab und das Feld zeigt, was getippt
   wird; eine halb getippte „1" soll nicht als „1" wegrollen. */
.anzahlwahl[data-rollt] .anzahlwahl__feld input:not(:focus) { color: transparent; }
.anzahlwahl__roll {
  grid-row: 1;
  grid-column: 2;
  justify-self: center;
  font-family: var(--font-data);
  font-size: var(--text-md);
  font-weight: var(--fw-semibold);
  color: var(--ink);
  pointer-events: none;
}
.anzahlwahl__feld input:focus ~ .anzahlwahl__roll { visibility: hidden; }
.anzahlwahl__feld:has(input:focus-visible) {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}

/* ================================================================
   C · RÜCKSICHT IST PFLICHT
   Jede Bewegung dieser Gruppe wird hier abgeschaltet. Die Zustände bleiben
   sichtbar — nur der Weg dorthin fällt weg.
   ================================================================ */
@media (prefers-reduced-motion: reduce) {
  .haken__zug,
  .herz__bild svg,
  .wahl__kreis::after,
  .schalter__griff,
  .eigenauswahl__pfeil,
  .fragebogen__balken > span,
  .dateiablage__zone,
  .dateiablage__zustand > span,
  .dateiablage__echt { transition: none; }

  /* Der Ring am Herzen ist nichts als Bewegung und hat ohne sie keine
     Aufgabe, also entsteht er gar nicht. Die Füllung bleibt, sie ist der
     Zustand. Die Dauer zu kürzen genügt hier nicht: der Ring endet auf
     durchsichtig, und eine auf 0.01 ms gekürzte Animation setzt ihn trotzdem
     für einen Moment sichtbar. */
  .herz input:checked ~ .herz__bild .herz__ring {
    animation: none;
    display: none;
  }
}

/* ================================================================
   D · VOM FELD ZUM FORMULAR

   Abschnitt A und B bedienen einzelne Felder. Was fehlte, war die Stufe
   darüber: Kopf, Abschnitt, Knopfzeile, Sendezustand — das Gerüst, in dem
   die Felder stehen. Die Muster stammen aus dem Atlassian Design System
   (atlassian.design/components/form) und sind aus der Beschreibung
   nachgebaut, nicht abgeschrieben; kein Wert und keine Zeile kommt von dort.

   Ein Gedanke zieht durch alle acht: Vorleseprogramme dürfen nicht
   durchreden. Ein Zähler, eine Vorschau und ein Sendezustand ändern sich
   im Tippen — eine Live-Region auf jedem davon wäre ein Dauerton. Darum
   spricht hier NUR, was einen Wechsel meldet, und der sichtbare Wert
   selbst ist `aria-hidden`. Je Baustein steht die Begründung darunter.
   ================================================================ */

/* ---------------------------------------------------------- Zeichenzaehler
   „23 / 280" unter oder über einem Textfeld.

   ENTSCHEID zur Live-Region, begründet: der Zähler selbst ist
   `aria-hidden`. Ein `aria-live` darauf würde bei jedem Tastendruck eine
   neue Zahl vorlesen — das Vorleseprogramm redet dann durch den Satz, den
   man gerade schreibt, und das Feld wird unbenutzbar. Statt dessen gibt es
   eine ZWEITE, leere Zeile mit `aria-live="polite"`, die nur an zwei Stellen
   überhaupt Text bekommt: beim Erreichen der Warnschwelle und beim Erreichen
   der Grenze. Zwei Meldungen pro Feld statt zweihundert.

   Die Grenze selbst hält `maxlength` am Feld — der Browser verhindert das
   Überschreiben, nicht das Skript.

   Der Zähler steht im Feldkopf am rechten Rand. Wächst die Zahl von einer
   auf zwei Stellen, wächst er nach links in die Lücke — die Beschriftung
   links bleibt stehen. Tabellarische Ziffern, damit eine 1 so breit ist wie
   eine 8 und der Zähler beim Tippen nicht atmet. */
.zeichenzaehler {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
  font-size: var(--text-2xs);
  /* Gewicht steht fest. Es beim Warnen zu erhöhen wäre eine Verschiebung —
     Gewicht 500 → 700 hat im System schon einmal den Nachbarn um 0.5 px
     geschoben (CLAUDE.md, „Keine Pushes"). */
  font-weight: var(--fw-semibold);
  color: var(--ink-faint);
  white-space: nowrap;
}
/* Zustandsfarbe ist die Zugabe, nie der Träger: dieselbe Auskunft steht als
   Wort in der Meldezeile darunter. */
.zeichenzaehler.ist-warn { color: var(--state-warn); }
.zeichenzaehler.ist-fail { color: var(--state-fail); }
.zeichenzaehler__meldung {
  margin: 0;
  /* Platz immer vorgehalten, gleiche Rechnung wie bei .feld__fehler —
     sonst springt das Formular, sobald die Warnung erscheint. */
  min-height: calc(var(--text-2xs) * var(--leading-normal));
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  color: var(--state-warn);
}
.zeichenzaehler__meldung.ist-fail { color: var(--state-fail); }

/* ----------------------------------------------------- Bestaetigungsmeldung
   Der geglückte Zustand eines Feldes. Das System hatte nur den Fehler —
   „Kürzel ist frei" war nicht darstellbar.

   `--state-pass` ist hier zulässig, obwohl Chroma den Zustandsfarben
   vorbehalten ist: „geprüft und in Ordnung" IST ein Zustand, und zwar ein
   gemessener. Er hängt aber nie an der Farbe allein — das Häkchen ist das
   Zeichen, der Satz das Wort, die Farbe die dritte Lage. Für farbschwache
   Menschen bleiben Zeichen und Satz.

   Der Platz ist derselbe wie beim Fehler. Wechselt ein Feld von „bestätigt"
   nach „fehlerhaft" oder zurück auf leer, rührt sich nichts. */
.bestaetigung {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2xs);
  margin: 0;
  min-height: calc(var(--text-2xs) * var(--leading-normal));
  font-size: var(--text-2xs);
  font-weight: var(--fw-semibold);
  color: var(--state-pass);
}
.bestaetigung > .symbol {
  flex: none;
  /* Das Zeichen sitzt auf der ersten Zeile, nicht auf halber Höhe des
     Satzes — sonst rutscht es bei zwei Zeilen Text nach unten weg. */
  margin-top: var(--space-3xs);
}
.bestaetigung b { font-weight: var(--fw-bold); }
/* Die Kante am Feld ist die vierte Lage, nicht die erste. Sie wächst nach
   innen (border-box), das Feld bleibt also exakt gleich gross — dieselbe
   Mechanik wie bei [aria-invalid] in Abschnitt A. */
.feld :is(input, select, textarea)[data-geprueft="ok"] {
  border-color: var(--state-pass);
  border-width: var(--border-w-stark);
}

/* ---------------------------------------------------------- Bedingtesfeld
   Ein Feld erscheint abhängig von einer anderen Antwort.

   OHNE SKRIPT. Der Auslöser ist die Wahl selbst: `:has(input:checked)` am
   Eintrag, das Folgefeld ist dessen direkter Nachbar. Kein Wert steht im
   CSS — die Regel gilt für jede Wahl und jedes Kästchen, auch für die, die
   noch niemand geschrieben hat. Eine Fassung mit Wert im Selektor
   (`[value="anderes"]`) wäre kürzer zu lesen und müsste bei jeder neuen
   Antwort nachgeführt werden; diese hier nicht.

   Das Folgefeld steht IM Markup direkt hinter seinem Auslöser. Damit
   verschiebt sich beim Erscheinen nur, was darunter liegt — alles über dem
   Auslöser bleibt, wo es war. Das ist der Grund für die Stelle, nicht die
   Optik.

   Kein Aus- und Einfahren: eine Höhe von 0 auf auto lässt sich nicht
   verlässlich überblenden, und ein Feld, das erst noch aufgeht, ist später
   fokussierbar als es aussieht. Es ist sofort da. */
.bedingtesfeld__folge { display: none; }
.wahl:has(input:checked) + .bedingtesfeld__folge,
.haken:has(input:checked) + .bedingtesfeld__folge {
  display: block;
  /* Eingezogen auf die Höhe des Antworttextes, damit man die Zugehörigkeit
     sieht. Einzug statt Strich links — der ist verworfen. */
  margin-inline-start: calc(var(--text-sm) + var(--space-xs));
  padding-top: var(--space-2xs);
  /* Unten mehr Luft als innen. Der Folgeblock ist eine eigene Einheit; ohne
     diesen Abstand trennte ihn vom NÄCHSTEN Eintrag nur die Lücke der Liste
     (4 px), während sein eigener Hinweis 8 px entfernt stand. Gemessen am
     2026-09-29: die Zeile las sich damit als Teil des folgenden Eintrags
     statt als Teil ihres Feldes, gegen Kapitel 4.4. Seit 2026-10-08 (G18) --space-xs: die
     Liste hat keine Lücke mehr, und 16 px rissen den Block vom nächsten Eintrag weg. */
  padding-bottom: var(--space-xs);
}
/* Auf schmalen Geräten kostet der Einzug zu viel Feldbreite. */
@media (max-width: 559px) {
  .wahl:has(input:checked) + .bedingtesfeld__folge,
  .haken:has(input:checked) + .bedingtesfeld__folge { margin-inline-start: 0; }
}

/* Was aufgeklappt ist, liegt zuoberst (Vasco, 2026-10-08, G7: «dann muss einfach Z-Index aufs
   Maximum sein»). Die Liste allein mit hohem z-index reicht nicht: sie gilt nur innerhalb des
   Stapels ihres Feldes, und ein späteres Feld mit eigenem z-index (etwa das Textfeld im
   Kompaktformular, --z-deckt) lag darum über der offenen Liste davor. Darum hebt das offene
   Bedienelement auch sein Feld und seine Zeile auf --z-aufgeklappt. */
.eigenauswahl[open],
.suchauswahl:has(> .suchauswahl__liste:not([hidden])),
.datumsfeld:has(.datumsfeld__blatt:not([hidden])),
.feld:has(.eigenauswahl[open], .suchauswahl__liste:not([hidden]), .datumsfeld__blatt:not([hidden])) {
  position: relative;
  z-index: var(--z-aufgeklappt);
}

/* ------------------------------------------------------------ Suchauswahl
   Eigene Liste statt <datalist>: dessen Vorschlagsliste zeichnet das
   Betriebssystem und ist in keinem Browser gestaltbar. Sie oeffnet UNTER dem
   Feld und kann es damit nie verdecken. */
.suchauswahl { position: relative; }
.suchauswahl__liste {
  position: absolute;
  top: 100%;
  inset-inline-start: 0;
  inset-inline-end: 0;
  z-index: var(--z-aufgeklappt);
  margin: var(--space-2xs) 0 0;
  padding: var(--space-2xs);
  list-style: none;
  /* So hoch, dass eine gewöhnliche Liste ganz zu sehen ist; gerollt wird erst,
     wenn der Bildschirm nicht reicht. Mit --space-5xl (128 px) rollten schon
     vier Einträge (Vasco, 2026-10-05 nachts). */
  max-height: min(28rem, 70dvh);
  overflow-y: auto;
  /* Liegt über dem Inhalt: Fläche und Kante je eine Stufe höher. Runde 8 (E3). */
  background: var(--surface-overlay);
  border: var(--border-w-stark) solid var(--border-strong);
  border-radius: var(--r-md);
}
.suchauswahl__liste > li {
  min-height: var(--ctl-h-sm);
  display: flex;
  align-items: center;
  padding: var(--space-2xs) var(--space-xs);
  border-radius: var(--r-xs);
  font-size: var(--text-sm);
  cursor: pointer;
}
.suchauswahl__liste > li:hover,
/* Relative Füllung: die Liste liegt selbst auf --surface-overlay. */
.suchauswahl__liste > li.ist-aktiv { background: var(--surface-state); }
/* Eine Liste in zwei Teilen: zuerst was zum Zusammenhang gehört (die Kontakte der Firma),
   dann unter einer Überschrift der Rest (Vasco, 2026-10-05). Die Überschrift ist kein
   Vorschlag: nicht wählbar, keine Fläche beim Zeigen. */
.suchauswahl__liste > li.suchauswahl__gruppe {
  min-height: 0;
  padding: var(--space-xs) var(--space-xs) var(--space-3xs);
  font-size: var(--text-xs);
  color: var(--ink-faint);
  cursor: default;
}
.suchauswahl__liste > li.suchauswahl__gruppe:hover { background: none; }
/* „X neu anlegen“ trägt ein Plus, damit es sich von einem Treffer unterscheidet. */
.suchauswahl__liste > li.suchauswahl__neu { gap: var(--space-2xs); }

/* ------------------------------------------------- Gesperrt, ueberall gleich
   Die Gesperrt-Fassung gab es nur an .feld-Eingaben und am Haken. Damit sah
   ein gesperrter Auswahlknopf, Schalter oder Regler genauso aus wie ein
   bedienbarer, und der Katalog konnte den Zustand nicht zeigen.
   Dieselbe Rechnung wie bei .btn[disabled]: Deckkraft runter, Zeiger sagt es.
   Die Groesse aendert sich nicht, sonst verschoebe sich die Reihe. */
.wahl:has(input:disabled) { cursor: not-allowed; color: var(--ink-faint); }
.wahl input:disabled ~ .wahl__kreis { opacity: .45; }

.schalter:has(input:disabled) { cursor: not-allowed; }
.schalter:has(input:disabled) .schalter__spur { opacity: .45; }
.schalter:has(input:disabled) .schalter__text { color: var(--ink-faint); }

.regler:has(input:disabled) .regler__kopf,
.regler:has(input:disabled) .regler__skala { color: var(--ink-faint); }
.regler input:disabled { opacity: .45; cursor: not-allowed; }

/* ============================================================
   Wahlmarken — die getroffene Auswahl, einzeln wieder wegnehmbar
   ============================================================ */
/* Atlassian nennt das Tag und Tag group. Der Baustein fehlte, und Vasco
   hatte die Lücke schon benannt, als er eine Auswahl vor sich hatte:
   „wie entferne ich es aus der Auswahl? Das will ich auch sehen oder
   zumindest muss ich hier das reseten können."

   Nicht zu verwechseln mit .marke aus den Grundlagen: die sagt einen
   Zustand an und wird gelesen. Diese hier ist eine Wahl und wird bedient.

   OHNE SKRIPT. Jede Marke ist ein Kästchen mit Beschriftung; wegnehmen
   heisst abwählen, und abgewählt blendet CSS sie aus. Der Wert bleibt
   damit im Formular stehen und geht beim Absenden korrekt nicht mit.
   Zurückholen macht der native type="reset" — kein eigener Zustand, den
   irgendwer nachhalten müsste. */
.wahlliste {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  margin: 0;
}
.wahlmarke {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  /* Rechts weniger, weil dort das Kreuz sitzt und selbst Luft mitbringt. */
  padding: var(--space-3xs) var(--space-2xs) var(--space-3xs) var(--space-xs);
  border-radius: var(--r-xs);
  font-size: var(--text-xs);
  font-weight: var(--fw-medium);
  color: var(--ink);
  background: var(--accent-soft);
  border: var(--border-w) solid var(--accent);
}
/* Die ganze Marke ist nur anklickbar, wenn sie ein <label> ist. Im Tokenfeld
   ist sie ein <span>, dort bedient allein ihr Kreuz; ein Handzeiger über
   dem Wort versprach dort einen Klick, der nichts tat. Seit dem
   2026-10-08 teilen sich beide Bausteine diese eine Marke (Redundanz R2):
   vorher standen dieselben zehn Angaben ein zweites Mal als
   .tokenfeld__marke da. */
label.wahlmarke { cursor: pointer; user-select: none; }
/* Das Kästchen bleibt bedienbar und auffindbar, nur unsichtbar. Entfernen
   würde die Marke aus dem Formular nehmen und für ein Vorleseprogramm
   unauffindbar machen. */
.wahlmarke > input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
/* Abgewählt heisst weg. :has liest den Zustand des Kästchens darin. */
.wahlliste:has(input) .wahlmarke:has(> input:not(:checked)) { display: none; }
/* Der Fokusring liegt innen, wie überall im System. */
.wahlmarke:has(> input:focus-visible) {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}
label.wahlmarke:hover { border-color: var(--accent-hover); }
.wahlmarke > svg {
  flex: none;
  width: var(--icon-sm);
  height: var(--icon-sm);
  color: var(--ink-muted);
}
label.wahlmarke:hover > svg { color: var(--ink); }

/* Ist nichts mehr gewählt, sagt die Liste das — eine leere Reihe sieht
   sonst aus wie ein kaputter Baustein. Dieselbe Regel wie bei .verhaeltnis
   ohne Bild. */
.wahlliste:not(:has(input:checked))::before {
  content: "Keine Auswahl";
  font-size: var(--text-xs);
  color: var(--ink-faint);
}
/* Solange nichts weggenommen wurde, gibt es nichts zurückzuholen. Der Knopf
   wird unsichtbar, behält aber seinen Platz — ein Zustandswechsel darf das
   Layout nie bewegen. */
.wahlliste:not(:has(input:not(:checked))) .wahlliste__zurueck {
  visibility: hidden;
}

/* ============================================================
   Passwortfeld — Sichtbar-Schalter im Feld
   ============================================================ */
/* Gebaut wie `.feld__leeren`: ein Knopf ohne eigene Flaeche, absolut in der
   unteren Ecke der Eingabe, angebunden an `.feld__eingabe` und nicht an das
   ganze Feld, weil die Beschriftung darueber liegt (Regel „Die Beschriftung
   steht ueber dem Feld").

   ANDERS ALS `.feld__leeren` STEHT ER IMMER: Leeren betrifft nur ein Feld mit
   Inhalt, aber ob getippt wird oder nicht, ein Passwort bleibt maskiert. Der
   Platz bleibt darum von Anfang an reserviert, kein Sprung beim ersten
   Zeichen. */
.feld--passwort .feld__eingabe { position: relative; }
.feld--passwort .feld__eingabe input {
  padding-inline-end: calc(var(--space-sm) + var(--icon) + var(--space-2xs));
}
.feld__sicht {
  position: absolute;
  inset-inline-end: var(--border-w);
  inset-block-end: var(--border-w);
  display: grid;
  place-items: center;
  width: var(--ctl-h);
  height: calc(var(--ctl-h) - 2 * var(--border-w));
  padding: 0;
  color: var(--ink-faint);
  background: none;
  border: 0;
  border-radius: var(--r-xs);
  cursor: pointer;
}
.feld__sicht:hover { color: var(--ink); }
/* Zwei Zeichen liegen im selben Rasterfeld, CSS zeigt nur eines — dieselbe
   Technik wie bei der Sprachausgabe (system/sprachausgabe.js): ein Tausch
   ueber innerHTML wuerde den Knopf fuer einen Rahmen leer lassen. Maskiert
   zeigt das offene Auge (klicken macht sichtbar), sichtbar das
   durchgestrichene (klicken maskiert wieder). */
.feld__sicht [data-zeichen="sichtbar"] { display: none; }
.feld__sicht[aria-pressed="true"] [data-zeichen="verborgen"] { display: none; }
.feld__sicht[aria-pressed="true"] [data-zeichen="sichtbar"] { display: block; }

/* ============================================================
   Tokenfeld — getippter Text wird zur entfernbaren Marke
   ============================================================ */
/* Der Baustein Wahlmarken deckt eine FESTE Liste ab: jede Marke ist ein
   Kaestchen mit vorgegebenem Wert, ohne Skript. Hier gibt es die Werte noch
   nicht — sie entstehen erst beim Tippen. Das kann kein Kaestchen der Welt
   abbilden, darum uebernimmt system/tokenfeld.js genau zwei Handgriffe:
   aus dem Text vor Komma oder Eingabetaste eine Marke machen, und eine
   Marke beim Klick auf ihr Kreuz wieder entfernen. Alles andere — die
   Eingabe selbst, ihr Fokus, ihre Tastatur — bleibt beim nativen `<input>`.

   Der Wert jeder Marke steht in einem eigenen `input type="hidden"`, damit
   ein Formular sie beim Absenden wie jedes andere Feld mitschickt, ohne
   dass das Skript den Zustand ein zweites Mal fuehren muesste. */
.tokenfeld {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2xs);
  min-height: var(--ctl-h);
  padding: var(--space-2xs) var(--space-sm);
  background: var(--surface-base);
  border: var(--border-w) solid var(--border-strong);
  border-radius: var(--r-sm);
}
.tokenfeld:focus-within { border-color: var(--accent); }
/* Ohne .marke-sprung: in einem Werkzeug ist der Name der Marke selbst ein
   Knopf, der den verknüpften Eintrag öffnet (crm.css). Er ist Text, kein
   Zeichen, und darf weder diese Form noch das Entfernen erben. */
.tokenfeld .wahlmarke button:not(.marke-sprung) {
  display: grid;
  place-items: center;
  padding: 0;
  color: var(--ink-muted);
  background: none;
  border: 0;
  border-radius: var(--r-xs);
  cursor: pointer;
}
.tokenfeld .wahlmarke button:not(.marke-sprung):hover { color: var(--ink); }
/* Ist der Name ein Knopf, stehen in der Marke zwei Knöpfe mit verschiedener
   Folge: öffnen und entfernen. Vier Pixel Luft dazwischen ohne Kante zeigten
   die Grenze nicht (mess.js, 2026-10-02). Sie stossen jetzt aneinander, und
   das Kreuz trägt eine eigene, hellere Fläche derselben Familie, wie die
   zweite Hälfte eines geteilten Knopfs. Eine Linie dazwischen wäre der
   verworfene Strich links. */
.tokenfeld .wahlmarke:has(> .marke-sprung) { gap: 0; padding-block: 0; padding-inline-end: 0; overflow: hidden; }
.tokenfeld .wahlmarke > .marke-sprung { padding-block: var(--space-3xs); padding-inline-end: var(--space-xs); }
.tokenfeld .wahlmarke > .marke-sprung + button {
  align-self: stretch;
  padding-inline: var(--space-2xs);
  background: var(--accent-soft-lift);
  border-radius: 0;
}
.tokenfeld .wahlmarke .symbol { width: var(--icon-sm); height: var(--icon-sm); }
/* Die Eingabe traegt selbst keine Flaeche und keine Kante — beides liegt
   schon am Wickel, eine zweite Kante darin waere ein Rahmen im Rahmen.

   `.tokenfeld .tokenfeld__eingabe` und nicht die Klasse allein: die
   Feldregel `.feld input` (grundlagen.css) wiegt (0,1,1) und schlug die
   einzelne Klasse (0,1,0). Bis zum 2026-09-29 stand darum genau der Rahmen
   im Rahmen da, den dieser Kommentar ausschliesst (Vasco: „ein Formular hat
   sogar zwei Felder"). */
.tokenfeld .tokenfeld__eingabe {
  /* `width: auto` gegen das `width: 100%` der Feldregel: sonst bricht die
     Eingabe in eine eigene Zeile unter die Marken. */
  width: auto;
  flex: 1 1 auto;
  min-width: var(--space-4xl);
  min-height: var(--ctl-h-sm);
  padding: var(--space-3xs) 0;
  font-family: var(--font-ui);
  font-size: var(--text-sm);
  color: var(--ink);
  background: none;
  border: 0;
  border-radius: 0;
  outline: 0;
}
/* Der Fokusring sitzt am ganzen Feld, wie bei der Eingabegruppe. Die
   Eingabe darin schaltet ihren ab; ohne diese Regel hätte das Tokenfeld
   mit der Tastatur gar keinen Ring gehabt, nur eine Kante in anderer Farbe. */
.tokenfeld:has(.tokenfeld__eingabe:focus-visible) {
  outline: var(--border-w-stark) solid var(--focus);
  outline-offset: calc(var(--border-w) * -2);
}

/* ============================================================
   Einstellungszeile — eine Einstellung je Zeile, mit Rücknahme
   ============================================================ */
/* Eine Einstellungsseite ist kein Formular. Ein Formular wird ausgefüllt und
   am Ende abgeschickt (Kapitel 4.7); eine Einstellung steht schon auf einem
   Wert, und die Frage ist nur, ob jemand ihn ändert. Daraus folgt alles
   Weitere: es gibt einen Standard, es gibt eine Abweichung davon, und es
   gibt einen Weg zurück.

   WOHER DAS KOMMT. Vasco brachte am 2026-09-29 eine fremde Musterseite mit
   sechs Regeln für Einstellungen. Drei davon deckte das System schon ab
   (4.4 gruppiert nach Zusammengehörigkeit, 4.12 flach vor tief, 3.7 sichert
   das Unumkehrbare), eine gehört nicht ins System (ab wann ein Suchfeld
   nötig wird, weiss nur die Anwendung). Gewählt aus der Vorschau
   docs/vorschau-einstellungen.html: E1 für die Markierung, S2 für das
   Speichern, G1 für die Wand vor dem Löschen.

   ZUSTAND VERSCHIEBT NIE (Kapitel 3.5). Markierung und Rücknahme haben
   ihren Platz, bevor sie sichtbar werden. Die erste Fassung blendete den
   Knopf mit `display` ein, und dann sprang die Zeile bei jedem Umlegen.
   Gemessen an der Vorschau: Zeilenhöhe und Textanfang bleiben auf 0.00 px
   gleich, geändert wie ungeändert. */
/* DIE LISTE FÜLLT IHRE SPALTE. Ohne `width` schrumpfte sie im Katalog auf
   0 px: die Bühne dort ist ein Flex-Behälter, und ein Rasterkind mit
   `minmax(0, 1fr)` hat keine eigene Mindestbreite, an der ein Flex-Element
   sich festhalten könnte. Gemessen am 2026-09-29, die Zeile war 12 px breit
   und 494 px hoch. */
.einstellungen {
  width: 100%;
  display: flex;
  flex-direction: column;
  /* Der Baustein kennt seinen Behälter, nicht das Gerät — dieselbe
     Begründung wie bei `.formular` weiter oben. */
  container-type: inline-size;
}

/* Drei Fächer: Text, Bedienelement, Rücknahme. Das dritte ist immer da,
   auch leer, und ist so breit wie ein Bedienelement hoch ist. Damit behält
   die Zeile ihre äussere Kante, egal was in ihr steht.

   Der Innenabstand vorn gehört dem Strich. Er hängt dadurch an der Zeile
   und nicht am Innenabstand der Karte darum — sonst stünde er bei jeder
   Karte woanders. */
.einstellungszeile {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto var(--ctl-h);
  align-items: center;
  gap: var(--space-2xs) var(--space-lg);
  padding-block: var(--space-md);
  padding-inline-start: var(--space-sm);
}
.einstellungszeile + .einstellungszeile {
  border-block-start: var(--border-w) solid var(--border-subtle);
}

/* Der Strich, der die Abweichung vom Standard anzeigt. Über die ganze
   Zeilenhöhe: er sagt damit auch, wie weit die Zeile reicht, und braucht
   keine Breite, die sonst dem Text fehlt.

   ZWEI FRAGEN, ZWEI ATTRIBUTE. `data-geaendert` heisst „weicht vom
   Standard ab" und bleibt über Sitzungen hinweg wahr; daran hängen Strich
   und Rücknahme. `data-offen` heisst „noch nicht gespeichert" und gilt nur
   bis zum Klick; daran hängt die Leiste. Beides an einem Attribut wäre
   kürzer und falsch: ein gespeicherter Wert, der vom Standard abweicht,
   ist geändert und trotzdem nicht offen. */
.einstellungszeile::before {
  content: "";
  position: absolute;
  inset-block: var(--space-2xs);
  inset-inline-start: 0;
  width: var(--border-w-stark);
  border-radius: var(--r-xs);
  background: var(--accent);
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease);
}
.einstellungszeile[data-geaendert="ja"]::before { opacity: 1; }

.einstellungszeile__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}
.einstellungszeile__text > label,
.einstellungszeile__text > span:first-child {
  font-size: var(--text-sm);
  font-weight: var(--fw-medium);
  color: var(--ink);
}
.einstellungszeile__text > .hinweis {
  font-size: var(--text-2xs);
  color: var(--ink-faint);
  max-width: 34ch;
}

.einstellungszeile__wert { width: min(var(--panel-w), 100%); }
/* Ein Wert aus mehreren Zeilen (eine Liste, ein Absatz) füllt die Spalte und wächst nur in der
   Höhe: in die Breite gezogen schob er die Beschriftung weg. Aus BewerbKlick (Einstellungen,
   Anschreiben-Regeln), seit 1.6.4. */
.einstellungszeile__wert textarea { width: 100%; resize: vertical; }
/* Der Schalter trägt in dieser Zeile keinen eigenen Text: die Beschriftung
   steht links und wäre sonst zweimal da. Das `for` bindet sie trotzdem, das
   Vorleseprogramm liest also weiterhin einen benannten Schalter. */
.einstellungszeile__wert .schalter { justify-content: flex-end; }
/* Der Schalter ist inline-flex und sass im Block des Wertes auf der Schriftlinie: seine Spur stand
   4 px über der Mitte des Zurücksetzen-Knopfs daneben (Vasco, 2026-10-08, G23, «Wochenbericht
   zusenden»). Als Flex-Behälter richtet der Wert ihn auf die Mitte der Zeile aus. */
.einstellungszeile__wert:has(> .schalter) { display: flex; justify-content: flex-end; align-items: center; }
.einstellungszeile__wert .schalter__text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.einstellungszeile__fach {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  min-width: var(--ctl-h);
}

/* Sichtbarkeit statt Anzeige, damit der Platz erhalten bleibt. `inert`
   nimmt den Knopf währenddessen aus Tabfolge und Vorleseprogramm — ein
   Knopf, den man nicht sieht, aber antabben kann, ist schlimmer als keiner.
   Gesetzt wird es im Skript, weil CSS keine Attribute schreibt. */
.einstellungszeile__zurueck {
  visibility: hidden;
  transition: visibility var(--t-sofort) var(--ease), color var(--t-sofort) var(--ease);
}
.einstellungszeile[data-geaendert="ja"] .einstellungszeile__zurueck { visibility: visible; }

/* Speichern und Abbrechen an der Zeile, für Werte, die ein fremdes System
   sofort erfährt (Kapitel 3.7). Sie stehen unter dem Feld und nicht daneben,
   darum über alle drei Spalten. Ohne Änderung gibt es sie nicht. */
.einstellungszeile__leiste {
  grid-column: 1 / -1;
  display: flex;
  gap: var(--space-sm);
  justify-content: flex-end;
  padding-block-start: var(--space-xs);
}
.einstellungszeile:not([data-offen="ja"]) .einstellungszeile__leiste { display: none; }

/* Schmaler Behälter: das Bedienelement rutscht unter den Text, das Fach für
   die Rücknahme bleibt oben rechts — es gehört zur Beschriftung und nicht
   zum Wert. Die Schwelle misst die Liste und nicht das Fenster.

   JEDES STÜCK MIT ZEILE UND SPALTE. Ohne das füllt das Raster von selbst
   auf, und dann landet das Fach unter dem Bedienelement in einer dritten
   Zeile, mit einer handhohen Lücke daneben. Zwei Spalten und drei Kinder
   gehen nicht von allein auf. */
@container (max-width: 30rem) {
  .einstellungszeile { grid-template-columns: minmax(0, 1fr) var(--ctl-h); }
  .einstellungszeile__text   { grid-column: 1; grid-row: 1; }
  .einstellungszeile__fach   { grid-column: 2; grid-row: 1; }
  .einstellungszeile__wert   { grid-column: 1 / -1; grid-row: 2; width: 100%; }
  .einstellungszeile__leiste { grid-column: 1 / -1; grid-row: 3; }
  .einstellungszeile__wert .schalter { justify-content: flex-start; }
}

/* ---------------------------------------------------- Gefahrenbereich
   Die Wand vor dem Unumkehrbaren. Sie besteht aus Abstand und einer
   Überschrift, nicht aus Farbe: „hier endet die Seite" ist kein Zustand,
   und die eine Regel der Marke lässt Farbe nur erscheinen, wo sie etwas
   bedeutet. Der Knopf darin trägt `.btn--gefahr` und trägt die Farbe damit
   an der Stelle, an der sie wirklich etwas heisst.

   Der Halt liegt ohnehin nicht in der Wand, sondern im Fenster dahinter:
   dort steht der Name der Sache, und er muss abgetippt werden. */
.gefahrenbereich {
  width: 100%;
  margin-block-start: var(--space-3xl);
  padding-block-start: var(--space-lg);
  border-block-start: var(--border-w) solid var(--border-subtle);
}
/* Die Überschrift trägt eine eigene Klasse, weil der Guide selbst `.kapitel
   h3` gestaltet: gleich schwer, aber später geladen, und damit stand die
   Überschrift des Bausteins im Katalog so gross wie eine Kapitelzeile. Zwei
   Klassen im Selektor wiegen schwerer als eine Klasse und ein Element. */
.gefahrenbereich > .gefahrenbereich__titel {
  display: block;
  margin: 0;
  font-size: var(--text-md);
  font-weight: var(--fw-semibold);
}
.gefahrenbereich__zeile {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md);
  align-items: center;
  justify-content: space-between;
  margin-block-start: var(--space-sm);
}
.gefahrenbereich__zeile p {
  margin: 0;
  max-width: 46ch;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}
/* Der abzutippende Name. `user-select: all` nimmt das Markieren ab, das
   Abtippen nicht: kopieren lässt er sich nur als Ganzes, und wer ihn ins
   Feld einsetzt, hat ihn immerhin gelesen. */
.gefahrenbereich__name { font-family: var(--font-data); user-select: all; }

/* Aufklappendes in einem Fenster schwebt über dem Inhalt, verankert am Feld.
   Die Liste der Eigenauswahl, der Suchauswahl und das Kalenderblatt liegen
   sonst absolut im Fensterinhalt, und `.fenster__inhalt` schneidet mit seinem
   `overflow-y: auto` alles ab, was unten hinausragt. Gemessen am 2026-10-02
   im Vertriebscockpit: vom letzten Auswahlfeld war nur der oberste Rand der
   Liste zu sehen. Ein z-index hilft nicht, abgeschnitten wird vom Rollbereich.

   Erster Versuch war die Liste im Fluss. Sie schob den Inhalt nach unten, und
   das Fenster sprang (Vasco: „wenn ein dropdown aufgeht, dann springt das
   fenster hoch, ich will das nicht"). Darum dieselbe Bauweise wie beim
   Aufsatz: position:fixed mit Anker. Fixed entkommt dem Rollbereich, der Anker
   hält die Liste am Feld, auch wenn der Inhalt rollt, und flip-block klappt sie
   nach oben, wenn unten kein Platz ist. Das Feld bleibt frei (Kapitel 3.13).

   Ein Ankername genügt je Art, weil immer nur ein Menü offen ist (name="menue",
   Kapitel 3.10) und Such- wie Kalenderliste beim Klick daneben schliessen.
   Dasselbe gilt in jedem Bereich, der selbst rollt und darum `.rollbereich` trägt, etwa eine
   Randspalte neben einem Dokument (gefunden in BewerbKlick, 2026-10-07: der Kalender wurde
   abgeschnitten und vom nächsten Abschnitt überdeckt). Beim Rollen geht das Blatt zu
   (datumsfeld.js), sonst bliebe es stehen, während das Feld wegrollt.
   OHNE Anker-Positionierung bleibt es beim Verhalten von vorher: absolut und
   im Zweifel abgeschnitten, aber bedienbar. */
@supports (anchor-name: --a) {
  /* Die Wurzel jeder Regel ist der Formularbaustein, das Fenster steht in
     :where(). So bleibt jede Klasse Wurzel in nur einer Gruppendatei. */
  .eigenauswahl:where(.fenster__inhalt *, .rollbereich *)[open] > summary { anchor-name: --fenster-auswahl; }
  .suchauswahl:where(.fenster__inhalt *, .rollbereich *):has(.suchauswahl__liste:not([hidden])) .feld__eingabe { anchor-name: --fenster-suche; }
  .datumsfeld:where(.fenster__inhalt *, .rollbereich *):has(> .datumsfeld__blatt:not([hidden])) { anchor-name: --fenster-datum; }

  .eigenauswahl__liste:where(.fenster__inhalt *, .rollbereich *),
  .eigenauswahl:where(.fenster__inhalt *, .rollbereich *)[data-richtung] .eigenauswahl__liste,
  .suchauswahl__liste:where(.fenster__inhalt *, .rollbereich *),
  .datumsfeld__blatt:where(.fenster__inhalt *, .rollbereich *) {
    position: fixed;
    inset: auto;
    margin-block: var(--space-2xs);
    max-height: 50dvh;
    overflow-y: auto;
    position-try-fallbacks: flip-block;
  }
  .eigenauswahl__liste:where(.fenster__inhalt *, .rollbereich *) { position-anchor: --fenster-auswahl; position-area: block-end span-inline-end; width: anchor-size(width); }
  .suchauswahl__liste:where(.fenster__inhalt *, .rollbereich *) { position-anchor: --fenster-suche; position-area: block-end span-inline-end; width: anchor-size(width); }
  .datumsfeld__blatt:where(.fenster__inhalt *, .rollbereich *) { position-anchor: --fenster-datum; position-area: block-end span-inline-start; }
}

/* ============================================================
   Kompaktformular — Eigenschaften als Liste, eine Zeile je Feld
   ============================================================ */
/* Für Fenster und Seitenblätter, in denen ein Eintrag bearbeitet wird, nicht
   neu erfasst: die Eigenschaftsliste, wie sie Asana neben einer Aufgabe zeigt.
   Vasco am 2026-10-02 im Vertriebscockpit: „das alles braucht so krass viel
   Platz … 1 zu 1 wie Asana“. Gemessen dort: das Fenster Projekt fiel von
   rund 1'500 auf 620 px Höhe, eine Zeile misst 38 px.

   WAS SICH ÄNDERT, und warum es keine Regel bricht:
   · Beschriftung links, Wert rechts. Die Beschriftung bleibt ein <label for>,
     nur die Anordnung wechselt; die Tab-Ordnung bleibt gleich (Kapitel 4.4).
   · Das Feld trägt keine Kante, bis man darauf zeigt oder hineinklickt. Die
     Zeile liest sich als Wert, nicht als Aufforderung. Beim Schreiben wird
     das Feld dunkler (--surface-base), ohne Kante und ohne Ring: in einer
     Liste von Feldern war jeder Rahmen einer zu viel (Vasco, 2026-10-07,
     „Kein weisser Ring, das dunklere Feld zeigt, wo man schreibt“). Knöpfe
     und Auswahlen im Formular behalten den Ring aus base.css.
   · Die Meldezeile hält hier KEINEN Platz frei (Ausnahme zu Kapitel 3.5):
     in einer Liste von zwölf Zeilen wären das zwölf leere Zeilen. Erscheint
     eine Meldung, rückt nur, was darunter steht. Gemeldet in docs/offen.md.
   · Mehrzeilige Texte bleiben gestapelt und über die ganze Breite: ein
     Absatz in einer 300-px-Spalte liest niemand.

   Die Breite der Beschriftung steht in --kompakt-label und lässt sich am
   Formular überschreiben. */
/* Alle Zeilen rücken so weit ein wie der Text in einer Textkarte (Innenabstand plus Kante). Bis zum
   2026-10-19 stand «Beschreibung» in der Karte 13 px rechts von «Firma», «Stand» und «Ort» (Vasco:
   «nicht linksbündig miteinander»). So liegen die Beschriftungen auf einer Linie und die Kante der
   Karte auf der Kante des Formulars. */
.formular--kompakt > .feld:not(:has(textarea)),
.formular--kompakt > .feldgruppe > .feld:not(:has(textarea)) { padding-inline: var(--kompakt-einzug); }
.formular--kompakt {
  --kompakt-einzug: calc(var(--space-sm) + var(--border-w));
  --kompakt-label: var(--label-w-eigen, var(--label-w));
  /* --label-w-eigen setzt ein Werkzeug, wenn jemand die Spalte breiter gezogen hat (R11). */
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-3xs);
}
.formular--kompakt .formular--kompakt { gap: var(--space-3xs); }
/* Text, Verknüpfung und Datum: Beschriftung und Eingabe in einer Zeile. */
.formular--kompakt .feld:not(:has(textarea)) .feld__eingabe:not(:has(> label.visually-hidden)) {
  display: grid;
  grid-template-columns: var(--kompakt-label) minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-xs);
}
.formular--kompakt .feld:not(:has(textarea)) .feld__eingabe > label { grid-row: 1; grid-column: 1; }
.formular--kompakt .feld:not(:has(textarea)) .feld__eingabe > input { grid-row: 1; grid-column: 2; }
/* Auswahl und Tokenfeld: das Feld selbst ist die Zeile. */
.formular--kompakt .feld[role="group"],
.formular--kompakt .feld:has(> .tokenfeld) {
  display: grid;
  grid-template-columns: var(--kompakt-label) minmax(0, 1fr);
  align-items: center;
  column-gap: var(--space-xs);
  row-gap: 0;
}
.formular--kompakt .feld[role="group"] > :not(.feld__kopf),
.formular--kompakt .feld:has(> .tokenfeld) > :not(label) { grid-column: 2; }
.formular--kompakt .feld:has(> .tokenfeld) > label { grid-column: 1; grid-row: 1; }
.formular--kompakt .feld:has(> .tokenfeld) > .tokenfeld { grid-row: 1; }
/* Die Beschriftung tritt zurück, der Wert steht in normaler Schrift. */
.formular--kompakt :is(.feld__eingabe > label, .feld__kopf > span:first-child, .feld > label) {
  font-size: var(--text-sm);
  font-weight: var(--fw-regular);
  color: var(--ink-muted);
}
/* Ohne Kante in Ruhe, Fläche beim Zeigen, dunkleres Feld beim Schreiben. */
/* Im Kompaktformular ist das Feld nur --ctl-h-sm hoch. Der Kalenderknopf behielt --ctl-h und ragte
   7 px über das Feld, das Zeichen sass 4 px über der Mitte (gemessen in BewerbKlick, 2026-10-08). */
.formular--kompakt .datumsfeld__knopf { height: calc(var(--ctl-h-sm) - 2 * var(--border-w)); }
/* Dasselbe für das Kreuz und das Auge (Vasco, 2026-10-08, G23: «das X innerhalb vom Feld der Firma
   ist nicht mittig»). Mit --ctl-h stand ihr Zeichen 3 px über der Mitte des 36 px hohen Feldes. */
.formular--kompakt :is(.feld__leeren, .feld__sicht) { height: calc(var(--ctl-h-sm) - 2 * var(--border-w)); }
.formular--kompakt .feld :is(input:not([type="checkbox"]):not([type="radio"]):not(.tokenfeld__eingabe), textarea),
.formular--kompakt .eigenauswahl > summary,
.formular--kompakt .tokenfeld {
  min-height: var(--ctl-h-sm);
  font-size: var(--text-sm);
  background: transparent;
  border-color: transparent;
  transition: background var(--t-sofort) var(--ease), border-color var(--t-sofort) var(--ease);
}
.formular--kompakt .feld :is(input:not([type="checkbox"]):not([type="radio"]):not(.tokenfeld__eingabe), textarea):hover,
.formular--kompakt .eigenauswahl > summary:hover,
.formular--kompakt .tokenfeld:hover { background: var(--surface-state); }
.formular--kompakt .eigenauswahl[open] > summary { background: var(--surface-base); border-color: var(--border-strong); }
.formular--kompakt .feld :is(input:not([type="checkbox"]):not([type="radio"]):not(.tokenfeld__eingabe), textarea):focus,
.formular--kompakt .tokenfeld:focus-within { background: var(--surface-base); border-color: transparent; outline: none; }
.formular--kompakt .tokenfeld .tokenfeld__eingabe:focus-visible { outline: none; }
.formular--kompakt .tokenfeld { padding-block: var(--space-3xs); }
.formular--kompakt .tokenfeld .tokenfeld__eingabe { min-height: var(--space-lg); background: transparent; }
/* Der Pfeil der Auswahl erscheint erst beim Zeigen: in Ruhe ist es ein Wert. */
.formular--kompakt .eigenauswahl > summary .eigenauswahl__pfeil { opacity: 0; }
.formular--kompakt .eigenauswahl > summary:hover .eigenauswahl__pfeil,
.formular--kompakt .eigenauswahl[open] .eigenauswahl__pfeil { opacity: 1; }
/* Ein Kästchen im Kompaktformular steht wie jedes Feld: die Beschriftung links in der Spalte der
   Beschriftungen, das Kästchen rechts in der Spalte der Werte, auf der Wertkante (Vasco, 2026-10-09:
   «ich muss es immer im Eingabebereich ankreuzen»). Das Markup bleibt label.haken. */
.formular--kompakt .feld > .haken {
  display: grid;
  grid-template-columns: var(--kompakt-label) minmax(0, 1fr);
  column-gap: var(--space-xs);
  align-items: center;
  min-height: var(--ctl-h-sm);
}
.formular--kompakt .feld > .haken > span:last-child { grid-column: 1; grid-row: 1; font-size: var(--text-sm); color: var(--ink-muted); }
/* Ein Kästchen klickt man, man zieht es nicht: der Zeiger zum Klicken gilt für das Kästchen, die Hand
   zum Ziehen nur für das Wort, wie bei jeder Beschriftung (Vasco, 2026-10-09). */
.formular--kompakt .feld > .haken { cursor: pointer; }
.formular--kompakt .feld > .haken > .haken__kasten { grid-column: 2; grid-row: 1; margin-block-start: 0; margin-inline-start: var(--wertkante); }
/* In einer Feldgruppe beginnen Kästchen und Schalter ebenso an der Wertkante, bündig mit dem Text
   der Felder darüber (Kapitel «Angaben stehen bündig übereinander», aus BewerbKlick). */
.feld[role="group"] > :is(.haken, .schalter) { margin-inline-start: var(--wertkante); }
/* Mehrzeilig: Überschrift darüber, Feld über die ganze Breite, leise Kante. */
.formular--kompakt .feld:has(textarea) { margin-top: var(--space-md); }
.formular--kompakt .feld:has(textarea) .feld__eingabe > label { font-size: var(--text-md); font-weight: var(--fw-semibold); color: var(--ink); }
.formular--kompakt .feld textarea { border-color: var(--border-subtle); }
/* Meldung und Hinweis stehen unter dem Wert, nicht unter der Beschriftung. */
.formular--kompakt .feld .feld__fehler:empty { display: none; }
.formular--kompakt .feld :is(.feld__fehler, .hinweis) { grid-column: 2; }
/* Bündig mit dem WERT im Feld, nicht mit der Feldkante: das Feld hat in Ruhe keine sichtbare
   Kante, und der Satz begann 13 px vor dem Text darüber (Vasco, 2026-10-08, G24: «das hängt einfach
   so in der Luft»). --wertkante ist Innenabstand plus Kante des Feldes. */
.formular--kompakt .feld:not([role="group"]):not(:has(> .tokenfeld)):not(:has(textarea)) > :is(.feld__fehler, .hinweis) {
  margin-inline-start: calc(var(--kompakt-label) + var(--space-xs) + var(--wertkante));
}
/* Die Beschriftung wird schmaler, wenn das FORMULAR schmal ist, nicht das
   Fenster: dasselbe Formular steht mal im breiten Fenster, mal im Seitenblatt. */
.formular--kompakt { container-type: inline-size; }
@container (max-width: 26rem) {
  /* Die Infobox wird mit schmal, sonst stünden ihre Werte 20 px rechts der
     Feldwerte (gemessen am 2026-10-03). */
  .formular--kompakt :is(.feld, .aufgabe-meta) { --kompakt-label: 6.5rem; }
}
/* Ein Raster im Kompaktformular wird eine Spalte: die Felder tragen die Beschriftung selbst
   (aus dem Vertriebscockpit übernommen, offen seit 2026-10-05). */
/* Zwei Spalten für Felder, .ganz über beide. Bisher gab es nur die Rücksetzung im Kompaktformular,
   die Grundregel dazu fehlte (aus dem Cockpit). */
.raster { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-md); }
.raster > .ganz { grid-column: 1 / -1; }
.formular--kompakt.raster, .formular--kompakt .raster { grid-template-columns: minmax(0, 1fr); gap: var(--space-3xs) 0; }
.formular--kompakt .raster > .ganz, .formular--kompakt.raster > .ganz { grid-column: auto; }
/* Griff für die Breite der Beschriftungsspalte (formular-kompakt.js). Beim Zeigen ein gepunkteter
   Griff mit kurzer, feiner Linie auf Höhe des Zeigers (--griff-y), beim Ziehen dasselbe, sonst
   nichts (U13, Vasco 2026-10-06). */
.formular--kompakt { position: relative; }
.kompakt-griff { position: absolute; inset-block: 0; inset-inline-start: calc(var(--kompakt-label, var(--label-w)) - 6px); inline-size: 12px; cursor: col-resize; touch-action: none; z-index: var(--z-griff); }
.kompakt-griff::before, .kompakt-griff::after { content: ''; position: absolute; opacity: 0; transition: opacity var(--t-fast) var(--ease); pointer-events: none; }
.kompakt-griff::after { inset-inline-start: 5.5px; inline-size: var(--border-w); block-size: var(--space-2xl); inset-block-start: calc(var(--griff-y, 1rem) - var(--space-2xl) / 2); background: var(--border-strong); }
/* Linie mit Punktgriff, wie jede verschiebbare Grenze (H21, J28). */
.kompakt-griff::before { inset-inline-start: 2px; inline-size: 8px; block-size: 14px; inset-block-start: calc(var(--griff-y, 1rem) - 7px); z-index: var(--z-ueber);
  background: radial-gradient(circle, var(--ink-muted) 1px, transparent 1.4px) 0 0 / 4px 4.67px; background-color: var(--surface-overlay); border-radius: var(--r-xs); }
/* Die Beschriftung ist der Griff, mit dem man ein Feld an einen anderen Platz zieht
   (formular-kompakt.js); Textkarten haben keinen. */
.formular--kompakt .feld-griff { cursor: grab; user-select: none; }
.kompakt-griff:hover::before, .kompakt-griff:hover::after, .kompakt-griff[data-zieht]::before, .kompakt-griff[data-zieht]::after { opacity: 1; }
/* Leere Felder hinter einem Schalter, wie in der Eigenschaftsliste (crm.css,
   `.felder-schalter`). Das Skript, das [data-leer] setzt, gehört dem
   Werkzeug: was „leer" heisst, weiss nur es. */
/* `data-immer` hält eine Zeile fest, auch wenn sie leer ist, etwa das
   Kästchen „Nachbearbeiten": ein Kästchen ohne Haken ist ein Wert, kein
   fehlendes Feld (Teil CRM). */
.formular--kompakt:not([data-alle-felder]) .feld[data-leer]:not([data-immer]) { display: none; }
/* BB4: „Immer verbergen“ (data-nie): das Feld fehlt auch, wenn alle Felder gezeigt werden. */
.formular--kompakt .feld[data-nie] { display: none; }
/* Eine Feldgruppe (.feldgruppe, im Cockpit .notiz-art) mit Hinweis: sind
   alle ihre Felder leer und verborgen, geht die Gruppe samt Hinweis mit.
   Ein Hinweis zu Feldern, die man nicht sieht, erklärt nichts. */
.formular--kompakt:not([data-alle-felder]) .feldgruppe:not(:has(.feld:not([data-leer]), .feld[data-immer])) { display: none; }

/* Stehen Karten im Formular, brauchen sie Luft zwischeneinander; die
   3xs zwischen zwei Zeilen läsen sich als eine Karte. */
.formular--kompakt:has(> :is(.eigenschaften-karte, .ablage-block)) { gap: var(--space-sm); }

/* Textfelder als eigene Karte (Teil CRM, Vasco, 2026-10-03: „Jede Gruppe
   hat einen Rahmen, sonst sieht man nicht, wo was anfängt und aufhört").
   Die Karte ist die Kante, das Textfeld selbst hat keine; beim Schreiben
   wird die Kante der Karte stark, wie bei .notiz-teil. Notizkarten bringen
   ihren eigenen Rahmen mit und bleiben aussen vor. */
.formular--kompakt .feld:has(textarea):not(.notiz-teil .feld) {
  margin-top: 0;
  padding: var(--space-xs) var(--space-sm);
  background: var(--surface-raised);
  border: var(--border-w) solid var(--border-subtle);
  border-radius: var(--r-md);
  transition: border-color var(--t-sofort) var(--ease);
}
/* Spezifisch genug, um die Kante beim Zeigen und beim Fokus der übrigen
   Felder im Kompaktformular zu schlagen (die Regel dort zählt 0,6,1). */
.formular--kompakt .feld:has(textarea):not(.notiz-teil .feld) .feld__eingabe > textarea:is(:hover, :focus, *) {
  padding-inline: 0;
  background: transparent;
  border-color: transparent;
}
/* Beim Schreiben zeigt die Karte den Fokus mit ihrer starken Kante, ohne Ring:
   ein Ring dazu wäre der zweite Rahmen (Vasco, 2026-10-07: „hier gibt es auch
   noch zu viele Rahmen“). */
.formular--kompakt .feld:has(textarea):not(.notiz-teil .feld):focus-within { border-color: var(--border-strong); }
/* Über dem Griff der Beschriftungsbreite: eine Textkarte hat keine
   Beschriftungsspalte, der Griff darf darin nicht reagieren (Vasco, 2026-10-07). */
.formular--kompakt .feld:has(textarea) { position: relative; z-index: var(--z-deckt); }
/* Textkarte mit Werkzeugleiste (M4, aus dem Vertriebscockpit, Vasco 2026-10-07): die Leiste
   steht oben in der Karte zwischen Titel und Text, mit denselben Befehlen wie „/“. Die
   Schreibfläche .fe (ein Textfeld oder die Fläche eines Editors) hat eine feste Höhe, unten
   rechts ziehbar; die gezogene Höhe gilt danach für alle Textkarten (--fe-hoehe,
   textkarte.js). Innen rollt der Text. „Volle Ansicht“ hebt die Karte in ein eigenes Fenster.
   Das Feld stellt die Eingabe vor die Beschriftung und kehrt die Reihenfolge um
   (column-reverse); darum trägt die Leiste order 1 und die Beschriftung order 2. */
.fe-werkzeug { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3xs); padding-block-end: var(--space-2xs); margin-block-end: var(--space-2xs); border-block-end: var(--border-w) solid var(--border-subtle); }
.fe-werkzeug:where(.feld__eingabe > *) { order: 1; }
.fe-werkzeug ~ label { order: 2; }
.fe-werkzeug .btn--symbol { min-width: 0; min-height: 0; width: calc(var(--space-lg) + var(--space-2xs)); height: calc(var(--space-lg) + var(--space-2xs)); }
@media (pointer: coarse) { .fe-werkzeug .btn--symbol { min-width: var(--tap-min); min-height: var(--tap-min); } }
.fe-werkzeug .btn > svg { inline-size: var(--icon-sm); block-size: var(--icon-sm); min-inline-size: var(--icon-sm); flex: none; }
.fe-werkzeug .trenner--hoch { margin-inline: var(--space-2xs); }
/* Ein Editor bringt einen Knopf für die Textart („Normaler Text ⌄“), gleich hoch wie die Symbolknöpfe. */
.fe-werkzeug .fe-werkzeug__art { min-height: calc(var(--space-lg) + var(--space-2xs)); padding-inline: var(--space-xs); gap: var(--space-2xs); text-decoration: none; }
/* Ist .fe die Fläche eines Editors (contenteditable), gilt dasselbe wie beim Textfeld der Karte: die
   Karte zeigt den Fokus mit ihrer starken Kante, die Fläche selbst bleibt ohne Kante und Ring. */
.formular--kompakt .feld .fe:focus { background: transparent; border-color: transparent; outline: none; }
.formular--kompakt .feld:has(.fe:focus) { border-color: var(--border-strong); }
.fe-werkzeug [data-fw="voll"] { margin-inline-start: auto; }
.feld .fe { block-size: var(--fe-hoehe, 12rem); min-block-size: 6rem; max-block-size: none; overflow: auto; resize: vertical; }
.fe-voll-fenster { inset: 0; margin: auto; inline-size: min(60rem, calc(100vw - 2 * var(--space-md))); block-size: min(90vh, 60rem); padding: var(--space-md); }
.fe-voll-fenster[open] { display: flex; flex-direction: column; }
.feld.fe-voll { flex: 1; display: flex; flex-direction: column; min-block-size: 0; border: 0; padding: 0; background: none; }
.feld.fe-voll > .feld__eingabe { flex: 1; min-block-size: 0; }
/* Im eigenen Fenster ist das Fenster der Rahmen; die Kante des Textfelds wäre der zweite. */
.feld.fe-voll .fe { block-size: auto; flex: 1; resize: none; padding-inline: 0; background: transparent; border-color: transparent; outline: none; }

@media (prefers-reduced-motion: reduce) {
  .formular--kompakt .feld :is(input, textarea),
  .formular--kompakt .eigenauswahl > summary,
  .formular--kompakt .tokenfeld,
  .formular--kompakt .feld:has(textarea) { transition: none; }
}

/* ---------------------------------------------------------- Schreibfläche, Fassung des Cockpits
   Vasco, 2026-10-08 (W8, nach dem Vergleich Cockpit gegen Kern): Schreibfläche und Felder wie im
   Vertriebscockpit. Bis dahin hatte der Kern nur Grösse und Werkzeugleiste der Schreibfläche; wie
   Text, Überschriften, Listen mit Kästchen, Tabellen, Code, aufklappbare Abschnitte und Lesezeichen
   darin aussehen, stand nur im Cockpit. Wörtlich übernommen aus CockpitCRM/app.html, ohne
   den Einzug links für die Ziehgriffe der Blöcke (die hat der Kern nicht). Die Lesefassung
   (.seite-text) teilt dieselben Regeln. */
.feld .fe { position: relative; font-family: var(--font-ui); font-size: var(--text-sm); line-height: var(--leading-snug); width: 100%; min-height: max(calc(var(--ctl-h) * 2), calc(var(--zeilen, 4) * 1.4em + var(--space-sm))); padding: var(--space-2xs) var(--space-sm); color: var(--ink); background: var(--surface-base); border: var(--border-w) solid var(--border-strong); border-radius: var(--r-sm); max-width: none; overflow-wrap: anywhere; cursor: text; }
.feld .fe:focus-visible, .feld .fe:focus { outline: none; border-color: var(--accent); }
.formular--kompakt .feld .fe { background: transparent; border-color: transparent; transition: background var(--t-sofort) var(--ease), border-color var(--t-sofort) var(--ease); }
.formular--kompakt .feld .fe:hover { background: var(--surface-state); }
.fe.fe--leer::before { content: attr(data-platzhalter); color: var(--ink-faint); pointer-events: none; position: absolute; inset-block-start: var(--space-2xs); inset-inline-start: var(--space-sm); }
.fe > * + *, .fe p + p { margin-top: var(--space-2xs); }
.fe h1, .fe h2, .fe h3 { margin-top: var(--space-md); }
.fe > :first-child { margin-top: 0; }
.fe table { border-collapse: collapse; width: 100%; margin-block-start: var(--space-lg); }
.fe :is(th, td) { border: var(--border-w) solid var(--border-subtle); padding: var(--space-2xs) var(--space-xs); text-align: start; vertical-align: top; min-width: 4rem; }
.fe th { font-weight: var(--fw-semibold); background: var(--surface-raised); }
.fe pre { padding: var(--space-xs) var(--space-sm); background: var(--surface-raised); border-radius: var(--r-sm); white-space: pre-wrap; }
.fe hr { border: 0; border-top: var(--border-w) solid var(--border-subtle); }
.fe .fe-ref { text-decoration: underline; text-decoration-color: var(--border-strong); text-underline-offset: 0.2em; cursor: pointer; }
.fe li:has(> input[type="checkbox"]) { list-style: none; }
.feld .fe li > input[type="checkbox"], .fe li > input[type="checkbox"] { display: inline-block; margin: 0 var(--space-xs) 0 calc(-1 * var(--space-lg)); padding: 0; width: 1rem; min-width: 0; height: 1rem; min-height: 0; vertical-align: -0.15em; cursor: pointer; border-radius: var(--r-xs); }
.fe li:has(> input[type="checkbox"]:checked) { color: var(--ink-faint); text-decoration: line-through; }
/* Die Lesefassung (.seite-text) zeigt dieselben Inhalte wie die Schreibfläche, gesetzt zum Lesen.
   Bisher hatte sie nur Aufklapper, Lesezeichen und Bild; Überschriften, Abstände, Zitat, Code,
   Trennlinie und Listen mit Kästchen stand nur im Cockpit (aus dem Cockpit, 2026-10-09). */
.seite-text { min-width: 0; max-width: 72ch; }
.seite-text > * + * { margin-top: var(--space-sm); }
.seite-text > :first-child { margin-top: 0; }
.seite-text h1 { font-size: var(--text-2xl); }
.seite-text h2 { font-size: var(--text-xl); margin-top: var(--space-xl); }
.seite-text h3 { font-size: var(--text-lg); margin-top: var(--space-lg); }
.seite-text h4 { margin-top: var(--space-md); }
.seite-text :is(ul, ol) { padding-inline-start: var(--space-lg); }
.seite-text li + li { margin-top: var(--space-3xs); }
.seite-text blockquote { margin-inline: 0; padding: var(--space-sm) var(--space-md); background: var(--surface-raised); border-radius: var(--r-md); color: var(--ink-muted); }
.seite-text blockquote > * + * { margin-top: var(--space-xs); }
.seite-text code { font-size: 0.92em; padding: 0 var(--space-3xs); background: var(--surface-raised); border-radius: var(--r-sm); }
.seite-text pre { overflow-x: auto; padding: var(--space-sm); background: var(--surface-raised); border-radius: var(--r-md); }
.seite-text pre code { padding: 0; background: none; }
.seite-text hr { border: 0; border-top: var(--border-w) solid var(--border-subtle); margin-block: var(--space-lg); }
.seite-text img { max-width: 100%; height: auto; border-radius: var(--r-sm); }
.seite-text li:has(> input[type="checkbox"]) { list-style: none; }
.seite-text li:has(> input[type="checkbox"]:checked) { color: var(--ink-faint); text-decoration: line-through; }
:is(.fe, .seite-text) h4 { font-size: var(--text-md); }
:is(.fe, .seite-text) details { margin-block: var(--space-xs); }
:is(.fe, .seite-text) details > summary { cursor: pointer; font-weight: var(--fw-semibold); }
:is(.fe, .seite-text) details > :not(summary) { margin-inline-start: var(--space-lg); margin-top: var(--space-xs); }
:is(.fe, .seite-text) details[data-h="1"] > summary { font-size: var(--text-2xl); font-weight: var(--fw-extrabold); }
:is(.fe, .seite-text) details[data-h="2"] > summary { font-size: var(--text-xl); font-weight: var(--fw-bold); }
:is(.fe, .seite-text) details[data-h="3"] > summary { font-size: var(--text-lg); font-weight: var(--fw-bold); }
:is(.fe, .seite-text) details[data-h="4"] > summary { font-size: var(--text-md); font-weight: var(--fw-bold); }
.fe summary { cursor: text; }
.fe summary::marker { cursor: pointer; }
:is(.fe, .seite-text) p.lesezeichen > a { display: grid; gap: var(--space-3xs); padding: var(--space-sm) var(--space-md); border: var(--border-w) solid var(--border-subtle); border-radius: var(--r-md); text-decoration: none; font-weight: var(--fw-semibold); color: var(--ink); }
:is(.fe, .seite-text) p.lesezeichen > a::after { content: attr(data-domain); font-weight: var(--fw-regular); font-size: var(--text-xs); color: var(--ink-faint); }
:is(.fe, .seite-text) p.lesezeichen > a:hover { background: var(--surface-state); }
:is(.fe, .seite-text) img.fe-bild { max-width: 100%; height: auto; border-radius: var(--r-md); }

/* Das Zeichen vor einer Beschriftung schrumpft nicht und steht leise (aus dem Cockpit). */
.feld-symbol { flex: none; color: var(--ink-muted); }
/* Der Schalter «Alle Felder» steht im Kompaktformular bündig mit den Beschriftungen (aus dem Cockpit). */
.formular--kompakt .felder-schalter { padding-inline: 0; }

/* ------------------------------------------------------------- hakenliste
   Eine Gruppe von Kästchen im fieldset mit sichtbarer Legende. Das Markup der einzelnen Zeile
   bleibt der Baustein haken; hier steht nur, was die Gruppe um ihn legt. Zusammenhangsregeln
   `.hakenliste .haken`, weil haken inline-flex ist und die Zeile in der Variante «beschreibung»
   die ganze Breite als Trefferfläche braucht. */
.hakenliste { border: 0; margin: 0; padding: 0; min-inline-size: 0; }
.hakenliste__legende {
  padding: 0;
  margin-bottom: var(--space-xs);
  font-size: var(--text-sm);
  font-weight: var(--fw-medium);
  color: var(--ink);
}
.hakenliste__liste { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2xs); }

.hakenliste--beschreibung .hakenliste__liste { align-items: stretch; gap: 0; border-block: var(--border-w) solid var(--border-subtle); }
.hakenliste--beschreibung .haken { display: flex; width: 100%; padding-block: var(--space-sm); gap: var(--space-sm); }
.hakenliste--beschreibung .haken + .haken { border-top: var(--border-w) solid var(--border-subtle); }
/* Das Kästchen steht mittig auf der Titelzeile, nicht auf der Mitte des ganzen Textblocks. */
.hakenliste--beschreibung .haken__kasten { margin-block-start: calc((1lh - 1em) / 2); }
.hakenliste__text { display: flex; flex-direction: column; gap: var(--space-3xs); }
.hakenliste__titel { font-weight: var(--fw-medium); color: var(--ink); }
.hakenliste__beschreibung { color: var(--ink-muted); font-size: var(--text-xs); }
/* ------------------------------------------------------------------- lupe
   Eine Lupe, die dem Zeiger oder den Pfeiltasten über eine Zeichnung folgt. Aufbau nach Magic UI «Lens»,
   eigenes Werk, 2026-10-10. Ohne Bibliothek: die Zeichnung liegt ein zweites Mal vergrössert unter einer
   runden Maske (clip-path), Lage und Grösse kommen aus Variablen, die system/lupe.js setzt.
   Grösse der Lupe: --lupe-groesse (Vorgabe 10rem), Vergrösserung: --lupe-zoom (Vorgabe 2.4).
   Senkrecht darf die Seite rollen, waagrecht führt der Finger die Lupe. */
.lupe { container-type: inline-size; inline-size: 100%; display: grid; gap: var(--space-xs); margin: 0; }
.lupe__flaeche {
  --lupe-d: var(--lupe-groesse, 10rem);
  --lupe-x: 50%;
  --lupe-y: 50%;
  position: relative;
  overflow: hidden;
  border: var(--border-w) solid var(--border-subtle);
  border-radius: var(--r-md);
  background: var(--surface-base);
  cursor: crosshair;
  touch-action: pan-y;
  user-select: none;
}
.lupe__glas {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--surface-base);
  clip-path: circle(calc(var(--lupe-d) / 2) at var(--lupe-x) var(--lupe-y));
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-fast) var(--ease);
}
.lupe__innen { position: absolute; inset: 0; transform: scale(var(--lupe-zoom, 2.4)); transform-origin: var(--lupe-x) var(--lupe-y); }
.lupe__ring {
  position: absolute;
  inset-inline-start: var(--lupe-x);
  inset-block-start: var(--lupe-y);
  width: var(--lupe-d);
  height: var(--lupe-d);
  margin-inline-start: calc(var(--lupe-d) / -2);
  margin-block-start: calc(var(--lupe-d) / -2);
  border: var(--border-w-stark) solid var(--ink);
  border-radius: var(--r-pill);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-fast) var(--ease);
}
.lupe__flaeche[data-lupe="an"] .lupe__glas, .lupe__flaeche[data-lupe="an"] .lupe__ring { opacity: 1; }
.lupe__flaeche:focus-visible { outline: var(--border-w-stark) solid var(--accent); outline-offset: calc(var(--border-w) * -2); }
.lupe__hinweis { margin: 0; font-size: var(--text-xs); color: var(--ink-faint); max-width: var(--wrap-text); }
@container (max-width: 33.99rem) { .lupe__flaeche { --lupe-d: min(var(--lupe-groesse, 10rem), 8rem); } }
/* Zeichnung in Token-Farben, hier ein Grundriss. f1 = Fläche base, f2 = raised, f3 = overlay. */
.lupe__bild { display: block; width: 100%; height: auto; }
.lupe__bild text { font-family: var(--font-ui); fill: var(--ink-muted); }
.lupe__bild .lupe__fett { fill: var(--ink); font-weight: var(--fw-semibold); }
.lupe__bild .lupe__faint { fill: var(--ink-faint); }
.lupe__bild .lupe__linie { fill: none; stroke: var(--ink); stroke-linecap: round; stroke-linejoin: round; }
.lupe__bild .lupe__fein { fill: none; stroke: var(--border-strong); stroke-linecap: round; stroke-linejoin: round; }
.lupe__bild .lupe__zart { fill: none; stroke: var(--border-subtle); stroke-linecap: round; stroke-linejoin: round; }
.lupe__bild .lupe__f1 { fill: var(--surface-base); stroke: none; }
.lupe__bild .lupe__f2 { fill: var(--surface-raised); stroke: none; }
.lupe__bild .lupe__f3 { fill: var(--surface-overlay); stroke: none; }

/* ============================================================
   kartenstapel — Karten vom Stapel ziehen (Aufbau nach React Bits «Stack», eigenes Werk, 2026-10-10)

   Vier Tourenkarten liegen als Fächer übereinander. Die oberste zieht man weg, tippt sie an oder
   blättert mit den Pfeiltasten; sie fliegt bis zur freien Fläche und legt sich hinten ein. Das Skript
   (system/kartenstapel.js) setzt Lage, Neigung und Deckkraft je Karte als inline transform. Die
   Malreihenfolge ist die DOM-Reihenfolge: Karten sind absolut gelegt, order wirkt dort nicht, und
   früher lag deshalb die hinterste Karte oben. Nur die Karte mit data-oben nimmt Zeigerereignisse an.
   Tiefe entsteht über Fläche, Kante und Staffelung, nicht über Schatten. Ohne Skript stehen die
   Karten untereinander und sind alle lesbar. */
.kartenstapel { display: grid; justify-items: center; gap: var(--space-md); padding: var(--space-xl); }
.kartenstapel__stapel { position: relative; inline-size: 100%; max-inline-size: 22rem; aspect-ratio: 4 / 5; outline: none; }
.kartenstapel__stapel:not([data-bereit]) { aspect-ratio: auto; display: grid; gap: var(--space-md); }
.kartenstapel__stapel:not([data-bereit]) .kartenstapel__karte { position: relative; }

.kartenstapel__karte { position: absolute; inset: 0; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; will-change: transform; }
.kartenstapel__stapel[data-bereit] .kartenstapel__karte { pointer-events: none; }
.kartenstapel__stapel[data-bereit] .kartenstapel__karte[data-oben] { pointer-events: auto; cursor: grab; touch-action: pan-y; }
.kartenstapel__stapel.ist-zieht .kartenstapel__karte[data-oben] { cursor: grabbing; }

.kartenstapel__flaeche {
  position: absolute; inset: 0; overflow: hidden; will-change: transform;
  background: var(--surface-raised); border: var(--border-w) solid var(--border-strong); border-radius: var(--r-md);
}
.kartenstapel__stapel:not([data-bereit]) .kartenstapel__flaeche { position: relative; }
.kartenstapel__inhalt { display: grid; grid-template-rows: minmax(0, 1fr) auto; block-size: 100%; }
.kartenstapel__stapel:not([data-bereit]) .kartenstapel__inhalt { block-size: auto; }
.kartenstapel__bild { min-block-size: 0; background: var(--surface-overlay); border-bottom: var(--border-w) solid var(--border-subtle); }
.kartenstapel__stapel:not([data-bereit]) .kartenstapel__bild { aspect-ratio: 4 / 3; }
/* Das Foto füllt die Fläche und fängt keinen Zeiger ab, sonst finge es das Ziehen der Karte. */
.kartenstapel__bild img { display: block; inline-size: 100%; block-size: 100%; object-fit: cover; pointer-events: none; }
.kartenstapel__foto--links { object-position: 20% 50%; }
.kartenstapel__unten { display: grid; gap: var(--space-3xs); padding: var(--space-sm) var(--space-md) var(--space-md); }
.kartenstapel__titel { font-size: var(--text-lg); font-weight: var(--fw-semibold); color: var(--ink); margin: 0; }
.kartenstapel__unten p { font-size: var(--text-sm); color: var(--ink-muted); margin: 0; }
.kartenstapel__unten .kartenstapel__preis { font-size: var(--text-xs); color: var(--ink-faint); }

/* Fokus liegt auf der obersten Karte, innen, damit der Rand nicht abgeschnitten wird. */
.kartenstapel__stapel:focus-visible .kartenstapel__karte[data-oben] .kartenstapel__flaeche { outline: var(--border-w) solid var(--focus); outline-offset: calc(var(--border-w) * -2); }

.kartenstapel__bedienung { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: var(--space-xs) var(--space-md); }
.kartenstapel__stand { font-size: var(--text-sm); color: var(--ink-muted); text-align: center; margin: 0; }
.kartenstapel__hinweis { font-size: var(--text-xs); color: var(--ink-faint); text-align: center; margin: 0; }

