/* ── Schriften ──────────────────────────────────────────────────────────
   Diese Zeile MUSS die erste Regel der Datei bleiben. Ein @import hinter
   einer beliebigen anderen Regel ist ungueltig und wird verworfen — genau
   das war der Fall (er stand hinter dem :root-Block der Skala), und das
   ganze Panel lief auf system-ui: keine Fehlermeldung, nichts Kaputtes zu
   sehen, nur ueberall die falsche Schrift.

   Bricolage Grotesque  Ueberschriften
   Instrument Sans      Fliesstext und Bedienelemente
   JetBrains Mono       Zahlen, Masse, Kennungen */
@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,400;12..96,600;12..96,700;12..96,800&family=Instrument+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');

/* ══ Vertrag: Breakpoints, Schriftskala, Dichte (Welle 1) ═══════════════
   DREI kanonische Bruchstellen — und keine vierte:

     640px   Telefon.   Die Skala selbst wird kleiner (Block direkt unter
                        :root), --dichte 0.75, Reiter werden Pillen,
                        Dialoge werden Bodenblaetter, ein Blatt (.ds-blatt)
                        kommt von unten.
                        TELEFON QUER ist auch Telefon: dieselbe Stufe gilt
                        fuer  (max-width: 640px), (orientation: landscape)
                        and (max-height: 500px)  — Skala, Dichte, Blaetter
                        ganz, Assistenten ohne Buehne, Hilfe-Knopf aus. Das
                        ist eine Eigenschaft des Geraets (390px Hoehe), keine
                        vierte Bruchstelle: hochkant 844px breit gibt es nicht,
                        quer 844px breit ist ein Telefon. Wer am Telefon
                        etwas anders setzt, schreibt BEIDE Bedingungen.
     900px   Schmal.    Seitenleiste wird Schublade, untere Leiste
                        erscheint, eine Spalte, Tippziele 44px, Kopfzeile
                        rollt mit, .ds-blatt wird Seitenpanel.
                        Telefon quer (932 x 430) gehoert auch hierher:
                        (max-width: 900px), (orientation: landscape) and
                        (max-height: 500px). Die Gegenstuecke ab 901/641 px
                        schreiben  (min-width: 901px) and (min-height: 501px),
                        (min-width: 901px) and (orientation: portrait)  —
                        NICHT «and (not (…))», das kennt Safari erst ab 16.4.
    1200px   Weit.      Zweite Spalte fuer Vorschau neben Formular;
                        .ds-content bleibt bei 1120px Lesebreite.

   Ein @media in diesem Panel nennt eine dieser drei Zahlen oder eine
   Eigenschaft des Geraets (hover, forced-colors, prefers-*). 768, 480,
   1024 und Verwandte gibt es nicht mehr — wer eine braucht, hat ein
   Layout, das an einer der drei nicht funktioniert, und repariert das.

   SCHRIFT: sieben Stufen in rem, streng aufsteigend, keine Inversion.
   Gemessen waren es 95 Schriftgroessen zwischen 9 und 56px — 12,5 / 12,6 /
   12,8 / 13 / 13,1 nebeneinander, und --fs-xs (12,5) war GROESSER als
   --fs-sm (12). Solche Unterschiede sieht niemand als Groesse; man sieht
   sie als schiefe Ausrichtung und unruhige Kanten.

     Token      Schreibtisch   Telefon (≤640 / quer)   Rolle
     --fs-xs    12px .75rem    10px             kleinste: Hinweise, Werte, Zaehler
     --fs-sm    12.5px         10.25px          Beschriftungen, Nebenzeilen
     --fs-base  13px           10.5px           Fliesstext im Panel
     --fs-md    14px           11.5px           Felder, Knoepfe, Absatz in Karten
     --fs-lg    16px           12.5px           Kartentitel, Einleitung
     --fs-xl    18px           14px             Abschnittstitel
     --fs-2xl   20px           15.5px           Seitentitel
     (--fs-3xl/4xl/5xl: Zahlen und Hero — 24/32/40 → 18/23/28)
     Die Untergrenze am Telefon bleibt 10px (--fs-xs): darunter wird geraten.
     Felder rendern dort in der Skala (11.5px), NICHT mit 16px: der iOS-Zoom
     beim Antippen wird an EINER Stelle verhindert (partials/theme-vars.ejs:
     maximum-scale=1 nur auf iOS/iPadOS). Tippziele bleiben 44px.

   rem statt px, damit die Browser-Grundschrift (Barrierefreiheit:
   «Schrift groesser» im System) durchschlaegt. Nichts im Panel setzt
   html { font-size }, darum ist 1rem = 16px die Rechenbasis.

   ALT (Aliasse — bleiben, weil ~980 Deklarationen in den Ansichten
   var(--fs-*) lesen; wer eine Ansicht anfasst, schreibt den neuen Namen):
     --fs-label  → --fs-sm   (Mono-Versallabels; 12.5px blieb die lesbare
                              Grenze fuer Versalien ohne Oberlaengen)
     --fs-wert   → --fs-xs   (Mono-Wertangaben: Zaehler, Masse, Prozente)
     --fs-2xs    → --fs-xs   (war nie definiert, 2 Nutzer fielen auf
                              «inherit» bzw. den Rueckfall 11px)

   DICHTE: --dichte multipliziert das Innenmass von Karten, Kacheln,
   Hero und Inhaltsbereich — 1 am Schreibtisch, 0.75 am Telefon. Eine
   Zahl, nicht zwanzig @media mit eigenem Padding.

   Ein neuer Wert gehoert in diese Liste oder gar nicht ins Panel. */
:root {
  /* Klassisch (ohne data-glas) ist hell; ds-glas.css setzt je Fassung
     light/dark. Ohne diese Zeile raten Rollbalken und Auswahllisten. */
  color-scheme: light;

  /* Schrift — Skala */
  --fs-xs:   0.75rem;     /* 12px   kleinste: Hinweise, Werte */
  --fs-sm:   0.78125rem;  /* 12.5px Beschriftungen */
  --fs-base: 0.8125rem;   /* 13px   Fliesstext im Panel */
  --fs-md:   0.875rem;    /* 14px */
  --fs-lg:   1rem;        /* 16px */
  --fs-xl:   1.125rem;    /* 18px */
  --fs-2xl:  1.25rem;     /* 20px */
  --fs-3xl:  1.5rem;      /* 24px */
  --fs-4xl:  2rem;        /* 32px */
  --fs-5xl:  2.5rem;      /* 40px */
  /* ALT — Aliasse auf die Skala, siehe Kopf. */
  --fs-label: var(--fs-sm);
  --fs-wert:  var(--fs-xs);
  --fs-2xs:   var(--fs-xs);

  /* Dichte */
  --dichte: 1;

  /* Abstaende */
  --sp-3xs:  2px;
  --sp-2xs:  4px;
  --sp-xs:   6px;
  --sp-sm:   8px;
  --sp-md:  12px;
  --sp-lg:  16px;
  --sp-xl:  20px;
  --sp-2xl: 24px;
  --sp-3xl: 32px;

  /* Rundungen — 999px (Pille) und 50% (Kreis) bleiben ausserhalb der Skala,
     die sind Form, nicht Groesse. --r-xs/--r-sm/--r-lg standen hier UND
     im Token-Block unten mit anderen Werten; gewonnen hat immer der
     untere (4/6/10 waren tote Zeilen). Sie stehen jetzt nur noch dort. */
  --r-md:    8px;
  --r-xl:   12px;
  --r-2xl:  16px;
}

/* ── Mobilstufe: die Skala selbst wird kleiner ──────────────────────────
   DER eine Block, der die Tokens am Telefon neu setzt. Alles, was
   var(--fs-*) liest, folgt von selbst; harte rem/px-Werte in den
   Ansichten tun es nicht — die werden auf Tokens gezogen, nicht hier
   nachgebessert. Tippziele bleiben 44px (Block «Tippziele» unten):
   kleinere Schrift heisst nicht kleinere Knoepfe. */
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  :root {
    --fs-xs:   0.625rem;    /* 10px    die Untergrenze; darunter wird geraten */
    --fs-sm:   0.640625rem; /* 10.25px */
    --fs-base: 0.65625rem;  /* 10.5px  Grundschrift am Telefon */
    --fs-md:   0.71875rem;  /* 11.5px */
    --fs-lg:   0.78125rem;  /* 12.5px */
    --fs-xl:   0.875rem;    /* 14px */
    --fs-2xl:  0.96875rem;  /* 15.5px */
    --fs-3xl:  1.125rem;    /* 18px */
    --fs-4xl:  1.4375rem;   /* 23px */
    --fs-5xl:  1.75rem;     /* 28px */
    --dichte:  0.75;
  }
  /* Bedienelemente ohne eigene Schrift erben die Skala, statt die
     Browser-Vorgabe 13.33px zu nehmen (Panel-U2 Punkt 6: «Bild wählen …»,
     «Noch keine Beiträge»). :where() hat keine Gewichtung, jede Regel mit
     eigener font-size gewinnt weiter. */
  :where(button, input, select, textarea) { font-size: inherit; }
}

/* ════════════════════════════════════════════════════════════════════════
   DeSoft Builder — Shared Admin Design System  ("Hell & modern")
   Linked by every admin page:  <link rel="stylesheet" href="/css/admin.css">
   Sidebar, Tour und Hilfe: /css/ds-leiste.css, ds-hilfe.css, Glasfassungen
   ds-glas.css (Quellen: views/quellen/, erzeugt mit erzeugen.cjs). This file
   owns the page chrome: layout, hero headers, cards, buttons, tables, forms.
   ════════════════════════════════════════════════════════════════════════ */

:root {
  /* ══ v7-Tokens — die eine Quelle. Werte aus AUFTRAG §1, unverändert. ══ */

  /* Flächen */
  --paper:#F4F3EF;        /* Arbeitsfläche */
  --surface:#FFFFFF;      /* Karten, Panels, Leisten */
  --surface-2:#FAF9F6;    /* Tabellenkopf, Gruppenzeile */

  /* Text */
  --ink:#14161A;
  --muted:#62676F;
  /* §7 — Kontrast mindestens 4.5:1. Die beiden Grautoene lagen bei 3.25:1
     und 2.45:1 auf Weiss: lesbar fuer gute Augen bei gutem Licht, und genau
     dafuer ist die Schwelle nicht gemacht. Nachgemessen ueber alle 17
     Ansichten waren sie die Ursache saemtlicher 293 Unterschreitungen.
     Jetzt 5.07:1 auf Weiss und 4.81:1 auf der Tabellenkopf-Flaeche. */
  /* #696F78 auf der Vertiefung waren 4,3:1 ("nicht geprüft" im Hosting).
     Eine Stufe dunkler bringt es ueber die Schwelle, ohne dass die Farbe
     ihre Rolle als gedaempfter Ton verliert. */
  --faint:#5E646D;        /* Feldbeschriftungen, Pfade */
  --disabled:#726E68;

  /* Linien */
  --line:#E3E1DA;         /* Karten, Trenner */
  --line-soft:#EFEDE7;    /* Zeilentrenner */
  --line-field:#D8D5CD;   /* Eingabefelder */

  /* Aktion — Petrolblau statt des bisherigen Violetts (§1: keine violetten Flächen) */
  --primary:#1D5C77;
  --primary-hover:#164B61;
  /* Links, Auswahl, Fokusring.
     Vorher #4A90C2 — und das reichte in KEINER seiner beiden Rollen: als
     Linktext auf Weiss 3,47:1, als Flaeche unter weisser Schrift ebenfalls
     3,47:1. Beide Rollen verlangen 4,5:1. Gemessen sind es jetzt 4,75:1,
     bei gleichem Blauton: #3878A8 ist der hellste Wert der Familie, der die
     Schwelle nimmt. Das traegt 58 Regeln im Panel, die weisse Schrift auf
     diese Flaeche setzen — sie alle einzeln zu aendern hiesse, denselben
     Fehler 58-mal zu verwalten. */
  --accent:#3878A8;
  /* Schrift AUF dem Akzent bzw. auf den Signalflaechen (--ok/--danger/
     --warn). Hier Weiss; die Nachtfassung (theme-vars.ejs) setzt dunkle
     Schrift, weil ihr Akzent (#7EC8F0) und ihre Signalfarben hell sind —
     weisse Schrift darauf kam auf 1,8:1. Regeln schreiben darum
     `color: var(--akzent-auf, #fff)` statt `color: #fff`. */
  --akzent-auf:#fff;
  /* Hintergrund einer Kundenwebsite-Vorschau (iframe, Mock): bleibt weiss, auch in der Nachtfassung des Panels. */
  --seite-weiss:#fff;
  --signal-auf:#fff;
  --tint:#EAF2F7;         /* aktive Zeile, Hinweisfläche */
  --tint-line:#CFE0EA;

  /* Zustände */
  /* #2E7D5B auf der eigenen Toenung waren 4,4:1 — knapp unter den
     geforderten 4,5:1. Zwei Stufen dunkler genuegen. */
  --ok:#256B4D;    --ok-bg:#E6F2EC;
  /* §7 — #A9711C erreichte auf der eigenen Tinte nur 3.67:1. Abgedunkelt
     auf 5.23:1; Weiss darauf (Warnpille) steigt dadurch ebenfalls. */
  --warn:#8A5A12;  --warn-bg:#FAF0DE;
  --danger:#B3352C;--danger-bg:#FBE9E7;--danger-hover:#8E2A22;

  /* Masse */
  --r-card:12px; --r-field:8px; --r-pill:999px;
  --h-field:38px; --h-row:52px; --h-bar:56px;
  --shadow:0 1px 2px rgba(20,22,26,.05);
  --shadow-float:0 2px 10px rgba(20,22,26,.09);

  /* Schrift */
  --font-head:'Bricolage Grotesque',system-ui,sans-serif;
  --font-ui:'Instrument Sans',system-ui,sans-serif;
  --font-mono:'JetBrains Mono',ui-monospace,monospace;

  /* ══ Altnamen — zeigen auf die v7-Tokens. ══
     Sie bleiben, solange eine Ansicht sie benutzt (Welle 5: die fuenf ungenutzten sind entfernt). Wer eine Ansicht anfasst,
     ersetzt sie dort durch die v7-Namen; bis dahin stimmt die Farbe trotzdem. */
  --bg: var(--paper);
  --sunken: var(--line-soft);
  --ink-2: var(--muted); --ink-3: var(--faint); --ink-4: var(--disabled);
  --line-2: var(--line-soft); --line-strong: var(--line-field);
  --accent-strong: var(--primary-hover);
  --accent-tint: var(--tint); --accent-tint-2: var(--tint-line);
  --ok-tint: var(--ok-bg); --warn-tint: var(--warn-bg); --danger-tint: var(--danger-bg);
  --text: var(--ink); --text-muted: var(--muted); --text-light: var(--faint);
  --border: var(--line); --border-strong: var(--line-field);
  --green: var(--ok); --green-bg: var(--ok-bg); --green-light: var(--ok-bg);
  --green-text:#215F44;
  --red: var(--danger); --red-light: var(--danger-bg);
  --amber: var(--warn);
  --orange: var(--warn); --orange-light: var(--warn-bg);
  --yellow: var(--warn); --yellow-light: var(--warn-bg);
  /* Violett gab es als eigene Kategorie — es zeigt jetzt auf die Aktionsfarbe. */
  --purple: var(--primary); --purple-light: var(--tint);
  --accent-light: var(--tint-line);
  /* --surface-alt wurde von fuenf Ansichten benutzt, war aber nie definiert. */
  --surface-alt: var(--surface-2);
  --brand: var(--ink);
  --accent-dark: var(--primary-hover);
  /* Verläufe sind in v7 nicht vorgesehen (§1). Die Namen bleiben, liefern aber
     eine Fläche — sonst müssten 40 Ansichten gleichzeitig umgebaut werden. */
  --grad: var(--primary);
  --grad-soft: var(--tint);
  /* Muss zu --accent passen. Stand auf 29,92,119 — das ist --primary.
     Der Name versprach das eine, der Wert lieferte das andere. */
  --accent-rgb:56,120,168;

  /* Seitenleiste: in v7 hell mit rechter Linie, nicht mehr dunkel (§3.3) */
  --sb: var(--surface); --sb-2: var(--surface-2); --sb-line: var(--line);
  --sb-ink: var(--ink); --sb-ink-2: var(--muted); --sb-ink-3: var(--faint);

  /* Die wirksamen Rundungen (263 Nutzer in den Ansichten) — frueher
     zusaetzlich oben in der Skala mit anderen Zahlen, die nie galten. */
  --r: var(--r-card); --r-sm: var(--r-field); --r-lg:16px; --r-xs:6px;
  --radius: var(--r-card); --radius-sm: var(--r-field); --radius-lg:16px;
  --sh: var(--shadow); --sh-sm: var(--shadow);
  --shadow-md: var(--shadow-float);
  --shadow-lg:0 18px 50px -20px rgba(20,22,26,.22);
  --panel-w:320px;          /* §5.3: Editor-Panel 428 → 320 */
  --sidebar-w:212px;        /* §3.3 */
  /* Fokusring — die Farbe steht als eigenes Token, weil sie je
     Fassung anders sein MUSS: --tint-line (#CFE0EA) ergibt auf Weiss
     1,3:1 und ist damit unsichtbar, auf dem dunklen Glas dagegen ~14:1.
     theme-vars.ejs setzt --ring-farbe je Fassung; hier steht der Wert
     fuer die Grundpalette (= Fassung „klassisch" und Rueckfallebene). */
  --ring-farbe: var(--primary);
  --ring:0 0 0 3px var(--ring-farbe);
  --t-fast:.13s ease; --t:.18s cubic-bezier(.4,0,.2,1);

  /* ── Die Ebenen ───────────────────────────────────────────────────────
     Vier Sprossen, mehr braucht das Panel nicht. Gezaehlt waren es 40
     verschiedene Werte bis hinauf zu 2147483600 — jede neue Zahl war
     groesser als die, die jemand gerade vor sich sah.

       leiste      klebt an der Seite und scrollt mit ihr
       schwebend   liegt ueber der Seite: untere Navigation, Speicherleiste
       fenster     nimmt die Seite gefangen: Dialog, Schublade, Menue
       meldung     steht ueber allem, auch ueber einem offenen Dialog —
                   sonst meldet das Panel den Erfolg hinter dem Fenster,
                   das man gerade schliesst

     Der Abstand von 200 zwischen den Sprossen laesst Platz fuer das, was
     innerhalb einer Ebene noch uebereinander liegen muss (Verdunkelung
     unter dem Fenster). */
  --z-leiste: 100;
  --z-schwebend: 200;
  --z-fenster: 400;
  --z-meldung: 600;
  /* Die Fuehrung erklaert die Oberflaeche — sie muss ueber ihr liegen,
     auch ueber einer Meldung. Dafuer gab es keinen Namen, also stand
     dort 99999. Das ist die Sprosse, die der Leiter fehlte. */
  --z-tour: 800;
}


* , *::before, *::after { box-sizing: border-box; }
body.ds-app { margin: 0; font-family: var(--font-ui); background: var(--bg); color: var(--ink); display: flex; min-height: 100vh; -webkit-font-smoothing: antialiased; }

/* ── Global polish: focus, scrollbars, motion ─── */
a { color: var(--accent-strong); }
/* P0-5 · Der Fokusring.
   Eine echte outline, kein Schatten: ein box-shadow wird von jedem
   `overflow: hidden` der Elternkette abgeschnitten, und im erzwungenen
   Kontrastmodus wird er gar nicht gezeichnet. Beides trifft das Panel
   an vielen Stellen — Karten, Tabellen, Schubladen sind alle
   beschneidend. */
:focus-visible {
  outline: 2px solid var(--ring-farbe);
  outline-offset: 2px;
  border-radius: var(--r-md);
}
/* Im Kontrastmodus bestimmt das Betriebssystem die Farben. Highlight
   ist die dort vorgesehene Systemfarbe fuer genau diesen Zweck. */
@media (forced-colors: active) {
  :focus-visible { outline: 2px solid Highlight; outline-offset: 2px; }
}
/* Ein Fokushinweis darf nicht erst entstehen.
   `transition: all` stand frueher in den Ansichten (Welle 5: ersetzt durch
   Eigenschaftslisten) und animierte damit auch outline-width und outline-offset —
   der Ring wuchs in 150 ms heran. Hier wird genau diese eine Eigenschaft
   ausgenommen; alles andere darf weiter weich wechseln.

   Das !important ist noetig, nicht bequem: admin.css steht VOR den
   <style>-Bloecken der Ansichten, und `.fi:focus` ist spezifischer als
   `:focus-visible`. Die Regel gilt beim EINTRETEN in den Fokus (CSS nimmt
   die Uebergangsangaben des Zielzustands); beim Verlassen bleibt es beim
   weichen Ausblenden. */
:focus-visible {
  transition-property: background-color, border-color, color, box-shadow,
                       opacity, transform, filter !important;
}
button, a, input, select, textarea, .ds-tile, .ds-card { transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast), box-shadow var(--t-fast), transform var(--t-fast); }
/* Der Sprunglink "Zum Inhalt springen" (partials/sidebar.ejs). Er liegt
   ueber dem oberen Rand und faehrt erst mit dem Tastaturfokus herein —
   wer die Maus benutzt, sieht ihn nie. */
.ds-skip {
  position: fixed; left: 12px; top: 12px; z-index: var(--z-tour);
  transform: translateY(calc(-100% - 24px));
  padding: 10px 14px; border-radius: var(--r-md);
  background: var(--surface); color: var(--text); font-weight: 650; text-decoration: none;
  font-size: var(--fs-base); /* sonst 16px am Telefon (Panel-U2 Punkt 2) */
  border: 2px solid var(--ring-farbe); box-shadow: var(--shadow-float);
}
/* Nur mit der TASTATUR (:focus-visible). Mit :focus fuhr er auch nach
   einem Mausklick oder einem Fokus per Skript herein und blieb oben links
   stehen — auf dem Bild des Inhabers ueber der Web-Easy-Kopfleiste. */
.ds-skip:focus-visible { transform: none; }
@supports not selector(:focus-visible) { .ds-skip:focus { transform: none; } }
/* Das Ziel des Sprunglinks ist eine Flaeche, kein Bedienelement: ein Ring
   um den ganzen Hauptbereich waere Laerm. */
#inhalt:focus { outline: none; }
/* Refined thin scrollbars across the admin */
.ds-app *::-webkit-scrollbar { width: 10px; height: 10px; }
.ds-app *::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
.ds-app *::-webkit-scrollbar-thumb:hover { background: var(--ink-4); background-clip: padding-box; }
.ds-app *::-webkit-scrollbar-track { background: transparent; }
.ds-app * { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }
body.ds-app .main { margin-left: var(--sidebar-w); flex: 1; display: flex; flex-direction: column; min-width: 0; }

/* ── Topbar ──────────────────────────────────────────────────────────── */
.ds-topbar { background: rgba(255,255,255,0.85); backdrop-filter: blur(var(--glas-blur, 12px)); border-bottom: 1px solid var(--border); padding: 0 1.75rem; min-height: 64px; display: flex; align-items: center; gap: var(--sp-lg); position: sticky; top: 0; z-index: 50; }
.ds-topbar h1 { font-size: var(--fs-lg); font-weight: 750; margin: 0; letter-spacing: -0.01em; }
.ds-topbar .ds-spacer { flex: 1; }

/* ── Page content wrapper ────────────────────────────────────────────── */
.ds-content { padding: calc(2rem * var(--dichte)) calc(1.75rem * var(--dichte)) 3rem; max-width: 1120px; width: 100%; margin: 0 auto; }

/* ── Hero header (the signature "hell & modern" banner per tool) ─────── */
/* Calm page intro (design: no accent-gradient banners — neutral Paper, accent only for actions) */
.ds-hero { position: relative; border-radius: var(--r); padding: calc(1.4rem * var(--dichte)) calc(1.6rem * var(--dichte)); margin-bottom: 1.5rem; color: var(--ink); background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-sm); }
.ds-hero::after { display: none; }
.ds-hero-row { display: flex; align-items: center; gap: var(--sp-lg); }
.ds-hero-icon { width: 44px; height: 44px; border-radius: var(--r-lg); background: var(--accent-tint); color: var(--accent-strong); display: flex; align-items: center; justify-content: center; font-size: var(--fs-2xl); flex-shrink: 0; }
.ds-hero-icon .ds-ico { width: 20px; height: 20px; }
.ds-hero h1 { font-size: var(--fs-2xl); font-weight: 800; margin: 0 0 0.2rem; letter-spacing: -0.02em; line-height: 1.1; color: var(--ink); }
.ds-hero p { margin: 0; font-size: var(--fs-md); line-height: 1.5; color: var(--ink-2); max-width: 70ch; }
.ds-hero strong { color: var(--ink); }
.ds-hero-actions { margin-left: auto; display: flex; gap: var(--sp-sm); flex-shrink: 0; }

/* ── Section heading ─────────────────────────────────────────────────── */
.ds-section-title { font-family: var(--font-mono); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink-3); margin: 1.75rem 0 0.85rem; }

/* ── Cards ───────────────────────────────────────────────────────────── */
/* Das Innenmass folgt --dichte (1 / 0.75 am Telefon). .card/.karte unten
   bekommen hier KEIN Padding: sie tragen in 45 Ansichten ihr eigenes, und
   eines aus dieser Datei traefe auch die, die bewusst randlos sind. */
.ds-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: calc(1.4rem * var(--dichte)); }
.ds-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: var(--sp-lg); }

/* ── Project / tool tile ─────────────────────────────────────────────── */
.ds-tile { display: flex; align-items: center; gap: var(--sp-lg); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: calc(1.1rem * var(--dichte)) calc(1.25rem * var(--dichte)); text-decoration: none; color: inherit; transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease; position: relative; }
.ds-tile::before { content: ''; position: absolute; left: 0; top: 14px; bottom: 14px; width: 3px; border-radius: var(--r-xs); background: var(--grad); opacity: 0; transition: opacity 0.16s; }
.ds-tile:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.ds-tile:hover::before { opacity: 1; }
.ds-tile-icon { width: 46px; height: 46px; border-radius: var(--r-xl); background: var(--grad-soft); color: var(--accent); display: flex; align-items: center; justify-content: center; font-size: var(--fs-2xl); flex-shrink: 0; }
.ds-tile-info { flex: 1; min-width: 0; }
.ds-tile-name { font-weight: 700; font-size: var(--fs-lg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-tile-sub { font-size: var(--fs-sm); color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-tile-meta { display: flex; gap: var(--sp-xs); align-items: center; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }
.ds-tile-arrow { color: var(--text-light); flex-shrink: 0; transition: transform 0.16s, color 0.16s; }
.ds-tile:hover .ds-tile-arrow { color: var(--accent); transform: translateX(3px); }

/* ── Badges ──────────────────────────────────────────────────────────── */
.ds-badge { font-size: var(--fs-xs); font-weight: 700; padding: 0.24rem 0.6rem; border-radius: 999px; background: var(--sunken); color: var(--ink-2); white-space: nowrap; display: inline-flex; align-items: center; gap: var(--sp-2xs); }
.ds-badge--on { background: var(--green-bg); color: var(--green-text); }
.ds-badge--accent { background: var(--accent-tint); color: var(--accent-strong); }
.ds-badge--amber { background: var(--warn-tint); color: var(--warn); }
.ds-badge--off { background: var(--sunken); color: var(--text-light); }

/* ── Status pill with dot (HANDOFF §5: Live=ok · In Entwicklung=warn · Entwurf=sunken) ── */
.ds-status { display: inline-flex; align-items: center; gap: var(--sp-xs); font-size: var(--fs-sm); font-weight: 600; padding: 0.24rem 0.62rem 0.24rem 0.5rem; border-radius: 999px; white-space: nowrap; font-family: var(--font-ui); }
.ds-status::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.ds-status--live { background: var(--ok-tint); color: var(--ok); }
.ds-status--dev  { background: var(--warn-tint); color: var(--warn); }
.ds-status--draft{ background: var(--sunken); color: var(--ink-2); }

/* ── Accent swatch picker (Darstellung screen; one accent per view) ── */
.ds-accent-picker { display: inline-flex; gap: var(--sp-sm); flex-wrap: wrap; }
.ds-accent-sw { width: 30px; height: 30px; border-radius: 50%; background: var(--sw); border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--line-strong); cursor: pointer; padding: 0; transition: transform var(--t-fast), box-shadow var(--t-fast); }
.ds-accent-sw:hover { transform: scale(1.08); }
.ds-accent-sw.active { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--sw); }

/* ── Mono numerals (HANDOFF §3: numbers/codes in JetBrains Mono) ── */
.ds-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
/* Regel 5 aus 9.2: Zahlen in Monoschrift, mit gleicher Ziffernbreite.
   Ohne tabular-nums springen die Spalten bei jeder Aktualisierung, weil
   eine 1 schmaler ist als eine 8 — und genau dort, wo verglichen wird
   (Betraege, Zaehler, Masse), ist das Springen am stoerendsten.
   .ds-num hatte es, die uebrigen Mono-Klassen nicht. */
.ds-kpi, .ds-label, .ds-section-title, .dsv-zaehler, .mono, .ov-kpi-w,
.ov-bereit-t, .launch-badge, .launch-percent {
  font-variant-numeric: tabular-nums;
}

/* ══ Das Vokabular des Panels ════════════════════════════════════════════
   Die ds-Primitive darunter heissen .ds-btn, .ds-input, .ds-card. Die
   Ansichten schreiben aber .btn, .fi, .card — und weil es dafuer hier
   keine Definition gab, hat jede Ansicht sich ihre eigene gebaut: 26-mal
   derselbe Knopf, 14-mal dasselbe Eingabefeld, siebenmal dieselbe Karte.

   Die Werte sind nicht erfunden, sondern abgelesen: das ist, worauf sich
   die Ansichten laengst geeinigt haben.

   Das ueberstimmt nichts — admin.css steht VOR den <style>-Bloecken der
   Ansichten. Wer .card lokal definiert, gewinnt weiterhin. Es geht darum,
   dass es ueberhaupt eine Definition gibt: fuer die Ansichten ohne eigene,
   und fuer alles, was noch dazukommt. */

/* Karte — eine Flaeche, auf der etwas steht. */
.card, .karte {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

/* Eingabefeld, mehrzeiliges Feld, Auswahl. */
.fi, .fta, .fse {
  width: 100%;
  padding: 0.65rem 0.85rem;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: var(--fs-md);
  font-family: inherit;
  background: var(--surface);
  color: var(--text);
  transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.fi:focus, .fta:focus, .fse:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
.fta { resize: vertical; min-height: 5rem; }

/* „Hier ist noch nichts." — eine gestrichelte Kante sagt: das ist kein
   Fehler, hier gehoert etwas hin. */
.empty-state {
  text-align: center;
  padding: clamp(2rem, 6vw, 3.5rem) 2rem;
  background: var(--surface);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius);
  color: var(--text-muted);
}

/* Knopf. Das hier ist der schlichte, den es am haeufigsten gibt. Die
   Fassungen .btn-primary, .btn-ghost, .btn-sm stehen unten bei ihren
   .ds-btn--Geschwistern, .btn-danger gleich darunter — dieselbe Regel fuer
   beide Namen, damit nicht jede Ansicht wieder ihre eigene Abschrift
   mitbringt (oberflaeche#15: 22 Ansichten hatten eine, und aus den kleinen
   Abweichungen darin wurden 68 verschiedene Knoepfe). */
.btn, .knopf {
  display: inline-flex; align-items: center; gap: var(--sp-sm);
  font-family: inherit; font-size: var(--fs-md); font-weight: 600;
  padding: 0.6rem 1.05rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.btn:hover, .knopf:hover { border-color: var(--accent); color: var(--accent-strong); }
.btn:disabled, .knopf:disabled { opacity: .5; cursor: not-allowed; }

/* Ein Ziel hat zwei Masse (P1-18). Die 48-px-Messung des Panels hat zehn
   Bloecke unter 900px — .ds-btn und .ds-btn--sm stehen in keinem davon,
   obwohl customers.ejs jede Zeilenaktion so rendert. Der Mangel sitzt am
   geteilten Knopf, nicht in den Ansichten.

   44px ueberall, auch am Telefon: die anerkannte Untergrenze (WCAG 2.5.5 /
   Apple HIG), siehe Block «Tippziele» unten. Hier stand bis Welle 5 «48px am
   Telefon» — eine Zahl, die kein Bauteil des Vertrags einhaelt und an der
   panel-check jede vertragsgemaesse Flaeche als Mangel meldete. */
.ds-btn, .btn, .knopf { min-height: 44px; }
.ds-btn--sm, .btn-sm { min-height: 44px; }

/* ── Buttons ─────────────────────────────────────────────────────────── */
/* Der Uebergang nennt die Eigenschaften einzeln. Vorher stand hier
   `transition: all` — das animierte auch outline-width und outline-offset,
   und damit WUCHS der Fokusring in 150ms heran, statt sofort dazustehen.
   Aufgefallen beim Messen: der Pruefstand las 2px/1px, weil er mitten in
   die Animation hineingriff. Ein Fokushinweis darf nicht erst entstehen.
   Ein Knopf des Design-Systems nennt die Eigenschaften einzeln. */
.ds-btn { display: inline-flex; align-items: center; gap: var(--sp-sm); font-family: inherit; font-size: var(--fs-md); font-weight: 600; padding: 0.6rem 1.05rem; border-radius: var(--r-sm); border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); cursor: pointer; text-decoration: none; transition: background 0.15s, border-color 0.15s, color 0.15s, box-shadow 0.15s; white-space: nowrap; }
.ds-btn:hover { border-color: var(--accent); color: var(--accent-strong); background: var(--surface); }
/* Die Hauptaktion faerbt mit --primary, nicht mit --accent. --accent ist
   laut seinem eigenen Kommentar (Zeile 99) die Farbe fuer „Links, Auswahl,
   Fokusring" — als Flaeche unter weisser Schrift ergibt #4A90C2 nur 3,47:1
   und verfehlt die geforderten 4,5:1. #1D5C77 ergibt 7,37:1.
   Betroffen ist allein die Fassung „klassisch": im Glas ueberschreibt die
   Regel weiter unten Flaeche UND Schrift mit --akzent/--akzent-auf (8,92:1).
   Die Grundpalette ist trotzdem zu beheben — sie ist die Rueckfallebene,
   wenn backdrop-filter fehlt oder Transparenz abgewaehlt ist. */
.ds-btn--primary, .btn-primary { background: var(--primary); color: #fff; border: 1px solid var(--primary); box-shadow: var(--sh-sm); }
.ds-btn--primary:hover, .btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: var(--akzent-auf, #fff); }
.ds-btn--strong { background: var(--ink); color: #fff; border: 1px solid var(--ink); }
.ds-btn--strong:hover { background: #000; border-color: #000; color: #fff; }
.ds-btn--ghost, .btn-ghost { background: var(--surface); color: var(--ink); border: 1px solid var(--line-strong); }
.ds-btn--ghost:hover, .btn-ghost:hover { border-color: var(--accent); color: var(--accent-strong); }
.ds-btn--sm, .btn-sm { padding: 0.4rem 0.8rem; font-size: var(--fs-base); }
.ds-btn:active, .btn:active { transform: translateY(0.5px); }
.ds-btn:disabled { opacity: 0.55; cursor: not-allowed; transform: none; }
.ds-btn--danger { border-color: var(--danger); color: var(--danger); background: var(--danger-tint); }
.ds-btn--danger:hover { background: var(--danger); border-color: var(--danger); color: var(--signal-auf, #fff); }
/* Die leise Gefahr der Listen („Löschen" neben „Bearbeiten"): Schrift in
   der Gefahrenfarbe, Flaeche erst beim Ueberfahren. So stand sie wortgleich
   in fuenf Ansichten; zwei davon mit #fff statt --surface, was nachts einen
   weissen Kasten ergab. */
.btn-danger { background: var(--surface); color: var(--danger); border: 1px solid var(--danger-tint); }
.btn-danger:hover { background: var(--danger-tint); border-color: var(--danger); color: var(--danger); }
/* Gefuellte Gefahr: fuer den EINEN Knopf, der etwas Unwiderrufliches tut.
   --danger-hover ist dunkler, nicht heller: wer zoegert und die Maus darauf
   haelt, soll nicht die Farbe fuer „ja, weiter" sehen. */
.ds-btn--gefahr {
  background: var(--danger); border-color: var(--danger); color: var(--signal-auf, #fff);
}
.ds-btn--gefahr:hover:not(:disabled) {
  background: var(--danger-hover, #8E2A22); border-color: var(--danger-hover, #8E2A22); color: var(--signal-auf, #fff);
}
.ds-btn--gefahr:disabled { opacity: .45; cursor: not-allowed; }

/* ── Die Rueckfrage ──────────────────────────────────────────────────────
   Ersetzt confirm(). Der Kasten traegt .modal-box und erbt damit im Glas
   die schwebende Fassung (siehe „Was schwebt" weiter unten) — ein Ort fuer
   das Aussehen, nicht zwei. */
.ds-frage-hg {
  position: fixed; inset: 0; z-index: var(--z-fenster, 400);
  background: rgba(20,19,17,.45);
  display: flex; align-items: center; justify-content: center; padding: 1rem;
}
.ds-frage {
  width: min(420px, 100%); background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-lg, 16px);
  padding: 1.35rem; box-shadow: var(--shadow-lg); color: var(--ink);
}
.ds-frage-titel {
  font-family: var(--font-head); font-size: var(--fs-lg); font-weight: 800;
  letter-spacing: -0.02em; margin: 0 0 .45rem; color: var(--ink);
}
.ds-frage-text { font-size: var(--fs-md); line-height: 1.6; color: var(--muted); margin: 0 0 .7rem; }
/* Der Preis-Anker: gross, allein, vor dem Text. Vorher stand der Betrag
   mitten in einem confirm()-Satz — man klickte auf „OK“, ohne ihn gelesen
   zu haben (P1-8). */
.ds-frage-preis {
  display: flex; align-items: baseline; gap: .4rem;
  margin: .2rem 0 .8rem; padding: .7rem .9rem;
  background: var(--tint); border: 1px solid var(--tint-line);
  border-radius: var(--r-md);
}
.ds-frage-betrag {
  font-family: var(--font-mono); font-size: var(--fs-3xl); font-weight: 600;
  letter-spacing: -.02em; color: var(--ink); line-height: 1;
}
.ds-frage-waehrung { font-size: var(--fs-md); font-weight: 600; color: var(--muted); }
[data-glas] .ds-frage-preis { background: var(--glas-fuellung); border-color: var(--glas-kante); }
[data-glas] .ds-frage-betrag { color: var(--text); }
.ds-frage-label { display: block; font-size: var(--fs-sm); font-weight: 600; margin: .8rem 0 .35rem; }
.ds-frage-feld { width: 100%; }
.ds-frage-zeile { display: flex; gap: .5rem; justify-content: flex-end; margin-top: 1.1rem; }
/* Auf dem Telefon stehen die beiden Knoepfe untereinander und in voller
   Breite — nebeneinander sind sie dort je 150px schmal. */
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .ds-frage-zeile { flex-direction: column-reverse; }
  .ds-frage-zeile .ds-btn { width: 100%; justify-content: center; min-height: 48px; }
}

/* ── Reusable components (adopt across views for consistency) ─────────────── */
/* Toggle switch */
.ds-switch { position: relative; width: 42px; height: 24px; flex-shrink: 0; display: inline-block; }
.ds-switch input { opacity: 0; width: 0; height: 0; }
.ds-switch span { position: absolute; inset: 0; background: var(--line-strong); border-radius: 999px; cursor: pointer; transition: background var(--t-fast); }
.ds-switch span::before { content: ''; position: absolute; width: 18px; height: 18px; left: 3px; top: 3px; background: #fff; border-radius: 50%; transition: transform var(--t-fast); box-shadow: 0 1px 3px rgba(0,0,0,0.2); }
.ds-switch input:checked + span { background: var(--accent); }
.ds-switch input:checked + span::before { transform: translateX(18px); }

/* ── Empty state ─────────────────────────────────────────────────────── */
.ds-empty { text-align: center; padding: calc(3.5rem * var(--dichte)) calc(2rem * var(--dichte)); background: var(--surface); border: 1px dashed var(--border-strong); border-radius: var(--radius-lg); }
.ds-empty-icon { width: 64px; height: 64px; margin: 0 auto 1rem; border-radius: var(--r-2xl); background: var(--grad-soft); display: flex; align-items: center; justify-content: center; font-size: 1.8rem; }
.ds-empty h3 { margin: 0 0 0.4rem; font-size: var(--fs-lg); font-weight: 750; }
.ds-empty p { margin: 0 auto 1.25rem; color: var(--text-muted); font-size: var(--fs-md); max-width: 42ch; line-height: 1.55; }

/* ── Ladefehler (dsApi.ladefehler) ───────────────────────────────────────
   Nicht der leere Zustand: "Keine Anfragen" nach einer abgelaufenen Sitzung
   liess glauben, es sei nichts gekommen. Links der Streifen der Gefahr,
   der Satz sagt, was los ist, der Knopf bietet den naechsten Versuch. */
.ds-ladefehler {
  display: flex; align-items: center; gap: var(--sp-md); flex-wrap: wrap; text-align: left;
  margin: var(--sp-sm) 0; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--danger-bg); border: 1px solid var(--danger); border-left-width: 4px;
  grid-column: 1 / -1;   /* in einem Kachelraster ueber alle Spalten */
}
.ds-ladefehler-t { flex: 1; min-width: 200px; margin: 0; font-size: var(--fs-md); line-height: 1.5; color: var(--text); }

/* ── Meldung (ds-toast.js) ───────────────────────────────────────────────
   Die Grundregel fehlte: ds-toast.js verliess sich auf das .toast-CSS der
   jeweiligen Ansicht. Ansichten ohne eigenes bekamen eine Meldung ohne
   Lage und ohne Ausblenden. Eigene Regeln der Ansichten stehen spaeter und
   gewinnen weiter; in Glas gelten die Regeln weiter unten. */
.toast {
  position: fixed; right: 1.5rem; bottom: 1.5rem; z-index: var(--z-meldung);
  max-width: min(420px, calc(100vw - 2rem)); padding: 0.75rem 1.1rem;
  border-radius: var(--r-md); font-size: var(--fs-md); font-weight: 600; line-height: 1.45;
  background: var(--ink); color: #fff; box-shadow: var(--shadow-float);
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity .25s, transform .25s;
}
.toast.show { opacity: 1; transform: none; }
.toast.success { background: var(--ok); color: var(--signal-auf, #fff); }
.toast.error { background: var(--danger); color: var(--signal-auf, #fff); }
.toast.warn { background: var(--warn); color: var(--signal-auf, #fff); }
.toast.info { background: var(--primary); color: #fff; }

/* ── Tables ──────────────────────────────────────────────────────────── */
.ds-table { width: 100%; border-collapse: collapse; font-size: var(--fs-md); }
.ds-table th { text-align: left; font-weight: 700; font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-light); padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--border); }
.ds-table td { padding: 0.8rem 0.9rem; border-bottom: 1px solid var(--border); }
.ds-table tr:last-child td { border-bottom: none; }
.ds-table tr:hover td { background: var(--surface-2); }

/* ── Inputs ──────────────────────────────────────────────────────────── */
.ds-input, .ds-select, .ds-textarea { width: 100%; font-family: inherit; font-size: var(--fs-md); padding: 0.65rem 0.85rem; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--surface); color: var(--ink); transition: border-color 0.15s, box-shadow 0.15s; }
.ds-input:focus, .ds-select:focus, .ds-textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-tint); }
.ds-input::placeholder, .ds-textarea::placeholder { color: var(--text-light); }
.ds-input:hover, .ds-select:hover, .ds-textarea:hover { border-color: var(--border-strong); }
/* Labels: JetBrains Mono, uppercase, +6% tracking (HANDOFF §3 / design spec) */
.ds-label { display: block; font-family: var(--font-mono); font-size: var(--fs-xs); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 7px; color: var(--ink-3); }

/* ── Underline tabs (design spec: border-bottom 2px accent on active, icon + label) ── */
/* ── Reiter ───────────────────────────────────────────────────────────
   Eine Fassung, nicht zwei. Hier stand bis P1-5 eine aeltere mit
   `padding: 13px 12px`, `.active` und einem Unterstrich in --accent;
   partials/tabs.ejs brachte gleichzeitig eine andere mit `height: 38px`,
   `.on` und --ink mit. Welche gewann, hing davon ab, wo eine Ansicht die
   Teilansicht einband — die Reiter waren je Ansicht anders hoch.

   Die Fassung von tabs.ejs ist die richtige (§2/06) und steht jetzt hier.
   Im Markup heisst der aktive Reiter `on`; `.active` kam nirgends vor. */
.ds-tabs {
  display: flex; gap: var(--sp-3xs);
  border-bottom: 1px solid var(--line);
  margin: 0 0 20px;
  overflow-x: auto; scrollbar-width: none;
}
.ds-tabs::-webkit-scrollbar { display: none; }
.ds-tab {
  flex: none; display: inline-flex; align-items: center; gap: var(--sp-xs);
  height: 38px; padding: 0 13px; margin-bottom: -1px;
  font-family: var(--font-ui); font-size: var(--fs-base); font-weight: 500;
  color: var(--faint); cursor: pointer;
  text-decoration: none; border-bottom: 2px solid transparent; white-space: nowrap;
  background: none; border-top: 0; border-left: 0; border-right: 0;
}
.ds-tab:hover { color: var(--ink); text-decoration: none; }
.ds-tab.on { color: var(--ink); font-weight: 650; border-bottom-color: var(--ink); }
.ds-tab.gesperrt { opacity: .55; }
.ds-tab-lock { display: inline-flex; }
.ds-tab-lock svg { width: 11px; height: 11px; }
.ds-tabs .ds-tab .ds-ico { width: 16px; height: 16px; }
/* Unter 640px werden aus Reitern Pillen (§3.4). */
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .ds-tabs { border-bottom: none; gap: var(--sp-xs); padding-bottom: 2px; }
  .ds-tab { height: 34px; border: 1px solid var(--line-field); border-radius: 999px; border-bottom-color: var(--line-field); }
  .ds-tab.on { background: var(--ink); color: #fff; border-color: var(--ink); }
}
/* Im Glas traegt der Reiterstreifen die Glaskante, nicht die Papierlinie. */
[data-glas] .ds-tabs { border-bottom-color: var(--glas-kante); }
[data-glas] .ds-tab { color: var(--label); }
[data-glas] .ds-tab:hover,
[data-glas] .ds-tab.on { color: var(--text); border-bottom-color: var(--text); }
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  [data-glas] .ds-tab { border-color: var(--glas-kante); }
  [data-glas] .ds-tab.on { background: var(--akzent, var(--accent)); color: var(--akzent-auf, #fff); border-color: transparent; }
}

/* ── Progress (dashboard, customers, config) ── */
.ds-progress { height: 6px; border-radius: 999px; background: var(--sunken); overflow: hidden; }
.ds-progress > span { display: block; height: 100%; background: var(--accent); border-radius: 999px; }

/* ── Fixed action bar (HANDOFF §3: Save/Preview/Publish always visible on editable pages) ── */
.ds-actionbar { position: fixed; left: var(--sidebar-w); right: 0; bottom: 0; z-index: 60;
  display: flex; align-items: center; gap: var(--sp-md); padding: 13px 28px;
  background: var(--surface); border-top: 1px solid var(--line); box-shadow: 0 -6px 24px -18px rgba(20,19,17,.16); }
.ds-actionbar .ds-actionbar-dirty { display: inline-flex; align-items: center; gap: var(--sp-sm); font-size: var(--fs-sm); color: var(--ink-3); }
.ds-actionbar .ds-actionbar-dirty::before { content: ''; width: 8px; height: 8px; border-radius: 50%; background: var(--warn); }
.ds-actionbar .ds-actionbar-info { display: flex; flex-direction: column; gap: var(--sp-3xs); min-width: 0; }
.ds-actionbar .ds-actionbar-title { font-weight: 700; font-size: var(--fs-md); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ds-actionbar .ds-actionbar-sub { font-size: var(--fs-sm); color: var(--ink-3); }
.ds-actionbar .ds-actionbar-actions { margin-left: auto; display: flex; align-items: center; gap: var(--sp-sm); flex-shrink: 0; }
/* Pages that mount an action bar add bottom padding so content isn't hidden behind it */
.ds-has-actionbar .ds-content, body.ds-has-actionbar .main { padding-bottom: 5.5rem; }

/* ── Lucide-style inline icons (stroke 1.75, sized to text) ── */
.ds-ico { width: 1.05em; height: 1.05em; flex-shrink: 0; vertical-align: -0.16em; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; fill: none; }
/* Regel 7 aus 9.2: EINE Strichstaerke. Gezaehlt waren sieben (1.5 · 1.75 ·
   1.8 · 2 · 2.2 · 2.4 · 2.5) ueber 152 handgezeichnete <svg> in den
   Ansichten. Das Design-System kennt 1.75, und .ds-ico setzt sie auch —
   nur gilt sie nicht fuer ein SVG, das jemand direkt ins Markup schreibt.

   Eine CSS-Regel gewinnt gegen das gleichnamige Attribut im SVG
   (Praesentationsattribute haben die niedrigste Prioritaet). Damit sehen
   die 152 aus wie die 589 aus dsIcon — ohne sie einzeln zu ersetzen.
   Sie einzeln zu ersetzen bleibt trotzdem richtig; dafuer muss aber je
   Stelle entschieden werden, WELCHES Icon gemeint ist. */
.ds-app svg:not(.ds-ico):not([data-eigen]) {
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ── Utilities ───────────────────────────────────────────────────────── */
.ds-lead { font-size: var(--fs-lg); color: var(--text-muted); line-height: 1.6; margin: 0 0 1.25rem; }

@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  body.ds-app { flex-direction: column; }
  body.ds-app .main { margin-left: 0; }
  .ds-content { padding: 1.25rem 1rem 2.5rem; }
  .ds-hero { padding: 1.5rem 1.4rem; border-radius: var(--radius); }
  .ds-hero-row { flex-wrap: wrap; }
  .ds-hero-actions { margin-left: 0; width: 100%; }
  .ds-hero h1 { font-size: var(--fs-2xl); }
  /* minmax(0,1fr) statt 1fr.

     1fr ist die Kurzform von minmax(auto, 1fr) — und "auto" heisst als
     Mindestmass: so breit wie der Inhalt. Eine Kachel, deren Inneres nicht
     schrumpfen kann, zieht die Spalte darum ueber den Bildschirm hinaus.
     Gemessen auf /media: Raster 358px breit, Spalte 445px, Seite liess
     sich seitlich wegschieben. Mit 0 als Mindestmass gilt die Breite des
     Rasters. */
  .ds-grid { grid-template-columns: minmax(0, 1fr); }
  .ds-grid > * { min-width: 0; }
}


/* ── Baustein 07 · Erklaerung, zusammenklappbar (AUFTRAG §2/07, §4.2) ──────
   Zu = eine Zeile 40px. Ersetzt die ganzflaechigen Erklaerkaesten; die Texte
   bleiben woertlich erhalten, sie blockieren nur nicht mehr. */
.ds-erkl { border: 1px solid var(--tint-line); background: var(--tint); border-radius: var(--r-lg);
           margin: 0 0 16px; overflow: hidden; }
.ds-erkl-kopf { display: flex; align-items: center; gap: var(--sp-sm); width: 100%; min-height: 40px;
                padding: 0 13px; background: none; border: none; cursor: pointer;
                font-family: var(--font-ui); font-size: var(--fs-base); text-align: left; }
.ds-erkl-i { flex: none; width: 18px; height: 18px; border-radius: 999px; border: 1.5px solid var(--primary);
             color: var(--primary); font-size: var(--fs-xs); font-weight: 700; display: flex;
             align-items: center; justify-content: center; }
.ds-erkl-t { flex: 1; font-size: var(--fs-base); font-weight: 600; color: var(--ink); }
.ds-erkl-a { flex: none; font-size: var(--fs-base); color: var(--primary); }
/* --primary ist in jeder Fassung dunkles Petrol — als Flaeche richtig,
   als Schrift auf der dunklen Hinweisflaeche der Nacht ein Loch (2:1). */
[data-glas="dunkel"] .ds-erkl-i { border-color: var(--akzent); color: var(--akzent); }
[data-glas="dunkel"] .ds-erkl-a { color: var(--akzent); }
.ds-erkl-inhalt { padding: 0 13px 13px; }
/* Der Inhalt traegt oft display:grid oder :flex — das sticht das hidden-Attribut.
   Ohne diese Regel klappt der Baustein sichtbar nicht zu. */
.ds-erkl-inhalt[hidden] { display: none !important; }
.ds-erkl.auf .ds-erkl-kopf { border-bottom: 1px solid var(--tint-line); }


/* ══ Glas ═══════════════════════════════════════════════════════════════
   Ein Materialwechsel, kein Funktionsumbau. Die Token stehen hier, die
   Modusfarben liefert partials/sidebar.ejs je nach appearance.colorScheme.

   Drei Lichter je Scheibe, nie eins: Kante oben, schwaechere Kante unten,
   und ein Bloom aus der oberen linken Ecke, dessen Ursprung AUSSERHALB der
   Scheibe liegt. Ein schmaler Verlauf mit harten Stopps liest sich als
   Strich quer durch den Inhalt, nicht als Licht — deshalb radial, breit,
   aus einer Ecke. */
[data-glas] {
  --glas-blur: 24px;          /* Inhalt */
  --glas-blur-float: 56px;    /* Schwebendes: Leiste, Blatt, Fenster, Menue */
  --glas-r: 22px;
  --glas-r-karte: 20px;
  --glas-r-feld: 14px;
  --glas-aura-von: 197deg;
  --glas-aura-bis: 222deg;    /* nie verlassen */
}

/* ══ §7 Abnahme: jeder Screen auf 390px bedienbar, ohne waagerechtes Scrollen ══
   Vier Ursachen, gemessen statt geraten:

   1. .main behielt den Leistenabstand. Die bestehende Regel greift nur bei
      `body.ds-app`; Ansichten mit eigenem Geruest (Dashboard) fielen durch.
   2. Das Hilfe-Panel steht per transform ausserhalb des Bildes und zaehlt in
      Chromium trotzdem zur Scrollbreite, solange es sichtbar ist.
   3. Tabellen (Benutzer) sind breiter als 390px.
   4. Die zweite Spalte der Systemeinstellungen blieb 220px breit. */
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  .main { margin-left: 0 !important; }
  .sidebar { transform: translateX(-100%); }
  /* .ds-open, nicht .open: das Skript in sidebar.ejs setzt diesen Namen.
     `.sidebar.open` stand hier und wurde nie von irgendetwas gesetzt —
     eine Regel, die aussieht, als kuemmere sie sich um den Fall. */
  .sidebar.ds-open { transform: none; }
  /* Die Notloesung ist weg (P1-7). `display: block` auf <table> loest die
     Spaltenzuordnung: die Kopfzeile steht dann nicht mehr ueber ihren
     Werten, und man scrollt eine Tabelle waagerecht, deren Kopf man nicht
     mehr sieht. Tabellen tragen jetzt `.ds-tab-karten` und werden zu
     Karten — eine Zeile, eine Karte, jeder Wert mit seinem Spaltennamen.

     Eine Tabelle OHNE diese Klasse bleibt eine Tabelle. Das ist Absicht:
     wo wirklich Zahlen verglichen werden, ist eine Tabelle richtig, und
     dort darf sie in ihrem eigenen Kasten scrollen — aber das entscheidet
     dann jemand bewusst, statt dass es fuer alle gilt. */

  /* ── Aus Zeilen werden Karten ──────────────────────────────────────── */
  .ds-tab-karten,
  .ds-tab-karten tbody,
  .ds-tab-karten tr,
  .ds-tab-karten td { display: block; width: 100%; }
  /* Der Kopf faellt weg — seine Namen stehen jetzt an den Zellen. */
  .ds-tab-karten thead { display: none; }
  .ds-tab-karten tr {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-card, 12px);
    margin-bottom: 10px;
    padding: 12px 14px;
    position: relative;
  }
  .ds-tab-karten td {
    border: 0;
    padding: 3px 0;
    display: flex; align-items: baseline; gap: var(--sp-sm);
  }
  /* Der Spaltenname vor dem Wert. Ohne ihn steht „admin" allein da und
     sagt nichts. */
  .ds-tab-karten td[data-spalte]::before {
    content: attr(data-spalte);
    flex: 0 0 8.5rem;
    color: var(--muted);
    font-size: var(--fs-sm);
    font-weight: 600;
  }
  /* Eine Zelle, die nur Knoepfe traegt, braucht keinen Namen — und die
     Knoepfe stehen rechts, wie in der Tabelle. */
  .ds-tab-karten td:not([data-spalte]) { justify-content: flex-end; padding-top: 8px; }
  .ds-tab-karten td[colspan] { justify-content: flex-start; }
  [data-glas] .ds-tab-karten tr { background: var(--glas-fuellung); border-color: var(--glas-kante); }
  /* Systemeinstellungen: die zweispaltige Anordnung wird gestapelt. Eine
     waagerechte Leiste waere hier falsch — die Navigation hat
     Abschnittsueberschriften ("App", "KI", "Rechtliches"), die in einer Zeile
     ihren Sinn verlieren. Als Flex-Kind ohne min-width:0 wuchs sie ausserdem
     auf 1057px Inhaltsbreite, statt zu scrollen. */
  .page-layout { flex-direction: column; }
  /* Kein max-height: ein Scrollkasten im Scrollkasten ist auf dem Handy
     schwer zu treffen, und die unteren Eintraege lagen dadurch rechnerisch
     ueber dem Inhaltsbereich. Die Navigation waechst mit und die Seite
     scrollt als Ganzes. */
  /* Nur die SYSTEM-Einstellungen. Die Projekt-Einstellungen tragen
     dieselbe Klasse, liegen aber in .settings-wrapper und haben eine
     flache Liste ohne Ueberschriften — dort ist die waagrechte
     Reiterzeile richtig, und overflow:visible!important hat sie
     zerschossen: die Reiter standen bis 2355px nach rechts. */
  .page-layout > .settings-nav {
                  width: 100% !important; min-width: 0 !important; height: auto !important;
                  max-height: none !important; overflow: visible !important;
                  position: static !important;
                  border-right: none !important; border-bottom: 1px solid var(--line); }
  .settings-body, .settings-main, .settings-content { width: 100%; min-width: 0; max-width: none !important; }
  /* Ausser skalierten Vorschauen: sie rendern in Geraetebreite (390, 1280 px)
     und werden per transform verkleinert, in einem Rahmen mit
     overflow:hidden. Mit max-width:100% renderte die Handy-Vorschau nur
     314 statt 390 CSS-px breit — eine andere Seite als auf dem Geraet.
     :where() haelt die Spezifitaet bei der von «iframe». */
  iframe:where(:not([style*="scale("]):not(.dvk-bild iframe):not(.dvk-gross-flaeche iframe):not(.pc-thumb-frame):not(.theme-preview-iframe-wrap iframe):not(.live-preview-box iframe)) { max-width: 100%; }

  /* Die drei letzten Ueberlaeufe, gemessen statt geraten. Sie stehen hier mit
     Vorrang, weil die Ursachen in seiteneigenen <style>-Bloecken liegen, die
     NACH dieser Datei laden — ohne !important gewaenne dort die alte Regel. */

  /* 1. .main ist Flex-Kind ohne min-width:0 und wuchs deshalb auf die
        Mindestbreite seines Inhalts (398px) statt auf 390 zu schrumpfen. */
  .main { min-width: 0; }

  /* 2. Beide Kopfleisten sind starre Flex-Zeilen. Auf 390px passt Titel und
        Aktion nicht nebeneinander — sie duerfen umbrechen. */
  .topbar, .ds-topbar { flex-wrap: wrap; height: auto !important;
                        padding-left: 1rem !important; padding-right: 1rem !important;
                        row-gap: var(--sp-sm); padding-top: 0.6rem !important; padding-bottom: 0.6rem !important; }
  .topbar > *, .ds-topbar > * { min-width: 0; }
  .ds-tabs { width: 100%; overflow-x: auto; }

  /* 3. Raster mit fester Mindestspalte (minmax(min(300px, 100%), 1fr)) passen auf 390px
        abzueglich Rand nicht mehr. §3.4 sieht auf dem Handy ohnehin eine
        Spalte vor. */
  /* Ohne !important: die Regel steht in derselben 900er-Abfrage wie die
     gleichlautende weiter oben und hat dieselbe Spezifitaet — sie gewinnt
     ohnehin, weil sie spaeter steht. Das !important fing frueher den
     Ueberlauf bei 320px auf; den gibt es nicht mehr, seit die Rasterspur
     ihre Mindestbreite auf die verfuegbare begrenzt (P2-10). Eine Regel,
     die eine andere Regel repariert, ist ein Zeichen dafuer, dass die
     andere falsch war. */
  .stats-grid, .projects-grid, .ds-grid, .ds-cards { grid-template-columns: minmax(0, 1fr); }
  /* Und die Kacheln selbst duerfen schmaler werden als ihr Inhalt —
     sonst schiebt das Innere die Kachel wieder auf. */
  .stats-grid > *, .projects-grid > *, .ds-grid > *, .ds-cards > * { min-width: 0; }

  /* Die Listenkachel selbst: Symbol, Text, Abzeichen, Pfeil in einer Reihe.
     Das Abzeichenfeld hatte flex-shrink:0 — auf 320px passte die Reihe
     nicht mehr und der Pfeil stand 11px neben dem Bildschirm. Statt ihn zu
     verstecken darf das Abzeichenfeld nachgeben und umbrechen; der Pfeil
     bleibt, wo er hingehoert. */
  .ds-tile { flex-wrap: wrap; }
  .ds-tile-meta { flex-shrink: 1; min-width: 0; flex-wrap: wrap; }
  .page-content, .ds-content { padding-left: 1rem !important; padding-right: 1rem !important; }
}
/* Geschlossene Schubladen duerfen die Scrollbreite nicht vergroessern. */
.ds-help-panel:not(.open) { visibility: hidden; }


/* ══ §7 Abnahme: Lesbarkeit ═══════════════════════════════════════════════
   Vorgabe aus der Abnahmeliste: kein Text unter der Grundschrift. Gemessen
   ueber alle 16 Screens waren es 725 Textstellen — die sich auf sehr wenige
   geteilte Klassen verteilen. Deshalb hier zentral statt in zehn Ansichten
   einzeln. Seit Welle 1 heisst «Grundschrift» var(--fs-base) der Skala:
   13px am Schreibtisch, 11px am Telefon — die Regeln hier skalieren mit.

   Das !important ist noetig, weil die Ursprungswerte in seiteneigenen
   <style>-Bloecken stehen, die NACH dieser Datei laden. */

/* ── Textboden: Grundschrift ────────────────────────────────────────────────
   Betrifft Mikro-Beschriftungen, die zwischen 9.5px und 12.8px lagen —
   Kennzeichen, Zaehler, Unterzeilen, Auswahlfelder. Lesbarkeit haengt nicht
   an der Bildschirmbreite, darum ohne Media Query. */
.baustein-name, .baustein-typ, .tag, .industry-tag, .category-count,
.logo-sub, .nav-admin, .sf-name, .sf-role, .ds-sw-name, .ds-sw-sub,
.theme-description, .mod-chip, .ds-status, .pg-file, .pg-blocks,
.project-domain, .pill, .mono, .feld label, .seg button, .lang-btn,
.lang-btn span, .del-modal label, label.fl,
select, .ds-select, option, .theme-select {
  font-size: var(--fs-base) !important;
  line-height: 1.35;
}
/* Die Beschriftung der Handy-Leiste steht bei der Leiste (ds-leiste.css,
   Quelle views/quellen/leiste.css): --fs-xs der Skala. */

/* ══ Tippziele am Telefon: Komponentenregeln, keine Nachzuegler ═══════════
   Vorher stand hier ein Reparaturblock mit 48 !important: Listen mit 22
   Klassen, die beim Messen zu klein waren, und nach jeder Messung eine
   Liste mehr («Nachzuegler aus der zweiten Messung»). Eine Regel, die
   aufzaehlt, was sie trifft, ist beim naechsten Bildschirm wieder falsch.

   Jetzt gilt die Mindestgroesse fuer die KOMPONENTEN des Systems
   (.ds-btn, .fi, .ds-tab, Leisten-Links) und fuer die ELEMENTE, aus denen
   alles andere besteht (button, input, select, textarea, label um einen
   Haken). Wer eine eigene Knopfklasse baut, baut sie auf <button> oder
   .ds-btn — und hat das Ziel damit von selbst. 44px, nicht 48: die
   anerkannte Untergrenze (WCAG 2.5.5 AAA / Apple HIG), und sie bleibt
   auch dann, wenn die Schrift auf 11px geht.

   Ohne !important: min-height sticht height immer, egal in welcher
   Reihenfolge; nur ein eigenes min-height in einer Ansicht waere staerker
   — und davon gibt es keines (gezaehlt ueber views/). */
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  /* Komponenten — dazu .btn/.knopf, die klassische Knopffamilie dieser
     Datei (siehe oben), bis Welle 2–4 sie durch .ds-btn ersetzt. Gemessen
     ohne sie: /gefunden acht Verweise a.knopf mit 40px, /config ein
     a.btn 41px breit. */
  .ds-btn, .btn, .knopf, .fi, .ds-tab, .nav-item, .ds-bn-item { min-height: 44px; }
  .ds-btn, .btn, .knopf, .ds-tab, .ds-bn-item { min-width: 44px; }
  /* Brotkrumen in der Kopfzeile sind Leisten-Links: inline, darum braucht
     die Mindesthoehe auch die Darstellung als Kasten. */
  /* Dazu die Rueckwege der Kopfzeilen (.topbar-back, .ed-header-back, .tb-back
     in ab/shop/orders/blog/newsletter — gemessen 27px auf /ab): dieselbe
     Rolle, bis Welle 2 sie durch ds-kopf.ejs ersetzt. (.ds-krume a der neuen
     Kopfzeile steht in ds-komponenten.css.) */
  .breadcrumb a, .topbar-breadcrumb a, .topbar-back, .ed-header-back, .tb-back {
    display: inline-flex; align-items: center; justify-content: center; min-height: 44px; min-width: 44px;
  }

  /* Elemente. input[type="text"] trifft <input class="fi" id="h_host">
     NICHT — ohne Attribut gibt es nichts zu vergleichen (128 solcher
     Felder in den Ansichten). Darum ueber Ausschluss: alles, was kein
     Haken, Radio, Schieber, Datei oder unsichtbar ist. */
  /* `html` davor, weil die Grundregel der Felder weiter unten (40px am
     Schreibtisch, gleiche Selektoren) spaeter steht und sonst gewaenne —
     gemessen: 40 statt 44. Ein Textbereich bleibt aussen vor, sein Boden
     liegt ohnehin bei 72px. */
  html input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="range"]):not([type="file"]) { min-height: 44px; }
  /* <select> braucht das eine !important dieses Blocks: als Elementregel
     verliert es gegen jede Ansicht, die ihrer Auswahlliste ein eigenes
     min-height gibt — gemessen auf 390px nach dem Umbau: /gefunden und
     /sobi/schnell «.wahl select» 40px, /formular «.fb-fg select» 42px,
     /ab «.vfg select» 38px. Vier Ansichten flicken oder eine Zeile hier;
     Welle 2–4 nimmt den Ansichten die eigenen Masse ohnehin weg. */
  html select { min-height: 44px !important; }
  /* Farbfelder waren 32×32. Ein Farbwaehler ist ein Ziel wie jedes andere. */
  input[type="color"] { min-width: 44px; }
  /* Schaltflaechen in Hoehe UND Breite: die Zeilenmenues «⋯» waren 32px
     breit bei voller Hoehe — ein 32px-Streifen ist mit dem Daumen kein
     Ziel. min-width hebt nur die zu schmalen an. Ausgenommen die dichten
     Streifen, die bewusst klein sind: Tour-Punkte, Format-Leiste,
     Farbtupfer, Mini-Knoepfe. */
  button:not(.tour-punkt):not(.fmt-btn):not(.tc-btn):not(.sw-tupf):not([class*="mini"]),
  [role="button"]:not([class*="mini"]) { min-height: 44px; min-width: 44px; }
  /* <summary> ist das Ziel eines Aufklapp-Kastens — kein <button>, aber
     dieselbe Rolle. Gemessen auf /leads: «Weitergeben» (.ds-erkl-kopf,
     min-height 40px aus Baustein 07) war 40px hoch, auch im Assistenten
     «Anfragen». Die Klappkaesten tragen in Welle 3–5 die halbe App. */
  summary:not([class*="mini"]) { min-height: 44px; }
  /* Ein Haken ist klein; sein Ziel ist die Beschriftung drumherum. */
  label:has(> input[type="checkbox"]), label:has(> input[type="radio"]) {
    display: inline-flex; align-items: center; gap: 0.5rem; min-height: 44px;
  }
  /* Radios kamen als 13×13 aus der Browservorgabe. (Haken sind Schalter,
     42×24 bzw. 52×32 am Telefon — Block «Schalter» weiter unten; hier
     stand bis Welle 1 eine zweite, widersprechende Groesse 22×22 fuer
     dieselben Haken, die nie gewann.) */
  input[type="radio"] {
    width: 22px; height: 22px; min-width: 22px; min-height: 22px;
    flex: none; accent-color: var(--accent);
  }
  /* Damit die groesseren Ziele nicht kleben. */
  .ds-tabs, .ds-bottomnav { gap: var(--sp-3xs); }
  /* Breite Knoepfe duerfen den Rahmen nicht sprengen. */
  .ds-btn--block { max-width: 100%; }


  /* ── Die Seite rollt, nicht ein Fenster darin ────────────────────────
     Am Rechner ist das Anwendungsgeruest richtig: Kopfzeile fest, Liste
     rollt darunter. Auf 390px blieben davon 404px von 844px zum Rollen —
     der Rest war feste Kopf- und Fusszone. Hier rollt die ganze Seite,
     die Kopfzone wandert beim Rollen mit weg.

     Ausgenommen: Editor und Easy-Modus (.ds-buehne). Dort muss die
     Website stehen bleiben, waehrend man sie bearbeitet.

     Ohne !important, mit Gewicht: `html body:not(…)` ist (0,1,2) und
     sticht das `body { height: 100vh; overflow: hidden }` (0,0,1) der
     Ansichten, `html body:not(…) .main` (0,1,3) deren `.main` (0,1,0)
     und das `body.ds-app .main` (0,1,2) oben in dieser Datei. */
  html body:not(.ds-buehne) {
    height: auto; min-height: 100vh; overflow: visible; display: block;
  }
  html body:not(.ds-buehne) .main {
    height: auto; overflow: visible; margin-left: 0; min-width: 0;
  }
  html body:not(.ds-buehne) .hub-below,
  html body:not(.ds-buehne) .hub-panels,
  html body:not(.ds-buehne) .hub-panel,
  html body:not(.ds-buehne) .panel-body,
  html body:not(.ds-buehne) .ds-content,
  html body:not(.ds-buehne) .page-content,
  html body:not(.ds-buehne) .settings-body {
    overflow: visible; height: auto; max-height: none;
  }
  html body:not(.ds-buehne) .hub-panel.active { display: block; }
}

/* Textboden, zweite Runde: dieselben 13px fuer die restlichen
   Mikro-Beschriftungen. Zwei Fundstellen bleiben bewusst aus:

   - SVG-<text> in den Diagrammen (Achsenbeschriftung). Die skaliert mit dem
     viewBox, ein fester px-Wert wuerde sie aus dem Bild schieben.
   - Fliesstext-Links im Absatz. Die erben die Groesse ihres Absatzes; sie
     sind kein Bedienelement im Sinn der 48px-Regel. */
.fl-label, .proj-meta, .kpi-l, .hf-lbl, .snav-label, .spalte-titel,
.kat-zahl, .stat-chip-label, .cust-meta, .hb-lang-btn, .fbtn, .hbtn,
.fl-chip, .hub-tab, th, code, .ds-btn, .btn {
  font-size: var(--fs-base) !important;
}
/* Behaelter, deren Kinder (p, strong, span) die zu kleine Groesse erben. */
.ds-card-sub, .ds-hint, .ds-note, .hf, .cust-card, .proj-card, .kpi,
.stat-chip, .fl-fuss, .hub-body { font-size: var(--fs-base); }


/* Dritte und letzte Runde §7. Die Reste sind Hinweiszeilen, Kennzeichen und
   Feldbeschriftungen in einzelnen Ansichten — teils ohne eigene Klasse,
   deshalb ueber ihren Behaelter angesprochen. */
.cust-fee-l, .sec-h, .login-hint, .hnote, .card-subtitle, .fhint,
.toggle-desc, .fl-hint, .launch-badge, .hb-meta, .topbar-badge,
.theme-badge, .filter-btn,
.color-palette span, .template-info p, .plan-feature span, .fhint a,
.form-group label, .card-body p, .fp-bottom span, .fg span, .fg div,
.fl-auto span strong, .am-switch__tx small, .hseg button {
  font-size: var(--fs-base) !important;
}
/* Aufzaehlungen in Hinweiskaesten erben sonst 12px vom Kasten. */
.ds-erkl-inhalt ol li, .ds-erkl-inhalt ul li, .hnote ol li { font-size: var(--fs-base); }

/* Letzte Runde §7. */
.hinweis, .ds-section-title, .adminbadge, .snav-badge, .fl-steplabel,
.fl-cancel, .fl-sec-lbl, .fl-autobadge, .filter-tag, .breadcrumb strong,
.danger-action-info p, .fl-sec2 span, .fl-toggle button, .modal-body div,
.proj-filter button, .baustein-lupe, .btn-delete-project {
  font-size: var(--fs-base) !important;
}

/* Zwei erbende Restfaelle: eine Verweiszeile im Seiten-Panel und der
   Einleitungstext der Theme-Galerie holten 11.5 bzw. 12.8px vom Elternteil. */
.hub-panel a, .theme-empty p, .theme-intro p, .gallery-intro p,
.add-theme-content p { font-size: var(--fs-base); }

/* Drei 900er-Bloecke, die hier verstreut standen, in einem. Die Tippziele
   fuer Knoepfe (.hseg/.seg button, .proj-filter button, .btn-preview …)
   kommen seit Welle 1 aus der Elementregel «button» oben — ein Knopf, der
   kein <button> ist, traegt .ds-btn. */
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  /* .ds-status traegt auf einem Screen ein onclick und ist damit Tippziel;
     .baustein-lupe und .btn-delete-project loesen eine EIGENE Aktion in
     einer klickbaren Zeile aus — dafuer zaehlt ihre eigene Flaeche. */
  .ds-status[onclick], .baustein-lupe, .btn-delete-project { min-height: 44px; min-width: 44px; }
  /* Nebenwirkung der Tippziele: die Kopfleisten brechen auf zwei bis
     drei Zeilen um und waren als sticky 124-152px hoch — auf einem Handy
     dauerhaft ein Sechstel des Bildes, nur fuer Titel und zwei Knoepfe.
     Sie scrollen jetzt mit. Die Navigation uebernimmt die untere Leiste,
     die dafuer gebaut ist. */
  .topbar, .ds-topbar { position: static !important; }
}


/* ══ §4.1 · Speicherleiste ═══════════════════════════════════════════════
   Genau EIN Speichermuster. Die Leiste erscheint erst, wenn wirklich etwas
   geaendert wurde — eine dauerhaft sichtbare Leiste waere nur ein weiterer
   Balken, der Platz kostet und nichts sagt. Die alten Knoepfe bleiben im
   Markup (Ansichten sprechen sie ueber ihre ID an), werden aber ausgeblendet. */
.ds-savebar { display: none; }
.ds-savebar.offen { display: flex; }
.ds-sb-versteckt { display: none !important; }
.ds-savebar .ds-actionbar-dirty { font-weight: 600; }

/* §7 — Nachzuegler aus dem umgebauten KI-Screen (§5.6) und dem
   Gesundheitsstreifen (§5.2). */
.ex, .focus-hint, .gh-pille, .help-close { font-size: var(--fs-base) !important; }
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  /* Ueber der unteren Navigationsleiste, nicht darunter. */
  .ds-savebar { left: 0; bottom: var(--bottomnav-h, 0px); }
  .ds-savebar .ds-actionbar-info { display: none; }
  .ds-savebar .ds-actionbar-actions { margin-left: auto; width: auto; }
  /* Pillen und Kreuz, die keine <button> sind: Ziel in beiden Massen. */
  .ex, .gh-pille, .help-close { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; }
}


/* ══ §3.4 · Bodenblätter ═════════════════════════════════════════════════
   "Jede Seitenleiste und jedes Modal wird ein Bodenblatt."

   Ein mittig schwebender Dialog ist auf dem Handy die falsche Form: er
   lässt oben und unten Rand stehen, den niemand braucht, und die Knöpfe
   landen in der Bildschirmmitte statt dort, wo der Daumen ist. Ein
   Bodenblatt kommt von unten, nutzt die volle Breite und endet dort, wo
   die Hand ohnehin liegt.

   Die Regel greift zentral über die Hüllen-Klassen aller Dialoge im Panel
   (modal-bd, modal-backdrop, del-modal-overlay, us-bd, schau) — die
   einzelnen Ansichten mussten dafür nicht angefasst werden. */
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .modal-bd, .modal-backdrop, .del-modal-overlay, .us-bd, .schau {
    align-items: flex-end !important;
    padding: 0 !important;
  }
  .modal-bd > *, .modal-backdrop > *, .del-modal-overlay > *,
  .us-bd > *, .schau > * {
    width: 100% !important;
    max-width: none !important;
    border-radius: 16px 16px 0 0 !important;
    max-height: 88vh !important;
    /* Die Höhe folgt dem Inhalt, bis das Blatt zu hoch würde. */
    height: auto !important;
    animation: dsBlattAuf .22s cubic-bezier(.32,.72,0,1);
  }
  /* Der Griff macht sichtbar, dass das Blatt von unten kommt. */
  .modal-bd > *::before, .modal-backdrop > *::before, .del-modal-overlay > *::before,
  .us-bd > *::before, .schau > *::before {
    content: '';
    display: block;
    width: 38px; height: 4px;
    border-radius: 999px;
    background: var(--line-strong, #d6d3cc);
    margin: 9px auto 2px;
    flex: none;
  }
  /* Knöpfe im Fuss eines Blatts nebeneinander und gross genug. */
  .modal-foot, .modal-ft, .us-dlg-fuss, .del-modal-actions {
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)) !important;
  }
}
@keyframes dsBlattAuf { from { transform: translateY(100%); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .modal-bd > *, .modal-backdrop > *, .del-modal-overlay > *, .us-bd > *, .schau > * { animation: none; }
}

/* ══ §3.4 Regel 1 · Nebeneinander wird Karussell, nicht Stapel ════════════
   "Stapeln verliert die Information ‚es gibt mehrere'." Bei einem Vergleich
   (Theme-Alternativen) steht eine Karte fast bildschirmbreit, die nächste
   guckt hervor — so sieht man auf einen Blick, dass es weitergeht. Die drei
   Vorschläge in /aendern und Web Easy bringt ds-vorschau-karten.js selbst als
   Wischreihe mit; die frühere Regel für .fl-varianten traf nichts mehr. */
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .themes-grid, .theme-grid {
    display: flex !important;
    grid-template-columns: none !important;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: var(--sp-md);
    padding-bottom: 6px;
    /* Damit die letzte Karte nicht am Rand klebt. */
    scroll-padding-left: 4px;
    -webkit-overflow-scrolling: touch;
  }
  .themes-grid > *, .theme-grid > * {
    flex: 0 0 86%;
    scroll-snap-align: start;
  }
  .themes-grid::-webkit-scrollbar { height: 4px; }
}

/* ══ §3.4 Regel 5 · Was nicht geht, wird benannt ══════════════════════════
   Baupläne und Bausteine brauchen Fläche. Ein Satz ist besser als eine
   unbedienbare Fassung — der Hinweis steht auf dem Handy oben, die Ansicht
   bleibt darunter benutzbar für alles, was ohne Ziehen geht. */
.nur-am-rechner { display: none; }
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .nur-am-rechner {
    display: flex; align-items: flex-start; gap: var(--sp-sm);
    background: var(--tint, #EAF2F7); border: 1px solid var(--accent);
    border-radius: var(--r-lg); padding: 12px 14px; margin: 0 0 12px;
    font-size: var(--fs-base); line-height: 1.55; color: var(--ink);
  }
  .nur-am-rechner b { display: block; }
}

/* ══ Beschriftungen: Mono, Versal, gesperrt ══════════════════════════════
   11px sind lesbar, WENN der Text kurz, versal und gesperrt ist. Als Prosa
   waeren dieselben 11px zu klein — darum gilt diese Groesse nur hier. */
.fl, .rep-label, .pd-q-lbl, .gruppe-lbl, .fk-lbl, .einst-zeile .gruppe-lbl,
.navliste-kopf .navliste-sub {
  font-size: var(--fs-label) !important;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  text-transform: uppercase;
  /* .1em auf einer langen Versalzeile zieht sie ueber zwei Zeilen und
     traegt zum Buchstabieren bei. .05em haelt die Anmutung und laesst die
     Zeile als Wort lesen. */
  letter-spacing: .05em;
  line-height: 1.4;
}
/* Wertangaben duerfen klein bleiben — aber nur in Mono. */
.seo-char-count, .ed-zahl, .navzeile-a, .bw-suche .gruppe-hinweis {
  font-size: var(--fs-wert);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
}
/* Hilfstexte sind Prosa und steigen mit --fs-xs auf 12,5px. */
.fhint, .gruppe-hinweis { color: var(--text-2, var(--text-muted)); }
.rep-item-header { font-size: var(--fs-base); }

/* ══ Kaestchen werden Schalter ═══════════════════════════════════════════
   Das Kaestchen war 15x15px — ein Viertel der empfohlenen Trefferflaeche,
   und daneben zu tippen tut nichts, was wie ein kaputter Schalter wirkt.
   Die ganze Zeile ist jetzt das Ziel. */
.cb-wrap {
  display: flex; align-items: center; gap: var(--sp-md);
  min-height: 52px; padding: var(--sp-sm) var(--sp-md);
  border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); cursor: pointer;
}
.cb-wrap:hover { border-color: var(--border-strong, var(--accent)); }
.cb-wrap input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  flex: none; width: 46px; height: 26px; margin: 0;
  border-radius: 999px; background: var(--border); position: relative;
  cursor: pointer; transition: background .16s;
}
.cb-wrap input[type="checkbox"]::after {
  content: ''; position: absolute; top: 3px; left: 3px;
  width: 20px; height: 20px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.28);
  transition: transform .16s;
}
.cb-wrap input[type="checkbox"]:checked { background: var(--accent); }
/* Wie der allgemeine Schalter unten: «ganz rechts» ist rechts. Mit translateX(20px) UND dem
   left: calc(100% - 2px) der allgemeinen Regel lag der Knopf 22 px rechts neben der Bahn, auf der
   Beschriftung («N●y Forms aktivieren» im Bausteinformular, seit editor.css das 15-px-Kaestchen
   nicht mehr erzwingt). */
.cb-wrap input[type="checkbox"]:checked::after { left: calc(100% - 3px); transform: translateX(-100%); }
.cb-wrap input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cb-wrap label { flex: 1; cursor: pointer; margin: 0; }
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  .cb-wrap { min-height: 58px; }
}

/* ══ Aura hinter allem ═══════════════════════════════════════════════════
   Drei weiche Lichter im Grund, damit die Scheiben etwas haben, wovor sie
   liegen. Ohne Hintergrund ist eine Glasflaeche nur eine graue Flaeche.

   Die Toene bleiben zwischen 197° und 222°; in der Nachtfassung kommt kein
   Warmton vor. Fest im Bild (fixed), damit sie beim Rollen nicht mitwandern
   und Streifen ziehen. */
[data-glas] body::before {
  content: ''; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(48% 42% at 12% 8%,  var(--glas-aura-1), transparent 70%),
    radial-gradient(52% 46% at 88% 22%, var(--glas-aura-2), transparent 72%),
    radial-gradient(60% 50% at 50% 108%, var(--glas-aura-3), transparent 70%);
}

/* ══ Die Flaechen werden Scheiben ════════════════════════════════════════
   Inhaltsflaechen bekommen die erste Unschaerfestufe. Was schwebt, bekommt
   in einem eigenen Schritt .glas--schwebend — dort gilt ausserdem: es
   bleibt in BEIDEN Fassungen dunkel, weil helles Werkzeug auf heller Seite
   verschwindet. */
[data-glas] .card,
[data-glas] .karte,
[data-glas] .hcard,
[data-glas] .ov-kpi,
[data-glas] .ov-liste,
[data-glas] .we-karte,
[data-glas] .vcard,
[data-glas] .variant,
[data-glas] .empty-state,
[data-glas] .settings-nav,
[data-glas] .spanel,
[data-glas] .block-card,
[data-glas] .navliste,
/* .hero-card und .err gehoeren dazu: es sind Karten, sie hiessen nur
   nie so. Ohne sie standen sie flach zwischen lauter Scheiben. */
[data-glas] .hero-card,
[data-glas] .err,
[data-glas] .seitenkasten {
  background: var(--glas-bloom), var(--glas-scheibe), var(--glas-unterlage);
  backdrop-filter: blur(var(--glas-blur)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--glas-blur)) saturate(150%);
  border: 1px solid var(--glas-kante);
  border-radius: var(--glas-r-karte);
  box-shadow: var(--glas-tiefe), var(--glas-glimmen),
              inset 0 1px 0 var(--glas-licht-oben),
              inset 0 -1px 0 var(--glas-licht-unten);
}

/* Eingabefelder: in der Nacht vertieft, am Tag erhaben wie Papier. */
[data-glas] .fi, [data-glas] .fta, [data-glas] .fse,
[data-glas] .feldkasten, [data-glas] .fk-feld, [data-glas] input[type="text"],
[data-glas] input[type="email"], [data-glas] input[type="search"],
/* Felder OHNE type und die uebrigen Textarten gehoerten nie dazu (Panel-N1):
   sie behielten den schwachen Rand ihrer Seitendatei, die mit type bekamen
   den fast weissen Lichtrand — im Tag war das Feld mit type unsichtbar. */
[data-glas] input:not([type]), [data-glas] input[type="number"],
[data-glas] input[type="url"], [data-glas] input[type="date"],
[data-glas] input[type="time"], [data-glas] input[type="datetime-local"],
/* type=tel gehoerte nicht dazu: das Telefonfeld stand im Glas als einziges
   Feld mit 8px Rundung und deckender Flaeche neben den Glasfeldern (K14). */
[data-glas] input[type="tel"],
[data-glas] input[type="password"], [data-glas] textarea, [data-glas] select,
/* Die eigenen Primitive des Design-Systems gehoerten nie dazu: .ds-input
   behielt im Glas 8px Rundung, waehrend .fi daneben 14px trug. Zwei
   Eingabefelder nebeneinander, sichtbar verschieden. */
[data-glas] .ds-input, [data-glas] .ds-select, [data-glas] .ds-textarea {
  background: var(--glas-feld);
  border: 1px solid var(--glas-feldrand);
  border-radius: var(--glas-r-feld);
  color: var(--text);
}
/* Der Rand gilt auch dort, wo eine Seitendatei spaeter ein eigenes
   Feld-Aussehen setzt (.ds-einst .fi, .fi:hover in editor.css …): doppeltes
   Attribut hebt die Rangfolge, ohne !important. Fokus, Fehlerzustand und
   Felder, die absichtlich ohne Rand stehen (border-style none), bleiben. */
[data-glas][data-glas] :is(.fi, .fta, .fse, .feldkasten, .fk-feld, .ds-input, .ds-select, .ds-textarea,
  input:not([type]), input[type="text"], input[type="email"], input[type="search"], input[type="tel"],
  input[type="password"], input[type="number"], input[type="url"], input[type="date"], input[type="time"],
  input[type="datetime-local"], textarea, select):not(:focus):not(:focus-visible):not([aria-invalid="true"]) {
  border-color: var(--glas-feldrand);
}
/* Rich-Text-Felder tragen den Rand am Rahmen (.rt, editor.css), ihre Textarea hat keinen:
   dort stand --border-strong (1,47:1 gegen den Grund, Panel-N1 im Bausteinformular). */
[data-glas][data-glas] .rt:not(:focus-within) { border-color: var(--glas-feldrand); }
[data-glas="dunkel"] .fi, [data-glas="dunkel"] .fta, [data-glas="dunkel"] .fse,
[data-glas="dunkel"] .feldkasten, [data-glas="dunkel"] input, [data-glas="dunkel"] textarea,
[data-glas="dunkel"] select {
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06);
}

/* Die Seitenleiste ist Teil der Scheibe, nicht ein Block davor. */
[data-glas] .sidebar {
  background: var(--glas-scheibe), var(--glas-unterlage);
  backdrop-filter: blur(var(--glas-blur)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--glas-blur)) saturate(150%);
  border-right: 1px solid var(--glas-kante);
}

/* Ein Leuchten je Bildschirm: nur die Hauptaktion. Zwei Leuchten heben
   sich auf. */
[data-glas] .btn-primary, [data-glas] .ds-btn--primary,
[data-glas] .knopf.stark, [data-glas] .ed-btn-strong {
  box-shadow: var(--glas-tiefe), 0 0 28px rgba(126,200,240,.22);
}

/* Deckende Fassung: gleiche Kanten, gleiche Schatten, nur ohne Glanz. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  [data-glas] .card, [data-glas] .karte, [data-glas] .hcard, [data-glas] .ov-kpi,
  [data-glas] .ov-liste, [data-glas] .we-karte, [data-glas] .vcard, [data-glas] .variant,
  [data-glas] .empty-state, [data-glas] .settings-nav, [data-glas] .spanel,
  [data-glas] .block-card, [data-glas] .navliste, [data-glas] .seitenkasten,
  [data-glas] .sidebar { background: var(--glas-deckend); }
}
@media (prefers-reduced-transparency: reduce) {
  [data-glas] .card, [data-glas] .karte, [data-glas] .hcard, [data-glas] .ov-kpi,
  [data-glas] .ov-liste, [data-glas] .we-karte, [data-glas] .vcard, [data-glas] .variant,
  [data-glas] .empty-state, [data-glas] .settings-nav, [data-glas] .spanel,
  [data-glas] .block-card, [data-glas] .navliste, [data-glas] .seitenkasten,
  [data-glas] .sidebar {
    background: var(--glas-deckend);
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
}

/* ══ Flaechen, die sich im Dunkeln umkehren ══════════════════════════════
   24 Regeln nehmen var(--ink) als Hintergrund. Am Tag ist das die
   dunkelste Farbe, in der Nacht die hellste — der Knopf wurde weiss auf
   weiss (gemessen 1,1:1). In Glas traegt die Hauptaktion darum den Akzent. */
[data-glas] .ds-btn--strong,
[data-glas] .btn-primary,
[data-glas] .ds-btn--primary,
[data-glas] .knopf.stark,
[data-glas] .ed-btn-strong,
[data-glas] .feldkasten-ok,
[data-glas] .fk-ok,
[data-glas] .btn.btn-primary,
[data-glas] .ds-savebar,
[data-glas] .ed-savebar {
  background: var(--akzent-fuell, var(--akzent, var(--accent)));
  color: var(--akzent-auf, #fff);
  border-color: transparent;
}

/* ── Meldungen ──────────────────────────────────────────────────────────
   Eine Meldung ist kein Knopf. Sie stand bisher in derselben Regel und war
   damit akzentfarben — und die Ansichten ueberschrieben das danach mit
   `background: var(--green)` und weisser Schrift. In der Nachtfassung sind
   --green/--red/--brand aber HELL: --brand zeigt auf --ink (#F3F8FA), und
   weisse Schrift darauf ergibt 1,05:1. Die Meldung war schlicht nicht da.

   Jetzt: ruhige, deckende Flaeche mit der normalen Textfarbe; die Bedeutung
   steckt im Streifen an der Kante. Das ist auch die zugaenglichere Loesung —
   Farbe allein darf nie der einzige Traeger einer Aussage sein (WCAG 1.4.1).

   `:root[data-glas]` statt `[data-glas]`: eine Klasse mehr als die
   `.toast.success`-Reste in den Ansichten, damit diese Regel gewinnt,
   solange es die Reste gibt. P1-4 loest sie durch partials/toast.ejs ab. */
:root[data-glas] .toast,
:root[data-glas] .melde,
:root[data-glas] .meldung {
  background: var(--glas-deckend);
  color: var(--text);
  border: 1px solid var(--glas-linie);
}
:root[data-glas] .toast.success, :root[data-glas] .melde.success, :root[data-glas] .meldung.success,
:root[data-glas] .toast.ok,      :root[data-glas] .melde.ok,      :root[data-glas] .meldung.ok {
  box-shadow: inset 3px 0 0 var(--ok), var(--glas-tiefe);
}
:root[data-glas] .toast.error, :root[data-glas] .melde.error, :root[data-glas] .meldung.error,
:root[data-glas] .toast.fehler {
  box-shadow: inset 3px 0 0 var(--danger), var(--glas-tiefe);
}
:root[data-glas] .toast.warn, :root[data-glas] .melde.warn, :root[data-glas] .meldung.warn {
  box-shadow: inset 3px 0 0 var(--warn), var(--glas-tiefe);
}
:root[data-glas] .toast.info, :root[data-glas] .melde.info, :root[data-glas] .meldung.info {
  box-shadow: inset 3px 0 0 var(--akzent), var(--glas-tiefe);
}
/* Die Schriftfarbe gehoert hierher: mehrere Ansichten setzen im Hover
   ausdruecklich `color: #fff` (.ed-btn-strong:hover) und stehen spaeter
   als admin.css — nachts weiss auf hellem Cyan, 1,8:1. Gesperrte Knoepfe
   bleiben aussen vor, sonst wird ihre gedaempfte Schrift beim Ueberfahren
   auf die volle Akzentflaeche gesetzt. */
[data-glas] .ds-btn--strong:hover:not(:disabled),
[data-glas] .btn-primary:hover:not(:disabled),
[data-glas] .knopf.stark:hover:not(:disabled),
[data-glas] .ed-btn-strong:hover:not(:disabled) {
  filter: brightness(1.08);
  background: var(--akzent-fuell, var(--akzent, var(--accent)));
  color: var(--akzent-auf, #fff);
  border-color: transparent;
}
/* Die Speicherleiste traegt in Glas den Akzent (Regel oben) — ihre Texte
   aber behielten --ink und --ink-3: nachts helle Schrift auf hellem Cyan,
   gemessen 1,4 bis 1,7:1 ("Nicht gespeichert", der Bereich, "Verwerfen").
   Alles darin folgt darum der Schrift auf dem Akzent. "Speichern" kehrt
   sich um, sonst ist der Knopf Akzent auf Akzent und hat keine Kante. */
[data-glas] .ds-savebar .ds-actionbar-dirty,
[data-glas] .ds-savebar .ds-actionbar-title,
[data-glas] .ds-savebar .ds-actionbar-sub { color: var(--akzent-auf, #fff); }
/* Die :not()-Kette ist Absicht: die Glasregel fuer schlichte Knoepfe weiter
   unten (.btn:not(.btn-primary):not(…) …) wiegt 0,6,0 und setzte sonst
   wieder --text auf das Cyan. */
[data-glas] .ds-savebar .btn[data-sb="verwerfen"]:not(.btn-primary):not(.btn-grad):not(.btn-danger):not(.btn-purple) {
  background: transparent; color: var(--akzent-auf, #fff);
  border: 1px solid color-mix(in srgb, var(--akzent-auf, #fff) 45%, transparent);
}
[data-glas] .ds-savebar .ds-btn--primary,
[data-glas] .ds-savebar .ds-btn--primary:hover:not(:disabled) {
  background: var(--akzent-auf, #fff); color: var(--akzent, var(--accent)); border-color: transparent;
}
/* Fehlgeschlagen (ds-savebar.js setzt die Klasse, solange ein Bereich
   nicht gespeichert werden konnte): die Farbe der Gefahr, damit der
   Zustand nicht wie "bitte speichern" aussieht, sondern wie "hat nicht
   geklappt". Die Worte sagen es zusaetzlich — Farbe allein genuegt nie. */
.ds-savebar--fehler { border-top-color: var(--danger); }
.ds-savebar--fehler .ds-actionbar-dirty { color: var(--danger); }
.ds-savebar--fehler .ds-actionbar-dirty::before { background: var(--danger); }
[data-glas] .ds-savebar.ds-savebar--fehler { background: var(--danger); }
[data-glas] .ds-savebar.ds-savebar--fehler .ds-actionbar-dirty,
[data-glas] .ds-savebar.ds-savebar--fehler .ds-actionbar-title,
[data-glas] .ds-savebar.ds-savebar--fehler .ds-actionbar-sub { color: var(--signal-auf, #fff); }
[data-glas] .ds-savebar.ds-savebar--fehler .ds-actionbar-dirty::before { background: var(--signal-auf, #fff); }
[data-glas] .ds-savebar.ds-savebar--fehler .btn[data-sb="verwerfen"]:not(.btn-primary):not(.btn-grad):not(.btn-danger):not(.btn-purple) {
  color: var(--signal-auf, #fff);
  border-color: color-mix(in srgb, var(--signal-auf, #fff) 45%, transparent);
}
[data-glas] .ds-savebar.ds-savebar--fehler .ds-btn--primary,
[data-glas] .ds-savebar.ds-savebar--fehler .ds-btn--primary:hover:not(:disabled) {
  background: var(--signal-auf, #fff); color: var(--danger);
}
/* Meldungen tragen keinen Akzent — sie sind Mitteilung, nicht Handlung. */
[data-glas] .melde, [data-glas] .toast, [data-glas] .meldung {
  background: var(--glas-deckend);
  color: var(--text);
  border: 1px solid var(--glas-kante);
  box-shadow: var(--glas-tiefe);
}

/* Marken und Pillen, die dieselbe Annahme treffen. */
[data-glas] .adminbadge, [data-glas] .nav-admin, [data-glas] .host-pille,
[data-glas] .rep-num, [data-glas] .ov-sp-bild {
  background: var(--glas-fuellung-stark);
  color: var(--text);
  border: 1px solid var(--glas-kante);
}

/* ══ Der Kopfbalken ══════════════════════════════════════════════════════
   19 Ansichten tragen dieselbe Regel als Kopie: background
   rgba(255,255,255,.85). Am Tag ist das Milchglas, in der Nacht ist es
   weiss unter hellem Text — gemessen 1,1:1, also unlesbar.

   Statt 19 Kopien anzufassen, gewinnt hier die Spezifitaet: [data-glas]
   .topbar sticht die Regel im <style> jeder Ansicht, unabhaengig von der
   Reihenfolge. Die Kopien selbst gehoeren zusammengelegt — das ist ein
   eigener Schritt, kein Nebenbei. */
[data-glas] .tabs,
[data-glas] .topbar, [data-glas] .ds-topbar {
  background: var(--glas-scheibe), var(--glas-unterlage);
  backdrop-filter: blur(var(--glas-blur-float)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--glas-blur-float)) saturate(150%);
  border-bottom: 1px solid var(--glas-kante);
  color: var(--text);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  [data-glas] .tabs,
  [data-glas] .topbar, [data-glas] .ds-topbar { background: var(--glas-deckend); }
}
@media (prefers-reduced-transparency: reduce) {
  [data-glas] .tabs,
  [data-glas] .topbar, [data-glas] .ds-topbar {
    background: var(--glas-deckend);
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
}

/* ── Die Projektkarte ───────────────────────────────────────────────────
   Die Karte ist eine <article>, der Name ist der Link. Damit die ganze
   Karte anklickbar bleibt, spannt der Link seine Flaeche darueber — ein
   Link im Markup, eine Karte im Bild. Die Knoepfe liegen darueber und
   brauchen kein preventDefault mehr (P1-3). */
.project-card { position: relative; }
.project-card-link {
  font-weight: 700; font-size: var(--fs-lg); letter-spacing: -0.01em;
  color: var(--ink); text-decoration: none; display: block;
}
.project-card-link::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
}
.project-card-link:hover { color: var(--accent-strong); }
/* Was man bedienen kann, liegt ueber der aufgespannten Flaeche. */
.project-card .btn-delete-project,
.project-card .ds-btn,
.project-card button { position: relative; z-index: 1; }
/* Der Fokus gehoert auf die KARTE, nicht auf ein unsichtbares Rechteck. */
.project-card:focus-within { outline: 2px solid var(--ring-farbe); outline-offset: 2px; }
.project-card-link:focus-visible { outline: none; }

/* ══ Die Uebersicht im Glas ══════════════════════════════════════════════
   Aus dem Entwurf, Abschnitt 2a. Gezaehlt hatte von sechs Bausteinen der
   Uebersicht genau EINER eine Glasfassung (.ov-kpi) — Suche, Filter,
   Ansichtswechsel, Tabelle und Zustandspille blieben flach und standen
   hart zwischen lauter Scheiben. Das ist derselbe Fehler, den 2c fuer
   Dialoge und Menues beschreibt, nur auf dem Bildschirm, den man am
   haeufigsten sieht. */

/* Suchfeld: vertieft wie jedes andere Feld, aber mit der weicheren
   Rundung des Entwurfs. */
[data-glas] .ov-suche {
  background: var(--glas-feld);
  border: 1px solid var(--glas-kante);
  border-radius: 14px;
  color: var(--label);
}
[data-glas] .ov-suche input { color: var(--text); }
[data-glas] .ov-suche input::placeholder { color: var(--label); }

/* Segmentwahl und Ansichtswechsel: die Schiene ist Glas, das gewaehlte
   Segment traegt die Lichtkante. */
[data-glas] .proj-filter,
[data-glas] .ov-ansicht {
  background: var(--glas-fuellung);
  border: 1px solid var(--glas-kante);
  border-radius: 14px;
}
[data-glas] .proj-filter button,
[data-glas] .ov-ansicht button {
  background: none; border: 1px solid transparent; color: var(--label);
}
[data-glas] .proj-filter button:hover,
[data-glas] .ov-ansicht button:hover { color: var(--text); }
[data-glas] .proj-filter button.is-active,
[data-glas] .ov-ansicht button.is-active {
  background: var(--glas-fuellung-stark);
  border-color: var(--glas-kante);
  color: var(--text);
  box-shadow: inset 0 1px 0 var(--glas-licht-oben);
}

/* Die Liste selbst ist eine Scheibe, kein Blatt Papier. */
[data-glas] .ov-liste, [data-glas] .ov-tab {
  background: none;
  border-color: var(--glas-kante);
  color: var(--text);
}
[data-glas] .ov-tab th {
  color: var(--label);
  border-bottom-color: var(--glas-kante);
}
[data-glas] .ov-tab td { border-bottom-color: var(--glas-linie); }
[data-glas] .ov-tab tbody tr:hover { background: var(--glas-fuellung); }

/* Zustandspille: Punkt UND Rahmen in der eigenen Farbe. Der Rahmen fehlte
   — auf einer Scheibe verliert eine bloss getoente Flaeche ihre Kante und
   die Pille zerfliesst. `currentColor` heisst: sie stimmt immer, egal
   welchen Zustand sie traegt. */
.ds-status { border: 1px solid currentColor; }
[data-glas] .ds-status--live  { background: rgba(106,207,168,.16); color: var(--ok); }
[data-glas] .ds-status--dev   { background: rgba(224,179,126,.16); color: var(--warn); }
[data-glas] .ds-status--draft { background: var(--glas-fuellung);  color: var(--text-2); }

/* Fortschritt: Spur im Glas, Fuellung im Akzent. */
[data-glas] .ov-bereit-b, [data-glas] .ds-progress {
  background: var(--glas-fuellung);
}
[data-glas] .ov-bereit-b > *, [data-glas] .ds-progress > * {
  background: var(--akzent, var(--accent));
}

/* Die Vorschau-Miniatur behaelt ihre helle Flaeche — dort steht die
   Website des Kunden, nicht das Panel. Nur die Kante wird zur Glaskante. */
[data-glas] .ov-sp-bild img, [data-glas] .ov-bild {
  border-color: var(--glas-kante);
}

/* ══ Wo die Meldung steht ════════════════════════════════════════════════
   18 Ansichten bringen ihre eigene .toast-Fassung mit, alle unten rechts
   auf z-index 999 und `bottom: 1.5rem`. Keine davon kennt --bottomnav-h:
   am Telefon lag die Meldung damit UNTER der schwebenden Leiste.

   `body .toast` ist eine Klasse mehr als `.toast` und gewinnt deshalb,
   obwohl admin.css frueher im Dokument steht. Gesetzt wird nur Lage und
   Ebene — Farbe, Form und das Ein-/Ausblenden bleiben bei den Ansichten,
   damit sich nichts unbeabsichtigt aendert.

   Die Fassungen selbst gehoeren zusammengelegt (ein partials/toast.ejs);
   das ist ein eigener Schritt und braucht eine Ansicht-fuer-Ansicht-Probe. */
body .toast, body .melde, body .meldung {
  z-index: var(--z-meldung);
  /* Ausserhalb der Telefonbreite gibt es keine untere Leiste — dann faellt
     --bottomnav-h weg und es bleibt der normale Abstand.

     ABWAEHLBAR, und das ist keine Feinheit: ein absolut gesetztes Element
     mit top UND bottom wird auf die volle Hoehe GEZOGEN. Die Meldung in
     /aendern steht oben — sie war dadurch so hoch wie das Fenster, ein
     schwarzer Balken ueber dem halben Bild. Wer oben steht, setzt
     --meldung-unten: auto. */
  bottom: var(--meldung-unten, calc(var(--bottomnav-h, 0px) + var(--meldung-abstand, 1.5rem)));
}

/* ══ Was schwebt ═════════════════════════════════════════════════════════
   Dialog, Menue und Schublade liegen UEBER der Seite. Sie standen bisher
   nicht in der Glas-Liste und blieben flach — eine harte Flaeche mitten
   zwischen Scheiben. Sie bekommen die zweite Unschaerfestufe
   (--glas-blur-float) und die freiere Unterlage, die eigens fuer Kaesten
   gedacht ist, in denen man LIEST: bei 86 % Deckung waere die Unschaerfe
   dahinter nur Rechenaufwand ohne Bild, bei zu wenig Deckung der Text
   nicht mehr zu lesen.

   [data-glas] sticht die Regeln im <style> der Ansichten — dieselbe
   Ueberlegung wie bei .topbar weiter oben. */
[data-glas] .del-modal,
[data-glas] .modal-box,
[data-glas] .pv-box,
[data-glas] .ds-help-panel,
[data-glas] .ov-menue,
[data-glas] .bl-menue,
[data-glas] .sf-menu,
[data-glas] .ds-switcher-menu {
  background: var(--glas-bloom), var(--glas-scheibe), var(--glas-unterlage-frei, var(--glas-unterlage));
  backdrop-filter: blur(var(--glas-blur-float)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--glas-blur-float)) saturate(150%);
  border: 1px solid var(--glas-kante);
  color: var(--text);
  box-shadow: var(--glas-tiefe), inset 0 1px 0 var(--glas-licht-oben);
}
/* Ohne backdrop-filter gaebe es keine Unschaerfe, aber sehr wohl
   Durchsicht — der Text laege dann auf dem Seiteninhalt. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  [data-glas] .del-modal, [data-glas] .modal-box, [data-glas] .pv-box,
  [data-glas] .ds-help-panel, [data-glas] .ov-menue, [data-glas] .bl-menue,
  [data-glas] .sf-menu, [data-glas] .ds-switcher-menu {
    background: var(--glas-deckend);
  }
}
@media (prefers-reduced-transparency: reduce) {
  [data-glas] .del-modal, [data-glas] .modal-box, [data-glas] .pv-box,
  [data-glas] .ds-help-panel, [data-glas] .ov-menue, [data-glas] .bl-menue,
  [data-glas] .sf-menu, [data-glas] .ds-switcher-menu {
    background: var(--glas-deckend);
    backdrop-filter: none; -webkit-backdrop-filter: none;
  }
}

/* ══ Der Zweitknopf ══════════════════════════════════════════════════════
   Die Glas-Regel deckte nur .ds-btn--primary und --strong ab. Der schlichte
   .ds-btn behielt background: var(--surface) — in der Nachtfassung #101A20,
   also eine flache Kachel auf einer Scheibe. Dabei ist er der Knopf, den
   man am haeufigsten sieht: „Oeffnen" in jeder Zeile, „Abbrechen" in jedem
   Dialog, das Zeichen fuer weitere Aktionen.
   Nicht fuer die gefuellten Fassungen — die haben ihre eigene Regel. */
/* Derselbe Satz fuer den anderen Namen. `.btn` steht in 23 Ansichten und
   hatte keine Glasfassung — nur .btn-primary hatte eine. Der schlichte
   Knopf blieb damit eine flache Kachel auf einer Scheibe, genau wie
   .ds-btn vor P1-11. Dazu die Bedienelemente, die ueberall gleich
   aussehen sollen und nirgends definiert waren. */
[data-glas] .btn:not(.btn-primary):not(.btn-grad):not(.btn-danger):not(.btn-purple),
[data-glas] .knopf:not(.stark),
[data-glas] .vp-btn, [data-glas] .snav-btn, [data-glas] .btn-reset,
[data-glas] .modal-close, [data-glas] .hamburger,
[data-glas] .theme-select,
/* Welle 2: die neuen Namen der Komponentenschicht (--primaer, --leise) stehen
   in der :not()-Liste. Ohne sie gewann diese Regel (0,5,0) gegen
   «[data-glas] .ds-btn--primaer» (0,2,0) in ds-komponenten.css — jeder
   Hauptknopf in Glas war eine graue Kachel, jeder leise Knopf trug einen
   Rahmen. Nachgewiesen auf /ds-muster, Web Easy und der Übersicht. */
[data-glas] .ds-btn:not(.ds-btn--primary):not(.ds-btn--primaer):not(.ds-btn--leise):not(.ds-btn--strong):not(.ds-btn--gefahr) {
  background: var(--glas-fuellung);
  border-color: var(--glas-linie);
  color: var(--text);
  backdrop-filter: blur(var(--glas-blur)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--glas-blur)) saturate(150%);
  box-shadow: inset 0 1px 0 var(--glas-licht-oben);
}
[data-glas] .btn:not(.btn-primary):not(.btn-grad):not(.btn-danger):not(.btn-purple):hover,
[data-glas] .knopf:not(.stark):hover,
[data-glas] .vp-btn:hover, [data-glas] .snav-btn:hover, [data-glas] .btn-reset:hover,
[data-glas] .modal-close:hover, [data-glas] .hamburger:hover,
[data-glas] .ds-btn:not(.ds-btn--primary):not(.ds-btn--primaer):not(.ds-btn--leise):not(.ds-btn--strong):not(.ds-btn--gefahr):hover {
  background: var(--glas-fuellung-stark);
  border-color: var(--glas-kante);
  color: var(--text);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  [data-glas] .ds-btn:not(.ds-btn--primary):not(.ds-btn--primaer):not(.ds-btn--leise):not(.ds-btn--strong):not(.ds-btn--gefahr) {
    background: var(--glas-deckend);
  }
}
/* Die gefuellte Gefahr (Bestaetigen in dsFrage bei Loeschen, Vom-Netz-
   Nehmen …) bleibt auch in Glas rot. Die Regel oben machte sie zur
   neutralen Kachel — die gefaehrliche Antwort war dann unscheinbarer als
   das harmlose «OK», das den Akzent traegt. Die Farben kommen aus
   .ds-btn--gefahr; --signal-auf ist nachts dunkel, tags weiss. */

/* ══ Gefuellte Pillen ════════════════════════════════════════════════════
   .host-pille trug im Zustand "an" die volle --ok-Farbe als Flaeche und
   hellen Text darauf: 1,8:1. In der Nachtfassung sind --ok/--warn/--danger
   HELLE Farben — sie taugen als Schrift, nicht als Grund unter Schrift.
   Also umgekehrt: die Farbe wird der Text, der Grund bleibt Glas. */
[data-glas] .host-zustand--an .host-pille,
[data-glas] .host-zustand--aus .host-pille,
[data-glas] .pille--ok, [data-glas] .pille--warn {
  /* Nicht die neutrale Fuellung: die ist in der Tagfassung dunkel und
     druecke den Zustandston auf 3,8:1. Eine Pille traegt die Toenung
     ihrer eigenen Farbe. */
  background: transparent;
  border: 1px solid currentColor;
}
[data-glas] .host-zustand--an .host-pille, [data-glas] .pille--ok { background: var(--ok-tint); }
[data-glas] .host-zustand--an .host-pille { color: var(--ok); }
/* Der Kasten dahinter ebenso: eine Toenung, keine Flaeche. */
[data-glas] .host-zustand--an { background: var(--ok-tint); border-color: rgba(106,207,168,.34); }
[data-glas] .host-zustand--an, [data-glas] .host-zustand--aus { color: var(--text); }

/* ══ Der Rest der Umkehrung ══════════════════════════════════════════════
   Vollstaendige Liste der Klassen, die var(--ink) als FLAECHE nehmen —
   ermittelt aus dem Bestand, nicht geraten: .adminbadge .bl-html .cust-av
   .ds-btn--strong .ed-btn-strong .fl-btn--dark .fl-logo .host-pille.
   Alle tragen weissen Text darauf. In der Nachtfassung ist --ink hell.

   Dazu .btn-preview: eine weisse Pille mit --accent-strong als Schrift.
   Seit --accent-strong hell ist (damit es auf dunklem Grund lesbar wird),
   verschwindet sie auf ihrem eigenen Weiss. Also wird auch sie Glas. */
[data-glas] .cust-av, [data-glas] .fl-logo, [data-glas] .bl-html,
[data-glas] .fl-btn--dark {
  background: var(--glas-fuellung-stark);
  color: var(--text);
  border: 1px solid var(--glas-kante);
}
[data-glas] .btn-preview {
  background: var(--glas-fuellung-stark);
  color: var(--text);
  border: 1px solid var(--glas-kante);
  backdrop-filter: blur(var(--glas-blur)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--glas-blur)) saturate(150%);
}

/* Die letzte Marke mit eigener heller Toenung: --yellow-light als Grund,
   dunkles Gelb als Schrift. In Glas dieselbe Umkehr wie bei den anderen. */
[data-glas] .snav-badge-yellow {
  background: var(--warn-tint);
  color: var(--warn);
  border: 1px solid rgba(224,179,126,.34);
}

/* Der Zaehler im aktiven Punkt trug rgba(255,255,255,.25) mit weisser
   Schrift — gedacht fuer die dunkle Seitenleiste. Auf heller Scheibe ist
   das weiss auf weiss. In Glas traegt er den Akzent, der in beiden
   Fassungen zu seiner Schrift passt. */
[data-glas] .nav-item.active .nav-badge {
  background: var(--akzent);
  color: var(--akzent-auf);
}

/* Das Kundenzeichen mit .accent behaelt eine gefuellte Flaeche — nur eben
   den Akzent, der zu seiner Schrift passt. Meine allgemeine Regel oben
   hatte ihm dunklen Text auf dunklem Akzent gegeben: 2,5:1, schlechter
   als vorher. Zwei Regeln gleicher Staerke, die sich widersprechen. */
[data-glas] .cust-av.accent {
  background: var(--akzent);
  color: var(--akzent-auf);
  border-color: transparent;
}

/* ══ Schalter: alle, nicht nur die in .cb-wrap ═══════════════════════════
   §3.3 nannte .cb-wrap input[type=checkbox] als das 15px-Kaestchen. Das
   stimmte — nur benutzt genau EINE von 22 Ansichten .cb-wrap. Die anderen
   51 Kaestchen im Panel stehen nackt in einem <label>, und die blieben
   22x22px: die Handy-Pruefung fand sie in Einstellungen und Hosting.

   Darum gilt die Schalterform jetzt fuer jedes Kaestchen. Wo eines in
   einem <label> sitzt, wird die ganze Zeile zum Ziel. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  flex: none; width: 42px; height: 24px; margin: 0;
  border-radius: 999px; background: var(--border);
  border: 1px solid var(--border-strong, var(--border));
  position: relative; cursor: pointer; vertical-align: middle;
  transition: background .16s, border-color .16s;
}
/* Der Knopf richtet sich nach der GROESSE des Schalters, nicht nach einer
   festen Zahl.

   Vorher waren es 18px, egal wie breit der Schalter war. Wer ihn irgendwo
   schmaler setzte — .am-switch im Seiten-Hub tat das mit 18px — behielt
   einen 18px-Knopf auf einer 18px-Bahn: der Knopf ragte hinaus und lag
   ueber der Beschriftung daneben. Im App-Modus las man deshalb "cebook"
   statt "Facebook".

   Mit top/bottom statt Hoehe und aspect-ratio waechst und schrumpft der
   Knopf mit. Und "ganz rechts" heisst rechts, nicht 18 Pixel weiter. */
input[type="checkbox"]::after {
  content: ''; position: absolute; top: 2px; bottom: 2px; left: 2px;
  aspect-ratio: 1; width: auto; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3);
  transition: left .16s;
}
input[type="checkbox"]:checked {
  background: var(--accent); border-color: var(--accent);
}
input[type="checkbox"]:checked::after { left: calc(100% - 2px); transform: translateX(-100%); }
input[type="checkbox"]:disabled { opacity: .5; cursor: default; }
input[type="checkbox"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  /* Am Telefon 32px hoch — das ist die Untergrenze, unter der ein Ziel
     regelmaessig verfehlt wird. */
  input[type="checkbox"] { width: 52px; height: 32px; }
  /* Die Zeile um das Kaestchen wird das Ziel, nicht nur das Kaestchen. */
  label > input[type="checkbox"] { margin-right: var(--sp-xs); }
}

/* Der Kopfverweis der Seitenleiste war 32px hoch — meine eigene Aenderung,
   als aus dem einen <a> ein <div> mit Verweis und Griff wurde. */
.sidebar-logo-link { min-height: 40px; }
/* In der Schublade (≤900) ist der Kopfverweis ein Tippziel: 44px statt 40
   (Panel-U7, gemessen 114×40 auf 24 Messungen). */
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) { .sidebar-logo-link { min-height: 44px; } }

/* ══ Variantenwähler ═════════════════════════════════════════════════════
   Ein Karussell statt zweier Pfeile. Die Masse stehen in ds-varianten.js
   an EINER Stelle und werden von dort gesetzt — hier steht nur, wie es
   aussieht, nicht wie breit es ist. */
/* Der Waehler ist ein Blatt (.ds-blatt): Huelle, Schleier, Griff und Schliessen kommen von ds-komponenten.css/ds-blatt.js. */
@media (min-width: 901px) and (min-height: 501px), (min-width: 901px) and (orientation: portrait) { .ds-blatt.dsv-blatt { width: min(920px, 100vw); } }
.dsv-blatt .ds-blatt-inhalt.dsv { padding: 0; gap: 0; }
.dsv-kopf {
  display: flex; align-items: center; gap: var(--sp-md);
  padding: var(--sp-md) var(--sp-lg);
  border-bottom: 1px solid var(--glas-linie, var(--border));
}
.dsv-titel { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.2; }
.dsv-name { font-size: var(--fs-lg); font-weight: 700; color: var(--text); }
.dsv-tech {
  font-family: var(--font-mono); font-size: var(--fs-wert);
  color: var(--text-2, var(--text-muted));
}
.dsv-zu {
  flex: none; width: 40px; height: 40px; border-radius: var(--r-md);
  border: 1px solid transparent; background: transparent;
  color: var(--text-2, var(--text-muted)); font-size: var(--fs-lg); cursor: pointer;
}
.dsv-zu:hover { background: var(--glas-fuellung, var(--surface-2)); color: var(--text); }

/* ── Die Bühne ───────────────────────────────────────────────────────── */
.dsv-buehne {
  position: relative; overflow: hidden; touch-action: pan-y; cursor: grab;
  /* Beim Ziehen darf weder Text markiert noch ein Bild mitgenommen werden —
     beides bricht die Zeigerereignisse ab. */
  user-select: none; -webkit-user-select: none;
}
.dsv-karte, .dsv-bild img { -webkit-user-drag: none; user-select: none; }
.dsv-buehne:active { cursor: grabbing; }
.dsv-spur { position: absolute; top: 18px; left: 0; display: flex; gap: 12px; will-change: transform; }

.dsv-karte {
  flex: none; width: 236px;
  display: flex; flex-direction: column; gap: 6px;
  padding: 0; border: 0; background: transparent; cursor: pointer;
  transform: scale(.82); opacity: .55;
  transition: transform .22s cubic-bezier(.22,.61,.36,1), opacity .22s;
  position: relative;
}
.dsv-karte.an { transform: scale(1); opacity: 1; }
.dsv-bild {
  /* overflow:hidden ist hier tragend, nicht kosmetisch: der Abschnitt wird
     in 1200px Breite gerendert und dann verkleinert — ohne Beschnitt laege
     er ueber der halben Buehne. */
  width: 100%; height: 152px; overflow: hidden;
  border: 1px solid var(--glas-linie, var(--border)); border-radius: var(--r-lg);
  background: var(--surface-2, #fff);
}
.dsv-karte.an .dsv-bild {
  border-color: var(--akzent, var(--accent));
  box-shadow: 0 0 0 2px var(--akzent, var(--accent)), var(--glas-tiefe, 0 8px 24px rgba(0,0,0,.2));
}
.dsv-bild img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dsv-bild--leer { background: var(--glas-fuellung, var(--surface-2)); }

/* Der Kartenname ist Bedienung und bleibt darum bei --fs-xs — nicht bei
   der Schriftgroesse des Abbilds darueber. */
.dsv-bez { display: flex; flex-direction: column; line-height: 1.25; text-align: left; }
.dsv-bez-n { font-size: var(--fs-xs); font-weight: 650; color: var(--text); }
.dsv-bez-t { font-family: var(--font-mono); font-size: var(--fs-label); color: var(--text-2, var(--text-muted)); }

/* Die Pille steht NUR auf der aktiven Karte: Nachbarkarten sind auf .82
   verkleinert, und Bedienschrift auf einem skalierten Element ist kleiner,
   als sie deklariert ist. */
.dsv-pille { display: none; }
.dsv-karte.an .dsv-pille {
  display: inline-block; position: absolute; top: 8px; left: 8px;
  font-family: var(--font-mono); font-size: var(--fs-label);
  text-transform: uppercase; letter-spacing: .08em;
  color: var(--warn); background: var(--warn-tint, rgba(224,179,126,.16));
  border: 1px solid currentColor; border-radius: 999px; padding: 2px 8px;
}

.dsv-pfeil {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 48px; height: 48px; border-radius: 50%;
  border: 1px solid var(--glas-linie, var(--border));
  background: var(--glas-deckend, var(--surface)); color: var(--text);
  font-size: 22px; line-height: 1; cursor: pointer;
}
.dsv-pfeil:hover { background: var(--glas-fuellung-stark, var(--surface-2)); }
.dsv-pfeil--links { left: 10px; }
.dsv-pfeil--rechts { right: 10px; }

.dsv-punkte { display: flex; justify-content: center; gap: 6px; flex-wrap: wrap; padding: 0 var(--sp-lg) var(--sp-sm); }
.dsv-punkt {
  width: 22px; height: 22px; padding: 0; border: 0; background: transparent; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.dsv-punkt::after {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: var(--border-strong, var(--border)); transition: background .16s, transform .16s;
}
.dsv-punkt.an::after { background: var(--akzent, var(--accent)); transform: scale(1.4); }

.dsv-fuss {
  display: flex; align-items: center; gap: var(--sp-sm);
  padding: var(--sp-md) var(--sp-lg);
  border-top: 1px solid var(--glas-linie, var(--border));
}
.dsv-luft { flex: 1; }
.dsv-zaehler { font-family: var(--font-mono); font-size: var(--fs-wert); color: var(--text-2, var(--text-muted)); }
/* Steht nur da, wenn die echten Abschnitte NICHT geladen haben: dann
   zeigen die Karten Skizzen, und das soll man wissen statt es zu
   raten. Gedaempft — eine Auskunft, keine Warnung. */
.dsv-skizze { font-family: var(--font-mono); font-size: var(--fs-2xs, 11px);
  color: var(--text-muted); opacity: .8; margin-left: var(--sp-sm, .75rem); }
.dsv-knopf {
  min-height: 44px; padding: 0 16px; border-radius: var(--r-lg);
  border: 1px solid var(--glas-linie, var(--border)); background: transparent;
  font: inherit; font-size: var(--fs-md); color: var(--text); cursor: pointer;
}
.dsv-knopf:hover { background: var(--glas-fuellung, var(--surface-2)); }
.dsv-knopf--stark {
  background: var(--akzent, var(--accent)); color: var(--akzent-auf, #fff);
  border-color: transparent; font-weight: 650;
}
.dsv-knopf--stark:hover { filter: brightness(1.08); background: var(--akzent, var(--accent)); }

/* ── Am Telefon: hochkant, mittig, alle Ziele 48px ────────────────────── */
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .dsv { width: 100%; }
  /* Die Karte folgt dem Bild, nicht umgekehrt.
     Die Anweisung sieht am Telefon eine hochkante Karte mit der
     HANDYFASSUNG des Abschnitts vor. Die gibt es als Bild nicht: die
     Registry traegt je Komponente ein Drahtbild des Querformat-Aufbaus
     (120x80). In einem 176x248-Rahmen bleiben davon zwei Wege — zwei
     Drittel abschneiden oder die halbe Karte leer lassen. Beides
     gemessen und angesehen, beides schlechter als eine Karte im
     Seitenverhaeltnis des Bildes. */
  .dsv-karte { width: 200px; }
  .dsv-bild { height: 134px; }
  .dsv-pfeil { width: 48px; height: 48px; }
  .dsv-zu { width: 48px; height: 48px; }
  /* Die Punkte fallen weg. Bei 21 Varianten und 48px Mindestziel waeren
     das acht Zeilen Punkte auf einem 390px-Schirm — mehr Platz als das
     Karussell selbst. Ziehen, Pfeile, Kartentippen und der Zaehler
     bleiben; kein Weg geht verloren. */
  .dsv-punkte { display: none; }
  .dsv-fuss { flex-wrap: wrap; }
  .dsv-fuss .dsv-knopf { flex: 1 1 auto; min-height: 48px; }
  .dsv-zaehler { flex: 1 1 100%; text-align: center; }
}
@media (prefers-reduced-motion: reduce) {
  .dsv-spur, .dsv-karte { transition: none !important; }
}

/* Der Weg zu allen Feldern eines Abschnitts. Eine Zeile, kein zweiter
   Kasten: der schnelle Weg (nur das Angeklickte) bleibt der erste. */
.gruppe-alle {
  width: 100%; margin-top: var(--sp-xs); padding: 8px;
  min-height: 36px; border: 1px dashed var(--glas-linie, var(--border));
  border-radius: var(--r-md); background: transparent;
  font: inherit; font-size: var(--fs-xs); color: var(--text-2, var(--text-muted));
  cursor: pointer; text-align: center;
}
.gruppe-alle:hover { background: var(--glas-fuellung, var(--surface-2)); color: var(--text); }
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) { .gruppe-alle { min-height: 44px; } }

/* ══ Symbole waehlen, indem man sie sieht ════════════════════════════════
   Ein Symbolfeld war eine Auswahlliste mit 82 Namen. Wer "shield-check"
   von "shield" unterscheiden will, muss dort raten. */
.symbolwahl {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(38px, 100%), 1fr));
  gap: 4px; max-height: 168px; overflow-y: auto;
  padding: 6px; border: 1px solid var(--glas-linie, var(--border));
  border-radius: var(--r-md); background: var(--glas-feld, var(--surface));
}
.symbolwahl-k {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 38px; padding: 0; border: 1px solid transparent;
  border-radius: var(--r-sm); background: transparent;
  color: var(--text-2, var(--text-muted)); cursor: pointer;
  font-size: var(--fs-label);
}
.symbolwahl-k:hover { background: var(--glas-fuellung, var(--surface-2)); color: var(--text); }
.symbolwahl-k.an {
  background: var(--akzent, var(--accent)); color: var(--akzent-auf, #fff);
  border-color: transparent;
}
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  .symbolwahl { grid-template-columns: repeat(auto-fill, minmax(min(44px, 100%), 1fr)); max-height: 200px; }
  .symbolwahl-k { min-height: 44px; }
}

/* ══ Abschnitt einfuegen ═════════════════════════════════════════════════
   Die Linie erscheint an der Unterkante des Abschnitts, ueber den man
   faehrt. Sie liegt im Elternfenster ueber dem Rahmen — im Rahmen selbst
   koennte sie nicht klicken. */
.einfuegen {
  position: absolute; left: 0; right: 0; z-index: 44;
  height: 0; border: 0; background: transparent; padding: 0;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; opacity: 0; transition: opacity .14s;
}
.einfuegen::before {
  content: ''; position: absolute; left: 8%; right: 8%; height: 2px;
  background: var(--akzent, var(--accent)); border-radius: 2px;
}
.einfuegen span {
  position: relative; z-index: 1;
  background: var(--akzent, var(--accent)); color: var(--akzent-auf, #fff);
  border-radius: 999px; padding: 6px 14px; min-height: 32px;
  display: inline-flex; align-items: center;
  font-size: var(--fs-xs); font-weight: 650;
  box-shadow: 0 4px 14px rgba(0,0,0,.28);
}
.einfuegen:hover, .einfuegen:focus-visible { opacity: 1; }
/* Ohne diese Zeile muesste man die 2px-Linie treffen. */
.einfuegen { padding: 14px 0; height: auto; transform: translateY(-50%); }
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  /* Am Telefon gibt es kein Ueberfahren — die Linie steht immer da. */
  .einfuegen { opacity: .9; }
  .einfuegen span { min-height: 44px; }
}

/* ── Was soll hierhin? ───────────────────────────────────────────────── */
.gw-hinter {
  position: fixed; inset: 0; z-index: 500; display: flex;
  align-items: center; justify-content: center; padding: var(--sp-lg);
  background: rgba(6,12,16,.55); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px);
}
.gw {
  width: min(720px, 100%); max-height: 88vh; overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-2xl, 22px); box-shadow: 0 24px 60px rgba(0,0,0,.34);
}
[data-glas] .gw {
  background: var(--glas-bloom), var(--glas-scheibe), var(--glas-unterlage-frei, var(--glas-unterlage));
  backdrop-filter: blur(var(--glas-blur-float)) saturate(150%);
  -webkit-backdrop-filter: blur(var(--glas-blur-float)) saturate(150%);
  border-color: var(--glas-kante);
}
.gw-kopf {
  display: flex; align-items: center; gap: var(--sp-md);
  padding: var(--sp-md) var(--sp-lg); border-bottom: 1px solid var(--glas-linie, var(--border));
  font-size: var(--fs-lg); color: var(--text);
}
.gw-kopf b { flex: 1; }
.gw-zu {
  width: 40px; height: 40px; border-radius: var(--r-md); border: 0;
  background: transparent; color: var(--text-2, var(--text-muted)); cursor: pointer; font-size: var(--fs-lg);
}
.gw-zu:hover { background: var(--glas-fuellung, var(--surface-2)); color: var(--text); }
.gw-raster {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr));
  gap: var(--sp-md); padding: var(--sp-lg); overflow-y: auto;
}
.gw-k {
  display: flex; flex-direction: column; gap: 6px;
  padding: 0; border: 0; background: transparent; cursor: pointer; text-align: left;
}
.gw-bild {
  /* `flex: none` — sonst schrumpft das Bild.
     .gw-k ist ein Flex-Behaelter, und ein Flex-Kind hat von Haus aus
     flex-shrink: 1. Im rollenden Raster wurde aus den angeforderten
     150px damit gemessene 22px: ein Streifen, auf dem nichts zu
     erkennen ist. Dieselbe Ursache, die im Anweisungsdokument schon
     einmal die Anfragekarten gestaucht hat. */
  flex: none;
  height: 104px; overflow: hidden; background: #fff;
  border: 1px solid var(--glas-linie, var(--border)); border-radius: var(--r-lg);
}
.gw-k:hover .gw-bild { border-color: var(--akzent, var(--accent)); }
/* `contain`, nicht `cover`: das Vorschaubild ist eine SCHEMATISCHE
   Zeichnung des Aufbaus. Beschnitten fehlen ihr genau die Kanten, an
   denen man sie erkennt — gemessen am Telefon 162x78 aus einem Bild
   im Verhaeltnis 3:2. Der helle Grund traegt die Zeichnung. */
.gw-bild img { width: 100%; height: 100%; object-fit: contain; display: block; }
.gw-z { display: flex; align-items: baseline; gap: 6px; color: var(--text); font-size: var(--fs-xs); font-weight: 650; }
.gw-z em { font-style: normal; font-family: var(--font-mono); font-size: var(--fs-label); color: var(--text-2, var(--text-muted)); }
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  /* Zwei Spalten a 84px zeigen von einem Seitenaufbau nichts mehr —
     gemeldet als "alle Komponenten in einem Fenster zusammengedrueckt und
     nicht erkennbar". Am Telefon EINE Spalte, dafuer ein Bild, auf dem
     man den Aufbau wirklich sieht. Gescrollt wird ohnehin. */
  /* `grid-auto-rows: min-content` — sonst bekommt die Zeile die
     MINDESThoehe des Knopfes (44px) statt der Hoehe seines Inhalts.
     Gemessen: Rasterzeile 44,8px, Inhalt 173px, der Rest lief ueber.
     Ein <button> traegt seine Kinder in der Rasterhoehe nicht von
     selbst. */
  .gw-raster { grid-template-columns: minmax(0, 1fr); grid-auto-rows: min-content;
               gap: var(--sp-sm); padding: var(--sp-md); }
  .gw-bild { height: 150px; }
  .gw-zu { width: 48px; height: 48px; }
  /* Und der Kasten wird ein Bodenblatt: die volle Breite statt eines
     Rahmens, der ringsum Platz verschenkt (gemessen 358 von 390px). */
  .gw-hinter { padding: 0; align-items: flex-end; }
  .gw {
    width: 100%; max-height: 86vh;
    border-radius: var(--r-2xl, 22px) var(--r-2xl, 22px) 0 0;
    border-bottom: 0;
  }
  .gw-kopf::before {
    content: ''; position: absolute; top: 6px; left: 50%; transform: translateX(-50%);
    width: 38px; height: 4px; border-radius: 999px;
    background: var(--line-strong, var(--border-strong));
  }
  .gw-kopf { position: relative; padding-top: 18px; }
}
/* Suche über alle Bausteine und die eigene Kachel der Einbettung (ae-wechseln.js).
   Stand vorher als eingespritzter Stilblock #gw-suche-stil in ae-wechseln.js. */
.gw-hinter .gw { max-height: min(88vh, 100%); }
.gw-suche { position: relative; display: flex; align-items: center; margin: var(--sp-sm, 8px) var(--sp-lg, 16px) 0; flex: none; }
.gw .gw-suche .gw-suche-feld {
  flex: 1; min-width: 0; box-sizing: border-box; height: 44px; min-height: 44px; padding: 0 44px 0 14px;
  font: inherit; font-size: var(--fs-base, 14px); color: var(--text); background: var(--surface-2, var(--surface));
  border: 1px solid var(--border-strong, var(--border)); border-radius: var(--r-lg, 12px);
}
.gw-suche-feld::-webkit-search-cancel-button { display: none; -webkit-appearance: none; }
.gw-suche-feld:focus { outline: 2px solid var(--akzent, var(--accent)); outline-offset: 1px; }
.gw-suche-weg {
  position: absolute; right: 2px; top: 50%; transform: translateY(-50%); width: 44px; height: 44px;
  border: 0; background: transparent; color: var(--text-2, var(--text-muted)); cursor: pointer; font-size: var(--fs-md, 14px);
}
.gw-suche-stand { flex: none; margin: 6px var(--sp-lg, 16px) 0; font-size: var(--fs-xs, 12px); color: var(--text-2, var(--text-muted)); }
.gw-treffer {
  display: flex; flex-direction: column; gap: var(--sp-sm, 8px); padding: var(--sp-md, 12px) var(--sp-lg, 16px) var(--sp-lg, 16px);
  overflow-y: auto; overscroll-behavior: contain; min-height: 0;
}
.gw-treffer[hidden], .gw-raster[hidden], .gw-suche-stand[hidden] { display: none; }
.gw-treffer-zeile {
  display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 12px; align-items: center;
  width: 100%; min-height: 64px; padding: 6px; text-align: left; cursor: pointer; font: inherit; color: var(--text);
  background: transparent; border: 1px solid var(--glas-linie, var(--border)); border-radius: var(--r-lg, 12px);
  /* Die Liste ist eine Flex-Spalte mit begrenzter Höhe: ohne flex-shrink:0 drückte sie bei vielen Treffern die
     Zeilen unter ihre Texthöhe, und die Beschreibungen liefen am Telefon über die nächste Zeile. */
  flex-shrink: 0;
}
.gw-treffer-zeile:hover, .gw-treffer-zeile:focus-visible { border-color: var(--akzent, var(--accent)); }
.gw-treffer-bild { height: 64px; overflow: hidden; background: #fff; border: 1px solid var(--glas-linie, var(--border)); border-radius: var(--r-md, 8px); }
.gw-treffer-bild img { width: 100%; height: 100%; object-fit: contain; display: block; }
.gw-treffer-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.gw-treffer-name { font-size: var(--fs-sm, 13px); font-weight: 650; overflow-wrap: anywhere; }
.gw-treffer-gruppe { font-size: var(--fs-xs, 12px); color: var(--text-2, var(--text-muted)); overflow-wrap: anywhere; }
.gw-treffer-text p {
  margin: 0; font-size: var(--fs-xs, 12px); color: var(--text-2, var(--text-muted));
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.gw-k--besonders .gw-bild { border-style: dashed; border-width: 2px; border-color: var(--akzent, var(--accent)); }
.gw-k--besonders .gw-z { flex-direction: column; align-items: flex-start; gap: 2px; }
.gw-k--besonders .gw-kurz { font-weight: 400; font-size: var(--fs-xs, 12px); color: var(--text-2, var(--text-muted)); overflow-wrap: anywhere; }
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .gw-hinter .gw { max-height: min(86vh, 100%); }
  .gw-suche, .gw-suche-stand { margin-left: var(--sp-md, 12px); margin-right: var(--sp-md, 12px); }
  .gw-treffer { padding-left: var(--sp-md, 12px); padding-right: var(--sp-md, 12px); }
  .gw-treffer-zeile { grid-template-columns: 84px minmax(0, 1fr); }
}

/* ══ Stand der Website im Easy-Modus ═════════════════════════════════════
   Eine Auskunft, keine Handlung — darum steht sie links von den drei
   Oeffnern und traegt keine Akzentfarbe. */
.tb-stand {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: var(--fs-wert);
}
.tb-stand-b {
  display: block; width: 42px; height: 5px; border-radius: 999px;
  background: var(--glas-fuellung-stark, var(--border)); overflow: hidden;
}
.tb-stand-b i { display: block; height: 100%; border-radius: 999px; background: var(--ok); transition: width .2s; }
.tb-stand-b i.knapp { background: var(--danger); }
.tb-stand-b i.gut { background: var(--warn); }
.tb-stand-b i.voll { background: var(--ok); }

.standliste .navliste-inhalt { padding: 6px; }
.standmarke {
  flex: none; width: 22px; height: 22px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-label); font-weight: 700;
}
.standmarke.marke--ja { background: var(--ok-tint); color: var(--ok); }
.standmarke.marke--nein { background: var(--warn-tint); color: var(--warn); }

/* Reihenfolge und Loeschen in der Abschnittsleiste. Das Loeschen traegt
   die Warnfarbe erst beim Ueberfahren — sonst schreit die Leiste. */
.blockleiste .pf-ord { font-size: var(--fs-base); }
.blockleiste .pf-weg:hover { color: var(--danger); }
.einfuegen--oben { z-index: 45; }

/* ══ Seitenliste mit Miniaturen ══════════════════════════════════════════
   Vier Zeilen, die sich nur im Namen unterscheiden, kann man nicht
   wiedererkennen — man muss sie lesen. Die Seite selbst ist das
   Erkennungsmerkmal. */
.navzeile { align-items: center; }
.navmini {
  flex: none; width: 62px; height: 78px; overflow: hidden;
  border: 1px solid var(--glas-linie, var(--border)); border-radius: var(--r-md);
  background: #fff; position: relative;
}
.navmini iframe {
  position: absolute; top: 0; left: 0;
  width: 1200px; height: 1500px; border: 0;
  transform: scale(.052); transform-origin: top left;
  pointer-events: none;
}
.navzeile--hier .navmini { border-color: var(--akzent, var(--accent)); }
@media (max-width: 640px), (orientation: landscape) and (max-height: 500px) {
  .navmini { width: 54px; height: 68px; }
  .navmini iframe { transform: scale(.045); }
}

/* Die vollen Einstellungen im Easy-Modus: derselbe Bildschirm wie im
   Editor, nur schlank gerendert und eingebettet. Kein Nachbau. */
.einst-voll {
  width: 100%; height: min(70vh, 620px); border: 0;
  border-radius: var(--r-lg); background: var(--surface);
  display: block;
}
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) { .einst-voll { height: 62vh; } }

/* Der Einstellungskasten mit den vollen Einstellungen darin. */
.einstliste--weit { width: min(880px, calc(100vw - 4rem)) !important; max-height: min(84vh, 760px) !important; }
.einstliste--weit .einst-voll { height: min(66vh, 640px); }
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  .einstliste--weit { width: calc(100vw - 1rem) !important; }
}

/* ══ §9.11 P1-23 · Einrichtung zuletzt, zugeklappt ════════════════════════
   shop.ejs hat die Begruendung selbst notiert: „Man richtet den Shop einmal
   ein und pflegt danach hundertmal Produkte — die seltene Aufgabe stand
   jeder haeufigen im Weg." Derselbe Fehler stand eine Ansicht weiter in
   den Anfragen.

   Die Regeln standen bis hierhin NUR in shop.ejs. Weil die Anfragen
   dasselbe Muster brauchen, gelten sie jetzt gemeinsam — sonst haetten
   zwei Ansichten zwei Fassungen desselben Kastens (P1-6, P1-19). Die
   alten Namen stehen mit in der Liste, damit shop.ejs unveraendert
   weiterlaeuft. */
.ds-einrichtung, .sh-einrichtung {
  border: 1px solid var(--line, var(--border)); border-radius: var(--r-xl);
  background: var(--surface); margin-top: 18px;
}
.ds-e-kopf, .sh-e-kopf {
  cursor: pointer; list-style: none; display: flex; align-items: center; gap: var(--sp-sm);
  padding: 12px 15px; font-size: var(--fs-md); font-weight: 600; color: var(--ink, var(--text));
}
.ds-e-kopf::-webkit-details-marker, .sh-e-kopf::-webkit-details-marker { display: none; }
.ds-e-kopf::after, .sh-e-kopf::after {
  content: '\25B8'; margin-left: auto; transition: transform .15s;
  color: var(--ink-3, var(--text-muted));
}
.ds-einrichtung[open] > .ds-e-kopf::after,
.sh-einrichtung[open] > .sh-e-kopf::after { transform: rotate(90deg); }
.ds-einrichtung[open] > .ds-e-kopf,
.sh-einrichtung[open] > .sh-e-kopf { border-bottom: 1px solid var(--line, var(--border)); }
.ds-e-sub, .sh-e-sub { font-size: var(--fs-base); font-weight: 400; color: var(--ink-3, var(--text-muted)); }
.ds-e-sub.ist-offen, .sh-e-sub.ist-offen { color: var(--warn-text, #8a6d3b); }
.ds-e-inhalt, .sh-e-inhalt { padding: 14px 15px; }
.ds-e-inhalt .card, .sh-e-inhalt .card { border: 1px solid var(--line, var(--border)); box-shadow: none; }
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) { .ds-e-kopf, .sh-e-kopf { min-height: 48px; } }

/* Der Streifen, der sagt, was noch fehlt. */
.ds-fehlt, .sh-fehlt {
  display: flex; align-items: flex-start; gap: var(--sp-md); flex-wrap: wrap;
  background: var(--warn-tint, var(--warn-bg)); border: 1px solid var(--warn, #e0b352);
  border-radius: var(--r-lg); padding: 12px 14px; margin-bottom: 14px;
}
.ds-fehlt[hidden], .sh-fehlt[hidden] { display: none !important; }
.ds-fehlt-i, .sh-fehlt-i {
  flex: none; width: 18px; height: 18px; border-radius: 999px;
  background: var(--warn, #e0b352); color: var(--signal-auf, #fff); font-weight: 700; font-size: var(--fs-base);
  display: flex; align-items: center; justify-content: center; margin-top: 1px;
}
.ds-fehlt-tx, .sh-fehlt-tx { flex: 1; min-width: 180px; font-size: var(--fs-base); line-height: 1.55; color: var(--ink, var(--text)); }
.ds-fehlt-tx b, .sh-fehlt-tx b { display: block; }

/* ══ §9.10 P2-19 · Eine Eigenschaft, eine Schaltergruppe ══════════════════
   Die Anfragen hatten drei getrennte Knoepfe fuer EINEN Bearbeitungsstand,
   jeder mit fest eingebauten Farben im style-Attribut — und keiner zeigte,
   welcher Stand gerade gilt. Eine Gruppe mit markiertem Ist-Zustand sagt
   beides in einem Blick: was gilt, und was man sonst waehlen kann.

   .seg stand bisher nur in bauplaene.ejs, obwohl admin.css es an zwei
   Stellen schon voraussetzte (Tippziele). Jetzt steht es einmal hier. */
.seg { display: inline-flex; background: var(--surface-2, var(--sunken)); border-radius: var(--radius-sm);
       padding: 2px; gap: var(--sp-3xs); }
.seg button { border: none; background: none; padding: .32rem .65rem;
              border-radius: calc(var(--radius-sm) - 2px); font-size: var(--fs-sm); font-weight: 650;
              cursor: pointer; color: var(--text-muted); font-family: inherit; }
.seg button:hover { color: var(--text); }
/* Zwei Namen fuer denselben Zustand: `.an` kommt aus bauplaene.ejs,
   `[aria-pressed="true"]` ist die Fassung, die auch vorgelesen wird. */
.seg button.an, .seg button[aria-pressed="true"] {
  background: var(--surface); color: var(--text); box-shadow: var(--sh-sm);
}

/* ══ §9.11 P1-24 · Gesperrt heisst gedaempft, nicht unsichtbar ════════════
   `opacity: .5` auf einem gefuellten Knopf senkt BEIDE Ebenen: die Flaeche
   wird heller und die weisse Schrift darauf verschwindet mit. Gemessen am
   gesperrten „Fragen vorschlagen" der Lead-Strecke: rund 1,2 : 1 — das
   sieht nach einem Darstellungsfehler aus, nicht nach einem Zustand.

   Ein gesperrter Knopf muss lesbar bleiben: man muss ja erkennen koennen,
   was man nicht tun kann. Also keine Durchsichtigkeit, sondern eine eigene
   Flaeche mit eigener Schriftfarbe — und der GRUND steht daneben, nicht im
   title-Attribut (am Finger gibt es keinen Mauszeiger, der ihn zeigt). */
.btn:disabled, .knopf:disabled, .ds-btn:disabled,
.btn[aria-disabled="true"], .ds-btn[aria-disabled="true"] {
  opacity: 1;
  background: var(--sunken);
  background-image: none;          /* --grad ist ein Bild, keine Farbe */
  color: var(--text-muted);
  border: 1px solid var(--line, var(--border));
  box-shadow: none;
  transform: none;
  cursor: not-allowed;
}
.btn:disabled:hover, .knopf:disabled:hover, .ds-btn:disabled:hover {
  background: var(--sunken); color: var(--text-muted); border-color: var(--line, var(--border));
}
/* Der Grund neben dem gesperrten Knopf. */
.ds-warum-gesperrt { font-size: var(--fs-xs); color: var(--text-2, var(--text-muted)); line-height: 1.45; margin: .35rem 0 0; }

/* Der Zustandssatz unter einem Feld (§9.11 P2-21). Amber traegt die
   Bedeutung, aber als FARBE auf Prosa — nicht als Versalzeile im
   Feldnamen. Die Farbe allein reicht nicht: der Satz sagt es auch. */
.ds-zustand-hinweis { color: var(--warn-text, var(--text-2, var(--text-muted))); }

/* ══ §9.7 · Die Tafel ════════════════════════════════════════════════════
   Die Seitenliste zeigte neun bis elf Signale je Zeile, immer. Jedes war
   einmal wichtig, also stand es dauernd da. Der Grund, warum sich das
   ueberladen anfuehlt, ist nicht die Gestaltung, sondern die
   Gleichzeitigkeit.

   Die Karte traegt drei Signale — Bild, Name, hoechstens EINE Marke —,
   und ihre Reihenfolge IST die Reihenfolge im Menue. Darum entfaellt die
   eigene Menuevorschau: die Anordnung ist die Vorschau. */
.pages-list-clean {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(208px, 100%), 1fr));
  gap: var(--sp-md, 14px);
  align-items: start;
}
/* Die Trennlinie zu den Pflichtseiten laeuft ueber die ganze Tafel — die
   Gruppierung nach Ort bleibt, auch als Raster. */
.pages-list-clean > .legal-divider { grid-column: 1 / -1; }

.tk {
  position: relative;
  display: flex; flex-direction: column; gap: 8px;
  background: var(--surface); border: 1px solid var(--line, var(--border));
  border-radius: var(--r-xl); padding: 8px;
}
.tk.tk--auf { border-color: var(--accent); }
.tk.is-done .tk-bild::after {
  content: ''; position: absolute; right: 6px; top: 6px; width: 16px; height: 16px;
  border-radius: 999px; background: var(--ok, var(--green));
  box-shadow: 0 0 0 2px var(--surface);
}
.tk-griff {
  position: absolute; left: 2px; top: 6px; z-index: 2;
  color: var(--ink-4, var(--text-light)); cursor: grab; opacity: 0;
  transition: opacity .12s;
}
.tk:hover .tk-griff, .tk:focus-within .tk-griff { opacity: 1; }
/* Am Finger gibt es kein Schweben: dort steht der Griff immer. */
@media (hover: none) { .tk-griff { opacity: 1; } }

.tk-schau {
  display: flex; flex-direction: column; gap: 7px;
  border: 0; background: none; padding: 0; font: inherit; color: inherit;
  text-align: left; cursor: pointer; width: 100%;
}
.tk-bild {
  position: relative; display: block; width: 100%; aspect-ratio: 208 / 124;
  overflow: hidden; border-radius: var(--r-md); background: #fff;
  border: 1px solid var(--line, var(--border));
}
.tk-rahmen {
  position: absolute; top: 0; left: 0; width: 1200px; height: 760px; border: 0;
  transform: scale(.173); transform-origin: top left; pointer-events: none;
}
.tk-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tk .pg-name {
  font-size: var(--fs-md); font-weight: 650; color: var(--ink, var(--text));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tk-marke {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-sm); line-height: 1.5; padding: 1px 7px; border-radius: 999px;
  background: var(--sunken); color: var(--ink-3, var(--text-muted));
}
.tk-marke--test { background: var(--accent-tint); color: var(--accent-strong, var(--accent)); }
.tk-marke--offen { background: var(--warn-tint, var(--warn-bg)); color: var(--warn-text, var(--text)); }
.tk-fuss { display: flex; gap: 6px; }
.tk-fuss .pg-edit { flex: 1; justify-content: center; }

/* ── Das Panel: ein Ort fuer alles Zweitrangige ───────────────────────── */
.tk-panel {
  position: absolute; z-index: var(--z-schwebend, 200);
  left: 0; top: calc(100% + 6px); width: min(356px, calc(100vw - 2rem));
  background: var(--surface); border: 1px solid var(--line, var(--border));
  border-radius: var(--r-xl); box-shadow: var(--shadow-md, var(--sh));
  padding: 12px; display: flex; flex-direction: column; gap: 10px;
}
.tk-panel[hidden] { display: none !important; }
.tk-p-kopf { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.tk-p-name { font-weight: 700; font-size: var(--fs-md); }
.tk-p-kopf .pg-file { font-size: var(--fs-sm); color: var(--ink-4, var(--text-light)); word-break: break-all; }
.tk-p-zu {
  margin-left: auto; border: 0; background: none; font-size: 20px; line-height: 1;
  color: var(--ink-3, var(--text-muted)); cursor: pointer; padding: 0 4px;
}
.tk-p-daten { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: var(--fs-sm); color: var(--ink-3, var(--text-muted)); }
.tk-p-abschnitt { display: flex; flex-direction: column; gap: 5px; }
.tk-p-lbl {
  font-size: var(--fs-label, 11px); text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-4, var(--text-light)); font-weight: 650;
}
/* Eine fehlende Uebersetzung ist eine Handlung, keine Meldung. */
.tk-lang-neu { border-style: dashed; color: var(--accent); }
.tk-lang-neu:hover { background: var(--accent-tint); }

.tk-p-akt { display: flex; flex-direction: column; gap: 2px; border-top: 1px solid var(--line, var(--border)); padding-top: 8px; }
.tk-p-zeile {
  display: flex; align-items: center; gap: 9px; width: 100%;
  border: 0; background: none; font: inherit; font-size: var(--fs-base);
  color: var(--ink, var(--text)); text-align: left; text-decoration: none;
  padding: 8px 8px; border-radius: var(--r-md); cursor: pointer; min-height: 40px;
}
.tk-p-zeile:hover { background: var(--surface-2, var(--sunken)); }
.tk-p-zeile--gefahr { color: var(--danger); }
.tk-p-zeile--gefahr:hover { background: var(--danger-tint, var(--danger-bg)); }
.tk-p-zeile.pg-done.is-on { color: var(--ok, var(--green)); }

/* ══ §9.9 · Die Tafel am Handy ═══════════════════════════════════════════
   Bei 390px koennen die Karten nicht nebeneinander stehen — also werden
   sie Zeilen. Was bleibt, ist das Konzept, nicht das Bild: die Gruppen
   nach Ort bleiben, die Reihenfolge ist weiterhin die des Menues, und die
   Vorschau schrumpft, verschwindet aber nicht — sie ist der schnellste
   Weg, eine Seite wiederzuerkennen, wenn sechs aehnlich heissen. */
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  .pages-list-clean { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .tk { flex-direction: row; align-items: center; gap: 10px; padding: 8px 10px; min-height: 68px; }
  .tk-griff { position: static; opacity: 1; }
  .tk-schau { flex-direction: row; align-items: center; gap: 10px; flex: 1; min-width: 0; }
  .tk-bild { width: 64px; height: 46px; aspect-ratio: auto; flex: none; }
  .tk-rahmen { transform: scale(.053); }
  .tk-fuss { flex: none; }
  .tk-fuss .pg-edit { min-height: 48px; }

  /* Das Panel wird ein Bodenblatt mit Griff. Die Karte bleibt oben
     sichtbar, damit klar ist, worueber man gerade entscheidet. */
  .tk-panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; width: 100%;
    max-height: 76vh; overflow-y: auto;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    border-bottom: 0; box-shadow: 0 -8px 32px rgba(0,0,0,.22);
    padding: 8px 14px calc(14px + env(safe-area-inset-bottom, 0px));
    z-index: var(--z-fenster, 400);
  }
  .tk-panel::before {
    content: ''; display: block; width: 38px; height: 4px; border-radius: 999px;
    background: var(--line-strong, var(--border-strong)); margin: 2px auto 8px;
  }
  /* Die fuenf Nebenaktionen zweispaltig, je 48px — wie im Entwurf. */
  .tk-p-akt { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px; }
  .tk-p-zeile { min-height: 48px; }
  .tk-p-zeile--gefahr { grid-column: 1 / -1; }
  /* Der Grund darunter scrollt nicht mit. */
  body.tk-blatt-auf { overflow: hidden; }
  body.tk-blatt-auf .tk--auf { position: relative; z-index: var(--z-fenster, 400); }
}

/* ══ Entwurf 3l · Pflicht, Fehlt noch, Freiwillig ════════════════════════
   Das Wort steht neben dem Feldnamen, nicht als Sternchen mit einer
   Legende am Seitenende — die es ohnehin nirgends gab. Und es ist ein
   Zustand: dasselbe Feld heisst „Pflicht", wenn etwas drinsteht, und
   „Fehlt noch", wenn nicht.

   Die Farbe traegt die Bedeutung NICHT allein: das Wort sagt es. */
.fl-mark {
  margin-left: .5em; font-weight: 600; letter-spacing: .04em;
  color: var(--ink-4, var(--text-light));
}
.fl-mark:empty { display: none; }
.fl-mark--fehlt { color: var(--warn-text, #8a6d3b); }
/* Der Rand sagt dasselbe noch einmal, fuer den Blick von weiter weg. */
.fi--fehlt { border-color: var(--warn, #e0b352) !important; }
.fi--fehlt:focus { border-color: var(--accent) !important; }

/* Der Hinweis, WO die Angabe erscheint. Prosa, nicht Versalien: er ist
   ein Satz, keine Beschriftung. */
.fhint {
  display: block; margin: .3rem 0 0; font-size: var(--fs-xs);
  line-height: 1.45; color: var(--text-2, var(--text-muted));
}
/* Der leise Hinweis unter dem Link-Feld eines Knopfes ohne Text (knopf-hinweis.js, ae-feldnamen.js). */
.fhint--knopf[hidden], .gruppe-hinweis--knopf[hidden] { display: none !important; }

/* ══ Rechts-Warnung ══════════════════════════════════════════════════════
   Ein Messwerkzeug an und kein Cookie-Hinweis: nicht zulaessig. Die
   Regeln standen in editor-list.ejs; seit die Messwerkzeuge nur noch
   EINMAL vorkommen (P1-16), braucht sie eine zweite Ansicht — also
   stehen sie hier statt zweimal dort. */
.recht-warnung {
  display: flex; align-items: flex-start; gap: var(--sp-md); margin: 0 0 16px;
  padding: 12px 14px; border: 1px solid var(--danger);
  background: var(--danger-tint, var(--danger-bg)); border-radius: var(--r-lg);
}
.recht-warnung[hidden] { display: none !important; }
.recht-warnung-ico {
  flex: none; width: 18px; height: 18px; border-radius: 999px; background: var(--danger);
  color: var(--signal-auf, #fff); font-weight: 700; font-size: var(--fs-base);
  display: flex; align-items: center; justify-content: center; margin-top: 1px;
}
.recht-warnung-tx { flex: 1; min-width: 180px; font-size: var(--fs-sm); line-height: 1.55; color: var(--ink, var(--text)); }
.recht-warnung-tx b { display: block; }

/* Ein Bereich, der aus den Einstellungen eingebettet ist (P1-16). */
.hub-einbett { width: 100%; height: 100%; min-height: 60vh; border: 0; display: block; background: var(--bg); }

/* Lueckentext auf einer ausgelieferten Seite. Nicht gelb wie ein
   Hinweis, sondern rot: [Adresse] im Impressum ist keine Anregung,
   sondern eine Pflichtseite mit einer Luecke. */
.tk-marke--lueckentext {
  background: var(--danger-tint, var(--danger-bg));
  color: var(--danger-text, var(--danger));
  font-weight: 650;
}
/* Zwei Seiten auf derselben Adresse: Eleventy baut die Website dann gar
   nicht mehr. Dieselbe Farbe wie der Lueckentext — es ist kein Hinweis,
   sondern ein Stillstand. */
.tk-marke--doppelt {
  background: var(--danger-tint, var(--danger-bg));
  color: var(--danger-text, var(--danger));
  font-weight: 650;
}
.tk-marke[hidden] { display: none !important; }

/* ══ Der Boden für Eingabefelder ═══════════════════════════════════
   Gemeldet: "Die Eingabefelder sind oft zu klein und funktionieren
   nicht immer gut." Gemessen: Farbzeilen 29px, Sprachwahl 34px,
   Statuswahl 29px, Baupläne 35px. Unter 40px trifft man auf einem
   Telefon nicht zuverlässig, und ein Textfeld wirkt wie ein Strich.

   Kästchen, Schalter, Schieber und Dateiwähler bleiben aussen vor:
   die haben ihre eigene, richtige Größe. Und `min-height` statt
   `height` — ein mehrzeiliges Feld darf wachsen. */
input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]):not([type="hidden"]),
select,
textarea {
  min-height: 40px;
}
/* Ein Textbereich ist mehrzeilig — sein Boden liegt höher. */
textarea { min-height: 72px; }

/* Symbolfeld: Liste und der Weg zum eigenen Bild nebeneinander. Am
   Telefon untereinander — zwei Bedienelemente in einer 390px-Zeile
   ergeben zwei zu schmale. */
.sym-reihe { display: flex; gap: var(--sp-xs, .5rem); align-items: center; flex-wrap: wrap; }
.sym-reihe select { flex: 1 1 200px; min-width: 0; }
.sym-reihe .btn { flex: 0 0 auto; min-height: 44px; }
/* Die Kachel "ohne Symbol" im Assistenten. */
.symbolwahl-k--ohne { color: var(--text-muted); }
.symbolwahl-bild {
  flex: 0 0 auto; min-height: 36px; padding: 0 .7rem; margin-left: auto;
  font-size: var(--fs-xs); font-family: inherit; color: var(--accent);
  background: none; border: 1px solid var(--border); border-radius: var(--r-sm); cursor: pointer;
}
.symbolwahl-bild:hover { background: var(--bg); border-color: var(--accent); }

/* ══ Eigene Farben (ds-farben.js) ═══════════════════════════════
   Die "Benutzerdefinierten Farben" im Windows-Dialog gehoeren dem
   Betriebssystem — eine Webseite kann sie nicht fuellen, und beim
   naechsten Oeffnen sind sie leer. Diese Leiste ist der Ersatz im Panel.

   Zwei Formen, kein Farbunterschied: die Hausfarben der Website sind
   Kreise, die gemerkten sind abgerundete Quadrate. Ueber die Farbe zu
   unterscheiden ginge nicht — die Farbe IST hier der Inhalt. */
.dsf { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.dsf[hidden] { display: none !important; }
.dsf-t {
  font-size: var(--fs-label, 11px); text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-light, var(--text-muted));
}
.dsf-band {
  display: flex; flex-wrap: wrap; align-items: center; gap: 5px;
  /* Achtzehn gemerkte plus neun Hausfarben waeren drei Reihen. In einem
     Kasten, der ueber dem Abschnitt schwebt, schiebt das den Rest vom
     Schirm — darum gedeckelt und scrollbar statt wachsend. */
  max-height: 104px; overflow-y: auto; overscroll-behavior: contain;
}
.dsf-p {
  all: unset; box-sizing: border-box; cursor: pointer; position: relative;
  width: 28px; height: 28px; flex: none; border-radius: var(--r-sm, 7px);
  border: 1px solid var(--line, var(--border));
  /* Ein zweiter Ring innen: sonst verschwindet Weiss auf einer hellen
     Flaeche und Schwarz auf einer dunklen — der Punkt waere unsichtbar,
     aber anklickbar. */
  box-shadow: inset 0 0 0 1px rgba(127, 127, 127, .28);
}
.dsf-p--haus { border-radius: 999px; }
.dsf-p:hover { transform: scale(1.08); }
.dsf-p:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dsf-p.an { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Aufraeum-Modus: die Punkte tun etwas ANDERES als sonst, das muss man
   sehen. Der weisse Schleier mit dunklem Kreuz liegt ueber jeder
   beliebigen Farbe kontrastreich — ein farbiges Zeichen koennte auf der
   Farbe darunter verschwinden. */
.dsf--raeumt .dsf-p:not(.dsf-p--haus)::after {
  content: '\00d7'; position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 16px; line-height: 1; font-weight: 700;
  color: #17161a; background: rgba(255, 255, 255, .84);
  border-radius: inherit;
}
.dsf-verw {
  all: unset; box-sizing: border-box; cursor: pointer; flex: none;
  width: 28px; height: 28px; border-radius: var(--r-sm, 7px);
  border: 1px dashed var(--line, var(--border));
  color: var(--text-muted); display: flex; align-items: center; justify-content: center;
}
.dsf-verw:hover { background: var(--sunken, rgba(127, 127, 127, .13)); color: var(--ink, var(--text)); }
.dsf-verw:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.dsf-verw.an { border-style: solid; border-color: var(--accent); color: var(--accent); }
.dsf-h { font-size: var(--fs-xs); line-height: 1.4; color: var(--text-muted); }
/* In den Farbzeilen steht die Leiste UNTER der Zeile, nicht in ihrer
   Reihe: sonst waere von neun Punkten neben einem 82px-Feld nichts mehr
   zu sehen. Die Zeilen selbst setzen kein `flex-wrap` — hier entsteht
   also kein Streit mit dem Stil in der Ansicht. */
.color-row, .pdz { flex-wrap: wrap; }
.color-row > .dsf, .pdz > .dsf { flex-basis: 100%; }
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  /* Am Telefon wird gezielt, nicht geklickt. */
  .dsf-p, .dsf-verw { width: 36px; height: 36px; }
  .dsf-band { max-height: 132px; gap: 6px; }
}
@media (prefers-reduced-motion: reduce) { .dsf-p:hover { transform: none; } }

/* ══ Web Easy 2 · Kopfleiste und Hilfe-Knopf ═════════════════════════════
   Die Kopfleiste auf /web-easy ist DECKEND — in hell, dunkel und Glas.
   Vorher lag sie als Glasscheibe (zwei halbdurchsichtige Verlaeufe, darunter
   background-color transparent) und deckte nur ueber backdrop-filter: ohne
   Grafikbeschleunigung oder mit ausgeschalteten Transparenzeffekten schimmerte
   beim Rollen das Satzfeld durch (Bild des Inhabers, WE2-10). Eine volle
   Farbe, kein Verlass auf den Filter. Mehr Spezifitaet als [data-glas] .topbar. */
.topbar.we-deckend,
[data-glas] .topbar.we-deckend {
  background-color: var(--glas-deckend, var(--surface, #fff));
  background-image: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
}

/* Der Hilfe-Knopf auf /web-easy: frueher rueckte er hier um die Hoehe der
   klebenden Leiste (--we-leiste-h) nach oben — und lag beim Rollen trotzdem
   ueber Zielkarten, Chips, «ändern» und am Rechner ueber dem Hauptknopf.
   Er sitzt dort jetzt oben in der deckenden Kopfleiste (web-easy.ejs). */

/* ══ Web Easy 2 · Freigabe im Vollbild (Schirm F) ═══════════════════════
   Solange die Freigabe offen ist (web-easy.ejs setzt html.we-fv-offen),
   gehoert der Schirm der Website: Seitenleiste, Menueleiste unten, Kopf-
   leiste, Fortschritt und Hilfe-Knopf weichen. Die Seite selbst rollt
   nicht – nur die Vorschau und das Panel haben eigene Rollbalken (vorher
   musste man fuer jede Stelle der Pruefliste nach oben und wieder nach
   unten rollen). Der Sprunglink «Zum Inhalt» faellt mit weg: der erste
   Tab-Schritt ist «Zurueck zur Auswahl» in der Leiste von F. */
html.we-fv-offen, html.we-fv-offen body { overflow: hidden !important; overscroll-behavior: none; }
html.we-fv-offen .sidebar, html.we-fv-offen .ds-bottomnav, html.we-fv-offen .ds-backdrop,
html.we-fv-offen .ds-skip, html.we-fv-offen .ds-help-fab, html.we-fv-offen body .ds-help-fab,
html.we-fv-offen .main > .topbar, html.we-fv-offen .main > .we-fortschritt {
  display: none !important;
}
html.we-fv-offen .main { margin-left: 0; padding-bottom: 0; min-height: 0; }

/* Die zugeklappte Schublade (unter 900 px links ausserhalb des Bildes) ist
   auch fuer Tastatur und Bildschirmleser zu. Vorher landeten auf /web-easy
   bei 390 px 14 von 25 Tab-Schritten unsichtbar in ihr (x = -199 px).
   visibility erst NACH dem Hinausgleiten (0,28 s wie ds-leiste.css), beim
   Oeffnen sofort sichtbar (.ds-open faellt aus dem :not heraus; .open ist
   der Hamburger in settings.ejs). */
@media (max-width: 900px), (orientation: landscape) and (max-height: 500px) {
  .sidebar:not(.ds-open):not(.open) {
    visibility: hidden;
    transition: transform 0.28s cubic-bezier(0.4,0,0.2,1), visibility 0s linear 0.28s;
  }
}

/* ══ Viele Vorschauen gleichzeitig: Glas ohne Unschaerfe ═════════════════
   Gemessen auf der Galerie von /web-easy (15 echte Vorschau-Iframes):
   42–43 Flaechen trugen backdrop-filter: blur(24px) saturate(150%), und das
   Rollen lief mit 14,4 Bildern je Sekunde am Schreibtisch und 33,8 am
   Telefon. Nicht die Iframes kosten das — mit ausgeblendeten Iframes blieb
   es bei 14,6/37,2. Allein backdrop-filter abgeschaltet: 60,0/60,0.
   Der Grund: jede Scheibe muss bei jedem Bild ihren Hintergrund neu
   unschaerfen, und auf der Galerie liegen 15 bewegte Dokumente darunter.

   Darum verliert die Unschaerfe NUR dort, wo viele Vorschauen zusammen
   stehen — Klasse am body, gesetzt vom Schirm selbst (we-galerie.js setzt
   und nimmt sie beim Schirmwechsel). Die uebrige App behaelt ihr Aussehen.

   Das Aussehen bleibt sonst gleich: dieselben Kanten, dieselben Schatten,
   nur die deckende Flaeche statt der milchigen — genau die Fassung, die
   @supports und prefers-reduced-transparency schon kennen (:1388, :1395).
   Darum hier dieselben Flaechen und dieselbe Farbe --glas-deckend; eine
   Klasse am body liegt in der Spezifitaet ueber [data-glas] <Flaeche>. */
body.we-ohne-glas .card, body.we-ohne-glas .karte, body.we-ohne-glas .hcard,
body.we-ohne-glas .ov-kpi, body.we-ohne-glas .ov-liste, body.we-ohne-glas .we-karte,
body.we-ohne-glas .vcard, body.we-ohne-glas .variant, body.we-ohne-glas .empty-state,
body.we-ohne-glas .settings-nav, body.we-ohne-glas .spanel, body.we-ohne-glas .block-card,
body.we-ohne-glas .navliste, body.we-ohne-glas .hero-card, body.we-ohne-glas .err,
body.we-ohne-glas .seitenkasten, body.we-ohne-glas .sidebar,
body.we-ohne-glas .tabs, body.we-ohne-glas .topbar, body.we-ohne-glas .ds-topbar,
body.we-ohne-glas .del-modal, body.we-ohne-glas .modal-box, body.we-ohne-glas .pv-box,
body.we-ohne-glas .ds-help-panel, body.we-ohne-glas .ov-menue, body.we-ohne-glas .bl-menue,
body.we-ohne-glas .sf-menu, body.we-ohne-glas .ds-switcher-menu,
body.we-ohne-glas .dsv, body.we-ohne-glas .gw,
body.we-ohne-glas .btn-preview, body.we-ohne-glas .ds-kopf--haftend {
  background: var(--glas-deckend, var(--surface));
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
/* Der Zweitknopf: dieselbe Fuellung wie bisher, nur ohne Filter — eine
   deckende Flaeche waere auf der Scheibe ein grauer Kasten (P1-11). */
body.we-ohne-glas .btn:not(.btn-primary):not(.btn-grad):not(.btn-danger):not(.btn-purple),
body.we-ohne-glas .knopf:not(.stark),
body.we-ohne-glas .vp-btn, body.we-ohne-glas .snav-btn, body.we-ohne-glas .btn-reset,
body.we-ohne-glas .modal-close, body.we-ohne-glas .hamburger,
body.we-ohne-glas .theme-select,
body.we-ohne-glas .ds-btn:not(.ds-btn--primary):not(.ds-btn--primaer):not(.ds-btn--leise):not(.ds-btn--strong):not(.ds-btn--gefahr) {
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
/* Die Aura des Grundes (body::before) bleibt: ohne sie waere der Grund flach,
   und gemessen bringt ihr Abschalten fast nichts (13,2–36,5 fps). Es ist
   allein die Unschaerfe, die kostet. */
