/* Aiveloq — Oberflaeche.
   Leitregel (Vision §29): der Kunde sieht "Mein Computer", nicht NixOS, VNC
   oder eine VM. Deshalb steht hier nirgends ein technischer Begriff. */

/* HELL IST DER STANDARD, nicht mehr dunkel.
   Christoph: "Passe die Farbgebung auf das weiss (hauptsaechlich) / Blau an."

   Die Blautoene sind nicht gewaehlt, sondern AUS DEM LOGO GEMESSEN
   (scripts/logos_bauen.py arbeitet mit derselben Quelle):
     #20c0f0  das Cyan der hellen Schlaufe
     #0a6ef0  das Blau der Flaeche — das ist der Akzent
     #2030f0  das Indigo im Schatten
   Damit traegt die Oberflaeche dieselbe Farbe wie das Zeichen darauf, ohne
   dass jemand eine zweite Palette pflegen muss.

   Dunkel bleibt erreichbar: wer sein System dunkel gestellt hat, bekommt es
   weiter. Umgedreht wurde nur, was OHNE Angabe gilt. */
:root {
  --bg: #f4f7fb;
  --surface: #ffffff;
  --surface-2: #eef3fa;
  --line: #dbe4f0;
  --text: #16202c;
  --muted: #586a7e;
  --accent: #0a6ef0;
  --accent-hell: #20c0f0;
  --accent-ink: #ffffff;
  /* Das Rot des Dunkelmodus ist auf Weiss zu blass — knapp 3:1. Ein
     Loeschknopf muss man lesen koennen. */
  --danger: #c0271f;
  /* Schrift AUF rotem Grund. Ohne dieses Gegenstueck muesste jede rote
     Flaeche #ffffff raten — und im dunklen Thema ist --danger hell, da
     waere Weiss auf Rot kaum lesbar. */
  --danger-ink: #ffffff;
  /* Fuer das, was kein Fehler ist, aber auffallen muss: eine Rueckfrage,
     eine Anweisung ohne Ergebnis. Rot waere falsch — da ist nichts kaputt. */
  --warn: #a35a00;
  --warn-flaeche: #fff4e2;
  /* Diagrammfarben. Eigene Token, nicht --accent wiederverwendet: die
     Oberflächenfarbe darf sich ändern, ohne dass Gewinn und Verlust ihre
     Bedeutung tauschen. Drei Rollen, jede in einem EIGENEN Graphen — damit
     stehen nie zwei davon nebeneinander, und die Paare bleiben auch bei
     Farbsehschwäche weit auseinander. */
  --viz-kosten: #2a78d6;
  --viz-geld: #1baf7a;
  --viz-verlust: #e34948;
  --radius: 10px;
  /* Breite des Chats. JavaScript ueberschreibt sie beim Ziehen am Trenner
     und merkt sie sich im Browser — hier steht nur, womit es anfaengt. */
  --chatbreite: 380px;

  /* ---------------------------------------------- Oeffentliche Seiten ----
     Startseite und Preisseite sind die ersten Flaechen dieses Produkts, die
     jemand sieht, der es noch nicht kennt. Sie brauchen groessere Radien,
     echte Tiefe und eine Flaeche zum Atmen — alles drei gibt es in der
     Arbeitsoberflaeche bewusst NICHT, weil dort Dichte zaehlt.

     EIGENE TOKENS UND KEINE ZWEITE PALETTE: die Farben bleiben --accent und
     --surface. Was hier dazukommt, sind Masse. Eine zweite Farbpalette waere
     eine zweite Stelle, an der jemand die Marke aendern muesste — und
     --bg/--text/--accent sind ausserdem an workspace/nixos/modules/
     branding.nix gekoppelt (scripts/farbe_einheitlich.py vergleicht sie). */
  --radius-gross: 20px;
  --radius-riesig: 28px;
  /* Zwei Stufen Tiefe, mehr braucht es nicht. Weich und weit statt hart und
     eng — ein enger Schatten sieht nach Rahmen aus, ein weiter nach Licht. */
  --schatten-weich: 0 1px 2px rgba(16, 32, 44, .04), 0 8px 24px rgba(16, 32, 44, .06);
  --schatten-tief:  0 2px 4px rgba(16, 32, 44, .05), 0 24px 64px rgba(16, 32, 44, .12);
  /* Die Hoehe der Topleiste. Steht als Token, weil der Aufmacher darunter
     durchlaeuft und seinen Abstand daraus rechnet — zwei Zahlen, die
     auseinanderlaufen koennen, waeren eine Kante an der falschen Stelle. */
  --leiste-hoch: 60px;
  /* Der ruhige Verlauf hinter dem Aufmacher. */
  --schleier: radial-gradient(1100px 520px at 50% -10%,
                color-mix(in srgb, var(--accent-hell) 24%, transparent), transparent 72%);
}

/* DUNKEL NUR AUF AUSDRUECKLICHEN WUNSCH, nicht nach Systemeinstellung.
   ========================================================================
   Hier stand eine Media Query auf prefers-color-scheme: dark — und damit sah
   Christoph, dessen System dunkel steht, weiter eine dunkle Oberflaeche,
   obwohl er "weiss (hauptsaechlich) / Blau" gewuenscht hatte. Gesehen auf
   einem Bildschirmfoto der fertigen Seite.

   Eine Systemeinstellung zu beachten ist normalerweise richtig. Hier gewinnt
   die Produktentscheidung: das Aussehen gehoert zur Marke, und die Marke ist
   hell und blau. Der dunkle Satz bleibt erreichbar ueber
   <html data-theme="dark"> — fuer den Tag, an dem es dafuer einen Schalter
   gibt. */
:root[data-theme="dark"] {
  --bg: #0f1216; --surface: #171b21; --surface-2: #1e242c;
  --line: #2a323c; --text: #e8edf3; --muted: #9aa7b6;
  --accent: #4da3ff; --accent-hell: #7fdcff; --accent-ink: #06121f;
  --danger: #ff6b6b; --danger-ink: #2a0b0b;
  /* Fuer die dunkle Flaeche neu gestuft, nicht umgedreht: dieselben drei
     Farbtoene, aber auf den dunklen Grund gewaehlt. */
  --viz-kosten: #3987e5; --viz-geld: #199e70; --viz-verlust: #e66767;
  --warn: #ffbf69; --warn-flaeche: #2e2318;
  /* Masse bleiben gleich, Schatten nicht: auf dunklem Grund traegt ein
     schwarzer Schatten nichts. Dort macht Tiefe ein heller Saum. */
  --schatten-weich: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .35);
  --schatten-tief:  0 2px 4px rgba(0, 0, 0, .5), 0 24px 64px rgba(0, 0, 0, .55);
  --schleier: radial-gradient(900px 420px at 50% -8%,
                color-mix(in srgb, var(--accent) 22%, transparent), transparent 70%);
}

* { box-sizing: border-box; }

/* DAS VERSTECKEN GEWINNT. IMMER.
   ========================================================================
   Die Browser-Voreinstellung fuer [hidden] ist nur "display: none" mit der
   SCHWAECHSTEN Herkunft. Jede eigene display-Regel schlaegt sie — und zwar
   lautlos: das Element steht einfach da, obwohl das Programm es versteckt hat.

   Dieses Projekt ist dreimal darauf hereingefallen:
     · der ausgeschaltete Computer zeigte einen schwarzen Balken (.desktop iframe)
     · der Hinweis zum Ablegen blieb stehen (.drop-hint)
     · und zuletzt ein <label hidden>, das die Regel "label { display: block }"
       wieder sichtbar machte.

   Jedes Mal wurde EINE Zeile nachgetragen: "<selektor>[hidden] { display: none }".
   Das ist Pflege, die nie fertig wird. Hier steht stattdessen die eine Zeile,
   die es fuer alle erledigt — und check.sh prueft, dass sie bleibt.

   !important ist hier richtig und nicht faul: "versteckt" ist keine Gestaltung,
   die man ueberstimmen koennen soll. Wer ein verstecktes Element zeigen will,
   nimmt ihm das Attribut. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

button { font: inherit; cursor: pointer; }

/* ------------------------------------------------------------- Anmelden --- */
body.centered {
  display: grid; place-items: center; min-height: 100dvh; padding: 16px;
}

.card {
  width: min(400px, 100%);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 32px;
}

.card h1 { margin: 0 0 4px; font-size: 28px; letter-spacing: -0.02em; }
.lead { margin: 0 0 24px; color: var(--muted); }

label { display: block; margin: 14px 0 6px; font-size: 13px; color: var(--muted); }

input, textarea {
  width: 100%; padding: 10px 12px;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px;
  font: inherit;
}
input:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; }

.card button[type="submit"] {
  width: 100%; margin-top: 20px; padding: 11px;
  background: var(--accent); color: var(--accent-ink);
  border: 0; border-radius: 8px; font-weight: 600;
}
.card button[disabled] { opacity: .6; cursor: default; }

.error { margin: 12px 0 0; color: var(--danger); font-size: 14px; }
.note  { margin: 24px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; }

/* ----------------------------------------------------------------- Shell --- */
.shell {
  display: grid;
  /* Die mittlere Spalte ist der Trenner. Die Breite steht in --chatbreite und
     wird beim Ziehen gesetzt; der Standardwert gilt, bis jemand schiebt. */
  grid-template-columns: var(--chatbreite) auto 1fr;
  height: 100dvh;
  /* KEINE ROLLBALKEN AN DER SEITE. Christoph: "Bitte keine Scollbalken!"
     Eine Anwendung, die genau einen Bildschirm hoch ist, scrollt nie als
     Seite — und wenn doch, ist das immer ein Fehler im Inneren und nicht
     etwas, das der Nutzer wegscrollen soll. Gemessen war es die Knopfreihe
     oben rechts: sie braucht rund 630 px und schob ab einer schmaleren
     Desktop-Spalte das ganze Dokument auseinander; der waagerechte Balken
     fraß dann 15 px Hoehe und holte den senkrechten gleich mit. */
  overflow: hidden;
}

@media (max-width: 860px) {
  /* Schmal uebereinander — und der Trenner verschwindet, weil man auf einem
     Telefon nichts seitlich zu schieben hat. */
  .shell { grid-template-columns: 1fr; grid-template-rows: 40dvh auto 60dvh; }
  .trenner { display: none; }
}

.pane { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.pane-chat { background: var(--surface); }
.pane-desktop { background: var(--bg); }

@media (max-width: 860px) {
  .pane-chat { border-right: 0; border-bottom: 1px solid var(--line); }
}

/* Die Kopfzeile, und warum hier so viel steht.
   Christoph: "Schaue, dass der Bereich oben links sauber dargestellt wird,
   auch wenn man den Bereich kleiner zieht!" — auf seinem Bild brach
   "Mein Konto" mitten im Namen auf zwei Zeilen um.

   Drei Dinge zusammen loesen das:
     1. In einem Knopf wird NIE umgebrochen (white-space: nowrap weiter unten).
        Das allein beseitigt den haesslichsten Teil.
     2. Die Reihe darf umbrechen statt ueberzulaufen (flex-wrap) — aber nur
        links. Brechen die Knoepfe RECHTS um, aendert sich die Hoehe des
        Bildschirmbereichs, und weil die Sandbox ihre Aufloesung an diese
        Hoehe anpasst, wuerde der ganze Bildschirm neu aufgebaut.
     3. Was dann noch nicht passt, wird abgeschnitten statt das Fenster zu
        sprengen. */
.pane-head {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; border-bottom: 1px solid var(--line);
  min-height: 52px;
  overflow: hidden;
}
.pane-head > * { min-width: 0; }
.pane-chat > .pane-head { flex-wrap: wrap; row-gap: 6px; }
.spacer { flex: 1; }

.status { font-size: 13px; color: var(--muted); white-space: nowrap; }
.status[data-state="RUNNING"] { color: #4ade80; }
.status[data-state="ERROR"]   { color: var(--danger); }

.budget { font-size: 13px; color: var(--muted); white-space: nowrap; }

/* Der Name der Sandbox darf kuerzen, statt zu draengen. */
#ws-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.link, .ghost {
  background: transparent; color: var(--muted);
  border: 1px solid transparent; border-radius: 7px;
  padding: 5px 10px; font-size: 13px;
  /* NIE innerhalb eines Knopfs umbrechen. Genau das sah Christoph:
     "Mein Konto" stand zweizeilig im Knopf. */
  white-space: nowrap;
}
.ghost { border-color: var(--line); color: var(--text); }
.link:hover, .ghost:hover { background: var(--surface-2); }

/* ------------------------------------------------------------------ Chat --- */
.chat { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 14px; }

.msg {
  max-width: 92%;
  /* min-width: 0 ist in einem Flex-Container Pflicht: ohne das waechst die
     Blase mit ihrem laengsten Wort, statt es umzubrechen. */
  min-width: 0;
  padding: 10px 13px;
  border-radius: 12px;
  /* Lange Woerter umbrechen — Dateipfade, Adressen, Befehle. Christoph: "Der
     text links geht wenn er lang ist ueber die sprechblasen hinaus und wird
     nicht gebrochen." overflow-wrap allein reicht nicht fuer zusammenhaengende
     Zeichenketten ohne Trennstelle, deshalb zusaetzlich word-break. */
  overflow-wrap: anywhere;
  word-break: break-word;
}
.msg p { margin: 0 0 8px; }
.msg p:last-child { margin: 0; }
.msg.assistant { background: var(--surface-2); align-self: flex-start; }
.msg.user { background: var(--accent); color: var(--accent-ink); align-self: flex-end; }
.msg.system { background: transparent; color: var(--muted); font-size: 13px; align-self: center; text-align: center; }

/* Mehrere Aussagen in einer Blase: sichtbar getrennt, aber ohne neue Blase.
   Eine Haarlinie statt nur Abstand — sonst liest es sich wie ein einziger,
   seltsam springender Text. */
.msg .teil + .teil {
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent);
}

/* ----------------------------------------------------------- Kopierknopf --
   Christoph: "Mach einen button an jede Sprechblase, der den Inhalt in die
   Zwischenablage kopiert."

   Liegt IN der Blase, oben rechts, und ist normalerweise fast unsichtbar: ein
   Knopf an jeder Blase, der immer voll da ist, macht aus einem Gespraech eine
   Werkzeugleiste. Auf Geraeten ohne Mauszeiger gibt es kein :hover, dort steht
   er dauerhaft halb sichtbar. */
.msg { position: relative; }
.kopier {
  position: absolute; top: 4px; right: 4px;
  padding: 2px 7px; border-radius: 6px;
  border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
  /* AUS DER EIGENEN TEXTFARBE, nicht aus der Flaechenfarbe. Mit
     var(--surface) stand auf der blauen Nutzerblase ein fast weisser Kasten
     mit weisser Schrift darauf — unlesbar, gesehen auf dem Bildschirmfoto.
     currentColor passt sich an: dunkel auf hell, hell auf blau. */
  background: color-mix(in srgb, currentColor 12%, transparent);
  color: inherit;
  font-size: 11px; line-height: 1.5;
  opacity: 0; transition: opacity .15s;
}
.msg:hover > .kopier, .kopier:focus-visible { opacity: .85; }
.kopier:hover { opacity: 1; }
@media (hover: none) { .kopier { opacity: .55; } }
/* Platz fuer den Knopf, damit er nicht auf der ersten Zeile liegt. */
.msg.assistant > .teil:first-of-type, .msg.user > .teil:first-of-type { padding-right: 66px; }

/* Was das Modell gedacht hat. Zugeklappt, zurueckhaltend, kursiv — es ist
   Beiwerk, keine Antwort. */
.denken {
  margin-top: 8px; font-size: 12px; color: var(--muted);
  border-left: 2px solid var(--line); padding-left: 10px;
}
.denken summary { cursor: pointer; user-select: none; }
.denken p { margin: 6px 0 0; font-style: italic; white-space: pre-wrap; }

/* ---------------------------------------------------------------- Artefakt --
   Das Band ueber dem Eingabefeld: was gerade entstanden ist.
   ------------------------------------------------------------------------- */
.artefakt {
  display: flex; align-items: center; gap: 10px;
  margin: 0 14px 10px; padding: 9px 12px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2);
}
.artefakt[hidden] { display: none; }
.artefakt-symbol { font-size: 18px; line-height: 1; }
.artefakt-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.artefakt-text strong {
  font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.artefakt-text .hint { font-size: 11px; }
.artefakt button {
  padding: 6px 13px; border-radius: 7px; border: 1px solid var(--line);
  background: var(--accent); color: var(--accent-ink);
  font-size: 13px; font-weight: 600; white-space: nowrap;
}

/* ------------------------------------------- Die Knoepfe, die kommen und gehen --
   Christoph: "Die buttons unten rechts sind nicht immer so gut sichtbar. Mach
   sie bitte in rot so lange sie angezeigt werden."

   Gemeint sind die beiden Baender ueber dem Eingabefeld: "Anhalten", solange
   gearbeitet wird, und "Anzeigen", wenn etwas entstanden ist. Beide sind nur
   ZEITWEISE da — und genau deshalb waren sie schlecht zu finden: ein Knopf, der
   meistens fehlt, wird nicht gesucht, sondern uebersehen. Blau half dabei
   nicht, weil die ganze Oberflaeche blau ist.

   Rot ist hier kein Gefahrenzeichen, sondern das einzige Signal, das in dieser
   Farbwelt nicht untergeht. Es gilt nur, solange die Baender sichtbar sind —
   verschwinden sie, verschwindet auch das Rot. */
.artefakt button,
.lauf button {
  border: 1px solid var(--danger);
  background: var(--danger);
  color: var(--danger-ink);
  font-weight: 600;
}
.artefakt button:hover,
.lauf button:hover { filter: brightness(1.12); }
.artefakt button:focus-visible,
.lauf button:focus-visible {
  outline: 2px solid var(--danger);
  outline-offset: 2px;
}
/* Der Rahmen des Bandes zieht mit: ein roter Knopf in einem grauen Kasten sieht
   aus wie ein Versehen, der Kasten selbst soll die Aufmerksamkeit tragen. */
.artefakt:not([hidden]),
.lauf:not([hidden]) {
  border-color: color-mix(in srgb, var(--danger) 45%, var(--line));
}

.composer { display: flex; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--line); }
.composer textarea { resize: none; }
.composer button {
  padding: 0 16px; background: var(--accent); color: var(--accent-ink);
  border: 0; border-radius: 8px; font-weight: 600; white-space: nowrap;
}
.composer button[disabled] { opacity: .5; cursor: default; }

/* --------------------------------------------------------------- Desktop --- */
/* overflow: hidden — das Bild der Sandbox soll den Bereich nie sprengen.
   place-items: center bleibt, weil der Platzhalter ("Du hast noch keine
   Sandbox") mittig stehen soll; der iframe darunter fuellt ueber seine eigene
   Regel ohnehin alles. */
.desktop {
  flex: 1; position: relative; display: grid; place-items: center;
  min-height: 0; overflow: hidden;
}
/* Dieselbe Falle wie bei .drop-hint: ein eigenes "display" schlaegt die
   schwache Browser-Voreinstellung von [hidden]. Der ausgeschaltete Computer
   zeigte deshalb einen schwarzen Balken unter der Meldung. */
.desktop iframe[hidden] { display: none; }
.desktop iframe { width: 100%; height: 100%; border: 0; display: block; background: #000; }

/* --------------------------------------------------------------- Ausschnitt --
   Der eingefrorene Bildschirm.

   position:absolute mit inset:0 ueber dem Rahmen. Der Hintergrund ist dunkel
   und halbdurchsichtig, damit sofort klar ist: das hier ist ein Standbild, kein
   Computer, der haengt. Genau diese Verwechslung ist der Grund fuer die
   Leiste unten mit dem Hinweistext.
   ------------------------------------------------------------------------- */
.shot {
  position: absolute; inset: 0; z-index: 30;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  background: rgba(8, 11, 16, 0.82);
}
.shot[hidden] { display: none; }
.shot canvas {
  /* Der Zeiger IST hier der Stift — deshalb kein Pfeil. */
  cursor: crosshair;
  /* touch-action, sonst scrollt das Tablet statt zu malen. Dieselbe Falle wie
     beim Trenner. */
  touch-action: none;
  max-width: 100%; max-height: calc(100% - 56px);
  background: #000; box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6);
}
.shot-leiste {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 10px 14px; box-sizing: border-box;
}
.shot-hinweis { color: var(--muted); font-size: 13px; }
/* DIE LEISTE LIEGT AUF EINEM DUNKLEN, EINGEFRORENEN BILDSCHIRM.
   Christoph: "Beim Screenshot sind die Buttons nochmal und Abbrechen schlecht
   sichtbar, mache sie auch in blau zunächst."

   "Abbrechen" und "Noch mal" waren .ghost — transparenter Grund mit
   Textfarbe. Auf hellem Papier ist das zurueckhaltend und richtig; ueber einem
   Bildschirmfoto ist es unsichtbar, weil beides vom Bild darunter abhaengt.
   Hier bekommt also JEDER Knopf eine eigene Flaeche. */
.shot-leiste button {
  padding: 7px 14px; border-radius: 7px;
  border: 1px solid var(--accent);
  background: var(--accent); color: var(--accent-ink); font-weight: 600;
}
.shot-leiste button:disabled { opacity: 0.45; }
/* Auch der zurueckhaltende Knopf bleibt sichtbar: heller Grund statt keiner. */
.shot-leiste button.ghost {
  background: var(--surface); color: var(--accent);
  border-color: var(--accent); font-weight: 600;
}
.shot-leiste button.ghost:hover { background: color-mix(in srgb, var(--accent) 12%, var(--surface)); }
/* Und der eine Knopf, der etwas ERZEUGT, wird rot — aber erst, wenn er es
   wirklich kann. Vorher waere Rot ein Versprechen, das der Knopf nicht haelt. */
.shot-leiste button.bereit {
  border-color: var(--danger);
  background: var(--danger);
  color: var(--danger-ink);
}
.shot-leiste button.bereit:hover { filter: brightness(1.12); }

.placeholder { max-width: 460px; padding: 32px; text-align: center; }
.placeholder h2 { margin: 0 0 10px; font-size: 22px; letter-spacing: -0.01em; }
.placeholder p  { margin: 0 0 20px; color: var(--muted); }
.placeholder button {
  padding: 11px 22px; background: var(--accent); color: var(--accent-ink);
  border: 0; border-radius: 8px; font-weight: 600;
}
.placeholder button[disabled] { opacity: .6; cursor: default; }
.hint { font-size: 13px; color: var(--muted); margin-top: 16px !important; }

/* ============================================================ Verwaltung ===
   Eigene Seite, gleiche Sprache. Bewusst dicht: eine Kontenliste soll man
   ueberblicken, nicht durchscrollen.

   KLASSENNAMEN AUF DEUTSCH, und das ist kein Geschmack: die erste Fassung
   hiess '.user'. Dieselbe Datei bedient aber auch die Chat-Blase
   <div class="msg user"> — die wurde dadurch zum Raster mit 220px zweiter
   Spalte, und der Text brach nach jedem Wort um. Aufgefallen ist das erst
   im Bild, nicht in einem Test. */

/* Beide Adminseiten teilen die Rueckwand. Getrennte body-Namen, weil eine
   Klasse am <body> dieser Seite allein gehoert — css_kollision.py besteht
   darauf, und zu Recht: ein body-Name, den es zweimal gibt, wird frueher
   oder spaeter fuer die falsche Seite gestylt. */
body.seite-konten, body.seite-betrieb { min-height: 100dvh; }

.admin-head {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 20px;
  background: var(--surface); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 5;
}
.admin-head a.link { text-decoration: none; }

.admin-main { max-width: 1100px; margin: 0 auto; padding: 24px 20px 64px; }

.tiles {
  display: grid; gap: 12px; margin-bottom: 28px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.tile {
  display: flex; flex-direction: column; gap: 2px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 16px;
}
.tile-label { font-size: 12px; color: var(--muted); text-transform: uppercase;
              letter-spacing: .06em; }
.tile-value { font-size: 24px; letter-spacing: -0.02em; }
.tile-sub { font-size: 12px; color: var(--muted); }

.block { margin-bottom: 36px; }
.block-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.block-head h2 { margin: 0; font-size: 17px; }
.block-head select {
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px; padding: 6px 10px; font: inherit;
}

.konten { display: flex; flex-direction: column; gap: 10px; }

.konto {
  display: grid; gap: 10px 16px; align-items: center;
  grid-template-columns: minmax(0, 1fr) 220px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 16px;
}
.konto.off { opacity: .55; }
.konto-id { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.konto-mail { color: var(--muted); font-size: 13px; }
.konto-facts {
  display: flex; gap: 14px; flex-wrap: wrap;
  margin-top: 4px; font-size: 12px; color: var(--muted);
}

.tag {
  font-size: 11px; padding: 1px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  color: var(--accent); border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}
.tag.off { background: transparent; color: var(--muted); border-color: var(--line); }
.tag.me  { background: transparent; color: var(--muted); border-color: var(--line); }

.konto-budget { display: flex; flex-direction: column; gap: 5px; }
.bar { height: 5px; background: var(--surface-2); border-radius: 999px; overflow: hidden; }
.bar > span { display: block; height: 100%; background: var(--accent); }
.bar > span.warn { background: #e8a33d; }
.bar > span.full { background: var(--danger); }
.budget-text { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 4px; }
.budget-in { width: 70px; padding: 3px 6px; font-size: 12px; }

.konto-actions { grid-column: 1 / -1; display: flex; gap: 6px; flex-wrap: wrap; }
.konto-actions .ghost { font-size: 12px; padding: 4px 9px; }
.ghost.danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, transparent); }

.row-msg { grid-column: 1 / -1; margin: 0; font-size: 12px; color: var(--muted); }
.row-msg.err { color: var(--danger); }

.tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.tbl th {
  text-align: left; font-weight: 600; font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
  padding: 6px 10px; border-bottom: 1px solid var(--line);
}
.tbl td { padding: 7px 10px; border-bottom: 1px solid var(--line); }
.tbl td.num, .tbl th.num { text-align: right; font-variant-numeric: tabular-nums; }
.tbl td.detail { color: var(--muted); font-size: 12px; max-width: 320px;
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tbl code { font-size: 12px; }

dialog {
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 24px; width: min(420px, calc(100vw - 32px));
}
dialog::backdrop { background: rgba(0, 0, 0, .55); }
dialog h3 { margin: 0 0 8px; font-size: 18px; }
.checkline { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
.checkline input { width: auto; }
.dlg-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; }
.dlg-actions button:not(.ghost) {
  background: var(--accent); color: var(--accent-ink);
  border: 0; border-radius: 8px; padding: 8px 16px; font-weight: 600;
}
.err { color: var(--danger); font-size: 13px; }

@media (max-width: 720px) {
  .konto { grid-template-columns: 1fr; }
}

/* ================================================================= Inbox ===
   Der Upload-Knopf und der Streifen darueber. Bewusst schmal: die Inbox ist
   eine Durchgangsstation, keine Dateiverwaltung — die ist im Computer rechts. */

.pane-chat { position: relative; }

.attach {
  display: grid; place-items: center;
  width: 40px; flex: 0 0 40px;
  border: 1px solid var(--line); border-radius: 8px;
  color: var(--muted); cursor: pointer;
  /* margin: 0 — und das ist kein Aufräumen, sondern eine Korrektur.
     Die allgemeine Regel fuer <label> gab diesem hier 14 Punkte oben und 6
     unten; der Plus-Knopf war dadurch 49 Punkte hoch, waehrend Eingabefeld und
     Senden-Knopf daneben 69 sind. Aufgefallen ist es erst, als rechts vom Feld
     ein zweiter Knopf derselben Art dazukam: zwei gleich aussehende Knoepfe
     links und rechts, verschieden hoch. Im Bild sieht man es sofort, im Code
     nie. */
  margin: 0; align-self: stretch;
}
.attach:hover { color: var(--text); border-color: var(--accent); }
.attach:focus-within { outline: 2px solid var(--accent); outline-offset: 1px; }
.sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.inbox {
  border-top: 1px solid var(--line);
  padding: 8px 16px 0;
  max-height: 168px; overflow-y: auto; flex: 0 0 auto;
}
.inbox-head {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 12px; color: var(--muted); margin-bottom: 4px;
}
.inbox-head strong { color: var(--text); font-size: 12px;
                     text-transform: uppercase; letter-spacing: .06em; }
.inbox-note { font-size: 11px; }

.inbox-list { list-style: none; margin: 0; padding: 0 0 8px; }
.inbox-list li {
  display: flex; align-items: center; gap: 8px;
  padding: 3px 0; font-size: 13px;
}
.in-name { flex: 1; min-width: 0; overflow: hidden;
           text-overflow: ellipsis; white-space: nowrap; }
.in-size { color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.in-del {
  background: none; border: 0; color: var(--muted);
  font-size: 17px; line-height: 1; padding: 0 2px;
}
.in-del:hover { color: var(--danger); }
.in-del[disabled] { opacity: .4; cursor: default; }

/* [hidden] setzt display:none — aber NUR mit der schwachen
   Browser-Voreinstellung. Jede eigene display-Regel schlaegt sie, und dann
   liegt die Ablageflaeche dauerhaft ueber dem Chat. Genau das ist hier
   passiert und war erst im Bild zu sehen. */
.drop-hint[hidden] { display: none; }

.drop-hint {
  position: absolute; inset: 8px; z-index: 4;
  display: grid; place-items: center;
  background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  border: 2px dashed var(--accent); border-radius: var(--radius);
  color: var(--accent); font-weight: 600;
  /* Sonst schluckt die Flaeche selbst die dragleave-Ereignisse ihrer Kinder. */
  pointer-events: none;
}

/* Aktive Umschalter (Ton, Mikrofon) sollen sich sichtbar vom Ruhezustand
   unterscheiden — sonst weiss niemand, ob das Mikrofon offen ist. */
.ghost[data-on="1"] {
  color: var(--accent);
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

/* ============================================================== Konto =====
   Die Startseite nach dem Anmelden. Ruhig und kurz: wer bin ich, welche
   Computer habe ich, wie fange ich an. */

/* "seite-konto", nicht "konto": ".konto" ist die Kontozeile in der Verwaltung
   und setzt dort ein Raster mit 220-px-Spalte. Als body-Klasse verwendet, wurde
   die ganze Seite zu diesem Raster — Kopfzeile links, Inhalt rechts daneben,
   abgeschnitten.

   Das ist die ZWEITE Namenskollision dieser Art an einem Tag (die erste traf
   die Chat-Blasen). Deshalb gilt ab jetzt die Regel, die scripts/css_kollision.py
   prueft: eine Klasse am <body> darf nirgends sonst vorkommen. */
body.seite-konto { min-height: 100dvh; }

.konto-head {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 20px;
  background: var(--surface); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 5;
}
.konto-head a.link { text-decoration: none; }

/* Wort-Bild-Marke. Der Schriftzug ist weit gesperrt und leicht — der Name soll
   ruhig wirken, das Zeichen traegt die Aufmerksamkeit. */
.marke {
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--text); text-decoration: none;
}
.marke span { font-size: 19px; font-weight: 300; letter-spacing: .12em; }
.marke.klein span { font-size: 15px; }
.marke img { display: block; }

.konto-main { max-width: 860px; margin: 0 auto; padding: 32px 20px 72px; }
.konto-main h1 { margin: 0 0 6px; font-size: 30px; letter-spacing: -0.02em; }
.konto-main h2 { margin: 0; font-size: 17px; }

.computer-liste { display: flex; flex-direction: column; gap: 12px; }

.computer {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px 18px;
}
.computer-kopf { display: flex; align-items: center; gap: 14px; }
.computer-marke { opacity: .75; flex: 0 0 auto; }
.computer-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.computer-knoepfe { display: flex; align-items: center; gap: 8px; }
/* Das Dreipunktmenü an einer Sandbox-Karte. Dieselbe Geste wie an den
   Chat-Kacheln (.reiter-menue) — man soll sie nicht zweimal lernen. */
.computer-knoepfe .punkte {
  order: 9;                 /* immer ganz rechts, egal was davor steht */
  padding: 2px 8px;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 17px;
  line-height: 1;
  cursor: pointer;
}
.computer-knoepfe .punkte:hover,
.computer-knoepfe .punkte:focus-visible { color: var(--text); }

.zustand { font-size: 13px; color: var(--muted); margin-left: 10px; }
.zustand.laeuft   { color: #4ade80; }
.zustand.fehler   { color: var(--danger); }
.zustand.arbeitet { color: #e8a33d; }

/* Ein Hauptknopf, egal ob <a> oder <button> — beides kommt hier vor. */
.haupt {
  display: inline-block; padding: 8px 18px; border: 0; border-radius: 8px;
  background: var(--accent); color: var(--accent-ink);
  font: inherit; font-weight: 600; text-decoration: none; cursor: pointer;
}
.haupt[disabled] { opacity: .6; cursor: default; }
a.ghost { text-decoration: none; display: inline-block; }

.leer {
  background: var(--surface); border: 1px dashed var(--line);
  border-radius: var(--radius); padding: 28px; text-align: center;
}
.leer p { margin: 0 0 6px; }

.anleitung { margin: 14px 0 12px; padding-left: 22px; }
.anleitung li { margin-bottom: 12px; }
.anleitung strong { display: block; }
.anleitung li > :not(strong) { color: var(--muted); }

/* ---------------------------------------------------------------- Chats ----
   Die Chatliste als KACHELN, nicht als scrollende Leiste. Wer fuenf Chats hat,
   soll sie alle sehen, ohne zu schieben. Zwei nebeneinander; ein langer Name
   nimmt die ganze Zeile — das macht auto-fit von selbst. */
.chat-reiter {
  padding: 8px 10px 6px;
  border-bottom: 1px solid var(--line);
}
.reiter-liste {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 6px;
  max-height: 32vh;
  overflow-y: auto;
  overflow-x: hidden;
}
.reiter {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 2px 6px;
  padding: 6px 8px;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
}
.reiter:hover { border-color: var(--accent); }
.reiter.aktiv { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }

.reiter-name {
  grid-column: 1;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 13px;
  text-align: left;
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Welches Modell — klein darunter, damit man die Chats auseinanderhaelt, ohne
   hineinzusehen. Genau dafuer sind mehrere Chats da. */
.reiter-modell {
  grid-column: 1;
  grid-row: 2;
  font-size: 11px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.reiter-menue {
  grid-column: 2;
  grid-row: 1 / span 2;
  border: 0;
  background: transparent;
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
  padding: 2px 4px;
  cursor: pointer;
}
.reiter-menue:hover { color: var(--text); }

.reiter-fuss { display: flex; align-items: center; gap: 8px; padding-top: 6px; }

/* Der Hinweis im Archiv nimmt die ganze Zeile — sonst steht er als vierte
   "Kachel" zwischen den Chats und sieht wie einer von ihnen aus. */
.reiter-hinweis { grid-column: 1 / -1; margin: 0 2px 2px; font-size: 12px; }

/* Umbenennen geschieht IN der Kachel. Die allgemeine input-Regel (100 % Breite,
   10 px Polster) wuerde die Kachel aufblasen — hier also alles kleiner. */
.reiter-feld {
  width: 100%;
  min-width: 0;
  padding: 1px 4px;
  border-radius: 5px;
  background: var(--bg);
  font-size: 13px;
}

/* Das Kachelmenue haengt an <body>, nicht in der Kachel: die Kachelliste
   scrollt, und ein Menue darin wuerde an ihrem Rand abgeschnitten — bei der
   untersten Kachel also genau dann, wenn man es braucht. */
.kachelmenue {
  position: fixed;
  z-index: 50;
  min-width: 170px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.28);
}
/* Hausregel: wer display setzt, stellt [hidden] wieder her — sonst gewinnt das
   eigene display gegen die schwache Voreinstellung des Browsers, und ein
   verstecktes Element bleibt sichtbar. In diesem Projekt schon dreimal passiert. */
.kachelmenue button[hidden] { display: none; }
.kachelmenue button {
  display: block;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--text);
  font-size: 13px;
  text-align: left;
}
.kachelmenue button:hover,
.kachelmenue button:focus-visible {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  outline: none;
}
.kachelmenue button.gefahr { color: var(--danger); }
.kachelmenue button.gefahr:hover,
.kachelmenue button.gefahr:focus-visible {
  background: color-mix(in srgb, var(--danger) 14%, transparent);
}

/* Die Rueckfrage vor dem Loeschen. */
.frage {
  width: min(420px, 92vw);
  padding: 18px 20px 16px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
}
.frage::backdrop { background: rgba(0, 0, 0, 0.55); }
.frage strong { display: block; font-size: 16px; }
.frage p { margin: 8px 0 0; color: var(--muted); font-size: 14px; }
.frage-knoepfe { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; }
.frage-knoepfe button { padding: 8px 14px; border-radius: 8px; font-size: 14px; }
.frage-knoepfe .gefahr {
  border: 1px solid var(--danger);
  background: var(--danger);
  color: #ffffff;
  font-weight: 600;
}
.frage-knoepfe .gefahr:hover { filter: brightness(1.08); }

/* Ein Trennstrich im Menue — zwischen "wechseln", "verwalten" und "neu". */
.kachelmenue hr {
  height: 1px;
  margin: 4px 6px;
  border: 0;
  background: var(--line);
}
/* Die gerade gezeigte Sandbox bekommt einen Haken. Ein Menue, in dem man
   nicht sieht, wo man gerade ist, beantwortet die halbe Frage nicht. */
.kachelmenue button.aktiv { font-weight: 600; }
.kachelmenue button.aktiv::before { content: "✓ "; }
/* Damit die Beschriftungen trotz Haken auf einer Flucht stehen — aber NUR in
   einem Menue, das ueberhaupt einen Haken hat. Sonst stehen die Punkte eines
   gewoehnlichen Menues ohne Grund eingerueckt da (auf der Startseite gesehen). */
.kachelmenue:has(button.aktiv) button:not(.aktiv)::before {
  content: "";
  padding-left: 1.1em;
}

/* Das Eingabefeld in einer Rueckfrage. Es war bisher unformatiert — ein nackter
   Browser-Kasten in einem gestalteten Fenster liest sich als unfertig. */
.frage input[type="text"] {
  width: 100%;
  margin-top: 12px;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: 14px;
}
.frage input[type="text"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}
/* Der Satz, der sagt, was zu tippen ist. */
.name-frage-bitte { margin-top: 14px !important; }
.name-frage-bitte code {
  padding: 1px 6px;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--text);
  font-size: 13px;
  user-select: all;          /* ein Doppelklick markiert den ganzen Namen */
}
/* Ein gesperrter Ja-Knopf muss AUSSEHEN wie gesperrt — sonst klickt man ihn
   und haelt die Seite fuer kaputt. */
.frage-knoepfe button:disabled { opacity: 0.45; cursor: not-allowed; }
.frage-knoepfe button:disabled:hover { filter: none; }

/* Der Name der Sandbox ist ein Knopf: dort wird gewechselt und verwaltet. */
.ws-wahl {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 42vw;
  padding: 3px 7px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.ws-wahl[hidden] { display: none; }
.ws-wahl:hover, .ws-wahl:focus-visible {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  outline: none;
}
.ws-wahl strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ws-pfeil { color: var(--muted); font-size: 11px; }

/* Die Modellwahl gehoert sichtbar an den Chat, nicht in Einstellungen: sie
   gilt pro Gespraech und darf mittendrin gewechselt werden. */
.chat-modell {
  display: flex;
  align-items: center;
  gap: 8px;
  /* UMBRECHEN STATT ZERQUETSCHEN. Seit hier drei Dinge stehen (Modell,
     Projekt, Gedanken-Schalter) blieb vom Modellnamen "An…" uebrig —
     gesehen auf einem Bildschirmfoto. Die Zeile ist eine Auskunft; zwei
     Zeilen sind in Ordnung, ein unlesbarer Name nicht. */
  flex-wrap: wrap;
  row-gap: 4px;
  padding: 6px 12px;
  font-size: 12px;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
}
/* Der Modellname bekommt den Platz, den er braucht — mindestens so viel,
   dass man ihn erkennt. */
.chat-modell .modell-knopf { flex: 1 1 11rem; min-width: 8rem; }
/* Der Schalter rutscht in die zweite Zeile, bevor etwas abgeschnitten wird. */
.chat-modell .denken-schalter { margin-left: auto; }
.chat-modell select {
  flex: 1;
  min-width: 0;
  padding: 3px 6px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 12px;
}

/* ------------------------------------------------------------- Trenner ----
   Schmal, aber greifbar: zwei Pixel sieht man, acht trifft man. */
.trenner {
  width: 10px;
  cursor: col-resize;
  background: transparent;
  border-left: 1px solid var(--line);
  /* OHNE touch-action faengt der Browser den Zeiger fuer seine eigenen Gesten
     ab (Scrollen, Textauswahl) und bricht das Ziehen ab, sobald man sich
     bewegt. Genau das war "laesst sich noch nicht vernuenftig ziehen".
     "flex: none" stand hier und war wirkungslos: .shell ist ein Raster. */
  touch-action: none;
  /* Ein sichtbarer Griff. Acht unsichtbare Pixel findet niemand. */
  display: flex;
  align-items: center;
  justify-content: center;
}
.trenner::before {
  content: "";
  width: 2px;
  height: 28px;
  border-radius: 2px;
  background: var(--line);
}
.trenner:hover::before, .trenner:focus-visible::before { background: var(--accent); }
.trenner:hover, .trenner:focus-visible { background: color-mix(in srgb, var(--accent) 25%, transparent); }
/* Waehrend des Ziehens darf nichts markiert werden und der iframe darf die
   Maus nicht abfangen — sonst bleibt der Trenner am Desktoprand haengen. */
body.zieht { user-select: none; }
body.zieht iframe { pointer-events: none; }

/* ------------------------------------------------------- Modellauswahl ----
   Abschnitte nach Zweck, beliebteste oben. Die Reihenfolge kommt von
   OpenRouter und ist deren echte Nutzungsrangliste. */
.modell-knopf {
  flex: 1;
  min-width: 0;
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 12px;
  text-align: left;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.modell-knopf:hover { border-color: var(--accent); }

/* Woran dieser Chat arbeitet. Zurueckhaltender als die Modellwahl: es ist
   eine Auskunft, die man selten aendert — aber man muss sie aendern koennen. */
.an-projekt {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 12px; color: var(--muted); white-space: nowrap;
}
.an-projekt[hidden] { display: none; }
.projekt-knopf {
  padding: 3px 7px; border-radius: 6px;
  border: 1px dashed var(--line); background: transparent;
  color: var(--accent); font: inherit; font-size: 12px; font-weight: 600;
  max-width: 14ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.projekt-knopf:hover { border-color: var(--accent); border-style: solid; }

.denken-schalter {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; color: var(--muted); white-space: nowrap; cursor: pointer;
}
.denken-schalter[hidden] { display: none; }

.modellwahl {
  width: min(560px, 92vw);
  max-height: 80vh;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
}
.modellwahl::backdrop { background: rgba(0, 0, 0, 0.55); }
.modellwahl-kopf {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
}
/* Die Suchleiste. Eigene Zeile unter dem Kopf, volle Breite — sie gehoert zu
   BEIDEN Spalten darunter, denn gesucht wird ueber alle Bereiche. In den Kopf
   gequetscht saehe sie aus, als betraefe sie nur die Ueberschrift. */
.modellwahl-suche {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--line);
}
.modellwahl-suche input {
  flex: 1 1 auto; min-width: 0;
  padding: 8px 11px;
  border: 1px solid var(--line); border-radius: 8px;
  background: var(--surface-2); color: var(--text);
  font: inherit; font-size: 14px;
}
.modellwahl-suche input:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
  border-color: var(--accent);
}
/* Der Haken „Nur kostenlose". flex-shrink: 0, damit er nicht zusammenfaellt,
   wenn das Suchfeld daneben waechst — ein halb abgeschnittenes Wort neben
   einem Kaestchen liest sich wie ein Fehler. */
.modellwahl-gratis {
  display: flex; align-items: center; gap: 6px;
  flex: 0 0 auto;
  font-size: 13px; color: var(--muted);
  cursor: pointer; user-select: none;
  white-space: nowrap;
}
.modellwahl-gratis input { flex: 0 0 auto; width: auto; min-width: 0; padding: 0;
                           accent-color: var(--accent); cursor: pointer; }
.modellwahl-gratis:hover { color: var(--text); }
/* Gesetzt soll man ihm ansehen, dass gefiltert wird — sonst sucht jemand ein
   Modell, das es gibt, und findet es nicht. */
.modellwahl-gratis:has(input:checked) { color: var(--accent); font-weight: 600; }

/* Fehler aus dem Dialog, im Dialog. */
#modellwahl-fehler {
  flex: 0 0 auto; font-size: 12px; color: var(--danger);
}

/* ---------------------------------------------------------- Favoriten ----- */
/* Der Stern zum Anheften. Ein <span role="button"> — die Zeile selbst ist
   schon ein <button>, und ein Button im Button ist ungueltiges HTML. */
.modell-stern {
  /* ABSOLUT, UND DAS WAR EIN FEHLER IM ERSTEN ANLAUF.
     Vorher stand hier `margin-left: auto` in .modell-kopf. Das sieht richtig
     aus, bis ein Name kurz genug ist, dass Name + Etikett in EINE Zeile
     passen: .modell-kopf bricht um (flex-wrap), und eine automatische Marge
     wirkt dann PRO ZEILE. Der Stern stand bei „TypeSafe: Jev Router" mitten
     zwischen Name und Etikett, bei allen anderen rechts aussen.
     Nur im Bild zu sehen, und genau so etwas laesst eine App unfertig wirken. */
  position: absolute;
  top: 6px; right: 6px;
  /* GROESSER ALS DAS ZEICHEN. Ein 12px-Stern ist auf einem Laptop-Trackpad
     nicht zu treffen, und daneben zu klicken heisst hier: Modell ausgewaehlt
     und Dialog zu. Der Fehlklick ist also teuer. */
  min-width: 28px; min-height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 6px;
  font-size: 15px; line-height: 1;
  color: var(--muted);
  cursor: pointer;
}
.modell-stern:hover { background: var(--surface-2); color: var(--accent); }
.modell-stern[aria-pressed="true"] { color: #e0a93b; }
.modell-stern:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.modellwahl-suche .hint { white-space: nowrap; font-size: 12px; }
.modellwahl-suche .hint[hidden] { display: none; }

/* Zweispaltig: links die Bereiche, rechts die Modelle. */
.modellwahl-koerper { display: grid; grid-template-columns: 210px 1fr; min-height: 0; }
.modellwahl-nav {
  max-height: 56vh; overflow-y: auto; padding: 8px;
  border-right: 1px solid var(--line);
}
.modellwahl-sprung {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 7px 8px; border: 0; border-radius: 7px;
  background: transparent; color: inherit; font-size: 13px; text-align: left;
}
.modellwahl-sprung:hover, .modellwahl-sprung:focus-visible {
  background: color-mix(in srgb, var(--accent) 12%, transparent); outline: none;
}
.modellwahl-zahl { margin-left: auto; font-size: 11px; color: var(--muted); }
.bereich-bild { font-size: 15px; line-height: 1; }
.modellwahl-liste { max-height: 56vh; overflow-y: auto; padding: 6px 8px; scroll-behavior: smooth; }
.modell-abschnitt { display: flex; align-items: center; gap: 8px; scroll-margin-top: 4px; }

@media (max-width: 640px) {
  /* Auf schmalen Schirmen ist eine Spalte genug — die Navigation faellt weg. */
  .modellwahl-koerper { grid-template-columns: 1fr; }
  .modellwahl-nav { display: none; }
}

.modell-abschnitt {
  margin: 14px 8px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.modell-abschnitt:first-child { margin-top: 4px; }

.modell-eintrag {
  display: block;
  width: 100%;
  /* Platz fuer den Stern oben rechts, und ein Bezugspunkt fuer ihn. */
  position: relative;
  padding: 8px 38px 8px 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.modell-eintrag:hover { background: rgba(127, 196, 255, 0.08); }
.modell-name { display: block; font-size: 14px; }
/* Was es kostet und was es NICHT kann, steht unter dem Namen — nicht in einer
   Enttaeuschung nach dem ersten Satz. */
.modell-info { display: block; font-size: 11px; color: var(--muted); }

/* [hidden] gewinnt NICHT gegen display — deshalb diese Zeile. Ohne sie waere
   der Knopf immer sichtbar, auch wenn es gar keine weiteren Bereiche gibt. */
.modellwahl-mehr[hidden] { display: none; }
.modellwahl-mehr {
  display: block;
  width: 100%;
  padding: 12px;
  border: 0;
  border-top: 1px solid var(--line);
  background: transparent;
  cursor: pointer;
}

/* Was wirklich auf dem Computer passiert ist — unter jeder Antwort.
   Ein Modell kann behaupten, was es will; ob eine Datei entstanden ist, weiss
   nur der Computer. */
.getan {
  margin-top: 6px;
  font-size: 11px;
  color: var(--muted);
}
/* Kostenlose Modelle sichtbar zurueckhaltender: sie stehen in der
   Beliebtheitsliste weit oben, taugen aber oft nicht fuer echte Arbeit. */
.modell-gratis .modell-name { opacity: 0.75; }

/* Die Schritte waehrend der Arbeit.
   Zurueckhaltend: sie sind Begleitung, nicht Inhalt. Der laufende Schritt ist
   deutlich, die erledigten verblassen — so sieht man die Reihenfolge, ohne
   dass die Blase zu einem Protokoll wird, das niemand liest. */
.schritt {
  margin: 2px 0;
  font-size: 12px;
  color: var(--muted);
  opacity: 0.68;   /* erledigt, aber noch lesbar — 0.5 war im hellen Modus zu blass */
  transition: opacity 0.2s;
}
.schritt.jetzt { opacity: 1; }
.schritt.jetzt::before {
  content: "▸ ";
  color: var(--accent);
}
/* WARTEN IST NICHT ARBEITEN. Wenn das Modell überlastet ist und wir gleich
   noch einmal fragen (agent/geduld.ts), soll das anders aussehen als
   Fortschritt — sonst liest man "Ich schreibe …" und "Ich versuche es noch
   einmal" als dasselbe. Kein Rot: es ist kein Fehler, nur eine Pause. */
.schritt[data-art="wartet"] { opacity: 1; }
.schritt[data-art="wartet"]::before {
  content: "◴ ";
  color: var(--warn, #c77700);
}
/* Solange gearbeitet wird, pulsiert die Blase ganz leicht — ein Lebenszeichen
   fuer die Sekunden, in denen das Modell nachdenkt und nichts meldet. */
.msg.arbeitet { animation: atmen 2s ease-in-out infinite; }
@keyframes atmen { 50% { opacity: 0.72; } }
@media (prefers-reduced-motion: reduce) {
  .msg.arbeitet { animation: none; }
}

/* ------------------------------------------------------------- Antworten ---
   Markdown in der Chatblase. Zurueckhaltend gesetzt: eine Ueberschrift in
   einer Chatnachricht soll gliedern, nicht schreien — im ersten Entwurf war
   sie groesser als die Bedienelemente der Seite. */
.antwort { font-size: 14px; line-height: 1.5; }
.antwort > :first-child { margin-top: 0; }
.antwort > :last-child { margin-bottom: 0; }
.antwort p { margin: 0 0 8px; }
.antwort h3, .antwort h4, .antwort h5, .antwort h6 {
  margin: 12px 0 6px;
  font-size: 14px;
  font-weight: 600;
}
.antwort ul, .antwort ol { margin: 0 0 8px; padding-left: 20px; }
.antwort li { margin: 2px 0; }
.antwort code {
  padding: 1px 4px;
  border-radius: 4px;
  background: color-mix(in srgb, var(--muted) 18%, transparent);
  font-size: 0.92em;
}
.antwort pre {
  margin: 0 0 8px;
  padding: 8px 10px;
  border-radius: 6px;
  background: color-mix(in srgb, var(--muted) 14%, transparent);
  /* Lange Zeilen duerfen die Blase nicht sprengen — und senkrecht gibt es
     hier nichts zu rollen. */
  overflow-x: auto;
  overflow-y: hidden;
}
.antwort pre code { padding: 0; background: none; font-size: 12px; }
.antwort blockquote {
  margin: 0 0 8px;
  padding-left: 10px;
  border-left: 2px solid var(--line);
  color: var(--muted);
}
.antwort a { color: var(--accent); }

/* Die Plattformzeilen in der Betriebskonsole: Titel links, Wert rechts. */
.systemzeile {
  display: flex;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
}
.systemzeile:last-child { border-bottom: 0; }
.systemzeile-titel { min-width: 180px; color: var(--muted); }

/* ----------------------------------------------------------- Anfragen ----- */
/* Die Liste im Betrieb. Jede Anfrage ist ein eigener Kasten und keine
   Tabellenzeile: der Freitext ist das Wichtigste daran, und ein Freitext in
   einer Tabellenzelle wird entweder abgeschnitten oder zerreisst das Raster. */
.anfrage {
  padding: 14px 0; border-bottom: 1px solid var(--line);
}
.anfrage:last-child { border-bottom: 0; }
.anfrage-kopf {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
}
.anfrage-wer { display: inline-flex; align-items: center; gap: 4px; }
/* Kaestchen und Beschriftung auf einer Zeile. Ohne das stapelt der Flex-Kopf
   darueber beides untereinander, und das Kaestchen steht ueber seinem
   eigenen Text — das sieht nach Fehler aus, nicht nach Schalter. */
.anfrage-filter { display: inline-flex; align-items: center; gap: 6px; }
/* Der Loeschknopf: Umriss wie die anderen, aber in Rot. Keine gefuellte rote
   Flaeche — die zoege in einer Liste aus Anfragen mehr Aufmerksamkeit auf
   sich als die Anfragen selbst. */
.ghost.gefahr { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 35%, transparent); }
.ghost.gefahr:hover { background: color-mix(in srgb, var(--danger) 8%, transparent); }
.anfrage-text {
  margin: 8px 0 0; color: var(--text); line-height: 1.55;
  /* Die Umbrueche aus dem Formular bleiben stehen. Wer seinen Bedarf in vier
     Zeilen aufzaehlt, soll ihn auch so gelesen bekommen. */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
/* Erledigt heisst blass, nicht weg: es soll noch auffindbar sein. */
.anfrage.erledigt { opacity: .55; }

/* Der Uebergabeknopf unter einer Antwort: deutlich, aber nicht der Hauptknopf
   der Seite — er ist ein Angebot, keine Aufforderung. */
.uebergabe {
  display: inline-block;
  margin-top: 10px;
  padding: 7px 12px;
  border: 1px solid var(--accent);
  border-radius: 8px;
  background: transparent;
  color: var(--accent);
  font-size: 13px;
}
.uebergabe:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }

/* Die Entscheidungsknoepfe.
   UNTEREINANDER UND VOLLE BREITE, nicht nebeneinander: die Titel sind
   verschieden lang, und vier verschieden breite Knoepfe in einer Zeile liest
   man als Rangfolge statt als Auswahl. Untereinander ist jeder gleich wichtig,
   und die Erklaerung hat Platz. */
/* NICHT ".frage": diesen Namen tragen schon die Bestaetigungsdialoge weiter
   oben (<dialog class="frage">). Ein zweites Regelwerk darauf haette ihnen
   display:flex und einen Abstand verpasst — und genau dieser Griff hat hier
   schon einmal einen geschlossenen Dialog dauerhaft sichtbar gemacht. */
.entscheidung { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.wahl {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: 9px 12px;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.wahl:hover  { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, transparent); }
.wahl:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.wahl-titel { font-size: 13px; font-weight: 600; }
.wahl-warum { font-size: 12px; color: var(--muted); }
/* Der QR-Code zum Koppeln.
   QUADRATISCH UND MIT WEISSEM RAND, auch im Dunkelmodus — ein QR-Leser sucht
   nach dunklen Modulen auf hellem Grund. Ein invertierter Code wird von
   manchen Kameras gelesen und von manchen nicht, und "manchmal geht es" ist
   bei einem Kopplungsvorgang schlimmer als "geht nie". */
.koppel-bild {
  display: block;
  width: min(280px, 70vw);
  margin: 14px auto 10px;
  padding: 12px;
  background: #fff;
  border-radius: 10px;
}
.koppel-bild svg { display: block; width: 100%; height: auto; }

.geraete { display: flex; flex-direction: column; gap: 10px; }
.geraet {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 12px 14px;
}
.geraet-name { font-weight: 600; }
.geraet-wann { font-size: 12px; color: var(--muted); }

.entscheidung-sonst { margin: 2px 0 0; font-size: 11px; color: var(--muted); }
.uebergabe[disabled] { opacity: .6; cursor: default; }

/* Die Hinweise an den Modellen. Klein und ruhig: sie sollen den Blick fuehren,
   nicht um Aufmerksamkeit kaempfen. */
.modell-kopf { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; }
/* HIESS BIS ZUM 08.10.2026 .marke — UND DAS WAR EIN FEHLER.
   Die Wort-Bild-Marke im Seitenkopf heisst auch .marke (Zeile 657). Gleiche
   Spezifitaet, und dieser Block steht SPAETER — also gewann er. Der Schriftzug
   im Kopf trug dadurch text-transform: uppercase und eine Pillen-Polsterung:
   auf jeder Seite stand "AIVELOQ" statt "aiveloq". Monatelang, ungesehen.
   Die Kollisionspruefung fand es nicht, weil sie Doppeldefinitionen nur fuer
   Dialog-Klassen prueft (css_kollision.py). Umbenannt wurde das ETIKETT und
   nicht die Marke: ein Preisklassen-Hinweis an einem Modell ist keine Marke. */
.etikett {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 6px;
  border-radius: 999px;
  white-space: nowrap;
}
.etikett-stern   { background: color-mix(in srgb, #e8a33d 22%, transparent); color: #a36a0d; }
.etikett-gratis  { background: color-mix(in srgb, var(--muted) 18%, transparent); color: var(--muted); }
.etikett-guenstig{ background: color-mix(in srgb, #2e9e5b 20%, transparent); color: #1d7a42; }
.etikett-stark   { background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--accent); }

/* Das Hilfe-Fenster. Dieselbe Bauart wie die Modellwahl — eine Seite, zwei
   Fenstersorten reichen. */
.hilfe {
  width: min(560px, 92vw);
  max-height: 86vh;
  padding: 0 20px 20px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
}
.hilfe::backdrop { background: rgba(0, 0, 0, 0.55); }
.hilfe-kopf {
  display: flex; align-items: center; gap: 10px;
  position: sticky; top: 0; padding: 16px 0 12px;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.hilfe .anleitung { margin: 16px 0 0; padding-left: 20px; }
.hilfe .anleitung li { margin-bottom: 14px; }
.hilfe .hint { margin-top: 16px; }

/* Die Arbeitsanzeige. Drei Punkte, die nacheinander aufleuchten — ruhig genug,
   um acht Minuten danebenzustehen, und deutlich genug, um zu sagen: es laeuft
   noch. Die Blase atmete vorher nur; das sieht man bei einem hellen Thema
   praktisch nicht. */
.denkt { display: inline-flex; gap: 4px; align-items: center; height: 14px; }
.denkt span {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--muted);
  animation: tupfen 1.3s ease-in-out infinite;
}
.denkt span:nth-child(2) { animation-delay: 0.18s; }
.denkt span:nth-child(3) { animation-delay: 0.36s; }
@keyframes tupfen {
  0%, 70%, 100% { opacity: 0.25; transform: translateY(0); }
  35%           { opacity: 1;    transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .denkt span { animation: none; opacity: 0.6; }
}

/* Das Arbeitsprotokoll. Breiter als die Hilfe: hier stehen Pfade. */
.protokoll { width: min(720px, 94vw); }
#protokoll-liste { padding-top: 8px; }
.prot-eintrag {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.prot-rechts {
  padding-left: 18px;
  border-left: 2px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
.prot-wirkung { margin: 0; font-size: 13px; overflow-wrap: anywhere; }
ul.prot-dateien { margin: 8px 0 0; padding-left: 18px; font-size: 12px; color: var(--muted); }
ul.prot-dateien li { overflow-wrap: anywhere; margin-bottom: 2px; }
@media (max-width: 680px) {
  .prot-eintrag { grid-template-columns: 1fr; gap: 10px; }
  .prot-rechts { padding-left: 12px; }
}
.prot-eintrag:last-child { border-bottom: 0; }
.prot-kopf { display: flex; flex-wrap: wrap; gap: 10px; align-items: baseline; font-size: 12px; }
.prot-zeit { color: var(--muted); }
.prot-modell { font-weight: 600; }
.prot-geraten { font-weight: 400; color: var(--muted); }
.prot-chat { color: var(--muted); margin-left: auto; }
.prot-frage { margin: 6px 0 0; font-size: 14px; overflow-wrap: anywhere; }
.prot-schritte, .prot-fuss {
  margin: 4px 0 0; font-size: 12px; color: var(--muted); overflow-wrap: anywhere;
}
.prot-system { color: #a36a0d; }

/* Der Hinweis statt des Einfuegen-Knopfs. Ruhig: er ist eine Auskunft, keine
   Aufforderung. */
.clip-hinweis {
  font-size: 11px;
  color: var(--muted);
  padding: 0 4px;
  white-space: nowrap;
}

/* ---------------------------------------------------------------- Lauf ----- */
/* Die Zeile "hier arbeitet noch etwas", mit dem Anhalten daneben.
   Sie sitzt UEBER dem Eingabefeld und nicht darunter: was den Zustand der
   Unterhaltung beschreibt, gehoert zum Verlauf, nicht zur Eingabe. */
.lauf {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; border-top: 1px solid var(--line);
  background: var(--surface-2); color: var(--muted);
  font-size: 13px;
}
.lauf .spacer { flex: 1 1 auto; }
/* Der blinkende Punkt. Bewusst langsam (1,6 s) — ein schnelles Blinken liest
   sich wie eine Warnung, und hier ist nichts kaputt. */
.lauf .punkt {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--accent); flex: 0 0 auto;
  animation: lauf-puls 1.6s ease-in-out infinite;
}
@keyframes lauf-puls { 0%, 100% { opacity: 1 } 50% { opacity: .25 } }
/* Wer kein bewegtes Bild will, bekommt keines. */
@media (prefers-reduced-motion: reduce) {
  .lauf .punkt { animation: none; }
}
.lauf button { white-space: nowrap; }

/* ------------------------------------------------------ Agent und Helfer --- */
/* Ein Helfer-Chat gehoert sichtbar unter seinen Auftraggeber. Einrueckung PLUS
   Linie: die Einrueckung allein ist bei zwei Spalten nicht eindeutig. */
.reiter.helfer {
  /* UEBER DIE GANZE BREITE, nicht in die naechste Spalte.
     Die Kachelliste ist zweispaltig, und darin landeten zwei aufgeklappte
     Helfer NEBENEINANDER — damit sah es aus wie zwei gewoehnliche Chats, und
     die Einrueckung, die sie ihrem Auftraggeber zuordnen soll, zeigte ins
     Leere. Untereinander ist eine Liste; nebeneinander ist ein Raster. */
  grid-column: 1 / -1;
  margin-left: 14px;
  border-left: 2px solid var(--accent);
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

/* Der Aufklapper am Auftraggeber. Er steht auch zugeklappt da — sonst waere
   nicht zu sehen, dass es Helfer gibt, und ein Klick waere ein Glueckstreffer. */
.reiter-mehr {
  grid-column: 1 / -1;
  justify-self: start;
  margin-top: 2px;
  padding: 1px 7px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: 6px;
  background: transparent;
  color: var(--accent);
  font-size: 11px;
}
.reiter-mehr:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
/* Der Auftraggeber bekommt kein lautes Abzeichen — nur das Wort "Agent" vor
   dem Modellnamen (siehe app.js) und einen etwas kraeftigeren Rahmen. */
.reiter.agent { border-color: var(--accent); }
/* Der Punkt wird absolut gesetzt — also braucht die Kachel einen Bezugspunkt.
   Ohne das sitzt er am naechsten positionierten Vorfahren, und das ist hier
   .pane-chat: dann klebt er oben rechts am ganzen Bereich. */
.reiter { position: relative; }
/* Derselbe pulsende Punkt wie an der Lauf-Zeile — ein Zeichen, nicht zwei. */
.reiter .punkt {
  position: absolute; top: 6px; right: 26px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--accent);
  animation: lauf-puls 1.6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .reiter .punkt { animation: none; } }

/* ------------------------------------------- Wiederholen und Schrittliste --- */
/* „Nochmal versuchen" sitzt unter der Fehlermeldung, nicht daneben: es ist die
   Folge dessen, was darueber steht. */
.nochmal {
  display: inline-block; margin-top: 8px;
  padding: 4px 10px; border-radius: 8px;
  border: 1px solid var(--accent);
  background: transparent; color: var(--accent);
  font-size: 13px; cursor: pointer;
}
.nochmal:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.nochmal:disabled { opacity: .5; cursor: default; }

/* Die zugeklappte Liste der schon erledigten Schritte. Bewusst so leise wie
   moeglich: sie ist ein Nachweis, keine Information fuer den Normalfall. */
.schritt-liste {
  margin: 2px 0 4px; font-size: 12px; color: var(--muted);
}
.schritt-liste summary { cursor: pointer; user-select: none; opacity: .75; }
.schritt-liste ol { margin: 4px 0 0; padding-left: 20px; }
.schritt-liste li { margin: 1px 0; }

/* ---------------------------------------------------------------- Graph ----- */
/* Der Baum eines Agent-Chats. Linien zeichnet CSS, die Knoten sind Knoepfe —
   anklickbar und mit der Tastatur erreichbar, was ein SVG nicht von selbst ist. */
.graph {
  padding: 8px 8px 10px;
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.knoten {
  display: block; width: 100%; text-align: left;
  padding: 5px 8px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface);
  color: var(--text); cursor: pointer; font: inherit;
}
.knoten.wurzel { border-color: var(--accent); font-weight: 600; }
.knoten.aktiv { outline: 2px solid var(--accent); outline-offset: 1px; }
.knoten:hover { background: var(--surface-2); }
.knoten-name {
  display: block; font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Woran gerade gearbeitet wird. Leiser als der Name und auf EINE Zeile
   begrenzt: der Text kommt aus einem Fortschrittsschritt und kann lang sein. */
.knoten-unten {
  display: block; font-size: 11px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Ein laufender Knoten atmet am Rand — kein zweiter Punkt, die Kachelliste
   darunter hat schon einen. */
.knoten.laeuft { border-color: var(--accent); }
.knoten.laeuft .knoten-unten { color: var(--accent); }

/* Die Aeste. Die senkrechte Linie geht vom Auftraggeber nach unten, der
   Querstrich zu jedem Helfer — gezeichnet mit Rahmen, ohne ein Bild. */
.aeste { margin: 0 0 0 14px; padding-top: 4px; }
.ast {
  position: relative;
  padding-left: 16px;
  padding-top: 6px;
}
.ast::before {          /* die Senkrechte */
  content: ""; position: absolute; left: 0; top: 0;
  width: 2px; background: color-mix(in srgb, var(--accent) 45%, var(--line));
  height: 100%;
}
.ast:last-child::before { height: 20px; }   /* endet am letzten Querstrich */
.ast::after {           /* der Querstrich */
  content: ""; position: absolute; left: 0; top: 20px;
  width: 14px; height: 2px;
  background: color-mix(in srgb, var(--accent) 45%, var(--line));
}
.graph-leer { margin: 8px 0 0 14px; font-size: 12px; }

/* ------------------------------------------------- Das muss man gesehen haben --
   Christoph: "Wenn Anweisung zu keinem direktem Ergebnis fuehren oder es fragen
   gibt, muss optisch etwas deutlicher darauf hingewiesen werden."

   Beides gab es vorher schon — aber nur als leise Fusszeile ("nichts auf deiner
   Sandbox veraendert") beziehungsweise gar nicht: eine Rueckfrage stand als
   gewoehnlicher Satz am Ende einer langen Antwort und ging darin unter. Wer
   danach weggeht und spaeter wiederkommt, glaubt, die Aufgabe laufe noch.

   Deshalb ein Band OBEN in der Blase, nicht unten: ganz oben steht es im Blick,
   bevor man die Antwort liest. Und die Blase selbst bekommt eine Kante, damit
   man sie auch im durchgescrollten Verlauf wiederfindet. */
.msg.achtung {
  border-left: 3px solid var(--warn);
  border-top-left-radius: 4px;
  border-bottom-left-radius: 4px;
}
.achtung-band {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 0 0 10px; padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--warn) 45%, transparent);
  border-radius: 8px;
  background: var(--warn-flaeche);
  color: var(--warn);
  font-size: 13px; line-height: 1.35;
}
.achtung-band strong { display: block; font-size: 13px; }
.achtung-band span { display: block; color: var(--text); opacity: .85; }
.achtung-zeichen { flex: 0 0 auto; font-size: 15px; line-height: 1.15; }

/* ------------------------------------------------------------- Warteliste --
   Was angestellt ist, waehrend schon gearbeitet wird.

   Steht ZWISCHEN Lauf-Zeile und Eingabefeld, also genau dort, wo der Satz
   hingegangen ist: oben "Arbeitet …", darunter "danach kommt das hier", und
   dann das Feld fuer den naechsten. Leise gehalten — das ist eine Auskunft,
   keine Aufforderung. */
.warteliste {
  display: flex; flex-direction: column; gap: 4px;
  padding: 6px 16px 2px;
  font-size: 12px; color: var(--muted);
}
.warteliste[hidden] { display: none; }
.warteliste-kopf { font-weight: 600; }
.warte-zeile {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 8px;
  border: 1px dashed var(--line); border-radius: 8px;
  background: var(--surface-2);
}
.warte-nr {
  flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--line); color: var(--text); font-size: 10px; font-weight: 600;
}
/* Der Text darf die Zeile nicht sprengen — eine Anweisung kann lang sein. */
.warte-text {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--text);
}
.warte-weg {
  flex: 0 0 auto; padding: 1px 7px; border-radius: 6px;
  border: 1px solid transparent; background: transparent;
  color: var(--muted); font-size: 14px; line-height: 1.2;
}
.warte-weg:hover { border-color: var(--danger); color: var(--danger); }
.warte-weg:disabled { opacity: .4; }
/* Die, die gerade dran ist, ist keine Warteschlange mehr. */
.warte-zeile.dran { border-style: solid; border-color: var(--accent); }

/* ------------------------------------------------------------------ Preise --
   Die Stellschrauben des Betriebs. Jede Zeile: Beschriftung, Feld, und daneben
   die Zahl, die das Feld einordnet — der kostendeckende Preis, die Marge. Ein
   Eingabefeld ohne diese Zahl waere eine Einladung zum Raten. */
.preise { display: flex; flex-direction: column; gap: 10px; max-width: 760px; }
.preis-zeile {
  display: grid;
  grid-template-columns: 220px 150px 1fr;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
  border-bottom: 1px solid var(--line);
}
.preis-zeile:last-child { border-bottom: 0; }
.preis-zeile label { font-size: 14px; }
.preis-zeile input { width: 100%; }
.preis-sub { font-size: 12px; color: var(--muted); }
.preis-sub button {
  margin-left: 6px; padding: 1px 8px; border-radius: 6px;
  border: 1px solid var(--accent); background: transparent;
  color: var(--accent); font-size: 12px;
}
/* Der Anteil ueber der Marge ist kein Formfehler — er ist ein Verlustgeschaeft.
   Deshalb rot und nicht grau. */
.preis-sub.warnung { color: var(--danger); font-weight: 600; }
@media (max-width: 720px) {
  .preis-zeile { grid-template-columns: 1fr; gap: 4px; }
}

/* Der aktuelle Gedanke in der Fortschrittszeile.
   Kursiv und leiser als eine Taetigkeitsmeldung: das eine ist, was das Modell
   TUT, das andere, was es sich dabei DENKT. */
.schritt .gedanke {
  font-style: italic;
  opacity: .85;
}
.schritt[data-art="denken"]::before {
  content: "– ";
  opacity: .6;
}

/* Der zweite Knopf im Artefakt-Band: herunterladen.
   Leiser als "Anzeigen" — das ist der Normalfall, dies der Ausgang. Beide
   bleiben rot (siehe oben), weil beide nur zeitweise da sind. */
.artefakt #artefakt-ab {
  background: transparent;
  color: var(--danger);
}
.artefakt #artefakt-ab:hover {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  filter: none;
}

/* ------------------------------------------------------------ Feedback-Chat --
   Baut auf .hilfe auf (static/feedback.js) und ergaenzt nur, was ein Chat
   zusaetzlich braucht: eine mitlaufende Liste, ein Eingabefeld und die rote
   Zahl am Knopf. Die Sprechblasen sind dieselben wie im Hauptchat (.msg) —
   derselbe Chat soll sich nicht an zwei Stellen verschieden anfuehlen. */
/* GENAU EIN SCROLLBALKEN, und zwar an der Liste.
   Gesehen im ersten Bild: der Dialog hatte einen eigenen Balken UND die Liste
   einen. Folge war nicht nur die doppelte Leiste — der Hinweis darunter, WAS
   uebertragen wird, lag unter der Kante. Ausgerechnet der Satz, den man vor dem
   Tippen lesen koennen muss, war nur zu finden, wenn man danach suchte.
   Deshalb: der Dialog ist eine Spalte fester Hoehe, Kopf, Eingabe und Hinweis
   stehen fest, und nur die Nachrichtenliste rollt. */
.feedback {
  width: min(620px, 94vw);
  display: flex; flex-direction: column;
  max-height: 86vh; overflow: hidden;
}
.feedback .fb-liste {
  display: flex; flex-direction: column; gap: 10px;
  /* BASIS auto, NICHT 0 — und das ist der Unterschied zwischen "passt" und
     "weg". Mit flex-basis:0 fordert die Liste keine Hoehe an; der Dialog ist
     aber nur so hoch wie sein Inhalt (86vh ist eine Obergrenze, keine Hoehe),
     also bekam sie nichts und schrumpfte auf einen Spalt. Im Bild war vom
     Gespraech eine halbe Zeile uebrig.
     Mit auto nimmt sie die Hoehe ihres Inhalts, waechst bei Platz und schrumpft
     bei Enge bis min-height — das ueberschreibt die automatische
     Flex-Mindesthoehe (min-content), die sonst jedes Schrumpfen verhindert. */
  flex: 1 1 auto; min-height: 140px; overflow-y: auto;
  padding: 16px 0 4px;
}
.feedback .fb-eingabe, .feedback > .hint, .feedback .fb-meldung { flex: 0 0 auto; }
.feedback .fb-liste .msg { max-width: 88%; }

/* Die Zeile, die der Server der ersten Nachricht voranstellt — sichtbar, aber
   leise: sie ist Auskunft darueber, was uebertragen wurde, und nicht Text des
   Nutzers. */
.feedback .fb-kopfzeile {
  font-size: 12px; opacity: .72; margin-bottom: 6px;
  padding-bottom: 5px; border-bottom: 1px solid currentColor;
}

.feedback .fb-eingabe { display: flex; gap: 8px; align-items: flex-end; margin-top: 10px; }
.feedback .fb-feld {
  flex: 1; resize: vertical; min-height: 62px;
  padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font: inherit; line-height: 1.45;
}
.feedback .fb-feld:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.feedback .fb-senden {
  padding: 10px 16px; border: 0; border-radius: var(--radius);
  background: var(--accent); color: var(--accent-ink);
  font: inherit; font-weight: 600; cursor: pointer;
}
.feedback .fb-senden:disabled { opacity: .55; cursor: default; }

/* Fehler in ROT und im Dialog, nicht als Hinweiszeile unter vielen: wer gerade
   etwas abschicken wollte, muss sehen, dass es nicht ankam. */
.feedback .fb-meldung {
  margin: 8px 0 0; font-size: 13px; color: var(--danger);
}

/* Die rote Zahl am Knopf. Sie ist der einzige Grund, ueberhaupt im Hintergrund
   nachzusehen — ohne sie merkt niemand, dass eine Antwort da ist. */
.fb-zahl {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 17px; height: 17px; margin-left: 5px; padding: 0 4px;
  border-radius: 9px; background: var(--danger); color: var(--danger-ink);
  font-size: 11px; font-weight: 700; line-height: 1;
  /* Nicht mit dem Text des Knopfs mitwachsen. */
  vertical-align: 1px;
}

/* GESCHLOSSENE DIALOGE BLEIBEN UNSICHTBAR.
   Der Browser erledigt das selbst — aber nur, solange niemand `display` auf
   einem <dialog> setzt. Genau das tun die beiden Feedback-Dialoge weiter unten
   (display:flex für das Spaltenlayout), und damit war die Regel des Browsers
   überschrieben: der Dialog stand dauerhaft in der Seite.
   Gesehen erst IM BILD — der Oberflächen-Screenshot zeigte den noch gar nicht
   geöffneten Dialog "Markieren und fragen" mitten auf der Kontoseite. Keine
   Messung hätte das gefunden; der Code war in sich schlüssig.
   Die Regel steht hier allgemein und nicht nur für die zwei: wer später
   irgendeinem Dialog ein `display` gibt, tappt sonst in dieselbe Falle. */
dialog:not([open]) { display: none; }

/* ---------------------------------------------------- Feedback: das Menü --
   Zwei Wege, keine Schublade. Große Flächen statt einer Liste: es sind zwei
   verschiedene Werkzeuge, keine zwei Einträge in einer Auswahl. */
.feedback-menue { width: min(520px, 94vw); }
.fb-wege { display: flex; flex-direction: column; gap: 10px; padding: 16px 0 4px; }
.fb-weg {
  display: block; width: 100%; text-align: left;
  padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font: inherit; cursor: pointer;
}
.fb-weg:hover, .fb-weg:focus-visible { border-color: var(--accent); background: var(--surface-2); }
.fb-weg strong { display: block; margin-bottom: 3px; }
.fb-weg span { display: block; font-size: 13px; color: var(--muted); line-height: 1.45; }

/* ------------------------------------------- Feedback: markieren & fragen --
   Dasselbe Spaltenlayout wie der Chat: nur das Bild rollt, Eingabe und Hinweis
   stehen fest. Sonst liegt der Satz, was übertragen wird, unter der Kante. */
.feedback-shot {
  width: min(900px, 96vw);
  display: flex; flex-direction: column;
  max-height: 90vh; overflow: hidden;
}
.feedback-shot .fb-shot-bild {
  flex: 1 1 auto; min-height: 120px;
  width: 100%; height: auto; object-fit: contain;
  margin-top: 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface-2);
  /* Ohne touch-action zieht der Finger die Seite statt des Kringels. */
  cursor: crosshair; touch-action: none;
}
.feedback-shot .fb-shot-hinweis { margin: 8px 0 0 !important; }
.feedback-shot .fb-shot-meldung { margin: 6px 0 0; font-size: 13px; color: var(--danger); }
.feedback-shot .fb-eingabe, .feedback-shot > .hint { flex: 0 0 auto; }
.feedback-shot .fb-shot-kommentar {
  flex: 1; resize: vertical; min-height: 46px;
  padding: 9px 11px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); color: var(--text); font: inherit; line-height: 1.45;
}
.feedback-shot .fb-shot-kommentar:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.feedback-shot .fb-shot-ok, .feedback-shot .fb-shot-neu {
  padding: 10px 16px; border: 1px solid var(--accent); border-radius: var(--radius);
  background: var(--accent); color: var(--accent-ink);
  font: inherit; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.feedback-shot .fb-shot-neu.ghost { background: var(--surface); color: var(--accent); }
.feedback-shot .fb-shot-ok:disabled { opacity: .55; cursor: default; }
/* Rot erst, wenn etwas eingekringelt wurde — dieselbe Regel wie beim
   Sandbox-Schnappschuss (style.css ".shot .bereit"). */
.feedback-shot .fb-shot-ok.bereit {
  border-color: var(--danger); background: var(--danger); color: var(--danger-ink);
}
.feedback-shot .fb-shot-ok.bereit:hover { filter: brightness(1.12); }

/* Bilder im Feedback-Verlauf. Christoph: "Das Screenshot soll im Prinzip als
   ganz normale Nachricht verschickt werden." Also keine Sonderbehandlung —
   dieselbe Blase, nur mit Bild darin. */
.feedback .fb-bild { display: block; margin-top: 8px; }
.feedback .fb-bild img {
  display: block; max-width: 100%; max-height: 260px;
  border-radius: 8px; border: 1px solid rgba(0, 0, 0, .18);
  /* Ohne das steht ein schmales Bild in einer breiten Blase links angeklebt. */
  width: auto;
}
.feedback .fb-bild:hover img { filter: brightness(1.04); }
.feedback .msg.user .fb-bild img { border-color: rgba(255, 255, 255, .35); }

/* Der Diktier-Knopf. Erbt .attach — gleiche 40x40-Fläche wie der Plus-Knopf
   links, damit die Eingabezeile symmetrisch bleibt. Nur zwei Dinge kommen
   dazu: er ist ein <button> (kein <label>), und er hat einen Ein-Zustand. */
/* border GEHOERT HIERHIN, obwohl .attach ihn schon setzt: ".composer button"
   weiter oben hat "border: 0" und ist spezifischer als ".attach" allein. Im
   Bild sah man es sofort — der Plus-Knopf hatte einen Rahmen, das Mikrofon
   schwebte daneben ohne. Gelesen haette man es nicht. */
.composer button.attach {
  border: 1px solid var(--line);
  background: var(--surface); color: var(--muted);
  padding: 0; cursor: pointer;
}
.composer button.attach:hover { border-color: var(--accent); color: var(--accent); }
.composer button.attach:disabled { opacity: .5; cursor: default; }
/* Läuft die Aufnahme: rot, nicht blau. Blau heißt in dieser Oberfläche
   "anklickbar", rot heißt "hier passiert gerade etwas, das du beenden musst" —
   dieselbe Logik wie beim Speichern eines umkringelten Screenshots. */
.composer button.attach[data-on="1"] {
  border-color: var(--danger); background: var(--danger); color: var(--danger-ink);
  animation: atmen 2s ease-in-out infinite;
}

/* ------------------------------------------------------------- Zahlen ------
   Sechs Zeitreihen untereinander. Je Graph EINE Reihe, deshalb keine Legende:
   es gibt nur eine Farbe, und die Überschrift sagt, was gezeichnet ist. */
.stats-steuer {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 18px; padding: 10px 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface);
}
.stats-steuer label { margin: 0; }
.stats-steuer select { width: auto; padding: 6px 10px; }
.stats-ansicht { display: flex; gap: 0; }
.stats-ansicht button {
  padding: 7px 14px; border: 1px solid var(--line); background: var(--surface);
  color: var(--text); font: inherit; cursor: pointer;
}
.stats-ansicht button:first-child { border-radius: 8px 0 0 8px; }
.stats-ansicht button:last-child { border-radius: 0 8px 8px 0; border-left: 0; }
.stats-ansicht button.an { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

.g-karte {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); padding: 14px 16px 10px; margin-bottom: 14px;
}
.g-kopf { display: flex; align-items: baseline; gap: 10px; margin-bottom: 2px; }
.g-kopf h3 { margin: 0; font-size: 15px; }
/* Die Kennzahl selbst gross, weil sie die Antwort ist — der Graph zeigt nur,
   wie sie zustande kam. */
.g-wert { margin-left: auto; font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums; }
.g-unter { margin: 0 0 6px; font-size: 13px; color: var(--muted); }
.g-flaeche { width: 100%; }

/* Raster und Achsen bleiben zurückhaltend — die Daten sind das Einzige, was
   laut sein darf. Durchgezogen, nicht gestrichelt: eine gestrichelte Linie
   sieht aus, als bedeute sie etwas. */
.g-raster { stroke: var(--line); stroke-width: 1; }
/* Die Null ist keine Rasterlinie: an ihr hängt der Unterschied zwischen
   Gewinn und Verlust. */
.g-null { stroke: var(--muted); stroke-width: 1; opacity: .75; }
.g-achse { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }

/* Ein Hinweis, der an einer Kurve hängt — etwa „ab hier gemessen". Er gehört
   an den Graphen und nicht in eine Fußnote, die niemand liest. */
.g-hinweis {
  margin: 6px 0 0; padding: 7px 10px; font-size: 12.5px; line-height: 1.45;
  border-left: 3px solid var(--warn); background: var(--warn-flaeche); color: var(--warn);
  border-radius: 0 6px 6px 0;
}
#stats-hinweise p { font-size: 13.5px; line-height: 1.5; color: var(--muted); }
#stats-hinweise strong { color: var(--text); }

/* „Diese Seite ist veraltet" — leise, aber nicht zu uebersehen.
   Kein Rot: es ist kein Fehler, sondern eine Auskunft. Und die Farben kommen
   aus den Tokens der Seite, damit es im Dunkelmodus mitgeht. */
.neue-fassung {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 16px 8px;
  padding: 10px 14px;
  border-radius: 10px;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--text);
  font-size: 13px;
  line-height: 1.4;
}
.neue-fassung[hidden] { display: none; }
.neue-fassung span { flex: 1; }
.neue-fassung button {
  flex: none;
  padding: 6px 14px;
  border-radius: 8px;
  border: 0;
  cursor: pointer;
  font: inherit;
  font-weight: 600;
  background: var(--accent);
  color: var(--accent-ink);
}

/* ------------------------------------------------- Die Sandbox entsteht ----
   Solange sie entsteht, gehoert der Bildschirm ihr. Ein modaler <dialog>
   sperrt die Seite wirklich; hier geht es nur noch darum, dass das Warten
   nicht wie ein Hänger aussieht. */
.einrichtung {
  width: min(460px, 92vw);
  padding: 24px 26px 20px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
  color: var(--text);
}
.einrichtung::backdrop {
  background: rgba(0, 0, 0, 0.62);
  backdrop-filter: blur(3px);
}
.einricht-kopf { display: flex; align-items: flex-start; gap: 14px; }
.einricht-kopf strong { display: block; font-size: 17px; }
.einricht-kopf p { margin: 4px 0 0; font-size: 13.5px; color: var(--muted); }

.einricht-balken {
  position: relative;
  height: 8px;
  margin-top: 20px;
  border-radius: 999px;
  background: var(--surface-2);
  overflow: hidden;
}
.einricht-balken i {
  display: block;
  width: 0;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--accent), var(--accent-hell));
  /* Sanft nachziehen statt springen: der Wert kommt in Schueben vom Server. */
  transition: width 400ms ease-out;
}
/* Ein Schimmer, der darueber laeuft. Er zeigt nichts an — er sagt nur, dass
   hier noch etwas passiert, wenn der Balken eine Weile stillsteht. */
.einricht-balken::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, #ffffff 45%, transparent), transparent);
  transform: translateX(-100%);
  animation: einricht-schimmer 1.8s ease-in-out infinite;
}
@keyframes einricht-schimmer { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
  .einricht-balken::after { animation: none; display: none; }
  .einricht-balken i { transition: none; }
}

.einricht-zahl {
  display: flex;
  margin: 8px 0 0;
  font-size: 12.5px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.einricht-seit { margin-left: auto; }

.einricht-schritte { margin: 18px 0 0; padding: 0; list-style: none; }
.einricht-schritte li {
  display: flex;
  gap: 9px;
  padding: 3px 0;
  font-size: 13.5px;
  color: var(--muted);
}
.einricht-schritte li::before { content: "○"; width: 1em; flex: none; }
.einricht-schritte li.fertig { color: var(--text); }
.einricht-schritte li.fertig::before { content: "✓"; color: #16a34a; }
.einricht-schritte li.laeuft { color: var(--text); font-weight: 600; }
.einricht-schritte li.laeuft::before { content: "◐"; color: var(--accent); }

.einricht-technik {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 2px 14px;
  margin: 18px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 12.5px;
}
.einricht-technik dt { color: var(--muted); }
.einricht-technik dd { margin: 0; color: var(--text); }

.einricht-weg { display: block; margin: 18px auto 0; }
.einricht-weg[hidden] { display: none; }

/* ============================================================================
   ÖFFENTLICHE SEITEN — Startseite und Preise
   ============================================================================

   Christoph: „Baue eine schicke Landing Page. Nutze den polierten Apple Style
   dafür." Was das hier konkret heißt, und warum:

     · GROSSE, RUHIGE SCHRIFT. Eine Überschrift, die den Platz nimmt, den sie
       braucht, wirkt sicher; eine, die sich klein macht, wirkt wie eine
       Bitte. clamp() statt Medienabfragen, damit sie stufenlos mitwächst.
     · WEISSRAUM STATT RAHMEN. Abschnitte werden durch Abstand getrennt, nicht
       durch Linien. Linien zerschneiden eine Seite in Kästen, Abstand lässt
       sie atmen.
     · TIEFE STATT KANTEN. Karten liegen auf der Fläche (Schatten), statt in
       sie eingezeichnet zu sein (Rahmen).
     · WENIG BEWEGUNG, UND NUR EINMAL. Abschnitte blenden beim ersten Erreichen
       auf. Keine Dauerbewegung, nichts, was beim Zurückscrollen wieder
       passiert — das ist der Unterschied zwischen gepflegt und zappelig.

   Alles davon gilt NUR hier. Die Arbeitsoberfläche bleibt dicht: dort sitzt
   jemand stundenlang und braucht Übersicht, keine Großzügigkeit. */

/* ------------------------------------------------------------- Topleiste --- */
/* Ersetzt .konto-head und .admin-head, die fünfmal handkopiert waren. Gebaut
   wird sie von static/topleiste.js — hier steht nur, wie sie aussieht. */
.topleiste {
  display: flex; align-items: center; gap: 10px;
  height: var(--leiste-hoch); padding: 0 20px;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  /* Der Milchglaseffekt ist der Grund, warum die Leiste über dem Inhalt
     liegen darf, ohne ihn zu verdecken. Browser ohne Unterstützung bekommen
     die 82-%-Fläche — lesbar, nur weniger hübsch. */
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  position: sticky; top: 0; z-index: 20;
}
/* Über dem Aufmacher: keine Kante, damit der Verlauf nicht zerschnitten wird. */
.topleiste.ueber { border-bottom-color: transparent; background: transparent; backdrop-filter: none; }
.topleiste.ueber.haftet {
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  backdrop-filter: saturate(180%) blur(16px);
  -webkit-backdrop-filter: saturate(180%) blur(16px);
  border-bottom-color: color-mix(in srgb, var(--line) 70%, transparent);
}
.topleiste .spacer { flex: 1; }

.topnav { display: flex; align-items: center; gap: 2px; margin-left: 10px; }
.topnav-punkt {
  color: var(--muted); text-decoration: none;
  padding: 6px 10px; border-radius: 8px; font-size: 14px;
  white-space: nowrap;
}
.topnav-punkt:hover { color: var(--text); background: var(--surface-2); }
.topnav-punkt.aktiv { color: var(--text); font-weight: 600; }

.topguthaben {
  font-size: 13px; color: var(--muted); text-decoration: none;
  padding: 5px 10px; border-radius: 999px; background: var(--surface-2);
  white-space: nowrap;
}
.topguthaben:hover { color: var(--text); }

.topwer {
  font-size: 13px; color: var(--muted); max-width: 190px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Zwei Sprachen, zwei Knöpfe — keine Auswahlliste. Bei genau zwei
   Möglichkeiten ist eine Liste ein Klick zu viel, und man sieht die Alternative
   nicht, ohne sie zu öffnen. */
.sprachwahl {
  display: inline-flex; gap: 2px; padding: 2px;
  background: var(--surface-2); border-radius: 999px;
}
.sprach-knopf {
  border: 0; background: transparent; color: var(--muted);
  padding: 4px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600; letter-spacing: .04em;
}
.sprach-knopf:hover:not(.aktiv) { color: var(--text); }
.sprach-knopf.aktiv { background: var(--surface); color: var(--text); box-shadow: var(--schatten-weich); }
.sprach-knopf[disabled] { opacity: .5; }

/* --------------------------------------------------- Namen von Menschen --- */
/* EIN NAME IST EINGABE, und Eingabe darf das Layout nicht sprengen.
   Seit der Kunde seinen Anzeigenamen selbst setzen kann (PUT /api/name), steht
   in der Begrüßung ein Text, den WIR nicht geschrieben haben. 80 Zeichen ohne
   Leerzeichen sind in Überschriftengröße rund 1200 Pixel — die Seite bekam
   dadurch einen waagerechten Bildlauf (gemessen: scrollWidth 1352 bei 1280
   Fensterbreite).

   `anywhere` und nicht `break-word`: break-word bricht erst, wenn die Zeile
   sonst überliefe, und lässt die Zeile vorher trotzdem zu breit werden.

   Die Kürzung der Länge im Handler (80 Zeichen) ist die andere Hälfte: sie
   verhindert Unfug in der Datenbank, nicht in der Darstellung. */
#gruss { overflow-wrap: anywhere; }
/* In der Topleiste war das schon abgefangen: `.topwer` kürzt seit jeher mit
   Auslassungspunkten (max-width, text-overflow). Hier stand kurzzeitig eine
   zweite .topwer-Regel — und zwei Blöcke mit demselben Klassennamen sind
   genau die Falle, wegen der es scripts/css_kollision.py gibt. Der
   Markenschriftzug stand monatelang falsch, weil .marke zweimal definiert war. */

/* -------------------------------------------------------- Name im Konto --- */
/* Feld und Knopf in einer Zeile, die Meldung dahinter. `flex-wrap` statt
   einer Umbruchregel je Breite: auf einem schmalen Fenster rutscht der Knopf
   von selbst unter das Feld. */
.namensform { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.namensform input { flex: 1 1 220px; max-width: 340px; }
/* Die Rueckmeldung neben dem Knopf.
   EIGENE REGELN, weil `.hinweis.gut` und `.hinweis.schlecht` bisher NUR
   innerhalb von `.formular` eine Farbe haben (die oeffentlichen Seiten) —
   hier stuende „Gespeichert ✓" und „Das hat nicht geklappt" in derselben
   Schriftfarbe nebeneinander. Erfolg und Fehler gleich aussehen zu lassen
   ist schlimmer, als gar nichts zu melden. */
.namensform .hinweis { font-size: 14px; }
.namensform .hinweis.gut { color: var(--viz-geld); }
.namensform .hinweis.schlecht { color: var(--danger); }

/* --------------------------------------------------------------- Gerüst --- */
.seite-start, .seite-preise, .seite-recht { background: var(--bg); }

.spalte { max-width: 1080px; margin: 0 auto; padding: 0 24px; }

.band { padding: 96px 0; }
.band.getoent { background: var(--surface-2); }
.band.abschluss { padding: 112px 0 128px; }

.abschnitt-titel {
  margin: 0 0 14px; text-align: center;
  font-size: clamp(28px, 3.4vw, 42px); font-weight: 600; letter-spacing: -0.025em;
  line-height: 1.12;
}
.abschnitt-titel.links { text-align: left; }
.abschnitt-unter {
  margin: 0 auto 52px; max-width: 620px; text-align: center;
  color: var(--muted); font-size: clamp(16px, 1.6vw, 19px); line-height: 1.55;
}
.abschnitt-unter.links { text-align: left; margin-left: 0; }
.mitte { text-align: center; margin: 44px 0 0; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }

/* ------------------------------------------------------------- Aufmacher -- */
.held {
  /* Der Verlauf beginnt HINTER der Topleiste, deshalb der negative Abstand:
     die Leiste liegt darüber und soll den Verlauf nicht abschneiden. */
  margin-top: calc(var(--leiste-hoch) * -1);
  padding: calc(var(--leiste-hoch) + 96px) 0 76px;
  position: relative;
  /* Der Grundschleier liegt direkt auf dem Abschnitt, der zweite Lichtpunkt
     auf ::before — denn nur DER bekommt eine Maske, und eine Maske auf dem
     Abschnitt selbst würde auch Titel und Knöpfe ausblenden. */
  background: var(--schleier);
}
/* Der zweite, versetzte Lichtpunkt.
   EINER ALLEIN liest sich als getönte Fläche; zwei, die sich überlagern und
   an verschiedenen Stellen sitzen, ergeben Licht. Aus --accent GEMISCHT und
   nicht als eigener Farbwert — sonst bliebe er bei einem Markenwechsel
   zurück. Die Maske lässt ihn nach unten auslaufen statt an einer Kante
   aufzuhören. */
.held::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(680px 320px at 82% 4%,
      color-mix(in srgb, var(--accent) 16%, transparent), transparent 66%),
    radial-gradient(520px 300px at 14% 22%,
      color-mix(in srgb, var(--accent-hell) 14%, transparent), transparent 64%);
  -webkit-mask-image: linear-gradient(to bottom, #000 68%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 68%, transparent 100%);
}
.held > * { position: relative; z-index: 1; }

/* Der Aufmacher weicht beim Weiterscrollen zurück.
   ÜBER EINE BILDLAUF-ZEITACHSE und nicht über ein scroll-Ereignis: die läuft
   im Darstellungsfaden und kostet den Hauptfaden nichts. Kann der Browser sie
   nicht, setzt static/bewegung.js die Klasse gar nicht erst — dann steht der
   Aufmacher still, und das ist kein Mangel. */
@supports (animation-timeline: view()) {
  @media not (prefers-reduced-motion: reduce) {
    .held-text.weicht {
      animation: held-weicht linear both;
      animation-timeline: view();
      /* Von „oben angekommen" bis „halb hinausgescrollt". Erst danach
         passiert etwas — sonst bewegt sich der Titel schon, bevor jemand
         ihn gelesen hat. */
      animation-range: exit-crossing 0% exit-crossing 85%;
    }
  }
}
@keyframes held-weicht {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(-40px) scale(.96); }
}
.held-text { text-align: center; }
.held h1 {
  margin: 0 0 20px;
  font-size: clamp(34px, 6vw, 68px); font-weight: 600;
  letter-spacing: -0.035em; line-height: 1.06;
  /* Der Zeilenumbruch steht im Text (\n) und wird hier wirksam. So entscheidet
     die Übersetzung selbst, wo sie umbricht — im Englischen sitzt die Zäsur an
     einer anderen Stelle als im Deutschen. */
  white-space: pre-line;
}
.held-unter {
  margin: 0 auto 32px; max-width: 640px;
  color: var(--muted); font-size: clamp(16px, 1.8vw, 20px); line-height: 1.5;
}
.held-knoepfe { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.held-fussnote { margin: 24px 0 0; color: var(--muted); font-size: 13px; }

.haupt.gross, .ghost.gross {
  padding: 14px 30px; border-radius: 999px; font-size: 16.5px;
  transition: transform .22s cubic-bezier(.22, .61, .36, 1), box-shadow .22s ease;
}
.haupt.gross { box-shadow: var(--schatten-weich); }
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .haupt.gross:hover, .ghost.gross:hover { transform: translateY(-1px) scale(1.02); }
  .haupt.gross:hover { box-shadow: var(--schatten-tief); }
}
/* Beim Drücken wieder herunter — ohne das fühlt sich der Knopf nach dem
   Loslassen schneller an als beim Klicken, und das liest sich als Hänger. */
.haupt.gross:active, .ghost.gross:active { transform: translateY(0) scale(.99); }
.haupt.klein { padding: 6px 14px; border-radius: 999px; font-size: 14px; }
.ghost.gross { border-color: var(--line); background: var(--surface); }

/* ---------------------------------------------------- Der Bildschirm ------ */
/* Der Rahmen ist nicht Deko: er sagt ohne ein Wort, dass hier ein echter
   Computer läuft und kein Chatfenster — und das ist der Unterschied, den die
   Seite überhaupt erklären muss. */
.schirm {
  margin: 56px auto 0; max-width: 960px;
  background: var(--surface); border-radius: var(--radius-riesig);
  box-shadow: var(--schatten-tief);
  overflow: hidden;
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}
.schirm-leiste {
  display: flex; gap: 7px; align-items: center;
  padding: 11px 16px; background: var(--surface-2);
  border-bottom: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}
.schirm-leiste span {
  width: 11px; height: 11px; border-radius: 50%;
  background: color-mix(in srgb, var(--muted) 35%, transparent);
}
.schirm-film { display: block; width: 100%; aspect-ratio: 16 / 9; background: var(--surface-2); }
.schirm-film[hidden] { display: none; }

/* Das Oberflaechenfoto im Aufmacher.
   ================================================================
   Derselbe Rahmen wie das Video, deshalb dieselbe Klasse — nur ohne die
   Versteck-Mechanik, denn ein Bild haengt an keinem Ladeereignis.

   `aspect-ratio` NICHT gesetzt: das Foto ist 1916x1136, also kein 16:9. Mit
   einer erzwungenen 16:9-Form wuerde die Oberflaeche verzerrt — und
   ausgerechnet das Bild, das zeigen soll, wie das Produkt WIRKLICH aussieht,
   zeigte es dann falsch. `height: auto` laesst dem Browser die echten
   Proportionen; `width`/`height` im Markup reservieren trotzdem den Platz. */
.schirm-bild { margin-top: 28px; }
.schirm-foto { display: block; width: 100%; height: auto; }

/* Die Unterschrift. AUSSERHALB des Rahmens, nicht darin: `.schirm` hat
   `overflow: hidden` und runde Ecken — ein figcaption darin wuerde unten
   angeschnitten. */
.schirm-text {
  margin: 14px auto 0; max-width: 680px; text-align: center;
  color: var(--muted); font-size: 14.5px; line-height: 1.6;
}

/* ----------------------------------------------------------- Drei Karten -- */
.drei {
  display: grid; gap: 20px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.karte {
  background: var(--surface); border-radius: var(--radius-gross);
  padding: 28px; box-shadow: var(--schatten-weich);
  border: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
}
.karte h3 { margin: 0 0 10px; font-size: 18px; font-weight: 600; letter-spacing: -0.01em; }
.karte p { margin: 0; color: var(--muted); line-height: 1.6; }

/* Beim Darüberfahren hebt sich die Karte einen Hauch.
   DREI PIXEL, nicht zehn. Eine Karte, die unter dem Zeiger springt, wirkt
   wie ein Knopf — und diese Karten sind keine. Der Effekt soll bestätigen,
   dass der Zeiger auf etwas liegt, und sonst nichts.

   `will-change` steht bewusst NICHT da: es würde für jede Karte dauerhaft
   eine eigene Ebene anlegen, auch wenn nie jemand hinfährt. */
.karte, .beispiel, .faq-punkt {
  transition: transform .3s cubic-bezier(.22, .61, .36, 1), box-shadow .3s ease;
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .karte:hover, .beispiel:hover {
    transform: translateY(-3px);
    box-shadow: var(--schatten-tief);
  }
}

/* --------------------------------------------- Gehefteter Abschnitt ------- */
/* Die Überschrift bleibt stehen, die Karten laufen daran vorbei.
   Das Muster, das Apple-Produktseiten tragen: eine Aussage bleibt im Blick,
   während die Belege durchlaufen.

   `align-items: start` ist die Zeile, an der es hängt — ohne sie streckt das
   Raster die linke Spalte auf volle Höhe, und `position: sticky` hat dann
   nichts, woran es kleben könnte. Das ist der häufigste Grund, warum ein
   sticky im Grid nicht wirkt, und man sieht es dem Ergebnis nicht an. */
.band.haftend { padding: 104px 0 108px; }
.zweispaltig {
  display: grid; gap: 48px;
  grid-template-columns: minmax(280px, 0.85fr) 1.15fr;
  align-items: start;
}
.haftet-kopf { position: sticky; top: calc(var(--leiste-hoch) + 56px); }
.haftet-kopf .abschnitt-unter { margin-bottom: 0; }
.untereinander { display: grid; gap: 18px; }
.karte.breit { padding: 32px; }
.karte.breit h3 { font-size: 21px; margin-bottom: 12px; }
.karte.breit p { font-size: 16px; }

/* ------------------------------------------------------------- Ablauf ----- */
/* Drei nummerierte Schritte auf der Startseite. Die Zahl ist gross: sie
   ordnet, ohne sich vor den Text zu stellen. Als <ol> und nicht als
   <div>-Reihe — die Reihenfolge ist hier die Aussage, und ein
   Vorleseprogramm soll sie kennen.

   HEISST `ablauf-*` UND NICHT `schritt-*`: `.schritt` gibt es bereits, in der
   Arbeitsoberflaeche, fuer die Schrittliste des Agenten (.schritt.jetzt,
   .schritt[data-art], .schritt .gedanke). Zwei Blöcke mit demselben
   Klassennamen sind die Falle, wegen der es scripts/css_kollision.py gibt —
   und die hat hier auch sofort angeschlagen. */
.ablauf {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 22px;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
.ablauf-schritt { position: relative; padding: 0 8px; }
.ablauf-zahl {
  display: block; margin-bottom: 10px;
  font-size: clamp(40px, 4.6vw, 58px); font-weight: 600;
  letter-spacing: -0.04em; line-height: 1;
  /* Ein Verlauf statt einer Flaeche: die Zahl soll Gewicht haben, ohne mit
     der Ueberschrift darunter um Aufmerksamkeit zu streiten. */
  background: linear-gradient(165deg,
    var(--accent),
    color-mix(in srgb, var(--accent) 45%, transparent));
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}
.ablauf-schritt h3 { margin: 0 0 8px; font-size: 19px; font-weight: 600; letter-spacing: -0.01em; }
.ablauf-schritt p { margin: 0; color: var(--muted); line-height: 1.6; }

/* ------------------------------------------------------------- Beispiele -- */
.beispiele {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.beispiel {
  background: var(--surface); border-radius: var(--radius-gross);
  padding: 24px; box-shadow: var(--schatten-weich);
  border: 1px solid color-mix(in srgb, var(--line) 50%, transparent);
  display: flex; flex-direction: column; gap: 8px;
}
/* ZWEI ZEILEN PLATZ, auch wenn der Titel nur eine braucht.
   Ohne das stand „2–14 Credits" 19 Pixel höher als die drei daneben, weil
   „Ein Bild erzeugen lassen" in eine Zeile passt und die anderen Titel nicht.
   Vier Preise auf vier verschiedenen Höhen lesen sich wie vier verschiedene
   Dinge — und im Englischen sitzt der Umbruch an einer anderen Stelle, also
   ist es nicht einmal dieselbe Karte, die ausschert.

   Gemessen und nicht geschätzt: getBoundingClientRect() über alle vier
   Karten, in beiden Sprachen. */
.beispiel-was {
  margin: 0; font-weight: 600; font-size: 15px;
  line-height: 1.45; min-height: 2.9em;
}
.beispiel-preis { margin: 0; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.beispiel-preis strong {
  font-size: clamp(20px, 2.2vw, 26px); font-weight: 600;
  letter-spacing: -0.02em; color: var(--accent);
}
.beispiel-preis span { color: var(--muted); font-size: 14px; }

/* Das Zeichen oben in der Karte.
   Der farbige Kreis dahinter ist nicht Deko: vier gleich aussehende weisse
   Karten liest man als Tabelle, vier mit einem eigenen Zeichen als vier
   Dinge. Die Flaeche ist aus --accent GEMISCHT und nicht ein eigener
   Farbwert — so zieht sie bei einem Markenwechsel mit. */
.beispiel-zeichen {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 13px;
  background: color-mix(in srgb, var(--accent) 11%, transparent);
  color: var(--accent);
}
.beispiel-zeichen svg { width: 24px; height: 24px; display: block; }

/* Das Credit/Euro-Verhaeltnis. Mittig unter den Karten, vor dem Hinweis —
   es ist die Umrechnung zu den Zahlen darueber und gehoert dichter an sie
   als an den Satz ueber Spannen. */
.beispiel-kurs {
  margin: 30px 0 0; text-align: center;
  font-size: 15px; font-weight: 600; letter-spacing: -0.01em;
  color: var(--text);
}
.beispiel-kurs[hidden] { display: none; }
/* Steht die Kurszeile da, rueckt der Hinweis naeher heran — sonst stehen
   zwei zentrierte Zeilen mit 28 Pixeln Luft dazwischen und sehen aus wie
   zwei Abschnitte. */
.beispiel-kurs:not([hidden]) + .beispiel-hinweis { margin-top: 10px; }
.beispiel-dazu { margin: 0; color: var(--muted); font-size: 13.5px; line-height: 1.55; }
.beispiel-hinweis {
  margin: 28px auto 0; max-width: 620px; text-align: center;
  color: var(--muted); font-size: 13.5px; line-height: 1.6;
}

/* ----------------------------------------------- Sonderausstattung -------- */
.zwei {
  display: grid; gap: 44px;
  grid-template-columns: 1fr 1fr; align-items: start;
}
.haken { margin: 22px 0 0; padding: 0; list-style: none; }
.haken li {
  position: relative; padding-left: 28px; margin-bottom: 12px;
  color: var(--muted); line-height: 1.55;
}
.haken li::before {
  content: "✓"; position: absolute; left: 0; top: 0;
  color: var(--accent); font-weight: 700;
}

.formular { display: block; }
.formular h3 { margin: 0 0 6px; }
.formular button[type="submit"] {
  margin-top: 18px; width: 100%;
  padding: 11px 18px; border: 0; border-radius: 10px;
  background: var(--accent); color: var(--accent-ink);
  font: inherit; font-weight: 600;
}
.formular button[disabled] { opacity: .6; cursor: default; }
.formular select {
  width: 100%; padding: 10px 12px;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px; font: inherit;
}
.formular .note { margin-top: 14px; }
.formular .hinweis { margin-top: 12px; }
.formular .hinweis.gut { color: var(--viz-geld); }
.formular .hinweis.schlecht { color: var(--danger); }

/* ------------------------------------------------------------ Rechtstexte -- */
/* Impressum und Datenschutzerklaerung. Eine SCHMALE Spalte und grosszuegige
   Zeilenhoehe — das sind Texte, die jemand wirklich von oben nach unten liest,
   und 1080 Pixel breite Absaetze liest niemand zu Ende.

   Die Ueberschriften sind kleiner als auf der Startseite: dort werben sie, hier
   gliedern sie. */
.band.recht { padding: calc(var(--leiste-hoch) + 56px) 0 80px; }
.band.recht .spalte { max-width: 760px; }
.band.recht h1 { margin-bottom: 8px; }
.band.recht h2 {
  margin: 38px 0 12px; font-size: 21px; font-weight: 600; letter-spacing: -0.01em;
}
.band.recht h3 { margin: 24px 0 6px; font-size: 16.5px; font-weight: 600; }
.band.recht p { margin: 0 0 14px; line-height: 1.7; color: var(--text); }
.band.recht .note { color: var(--muted); font-size: 14px; }
.band.recht ul.haken { margin: 0 0 14px; }
.band.recht ul.haken li { line-height: 1.7; }
.band.recht a { color: var(--accent); }

/* ------------------------------------------------------------------ Fuss -- */
.fuss { border-top: 1px solid var(--line); padding: 28px 0; background: var(--surface); }
.fuss-reihe { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.fuss-reihe .spacer { flex: 1; }
.fuss-reihe a { color: var(--muted); text-decoration: none; font-size: 14px; }
.fuss-reihe a:hover { color: var(--text); }
.fuss-marke { font-weight: 300; letter-spacing: .12em; color: var(--muted); }
/* „Cookie-Einstellungen" ist ein KNOPF (er öffnet etwas, er führt nirgends
   hin), soll aber neben Impressum und Datenschutz stehen, als wäre er einer
   von ihnen. Ein Knopf, der anders aussieht als die Links daneben, sieht
   nach Fehler aus. */
.fuss-einstellung {
  background: none; border: 0; padding: 0;
  color: var(--muted); font-size: 14px;
}
.fuss-einstellung:hover { color: var(--text); }

/* ------------------------------------------------------- Cookie-Banner ---- */
/* Unten rechts als Karte, NICHT als Vorhang über der Seite. Wer kommt, um zu
   lesen, was Aiveloq ist, soll das auch vor seiner Entscheidung können — eine
   Seite, die erst nach dem Klick lesbar wird, drängt zum schnellsten Knopf,
   und das wäre eine erzwungene Einwilligung.

   Die beiden Knöpfe sind GLEICH: gleiche Größe, gleiche Farbe, gleiches
   Gewicht (static/einwilligung.js erklärt, warum). */
.einwilligung {
  position: fixed; right: 20px; bottom: 20px; z-index: 60;
  width: min(400px, calc(100vw - 40px));
  background: var(--surface); color: var(--text);
  border: 1px solid var(--line); border-radius: var(--radius-gross);
  box-shadow: var(--schatten-tief);
  padding: 20px 22px;
}
.einwilligung h2 { margin: 0 0 8px; font-size: 16px; font-weight: 600; }
.einwilligung p { margin: 0 0 16px; color: var(--muted); font-size: 14px; line-height: 1.55; }
.einwilligung a { color: var(--accent); }
.einwilligung-knoepfe { display: flex; gap: 10px; }
.einwilligung-knopf {
  flex: 1; padding: 9px 14px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--text);
  font-weight: 600;
}
.einwilligung-knopf:hover { border-color: var(--accent); }

/* ------------------------------------------------- Einblenden beim Scrollen */
/* EINMAL, nicht bei jedem Vorbeikommen. static/start.js nimmt die Klasse
   dauerhaft weg, sobald ein Element im Bild war. Ein Abschnitt, der beim
   Zurückscrollen wieder aufblendet, wirkt nervös — und wer zurückscrollt,
   sucht etwas und will es sofort sehen. */
/* NUR WENN JAVASCRIPT WIRKLICH LAEUFT.
   Hier stand `.sicht { opacity: 0 }` ohne Bedingung — und damit haette ein
   Skriptfehler, ein Netzausfall beim Laden des Moduls oder ein Browser mit
   abgeschaltetem JavaScript die halbe Startseite UNSICHTBAR gemacht. Nicht
   unanimiert: unsichtbar. Auf einer oeffentlichen Seite ist das der
   Unterschied zwischen weniger huebsch und leer.

   `html.js` setzt ein winziges Skript im <head> der Seite. Es laeuft, bevor
   irgendetwas gezeichnet wird; faellt es aus, fehlt die Klasse und der Inhalt
   steht einfach da. */
html.js .sicht {
  /* Der Versatz innerhalb einer Kartenreihe. HIER auf 0 gesetzt und nicht nur
     als Rückfall in var() geschrieben: so steht in dieser Datei, dass es die
     Variable gibt und wer sie überschreibt (static/bewegung.js, je Karte).
     Eine Variable, die nur im JavaScript auftaucht, findet beim Aufräumen
     niemand wieder — scripts/css_kollision.py hat genau danach gefragt. */
  --verzug: 0s;
  opacity: 0;
  /* Etwas tiefer als vorher (14px) und eine Spur kleiner. Die Mischung aus
     Heben und Wachsen ist der Unterschied zwischen „rutscht herein" und
     „tritt hervor" — Apple benutzt sie auf jeder Produktseite, und sie
     funktioniert, weil beide Bewegungen aus derselben Richtung kommen. */
  transform: translateY(22px) scale(.985);
}
html.js .sicht.da {
  opacity: 1; transform: none;
  /* --verzug setzt static/bewegung.js je Geschwisterkarte. Ohne Wert ist er
     0s, also gibt es auch ohne Skript keine stehengebliebene Verzögerung. */
  transition:
    opacity .62s cubic-bezier(.22, .61, .36, 1) var(--verzug, 0s),
    transform .62s cubic-bezier(.22, .61, .36, 1) var(--verzug, 0s);
}

/* Wer Bewegung abbestellt hat, bekommt den Inhalt sofort — und zwar
   vollständig. Ein `transition: none` allein würde die Startwerte stehen
   lassen und den Abschnitt unsichtbar machen, falls das Skript nicht läuft. */
@media (prefers-reduced-motion: reduce) {
  html.js .sicht { opacity: 1; transform: none; }
  html.js .sicht.da { transition: none; }
}

/* ---------------------------------------------------------------- schmal -- */
@media (max-width: 860px) {
  .band { padding: 64px 0; }
  .band.haftend { padding: 64px 0; }
  .held { padding: calc(var(--leiste-hoch) + 44px) 0 56px; }
  .zwei { grid-template-columns: 1fr; gap: 32px; }
  .schirm { margin-top: 40px; border-radius: var(--radius-gross); }
  /* Auf einer Spalte gibt es nichts, woran die Ueberschrift kleben koennte —
     und eine Ueberschrift, die ueber ihrem eigenen Text klebt, sieht aus wie
     ein Fehler. Also zurueck in die gewoehnliche Reihenfolge. */
  .zweispaltig { grid-template-columns: 1fr; gap: 28px; }
  .haftet-kopf { position: static; }
  .haftet-kopf .abschnitt-titel, .haftet-kopf .abschnitt-unter { text-align: center; }
  .haftet-kopf .abschnitt-unter { margin-left: auto; margin-right: auto; }
}

/* Die Topleiste am Handy: die Navigation darf weg, der Anmeldezustand nicht.
   Wer auf einem Telefon auf der Startseite landet, muss zwei Dinge können —
   sehen, was das ist, und sich anmelden. Alles andere ist dort Ballast. */
@media (max-width: 680px) {
  .topleiste { padding: 0 14px; gap: 8px; }
  .topnav { display: none; }
  .topwer { display: none; }
  .topguthaben { display: none; }
  .spalte { padding: 0 16px; }
}

/* ---------------------------------------------------------------- Preise --- */
/* Die Karten stehen in einer Reihe und sind GLEICH HOCH, auch wenn eine davon
   keinen Bonus hat. Deshalb bekommt die leere Bonuszeile ein geschütztes
   Leerzeichen statt zu verschwinden: Knöpfe, die nicht auf einer Linie liegen,
   lassen eine Preistabelle unsortiert aussehen. */
.preis-kopf { padding-top: 56px; }
.preis-stand {
  margin: -30px auto 34px; text-align: center;
  color: var(--muted); font-size: 14px;
}
.mitte-text { text-align: center; }

.pakete {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  align-items: stretch;
}
.paket {
  position: relative;
  display: flex; flex-direction: column; gap: 6px;
  background: var(--surface); border-radius: var(--radius-gross);
  padding: 28px 24px 24px;
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  box-shadow: var(--schatten-weich);
}
/* Das beliebteste Paket steht etwas höher und trägt den Akzent als Rahmen.
   Mehr nicht — eine Karte, die zusätzlich größer, bunter und mit Verlauf
   unterlegt wäre, sähe nach Verkaufsdruck aus statt nach einer Empfehlung. */
.paket.hervor {
  border-color: var(--accent);
  box-shadow: var(--schatten-tief);
}
.paket-aufkleber {
  position: absolute; top: -11px; left: 50%; transform: translateX(-50%);
  background: var(--accent); color: var(--accent-ink);
  font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: 4px 12px; border-radius: 999px; white-space: nowrap;
}
.paket:not(.hervor) .paket-aufkleber {
  background: var(--surface-2); color: var(--muted);
}
.paket-preis {
  margin: 0; font-size: clamp(26px, 3vw, 34px); font-weight: 600;
  letter-spacing: -0.03em;
}
.paket-menge { margin: 0; color: var(--text); }
.paket-menge strong { font-size: 18px; font-weight: 600; }
.paket-menge span { color: var(--muted); font-size: 14px; }
.paket-bonus { margin: 0; color: var(--viz-geld); font-size: 13.5px; font-weight: 600; }
/* Haelt die Zeile frei, wenn ein Paket keinen Bonus hat — damit alle
   Knoepfe auf einer Linie liegen. Heisst .ohne und NICHT .leer: diese
   Klasse ist schon vergeben (der Leerzustand-Kasten, Zeile 739, mit
   padding 28px und gestricheltem Rand). */
.paket-bonus.ohne { visibility: hidden; }
.paket-je { margin: 0 0 14px; color: var(--muted); font-size: 12.5px; }
/* Der Knopf wird nach unten gedrückt, damit alle Knöpfe auf einer Linie
   liegen — unabhängig davon, wie lang die Texte darüber sind. */
.paket .haupt { margin-top: auto; text-align: center; }
.paket-note { margin: 10px 0 0; color: var(--muted); font-size: 12px; text-align: center; }
.preis-fuss { margin: 22px 0 0; text-align: center; color: var(--muted); font-size: 12.5px; }

.faq {
  display: grid; gap: 18px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.faq-punkt h3 { margin: 0 0 7px; font-size: 16px; font-weight: 600; }
.faq-punkt p { margin: 0; color: var(--muted); line-height: 1.6; }
