
/* E-44 (#106): Kontraste gegen WCAG 2.2 AA nachgemessen (Werte im Commit dokumentiert).
   --rand  #3a4241 → #6d7773 : 1,36:1 → 3,04:1 auf --flaeche (SC 1.4.11 verlangt 3:1)
   --fuss  #6f7674 → #5f6664 : 4,04:1 → 5,11:1 auf der hellen Fläche (SC 1.4.3, 4,5:1)
   --charcoal wird für Rahmen nicht mehr benutzt (2,38:1) – Felder bekommen --rand
   Tabellenkopf: Akzent auf #2e3534 war 4,15:1 → --text mit 10,90:1
   --fokus #7fd8e4: 8,60:1 auf --flaeche; --fokus-hell #0f5259 für die helle Fläche (7,70:1) */
:root{--bg:#222823;--flaeche:#262d2c;--flaeche2:#1b201f;--rand:#6d7773;--text:#EFEFEE;--gedimmt:#9aa3a1;--akzent:#39A2AE;--charcoal:#575A5E;--violett:#30011E;--absatz:#c9cdcc;--fuss:#5f6664;--fokus:#7fd8e4;--fokus-hell:#0f5259;--blase:#2f3d40}
/* Ohne border-box addiert sich jedes Innenabstand-Paar zur Breite: Felder mit width:100% ragten
   aus ihrer Karte, und die Chat-Spalte lief auf schmalen Geräten seitlich über (Fund im Screenshot). */
*,*::before,*::after{box-sizing:border-box}
/* Sichttest 2026-09-01 (Chris, Punkt 7): UI-Skalierung. Die Oberfläche rechnet fast durchgängig in
   rem – deshalb genügt EINE Wurzel-Schriftgröße, um alles mitzuskalieren. Der Server setzt dazu
   die Klasse am <html> (Cookie ui_skala, reine Darstellungspräferenz, POST /ui/skala).
   Ohne Klasse gilt „Automatisch": heutige Größe als Untergrenze, auf großen Bildschirmen sanft
   mehr (clamp; 0.9rem misst hier die Browser-Grundgröße, meist 16 px). Tippziele behalten ihre
   px-Untergrenze (SC 2.5.8) – die darf auch bei 75 % nicht unter 24 px rutschen. */
html{font-size:clamp(16px,0.9rem + 0.2vw,19px)}
html.skala-75{font-size:75%}
html.skala-100{font-size:100%}
html.skala-125{font-size:125%}
html.skala-150{font-size:150%}
body{font-family:'Segoe UI',system-ui,-apple-system,sans-serif;max-width:900px;margin:2rem auto;padding:0 1rem;color:var(--text);background:var(--bg)}
/* #437 Runde 2 (Prüfbefund 3): `--marke-primaer(-text)` als Fläche/Text der Kopfzeile verdrahtet –
   OHNE Marken-Profil (Variable unbelegt) fallen beide Eigenschaften auf ihren bisherigen Wert
   zurück (transparent bzw. geerbte Textfarbe), die Ausgabe bleibt byte-gleich (AK 5). */
header{display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--rand);padding-bottom:.6rem;background:var(--marke-primaer,transparent);color:var(--marke-primaer-text,inherit)}
/* Alles hier statt in style='…'-Attributen: die CSP (default-src 'self', kein 'unsafe-inline')
   verwirft Inline-Stile im Browser – die Wortmarke wäre sonst ein blau/türkis unterstrichener Link. */
.marke{display:flex;align-items:center;gap:.6rem;text-decoration:none;color:inherit}
/* #797: eigenes Logo-Bild tritt an die Stelle des 26×26 px Inline-SVG-Icons (LOGO_SVG) – dieselbe
   Höhe, damit Kopfzeile/Login-Karte nicht springen, Breite frei nach Seitenverhältnis des Bilds. */
.marken-logo{height:26px;width:auto;display:block}
/* Nachschliff #845 (Prüferbefund niedrig): OHNE Deckel wächst ein sehr breites Logo (Rückfall aufs
   volle Bild, wenn kein schmales Kachel-Profil hinterlegt ist, `_branding_logo_html`) mit fester
   Höhe beliebig in die Breite und verdrängt in der schmalen Seitenleiste/Kopfzeile Mandantennamen
   und Schließen-Knopf. `.marke` trägt NUR die kompakte Stelle (Kopfzeile UND Seitenleiste, beide
   `class='marke'`, `_seite`/`_seitenleiste` oben) – die breite Login-Karte (`.mkopf`, volles Logo
   mit eigener Absicht, #797) bleibt bewusst ungedeckelt. `object-fit:contain` verzerrt dabei nicht. */
.marke .marken-logo{max-width:120px;object-fit:contain}
h1{font-size:1rem;margin:0;text-transform:uppercase;letter-spacing:.32em;font-weight:600}
h1 .sub{color:var(--akzent);letter-spacing:.15em;font-weight:400;font-size:.78rem}
h2{font-size:.95rem;text-transform:uppercase;letter-spacing:.12em;margin:.1rem 0 .6rem;border-bottom:2px solid var(--akzent);display:inline-block;padding-bottom:.25rem}
/* #553 (Feed-Reiter Teil 2, AC 1, zweite Hälfte „kein horizontales Scrollen"): Karten zeigen
   Fremdtext, dessen Form niemand vorhersagt – eine 120-stellige URL in einem Karten-Körper oder
   in der Herkunftszeile des Feeds ist ein einziges umbruchfreies Zeichenband und damit breiter
   als die 390 px des schmalen Geräts (#216). Ohne Umbruchregel schiebt ein solches Band die
   GANZE Seite seitlich, nicht nur die Karte.
   `anywhere` statt `break-word`: break-word bricht erst, wenn das Wort allein in seiner Zeile
   noch überläuft, und zählt beim min-content-Maß weiterhin als unteilbar – genau dieses Maß
   macht die Seite breit. `anywhere` senkt es mit.
   Die Regel steht hier auf `.card` und NICHT zusätzlich in der Mobil-Regel weiter unten: so gilt
   sie auf jeder Breite (390 px eingeschlossen) und für jede Karte der Plattform; eine zweite,
   breitenabhängige Kopie wäre dieselbe Aussage doppelt und könnte auseinanderlaufen.
   `.beleg` (Buchungsliste, #204) setzt für sich weiterhin `break-word` mit `min-width` und bleibt
   von der Vererbung unberührt – die dortige Begründung gilt unverändert. */
.card{position:relative;background:var(--flaeche);border:1px solid var(--rand);border-radius:4px;padding:1rem;margin:1rem 0;overflow-wrap:anywhere}
.card::after{content:'';position:absolute;top:-1px;right:-1px;width:22px;height:22px;border-top:2px solid var(--akzent);border-right:2px solid var(--akzent);border-radius:0 4px 0 0;pointer-events:none}
label{display:block;margin:.5rem 0 .2rem;color:var(--gedimmt);font-size:.9rem}
input[type=text],input[type=password],input[type=email],textarea,select{width:100%;padding:.55rem;border:1px solid var(--rand);border-radius:3px;font:inherit;background:var(--flaeche2);color:var(--text)}
/* E-44: sichtbarer Fokus auf ALLEM, was bedienbar ist (SC 2.4.7). Wer `outline` anfasst, schuldet
   3:1 (SC 1.4.11) – deshalb ein eigener, gemessener Ring statt outline:none. */
:focus-visible{outline:3px solid var(--fokus);outline-offset:2px;border-radius:3px}
.hell :focus-visible,.mfuss :focus-visible{outline-color:var(--fokus-hell)}
.hell .mkarte :focus-visible{outline-color:var(--fokus)}
input:focus-visible,textarea:focus-visible,select:focus-visible{border-color:var(--akzent)}
/* Tippziele mindestens 24×24 px (SC 2.5.8) */
button,.btn,a.ikon,label.dateiknopf{min-height:24px;min-width:24px}
/* #553 (Feed-Reiter Teil 2): auf schmalem Mobil-Viewport 44×44 px statt der plattformweiten
   24-px-Untergrenze oben – der Feed ist Wisch-/Daumenbedienung mit Karte-für-Karte-Entscheidungen,
   ein Fehltreffer schaltet unwiderruflich weiter. `.feedaktionen` (Aktionsknöpfe der Karte) und
   `.feed-start` (der „Los"-Knopf im Leerzustand) sind die einzigen Bedienflächen dieses Reiters;
   die Regel bleibt bewusst auf den Feed beschränkt, statt platform­weit auf 44 px zu heben. */
@media(max-width:480px){.feedaktionen button,.feed-start button{min-height:44px;min-width:44px}}
/* Nachbesserung #679 (4): der Palette-Kopf (`.feedpalette>summary`) und ihr Suchfeld
   (`.feedsuche input[type=search]`) sind dieselbe Bedienfläche wie die Aktionsknöpfe oben –
   ohne diese Ergänzung blieb ausgerechnet der Weg IN die Palette hinein unter der Untergrenze.
   Eigene Regel statt Anhängen an die Selektorliste oben, damit deren gepinnte Zeichenkette
   (`tests/test_feed.py::test_leerzustand_traegt_im_markup_die_klasse_der_44px_regel`) unverändert
   bleibt. */
@media(max-width:480px){.feedpalette>summary,.feedsuche input[type=search]{min-height:44px;
min-width:44px}}
/* #669 (Betreuungsübersicht): dieselbe Untergrenze wie beim Feed, aus demselben Grund (SC 2.5.8
   auf schmalem Mobil-Viewport) – Filterleiste (`.betreuungfilterleiste a`) und die Zeilenaktion
   „Ansehen" (`.betreuungzeile a`) sind die einzigen Bedienflächen dieser Seite. `display:inline-block`
   ist Pflicht: auf einer nicht-ersetzten Inline-Box (`<td><a>…</a></td>`) greift `min-height` nicht,
   der Mandantenlink blieb 19 px hoch (Prüferfund der Merge-Runde 23.09., Mobiltest rot). */
@media(max-width:480px){.betreuungfilterleiste a,.betreuungzeile a{display:inline-block;min-height:44px;min-width:44px}}
/* #715 (Nachzug #669): der aktive Filter-Chip (Klasse `an`, gesetzt in `betreuung.py`) trug bisher
   kein CSS und war optisch nicht vom Rest der Leiste zu unterscheiden – `aria-pressed` allein hilft
   nur Screenreader-Nutzern. Invertierte Farben mit den vorhandenen Variablen statt einer neuen
   Akzentfarbe: mindestens so kontrastreich wie `.btn` (E-44), mit vertauschter Fläche/Textfarbe. */
.betreuungfilterleiste .an{background:var(--text);color:var(--bg);border:1px solid var(--text)}
/* #715: visuell verborgen, für Screenreader vorhanden (Standardmuster „sr-only") – bislang gab es
   im Stylesheet keine solche Klasse, obwohl `betreuung.py` sie jetzt für die stumme Kopfzelle über
   „Ansehen" braucht. `display:none`-Inhalte überspringen Screenreader, dieser Text soll aber
   vorgelesen werden. Ohne `position:absolute`: die Klasse steht auch in Scrollcontainern ohne
   positionierten Vorfahren (z. B. `table.vtab`, `app.py` unten), ein absolut positioniertes Element
   würde dort aus dem Container heraus an seinen statischen Dokumentplatz rutschen und die Seite
   waagerecht aufreißen. `clip-path:inset(50%)` blendet den 1×1-Pixel-Kasten trotzdem aus. */
.sr-only{display:inline-block;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);
white-space:nowrap;border:0}
/* #645 (Feed-Aktionen 1): responsives Raster + Command-Palette, sobald mehr Aktionen anstehen als
   `_HAUPT_ANZAHL` (feed.py) flach trägt. Zellen schrumpfen (`minmax(…,1fr)`, `min-width:0`) statt
   die Karte seitlich zu schieben (AC „kein horizontaler Überlauf"). Natives `details`-Element statt
   eines eigenen Auf-/Zuklapp-Skripts — kein JavaScript, die CSP bleibt unverändert. */
.feedaktionen{display:flex;flex-direction:column;gap:.5rem}
.feedpalette{margin-top:.3rem}
.feedpalette>summary{cursor:pointer;list-style:none;width:fit-content}
.feedpalette>summary::-webkit-details-marker{display:none}
.feedsuche{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;margin:.5rem 0}
.feedsuche input[type=search]{flex:1 1 12rem}
.feedraster{display:grid;grid-template-columns:repeat(auto-fill,minmax(9rem,1fr));gap:.5rem;margin-top:.4rem}
.feedraster form{margin:0;min-width:0}
.feedraster button{width:100%}
.kurzbefehl{opacity:.7;font-size:.8em;margin-left:.3rem}
.feedleer{color:var(--gedimmt)}
/* Nur für Hilfsmittel: display:none/visibility:hidden würden Live-Bereiche wirkungslos machen */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
.sprung{position:absolute;left:-9999px;top:0;z-index:50;background:var(--flaeche);color:var(--text);padding:.5rem .9rem;border:1px solid var(--akzent);border-radius:0 0 4px 0}
.sprung:focus{left:0}
select{width:auto;min-width:12rem}
button{padding:.55rem 1.2rem;border:0;border-radius:3px;background:var(--akzent);color:#0d1413;cursor:pointer;font:inherit;font-weight:600;letter-spacing:.06em;text-transform:uppercase;font-size:.85rem}
button:hover{background:#46b7c4}
button:disabled{background:var(--flaeche2);color:var(--gedimmt);border:1px solid var(--rand);cursor:wait}
button.sek,summary.sek,a.btn.sek{background:transparent;border:1px solid var(--rand);color:var(--absatz);text-transform:none;letter-spacing:0;font-weight:400;padding:.35rem .7rem;font-size:.8rem}
button.sek:hover,summary.sek:hover,a.btn.sek:hover{border-color:var(--akzent);color:var(--text);background:transparent}
summary.sek{display:inline-block;border-radius:3px}
button.link{padding:0;border:0;background:none;color:inherit;font:inherit;text-transform:none;letter-spacing:0;text-decoration:underline;cursor:pointer}
button.link:hover{background:none;color:var(--text)}
.inl{display:inline}
.qr{background:#fff;display:inline-block;padding:8px;border-radius:6px}
.stand{font-size:1.6rem;margin:.2rem 0}
.card.warn{border-color:#e0708a}   /* E-32 (#90): Banner Intern-Mandant */
.card.warn::after{border-color:#e0708a}
/* E-37 (#95): Gefahrenbereich der Kontoseite – abgesetzt, roter Rahmen, kein Primärknopf */
.card.gefahr{border-color:#e0708a;margin-top:2.5rem}
.card.gefahr::after{border-color:#e0708a}
.card.gefahr h2{border-bottom-color:#e0708a;color:#e0708a}
/* E-57 (#123, A6): Speicherfristen-Kasten (AGB-Annahme, Konto) – auffällig, aber kein Warnrot */
.fristen{border:1px solid var(--akzent);border-radius:4px;padding:.6rem .9rem;margin:.9rem 0}
.fristen h3{margin:0 0 .3rem;font-size:1rem;letter-spacing:.04em}
.fristen ul{margin:.3rem 0 .3rem 1.2rem;padding:0}
.fristen li{margin:.15rem 0}
/* E-35 (#93): Wiederherstellungscodes – einmalige Anzeige, gut abtippbar */
.codes{border:1px dashed var(--akzent);border-radius:4px;padding:.6rem .9rem;margin:.6rem 0}
.codes ul{list-style:none;margin:.6rem 0;padding:0;display:grid;grid-template-columns:repeat(2,max-content);gap:.3rem 1.4rem}
.codes code{font-size:1.05rem;letter-spacing:.08em}
@media(max-width:640px){.codes ul{grid-template-columns:1fr}}
/* #229: Rückmeldung nach „Als Textdatei speichern“ – Muster #geheim-status, aber mit eigener
   Farbregel: das Statusfeld trägt `klein`, und `.klein{color:var(--gedimmt)}` steht WEITER UNTEN
   im Stilblatt als `.ok`/`.fehler`. Bei gleicher Spezifität (0,1,0) gewinnt die spätere Regel –
   die Bestätigung war deshalb grau statt türkis (Prüferbefund zum verlorenen Vorgänger-Stand).
   Die ID hebt die Spezifität auf (1,1,0) und schlägt `.klein` unabhängig von der Reihenfolge.
   Kontrast: #7fd8c9 9,0:1 auf --bg / 8,4:1 auf --flaeche, #e0708a 4,9:1 bzw. 4,6:1 (SC 1.4.3).
   Solange nichts gemeldet wurde, ist der Live-Bereich unsichtbar – aber NICHT display:none
   (Regel für `:empty` weiter unten, #245). */
#codes-status{display:block;margin-top:.35rem}
#codes-status.ok{color:#7fd8c9}
#codes-status.fehler{color:#e0708a}
/* #183 (Paket „zweifa-seiten"): TOTP-Geheimnis zum Abtippen. Angezeigt wird es in Vierergruppen
   (word-spacing trennt die Gruppen, ohne dass ein Zeichen dazukommt); die Kopiervorlage im
   data-Attribut bleibt das Roh-Geheimnis. Die Gruppen dürfen umbrechen, einzelne Gruppen nicht. */
.geheim{border:1px dashed var(--rand);border-radius:4px;padding:.6rem .9rem;margin:.7rem 0}
.geheim code{display:inline-block;max-width:min(100%,17.5em);background:none;padding:0;font-size:1.15rem;line-height:1.8;letter-spacing:.09em;word-spacing:.5em}
.geheim p{margin:.3rem 0}
#geheim-status{display:block;margin-top:.35rem}      /* Rückmeldung unter den Knopf, nicht um ihn herum */
/* #245: Beide Statusfelder standen leer auf `display:none` – damit war der role=status-Bereich VOR
   der ersten Ansage gar nicht im Accessibility-Baum (gemessen: ignored: notRendered), entgegen
   BARRIEREFREIHEIT 4.1.3 („dauerhaft im DOM stehende, leere Bereiche"). Screenreader melden dann
   den ersten Klick nicht an, weil sie den Live-Bereich erst beim Erscheinen kennenlernen. Statt
   auszublenden wird das leere Feld deshalb nur visuell versteckt – dieselbe Technik wie `.vh`:
   aus dem Fluss genommen und auf 1×1 px geklemmt, also ohne sichtbare Leerzeile im Layout.
   Die Spezifität (1,1,0) schlägt die Grundregeln oben, unabhängig von der Reihenfolge. */
#codes-status:empty,#geheim-status:empty{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
/* Gesperrte Wahlmöglichkeit: gedämpft und mit Marke, damit sie beim Überfliegen nicht gleichrangig
   neben der wählbaren steht (#183, Verfahrenswahl für plattform-admin). */
h2.gesperrt,.hell h2.gesperrt{color:var(--gedimmt)}
.sperrmarke{display:inline-block;border:1px solid var(--rand);border-radius:3px;padding:0 .4rem;margin-right:.4rem;font-size:.72rem;text-transform:uppercase;letter-spacing:.08em;color:var(--gedimmt)}
.fehler{color:#e0708a}.ok{color:#7fd8c9}
/* E2E-Sichttest 2026-08-31 (Chris): Abweisungen (z. B. Betragsgrenzen beim Aufladen) waren als
   .klein-Zeile leicht zu übersehen – generischer Fehler-Banner im Warnrot des Themes (wie .card.warn),
   volle Breite, größere Schrift; :empty hält den leeren Live-Bereich (aria-live) unsichtbar. */
.fehlerbanner{display:block;margin:.6rem 0;padding:.6rem .9rem;border:1px solid #e0708a;border-left:4px solid #e0708a;border-radius:4px;background:rgba(224,112,138,.12);color:#e0708a;font-size:1rem;font-weight:600}
.fehlerbanner:empty{display:none}
.msg{padding:.6rem .8rem;border-radius:4px;margin:.4rem 0;white-space:pre-wrap}
.user{background:rgba(57,162,174,.14);border:1px solid rgba(57,162,174,.35)}
.assistant{background:var(--flaeche2);border:1px solid var(--rand)}
.klein{font-size:.85rem;color:var(--gedimmt)}
a{color:var(--akzent)}
ul.files li{display:flex;justify-content:space-between;gap:1rem;padding:.2rem 0}
code{background:#171c1b;padding:0 .3rem;border-radius:3px;color:#8fd4dc}
.msg.md{white-space:normal}  /* erst nach dem md()-Rendern; vorher/ohne JS bleibt pre-wrap erhalten */
.msg p{margin:.3rem 0}
.msg h3,.msg h4,.msg h5{margin:.5rem 0 .2rem;font-size:1rem;letter-spacing:.05em}
.msg pre{background:#171c1b;border:1px solid var(--rand);border-radius:4px;padding:.5rem;overflow-x:auto;margin:.4rem 0;white-space:pre}
.msg pre:focus-visible{outline:3px solid var(--fokus);outline-offset:1px}
.msg pre code{background:none;padding:0}
.msg table{border-collapse:collapse;margin:.4rem 0;display:block;overflow-x:auto;max-width:100%}
.msg th,.msg td{border:1px solid var(--rand);padding:.25rem .5rem;text-align:left;vertical-align:top}
.msg th{background:#2e3534;color:var(--text);font-weight:600;letter-spacing:.04em}
.msg ul,.msg ol{margin:.3rem 0 .3rem 1.2rem;padding:0}
/* #183 (A4): Zitat im Chat – dieselbe Optik wie im Dokumentenrenderer (.dok blockquote), damit ein
   zitierter Absatz auch als solcher zu erkennen ist und nicht wie eigener Fließtext wirkt. */
.msg blockquote{border-left:3px solid var(--akzent);margin:.4rem 0;padding-left:.6rem;color:var(--gedimmt)}
.msg blockquote p{margin:0}
.msg a{color:var(--akzent)}
/* #204: Belegspalte der Buchungsliste (/konto). Gekürzt wird EINMAL, und zwar auf dem Server
   (`BELEG_MAX` + „…“); die Spalte schneidet nicht noch einmal nach, sondern bricht um. Vorher
   lagen zwei unabhängige Schnitte übereinander – der harte bei 28 Zeichen und die Spaltenkante –
   und keiner der beiden war als Kürzung zu erkennen. Der volle Text steht im `title`.
   `min-width` gemessen bei 390 px (Hands-on 2026-09-02): ohne sie drückte der Tabellenumbruch die
   Spalte auf drei Zeichen Breite und zerlegte „Gutschrift“ in „Gutsc/hrift“ – schlimmer als der
   Schnitt, den dieses Paket abräumt. Und `break-word` statt `anywhere`: getrennt wird nur ein
   Wort, das für sich zu lang ist, nicht an jeder beliebigen Stelle. */
.beleg{white-space:normal;overflow-wrap:break-word;min-width:11rem;max-width:22rem}
/* E-44 (#106): Die alte Dauerschleife erfüllte alle drei Bedingungen von SC 2.2.2 (Level A):
   startet selbst, läuft länger als 5 s, steht neben bedienbarem Inhalt. Jetzt statischer Text;
   die dezente Pulsation ist reine Zugabe und fällt bei prefers-reduced-motion weg. */
.tippt::after{content:'Antwort wird erzeugt …';color:var(--gedimmt);font-size:.9rem}
@media(prefers-reduced-motion:no-preference){.tippt::after{animation:pulsiert 1.6s ease-in-out infinite}}
@keyframes pulsiert{0%,100%{opacity:.55}50%{opacity:1}}
@media(max-width:640px){body{margin:.8rem auto}header{flex-wrap:wrap;gap:.3rem}.card{padding:.7rem}textarea{min-height:5rem}}
/* ---------------------------------------------------------------- E-43 (#105): Chat-Oberfläche
   Zentrierte Spalte für Nachrichten UND Eingabefeld in derselben Breite – sobald beide
   unterschiedlich breit sind, wirkt eine Chat-Oberfläche gebastelt. Rollen-Asymmetrie:
   die Frage bekommt eine Blase, die Antwort läuft als Fließtext. Das ist das eine Merkmal, an dem
   ChatGPT und Claude sofort erkennbar sind.
   Sichttest 2026-09-01 (Chris, Befund 1): Die Spalte stand auf 44rem – auf breiten Schirmen
   quetschten sich Knöpfe und Aufwand-Regler in der Werkzeugzeile, obwohl daneben Platz frei war.
   Jetzt EINE Variable für alle vier Stellen (Strom, Eingabebox, Leerzustand, Banner); wer die
   Breite ändert, ändert sie an einer Stelle und nie wieder verstreut. */
:root{--spaltenbreite:56rem}
/* #437 Runde 2 (Prüfbefund 3): `--marke-hintergrund(-text)` als Fläche/Text der Chat-Schale –
   Rückfall auf die bisherigen Werte (--bg/--text) ohne Profil, byte-gleich (AK 5). */
body.chat{max-width:none;margin:0;padding:0;height:100dvh;overflow:hidden;background:var(--marke-hintergrund,var(--bg));color:var(--marke-hintergrund-text,var(--text))}
.rahmen{display:grid;grid-template-columns:16rem 1fr;height:100dvh}
/* Eine Spalte, sobald die Seitenleiste aus dem Fluss ist: bei `display:none` würde `.haupt`
   sonst zum ERSTEN Rasterelement und landete in der 0-Pixel-Spalte (Fund im Screenshot). */
body.chat.zu .rahmen{grid-template-columns:1fr}
/* #437 Runde 2 (Prüfbefund 3): `--marke-sekundaer(-text)` als Fläche/Text der Navigation
   (Nebenflächen, s. store.py BRANDING_FARB_FELDER) – Rückfall auf --flaeche2/--text, byte-gleich
   ohne Profil (AK 5). */
.seitenleiste{background:var(--marke-sekundaer,var(--flaeche2));color:var(--marke-sekundaer-text,var(--text));border-right:1px solid var(--rand);display:flex;flex-direction:column;min-width:0;overflow:hidden}
body.chat.zu .seitenleiste{display:none}
body.chat:not(.zu) .slauf{display:none}
.slkopf{display:flex;align-items:center;justify-content:space-between;gap:.4rem;padding:.9rem .8rem .6rem}
.slkopf .marke h1{font-size:.8rem;letter-spacing:.24em}
.slliste{list-style:none;margin:0;padding:0 .5rem 1rem;overflow-y:auto;flex:1}
.slliste li{display:flex;align-items:center;gap:.3rem;border-radius:4px;padding:.1rem .2rem}
.slliste li:hover{background:var(--flaeche)}
.slliste li a{flex:1;display:block;padding:.4rem .3rem;color:var(--absatz);text-decoration:none;font-size:.9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.slliste li.aktiv{background:var(--flaeche)}
.slliste li.aktiv a{color:var(--text)}
.slgruppe{font-size:.75rem;text-transform:uppercase;letter-spacing:.14em;color:var(--gedimmt);margin:.9rem .5rem .2rem;border:0;padding:0;display:block}
/* rem statt px (Punkt 7): skaliert mit der Wurzelgröße; die 24-px-Untergrenze oben bleibt */
.ikon{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;padding:0;border:1px solid var(--rand);border-radius:4px;background:transparent;color:var(--absatz);font-size:1rem;line-height:1;text-decoration:none}
.ikon:hover{border-color:var(--akzent);color:var(--text);background:transparent}
.neu{display:block;margin:0 .8rem .4rem;text-align:center}
.haupt{display:flex;flex-direction:column;min-width:0;min-height:0}
/* #437 Runde 2 (Prüfbefund 3): dieselbe Kopf-Fläche wie `header` oben, plus `--marke-akzent` als
   Rahmenfarbe (Hervorhebung, s. store.py BRANDING_FARB_FELDER) – Rückfall auf --rand, byte-gleich
   ohne Profil (AK 5). */
.chatkopf{display:flex;align-items:center;justify-content:space-between;gap:.6rem;padding:.6rem 1rem;border-bottom:1px solid var(--marke-akzent,var(--rand));flex:0 0 auto;background:var(--marke-primaer,transparent);color:var(--marke-primaer-text,inherit)}
/* #234 (Runde 3, gemessen 2026-09-10): `position:relative` ist kein Beiwerk. Ohne einen
   positionierten Vorfahren misst sich ein `position:absolute`-Kind des Stroms an der Seite
   statt am Strom – die versteckte Überschrift „Chatverlauf" (`h2.vh`, 1 px) landete dadurch
   an ihrer Stelle im GESCROLLTEN Inhalt auf der Seite (bei 844×390 gemessen y=431) und machte
   die Seite 42 px länger als das Fenster, obwohl sichtbar nichts unter der Kante stand. Mit
   der Regel bleibt der Überhang im Strom, wo er hingehört. */
.strom{position:relative;flex:1;overflow-y:auto;overscroll-behavior:contain}
.spalte{max-width:var(--spaltenbreite);margin:0 auto;padding:1.2rem 1rem 0}
.eingabezone{flex:0 0 auto;position:relative;padding:.4rem 1rem 1rem}
.eingabebox{max-width:var(--spaltenbreite);margin:0 auto;border:1px solid var(--rand);border-radius:12px;background:var(--flaeche);padding:.6rem .7rem}
.eingabebox:focus-within{border-color:var(--akzent)}
#nachricht{border:0;background:transparent;padding:.2rem;min-height:4rem;max-height:12rem;resize:none;width:100%;color:var(--text);font:inherit}
#nachricht:focus,#nachricht:focus-visible{outline:none;border:0}
/* Sichttest 2026-09-01 (Chris, Punkt 3): Die Werkzeugzeile brach auf dem Desktop in ZWEI Zeilen um –
   das drückte das Eingabefeld nach oben und wirkte unaufgeräumt. Deshalb stand hier `nowrap`.
   Chris-Sichttest 2026-09-02 (Betreiber-Konto dipsitter, VOLLES Modul-Set): `nowrap` löst das
   Problem nicht, es verschiebt es. Mit allen sieben Pilotmodulen trägt die Zeile Datei anhängen ·
   Diktieren · Dateiauswahl · Dateien verwalten · Als Datei speichern · Arbeitsdaten löschen ·
   Aufwand-Regler · Kontextfenster · Senden – gemessen 1136 px Bedarf in einer 994 px breiten Box.
   Was nicht umbrechen darf, LÄUFT DANN HINAUS: gemessen 301 px über die Eingabebox bei 1920 px
   Fensterbreite, 465 px bei 1024 px – dort lag der Senden-Knopf bei x=1375 in einem 1024 px
   breiten Fenster, also vollständig außerhalb des Sichtfelds, und die Dateiauswahl war auf 15 px
   zusammengequetscht. Die Einzeiligkeit war eine Zusage für ein Zwei-Modul-Set; bei sieben
   Modulen gibt es keine Zeile, in die alles passt. Also: umbrechen, wenn es sonst nicht passt
   (solange es passt, bleibt die Zeile von selbst einzeilig – der Umbruch kostet nichts, solange
   nichts umbricht), und NICHTS ausblenden, was heute sichtbar ist.
   Die Reihenfolge im DOM ist zugleich die Lesereihenfolge und die Tab-Reihenfolge – kein `order`,
   keine Umsortierung: Bedienknöpfe zuerst, danach Regler, Kontextfenster, Senden. Die Lücke bzw.
   die auto-Marge am Kontextfenster hält Kontextfenster und Senden am rechten Rand ihrer Zeile. */
/* position:relative: die Werkzeugzeile ist der ANKER der Menüpanels – siehe .menupanel unten. */
.werkzeuge{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem;padding-top:.4rem;position:relative}
.werkzeuge>*{flex:0 0 auto}
.rollezeile{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem .6rem;padding-top:.35rem}.rollezeile>p{margin:0;flex:1 1 14rem;min-width:0}   /* #954: Hinweis + Knopf der Rollenwahl in EIGENER Zeile unter .werkzeuge (dort flex:0 0 auto – der Satz lief auf 390 px hinaus) */
/* Auswahlfelder: `min-width:0` ließ die Dateiauswahl auf 15 px schrumpfen (Messung oben) – ein
   Auswahlfeld ohne lesbaren Inhalt ist kein Bedienelement mehr. Der zweite Teil ist die
   flex-Basis: In einer umbrechenden Zeile hilft Schrumpfen NICHT – der Browser bricht um, bevor
   er schrumpft, denn er verteilt nach der WUNSCHbreite (hier: der Optionstext, gedeckelt auf
   14 rem). Zwei Felder à 14 rem allein sind über die Hälfte der Box. Mit der kleinen Basis
   (7 rem) plant der Umbruch mit dem schmalen Feld, und der freie Rest der Zeile wächst hinterher
   wieder hinein (flex-grow, gedeckelt auf 14 rem): schmales Modul-Set = eine Zeile mit breiten
   Feldern, volles Modul-Set = Umbruch statt „– keine…". */
.werkzeuge select{width:auto;min-width:7rem;max-width:14rem;flex:1 1 7rem;text-overflow:ellipsis;font-size:.82rem;padding:.3rem .4rem;background:var(--flaeche2)}
.werkzeuge .luecke{flex:1 1 .2rem;min-width:0}
/* Prüferbefund 2026-09-02 (#183-Reihe): OHNE JavaScript steht statt des Reglers die Hülle
   `#effort-fallback` mit dem Prüftiefe-Select in der Zeile. Sie ist ein Flex-Element ohne
   Deckel, und die Mobil-/Skala-Regel `.werkzeuge select{max-width:100%}` bezieht die 100 %
   auf eben diese Hülle – also auf nichts. Ergebnis: die Hülle wuchs auf die Breite der
   längsten Option („Gründlich – volles Prüfprotokoll, höchster Verbrauch", gemessen 520 px)
   und lief aus der Eingabebox hinaus: 196 px bei 390 px Fensterbreite und 150 % Schrift, wo
   „Senden" dann unter dem Gesprächsverlauf lag und nicht mehr klickbar war; 70 px bei 900 px.
   Der Deckel an der Hülle schließt die Lücke (mit JavaScript ist sie ohnehin versteckt). */
.werkzeuge #effort-fallback{min-width:0;max-width:100%}
/* Der Schluss der Zeile (Kontextfenster + „Senden") ist EIN Element: er bricht als Ganzes um und
   sitzt über `margin-left:auto` am rechten Rand SEINER Zeile – auch wenn die Lücke eine Zeile
   höher liegt. Ohne die Hülle brach „Senden" bei 1024 px allein in eine dritte Zeile und stand
   dort links unter den Knöpfen (gemessen im Sichttest 2026-09-02). */
.werkzeuge .schluss{display:inline-flex;align-items:center;gap:.35rem;flex:0 0 auto;margin-left:auto}
.werkzeuge .dateiknopf,.werkzeuge>button,.werkzeuge>.schluss>button,.werkzeuge>details.menu>summary{white-space:nowrap}
/* Der Sprachstatus ist der einzige freie Text in der Zeile – er darf sie nicht sprengen (#156) */
.werkzeuge #sprach-status{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Punkt 3: „Dateien verwalten" bleibt dauerhaft sichtbarer Knopf auf Eingabehöhe. Vorher machte
   `details.menu[open]{position:absolute}` das GANZE details absolut – der Knopf verschwand beim
   Aufklappen aus der Zeile. Jetzt schwebt nur das Panel (über der Eingabezone, nach oben).
   #183: Das details ist dabei `static`, NICHT `relative` – der Anker des Panels ist die
   Werkzeugzeile, nicht der einzelne Knopf. Am Knopf verankert lief das Panel aus dem Fenster,
   sobald der Knopf weit rechts steht: „Als Datei speichern" ragte bei 390 px um 129 px hinaus,
   „Als Excel-Datei" lag ganz außerhalb des Sichtfelds und war ohne Querscrollen nicht
   auslösbar (am Desktop 36 px, Satz mitten im Wort abgeschnitten). An der Zeile verankert und
   auf ihre Breite gedeckelt (`width:min(34rem,100%)`) kann kein Panel mehr über die
   Fensterkante hinauslaufen – unabhängig davon, wo sein Knopf in der Zeile sitzt und ob die
   Zeile umbricht. Das details bleibt im Fluss, der Knopf also sichtbar (Punkt 3 oben). */
.werkzeuge>details.menu[open]{position:static;right:auto;z-index:auto;background:none;border:0;border-radius:0;padding:0;width:auto}
.werkzeuge details.menu>.menupanel{position:absolute;bottom:calc(100% + .5rem);left:0;z-index:25;width:min(34rem,100%);max-height:min(60vh,30rem);overflow:auto;background:var(--flaeche);border:1px solid var(--rand);border-radius:8px;padding:.7rem .8rem;box-shadow:0 6px 22px rgba(0,0,0,.45)}
.dateiknopf{display:inline-flex;align-items:center;gap:.3rem;margin:0;padding:.35rem .7rem;border:1px solid var(--rand);border-radius:4px;color:var(--absatz);font-size:.8rem;cursor:pointer}
.dateiknopf:hover{border-color:var(--akzent);color:var(--text)}
/* opacity statt display:none – ein verstecktes Dateifeld muss für Hilfsmittel bedienbar bleiben (#106) */
/* #234 (Runde 3, Nachzugspunkt aus der Abnahme Runde 2): Wer mit der Tastatur auf das
   Dateifeld springt, soll den A6-Fristensatz MITGEBRACHT bekommen. Das Feld selbst ist 1 px
   groß; ohne Rand scrollte der Browser genau dieses eine Pixel ins Bild und der Satz darunter
   bliebe unter der Kante. `scroll-margin-bottom` deckt den Rechtsfuß ab (gemessen 89–288 px,
   je nach Breite). In den gemessenen Lagen greift es gar nicht – dort steht der Satz ohnehin
   im Startbild (Höhen-Query oben) und die Seite hat nichts zu scrollen; es ist die
   Absicherung für alles daneben (große Schriftgrade, Zoom, Zeilenumbruch). Weil keine Lage es
   ausübt, hält der Test es doppelt fest: als Wert dieser CSS und als Wert, den der Browser am
   Feld berechnet, gehalten gegen den Abstand vom Knopf zum Ende des A6-Satzes
   (tests/test_strom_hoehe.py, Prüferbefund Runde 3). */
.dateiknopf input[type=file]{position:absolute;width:1px;height:1px;opacity:0;scroll-margin-bottom:12rem}
.dateiknopf:focus-within{outline:3px solid var(--fokus);outline-offset:2px}
/* #151 Pilotmodul upload-komfort: Drop-Overlay über der Eingabezone, Anhang-Chip, Inline-Meldung */
/* #183 (P2): Das Signal war kleiner als die Wirkfläche – das Overlay lag nur über der Eingabezone
   (y 646–900), der `drop`-Empfänger ist aber das ganze Dokument. Wer oben im Verlauf loslässt,
   lädt trotzdem hoch; wer die gestrichelte Fläche für die einzige Zielzone hält, zielt umsonst.
   Jetzt deckt das Overlay die ganze Arbeitsfläche (position:fixed) und dunkelt sie ab. */
#drop-overlay{position:fixed;inset:0;z-index:40;display:flex;align-items:center;justify-content:center;border:3px dashed var(--akzent);background:rgba(27,32,31,.92);font-weight:600;font-size:1.1rem;text-align:center;padding:1rem;pointer-events:none}
#drop-overlay[hidden]{display:none}
/* Chris-Sichttest Runde 2 (#151): Die Chip-Zeile sitzt IN der Eingabebox über dem Textfeld (Claude-
   Vorbild) – vorher stand sie in der Eingabezone daneben und rutschte an den linken Seitenrand.
   Die Breite kommt damit von der Box; der Chip selbst bleibt eine Pille mit Vorschau, Name,
   flüchtigem Status und Abwählen-X. */
#anhang-chips{display:flex;flex-wrap:wrap;gap:.4rem;margin:0 0 .45rem}
#anhang-chips:empty{display:none}
.anhang-chip{display:inline-flex;align-items:center;gap:.45rem;max-width:100%;padding:.25rem .35rem .25rem .45rem;border:1px solid var(--rand);border-radius:10px;background:var(--flaeche2);font-size:.82rem}
/* Mini-Vorschau: ~40 px, beschnitten statt verzerrt (object-fit), abgerundet – Quelle ist
   GET /datei/{sha} auf demselben Host (CSP img-src 'self'; blob:-URLs wären verboten). */
.anhang-chip .chipbild{flex:0 0 auto;width:2.5rem;height:2.5rem;object-fit:cover;border-radius:6px;background:var(--flaeche)}
.anhang-chip .chipsymbol{flex:0 0 auto;font-size:1.15rem;line-height:1;padding:0 .1rem}
.anhang-chip .chipname{max-width:16rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.anhang-chip .chipstatus{color:var(--absatz)}
.anhang-chip .chipstatus:empty{display:none}
/* Sichttest 2026-09-01 (Chris, Punkt 1): „angehängt" hat seine Aufgabe nach wenigen Sekunden
   erfüllt – der Text blendet dann langsam aus; Chip und X bleiben. Die Klasse setzt upload.js,
   die Zeit steckt in der Animation (4 s Verzögerung, 1,2 s Überblendung). Bei
   prefers-reduced-motion bleibt der Text schlicht stehen – kein Bewegungsreiz. */
@media(prefers-reduced-motion:no-preference){
  .anhang-chip .chipstatus.verblasst{animation:chipverblasst 1.2s ease-in 4s forwards}
}
@keyframes chipverblasst{from{opacity:1}to{opacity:0}}
/* Das X ist mit JavaScript ein Knopf, im Server-Chip ohne JavaScript ein Link auf dieselbe Seite
   ohne `datei`-Parameter – beide sehen gleich aus. */
.anhang-chip .chipx{display:inline-flex;align-items:center;padding:0 .45rem;background:none;border:0;color:var(--absatz);font-size:1rem;line-height:1.4;text-decoration:none;cursor:pointer}
.anhang-chip .chipx:hover{color:var(--text)}
/* #156 Spracheingabe-Pilot: Mikro-Knopf – der Aufnahme-Zustand muss unübersehbar sein (pulsiert) */
.mikro.auf{border-color:#e0708a;color:#e0708a;animation:mikropuls 1.2s ease-in-out infinite}
@keyframes mikropuls{0%,100%{opacity:1}50%{opacity:.45}}
#sprach-status{font-size:.78rem;color:var(--gedimmt)}
#sprach-status.fehler{color:#e0708a}
/* Sichttest 2026-09-01 (Chris, Punkt 2): „Transkribiere…" braucht ein Lebenszeichen – ein reiner
   CSS-Spinner aus einem Rahmen (kein Bild, kein Emoji, CSP-fest). Wer Bewegung abbestellt hat
   (prefers-reduced-motion), bekommt statt der Drehung schlicht drei Punkte. */
#sprach-status.laeuft::before{content:'';display:inline-block;width:.8rem;height:.8rem;margin-right:.4rem;vertical-align:-.1rem;border:2px solid var(--rand);border-top-color:var(--akzent);border-radius:50%;animation:sprachdreht .9s linear infinite}
@media(prefers-reduced-motion:reduce){#sprach-status.laeuft::before{content:'…';width:auto;height:auto;border:0;animation:none;color:var(--akzent)}}
@keyframes sprachdreht{to{transform:rotate(360deg)}}
.runter{position:absolute;left:50%;top:-1.1rem;transform:translateX(-50%);width:2.15rem;height:2.15rem;padding:0;border-radius:50%;background:var(--flaeche);border:1px solid var(--rand);color:var(--text);font-size:1rem;z-index:5}
/* Sichttest 2026-09-01 (Chris, Punkt 6): Die Fußzeile stand in 44 rem und lief dadurch über viele
   Zeilen. Sie nutzt jetzt die volle Breite der Eingabespalte, mit kleinerem Grad und straffer
   Zeilenhöhe – ausschließlich Layout: die Wortlaute (Fristen, Art. 9/10, KI-Hinweis, Disclaimer)
   sind rechtlich abgestimmt und bleiben zeichengenau (Test pinnt sie). */
.chatfuss{max-width:none;margin:.3rem auto 0;font-size:.72rem;line-height:1.4;color:var(--gedimmt);text-align:center}
.eingabezone .chatfuss{padding:0 .4rem}
/* Rollen-Asymmetrie: Frage in der Blase, Antwort ohne Kasten */
.chat .msg{padding:0;border:0;background:none;border-radius:0;margin:1.1rem 0}
.chat .msg.user{background:var(--blase);border:1px solid var(--rand);border-radius:14px 14px 4px 14px;padding:.6rem .9rem;margin-left:auto;width:fit-content;max-width:85%}
.chat .msg.assistant{background:none;border:0;padding:0;color:var(--absatz)}
/* #183 (F6): Fehlgeschlagener Auftrag – dieselbe Warnfarbe wie .fehlerbanner, damit die Karte auf
   den ersten Blick keine Antwort ist. MUSS hier stehen und nicht oben bei .msg: `.chat .msg` und
   `.chat .msg.assistant` (beide 0,3,0) räumen Rahmen und Grund ab und stehen weiter unten – eine
   Regel `.msg.fehlerkarte` (0,2,0) im allgemeinen Block bliebe wirkungslos (gemessen: border 0px,
   Grund transparent). Der Grund bindet Fehlerzeile UND „Jetzt anmelden" zu EINER Karte. */
.chat .msg.fehlerkarte{background:rgba(224,112,138,.08);border-radius:4px;padding:.1rem .7rem .5rem}
.chat .msg h3{margin:0;font-size:inherit;font-weight:inherit;letter-spacing:0;text-transform:none;color:inherit}
.aktionen{display:flex;gap:.4rem;margin:.3rem 0 0;opacity:.75}
.aktionen:hover,.aktionen:focus-within{opacity:1}
/* #216: Der Leerzustand stand auf FESTEN 20vh Kopfpolster – ein Maß am Fenster, nicht am Platz,
   den der Strom übrig hat. Bei 390 px (gemessen 2026-09-03: Strom 339 px hoch, Banner 140 px,
   20vh = 169 px) rutschte „Womit können wir helfen?" auf y=448 in einem Bereich, der bei 458
   endet – und weil app.js beim Bestätigungsbanner auf `top:0` springt (#183), blieb sie dort:
   optisch hinter der Eingabebox, die Beispiel-Pillen ganz weg. Jetzt misst der Leerzustand am
   ÜBRIGEN Platz: er wächst auf die Resthöhe des Stroms (`flex:1 0 auto` – wachsen ja, unter die
   eigene Inhaltshöhe schrumpfen nie) und zentriert seinen Inhalt darin. Ein Banner nimmt dem
   Rest Platz weg, statt die Überschrift hinauszuschieben; reicht es nicht, scrollt der Strom.
   `:has()` hält die Flex-Spalte auf den Leerzustand beschränkt – der Gesprächsstrom bleibt
   unverändert ein Blockfluss. Ohne `:has()`-Unterstützung steht der Leerzustand oben statt
   mittig: weniger hübsch, aber nie verdeckt (Prüfer 2026-09-03 mit abgeschalteter Regel
   nachgemessen – bei 390 und 1280 px, mit und ohne Banner, überall sichtbar).
   `width:100%` an der Spalte ist kein Beiwerk (Prüferbefund 2026-09-03): In einer Flex-Spalte
   dehnt sich ein Kind mit `margin:0 auto` NICHT auf die Breite des Elternteils, sondern
   schrumpft auf seinen Inhalt. Ein kurzes Bestätigungsbanner stand dadurch als schmales
   Kärtchen mitten im Strom statt über die Spaltenbreite (gemessen: 109 statt 916 px bei
   1280 px) – die eine Spaltenbreite aus `--spaltenbreite` gilt weiter für alle vier Stellen. */
.leer{max-width:var(--spaltenbreite);margin:0 auto;padding:1.5rem 1rem 2rem;text-align:center;
      display:flex;flex-direction:column;justify-content:center}
.strom:has(>.leer){display:flex;flex-direction:column}
.strom>.leer{flex:1 0 auto}
.strom>.spalte{flex:0 0 auto;width:100%}
.leer h2{border:0;display:block;font-family:Georgia,'Times New Roman',serif;font-size:1.6rem;text-transform:none;letter-spacing:0;margin:0 0 1.2rem}
.pillen{display:flex;flex-wrap:wrap;gap:.5rem;justify-content:center}
.pille{background:var(--flaeche);border:1px solid var(--rand);border-radius:999px;color:var(--absatz);font-size:.85rem;padding:.45rem .9rem;text-transform:none;letter-spacing:0;font-weight:400}
.pille:hover{border-color:var(--akzent);color:var(--text);background:var(--flaeche)}
.chatbanner{max-width:var(--spaltenbreite);margin:0 auto}
/* Sichttest 2026-09-01 (Befund 2): Zeile unter der Antwort, wenn während des Gesprächs eine Datei
   entstanden ist – dezent, aber anklickbar (öffnet GET /datei/{sha} in einem neuen Tab). */
.neuedatei{margin:.5rem 0 0;font-size:.82rem;color:var(--gedimmt)}
.neuedatei a{margin-left:.15rem}
/* #234 (Runde 3, 2026-09-10): Der Block gilt seit Chris' Entscheidung „Variante A" auch für
   FLACHE Fenster bis 1100 px Breite – das Querformat-Handy (844×390) ist 4 px breiter als die
   840er Schwelle und bekam deshalb die Desktop-Seitenleiste ins Raster, die von 844 px Breite
   256 px nahm. Die Chatspalte blieb mit 588 px so schmal, dass der Rechtsfuß auf 194 px
   umbrach (gemessen); ohne die Leiste im Raster sind es 101 px, und genau diese ~90 px
   entscheiden darüber, ob der A6-Fristensatz im Startbild steht (siehe Höhen-Query unten).
   Die 1100 px sind die Grenze zum echten Desktop: breite flache Fenster (1280×530) behalten
   ihre Seitenleiste im Raster, sie haben die Breite dafür. Dieselbe Bedingung steht im Skript
   (`matchMedia`), das die Leiste beim Laden zuklappt – sonst deckte sie hier das Gespräch ab. */
@media(max-width:840px),(max-height:580px) and (max-width:1100px){
  /* Die Seitenleiste liegt hier immer außerhalb des Rasters (fixiert oder ausgeblendet) → eine Spalte */
  .rahmen,body.chat:not(.zu) .rahmen{grid-template-columns:1fr}
  .seitenleiste{position:fixed;inset:0 auto 0 0;width:min(17rem,86vw);z-index:30;box-shadow:0 0 0 100vmax rgba(0,0,0,.5)}
  body.chat.zu .seitenleiste{display:none}
  body.chat:not(.zu) .seitenleiste{display:flex}
  body.chat:not(.zu) .slauf{display:inline}
  .spalte,.eingabebox{max-width:none}
  .chatkopf{flex-wrap:wrap;row-gap:.2rem;padding:.5rem .7rem}
  .chatkopf .klein{font-size:.78rem;line-height:1.5}
  /* Punkt 3: einzeilig war eine Desktop-Zusage – seit dem Befund vom 2026-09-02 bricht die Zeile
     überall um, sobald sie muss; hier bleibt nur der größere Abstand für Fingerbedienung. */
  .werkzeuge{gap:.4rem}
  .werkzeuge select{max-width:100%}
  /* Befund 1 (Sichttest 2026-09-01): Die feste Reglerbreite gilt der einzeiligen Desktop-Zeile –
     in der umbrechenden Zeile darf er die Restbreite nehmen statt eine Zeile für sich zu belegen */
  .werkzeuge .aufwand{flex:1 1 12rem;width:auto;max-width:100%}   /* #1057: `.werkzeuge` erhöht die Spezifität gegen die Grundregel weiter unten (Kaskadenfalle) – `min-width:10rem` bleibt von dort bewusst gültig, sie schützt laut Sichttest 2026-09-02 vor Stauchung auch in der umbrechenden Zeile; Ausnahme #987: die Höhen-Query weiter unten setzt mit demselben Selektor nur Startbreite und Boden (`flex-basis:9rem;min-width:8rem`), Wachsen und Nachgeben (`flex-grow`/`-shrink`, `width`, `max-width`) bleiben von hier */
  .werkzeuge .luecke{flex:1 1 auto}
  /* #183: Auf schmalen Geräten nimmt das Panel die GANZE Werkzeugzeile ein – die Viewport-Maße
     (früher 92vw) rechnen den linken Rand der Eingabezone nicht mit und liefen deshalb rechts
     über die Fensterkante hinaus. */
  .werkzeuge details.menu>.menupanel{width:100%}
  /* #162 (Entdopplung): Auf schmalen Geräten hat die Werkzeugzeile keinen Platz für die
     Kontextfenster-Anzeige – sie ist Beiwerk und weicht den Bedienelementen. */
  .kontextfenster{display:none}
}
/* #234 (Prüferbefund Paket 42, 2026-09-03, vorbestehend): Bei geringer FENSTERHÖHE ist die
   Eingabezone höher als das Fenster. Gemessen in der Betreibersicht mit allen Pilotmodulen:
   bei 844×390 Kopfzeile 63 px + Eingabezone 403 px (davon Rechtsfuß 194 px und umbrechende
   Werkzeugzeile 91 px), bei 360×640 sogar 113 + 571 px. Im festen Rahmen (`height:100dvh` +
   `overflow:hidden`) blieb für `.strom{flex:1}` nichts übrig: clientHeight 0 – der gesamte
   Gesprächsstrom unsichtbar und nicht scrollbar, und mit ihm der Intern-Warnbanner (E-32/#90),
   der in seiner `.spalte` hängt. Vom unteren Ende der Eingabezone war dabei auf `main` mehr
   abgeschnitten als sichtbar: bei 844×390 standen 466 px Inhalt in einem 390-px-Fenster, die
   letzten 76 px (Art.-9-Satz-Ende, Disclaimer, Speicherstand, Impressum/Datenschutz) lagen
   unter der Kante und waren NICHT erreichbar (`body{overflow:hidden}`, Mausrad 0 px).

   ENTSCHEIDUNG CHRIS 2026-09-10, Variante A (#234): Der A6-Fristensatz muss VOR dem Upload
   OHNE Scrollen im Bild stehen („deutlich sichtbar"). Die Runde-2-Fassung dieses Blocks machte
   die SEITE scrollbar und schob den Satz damit unter die Kante (bei 844×390 von y 256 auf
   y 490, erst nach 309 px Radscroll sichtbar) – sie ist ersetzt. Nicht mehr die Seite wächst,
   sondern die Eingabezone wird kleiner, bis Kopf + Zone + ein Streifen Strom ins Fenster
   passen. Der Rechtsfuß wird dafür NICHT gekürzt und nichts eingeklappt – die Wortlaute sind
   rechtlich abgestimmt (Chris-Frage 12/D3, Kanzlei-OK fehlt); es geht ausschließlich um
   Polster, Zeilenhöhe und die Breite, über die derselbe Text umbricht.

   Vier Teile, alle gemessen (Playwright, Betreibersicht, alle Pilotmodule, Intern-Mandant,
   40 Gespräche – `tests/test_strom_hoehe.py` fährt dieselbe Messung):
     * Der DECKEL des Stroms ist an Kopf + Eingabezone GEKOPPELT, und zwar durch den festen
       Rahmen selbst: `.rahmen{height:100dvh}` und `.strom{flex:1}` geben dem Strom genau den
       Rest. Deshalb steht hier KEIN `max-height` mehr – ein zweiter Deckel (Runde 2: 60dvh)
       nähme dem Strom Platz, den keiner braucht, und schöbe die Zone aus dem Bild.
     * Die KOMPAKTREGELN holen den Platz: Kopfzeile und Zone mit kleineren Polstern, das
       Eingabefeld startet mit 2,2 statt 4 rem, die Bedienelemente der Werkzeugzeile werden
       flacher (sie brauchen dadurch 2 statt 3 Zeilen), der Rechtsfuß bekommt straffere
       Zeilenhöhe (1.25 statt 1.4). Gemessen bei 844×390: Zone 403 → 211 px, Kopf 63 → 68 px
       (die Leiste ist aus dem Raster, dafür steht der ☰-Knopf in der Kopfzeile), Strom
       0 → 111 px, und die Zone endet exakt an der Fensterkante – nichts ist mehr
       abgeschnitten. 1280×530: Strom 196 → 281 px, ebenfalls ohne Seitenscroll.
     * Der MINDESTSTREIFEN (`min-height:5rem`) hält den Befund fern: wo Kopf + Zone auch
       kompakt nicht ins Fenster passen (sehr kleine Fenster, große Schriftgrade), bleibt der
       Strom sichtbar und scrollbar, statt wieder auf 0 zu fallen.
     * Der NOTAUSGANG: `body.chat` darf dann mitwachsen und scrollen (`height:auto`,
       `overflow-y:auto`). Das ist der Unterschied zu `main` – dort schnitt `overflow:hidden`
       ab, was nicht passte, und niemand kam daran. In den Ziel-Lagen greift er nicht
       (844×390, 360×640, 1280×530: Seite = Fenster, Mausrad 0 px). UNTERHALB rund 600 px
       Fensterhöhe greift er sehr wohl, und das ist der Zweck – nachgemessen 390×600 5 px
       Überhang, 640×450 27 px, 360×500 119 px, jeweils per Mausrad vollständig erreichbar;
       auf `main` waren es dort 33, 57 und 184 px, die NIEMAND erreichen konnte. Der frühere
       Satz „in allen gemessenen Ziel-Lagen greift er gar nicht" verallgemeinerte eine
       Eigenschaft, die unterhalb 600 px nicht mehr trägt (Prüferbefund Runde 3).
   Bewusst KEIN `overflow` an der Eingabezone selbst: dort schweben die Menüpanels
   (`.menupanel`, bis 60vh hoch, nach OBEN über die Kante der Zone hinaus) und der Runter-Knopf
   (`.runter{top:-1.1rem}`) – ein Scroll-Kasten an der Zone schnitte sie ab (#183).

   WIRKBEREICH (unverändert aus Runde 2, dort nachgemessen): Ob der Rahmen bricht, hängt an
   Kopf + Eingabezone, und die sind BREITENabhängig – 334 px bei 1280, 425 bei 1024, 453 bei
   900, 466 bei 844, 684 bei 360 Pixel Breite. Daraus die zwei Bedingungen:
     * `max-height:580px` – flache Fenster bei jeder Breite: 844×530 behielt im festen Rahmen
       65 px Strom, 1024×580 155 px, 1280×530 196 px; mit den Kompaktregeln sind es 251, 338
       und 281 px. Die Schwelle liegt bewusst unter 600 px – bei 1280×600 trägt der feste
       Rahmen (266 px Strom), und diese Klasse hatte Fund 1 der Abnahme Runde 2 als
       verschlechtert gemeldet.
     * `max-height:720px` zusammen mit `max-width:560px` – das schmale Gerät im Hochformat, wo
       Kopf + Zone allein bis 684 px belegen (360×640, 480×720, 540×700).
   Draußen bleiben die Fenster, in denen nichts kaputt war; für 1024×720, 1280×900 und das
   Hochformat 390×844 aus #216 ist die Geometrie mit und ohne diesen Block pixelgleich
   nachgemessen (Strom 295, 566 und 232 px).
   Was Variante A KOSTET, offen benannt: Im schmalen Hochformat bleibt der Strom ein Schlitz –
   360×640 kommt von 0 px (Befund) auf 101 px, nicht mehr. Der Rechtsfuß braucht dort allein
   288 px von 640, und der A6-Satz hat Vorrang. Mehr Strom gäbe es nur über einen kürzeren
   Rechtsfuß – das ist die Kanzleifrage aus #234 (Chris-Frage 12/D3), keine CSS-Frage. */
@media(max-height:580px),(max-height:720px) and (max-width:560px){
  body.chat{height:auto;min-height:100dvh;overflow-x:hidden;overflow-y:auto}
  .rahmen{height:100dvh}
  .strom{min-height:5rem}
  .chatkopf{padding:.25rem .7rem;flex-wrap:nowrap}.chatkopf .kopfwerk{flex:0 0 auto}.chatkopf .kopfrechts{flex:0 1 auto;min-width:0;flex-wrap:nowrap;justify-content:flex-start;overflow-x:auto;padding:.35rem;margin:-.35rem;background:linear-gradient(to left,var(--marke-primaer,var(--marke-hintergrund,var(--bg))) 40%,transparent) right/2.5rem 100% no-repeat local,linear-gradient(to left,rgba(0,0,0,.5),transparent) right/1rem 100% no-repeat scroll}.chatkopf .kopfrechts>span:first-child{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}   /* #987: EINE Kopfreihe statt vier (360 px: 105 → 41 px, 844 px: 68 → 41 px). Die Links laufen als Streifen, der bei Platzmangel selbst waagrecht scrollt, statt in drei Reihen umzubrechen; die Adresse (reiner Text, auf allen anderen Seiten weiter im Kopf) ist im Chat nur noch für Vorleser da (Muster `.vh`, Welle 45 – `display:none` nahm sie auch ihnen). Keine Schrift wird kleiner. Polster + Gegenrand halten den Fokusrahmen im Scrollkasten sichtbar, ohne die Reihe zu erhöhen; `.chatkopf .kopfrechts` schlägt die spätere Grundregel `.kopfrechts` über die Spezifität, nicht über die Reihenfolge. Welle 45: ein Schatten am rechten Rand zeigt, dass weitere Links folgen (Scrollleisten sind am Handy unsichtbar) – nur bei Überlauf und nicht am Ende: die Abdeckung in der Kopfzeilenfarbe wandert mit dem Inhalt (`local`) und liegt am Ende über dem feststehenden Schatten (`scroll`); Textfarben bleiben, der Schatten liegt unter dem Text. */
  .eingabezone{padding:.15rem .7rem .25rem}
  .eingabebox{padding:.3rem .5rem}
  #nachricht{min-height:2.2rem;max-height:5rem}
  /* Nachzug Runde 3 (Prüferbefund 2026-09-10): KEIN `gap` hier. Der Mobil-Block oben setzt
     `.werkzeuge{gap:.4rem}` als Zusage aus dem Sichttest 2026-09-02 (Fingerbedienung); dieser
     Block steht später bei gleicher Spezifität und hätte sie still zurückgenommen. Der Gewinn
     wäre rund 2 px an einer umbrechenden Zeile – dafür wird keine Trefferfläche kleiner. */
  .werkzeuge{padding-top:.25rem}.werkzeuge .aufwand{flex-basis:9rem;min-width:8rem}   /* #987: Aufwand-Regler startet mit 9 statt 12 rem (Boden 8 statt 10 rem, sonst klemmt der Boden die Ausgangsbreite) – im gesperrten Rollengespräch teilt er sich bei 360 px die Reihe mit „Als Datei speichern“ (Werkzeugzeile 142 → 112 px), bei 844 px bleibt „Senden“ in Reihe 1 (82 → 50 px) */
  .werkzeuge select{padding:.2rem .35rem}
  /* Die POLSTER der Bedienelemente werden hier sehr wohl kleiner (.2/.5 statt .35/.7 rem), und
     das kostet Trefferfläche – bewusst: sie bringen die Werkzeugzeile von 3 auf 2 Zeilen und
     sind damit der größte Einzelposten, aus dem der A6-Satz seinen Platz bekommt. Anders als
     beim `gap` steht hier also ein echter Gewinn dagegen. Offen für den Sichttest: reicht die
     Fläche auf einem echten Gerät im Querformat noch (Frage an Chris, #234). */
  .werkzeuge button.sek,.werkzeuge summary.sek,.werkzeuge .dateiknopf{padding:.2rem .5rem}
  .werkzeuge>.schluss>button,.werkzeuge>button:not(.sek){padding:.3rem .8rem}
  .chatfuss{line-height:1.25;margin-top:.15rem}
  .rollezeile{padding-top:.2rem;gap:.2rem .5rem}.rollezeile>p{font-size:.75rem;line-height:1.25}.rollezeile>a.btn.sek{padding:.15rem .5rem;font-size:.75rem}.rollezeile:has(#rolle-neu)>p{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}   /* #954; #987: im GESPERRTEN Gespräch klappt der Hinweissatz hier ein (360 px: Rollenzeile 60 → 27 px) – gesperrtes Select und Knopf „Neues Gespräch mit dieser Rolle“ tragen die Regel sichtbar, Vorleser bekommen den Satz weiter über `aria-describedby` (visuell verborgen wie `.vh`, nicht `display:none`). Im NEUEN Gespräch bleibt er stehen: dort ist er die Regel vor dem ersten Wort. */
}
/* ---------------------------------------------------------------- #162 Profil-Cockpit (Pilotmodul)
   Das Limit-Menü sitzt in der Kopfzeile; wie in der Werkzeugzeile bleibt das details IM FLUSS und
   nur das Panel schwebt (sonst misst sich `position:absolute` am Viewport und das Panel landet am
   Fensterrand – Befund der Gesprächsliste). Balken sind <progress>: die CSP verbietet
   style-Attribute, eine Breite je Prozentwert ginge nur inline. */
.kopfwerk{display:flex;align-items:center;gap:.4rem;min-width:0}
.chatkopf details.menu.kopfmenu[open]{position:relative;right:auto;z-index:auto;background:none;border:0;border-radius:0;padding:0;width:auto}
.chatkopf details.menu.kopfmenu>.menupanel{position:absolute;top:calc(100% + .4rem);left:0;z-index:25;width:min(28rem,92vw);max-height:min(70vh,32rem);overflow:auto;background:var(--flaeche);border:1px solid var(--rand);border-radius:8px;padding:.7rem .8rem;box-shadow:0 6px 22px rgba(0,0,0,.45)}
.chatkopf details.menu.kopfmenu>summary{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:min(22rem,45vw)}
.menupanel h3{margin:.6rem 0 .3rem;font-size:.85rem;text-transform:uppercase;letter-spacing:.1em;color:var(--gedimmt)}
.menupanel h3:first-child{margin-top:0}
/* Chris-Sichttest 2026-09-01 (#162, Entdopplung): Die Kontextfenster-Anzeige steht jetzt UNTEN in
   der Werkzeugzeile – rechts außen zwischen Lücke und „Senden", gedimmt und klein, damit sie neben
   der Eingabe nicht um Aufmerksamkeit ringt. In der einzeiligen Desktop-Zeile darf sie nicht
   umbrechen; wird es eng (schmale Geräte, Media-Query oben), verschwindet sie ganz – der Wert ist
   Beiwerk, Knöpfe und Auswahlfelder haben Vorrang.
   Chris-Sichttest 2026-09-02 (volles Modul-Set): Die Anzeige sitzt jetzt mit „Senden" in der
   Hülle `.schluss` – die hält beide beim Umbrechen zusammen und am rechten Rand ihrer Zeile.
   `white-space:nowrap` bleibt: „Kontextfenster 12k / 200k (6 %)" ist EINE Angabe und darf nicht
   mitten im Bruch umbrechen. */
.kontextfenster{flex:0 0 auto;font-size:.75rem;color:var(--gedimmt);white-space:nowrap;font-variant-numeric:tabular-nums}
progress.kbalken{display:block;width:100%;height:.55rem;margin:.25rem 0;border:1px solid var(--rand);border-radius:999px;background:var(--flaeche2);accent-color:var(--akzent)}
progress.kbalken::-webkit-progress-bar{background:var(--flaeche2);border-radius:999px}
progress.kbalken::-webkit-progress-value{background:var(--akzent);border-radius:999px}
progress.kbalken::-moz-progress-bar{background:var(--akzent);border-radius:999px}
.klimit{display:flex;flex-wrap:wrap;align-items:baseline;gap:.2rem .8rem;margin-top:.6rem;font-size:.9rem}
.klimit .kname{font-weight:600}
.klimit .kreset{color:var(--gedimmt);font-size:.82rem}
.klimit .kproz{margin-left:auto;font-variant-numeric:tabular-nums}
.kdetail{margin:.1rem 0 0}
/* Aufwand-Regler (Referenzmuster): Titel oben, Regler in der Mitte, „Schneller"/„Intelligenter"
   an den Enden. Steht in der Werkzeugzeile an der Stelle des Prüftiefe-Selects.
   Sichttest 2026-09-01 (Chris, Befund 1): Als schrumpfendes Element (flex:0 1 auto, min 11rem)
   wurde der Regler in der vollen Zeile zusammengequetscht – „Aufwand gründliche Prüfung" (die
   damals längste Beschriftung; seit #150 heißen die Stellungen Schnell · Standard · Auto · Max
   und sind allesamt kürzer) und „Schneller/Intelligenter" brachen um. Er bekommt darum eine
   FESTE Breite (12rem), gibt also nicht mehr nach, und beide Beschriftungen stehen auf einer
   Zeile (nowrap). Die Breite BLEIBT: Sie hält die Zeile ruhig, wenn der Kunde den Regler zieht
   – ein mitwachsendes Element würde bei jedem Namenswechsel die Nachbarn verschieben.
   Chris-Sichttest 2026-09-02 (volles Modul-Set): Die feste Breite bleibt – der Regler gibt weiter
   nicht nach, sondern die Zeile bricht um (flex-wrap oben). `min-width` hält ihn auch dort über
   der Grenze, wo er als letztes Element einer Zeile sonst gestaucht würde; `max-width:100%`
   verhindert das Gegenstück, dass 12 rem bei 150 % Schriftgröße breiter werden als die Box. */
.aufwand{display:inline-flex;flex-direction:column;flex:0 1 12rem;width:12rem;min-width:10rem;max-width:100%;gap:.05rem}
.aufwand[hidden]{display:none}
.aufwand-titel{font-size:.78rem;color:var(--gedimmt);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.aufwand-titel b{color:var(--absatz);font-weight:600}
.aufwand input[type=range]{width:100%;margin:0;accent-color:var(--akzent);background:transparent}
.aufwand-enden{display:flex;justify-content:space-between;font-size:.7rem;color:var(--gedimmt);white-space:nowrap}
.aufwand.aufwand-auto input[type=range]{accent-color:var(--gedimmt);opacity:.55}
/* #413 (axe-Fund, PR #401; Nachbesserung Runde 2 nach Prüfer-Befund): Bei Prüftiefe „automatisch"
   (Standard, ohne dass ein Mandant je umschaltet) blendete diese Zeile die Enden
   „Schneller"/„Intelligenter" per opacity ab – das MISCHT den Text mit dem Hintergrund statt ihn
   nur optisch zurückzunehmen, und die Box liegt auf --flaeche (app.py:527), nicht auf --bg. Die
   erste Fassung (opacity .85) kam so nur auf ~4,37:1 auf --flaeche – axe blieb rot. Jetzt keine
   Deckkraft mehr auf dem Text: --gedimmt#9aa3a1 unverändert (kein Mischen) ergibt gerechnet
   5,44:1 auf --flaeche#262d2c und 5,82:1 auf --bg#222823 (SC 1.4.3 verlangt 4,5:1) – axe-Bestätigung
   durch den CI-Lauf. „Automatisch" bleibt trotzdem sichtbar unterschieden: der Regler-Griff (Zeile
   darüber) bleibt bei opacity .55 gedämpft – er ist eine Bedienfläche, keine Textfarbe, SC 1.4.3
   gilt dafür nicht. */
/* Aufschlüsselung (/konto/verbrauch) */
table.vtab{border-collapse:collapse;width:100%;margin:.5rem 0;display:block;overflow-x:auto}
table.vtab th,table.vtab td{border:1px solid var(--rand);padding:.3rem .5rem;text-align:left;white-space:nowrap}
table.vtab th{background:#2e3534;color:var(--text);font-weight:600;letter-spacing:.04em}
#entwurf:empty{display:none}
dialog{background:var(--flaeche);color:var(--text);border:1px solid var(--akzent);border-radius:8px;padding:1.2rem 1.4rem;max-width:26rem}
dialog::backdrop{background:rgba(0,0,0,.6)}
dialog h2{border-bottom-color:var(--akzent)}
details.menu{position:relative}
details.menu>summary{list-style:none;cursor:pointer}
details.menu>summary::-webkit-details-marker{display:none}
details.menu[open]>summary{border-color:var(--akzent);color:var(--text)}
details.menu>div,details.menu>form{position:static}
details.menu[open]{position:absolute;right:.4rem;z-index:25;background:var(--flaeche);border:1px solid var(--rand);border-radius:6px;padding:.5rem .6rem;width:15rem}
details.menu[open] form+form{border-top:1px solid var(--rand);margin-top:.5rem;padding-top:.5rem}
/* E2E-Sichttest 2026-09-01: Das ⋮-Menü der Gesprächsliste hatte keinen positionierten Vorfahren –
   `position:absolute` maß sich am Viewport, das Panel erschien oben rechts am Fensterrand statt am
   Eintrag (Screenshot-Befund). In der Seitenleiste klappt das Panel deshalb IM FLUSS direkt unter
   dem jeweiligen Eintrag auf (eigene Zeile via flex-wrap): es bleibt in der Seitenleiste, scrollt
   mit der Liste und kann auch auf schmalen Bildschirmen nicht aus dem Viewport laufen. */
.slliste li{position:relative;flex-wrap:wrap}
.slliste details.menu[open]{position:static;flex-basis:100%;width:auto;margin:.15rem 0 .4rem}
/* HMEM-005 (#169): „Merken" an der Antwort. Gleiche Lehre wie in der Seitenleiste – das Panel
   klappt IM FLUSS unter der Aktionszeile auf (flex-wrap, eigene Zeile), nicht absolut am
   Viewport: der Gesprächsstrom scrollt, ein absolut gesetztes Panel bliebe beim Scrollen stehen
   oder liefe auf 390 px aus dem Bild.
   Sichttest 2026-09-02: Der Aufklapper stand zuerst als dritter Knopf IN der Reihe und sprang
   beim Öffnen eine Zeile nach unten – der angeklickte Knopf wanderte unter dem Finger weg. Er
   belegt jetzt IMMER seine eigene Zeile (flex-basis:100%, offen wie geschlossen): nichts
   bewegt sich beim Klick, und die einzige Aktion mit Schreibwirkung steht sichtbar getrennt von
   den folgenlosen Kopieren/Neu generieren. Zurückhaltend bleibt sie trotzdem – derselbe
   .sek-Knopf, dieselbe .aktionen-Zeile mit opacity .75, bis man sie berührt. */
.aktionen{flex-wrap:wrap}
.aktionen details.menu.merkmenu{flex-basis:100%}
.aktionen details.menu.merkmenu>summary{width:fit-content}
.aktionen details.menu.merkmenu[open]{position:static;width:auto;right:auto;
  z-index:auto;background:none;border:0;border-radius:0;padding:0;margin:.1rem 0 0}
.merkmenu>.menupanel{margin:.45rem 0 .2rem;background:var(--flaeche);border:1px solid var(--rand);
  border-radius:8px;padding:.7rem .8rem;max-width:var(--spaltenbreite)}
.merkmenu>.menupanel textarea{width:100%;min-height:5.5rem;resize:vertical}
.merkmenu>.menupanel select{width:100%;max-width:24rem}
.merkmenu>.menupanel label{display:block;margin:.5rem 0 .15rem;font-size:.85rem;letter-spacing:.04em;color:var(--gedimmt)}
.merkmenu>.menupanel p{margin:.35rem 0}
/* #189 („Eine Antwort, nicht fünf"): Aufklapper „Arbeitsschritte" ÜBER dem Ergebnis. Optik der
   `.menu`-Aufklapper (derselbe `summary.sek`, dieselbe Panel-Kachel wie `.merkmenu`), aber
   bewusst ohne deren Klasse `menu`: `menuesSchliessen` schließt jedes `details.menu` beim
   Außenklick – richtig für ein Menü, falsch für einen Lesetext (und es würde ihn während des
   Streams zuklappen). Die Klasse `aufklapp` sagt genau das aus (Muster `.loeschzone` in
   dateien.py, gepinnt in test_menue_aussenklick). Zurückhaltend wie die Aktionszeile: gedämpft,
   bis man ihn berührt. */
.schrittmenu{margin:.2rem 0 .1rem}
.schrittmenu>summary{list-style:none;cursor:pointer;width:fit-content;opacity:.75}
.schrittmenu>summary::-webkit-details-marker{display:none}
.schrittmenu>summary:hover,.schrittmenu>summary:focus-visible,.schrittmenu[open]>summary{opacity:1}
.schrittmenu[open]>summary{border-color:var(--akzent);color:var(--text)}
.schrittpanel{margin:.45rem 0 .3rem;background:var(--flaeche);border:1px solid var(--rand);
  border-radius:8px;padding:.6rem .8rem;max-width:var(--spaltenbreite);color:var(--gedimmt);font-size:.95rem}
.schrittpanel>p.klein{margin:0 0 .4rem;color:var(--gedimmt)}
.schritt+.schritt{border-top:1px solid var(--rand);margin-top:.55rem;padding-top:.55rem}
/* Während des Streams steht der Aufklapper offen (der Fortschritt darf nicht verschwinden) und
   die laufende Zeile trägt den Namen des gerade genutzten Werkzeugs. */
.schrittlauf{margin:.4rem 0 0;color:var(--gedimmt);font-size:.9rem}
/* #200: Das Symbol des Gateways ist Schmuck – es steht vor dem Etikett, trägt aber keine Aussage
   (aria-hidden im Markup). Der Abstand kommt aus dem Stylesheet, nicht aus einem Leerzeichen im
   Text: sonst läse ein Schirmleser es als Teil des Satzes mit. */
.schrittsymbol{margin-right:.4rem}
/* E2E-Sichttest 2026-09-01 (Feinschliff): Dateiverwaltung nach dem Muster der Gesprächsliste –
   je Datei ein Eintrag (Name mit Ellipse). Sichttest-Punkt 5 (Chris): Die Liste war zu klein und
   zu gedrängt, und das ⋮-Menü daneben war ein zweiter Bedienweg ohne eigenen Nutzen. Jetzt
   großzügige Zeilen, und der Klick auf den NAMEN öffnet die Übersicht der Datei – das ⋮-Menü ist
   darin aufgegangen. Alles bleibt IM FLUSS (die Liste selbst scrollt im Panel), nie am Viewport
   gemessen; der Eintrag bleibt position:relative als Anker für künftige Bedienelemente. */
.dateiliste{list-style:none;margin:.1rem 0 0;padding:0;font-size:.95rem}
.dateiliste li{position:relative;display:block;border-radius:4px}
.dateiliste li+li{border-top:1px solid var(--rand)}
.dateiname{display:block;list-style:none;cursor:pointer;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:.45rem .4rem;border-radius:4px;color:var(--absatz)}
.dateiname::-webkit-details-marker{display:none}
.dateiname:hover{background:var(--flaeche2);color:var(--text)}
details.dateiinfo[open]>.dateiname{color:var(--text);font-weight:600}
.dateikarte{margin:0 0 .5rem;padding:.2rem .4rem .3rem;border-left:2px solid var(--akzent)}
.dateimeta{color:var(--absatz);overflow-wrap:anywhere;font-size:.9rem}
.dateikarte a{display:block;font-size:.9rem;margin:.2rem 0;padding:.15rem 0}
.dateikarte label{margin:.6rem 0 .15rem;font-size:.85rem}
.dateikarte input[type=text]{font-size:.9rem;padding:.4rem .5rem}
.dateikarte p{margin:.35rem 0}
.dateikarte button.sek{max-width:100%;white-space:normal;overflow-wrap:anywhere;text-align:left}
.dateikarte [disabled]{opacity:.5;cursor:not-allowed}
/* E-39 (#98): Hinweisleiste über dem Eingabefeld */
.merke{border-left:3px solid #e0708a;background:rgba(224,112,138,.08);padding:.5rem .8rem;margin:.6rem 0;font-size:.9rem}
/* #237: Maskottchen statt Textknopf „Support". Der Textknopf (.supportknopf) ist ersatzlos weg –
   niemand verlinkte ihn sonst. Das Element ist ein <a href='/support'> und bleibt es ohne
   JavaScript; das Skript fängt nur den Klick ab. `line-height:0` nimmt dem Bild die Zeilenkante,
   sonst sitzt das Maskottchen ein paar Pixel über seinem eigenen Rand.

   #237 Runde 3 (Befund 5): Die feste Position sitzt jetzt an der Hülle, nicht am Link – die drei
   Blasen sind eigene Knöpfe neben dem Bild und brauchen denselben Bezugsrahmen. Die Hülle ist
   genau so groß wie das Maskottchen; die Blasen ragen absolut positioniert darüber hinaus. Das
   ist Absicht: Eine Hülle so groß wie der Fächer wäre ein unsichtbarer Block über der Fußzeile,
   der deren Links schluckt. */
.maskottchen-huelle{position:fixed;right:1rem;bottom:1rem;z-index:20;width:5rem;height:5rem}
.maskottchen{position:relative;display:block;width:100%;height:100%;padding:0;border:0;border-radius:50%;background:transparent;line-height:0;cursor:pointer;transition:transform .15s ease}
.maskottchen img{width:100%;height:100%;display:block;filter:drop-shadow(0 3px 8px rgba(0,0,0,.55))}
.maskottchen:hover,.maskottchen:focus-visible{transform:translateY(-4px)}
/* Die Blasen erscheinen nur bei Maus-Hover UND Tastatur-Fokus (Muster .hilfe-text) – kein
   Skript (CSP). Unsichtbar sind sie `pointer-events:none`: eine blasse Blase darf weder einen
   Klick auf das Maskottchen abfangen noch einen auf die Fußzeilen-Links darunter. Die Werte für
   Farbe, Rand und Schrift stehen hier vollständig, weil die globale `button`-Regel sonst
   Versalien, Sperrung und Akzent-Hintergrund durchreicht. */
.sprechblase{position:absolute;white-space:nowrap;background:var(--flaeche);border:1px solid var(--akzent);border-radius:999px;padding:.16rem .6rem;color:var(--akzent);font:inherit;font-size:.78rem;font-weight:400;line-height:1.3;letter-spacing:0;text-transform:none;cursor:pointer;opacity:0;translate:.45rem .3rem;transition:opacity .2s ease,translate .2s ease;transition-delay:0s;pointer-events:none}
.sprechblase[hidden]{display:none}            /* ohne Skript kein toter Knopf (maskottchen.js) */
.sprechblase:hover,.sprechblase:focus-visible{background:var(--flaeche2);color:var(--text);border-color:var(--akzent)}
/* Unsichtbarer Saum um jede Blase: Ohne ihn liegt zwischen Maskottchen und Blase ein paar Pixel
   breites Loch, in dem die Hülle ihr :hover verliert – die Maus käme nie an der Blase an, die
   sie anklicken soll. */
/* Bewusst ECKIG (kein border-radius): Ein runder Saum hat sein Loch ausgerechnet in den Ecken –
   und genau über die Ecke wandert die Maus vom Maskottchen zur Blase (Sichtmessung: mit rundem
   Saum blieb „Fehler?" auf dem Weg dorthin stehen und war nicht mehr anklickbar). */
.sprechblase::after{content:'';position:absolute;inset:-.45rem}
/* Je weiter außen die Blase, desto breiter das Loch (gemessen 1280×800: 8 px unter „Fragen?",
   9 px neben „Fehler?", 33 px neben „Ideen?"). Der Saum reicht deshalb je Blase bis unter das
   Maskottchen. Er ist nur scharf, solange die Blase sichtbar ist – `pointer-events` erbt er. */
.sprechblase[data-reiter='fragen']::after{inset:-.45rem -.45rem -1rem}
.sprechblase[data-reiter='fehler']::after{inset:-.45rem -.85rem -.45rem -.45rem}
.sprechblase[data-reiter='idee']::after{inset:-.45rem -2.4rem -.45rem -.45rem}
/* Der Fächer gegen den Uhrzeigersinn: „Fragen?" über dem Kopf, „Fehler?" schräg links oben,
   „Ideen?" links auf Kopfhöhe. Keine der drei liegt auf dem Text der Fußzeile – gemessen wurden
   die Zeilenkästen selbst (Range.getClientRects), nicht der Kasten samt Gasse (#237 Runde 3). */
.sprechblase[data-reiter='fragen']{left:50%;bottom:calc(100% + .15rem);transform:translateX(-50%)}
.sprechblase[data-reiter='fehler']{right:calc(100% + .2rem);bottom:calc(100% - .85rem)}
.sprechblase[data-reiter='idee']{right:calc(100% + 1.6rem);bottom:1.9rem}
/* Gestaffelt herein, gemeinsam hinaus: Die Verzögerung steht NUR in der Sichtbar-Regel. Stünde
   sie in der Grundregel, würde auch das Ausblenden nachhinken und die dritte Blase bliebe eine
   Sekunde allein stehen, nachdem die Maus längst weg ist. */
.maskottchen-huelle:hover .sprechblase,
.maskottchen-huelle:focus-within .sprechblase{opacity:1;translate:0 0;pointer-events:auto}
.maskottchen-huelle:hover .sprechblase[data-reiter='fehler'],
.maskottchen-huelle:focus-within .sprechblase[data-reiter='fehler']{transition-delay:.5s}
.maskottchen-huelle:hover .sprechblase[data-reiter='idee'],
.maskottchen-huelle:focus-within .sprechblase[data-reiter='idee']{transition-delay:1s}
/* Wer eine Blase per Tabulator ansteuert, soll sie sofort sehen – nicht erst nach einer Sekunde. */
.maskottchen-huelle:focus-within .sprechblase:focus{opacity:1;translate:0 0;pointer-events:auto;transition-delay:0s}
@media(prefers-reduced-motion:reduce){
  .sprechblase{translate:none;transition:opacity .2s ease}
}
/* Bei offenem Panel ist die Einladung erledigt – sonst steht sie im Sichttest über der
   Fußzeile, während daneben schon das Gespräch läuft. Zuletzt und mit einer Klasse mehr, damit
   diese Regel die Sichtbar-Regeln oben schlägt. */
.maskottchen-huelle .maskottchen[aria-expanded='true']~.sprechblase{opacity:0;pointer-events:none;transition-delay:0s}
/* Das Panel steht ÜBER dem Maskottchen, nicht darauf: Der Knopf bleibt sichtbar und erreichbar,
   damit der Fokus nach dem Schließen ein Ziel hat.
   #237 Runde 2 (Chris-Sichttest 2026-09-04, Befund 3): `top` UND `bottom` statt `max-height`.
   Vorher endete das Panel nach 520 px mitten im freien Bildschirm, und im Reiter „Fragen" musste
   man scrollen, um den Sendeknopf zu sehen. Jetzt spannt es von knapp unter dem Kopf bis über das
   Maskottchen; `overflow:hidden` hält den Rahmen dicht, gescrollt wird nur innen (der Verlauf). */
.support-panel{position:fixed;right:1rem;top:4.5rem;bottom:6.6rem;z-index:21;display:flex;flex-direction:column;gap:.35rem;width:400px;max-width:calc(100vw - 2rem);height:auto;overflow:hidden;background:var(--flaeche);border:1px solid var(--akzent);border-radius:10px;padding:.7rem .8rem;box-shadow:0 6px 26px rgba(0,0,0,.55)}
.support-panel[hidden]{display:none}          /* schlägt das display:flex – sonst bliebe es sichtbar */
.sp-kopf{display:flex;align-items:center;justify-content:space-between;gap:.5rem;flex:0 0 auto}
.sp-kopf h2{margin:0;padding-bottom:.15rem}
.support-panel>p{margin:.1rem 0;flex:0 0 auto}
.support-panel .merke{margin:.25rem 0;padding:.3rem .55rem;font-size:.78rem;line-height:1.35}
.support-panel #s-verlauf{flex:1 1 auto;min-height:5rem;overflow-y:auto;overscroll-behavior:contain;padding-right:.15rem}
.support-panel .msg{font-size:.88rem;margin:.5rem 0}
.support-panel #s-nachricht{flex:0 0 auto;min-height:3.2rem;max-height:7rem;resize:none;font-size:.9rem;padding:.45rem}
.sp-knoepfe{display:flex;align-items:center;flex-wrap:wrap;gap:.4rem;flex:0 0 auto}
.sp-knoepfe button{padding:.4rem .9rem;font-size:.8rem}
.sp-fuss{display:flex;align-items:center;flex-wrap:wrap;gap:.4rem;flex:0 0 auto}
.sp-fuss a{color:var(--akzent)}
/* #237 Runde 2 (Befund 2): drei Reiter statt gestapelter Bereiche. Die Reiterzeile hat feste
   Höhe, die Tafel darunter bekommt den ganzen Rest (`flex:1 1 auto;min-height:0`) – ohne
   min-height:0 wächst ein Flex-Kind an seinem Inhalt statt zu scrollen. */
.sp-reiter{display:flex;gap:.25rem;flex:0 0 auto;border-bottom:1px solid var(--rand);margin:.15rem 0 .1rem}
.sp-reiter button{flex:0 0 auto;padding:.3rem .75rem;font-size:.8rem;background:transparent;color:var(--absatz);border:1px solid var(--rand);border-bottom:0;border-radius:6px 6px 0 0;text-transform:none;letter-spacing:0}
.sp-reiter button:hover{color:var(--text)}
.sp-reiter button[aria-selected='true']{background:var(--flaeche2);color:var(--akzent);border-color:var(--akzent)}
.sp-reiter button[hidden]{display:none}
.sp-tafel{display:flex;flex-direction:column;gap:.35rem;flex:1 1 auto;min-height:0}
.sp-tafel[hidden]{display:none}               /* schlägt das display:flex, wie beim Panel selbst */
/* Alles in einer Tafel behält seine Höhe; wer wachsen darf, sagt es selbst (#s-verlauf unten).
   Ohne diese Zeile schrumpfen Hinweis, Eingabe und Fußzeile, sobald der Verlauf lang wird. */
.sp-tafel>*{flex:0 0 auto}
/* Im Gespräch scrollt NUR der Verlauf (Eingabe und Fuß bleiben stehen); in den Melde-Reitern
   darf die ganze Tafel rollen – dort gibt es nichts, was am Boden kleben müsste. */
#s-tafel-fragen{overflow:hidden}
#s-tafel-idee,#s-tafel-fehler{overflow-y:auto;overscroll-behavior:contain}
#s-angebot:empty{display:none}
/* Der Melde-Reiter füllt seine Tafel: das Textfeld nimmt den freien Platz, Knöpfe und Status
   bleiben darunter stehen. Sonst klebt ein kleines Feld oben und darunter ist der halbe Reiter
   leer – Platz, den man beim Beschreiben eines Fehlers gut gebrauchen kann. */
.support-panel .meldung-form{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}
.support-panel .meldung-form textarea{flex:1 1 auto;min-height:4rem;font-size:.9rem}
.support-panel .meldung-vorschau{max-height:5rem}
/* Sichtprüfung 2026-09-04 (#237): Das Maskottchen lag ÜBER der Fußzeile – und deren Wortlaute
   (KI-Hinweis Art. 50, Speicherfristen, Art. 9/10, Disclaimer) sind rechtlich abgestimmt und
   dürfen von nichts verdeckt werden. Die Seite macht deshalb Platz, und zwar nur dann, wenn das
   Widget wirklich in der Seite steht: `mitwidget` setzt GET / genau dann, wenn das Pilotmodul
   `support-chat` für den Mandanten aktiv ist (E-67/#237).
   Breit reicht Platz rechts (die Zeilen brechen früher um), schmal nicht – dort ist die Fußzeile
   ohnehin über die ganze Breite, also weicht sie nach oben aus.

   #237 Runde 3 (Befund 5): 6,5 rem waren das Maß des Maskottchens allein. Der Fächer reicht rund
   70 px weiter nach links, und die Sichtmessung fand dort genau das, was Runde 2 abstellen
   sollte: „Fehler?" und „Ideen?" lagen auf der ersten und zweiten Zeile der Fußzeile. Die Gasse
   ist deshalb so breit wie der Fächer. Gemessen (1280×800, hover): Fußzeilentext endet bei
   x 1093, die linkeste Blase beginnt bei x 1112 – keine Überlappung mehr. */
body.chat.mitwidget .chatfuss{padding-right:11rem}
@media(max-width:640px){
  .maskottchen-huelle{width:3.5rem;height:3.5rem;right:.6rem;bottom:.6rem}
  /* #237 Runde 3: derselbe Fächer, nur enger – schmal ist rechts neben dem Maskottchen kein
     Platz, und die Fußzeile weicht hier nach oben aus statt zur Seite. */
  .sprechblase{font-size:.72rem;padding:.18rem .5rem}
  .sprechblase[data-reiter='fehler']{right:calc(100% + .1rem);bottom:calc(100% - .55rem)}
  .sprechblase[data-reiter='idee']{right:calc(100% + 1.2rem);bottom:1.3rem}
  /* #237 Runde 2: schmal über die ganze Breite und über die volle Höhe – `top`/`bottom` statt
     max-height, damit auch hier nur der Verlauf scrollt und nicht das ganze Panel. */
  .support-panel{left:.5rem;right:.5rem;width:auto;max-width:none;top:.5rem;bottom:4.6rem}
  /* #237 Runde 3: 3,6 rem waren das Maß des Maskottchens allein; jetzt muss der Text auch über
     dem Fächer bleiben (gemessen 390×844: Blasen ab y 752, Text endet bei y 740). */
  body.chat.mitwidget .chatfuss{padding-right:0;padding-bottom:5.4rem}
}
/* E-67 (#237): Meldung „Idee/Fehler" – Angebot unter der Antwort, Screenshot-Vorschau als data:-URL
   (blob: verbietet die CSP img-src). Alles über Klassen, meldung.js setzt nie ein style-Attribut. */
.meldung-form{font-size:.9rem}
.meldung-form p{margin:.2rem 0}
.meldung-form textarea{width:100%;min-height:5rem;margin:.45rem 0}
.meldung-form[hidden],.meldung-fertig[hidden]{display:none}
.meldung-fertig{border-left:3px solid var(--akzent);padding:.5rem .7rem;margin:.5rem 0;font-size:.9rem}
/* Hinweis aus der Markerzeile: eine Zeile mit Knopf, kein zweites Formular im Gespräch (#237 R2) */
.meldung-hinweis{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;border-left:3px solid var(--akzent);background:var(--flaeche2);padding:.4rem .6rem;margin:.4rem 0;font-size:.85rem}
.meldung-hinweis button{padding:.25rem .7rem;font-size:.8rem}
.meldung-knopfzeile{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}
.meldung-vorschau{display:block;max-width:14rem;max-height:9rem;margin:.45rem 0;border:1px solid var(--rand);border-radius:3px}
/* Sichttest 2026-09-04 (Befund 4): `display:block` schlug das `hidden`-Attribut – vor dem ersten
   Screenshot malte der Browser deshalb das Platzhalter-Icon eines kaputten Bildes. */
.meldung-vorschau[hidden]{display:none}
.meldung-status{margin:.45rem 0 0;font-size:.85rem;color:var(--gedimmt)}
.meldung-status.fehler{color:#e0708a}
/* E-27: öffentliche AVV-Seiten – Honeypot, Dokumentansicht, Formularzeilen */
.hp{position:absolute;left:-10000px;top:auto;width:1px;height:1px;overflow:hidden}
.dok{font-size:.92rem;line-height:1.55}
.dok h1{font-size:1.15rem;text-transform:none;letter-spacing:0;margin:.2rem 0 .8rem;border-bottom:1px solid var(--rand);padding-bottom:.4rem}
.dok h2{font-size:1rem;text-transform:none;letter-spacing:0;border:0;display:block;margin:1.1rem 0 .3rem;color:var(--akzent)}
.dok h3{font-size:.95rem;margin:.8rem 0 .2rem}
.dok table{border-collapse:collapse;margin:.5rem 0;width:100%}
.dok th,.dok td{border:1px solid var(--rand);padding:.3rem .5rem;text-align:left;vertical-align:top;font-size:.85rem}
.dok th{background:#2e3534;color:var(--text);font-weight:600}
.dok ul{margin:.3rem 0 .3rem 1.2rem;padding:0}
.dok blockquote{border-left:3px solid var(--akzent);margin:.4rem 0;padding-left:.6rem;color:var(--gedimmt)}
.zeile{display:grid;grid-template-columns:1fr 2fr;gap:.6rem}
.check{display:flex;gap:.6rem;align-items:flex-start;margin:.9rem 0;color:var(--text);font-size:.95rem}.check input{margin-top:.3rem}
/* E2E-Sichttest 2026-08-31: Hilfe-Symbol mit CSS-Tooltip an den Erklärungs-Haken – ohne Skript
   (CSP default-src 'self'); sichtbar bei Maus-Hover UND Tastatur-Fokus (:hover/:focus-visible). */
.hilfe-anker{position:relative;display:inline-block;vertical-align:baseline}
.hilfe{display:inline-flex;align-items:center;justify-content:center;width:1.15rem;height:1.15rem;border-radius:50%;border:1px solid var(--akzent);color:var(--akzent);font-size:.78rem;font-weight:700;line-height:1;cursor:help;user-select:none}
.hilfe-text{display:none;position:absolute;bottom:calc(100% + .5rem);right:-1rem;width:min(21rem,80vw);background:var(--flaeche2);border:1px solid var(--akzent);border-radius:6px;padding:.55rem .75rem;color:var(--absatz);font-size:.83rem;line-height:1.5;z-index:30;box-shadow:0 2px 10px rgba(0,0,0,.35)}
.hilfe:hover+.hilfe-text,.hilfe:focus-visible+.hilfe-text{display:block}
.vorschlag{border-left:3px solid var(--akzent);padding:.4rem .6rem;margin:.6rem 0;color:var(--gedimmt);font-size:.85rem}
.btn{display:inline-block;padding:.55rem 1.2rem;border-radius:3px;background:var(--akzent);color:#0d1413;text-decoration:none;font-weight:600;letter-spacing:.06em;text-transform:uppercase;font-size:.85rem}
@media(max-width:640px){.zeile{grid-template-columns:1fr}}
/* Mail-Look: oeffentliche Seiten vor dem Login im Design der Transaktionsmails (Chris, 2026-08-24) */
body.hell{background:#EFEFEE;color:var(--absatz);max-width:none;margin:0;padding:36px 12px 8px;display:flex;flex-direction:column;align-items:center}
.mrahmen{width:min(680px,100%)}
.mleiste{background:var(--akzent);height:5px;border-radius:10px 10px 0 0}
.mkarte{background:var(--bg);border-radius:0 0 10px 10px;padding:0 44px 40px;font-size:16px;line-height:1.6}
.mkopf{display:flex;align-items:center;gap:.6rem;padding:22px 0 16px;border-bottom:1px solid var(--rand)}
.mkopf a{display:flex;align-items:center;gap:.6rem;text-decoration:none;color:inherit}
.mkopf b{color:var(--text);font-size:15px;font-weight:600;letter-spacing:5px}
.kicker{margin:34px 0 10px;font-size:13px;font-weight:700;letter-spacing:3px;color:var(--akzent);text-transform:uppercase}
.mtitel{font-family:Georgia,'Times New Roman',serif;font-weight:400;font-size:1.85rem;line-height:1.25;letter-spacing:0;text-transform:none;margin:0 0 20px;color:var(--text)}
.mkopf+.mtitel{margin-top:32px}
.mfuss{text-align:center;padding:22px 12px 8px;font-size:14px;line-height:1.7;color:var(--fuss)}
.mfuss a{color:var(--fuss)}
.hell .card{background:transparent;border:0;border-radius:0;padding:0;margin:0}
.hell .card::after{display:none}
.hell .card+.card{border-top:1px solid var(--rand);margin-top:30px;padding-top:26px}
.hell .card.dok{background:var(--flaeche2);border:1px solid var(--rand);border-radius:8px;padding:24px 28px;margin:26px 0}
.hell h2{font-family:Georgia,'Times New Roman',serif;font-weight:400;font-size:1.35rem;text-transform:none;letter-spacing:0;border:0;display:block;padding:0;margin:1.7rem 0 .7rem;color:var(--text)}
.hell h3{font-size:1.05rem;margin:1.2rem 0 .4rem;color:var(--text);letter-spacing:0}
.hell ul{margin:.5rem 0 1rem;padding-left:1.35rem}
.hell li{margin:0 0 .5rem}
.hell label{margin:1rem 0 .35rem;font-size:.88rem}
.hell input[type=text],.hell input[type=password],.hell input[type=email],.hell textarea{border-radius:6px;padding:.7rem .85rem;font-size:16px}
.hell button{border-radius:6px;padding:.8rem 1.9rem;font-size:16px;font-weight:700;letter-spacing:0;text-transform:none}
.hell button::after,.hell .btn::after{content:'  →'}
/* #183 (D5): Knopf, der wie ein Verweis aussieht – für Nebenwege, die zustandsändernd sind
   (POST + CSRF) und deshalb kein <a> sein dürfen. Ohne Pfeil, damit auf einer Seite genau
   EIN Primärknopf steht. Trefferfläche bleibt die Textzeile. */
button.textknopf,.hell button.textknopf{background:transparent;border:0;border-radius:0;padding:.2rem 0;margin:0;font:inherit;font-size:.85rem;font-weight:400;letter-spacing:0;text-transform:none;color:var(--akzent);text-decoration:underline;cursor:pointer}
button.textknopf:hover,.hell button.textknopf:hover{background:transparent;color:var(--text)}
.hell button.textknopf::after{content:''}
.hell .btn{border-radius:6px;padding:.75rem 1.6rem;font-size:15px;font-weight:700;letter-spacing:0;text-transform:none}
.hell .vorschlag{border:0;border-radius:6px;background:var(--flaeche2);padding:.7rem .9rem;margin:.8rem 0}
.hell .dok h1{font-family:Georgia,'Times New Roman',serif;font-size:1.35rem;font-weight:400;letter-spacing:0;text-transform:none;border-bottom:0;margin:.1rem 0 .6rem;color:var(--text)}
.hell .dok h2{font-size:1.1rem;margin:1.2rem 0 .4rem;color:var(--text)}
.hell .dok p,.hell .dok li{font-size:15px}
@media(max-width:640px){body.hell{padding:14px 8px 6px}.mkarte{padding:0 20px 30px}.mtitel{font-size:1.5rem}}
/* ================================================ #183 (Design-Paket, Abschnitt 2 der Synthese)
   Vier Befunde des UI-Laufs haben EINE Ursache und bekommen deshalb EINE Antwort statt vier
   Sonderfälle. Alles hier ist Rahmenwerk, das jede Seite benutzen darf.

   D2 – Arbeitsflächen dürfen breiter sein als Lesetexte. `body{max-width:900px}` ist die richtige
   Breite für einen Fließtext und die falsche für einen Kontrollstand: auf /admin blieben 414 px
   des 1280er Fensters leer, während vier magere Karten untereinander standen und die
   14-Spalten-Tabelle in 803 px gequetscht wurde. `body.breit` hebt die Grenze für Seiten, die
   Übersicht sind (heute /admin) – die Lesebreite aller anderen Seiten bleibt unangetastet. */
body.breit{max-width:1200px}
/* Kartenraster: schmale Karten nebeneinander, breite über die volle Zeile (`.card.voll`).
   auto-fit + minmax heißt: kein Umbruchpunkt wird gepflegt, das Raster rechnet ihn selbst aus. */
.raster{display:grid;grid-template-columns:repeat(auto-fit,minmax(23rem,1fr));gap:0 1rem;align-items:start}
.raster>.card{margin:1rem 0}
.raster>.card.voll{grid-column:1/-1}
@media(max-width:640px){.raster{display:block}}
/* Tabellen, die breiter sind als ihre Karte: der Überlauf muss SICHTBAR sein. `overflow:auto`
   allein zeigt auf Trackpads keinen Balken – die Karte wirkte beendet, obwohl 5 von 14 Spalten
   fehlten. Drei Mittel zusammen: eine Zeile, die es sagt; eine Fokus-fähige Region (tabindex),
   damit die Tastatur scrollen kann; und ein Verlauf an der rechten Kante als optisches Signal.
   Die erste Spalte bleibt stehen (sticky) – ohne sie stehen Saldo und Modell ohne Zuordnung da.
   Prüferlauf #183: Der Kantenschatten war rgba(0,0,0,.5) auf fast schwarzem Grund – am Bild kam
   nichts an, der Kommentar versprach mehr als die Seite hielt. In einem dunklen Theme zeichnet
   Licht die Kante, nicht Schatten: heller Schleier + eine sichtbare, schmale Bildlaufleiste. */
.tabelle{overflow-x:auto;max-width:100%;background:linear-gradient(to right,var(--flaeche) 30%,rgba(38,45,44,0)),linear-gradient(to left,var(--flaeche) 30%,rgba(38,45,44,0)) 100% 0,radial-gradient(farthest-side at 0 50%,rgba(255,255,255,.16),rgba(255,255,255,0)),radial-gradient(farthest-side at 100% 50%,rgba(255,255,255,.16),rgba(255,255,255,0)) 100% 0;background-repeat:no-repeat;background-size:2.5rem 100%,2.5rem 100%,.9rem 100%,.9rem 100%;background-attachment:local,local,scroll,scroll;scrollbar-color:var(--gedimmt) transparent}
.tabelle::-webkit-scrollbar{height:.55rem}
.tabelle::-webkit-scrollbar-thumb{background:var(--gedimmt);border-radius:999px}
.tabelle:focus-visible{outline:3px solid var(--fokus);outline-offset:2px}
.tabelle table{border-collapse:collapse;margin:.2rem 0;width:auto}
.tabelle th,.tabelle td{border:1px solid var(--rand);padding:.25rem .5rem;text-align:left;vertical-align:top;white-space:nowrap}
.tabelle th{background:#2e3534;color:var(--text);font-weight:600;letter-spacing:.04em}
.tabelle td:first-child,.tabelle th:first-child{position:sticky;left:0;z-index:1;background:var(--flaeche)}
.tabelle th:first-child{background:#2e3534}
/* Sichttest #169 (M1/M3, 2026-09-02): `white-space:normal` bricht nur ZWISCHEN Wörtern. Der
   Schlüssel eines gemerkten Chat-Eintrags (`chat.zusammenfassung.<12hex>.<12hex>`, 45 Zeichen)
   ist ein einziges Wort ohne Umbruchstelle – er trieb die erste Spalte über die Kartenbreite
   (gemessen: Region clientWidth 1130 / scrollWidth 1195, „Firmenweit sichtbar mac…“) und ließ auf
   390 px die ganze Seite waagerecht scrollen (documentElement.scrollWidth 432 bei 390). `anywhere`
   erlaubt den Umbruch mitten im Wort, aber erst wenn es sonst nicht passt. */
.tabelle td.wrap,.tabelle th.wrap{white-space:normal;min-width:16rem;overflow-wrap:anywhere}
/* Sichttest #169 (M2): Eine klebende erste Spalte, die breiter ist als der Scrollweg zur zweiten,
   frisst die zweite auf – in der Wissensliste verschwand beim Seitwärtsscrollen die Spalte „Art“
   restlos darunter, Kopfzelle inklusive. `.frei` schaltet das Kleben für solche Tabellen ab; für
   schmale Fenster trägt die Stapelansicht ohnehin. */
.tabelle.frei td:first-child{position:static;background:none}
/* Prüferlauf #183 (D6, zweiter Teil): `.umbruch` ist `.wrap` ohne Mindestbreite – eine Zelle, die
   umbrechen DARF, aber nichts reserviert. Nötig geworden mit der Spalte „Gerät": `html` skaliert
   die Schrift mit dem Fenster (`clamp(16px,0.9rem + 0.2vw,19px)`), `body{max-width:900px}` ist
   dagegen in px festgenagelt – die Tabelle wächst also, die Karte nicht. Mit sechs Spalten und
   durchweg `nowrap` maß die Tabelle bei 1920 px Fensterbreite 893 px in einer 825 px breiten
   Region: die Aktionsspalte lag ab rund 1400 px wieder außerhalb, „Beenden" abgeschnitten, also
   derselbe Befund wie unten – diesmal am oberen Ende. Die Geräteangabe ist die einzige Zelle mit
   mehreren Wörtern; darf sie umbrechen, schrumpft die Tabelle auf die Kartenbreite. */
.tabelle td.umbruch{white-space:normal}
.tabellenhinweis{margin:.2rem 0 .1rem}
/* Prüferlauf #183: Eine Tabelle, deren letzte Spalte die HANDLUNG trägt, ist auf dem Handy keine
   Tabelle mehr – der „Beenden"-Knopf der Sitzungsliste lag bei x=558 in einer 334 px breiten
   Region, unsichtbar und unerreichbar, und die Markierung „diese Sitzung" gleich mit. Seitliches
   Scrollen ist dafür kein Ersatz: was man nicht sieht, sucht man nicht. Unter der Schwelle wird
   jede Zeile deshalb zum Block, der Spaltenname kommt aus `data-spalte` vor den Wert, die
   Kopfzeile entfällt. Nur für Tabellen mit `.stapel` – Zahlenwerke wie die Mandantenliste bleiben
   Tabellen, dort trägt der Vergleich über die Zeilen die Aussage.

   #183 (D6, zweiter Teil): Schwelle von 640 auf 900 px angehoben. Die sechste Spalte („Gerät")
   hat die Tabelle über die Kartenbreite hinaus wachsen lassen – gemessen im Sichttest lag die
   Aktionsspalte zwischen 640 und 860 px Fensterbreite wieder außerhalb der sichtbaren Region,
   also genau der Befund von oben, nur eine Fenstergröße höher (schmales Desktop-Fenster, Tablet
   hochkant). Ab 900 px passt die Tabelle mit rund 40 px Luft in die Karte. */
@media(max-width:899px){
.tabelle.stapel{overflow-x:visible;background:none}
.tabelle.stapel table,.tabelle.stapel tbody,.tabelle.stapel tr,.tabelle.stapel td{display:block;width:auto}
.tabelle.stapel thead{display:none}
.tabelle.stapel tr{border:1px solid var(--rand);border-radius:6px;padding:.35rem .6rem;margin:0 0 .5rem}
.tabelle.stapel tr.eigene{background:rgba(57,162,174,.12)}
.tabelle.stapel td{border:0;padding:.1rem 0;white-space:normal;position:static;background:none;overflow-wrap:anywhere}
.tabelle.stapel td::before{content:attr(data-spalte) ": ";color:var(--gedimmt)}
}
/* Semantische Stufen-Etiketten. Der Befund: „Stufe: grenze" sah byte-gleich aus wie „Stufe: ok" –
   ausgerechnet der Zustand, der den systemd-Alarm auslöst. Farbe allein trägt die Aussage nicht
   (SC 1.4.1), deshalb steht das Wort im Etikett und der Rahmen zeichnet es zusätzlich. */
.badge{display:inline-block;border:1px solid var(--rand);border-radius:999px;padding:.05rem .6rem;font-size:.8rem;font-weight:600;letter-spacing:.04em;white-space:nowrap}
.badge-ok{border-color:#7fd8c9;color:#7fd8c9}
.badge-warnung{border-color:#e8c07a;color:#e8c07a}
.badge-grenze{border-color:#e0708a;color:#e0708a}
/* #169 (H3): „wirkt gerade nicht — Deckel" an der Wissenszeile. Eigene Klasse, weil `.klein`
   weiter unten steht und jede Farbklasse gleicher Spezifität überschreibt. Bernstein wie
   `.badge-warnung`: es ist eine Einschränkung, kein Fehler — und das Wort trägt die Aussage
   ohnehin allein (SC 1.4.1). */
.wnicht{color:#e8c07a}
/* #169 Phase 4 (Sichttest 2026-09-02): Filterzeile über der Wissensliste. Beschriftung und Feld
   sind EIN Flex-Element (`.wpaar`) – als getrennte Elemente riss der Umbruch auf 390 px die Paare
   auseinander („Art [alle Arten] Status" / „[alle Status] Sichtbar für"), und jede Beschriftung
   stand über dem falschen Feld. Die Zeile selbst bricht weiter um. */
.wfilter p{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem .9rem}
.wpaar{display:inline-flex;align-items:center;gap:.35rem;max-width:100%}
.wfilter label{color:var(--gedimmt);font-size:.85rem;white-space:nowrap}
/* Formular- und Filterfelder bleiben im Kasten: der Datenklassen-Auswahlliste gibt der Browser
   sonst die Breite ihrer längsten (gesperrten, erklärten) Option und schiebt sie aus der Karte. */
.wform{max-width:38rem}
.wform input,.wform textarea,.wform select,.wfilter select{max-width:100%}
.wform input[type=text]{width:100%}
/* Erfolgsmeldungen mit Fläche statt einer Zeile Farbtext (Muster `.fristen`). Eine Buchung mit
   Ledger-Wirkung hatte bisher die schwächste Rückmeldung der Seite. */
.erfolg{border:1px solid #7fd8c9;border-left:4px solid #7fd8c9;border-radius:4px;background:rgba(127,216,201,.10);padding:.6rem .9rem;margin:.6rem 0}
.erfolg h3{margin:0 0 .2rem;font-size:.95rem;letter-spacing:.04em;color:#7fd8c9}
.erfolg p{margin:.2rem 0}
.erfolg:empty{display:none}
/* Kopfzeile: „· Abmelden" landete allein in Zeile 2 – mit führendem Trennpunkt, weil der Trenner
   ein Textzeichen zwischen den Gliedern war. Ein Trenner, der umbrechen kann, ist immer ein
   Kandidat für eine solche Waise; deshalb trennt jetzt Abstand statt Zeichen. Die Glieder sind
   eigene Flex-Elemente (`.kopfrechts>span`), brechen als Ganzes um und tragen nichts vor sich her. */
.kopfrechts{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:.15rem 1.15rem;font-size:.85rem;color:var(--gedimmt)}
.kopfrechts>span{white-space:nowrap}
/* Formularfelder auf Arbeitsseiten: Auswahl 204 px neben Textfeld 830 px kam aus zwei Regeln, die
   sich widersprachen. Eine Höchstbreite für beide – und kurze Werte bekommen kurze Felder. */
body.breit .card input[type=text],body.breit .card input[type=email],body.breit .card select{max-width:26rem}
body.breit .card input.kurz{max-width:10rem}
/* h3 war größer als h2 und h1 (19,8 / 16,1 / 17,0 px) – die Ebenen standen auf dem Kopf. */
h3{font-size:.95rem;margin:1rem 0 .3rem;letter-spacing:.06em}
/* Die eigene Sitzung in der Sitzungsliste – erkennbar ohne Farbe allein (Wort „diese Sitzung"
   steht in der Zeile), die Fläche macht sie zusätzlich auffindbar. */
tr.eigene>td{background:rgba(57,162,174,.12)}
/* Mobil 390 px: die Kontoseite lief um 17 px über – Sitzungstabelle 407 px in 334 px und ein
   Auswahlfeld, das seinen längsten Optionstext ausschreiben wollte (379 px). Beides gedeckelt;
   die Tabelle scrollt in ihrer Region, das Feld bleibt in der Karte. */
select{max-width:100%}
@media(max-width:640px){.card select,.card input[type=text],.card input[type=email],
  body.breit .card select,body.breit .card input[type=text],body.breit .card input[type=email]{max-width:100%}}
/* Nachtrag Sichttest 2026-09-02 (#169 Phase 4): Die Zeile darüber allein reichte auf breiten
   Seiten NICHT. `body.breit .card select` (0,1,3) schlägt `.card select` (0,1,2) – der 26rem-
   Deckel blieb also gerade dort stehen, wo er nicht hingehört: bei 390 px lief die
   Datenklassen-Auswahl 54 px aus der Karte (416 px in 334 px). Der Deckel ist eine Regel gegen
   ZU BREITE Felder auf großen Seiten; unter 640 px ist er sinnlos, weil die Karte ohnehin
   schmaler ist als 26 rem. Deshalb steht `body.breit` in der Ausnahme mit drin. */
/* Bei vergrößerter Schrift (125/150 %) darf die Werkzeugzeile umbrechen statt zu quetschen: bei
   150 % blieb von „Prüftiefe" nur „Pr…" und vom Datei-Feld „– keine Dat…" – und zwar genau bei den
   Nutzern, die vergrößert haben, weil sie sonst nichts lesen können. Das Umbrechen selbst steht
   seit dem Befund vom 2026-09-02 in der Grundregel `.werkzeuge` (volles Modul-Set läuft sonst aus
   der Box); hier bleibt der Deckel für die Auswahlfelder. */
html.skala-125 .werkzeuge select,html.skala-150 .werkzeuge select{max-width:100%}
/* Löschen ist die destruktive Handlung und darf nicht aussehen wie Umbenennen (#183, D4). */
summary.sek.gefahr,button.gefahr{border:1px solid #e0708a;color:#e0708a;background:transparent}
button.gefahr{text-transform:none;letter-spacing:0;font-weight:600;padding:.4rem .8rem;font-size:.85rem;
  max-width:100%;white-space:normal;overflow-wrap:anywhere;text-align:left}
/* #190: Die Gefahr-Farbe schafft auf den drei dunklen Flächen 4,59–5,38:1 (SC 1.4.3) – der
   HOVER-Zustand aber nur 3,90:1, weil die 12-%-Tönung den Grund aufhellt. Deshalb hellt der Text
   beim Überfahren mit auf (#e5899f: 4,79–5,58:1); der Rahmen bleibt #e0708a und liegt mit
   3,90–4,54:1 weiter über den 3:1 aus SC 1.4.11. Gerechnet in docs/BARRIEREFREIHEIT.md. */
summary.sek.gefahr:hover,button.gefahr:hover{border-color:#e0708a;background:rgba(224,112,138,.12);color:#e5899f}
/* #190, im Hands-on aufgefallen: Aufgeklappt schlug `details.menu[open]>summary` (0,2,2) die
   Gefahr-Optik (0,2,1) – der Knopf wurde beim Öffnen wieder grau und sah aus wie „Umbenennen".
   Bei „Datei löschen" passiert das nicht, weil der Aufklapper dort `aufklapp` heißt, nicht `menu`.
   Zwei Regeln mit höherer Spezifität, damit beide unumkehrbaren Aktionen in JEDEM Zustand
   gleich aussehen – die Hover-Zeile muss mit, sonst gewönne die Ruhefarbe über der Tönung. */
details.menu[open]>summary.sek.gefahr{border-color:#e0708a;color:#e0708a}
details.menu[open]>summary.sek.gefahr:hover{color:#e5899f}
.loeschzone{margin-top:.9rem;border-top:1px solid var(--rand);padding-top:.6rem}
/* Der Explorer lief vertikal über, ohne es zu zeigen: der letzte Eintrag stand 61 px unter der
   Kante, der Löschknopf war angeschnitten. Derselbe Verlauf wie bei den Tabellen, nur senkrecht. */
.werkzeuge details.menu>.menupanel{background-image:linear-gradient(var(--flaeche) 30%,rgba(38,45,44,0)),linear-gradient(rgba(38,45,44,0),var(--flaeche) 70%),radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.45),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.45),rgba(0,0,0,0));background-repeat:no-repeat;background-size:100% 2.5rem,100% 2.5rem,100% .7rem,100% .7rem;background-position:0 0,0 100%,0 0,0 100%;background-attachment:local,local,scroll,scroll}
