/* app.css — Nuvisan ICB, DMPK Order Entry
   Farbschema nach dem Frontend-Mockup: Nuvisan-Blau #1E2BD3, Pink #FF406D,
   IBM Plex Sans/Mono. Die Schriften werden nicht von einem CDN geladen (die VM
   hat keinen zwingenden Internetzugang) — es greift der System-Fallback, wenn
   IBM Plex lokal nicht installiert ist. */

:root {
  --blau: #1E2BD3;
  --pink: #FF406D;

  /* Beide Werte sind aus den Nuvisan Brand Guidelines 2025 uebernommen und
     nicht selbst angemischt: --pink-tief ist der dort im Abschnitt
     „Colour / Tint" gefuehrte Tint der Marken-Pink, --off-weiss die
     Primaerfarbe „Off white" aus „Colour / Primary".

     --pink-tief existiert aus einem Kontrastgrund, nicht aus Geschmack. Die
     Marke will Pink als Signalfarbe fuer Interaktion — das reine --pink
     (#FF406D) traegt weisse Schrift aber nur mit 3,4:1 und verfehlt damit die
     4,5:1, die fuer Text in Normalgroesse verlangt sind. #BF3052 erreicht
     5,6:1. Deshalb die Aufteilung: Knoepfe mit Beschriftung in Normalgroesse
     bekommen den tiefen Ton, das reine Marken-Pink bleibt grossen, fetten
     Beschriftungen ab 18,7px vorbehalten, wo 3:1 ausreicht. Ein Token allein
     kann beides nicht leisten. */
  --pink-tief: #BF3052;
  --off-weiss: #f9f9f9;

  --tinte: #16181f;
  --grau: #5b6070;
  --linie: #dfe2ea;
  --flaeche: #f6f7fa;
  --weiss: #fff;
  --warn: #b3541e;
  --rot: #b42318;

  /* Arbeitsbreite der Inhaltsflaeche. EINE Stelle, damit Inhalt und
     Fussbereich nicht auseinanderlaufen. Deutlich grosszuegiger als die alte
     Lesebreite: der Hauptinhalt sind achtspaltige Tabellen und kein
     Fliesstext, dem ~90 Zeichen je Zeile gut taeten. Begrenzt bleibt sie
     trotzdem — ueber die volle Breite eines sehr grossen Monitors verliert
     das Auge beim Wandern ueber die Zeile den Zeilenbezug. Wer echtes
     Full-Width will, aendert genau diese Zeile auf 100%. */
  --inhaltsbreite: 1800px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: "IBM Plex Sans", -apple-system, "Segoe UI", Roboto, sans-serif;
  color: var(--tinte);
  background: var(--flaeche);
  font-size: 15px;
  line-height: 1.5;
}

code, .mono, td.mono { font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace; }

/* ─── Kopfzeile ─────────────────────────────────────────────────────────── */
/* `min-height` und NICHT `height`: Die Leiste darf umbrechen (`flex-wrap:
   wrap`), und eine feste Hoehe widerspricht dem. Sie legt die Leiste auf genau
   eine Zeile fest, waehrend der Umbruch beliebig viele erzeugt — die zweite
   Zeile hat dann keinen Platz mehr in der Leiste, laeuft unten aus ihr heraus
   und legt sich ueber den Inhalt darunter. Mit `min-height` und senkrechtem
   Innenabstand waechst die Leiste stattdessen mit: Sie ist im unumgebrochenen
   Fall exakt so hoch wie zuvor (56 px, denn der Inhalt bleibt mit Polster
   darunter), und bei Umbruch so hoch, wie ihre Zeilen es brauchen.

   Warum das lange nicht auffiel, obwohl der Fehler alt ist: Unter der Leiste
   lag bisher immer eine weisse Inhaltsflaeche. Der ueberstehende Text war dort
   zwar falsch platziert, aber lesbar und sah nur unsauber aus. Seit die
   Anmelde- und die Startseite direkt unter der Leiste eine Bildflaeche zeigen,
   liegt derselbe Text auf dem Foto und ist schlicht nicht mehr zu entziffern —
   aus einem Schoenheitsfehler ist ein Bedienungsfehler geworden.

   `row-gap` kleiner als `column-gap`: Waagerecht trennt der Abstand
   Menuepunkte voneinander, senkrecht nur zwei Zeilen derselben Leiste. Ohne
   eigenen Wert klebten die umgebrochenen Zeilen aneinander (Grundwert 0) oder
   rissen die Leiste unnoetig auseinander (der volle Spaltenabstand). */
.topbar {
  display: flex; align-items: center; column-gap: 1.5rem; row-gap: .5rem;
  background: var(--weiss); border-bottom: 2px solid var(--blau);
  padding: .5rem 1.5rem; min-height: 56px; flex-wrap: wrap;
}
.brand { font-weight: 700; color: var(--blau); text-decoration: none; letter-spacing: -.02em; }
.brand span { color: var(--tinte); font-weight: 400; }
/* Flex-BASIS statt `flex: 1`: Beides laesst die Navigation den freien Platz
   fuellen, aber `flex: 1` heisst `flex-basis: 0` — die Navigation beansprucht
   dann rechnerisch keine Breite und bleibt deshalb IMMER auf der Zeile der
   Marke, egal wie schmal das Fenster wird. Sie quetschte sich dort in die paar
   Pixel rechts neben dem Logo und stapelte ihre elf Punkte in eine
   ausgefranste Spalte, die die Leiste auf ein Vielfaches ihrer Hoehe trieb.
   Mit 22rem sagt sie, wie viel Platz sie braucht, um als Zeile zu taugen;
   reicht der nicht, rutscht sie als Ganzes auf eine eigene Zeile und nutzt
   dort die volle Breite. Kein Media-Query: Der Umbruchpunkt ergibt sich aus
   dem, was danebensteht, und verschiebt sich von allein mit, wenn die Marke
   oder die Nutzerzeile einmal laenger werden. `flex-grow: 1` bleibt, damit
   sich bei breitem Fenster nichts aendert. */
.topbar nav { display: flex; gap: 1rem; flex: 1 1 22rem; flex-wrap: wrap; }
.topbar nav a { color: var(--tinte); text-decoration: none; padding: 2px 0; border-bottom: 2px solid transparent; }
.topbar nav a:hover { border-bottom-color: var(--pink); }
.topbar nav a.on { border-bottom-color: var(--blau); font-weight: 600; }
.topbar nav a.muted { color: var(--grau); }
.who { display: flex; align-items: center; gap: .6rem; font-size: 13px; }
.who .role {
  background: var(--blau); color: #fff; border-radius: 999px;
  padding: 1px 9px; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
}
.who a { color: var(--grau); }

/* „Über"-Knopf: rechtsbuendig in der Kopfzeile, ausserhalb von <nav> (er
   steht dort auch ohne angemeldeten Nutzer). margin-left:auto wirkt
   unabhaengig davon, ob nav/.who gerade gerendert werden.

   Farbe bewusst OHNE var(--blau): das kraeftige Markenblau ist die
   Interaktionsfarbe der Arbeitsbereiche (nav a.on, primaere Knoepfe). „Über"
   ist kein Arbeitsbereich, sondern App-Chrome — deshalb dieselbe zurueck-
   haltende Farbgebung wie bei .muted (Dashboard/LIMS) und im Fussbereich:
   Ruhezustand Grau, beim Hover die normale Fliesstextfarbe (Tinte). Keine
   neue Farbkonstante, nur vorhandene Token.

   Das `background: none` in der Hover-Regel unten ist keine Wiederholung des
   Ruhezustands, sondern dessen Verteidigung: `.ueberknopf` ist ein <button>
   ohne Flaeche, und die allgemeine Regel `button:hover` hat mit Element plus
   Pseudoklasse hoehere Spezifitaet als die Ruheregel mit der Klasse allein.
   Solange die Hover-Regel die Flaeche nicht selbst zuruecksetzt, gewinnt beim
   Hover die allgemeine Knopfregel und legt einen farbigen Block in die
   Kopfzeile. Wer die Zeile als redundant loescht, holt genau diesen Block
   zurueck. */
.ueberknopf {
  margin-left: auto;
  background: none; border: none; padding: 0;
  font: inherit; font-size: 13px; color: var(--grau);
  cursor: pointer;
}
.ueberknopf:hover { color: var(--tinte); background: none; }

/* .topbar bekommt bewusst KEINE Breitengrenze — sie bleibt volle Breite.
   Damit rueckt der Inhalt naeher an die Navigation heran statt weiter weg. */
main { max-width: var(--inhaltsbreite); margin: 0 auto; padding: 1.75rem 2rem 3rem; }
footer {
  max-width: var(--inhaltsbreite); margin: 0 auto; padding: 1rem 2rem 2rem;
  color: var(--grau); font-size: 12px; border-top: 1px solid var(--linie);
}

h1 { font-size: 1.5rem; margin: 0 0 .3rem; letter-spacing: -.02em; }
h2 { font-size: 1.05rem; margin: 1.75rem 0 .6rem; }
h3 { font-size: .95rem; margin: 1.25rem 0 .4rem; }
.lead { color: var(--grau); margin: 0 0 1.5rem; }

/* ─── Karten und Tabellen ───────────────────────────────────────────────── */
.card {
  background: var(--weiss); border: 1px solid var(--linie);
  border-radius: 8px; padding: 1.1rem 1.25rem; margin-bottom: 1rem;
}
table { width: 100%; border-collapse: collapse; background: var(--weiss); }
.scroll { overflow-x: auto; border: 1px solid var(--linie); border-radius: 8px; }
th, td { text-align: left; padding: .55rem .7rem; border-bottom: 1px solid var(--linie); vertical-align: top; }
th { background: var(--flaeche); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--grau); }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: #fafbfd; }
td a { color: var(--blau); }

/* Kennungen brechen NICHT um — weder am Bindestrich noch am Leerzeichen.
   `ANO-0007` in `ANO-` / `0007` zu zerlegen ist kein Platzproblem, sondern
   eine falsche Silbentrennung: der Browser darf an `-` umbrechen, und bei
   einer Kennung ergibt das eine Zeichenkette, die es nicht gibt. Betrifft
   Auftrags-, Projekt- und Laborauftragsnummern gleichermassen.

   Die Spalte „Laborauftraege" fuehrt ihre Eintraege als `<div class="mono">`
   untereinander. Das bleibt mehrzeilig: `nowrap` unterbindet nur den Umbruch
   INNERHALB eines Elements, und jedes <div> ist ein eigener Block. */
td.mono, td .mono, .nowrap { white-space: nowrap; }

/* Kundennamen umbrechen nur zwischen Woertern, nie innerhalb. */
td { overflow-wrap: normal; word-break: normal; hyphens: manual; }

/* `width: 100%` allein laesst die Tabelle nie ueber den Container wachsen —
   sie presst stattdessen die Spalten zusammen, und der Scrollbalken von
   `.scroll` erscheint nie. Mit `min-width: max-content` bekommt die Tabelle
   ihre natuerliche Mindestbreite; erst wenn die nicht passt, scrollt `.scroll`
   waagerecht. Zusammenpressen ist damit kein Ergebnis mehr, sondern Scrollen
   die Ausweichreaktion — und erst das traegt das `nowrap` oben, das sonst
   Text abschneiden wuerde. */
.scroll > table { width: 100%; min-width: max-content; }

/* ─── Status ────────────────────────────────────────────────────────────── */
.status {
  display: inline-block; padding: 1px 9px; border-radius: 999px;
  font-size: 11.5px; border: 1px solid var(--linie); background: var(--flaeche); white-space: nowrap;
}
.status.neu { border-color: var(--pink); color: var(--pink); }
.status.validiert, .status.freigegeben { border-color: var(--blau); color: var(--blau); }
.status.reported { background: var(--blau); color: #fff; border-color: var(--blau); }

/* ─── Formular ──────────────────────────────────────────────────────────── */
label { display: block; font-size: 12.5px; font-weight: 600; margin-bottom: .25rem; }
/* `input[type=password]` steht hier mit, obwohl die Anwendung kein Passwort
   prueft: Die Anmeldemaske zeigt ein deaktiviertes Passwortfeld (Begruendung
   in login.html), und ein Feld, das aussieht wie kein anderes Feld der
   Anwendung, liest sich als Fehler statt als Ausblick. Wer die Anmeldung
   spaeter wirklich anbindet, findet die Gestaltung damit schon fertig vor. */
select, input[type=text], input[type=password], input[type=number], textarea {
  width: 100%; padding: .45rem .6rem; border: 1px solid var(--linie);
  border-radius: 6px; font: inherit; background: var(--weiss);
}
/* Eingaben brauchen keine 1700 Pixel. Mit der groesseren Inhaltsbreite waere
   ein einzeiliges Textfeld sonst so breit wie die Seite. Die Deckelung sitzt
   am Feld und nicht am Formular, damit sie unabhaengig vom Container gilt —
   ein Suchfeld in einer breiten Filterleiste soll genauso aussehen wie eines
   in einer Karte. `.scroll`-Tabellen und `.grid`-Layouts bleiben unberuehrt. */
input[type=text], input[type=search], input[type=number],
input[type=date], input[type=password], select, textarea { max-width: 48rem; }

/* Fokus heisst in dieser Anwendung Pink — und zwar auch bei Eingaben, nicht
   nur bei Knoepfen. Knoepfe wechseln bei Hover, Tastaturfokus und Druck von
   Markenblau nach Pink (siehe die Knopfregel weiter unten); solange die
   Eingabe daneben blau aufleuchtete, hatte die Anwendung zwei Muster fuer
   dieselbe Aussage „hier stehst du gerade". Wer sich mit der Tastatur durch
   ein Formular bewegt, laeuft dabei abwechselnd durch Felder und Knoepfe und
   saehe die Fokusfarbe bei jedem Schritt springen. Ein Muster, das nur fuer
   die Haelfte der Bedienelemente gilt, ist keines.

   Warum `--pink` und NICHT `--pink-tief`: Ein Fokusrahmen ist der Umriss eines
   Bedienelements und kein Text. Dafuer gilt die Kontrastschwelle 3:1 (WCAG
   1.4.11, Non-text Contrast) und nicht die 4,5:1 fuer Fliesstext, wegen der es
   `--pink-tief` ueberhaupt gibt. #FF406D erreicht gegen Weiss 3,4:1 und ist
   damit hier zulaessig — und es ist das Marken-Pink selbst, dasselbe, das die
   `.cta`-Knoepfe tragen. Der tiefe Ton waere an dieser Stelle nicht sicherer,
   sondern nur eine zweite Roetung ohne Anlass. */
select:focus, input:focus, textarea:focus { outline: 2px solid var(--pink); outline-offset: -1px; }
/* Gilt fuer Textfelder wie fuer Auswahlfelder: Ein gesperrtes Feld muss auf den
   ersten Blick gesperrt aussehen. Das ist besonders am Passwortfeld der
   Anmeldemaske noetig — es ist absichtlich deaktiviert, und ohne den
   gedaempften Grund saehe es aus wie ein Feld, in das man tippen soll.
   Ausdruecklich nach Typ aufgezaehlt und nicht als `input:disabled`: Ein
   Ankreuzfeld ist kein Eingabefeld, und ein gesperrter Haken (siehe
   rechte.html) soll blass bleiben, aber keinen Feldhintergrund bekommen. */
select:disabled,
input[type=text]:disabled, input[type=password]:disabled,
input[type=number]:disabled, input[type=date]:disabled,
textarea:disabled { background: var(--flaeche); color: #9aa0b0; }
.grid { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.field-hint { font-size: 11.5px; color: var(--grau); font-weight: 400; }

/* Karte "Versuchsdaten" der Lauf-Seite (Task 2, Plan "Lauf-Seite neu
   ordnen"): drei Bloecke Gemeinsam/Compounds/IC statt einer ununterbrochenen
   Feldliste -- derselbe Grund wie bei fieldset.feldgruppe oben, nur ohne
   <fieldset>, weil die Bloecke KEIN eigenes Formular sind (ein Formular fuer
   die ganze Karte, Ruling 3 im Plan). */
.vd-block { border: 1px solid var(--linie); border-radius: 6px; padding: .7rem .9rem; margin-top: .8rem; }
.vd-block h4 { margin: 0 0 .5rem; font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--grau); }
.vd-raster { display: grid; gap: .7rem; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.vd-zwei { display: grid; gap: .8rem; grid-template-columns: 1fr 1fr; }
@media (max-width: 900px) { .vd-zwei { grid-template-columns: 1fr; } }
.vd-block input[type=text].breit, .vd-block input.breit { width: 100%; }

/* Feldgruppen der Stammdatenmaske. Ein Compound fuehrt rund 20 Felder; ohne
   Rahmen und Ueberschrift waere die Maske eine ununterbrochene Liste, in der
   Chargenangaben und ICL-Anschluss ununterscheidbar nebeneinander stuenden.
   Nur ein duenner Rahmen und keine Flaeche: die Gruppe soll die Felder
   ordnen, nicht mit der Karte darum konkurrieren. */
fieldset.feldgruppe {
  border: 1px solid var(--linie); border-radius: 6px;
  padding: .9rem 1rem 1rem; margin: 1.1rem 0 0; min-width: 0;
}
fieldset.feldgruppe > legend {
  padding: 0 .4rem; font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; color: var(--grau);
}

button, .btn {
  font: inherit; font-weight: 600; cursor: pointer; border-radius: 6px;
  padding: .5rem 1rem; border: 1px solid var(--blau);
  background: var(--blau); color: #fff; text-decoration: none; display: inline-block;
}
/* `display: inline-block` oben gewinnt sonst gegen das [hidden] des Browsers:
   dessen Regel steht im Standard-Stylesheet und verliert gegen JEDE Angabe
   aus dieser Datei — Spezifitaet spielt dabei keine Rolle. Ohne diese Zeile
   bliebe ein Knopf sichtbar, den ein Skript ausdruecklich versteckt hat.
   Dieselbe Falle wie bei `.modeswitch[hidden]` und `.draft-bar[hidden]`, hier
   aber ein fuer alle Mal an der allgemeinen Knopfregel statt ein drittes Mal
   je Bauteil. Aufgefallen an [+] und ✕ der Ergebnisablage (Ticket #37): beide
   standen ohne JavaScript da und taten nichts. */
button[hidden], .btn[hidden] { display: none; }
/* Das tragende Interaktionsmuster der Marke: Knoepfe ruhen im Markenblau und
   wechseln bei Hover, Tastaturfokus und Druck in die Marken-Pink. Es gilt
   deshalb hier, an der allgemeinen Knopfregel, und nicht in den Stylesheets
   einzelner Seiten — ein Muster, das nur auf der Anmelde- und der Startseite
   gilt, ist kein Muster, sondern eine Ausnahme, die der Nutzer nirgends
   wiedererkennt. Wer das Verhalten aendert, aendert es fuer die ganze
   Anwendung, und genau das ist beabsichtigt.

   Warum zwei Rottoene und nicht einer: Beschriftungen in Normalgroesse
   bekommen --pink-tief, weil weisser Text auf dem reinen --pink nur 3,4:1
   erreicht und damit unter den geforderten 4,5:1 bleibt. Die .cta-Knoepfe der
   Anmelde- und Startseite sind mit 1,2rem (19,2 px) und Fettschnitt gross
   genug, dass die Schwelle 3:1 gilt — dort darf und soll das reine
   Marken-Pink stehen, das die Guidelines fuer Handlungsaufforderungen
   vorsehen. Der tiefe Ton ist also kein Ersatz fuer das Marken-Pink, sondern
   dessen lesbare Variante fuer kleine Schrift.

   Warum :focus-visible und nicht :focus: ein Mausklick soll den Fokusrahmen
   nicht stehen lassen — das sieht nach einem haengengebliebenen Zustand aus.
   Die Tastaturbedienung braucht ihn dagegen zwingend, sonst ist beim Tabben
   nicht erkennbar, wo man steht. :focus-visible trennt genau diese beiden
   Faelle und ist deshalb die richtige Bedingung, nicht die bequemere. */
button:hover, .btn:hover,
button:focus-visible, .btn:focus-visible {
  background: var(--pink-tief); border-color: var(--pink-tief);
}
/* Gedrueckt bleibt gedrueckt-pink: ein dritter Ton fuer :active waere eine
   Farbe mehr, die niemand als eigene Bedeutung liest. */
button:active, .btn:active { background: var(--pink-tief); border-color: var(--pink-tief); }

/* Sichtbarer Tastaturfokus zusaetzlich zum Farbwechsel — die Farbe allein
   traegt die Information nicht, wenn zwei Knoepfe nebeneinanderstehen. */
button:focus-visible, .btn:focus-visible { outline: 2px solid var(--pink-tief); outline-offset: 2px; }

/* Die grossen Handlungsknoepfe auf Anmelde- und Startseite. Die Regel steht
   NACH der allgemeinen Hover-Regel, damit ihr reines Marken-Pink gewinnt —
   sie ist der von den Guidelines gewollte Sonderfall, nicht ein Zufall der
   Reihenfolge. Wer sie nach oben schiebt, bekommt wieder den tiefen Ton. */
.cta, a.cta {
  font-size: 1.2rem; font-weight: 700; padding: .85rem 1.9rem;
  border-radius: 8px; letter-spacing: .01em;
}
.cta:hover, .cta:focus-visible,
a.cta:hover, a.cta:focus-visible { background: var(--pink); border-color: var(--pink); }

button.ghost, .btn.ghost { background: var(--weiss); color: var(--blau); }
/* Der Geisterknopf hat keine Flaeche zum Umfaerben, also traegt die Schrift
   den Wechsel — auf einem sehr hellen Pink-Anflug, damit die Zustaende
   unterscheidbar bleiben, ohne dass ein zweiter Vollton entsteht. */
button.ghost:hover, button.ghost:focus-visible {
  background: #fdeff3; color: var(--pink-tief); border-color: var(--pink-tief);
}

/* Umgekehrter Tausch: was schon im Ruhezustand pink ist, kann bei Hover nicht
   nach Pink wechseln. Der Knopf geht deshalb ins Markenblau — dieselbe
   Bewegung zwischen denselben zwei Markenfarben, nur andersherum. */
button.pink { background: var(--pink); border-color: var(--pink); }
button.pink:hover, button.pink:focus-visible { background: var(--blau); border-color: var(--blau); }
button:disabled { opacity: .45; cursor: not-allowed; }
/* Ein ausgegrauter Knopf darf auf Hover nicht die Signalfarbe annehmen — Pink
   heisst „hier kannst du handeln", und genau das stimmt hier nicht. Er bleibt
   deshalb im gedaempften Blau stehen. Die Regel steht bewusst NACH der
   allgemeinen Hover-Regel und schlaegt sie auch ueber die Spezifitaet. */
button:disabled:hover, button:disabled:focus-visible { background: var(--blau); border-color: var(--blau); }
button.link {
  background: none; border: none; color: var(--grau); padding: 0;
  font-weight: 400; text-decoration: underline;
}
/* Abbruch-Knopf im Lauf: rot statt grau, damit er sich von "lösen" und
   anderen Textknoepfen abhebt — ein Lauf abzubrechen ist kein Regelweg. */
button.link.gefahr { color: var(--rot); font-weight: 600; }
button.link.gefahr:hover, button.link.gefahr:focus-visible { color: #8a1a12; }

/* ─── Warenkorb ─────────────────────────────────────────────────────────── */
.cart-item {
  border: 1px solid var(--linie); border-left: 3px solid var(--blau);
  border-radius: 6px; padding: .7rem .9rem; margin-bottom: .6rem; background: var(--weiss);
}
.cart-item h4 { margin: 0 0 .2rem; font-size: 13.5px; }
.cart-item dl { margin: .35rem 0 0; font-size: 12.5px; color: var(--grau); display: flex; flex-wrap: wrap; gap: .2rem 1rem; }
.cart-item dt { font-weight: 600; }
.cart-item dd { margin: 0 .8rem 0 0; }
.empty { color: var(--grau); font-size: 13px; font-style: italic; }

/* ─── Hinweise ──────────────────────────────────────────────────────────── */
.note, .warn, .stub {
  border-radius: 6px; padding: .7rem .9rem; font-size: 13px; margin-bottom: 1rem;
}
.note { background: #eef0fd; border-left: 3px solid var(--blau); }
.warn { background: #fff3f6; border-left: 3px solid var(--pink); color: var(--warn); }
.stub { background: #fff8e8; border-left: 3px solid #d9a417; }
.exclusive { color: var(--pink); font-weight: 600; }

/* Platzhalter-Kacheln der Stub-Seiten: absichtlich ohne Zahlen. */
.tiles { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.tile {
  background: var(--weiss); border: 1px dashed var(--linie); border-radius: 8px;
  padding: 1.1rem; text-align: center; color: var(--grau);
}
.tile .ph { font-size: 1.6rem; color: #c3c8d6; font-family: "IBM Plex Mono", monospace; }
.tile h4 { margin: .3rem 0 .1rem; color: var(--tinte); font-size: 13px; }
.tile p { margin: 0; font-size: 11.5px; }

/* ─── Logo ──────────────────────────────────────────────────────────────── */
/* Die Bilddatei ist austauschbar (app/static/img/NUVISAN_logo.png). Deshalb
   wird nur die Hoehe gesetzt und die Breite mitlaufen gelassen — ein Logo mit
   anderem Seitenverhaeltnis passt damit ohne CSS-Aenderung. */
.brand { display: flex; align-items: center; gap: .7rem; }
.brand .logo { height: 26px; width: auto; display: block; }
.brand-app {
  color: var(--tinte); font-weight: 400; font-size: 14px;
  padding-left: .7rem; border-left: 1px solid var(--linie);
}

.badge {
  display: inline-block; margin-left: .35rem; background: var(--pink); color: #fff;
  border-radius: 999px; padding: 0 6px; font-size: 11px; font-weight: 700;
}

/* ─── Arbeitsseiten: volle Bildschirmbreite ─────────────────────────────
   Seitenbezogen ueber die Klasse am <body> (base.html: `seite-{{ active }}`)
   und ausdruecklich NICHT ueber --inhaltsbreite. Die 1800px-Grenze oben in
   :root ist fuer Formular- und Leseseiten richtig begruendet (Neuer Auftrag,
   Rechte, Technische Doku, Zellmaterial, Start) und bleibt dort unveraendert;
   alle Seiten, deren Inhalt Tabellen oder Karten sind -- Auftraege, Audit
   Trail, Postfach, Stammdaten, Dashboard, wie der Arbeitsvorrat selbst --
   sind der Gegenfall und stehen deshalb in derselben Selektorliste. Der
   Arbeitsvorrat zeigt eine zehnspaltige Arbeitsliste, bei der jede nicht
   genutzte Bildschirmbreite unmittelbar eine Spalte in den waagerechten
   Scroll schiebt — hier kostet die Lesebreite mehr, als sie bringt; dieselbe
   Rechnung gilt fuer die uebrigen Arbeitsseiten. Verworfen: `--inhaltsbreite:
   100%` -- traefe auch die Formular- und Leseseiten, fuer die die Grenze
   eigens begruendet ist. Verworfen: eine zweite Template-Variable "breit" --
   `active` bleibt der einzige Mechanismus, den base.html an die Klasse gibt.

   Kein `position: sticky` am Tabellenkopf, und das ist eine Entscheidung und
   kein Versaeumnis: `.scroll` faengt mit `overflow-x: auto` nach CSS-Overflow
   auch die senkrechte Achse als `auto` ab, scrollt mangels Hoehenbegrenzung
   aber nie selbst. Ein `sticky` darin haette keinen Scrollport, an dem es
   haften koennte — es waere eine Regel ohne Wirkung. Wirksam wuerde es erst
   mit einer `max-height` auf `.scroll`, und die brauchte einen zweiten
   Scrollbalken im Inhalt: das Gegenteil des Ziels, weniger zu scrollen. */
.seite-labor main, .seite-orders main, .seite-audit main, .seite-inbox main, .seite-stammdaten main, .seite-dashboard main, .seite-workflows main   { max-width: 100%; padding: 1.25rem 1rem 2rem; }
.seite-labor footer, .seite-orders footer, .seite-audit footer, .seite-inbox footer, .seite-stammdaten footer, .seite-dashboard footer, .seite-workflows footer { max-width: 100%; padding: .8rem 1rem 1.5rem; }

/* Dichte. Alles seitenbezogen: die globalen Werte sind fuer Formular- und
   Leseseiten richtig, hier gilt der Gegenfall. Der Arbeitsvorrat ist eine
   Arbeitsliste, die im Betrieb ueber den Bildschirmrand hinauswaechst — jede
   eingesparte Zeile Hoehe und jedes eingesparte Achtel Zoll Spaltenbreite ist
   eine Zeile mehr, die ohne Scrollen sichtbar bleibt. Die Dichte bleibt dabei
   Labor-exklusiv: das ist eine Entscheidung der Arbeitsliste, nicht der
   uebrigen Arbeitsseiten -- nur die Breite wandert. */
.seite-labor table               { font-size: 13px; line-height: 1.35; }
.seite-labor th, .seite-labor td { padding: .3rem .5rem; }
.seite-labor .lead               { margin-bottom: .8rem; }
.seite-labor .subnav,
.seite-labor .toolbar            { margin-bottom: .6rem; }
.seite-labor .card,
.seite-labor .filterbar          { padding: .7rem .9rem; margin-bottom: .6rem; }
.seite-labor .readonly-hint,
.seite-labor .params             { font-size: 11.5px; }

/* Assay-Parameter standen je Paar auf einer eigenen Zeile (`<br>` je Eintrag).
   Bei fuenf Parametern war die Tabellenzeile damit fuenf Zeilen hoch — der
   groesste einzelne Hoehentreiber der Liste. Jetzt stehen die Paare
   nebeneinander und brechen erst am Ende der Spalte um.

   Die `max-width` sitzt am inneren Block und NICHT am `<td>`: die Tabelle
   laeuft mit `min-width: max-content` (siehe oben), und der max-content-Beitrag
   einer Zelle wird von der max-width des Inhaltsblocks gedeckelt. Damit nimmt
   dieselbe Regel zugleich waagerechte Breite weg, statt sie nur umzubrechen.
   Am `<td>` waere die Wirkung bei automatischem Tabellenlayout unzuverlaessig. */
.paramwrap { max-width: 22rem; }
.param     { display: inline-block; white-space: nowrap; margin-right: .55rem; }

.toolbar {
  display: flex; gap: .5rem; flex-wrap: wrap; align-items: center;
  margin-bottom: 1rem;
}
.toolbar .spacer { flex: 1; }
.chip {
  display: inline-block; padding: .3rem .75rem; border-radius: 999px;
  border: 1px solid var(--linie); background: var(--weiss); color: var(--tinte);
  text-decoration: none; font-size: 13px;
}
.chip:hover { border-color: var(--blau); }
.chip.on { background: var(--blau); color: #fff; border-color: var(--blau); font-weight: 600; }
.exportbar { display: flex; gap: .4rem; flex-wrap: wrap; align-items: center; font-size: 13px; }
.exportbar a {
  color: var(--blau); text-decoration: none; border: 1px solid var(--linie);
  border-radius: 6px; padding: .25rem .6rem; background: var(--weiss);
}
.exportbar a:hover { border-color: var(--blau); background: #eef0fd; }

tr.blocked { background: #fff8f9; }
tr.blocked td { color: var(--grau); }

/* Zellbestand-Uebersicht (E2, docs/plaene/2026-09-18-zellbestand-uebersicht.md):
   eine Zeile je Charge, Material-Spalten links. Folgezeilen desselben
   Materials wiederholen diese Spalten -- gedaempft, damit die Gruppe beim
   Ueberfliegen sichtbar bleibt, ohne eine Kopfzeile mit abweichender
   Zellenzahl zu brauchen (die wuerde aus Spaltenverwaltung und
   `spalten_anordnen.js` herausfallen, siehe dessen Dateikopf). */
tr.folgezeile td.material { color: var(--grau); }
.tag {
  display: inline-block; font-size: 11px; padding: 0 6px; border-radius: 4px;
  background: var(--flaeche); border: 1px solid var(--linie); color: var(--grau);
  white-space: nowrap;
}
.tag.wait { background: #fff3f6; border-color: var(--pink); color: var(--warn); }
.params { font-size: 12px; color: var(--grau); }
.params b { color: var(--tinte); font-weight: 600; }

/* ─── Stammdaten ────────────────────────────────────────────────────────── */
.subnav { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.readonly-hint { font-size: 12px; color: var(--grau); }
.actions { display: flex; gap: .4rem; flex-wrap: wrap; }
.inline-form { display: inline; }
textarea.code {
  font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
  font-size: 12.5px; min-height: 260px;
}
.two-col { display: grid; gap: 1.5rem; grid-template-columns: 2fr 1fr; align-items: start; }
@media (max-width: 900px) { .two-col { grid-template-columns: 1fr; } }

/* ─── Rechtematrix ──────────────────────────────────────────────────────── */
/* Nur drei Regeln, alles Weitere kommt von `table` und `.scroll`. Die Matrix
   ist eine Tabelle wie jede andere im Haus; sie braucht lediglich die Haken
   mittig und die Kategoriezeile als Zwischenueberschrift erkennbar. */
.rechtematrix td.haken { text-align: center; }
.rechtematrix td.haken input { width: auto; }
.rechtematrix tr.gruppe th {
  background: var(--flaeche); color: var(--tinte); font-weight: 700;
}

/* ─── Postfach ──────────────────────────────────────────────────────────── */
.note-item {
  background: var(--weiss); border: 1px solid var(--linie); border-left: 3px solid var(--linie);
  border-radius: 6px; padding: .7rem .9rem; margin-bottom: .5rem;
}
.note-item.unread { border-left-color: var(--pink); }
.note-item h4 { margin: 0 0 .2rem; font-size: 13.5px; }
.note-item .meta { font-size: 11.5px; color: var(--grau); }
.note-item pre {
  margin: .4rem 0 0; font-size: 12px; white-space: pre-wrap;
  font-family: inherit; color: var(--grau);
}

/* ─── Compound-Mehrfachauswahl ──────────────────────────────────────────── */
/* Liste statt <select multiple>: die Mehrfachauswahl per Strg+Klick wird
   erfahrungsgemaess uebersehen, und die Haken bleiben beim Filtern sichtbar. */
.picklist {
  max-height: 200px; overflow-y: auto; border: 1px solid var(--linie);
  border-radius: 6px; background: var(--weiss); margin-top: .4rem;
}
.picklist .empty { padding: .6rem .7rem; margin: 0; }
.pick {
  display: flex; align-items: center; gap: .5rem; font-weight: 400;
  font-size: 13.5px; padding: .35rem .7rem; margin: 0; cursor: pointer;
  border-bottom: 1px solid var(--flaeche);
}
.pick:last-child { border-bottom: none; }
.pick:hover { background: #fafbfd; }
.pick input { width: auto; }
/* Abschlusszeile der Fensterung („… und n weitere"). Klebt am unteren Rand des
   Listenfeldes: sie steht hinter 200 Zeilen und waere sonst genau fuer den
   Nutzer unsichtbar, der sie braucht — naemlich fuer den, der gar nicht erst
   nach unten blaettert. Ruhige Flaeche und Trennkante, damit sie nicht wie
   eine weitere waehlbare Zeile ohne Haken wirkt. */
.picklist .rest {
  position: sticky; bottom: 0;
  background: var(--flaeche); border-top: 1px solid var(--linie);
}
/* Hervorhebung der Tastaturbedienung. Bewusst NICHT wie ein gesetzter Haken:
   der Haken ist die Auswahl, die Hervorhebung sagt nur, worauf Enter als
   naechstes wirkt. Beides zugleich kommt vor, deshalb eine Kante am Rand
   statt einer zweiten Flaeche — sonst waeren die zwei Zustaende nicht
   auseinanderzuhalten. Kraeftiger als :hover, weil die Maus nur begleitet,
   die Tastatur aber fuehrt. */
.pick.hervor {
  background: #eceefc; box-shadow: inset 3px 0 0 var(--blau);
}

/* ─── Substanzliste aus Datei ───────────────────────────────────────────── */
/* Umschalter zwischen Auswahlliste und Datei-Import. Radios und keine Chips,
   weil es genau ein Weg von zwei ist und die Wahl den Block darunter tauscht. */
.modeswitch {
  display: flex; gap: 1.2rem; flex-wrap: wrap; align-items: center;
  margin-top: .9rem; padding-bottom: .1rem;
}
.modeswitch label {
  display: inline-flex; align-items: center; gap: .4rem;
  font-weight: 400; font-size: 13.5px; margin: 0; cursor: pointer;
}
.modeswitch input { width: auto; }
/* Eigene Regel wie bei .draft-bar: display:flex gewinnt sonst gegen [hidden].
   Der Umschalter verschwindet, wenn compound_import.js nicht geladen ist. */
.modeswitch[hidden] { display: none; }

/* Die Ablegeflaeche ist gestrichelt wie die Platzhalter-Kacheln: sie ist eine
   Aufforderung und noch kein Inhalt. Klickbar ist sie ebenfalls — Drag & Drop
   allein waere auf Rechnern ohne Maus keine Bedienung. */
.dropzone {
  border: 1px dashed var(--linie); border-radius: 8px; background: var(--weiss);
  padding: 1.1rem; text-align: center; color: var(--grau); font-size: 13px;
  cursor: pointer; margin-top: .9rem;
}
.dropzone strong { color: var(--tinte); }
.dropzone:hover { border-color: var(--blau); }
.dropzone.over {
  border-style: solid; border-color: var(--blau); background: #eef0fd;
}
/* Waehrend eine Datei laeuft. Sichtbar gedaempft, damit niemand dieselbe Datei
   ein zweites Mal ablegt, weil nichts zu passieren scheint — bei einem Import,
   der alles oder nichts anlegt, waere der zweite Anlauf lauter Dubletten. */
.dropzone.busy { opacity: .55; cursor: progress; }

/* Zeilennummern neben dem Textfeld der Text-Erkennung (Plan
   2026-09-12-erkennung-etappe-1.md, Entscheidung 7.1): die Vorschau nennt
   dieselbe Zahl wie die Eingabe, deshalb muss die Nummer der TEXTZEILE
   entsprechen und nicht der umgebrochenen Bildschirmzeile. `wrap="off"` am
   Textfeld (order_new.html) sorgt dafuer, dass eine Textzeile immer genau
   eine Bildschirmzeile ist -- ein CSS-Zaehler allein koennte das bei
   Zeilenumbruch nicht garantieren. */
.text-mit-zeilennummern { display: flex; align-items: stretch; }
.zeilennummern {
  flex: 0 0 auto; padding: .5rem .5rem .5rem 0; text-align: right;
  font: 13px/1.5 var(--mono, monospace); color: var(--grau);
  background: var(--weiss); border: 1px solid var(--linie); border-right: none;
  border-radius: 8px 0 0 8px; white-space: pre; overflow: hidden;
}
#text-eingabe {
  flex: 1 1 auto; font: 13px/1.5 var(--mono, monospace);
  border-radius: 0 8px 8px 0; overflow-x: auto; resize: vertical;
}

/* Plan Etappe 3, E7: das Textfeld soll die volle Kartenbreite nutzen, nicht
   die 48rem der generischen `textarea`-Regel oben (Zeile 211) -- die ist fuer
   einzeilige Felder richtig (ein Kommentarfeld ueber die halbe Bildschirm-
   breite liest sich schlecht) und hier falsch: hinein kommt eine kopierte
   Tabelle, und an einer umbrechenden Zeile sieht man die Spalten nicht mehr.
   Verworfen: die globale Regel anheben -- sie schuetzt die anderen Felder. */
#text-eingabe, .text-mit-zeilennummern { max-width: none; }

/* Editierbare Vorschau-Tabelle (Plan Etappe 3, Abschnitt 3.3): jede Zelle ein
   Eingabefeld. Ohne `width:100%` waeren die Felder auf ihre Platzhalterbreite
   geschrumpft und die Spalten liefen aus dem Raster; `min-width` haelt ein
   kurzes Kuerzel trotzdem lesbar breit. */
#preview-table td input[type="text"] { width: 100%; min-width: 7rem; }

/* Von Hand geaendert oder nachgetragen (E5): bleibt bis ins Audit sichtbar,
   deshalb eine EIGENE Farbe und nicht `.warn` -- das stuende hier fuer einen
   Fehler, nicht fuer eine bewusste Korrektur. */
.von-hand { background: #fffbe6; }

/* Zeile aus dem Fliesstext (E9): heller Hintergrund, damit sie neben einer
   Tabellenzeile auffaellt, ohne wie ein Fehler auszusehen (das bleibt
   `.blocked` vorbehalten). */
.zeile-fliesstext { background: #fff8f0; }

/* Zuordnung nicht (mehr) geprueft (E3): grau statt geloescht -- der letzte
   Stand bleibt lesbar, nur als veraltet gekennzeichnet, bis der Pruef-Knopf
   erneut lief. */
.ungeprueft { color: var(--grau); }

/* Ergebnis je Vorschauzeile. `missing` verlangt einen Stammdatensatz,
   `pending` nur die Prep.-Nr. — zwei verschiedene Aufgaben, deshalb zwei
   Farben aus der vorhandenen Palette (siehe .tag.wait und .stub). */
.tag.missing { background: #fff3f6; border-color: var(--pink); color: var(--pink); }
.tag.pending { background: #fff8e8; border-color: #d9a417; color: var(--warn); }

/* ICL-Stand am Treffer. Im Normalfall eine Randnotiz, bei ausstehender
   Registrierung hervorgehoben — sie blockiert den Auftrag spaeter. */
.icl { font-size: 11.5px; color: var(--grau); }
.icl.ausstehend { color: var(--warn); font-weight: 600; }

/* Ein gespeichertes LLM-Modell, das die Auswahlliste des Gateways nicht mehr
   fuehrt (Plan 2026-09-18-llm-einstellungen.md, E5) — bleibt als eigene
   Option sichtbar statt zu verschwinden, in derselben Warnfarbe wie
   .icl.ausstehend. `color` ist die einzige Eigenschaft, die Browser auf
   <option> zuverlaessig darstellen; Hintergrund/Rahmen wie bei .tag greifen
   dort nicht. */
.modell-veraltet { color: var(--warn); font-weight: 600; }

/* Logo auf der Anmeldeseite — dort gibt es keine Navigation, die es traegt. */
.logo-login { height: 40px; width: auto; display: block; margin-bottom: 1.2rem; }

/* Ruecknahme eines Statusschritts. Bewusst leiser als der Vorwaertsknopf —
   es ist die Ausnahme, nicht der Regelweg, soll aber auffindbar sein, ohne
   dass jemand zum Telefon greift. */
.revert {
  margin-top: .8rem; padding-top: .7rem;
  border-top: 1px dashed var(--linie);
}
.revert form {
  display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
  font-size: 12.5px;
}
.revert form input[type="text"] { width: auto; flex: 1 1 14rem; max-width: 22rem; }
.revert .field-hint { display: block; }

/* Positionen in der Kopfzeile: bewusst NICHT .revert, obwohl das Formular
   dahinter genauso "Grund + Knopf" ist. .revert bringt margin-top,
   padding-top und einen gestrichelten Rahmen fuer die "Zurueck"-Zeile mit —
   auf jede Position angewendet brach dadurch jede einzelne mit eigener
   gestrichelter Linie um. */
.position-loesen { display: inline-flex; align-items: center; gap: .3rem; margin: 0; }
.position-loesen input { width: 7rem; font-size: 12px; padding: .15rem .35rem; }
.position-loesen .link { font-size: 12.5px; }

/* Hinweis auf den offenen Entwurf im Order Entry. Bewusst zurueckhaltend:
   er steht ueber dem Formular, solange etwas Ungesendetes da ist, und soll
   beruhigen, nicht warnen. Das [hidden] braucht die eigene Regel, weil
   display:flex sonst gewinnt. */
.draft-bar {
  display: flex; flex-wrap: wrap; gap: .5rem 1rem;
  align-items: center; justify-content: space-between;
  background: #eef0fd; border-left: 3px solid var(--blau);
  border-radius: 4px; padding: .55rem .8rem; margin: 0 0 1.2rem;
  font-size: 12.5px; line-height: 1.45;
}
.draft-bar[hidden] { display: none; }
.draft-bar .link { font-size: 12.5px; white-space: nowrap; }

/* ─── Filterleiste ──────────────────────────────────────────────────────── */
.filterbar { padding: .9rem 1rem; }
.filterbar-row {
  display: flex; gap: .9rem; flex-wrap: wrap; align-items: flex-start;
}
.filterbar-row > div { min-width: 150px; }
.filterbar-row .grow { flex: 1 1 340px; }
.filterbar-actions { display: flex; gap: .4rem; align-items: center; padding-top: 1.3rem; }
input[type=search] {
  width: 100%; padding: .45rem .6rem; border: 1px solid var(--linie);
  border-radius: 6px; font: inherit; background: var(--weiss);
}
/* Muss mitgezogen werden: `input[type=search]:focus` ist spezifischer als das
   allgemeine `input:focus` weiter oben und gewaenne, bliebe hier Blau stehen —
   ausgerechnet das Suchfeld der Filterleiste, das am haeufigsten angeklickt
   wird, waere dann das einzige Feld mit blauem Fokusrahmen. */
input[type=search]:focus { outline: 2px solid var(--pink); outline-offset: -1px; }

/* Sammelanlage: die uebersprungenen Zeilen stehen als Liste im Ergebnisfeld,
   je Zeile mit ihrem Grund. Nur die Einrueckung — Farbe und Rahmen kommen von
   .warn bzw. .note. */
#bulk-result ul { margin: .4rem 0 0; padding-left: 1.2rem; }

/* Anforderungsblock: bei leerer Chargenliste hervorgehoben, sonst ruhig. */
#request-block { border-top: 1px solid var(--linie); padding-top: .8rem; }
#request-block.dringend { border-top-color: var(--pink); }

/* ─── Parameter-Schema-Editor ───────────────────────────────────────────── */
.schema-feld {
  border: 1px solid var(--linie); border-left: 3px solid var(--blau);
  border-radius: 6px; padding: .8rem .9rem; margin-bottom: .7rem;
  background: var(--weiss);
}
.sf-kopf { display: flex; align-items: center; gap: .5rem; margin-bottom: .7rem; }
.sf-kopf .spacer { flex: 1; }
.sf-kopf .link { font-size: 12.5px; }
.sf-kopf .link:disabled { opacity: .3; cursor: not-allowed; text-decoration: none; }
.schema-editor .vorschau { background: var(--flaeche); border-style: dashed; margin-top: 1rem; }
.schema-editor .vorschau h4 { margin: 0 0 .7rem; font-size: 13px; color: var(--grau); }
/* Die Vorschaufelder sind seit Nachtrag 22.09.2026 (N7) BEDIENBAR -- der
   Auftraggeber will "so tun, als wuerde man bestellen", und ein
   ausgegrautes, deaktiviertes Feld liess sich nicht aufklappen. Die
   fruehere Grau-Regel fuer `select:disabled`/`input:disabled` ist deshalb
   ERSATZLOS entfernt: die Felder sehen jetzt aus wie normale Eingabefelder
   der Bestellmaske (keine eigene Regel noetig, `select`/`input` weiter oben
   gelten unveraendert). Gilt genauso fuer die Vorschau des Assay-Assistenten
   (`#assistent-entwurf-vorschau` in stammdaten_form.html) -- sie liegt NICHT
   unter `.schema-editor`, braucht aber keine eigene Regel, weil es ohne die
   Grau-Regel nichts mehr gibt, das sie von hier unterscheiden muesste. */
.vorschau-hinweis { margin: .5rem 0 0; font-style: italic; }
.schema-editor .warn ul { margin: .4rem 0 0; padding-left: 1.2rem; }
/* Markierung "vom Assay-Assistenten vorgeschlagen" (dezent, kein Alarm --
   dafuer gibt es .warn): gepunktete Unterstreichung, Tooltip traegt den
   eigentlichen Hinweistext. Reine Anzeige, siehe schema_editor.js. */
.vorschlag-hinweis { margin: .3rem 0 0; }
.vorschlag-hinweis .vorschlag {
  border-bottom: 1px dotted var(--grau); cursor: help;
}
details.expert { margin-top: 1rem; }
details.expert summary {
  cursor: pointer; font-size: 12.5px; color: var(--grau); user-select: none;
}
details.expert summary:hover { color: var(--blau); }

/* ─── Assay-Assistent: Entwurf und Uebernahme (Plan N1, Nachtrag 21.09.2026) ─
   Der Entwurf-Block ist eine EINGERUECKTE Karte innerhalb des Assistenten-
   Bereichs -- dieselbe .card-Optik, nur erkennbar untergeordnet, damit klar
   bleibt: das hier ist der Entwurf, nicht die Maske selbst. */
#assistent-entwurf {
  margin-left: .8rem; background: var(--flaeche); border-style: dashed;
}
#assistent-entwurf dl { margin: 0 0 .8rem; }
#assistent-entwurf dt { font-size: 12px; color: var(--grau); margin-top: .5rem; }
#assistent-entwurf dd { margin: 0; }

/* Kennzeichnet ein Maskenfeld, dessen Wert aus einer Uebernahme des
   Assistenten stammt (Plan N2) -- dezent, ein linker Rand statt Farbflaeche:
   das Feld bleibt normal bedienbar, die Markierung ist ein Hinweis auf die
   HERKUNFT, keine Sperre. Verschwindet, sobald der Nutzer das Feld von Hand
   aendert (assay_assistent.js) oder die Uebernahme zurueckgenommen wird. */
.vom-assistenten {
  border-left: 3px solid var(--blau) !important;
}

/* `button[hidden]` ist bereits weiter oben abgedeckt (CLAUDE.md §6) -- die
   beiden neuen Knoepfe hier setzen `disabled`, nicht `hidden`, deshalb keine
   eigene Regel noetig. */

/* ─── Modal für Bulk-Statusänderung ──────────────────────────────────────── */
dialog.modal {
  border: 1px solid var(--linie);
  border-radius: 0.5rem;
  padding: 2rem;
  max-width: 500px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

dialog.modal::backdrop {
  background-color: rgba(0, 0, 0, 0.5);
}

dialog.modal h3 {
  margin: 0 0 1rem;
  font-size: 1.25rem;
  color: var(--tinte);
}

dialog.modal p {
  margin: 0.6rem 0;
}

dialog.modal fieldset {
  border: none;
  padding: 0;
  margin: 1rem 0;
}

dialog.modal label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 500;
}

dialog.modal select {
  width: 100%;
  padding: 0.5rem;
  border: 1px solid var(--linie);
  border-radius: 0.25rem;
  font-size: 14px;
}

dialog.modal .buttons {
  display: flex;
  gap: 1rem;
  justify-content: flex-end;
  margin-top: 1.5rem;
}

dialog.modal button {
  padding: 0.6rem 1.2rem;
  border: 1px solid var(--linie);
  border-radius: 0.25rem;
  cursor: pointer;
  font-size: 14px;
}

dialog.modal button.primary {
  background: var(--blau);
  color: white;
  border-color: var(--blau);
}

/* Der Dialog ist kein Sonderfall. Sein Bestaetigungsknopf ist ein primaerer
   Knopf wie jeder andere und folgt deshalb demselben Muster Blau→Pink; ein
   eigenes Dunkelblau an dieser Stelle wuerde das Muster genau dort aushebeln,
   wo die Entscheidung faellt. */
dialog.modal button.primary:hover,
dialog.modal button.primary:focus-visible {
  background: var(--pink-tief);
  border-color: var(--pink-tief);
}

dialog.modal button.primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

dialog.modal button.ghost {
  background: transparent;
  color: var(--tinte);
  border-color: var(--linie);
}

dialog.modal button.ghost:hover {
  background: var(--flaeche);
}

/* ─── „Über"-Dialog ──────────────────────────────────────────────────────── */
dialog.ueber { min-width: 320px; }
dialog.ueber .ueber-angaben {
  display: grid; grid-template-columns: max-content 1fr;
  gap: .3rem 1rem; margin: 1.25rem 0 0;
}
dialog.ueber .ueber-angaben dt { color: var(--grau); }
dialog.ueber .ueber-angaben dd { margin: 0; }

/* Feste Abmessungen an der KLASSE, nicht am spaeteren <img>: das leere
   Kaestchen belegt exakt den Platz, den das Bild einnehmen wird — beim
   Einsetzen verschiebt sich dadurch nichts. */
.ueber-logo { width: 200px; height: 80px; object-fit: contain; display: block; }
.ueber-logo--leer {
  border: 2px dashed var(--linie);
  border-radius: 4px;
  /* KEIN Firmenname als Ersatztext: das Unternehmen ist nicht gegruendet, und
     ein Name an dieser Stelle behauptete etwas, das es nicht gibt. Die
     Information fuer Screenreader traegt der aria-label am Element. */
}

/* ─── Hilfe-Chat ─────────────────────────────────────────────────────────── */
/* Der Knopf steht in der Navigationsleiste neben dem Postfach und muss dort
   aussehen wie die Links daneben — er ist ein <button>, weil er nirgendwohin
   fuehrt, sondern einen Dialog oeffnet. Die Angleichung passiert hier und
   nicht ueber eine Kopie der Linkregeln, damit eine Aenderung an der
   Navigation beides zugleich trifft. */
nav .navknopf {
  background: none; border: none; padding: 0;
  font: inherit; color: inherit;
  cursor: pointer;
}
/* `background: none` wie beim „Über"-Knopf und aus demselben Grund: der
   Navigationsknopf ist ein <button> ohne Flaeche, und `button:hover` schlaegt
   die Ruheregel dieser Klasse. Ohne das Zuruecksetzen bekommt die
   Navigationsleiste beim Hover einen farbigen Block, wo ein Link stehen soll.
   Die Zeile ist also nicht redundant, sondern das, was den Knopf ueberhaupt
   wie ein Link aussehen laesst. */
nav .navknopf:hover { color: var(--blau); background: none; }

/* Breiter als die uebrigen Dialoge: hier steht Fliesstext und kein Formular
   aus drei Feldern. */
dialog.hilfe { max-width: 640px; width: 92vw; }

/* Feste Hoehe mit eigenem Rollbalken. Ohne sie waechst der Dialog mit jeder
   Antwort, bis der Senden-Knopf unter den Fensterrand rutscht — und genau
   dann, wenn der Verlauf am laengsten ist, ist er am wenigsten erreichbar. */
.hilfe-verlauf {
  margin: 1rem 0 0;
  max-height: 45vh;
  overflow-y: auto;
  border: 1px solid var(--linie);
  border-radius: .25rem;
  padding: .6rem;
  background: var(--flaeche);
}
.hilfe-verlauf .empty { margin: 0; color: var(--grau); font-size: 13px; }

.hilfe-eintrag {
  border-left: 3px solid var(--linie);
  padding: .35rem .6rem;
  margin-bottom: .6rem;
  background: #fff;
}
.hilfe-eintrag:last-child { margin-bottom: 0; }
.hilfe-eintrag.frage { border-left-color: var(--grau); }
.hilfe-eintrag.antwort { border-left-color: var(--blau); }
.hilfe-eintrag.wartet { border-left-color: var(--linie); color: var(--grau); }
.hilfe-eintrag.fehler { border-left-color: var(--pink); }

/* „Antwort von: <Anbieter>" steht sichtbar an jeder Antwort. Ohne diese Zeile
   liesse sich nicht unterscheiden, ob eine unpassende Antwort vom Modell
   kommt, vom Retrieval oder daher, dass gar kein Anbieter eingestellt ist. */
.hilfe-eintrag .hilfe-kopf {
  font-size: 11.5px; color: var(--grau); margin-bottom: .2rem;
}
/* `pre-wrap`: Absaetze der Antwort bleiben erhalten, lange Zeilen brechen
   trotzdem um. */
.hilfe-eintrag .hilfe-text { font-size: 13.5px; white-space: pre-wrap; }
.hilfe-eintrag .hilfe-fuss {
  font-size: 11.5px; color: var(--grau); margin-top: .35rem;
}

dialog.hilfe form { margin-top: .9rem; }
dialog.hilfe input[type="text"] {
  width: 100%; padding: .5rem;
  border: 1px solid var(--linie); border-radius: .25rem;
  font-size: 14px;
}

/* ─── Technik-Doku-Chat (#021) ─────────────────────────────────────────────
   Derselbe Verlauf wie im Hilfe-Dialog (.hilfe-verlauf / .hilfe-eintrag) —
   die Klassen beschreiben eine Chatzeile und kein Fenster, deshalb tragen sie
   hier genauso. Anders ist nur die Umgebung: Der Technik-Doku-Chat steht auf
   einer EIGENEN SEITE und nicht in einem <dialog>, und damit greifen die
   dialog-gebundenen Regeln darueber nicht. Die paar Zeilen sind der ganze
   Unterschied. */
.technik-chat { max-width: 760px; margin-top: 1rem; }
/* Hoeher als im Dialog: eine Seite hat den Platz, und Architekturantworten
   sind laenger als eine Bedienungsauskunft. */
.technik-chat .hilfe-verlauf { max-height: 55vh; }
.technik-chat form { margin-top: .9rem; }
.technik-chat label {
  display: block; font-size: 12px; color: var(--grau); margin-bottom: .25rem;
}
.technik-chat input[type="text"] {
  width: 100%; padding: .5rem;
  border: 1px solid var(--linie); border-radius: .25rem;
  font-size: 14px;
}
.technik-chat .buttons { margin-top: .6rem; display: flex; justify-content: flex-end; }

/* ─── Grid-Management der Stammdatenliste (Teil C) ─────────────────────────
   Werkzeugleiste (Freitextfilter + Zahnrad), sortierbare Ueberschriften und
   der Spaltendialog. Der Dialog erbt sein Aussehen von `dialog.modal` — hier
   stehen nur die Unterschiede: er wird laenger als eine Bestaetigung und
   braucht deshalb eine eigene Hoehenbegrenzung mit Bildlauf, sonst waechst er
   bei zwanzig Spalten aus dem Fenster heraus. */
.grid-werkzeuge {
  display: flex; flex-wrap: wrap; gap: .6rem;
  align-items: flex-end; justify-content: space-between;
  margin: 1rem 0 .4rem;
}
.grid-filter { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.grid-filter label { font-size: 12px; color: var(--grau); }
.grid-filter input[type="search"] {
  padding: .4rem .5rem; min-width: 260px;
  border: 1px solid var(--linie); border-radius: .25rem; font-size: 14px;
}

/* Die Ueberschrift ist ein Link und soll trotzdem wie eine Ueberschrift
   aussehen: die Farbe kommt erst beim Zeigen dazu. Ein durchgehend blau
   unterstrichener Tabellenkopf laese sich als Navigation missverstehen. */
th .sortlink { color: inherit; text-decoration: none; white-space: nowrap; }
th .sortlink:hover { color: var(--blau); text-decoration: underline; }

dialog.grid-dialog { max-width: 460px; }
.grid-spaltenliste {
  list-style: none; margin: 0; padding: 0;
  max-height: 50vh; overflow-y: auto;
  border: 1px solid var(--linie); border-radius: .25rem;
}
.grid-spaltenliste li {
  display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; padding: .3rem .5rem;
  border-bottom: 1px solid var(--linie);
}
.grid-spaltenliste li:last-child { border-bottom: none; }
.grid-spaltenliste label {
  display: flex; align-items: center; gap: .45rem;
  font-size: 13px; margin: 0; cursor: pointer;
}
.grid-schieben { display: flex; gap: .25rem; }
.grid-schieben button { padding: .1rem .45rem; font-size: 12px; line-height: 1.4; }

/* ─── Anmeldeseite ──────────────────────────────────────────────────────────
   Eine zentrierte weisse Karte auf ruhigem, hellem Grund — dasselbe Muster wie
   die Anmeldemaske des Nuvisan „Shipping Hub". Ausdruecklich OHNE Bild: Die
   Anmeldung ist der Moment, in dem jemand genau zwei Dinge tun soll (Konto
   waehlen, absenden), und ein grossflaechiges Motiv dahinter traegt zu diesen
   beiden Handgriffen nichts bei. Das Markenvisual bleibt der Startseite
   vorbehalten, wo es die Begruessung traegt und nicht ein Formular stoert.

   Warum `--flaeche` und nicht `--off-weiss`: Beide sind ruhige helle Toene,
   aber die Karte ist weiss, und der Grund muss sie tragen, ohne mit ihr zu
   verschmelzen. `--off-weiss` (#f9f9f9) liegt so dicht an #fff, dass die
   Kartenkante nur noch am Schatten haengt; `--flaeche` (#f6f7fa) ist eine
   Spur dunkler und leicht kuehl und setzt die Karte auch ohne Schatten ab.
   Dazu kommt: `--flaeche` ist ohnehin der Seitengrund der ganzen Anwendung
   (siehe `body`) — die Anmeldung steht damit auf demselben Boden wie alles
   dahinter und nicht auf einer Sonderfarbe, die nur hier vorkommt.

   Die Hoehe waechst aus dem Innenabstand und wird von `min-height` nur
   angehoben, nicht gedeckelt. Auf niedrigen Fenstern kann die Karte damit
   nichts abschneiden: sie schiebt die Flaeche einfach auf ihre eigene Hoehe
   auf. */
.login-hero {
  display: flex; align-items: center; justify-content: center;
  min-height: 78vh; padding: 3rem 1rem;
  background: var(--flaeche);
}

/* Die Karte darf schmaler werden als ihre 460px, sonst passte sie bei 480px
   Fensterbreite nicht mehr neben den Innenabstand der Flaeche.

   Der Schatten ist mit dem Wegfall des Bildes deutlich zurueckgenommen. Er war
   auf das dunkelblau ueberzogene Foto eingestellt und musste die Karte dort
   gegen ein unruhiges Motiv herausheben; auf dem hellen `--flaeche`-Grund
   traegt schon der Farbunterschied die Kante, und derselbe kraeftige Schatten
   liegt jetzt als dunkler Hof unter einer Maske, die ausdruecklich schlicht
   sein soll. Die Linie kommt dazu, damit die Kante auch dort steht, wo der
   flache Schatten sie nicht mehr zeichnet — oben. */
.login-karte {
  width: 100%; max-width: 460px;
  background: var(--weiss); border-radius: 12px;
  padding: 2.4rem 2.2rem;
  border: 1px solid var(--linie);
  box-shadow: 0 6px 20px rgba(9, 14, 62, .07);
}

/* Nur Logo und Ueberschrift stehen mittig. Label, Fliesstext und Hinweiskasten
   bleiben linksbuendig — mittig gesetzter Fliesstext hat keine feste
   Zeilenanfangskante und liest sich schlechter. */
.login-karte .logo-login { margin: 0 auto 1.4rem; height: 44px; }
/* Eigene Groesse fuer die Ueberschrift IN der Karte: der Anwendungsname aus
   der Konfiguration ist lang („DMPK Order Entry & Lab Management") und laeuft
   in der 460px-Karte mit den 1,5rem der Grundregel so um, dass in der zweiten
   Zeile nur noch ein einzelnes Wort steht. Der Name wird deshalb nicht
   gekuerzt — er steht in der Konfiguration richtig —, sondern die Schrift wird
   auf ein Mass gebracht, bei dem er in eine Zeile passt. Die Zeilenhoehe ist
   trotzdem gesetzt: ein noch laengerer Name (die Konfiguration ist frei) soll
   dann in zwei sauber gesetzten Zeilen stehen und nicht auseinanderfallen. */
.login-karte h1 {
  text-align: center; margin-bottom: .6rem;
  font-size: 1.28rem; line-height: 1.3;
}
.login-karte .lead { margin: 0 0 1.4rem; font-size: 13px; color: var(--grau); }

/* Die Grundregel fuer .logo-login (Abschnitt „Logo") bleibt absichtlich
   unveraendert: sie ist der dokumentierte Austauschpunkt fuer ein Logo mit
   anderem Seitenverhaeltnis. Die Anpassung an die zentrierte Karte steht
   deshalb hier als Zusatzregel INNERHALB der Karte und nirgends sonst. */

/* Auswahlfeld und Knopf ueber die volle Kartenbreite. Die allgemeine Deckelung
   auf 48rem steht dem nicht im Weg — die Karte ist mit 460px deutlich
   schmaler. */
.login-karte select { width: 100%; }
.login-karte .cta { width: 100%; }

/* Der Hinweiskasten sitzt in der Karte und darf dort nicht so viel Platz
   nehmen wie im breiten Inhaltsbereich. */
.login-karte .note { margin: 1.4rem 0 0; font-size: 12px; line-height: 1.5; }

/* ─── Startseite ────────────────────────────────────────────────────────────
   Die Bildflaeche der Marke: das Nuvisan-Primaervisual mit einem Farbverlauf
   darueber, und darauf Begruessung, Rollenzeile und zwei Handlungsknoepfe,
   mittig untereinander. Hintergrund und Layout stehen hier in EINER Regel —
   die Startseite ist seit dem Umbau der Anmeldeseite die einzige Seite mit
   diesem Hintergrund, und eine eigene Klasse fuer einen einzigen Nutzer waere
   nur eine Indirektion, die den Verlauf von der Seite wegtraegt, auf der er
   zu sehen ist.

   Die Flaeche ist FLACHER als die der Anmeldeseite — dort fuellt eine Karte
   den Blick und braucht ihre Hoehe, hier steht nur Text darauf, und eine
   ebenso hohe Flaeche schoebe den Schnellzugriff komplett unter die Kante.
   Die Rechenweise ist dieselbe wie dort: Die Hoehe waechst aus dem
   Innenabstand, `min-height` hebt sie nur an und deckelt sie nicht. Auf
   niedrigen Fenstern kann die Ueberschrift damit nichts abschneiden.

   Warum ueber dem Foto ein Farbverlauf liegt: Das Visual ist kontrastreich und
   in der Helligkeit ungleichmaessig — es reicht von fast weissen Lichtflecken
   oben links bis in tiefes Blau unten rechts. Weisse Schrift darauf steht mal
   klar, mal verschwindet sie im hellen Bildbereich, und der Fokusrahmen der
   Bedienelemente waere je nach Bildstelle unterschiedlich gut zu sehen. Der
   Verlauf in Markenblau zieht die Helligkeit des Bildes auf ein gleichmaessig
   mittleres Niveau zusammen: Text und Knopfkanten stehen an jeder Stelle
   gleich ruhig, der Fokusrahmen bleibt ueberall erkennbar.

   Die Deckkraft ist bewusst niedrig gehalten. Der Verlauf soll das Bild
   BAENDIGEN und nicht ersetzen — das Molekuelmotiv ist der Anlass dieser
   Flaeche und muss als solches erkennbar bleiben, sonst traegt die Seite das
   Markenvisual nur noch als Dateinamen. Die Werte sind an genau dieser Grenze
   eingestellt: hoeher, und das Motiv verschwindet in einer blauen Flaeche;
   niedriger, und die Kanten der Elemente darauf beginnen im Bild zu flimmern.
   Wer ein Element deutlicher abgesetzt haben will, dreht deshalb an dessen
   eigenem Schatten und nicht an diesen beiden Alphawerten.

   Warum Verlauf und Bild in EINER `background`-Angabe stehen muessen: Eine
   zweite, spaetere `background`-Deklaration ersetzt die erste vollstaendig —
   der Verlauf stuende dann ANSTELLE des Bildes und nicht darueber. Beide
   Ebenen gehoeren deshalb kommagetrennt in dieselbe Angabe, der Verlauf als
   erste (oberste) Ebene, die Bild-URL als zweite (unterste). */
.start-hero {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  min-height: 44vh; padding: 3rem 1.2rem;
  text-align: center;
  background:
    linear-gradient(115deg, rgba(30, 43, 211, .62), rgba(22, 32, 158, .50)),
    url("/static/img/nuvisan_visual.jpg") center / cover no-repeat;
}
/* Weisse Schrift auf der Bildflaeche — die Ueberschrift traegt hier keinen
   Kasten, der sie vom Hintergrund trennen wuerde, also muss der Kontrast aus
   der Farbe kommen. Der Schatten haelt sie auch ueber den hellen Stellen des
   Visuals lesbar. */
.start-hero h1 {
  margin: 0; color: var(--weiss);
  font-size: clamp(1.6rem, 4vw, 2.4rem); line-height: 1.2;
  text-shadow: 0 2px 14px rgba(9, 14, 62, .45);
}
/* Gedaempftes Weiss: Die Rollenzeile ist eine Angabe und keine Ansage — sie
   soll unter der Begruessung stehen, ohne mit ihr um Aufmerksamkeit zu
   ringen. Reines Grau waere auf dem blauen Grund zu dunkel. */
.start-rolle {
  margin: .6rem 0 0; color: rgba(255, 255, 255, .82); font-size: 14px;
}

/* `flex-wrap`: Bei schmalem Fenster rutschen die Knoepfe untereinander statt
   ueber den Rand hinauszulaufen.

   Die Breite muss dabei ausdruecklich gesetzt werden: `.start-hero` ist eine
   Spalte mit `align-items: center`, und ein zentriertes Kind bekommt dort nur
   seine Inhaltsbreite. Die Knopfreihe waere damit von vornherein so schmal
   wie ihr Umbruchergebnis und knickte auch auf einem 1440px-Monitor um.
   `width: 100%` gibt ihr die volle Flaeche, `max-width` haelt die beiden
   Knoepfe trotzdem als Paar beieinander, statt sie an die Fensterraender zu
   schieben. */
.start-knoepfe {
  margin-top: 1.8rem;
  width: 100%; max-width: 32rem;
  display: flex; flex-wrap: wrap; gap: .9rem; justify-content: center;
}
/* Gleiche Breite fuer zwei gleichrangige Wege: „Neuer Auftrag" ist die
   laengere Beschriftung, und ohne feste Basis waere der eine Knopf sichtbar
   groesser als der andere — das liest sich als Rangfolge, die hier gerade
   nicht gemeint ist.

   Dieselbe Basis loest zugleich den Umbruch aus, und das ist der eigentliche
   Grund fuer sie. `flex-wrap` allein umbricht nur, wenn der Platz WIRKLICH
   nicht reicht; die beiden Beschriftungen sind kurz genug, dass sie sich auch
   auf 480px noch nebeneinanderquetschen — nebeneinander, aber ohne Luft und
   ohne die Ruhe, die zwei grosse Handlungsknoepfe brauchen. Mit 14rem je
   Knopf passen sie unterhalb von rund 500px Fensterbreite nicht mehr in eine
   Zeile und stehen dort sauber untereinander. `flex-grow` bleibt bei 0: Auf
   einem breiten Monitor sollen die Knoepfe nicht ueber die halbe Bildschirm-
   breite auseinandergezogen werden. */
.start-knoepfe .cta { flex: 0 1 14rem; text-align: center; }

/* ─── Schnellzugriff-Kacheln ────────────────────────────────────────────────
   Bewusst NICHT `.tiles`/`.tile`: Jene Kachel ist im Repo die
   Platzhalter-Kachel der Stub-Seiten (gestrichelter Rand, „—" statt Zahl) und
   heisst dort „hier fehlt noch etwas". Diese hier verweist auf Bereiche, die
   es gibt und die arbeiten — sie ist deshalb durchgezogen umrandet und liegt
   auf weissem Grund. Wer beide Bedeutungen in eine Klasse legt, nimmt dem
   gestrichelten Rand seine Aussage. */
/* auto-fit collapsed empty tracks, single tile stretched full row;
   auto-fill keeps tracks, both groups same width (#77) */
.start-kacheln {
  display: grid; gap: 1.1rem;
  /* Breiter als zuvor (220px -> 280px), Nutzerwunsch 28.09.2026: die Kacheln
     wirkten gedrungen, der Beschreibungstext brach auf drei/vier Zeilen. Bei
     typischen Fensterbreiten stehen jetzt 2-4 statt 3-5 Spalten, jede Kachel
     dadurch deutlich breiter. */
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
/* Schnellzugriff bleibt eine feste Liste von genau 5 Kacheln (start.html) und
   soll immer auf EINER Zeile stehen, nicht wie Workflows mit wachsender Zahl
   umbrechen (Nutzerwunsch 28.09.2026, zweite Runde) -- deshalb eine feste
   Spaltenzahl statt `auto-fill`. Jede Kachel nimmt automatisch 1/5 der
   verfuegbaren Breite, dadurch auch breiter als vorher. Unter 700px waeren
   fuenf gleich breite Spalten nur noch schmale Streifen; dort greift wieder
   das umbrechende Raster wie bei Workflows. */
.start-kacheln--fest { grid-template-columns: repeat(5, 1fr); }
@media (max-width: 700px) {
  .start-kacheln--fest { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); }
}
.start-kachel {
  display: block; text-decoration: none;
  background: var(--weiss); border: 1px solid var(--linie);
  border-left: 3px solid var(--blau); border-radius: 8px;
  padding: 1.15rem 1.35rem;
}
/* Die Kachel ist ein Link und muss das zeigen. Der farbige linke Balken
   wechselt dabei ins Marken-Pink — dieselbe Bewegung wie bei den Knoepfen,
   nur an dem Element, das hier die Farbe traegt. Eine ganz pinke Flaeche
   waere zu laut fuer eine Liste aus fuenf gleichrangigen Verweisen. */
.start-kachel:hover, .start-kachel:focus-visible {
  border-left-color: var(--pink); box-shadow: 0 4px 14px rgba(9, 14, 62, .10);
}
.start-kachel h3 { margin: 0 0 .3rem; font-size: 14px; color: var(--tinte); }
.start-kachel p { margin: 0; font-size: 12.5px; color: var(--grau); }
/* Geplante Kachel: Workflow ohne Modul, grau ausgeblendet, keine Interaktion. */
.start-kachel--geplant { opacity:.6; border-left-color: var(--linie); cursor: default; }
.start-kachel--geplant:hover { border-left-color: var(--linie); box-shadow: none; }

/* ─── Hilfe-Zeile ───────────────────────────────────────────────────────────
   Frage und Knopf stehen nebeneinander in einem ruhigen Kasten. `flex-wrap`
   aus demselben Grund wie oben: bei schmalem Fenster rutscht der Knopf unter
   die Frage, statt sie zu quetschen. */
.start-hilfe {
  margin-top: 1.8rem;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: .9rem; justify-content: space-between;
  background: var(--off-weiss); border: 1px solid var(--linie);
  border-radius: 8px; padding: 1rem 1.1rem;
}
.start-hilfe p { margin: 0; font-size: 14px; color: var(--tinte); }

/* ─── Griffe fuer die Spaltenbreite ─────────────────────────────────────────
   Gehoert zu app/static/spaltenbreite.js. Dort steht das Warum der Machart
   („messen, dann einfrieren"), hier nur, was die Regeln leisten.

   DIE REGEL HAENGT AM MERKMAL, NICHT AN DER SEITE.
   Zuerst stand hier `.seite-labor th`, weil das Ziehen nur im Arbeitsvorrat
   verdrahtet war. Mit den Stammdatenlisten kam die zweite Seite dazu, und eine
   Aufzaehlung `.seite-labor th, .seite-stammdaten th, …` waere bei jeder
   weiteren Liste erneut zu ergaenzen — und genau das vergisst irgendwann
   jemand, mit dem Ergebnis, dass alle Griffe uebereinander in der linken
   oberen Ecke liegen (siehe unten, warum). `table[data-spaltenbreite] th`
   greift dagegen ueberall dort, wo das Skript ohnehin andockt: dasselbe
   Attribut entscheidet ueber Verhalten UND Darstellung, es gibt keine zweite
   Liste zu pflegen.

   Nebenwirkung, die hier festgehalten gehoert: Der Selektor enthaelt kein
   `.seite-labor th` mehr. `tests/test_layout_breite.py` liest die Dichteregel
   ueber `CSS.index(".seite-labor th")`, also ueber das ERSTE Vorkommen —
   dieser Block kann ihr jetzt nicht mehr in die Quere kommen, egal wo er
   steht. Fuer `main {` und `footer {` gilt die Reihenfolge unveraendert
   weiter.

   `position: relative` am <th> ist keine Kosmetik, sondern der Bezugspunkt:
   der Griff ist `position: absolute` und richtet sich am naechsten
   positionierten Vorfahren aus. Ohne diese Zeile waere das der Seitenrumpf,
   und alle Griffe laegen uebereinander in der linken oberen Ecke. */
table[data-spaltenbreite] th { position: relative; }

/* Sichtbarkeit in DREI Stufen — und die erste Fassung hatte nur zwei.
   Ursprünglich war der Griff im Ruhezustand unsichtbar und zeigte sich erst,
   wenn der Zeiger auf ihm stand. Die Begruendung dafuer war, zehn dauerhafte
   Striche waeren Laerm in einer Kopfzeile, die ohnehin zehn Beschriftungen
   traegt. Das Argument stimmt weiterhin — nur war das Ergebnis eine Funktion,
   die niemand fand: der Griff ist 6 Pixel breit, und wer nicht weiss, dass er
   da ist, faehrt nie zufaellig darueber. Ein Bedienelement, das man nur
   findet, wenn man schon weiss, dass es existiert, gibt es fuer die meisten
   Nutzer nicht.
   Deshalb jetzt:
     Ruhe          — unsichtbar, wie bisher. Wer liest, sieht keine Striche.
     Zeiger im Kopf— alle Griffe als duenne Linie in --linie. Das ist die
                     Einladung: sie erscheint, sobald jemand ueberhaupt im
                     Tabellenkopf unterwegs ist, und verschwindet beim
                     Verlassen wieder. Derselbe Ton wie die Tabellenlinien,
                     also kein neues Signal, sondern eine angedeutete Kante.
     Griff getroffen— --blau. Erst hier wird aus der Andeutung ein Ziel.
   Der Aufhaenger ist `thead:hover` und nicht `table:hover`: beim Ueberfliegen
   der Zeilen weiter unten haben die Griffe nichts zu suchen.

   Er ragt um seine halbe Breite ueber den Spaltenrand hinaus (`right: -3px`):
   5 Pixel sind ein kleines Ziel, und ein Griff, der auf beiden Seiten der
   gedachten Trennlinie liegt, laesst sich treffen, ohne ihn zu suchen.

   `touch-action: none` ist kein Detail: ohne die Zeile wertet ein Touchgeraet
   die Zieh-Geste als Scrollen aus, nimmt dem Griff den Zeiger weg und die
   Spalte bleibt stehen, waehrend die Tabelle wegrutscht. */
.spalten-griff {
  position: absolute; top: 0; right: -3px; z-index: 1;
  width: 6px; height: 100%;
  cursor: col-resize; user-select: none; touch-action: none;
  background: transparent;
}
table[data-spaltenbreite] thead:hover .spalten-griff { background: var(--linie); }
/* Die beiden Selektoren sind absichtlich so lang wie der darueber und nicht
   das kuerzere `.spalten-griff:hover`. Spezifitaet: die Einladungsregel ist
   (0,2,3), ein blosses `.spalten-griff:hover` waere (0,2,0) und verloere —
   der Griff bliebe grau, gerade wenn der Zeiger auf ihm steht. Gleich lang
   und weiter unten heisst: diese hier gewinnt. */
table[data-spaltenbreite] thead .spalten-griff:hover,
table[data-spaltenbreite] thead .spalten-griff.zieht { background: var(--blau); }

/* Waehrend des Ziehens am <body>: die Textauswahl unterbinden (sonst markiert
   der Zug quer ueber den Kopf saemtliche Spaltentitel blau) und den Cursor
   stabil halten, damit er nicht bei jedem Pixel zwischen col-resize und dem
   Cursor des gerade ueberfahrenen Elements springt. */
body.spalten-ziehen { user-select: none; cursor: col-resize; }

/* ─── Chargenwahl in der Compound-Auswahl ──────────────────────────────────
   Nur der seltene Fall: ein Compound, der mehrfach hergestellt wurde. Die
   Auswahlliste steht IN der Zeile neben der Compound-ID, damit die Zuordnung
   ohne Blickwechsel lesbar bleibt — eine Liste darunter waere bei mehreren
   solchen Eintraegen nicht mehr eindeutig einer Zeile zuzuordnen.

   Schmal gehalten: eine Prep.-Nr. ist kurz, und der Eintrag soll nicht breiter
   werden als seine Nachbarn, sonst faellt der Ausnahmefall staerker ins Auge
   als die 101 gewoehnlichen Eintraege daneben. */
/* Die Zeile ist ein <div>, die Beschriftung ein <label> darin — siehe die
   Begruendung an `renderCompounds`. Damit ein Klick auf den Text weiterhin
   auswaehlt wie frueher, fuellt das <label> die Zeile aus. */
.pick > label { display: flex; align-items: center; gap: .45rem; flex: 1; cursor: pointer; margin: 0; }

.pick .chargenwahl {
  width: auto; max-width: 14rem;
  margin-left: .1rem; padding: .1rem .3rem;
  font-size: 12.5px;
}

/* ─── Workflow-Kacheln ────────────────────────────────────────────────────────
   Anzeigebereich für die verfügbaren Workflows unter dem Arbeitsvorrat. Blau
   für die gekoppelten Workflows (Auszeichnung, nicht Warnung): die Signalfarbe
   Pink ist im Repo für Interaktionen reserviert und trägt nur tiefe Variante
   in Normaltext, eine pinke Umrandung einer ganzen Kachel behauptete eine
   Warnung, wo Auszeichnung gemeint ist. */

.workflow-uebergabe { margin-top: 1.5rem; }
.workflow-uebergabe h2 { font-size: 15px; margin: 0 0 .2rem; }
.kachelreihe {
  display: grid; gap: .9rem; margin-top: .8rem;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}
.workflow-kachel {
  background: var(--weiss); border: 1px solid var(--linie);
  border-radius: 8px; padding: .9rem 1rem;
  display: flex; flex-direction: column; gap: .5rem;
}
.workflow-kachel.gekoppelt { border-color: var(--blau); }
.workflow-kachel .kopf { display: flex; align-items: center; gap: .5rem; }
.workflow-kachel .icon { font-size: 1.5rem; line-height: 1; }
.workflow-kachel h3 { margin: 0; font-size: 13.5px; color: var(--tinte); }
.workflow-kachel .kurz { margin: 0; font-size: 12px; color: var(--grau); }
.workflow-kachel .zaehler { margin: 0; font-size: 12.5px; }
.workflow-kachel .zaehler strong { color: var(--blau); font-size: 15px; }
.workflow-kachel .formate { display: flex; gap: .4rem; flex-wrap: wrap; }
.workflow-kachel .formate a {
  /* gleiche Machart wie .exportbar a */
  color: var(--blau); text-decoration: none;
  border: 1px solid var(--linie);
  border-radius: 6px; padding: .2rem .55rem;
  background: var(--weiss); font-size: 12px;
}
.workflow-kachel .formate a:hover {
  border-color: var(--blau); background: #eef0fd;
}
.uebergabe-form {
  display: flex; gap: .4rem; flex-wrap: wrap; align-items: center;
}
.uebergabe-form input[type=text] {
  flex: 1 1 8rem; min-width: 6rem;
}
.workflow-kachel.leer { opacity: .55; }
.workflow-kachel .hinweis { margin: 0; }
/* Die Kachel wächst nach unten; die Aktionen sollen unten bündig stehen,
   damit die Reihe nicht ausfranst. */
.workflow-kachel .formate, .uebergabe-form { margin-top: auto; }

/* Der zweite Weg in die Anwendung: oeffnen, ohne zu uebergeben. Bewusst als
   Link und nicht als Knopf — er loest nichts aus, er wechselt nur den Ort. */
.workflow-kachel .anwendung { margin: 0; font-size: 12.5px; }
.workflow-kachel .anwendung a { color: var(--blau); font-weight: 600; }

/* ─── Workflow-Lauf (Compact Mode) ─────────────────────────────────────── */
.seite-workflows table               { font-size: 13px; line-height: 1.35; }
.seite-workflows th, .seite-workflows td { padding: .3rem .5rem; }
/* minmax(0, 1fr) statt 1fr: ein nacktes 1fr ist minmax(auto, 1fr) und
   waechst mit dem Mindestinhalt der Spalte -- die Report-Tabelle schob die
   Dateiliste des dritten Schritts 700 px weiter nach rechts als die der
   ersten beiden (Nutzerbefund 28.09.2026). Mit 0 als Untergrenze bleibt die
   Spalte in der Karte und ihre Tabellen scrollen in ihrem .scroll-Rahmen;
   alle Dateilisten stehen dann buendig untereinander. */
/* Ueberschrift der Schritte-Karte mit dem ZIP-Knopf rechts -- als Flex, nicht
   als Float: ein Float neben einem Grid-Container (BFC) drueckt dessen
   erste Zeile schmaler, die Dateiliste von Schritt 1 stand deshalb 140 px
   weiter links als die der anderen Schritte (Nutzerbefund 28.09.2026). */
.schritt-kopf { display:flex; justify-content:space-between; align-items:center; gap:.6rem; }
.schritt { display:grid; grid-template-columns:150px minmax(0, 1fr) 300px; gap:1rem; padding:.9rem 0; border-bottom:1px solid var(--linie); align-items:start; }
.schritt:last-child { border-bottom:none; }
.schritt .nr { font-weight:600; font-size:12px; text-transform:uppercase; letter-spacing:.04em; color:var(--grau); }
.schritt .nr b { display:block; color:var(--tinte); font-size:15px; text-transform:none; letter-spacing:0; }
.schritt .param { display:flex; gap:.6rem; flex-wrap:wrap; }
.schritt .param > div { min-width:120px; }
.schritt .stand { font-size:12.5px; color:var(--grau); justify-self:end; width:100%; }
.schritt .stand a { display:block; }
/* Dateiliste am Schritt (Nutzerbefund 28.09.2026: eingerueckte, endlose
   Linkliste). Eine Zeile je Datei mit Typ-Kuerzel, lange Namen werden mit
   Auslassung gekuerzt und stehen vollstaendig im title. */
.schritt .stand .stand-kopf { margin:0 0 .35rem; }
.schritt .stand details summary { cursor:pointer; color:var(--blau); }
.dateiliste { list-style:none; margin:0; padding:0; }
.dateiliste li { display:flex; align-items:center; gap:.4rem; padding:.15rem 0; border-top:1px solid var(--linie); min-width:0; }
.dateiliste li:first-child { border-top:none; }
.dateiliste a { display:block; flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-family:var(--mono, ui-monospace, monospace); font-size:11.5px; }
.dateityp { flex:0 0 auto; font-size:10px; font-weight:600; text-transform:uppercase; letter-spacing:.04em; padding:.05rem .35rem; border-radius:4px; background:#eef0f4; color:var(--grau); }
.dateityp.xlsx { background:#e5f4ea; color:#1e6b3a; }
.dateityp.docx { background:#e6eefb; color:#1f4fa0; }
.dateityp.csv  { background:#fff4e0; color:#8a5a00; }
.status.in_arbeit { border-color:var(--blau); color:var(--blau); background:#fff; }
.status.abgeschlossen { background:var(--blau); color:#fff; border-color:var(--blau); }
.status.abgebrochen { color:var(--grau); font-style:italic; }
