/* ISOCAL Relay - Gemeinsame Styles
 *
 * FARB-SSOT aller drei UIs (Admin-Panel, Kunden-Portal, Dispatcher-Portal).
 * Farbschema: ISOLY (Teal/Lime), Stand 2026-07-31.
 * Quelle der Marken-Tokens: ISOLY-DESIGN-TOKENS.md (Variante A).
 *
 * WICHTIGSTE AENDERUNG SEIT 2026-07-29: Obere Leiste und Dialogkoepfe sind
 * LIME mit TEAL-Schrift (vorher Teal-900 mit Weiss) - Anwender-Entscheidung
 * 2026-07-31, damit App und Weboberflaechen dasselbe Gesicht haben. Daran
 * haengen die Logo-Varianten: helle Flaeche verlangt isoly-logo.svg, nicht die
 * Reverse-Fassung. Begruendung und Kontrastwerte stehen bei .navbar-isocal.
 *
 * ── Zwei Regeln, die beim Aendern von Farben gelten ──────────────────────
 *
 * 1. DER AKZENT RICHTET SICH NACH DER FLAECHE, NICHT NACH DEM ELEMENT.
 *    Auf hellem Grund ist der Akzent Teal-600 (--isoly-accent), auf dunklem
 *    Grund Lime (--isoly-accent-on-dark). Wer ein Akzent-Token setzt, prueft
 *    ZUERST den Hintergrund der umgebenden Flaeche.
 *    Lime ist FLAECHENfarbe, nie Textfarbe auf Hell: Lime auf Weiss = 1,41:1.
 *
 * 2. JEDER KONTRASTWERT IST NACHGERECHNET (WCAG 2.1, Relativluminanz), nicht
 *    aus der Tokens-Doku uebernommen - deren Kontrasttabelle weicht 4-8 % ab.
 *    Belegte Abweichung: die Doku nennt Teal-500 #2C8F63 als "accent-onLight";
 *    nachgerechnet sind das nur 4,03:1 auf Weiss (unter AA). Dieses Projekt
 *    verwendet daher Teal-600 #1D6B4F (6,43:1 auf Weiss, 5,99:1 auf Teal-50).
 *
 * Herleitung der abgeleiteten Funktionsfarben: Die Funktionsfarben der
 * Tokens-Doku reissen als Text bzw. mit weisser Schrift das AA-Minimum
 * (Warnung #E8A13C 2,19:1, Fehler #D8503F 4,09:1, Info #3A8F86 3,85:1).
 * Sie wurden deshalb entlang EINER Achse abgeleitet: Farbton und Saettigung
 * bleiben fix, allein die HSL-Helligkeit wurde gesenkt, bis 4,5:1 gegen den
 * DUNKELSTEN Untergrund erreicht war, auf dem die Farbe vorkommt. Genommen
 * wurde jeweils der erste (= minimal veraenderte) passende Wert.
 * Vollstaendige Kontrast-Tabelle: docs/handoff/HANDOFF_2026-07-29_isoly-farbschema.md
 */

/* ── Schrift: Quicksand, lokal gehostet ───────────────────────────────────
 * Variable Font (300-700) in einer Datei je Subset, SIL Open Font License.
 * Bewusst KEIN Google-Fonts-CDN: kein Drittanbieter-Request aus dem Portal.
 * Pfad relativ zur CSS-Datei -> loest hinter dem Apache-Reverse-Proxy
 * genauso auf wie bei Direktzugriff (siehe .claude/rules/reverse-proxy-pfade.md).
 * latin-ext wird per unicode-range nur geladen, wenn es gebraucht wird.
 */
@font-face {
  font-family: 'Quicksand';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('fonts/quicksand-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Quicksand';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('fonts/quicksand-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* ── ISOLY-Marken-Skala (unveraendert aus den Design-Tokens) ─────────── */
  --teal-900: #0A3836;
  --teal-800: #0C433E;
  --teal-700: #13524A;
  --teal-600: #1D6B4F;
  --teal-500: #2C8F63;
  --teal-400: #5B736C;
  --teal-300: #B7CFC7;
  --teal-150: #E2EBE6;
  --teal-50:  #F4F8F5;
  --lime-500: #A3ED6B;
  --lime-600: #8AD94F;

  /* RGB-Tripel fuer rgba() und fuer die --bs-*-rgb-Variablen -
   * rgba() kann kein Hex-Token aufnehmen */
  --teal-900-rgb: 10, 56, 54;
  --teal-700-rgb: 19, 82, 74;
  --teal-600-rgb: 29, 107, 79;
  --teal-500-rgb: 44, 143, 99;
  --teal-50-rgb:  244, 248, 245;

  /* Weiss als Token: Text auf gesaettigter Markenflaeche. Bewusst ein
   * eigener Name statt #fff im Komponentencode - so ist an jeder Stelle
   * lesbar, dass es sich um eine Kontrastentscheidung handelt. */
  --isoly-on-brand: #FFFFFF;

  /* ── Semantische Rollen (Light Theme, kein Dark Mode) ────────────────── */
  --isoly-bg:             var(--teal-50);    /* Seitenhintergrund */
  --isoly-surface:        #FFFFFF;           /* Karten */
  --isoly-text:           var(--teal-900);   /* 12,01:1 auf Seitengrund */
  --isoly-text-muted:     var(--teal-400);   /* 4,76:1 auf Seitengrund */
  --isoly-border:         var(--teal-150);  /* NUR Trennlinien - siehe unten */
  /* Kartenrahmen, eigenes Token seit TAB-02 (2026-08-02).
   * 5,10:1 auf Weiss · 4,76:1 auf Seitengrund. Warum getrennt von
   * --isoly-border: siehe die Herleitung an .card. */
  --isoly-border-card:    var(--teal-400);
  /* Begrenzung von BEDIENELEMENTEN. Eigenes Token, weil hier eine andere
     Schwelle gilt: WCAG 1.4.11 verlangt 3,0:1 fuer UI-Bestandteile, waehrend
     eine Trennlinie dekorativ ist. teal-400 erreicht auf Weiss 5,10:1 und auf
     dem Seitengrund 4,76:1 - der einzige Wert der Marken-Rampe, der beides
     schafft (teal-300 kommt nur auf 1,65:1). Kein neuer Farbwert: derselbe
     Ton dient bereits als --isoly-text-muted. */
  --isoly-border-control: var(--teal-400);
  --isoly-radius:         14px;

  /* Akzent - Flaeche entscheidet, siehe Regel 1 im Kopf */
  --isoly-accent:         var(--teal-600);   /* auf HELL  6,43:1 auf Weiss */
  --isoly-accent-on-dark: var(--lime-500);   /* auf DUNKEL 9,12:1 auf Teal-900 */

  /* CTA: Lime-Flaeche mit Teal-Text (9,12:1), Hover Lime-600 (7,43:1) */
  --isoly-cta-bg:       var(--lime-500);
  --isoly-cta-bg-hover: var(--lime-600);
  --isoly-cta-fg:       var(--teal-900);

  /* ── Funktionsfarben: FLAECHE und TEXT sind getrennte Rollen ──────────
   * Die Flaechen-Variante traegt weissen bzw. Teal-Text; die Text-Variante
   * ist so weit abgedunkelt, dass sie auf Weiss, auf Teal-50 UND auf dem
   * eigenen Tint mindestens 4,5:1 erreicht. Beide Rollen zu vermischen war
   * der Hauptfehlerweg - deshalb zwei Token statt einem.
   */
  --isoly-success:      var(--teal-600);  /* + weiss = 6,43:1 */
  --isoly-success-text: #227C5C;          /* weiss 5,11 · teal-50 4,77 · Tint 4,52 */
  --isoly-success-tint: #DFF6EE;

  --isoly-danger:       #D54230;          /* + weiss = 4,51:1 (aus #D8503F abgedunkelt) */
  --isoly-danger-text:  #BD3726;          /* weiss 5,62 · teal-50 5,24 · Tint 4,51 */
  --isoly-danger-tint:  #F6E2DF;

  --isoly-warning:       #E8A13C;         /* + Teal-900-Text = 5,88:1 (NUR mit dunklem Text!) */
  --isoly-warning-hover: #E59421;         /* -6 % Helligkeit, Teal-900-Text noch 5,26:1 */
  --isoly-warning-text:  #976012;         /* weiss 5,25 · teal-50 4,90 · Tint 4,53 */
  --isoly-warning-tint:  #F6EDDF;

  /* ── Verbindlichkeits-Stufen (§3.16) ───────────────────────────────
   *
   * Ampel-Logik der App-Seite, Betreiber-Zuruf 2026-08-30: „zur besseren
   * optischen Unterscheidung die Verbindlichkeits-Chips nach Wert
   * unterschiedlich faerben".
   *
   * ALLE WERTE SELBST NACHGERECHNET (WCAG 2.x, Ziel 4,5:1 — Chips sind
   * Kleinschrift, die 3:1-Grossschrift-Schwelle gilt hier NICHT):
   *
   *   FREI      lime-500 + teal-900   9,12:1
   *   STANDARD  #E8A13C  + teal-900   5,88:1
   *   FEST      teal-900 + weiss     12,87:1
   *   OHNE      teal-150 + teal-900  10,58:1
   *
   * CIEDE2000 untereinander: 25,6 bis 69,1 — alle weit ueber dem
   * Hausziel von 15.
   *
   * BEIDE RECHENWEGE SIND GEGENGEPRUEFT, jeder an SEINEN Randwerten:
   *
   *   WCAG       weiss/schwarz = 21,00 · gleiche Farbe = 1,00
   *   CIEDE2000  weiss/schwarz = 100,00 · gleiche Farbe = 0,00
   *
   * Hier stand zuerst nur das erste Paar — als Beleg fuer BEIDE Masse.
   * Das war falsch: 21,00 ist der WCAG-Maximalwert und sagt ueber die
   * CIEDE2000-Rechnung nichts. Eine Kontrollrechnung, die der falschen
   * Groesse zugeordnet ist, taeuscht eine Absicherung vor, die es nicht
   * gibt (`no-empirical-fixes.md`, Pflicht-Vorgehen 4). Gefunden vom
   * regel-check, der eine gegen die Sharma/Wu/Dalal-Testdaten
   * verifizierte Implementierung dagegengehalten hat.
   *
   * WARUM `--isoly-stufe-standard` EIN EIGENES TOKEN IST, obwohl es
   * denselben Wert wie `--isoly-warning` traegt: Es ist eine
   * STUFEN-Farbe, keine Warn-Semantik. Die App-Seite hat genau das
   * benannt und angeboten, sie „beidseitig abgestimmt zu tauschen" —
   * mit `var(--isoly-warning)` traefe ein solcher Tausch auch jeden
   * Warnhinweis. So ist es eine Zeile.
   *
   * KEINE der drei Flaechen hebt sich allein vom weissen Karten-Grund
   * ab (lime 1,41 · orange 2,19 · teal-150 1,22 — nur FEST mit 12,87).
   *
   * HIER STAND „Deshalb traegt jeder Chip einen teal-900-Rand". Er ist
   * mit CHIP-RUHE-01 (2026-08-31) gefallen — Betreiber-Sichtung, die
   * randlose Form ist beidseitig abgenommen. Die Zahlen darueber bleiben
   * richtig und sind der Grund, warum der Zustand seither im
   * SCHRIFTGEWICHT steckt; die Herleitung steht bei den `.es-vb`-Regeln.
   */
  --isoly-stufe-frei:     var(--lime-500);
  --isoly-stufe-standard: #E8A13C;
  --isoly-stufe-fest:     var(--teal-900);
  --isoly-stufe-ohne:     var(--teal-150);

  --isoly-info:         #22828F;          /* + weiss = 4,51:1 */
  --isoly-info-text:    #1F7783;          /* weiss 5,22 · teal-50 4,87 · Tint 4,54 */
  --isoly-info-tint:    #DFF3F6;

  --isoly-neutral:      #6D7883;          /* + weiss = 4,50:1, neutrales Grau */

  /* RGB-Tripel der Funktionsfarben fuer die --bs-*-rgb-Variablen */
  --isoly-danger-rgb:   213, 66, 48;
  --isoly-warning-rgb:  232, 161, 60;
  --isoly-info-rgb:     34, 130, 143;
  --isoly-neutral-rgb:  109, 120, 131;

  /* Die alten Token mit dem Praefix aus der Blau-Zeit wurden mit diesem
   * Milestone ersatzlos entfernt: alle sieben Konsumenten (dispatcher.css,
   * live-status.html, machines.html) sind auf die --isoly-*-Namen
   * umgestellt. Aliase waeren hier nicht nur toter Code, sondern
   * irrefuehrend - ein Token, dessen Name "blue" enthaelt und das einen
   * Gruenwert traegt, ist eine Falle fuer den naechsten Leser.
   * (Kein Alt-Praefix als Literal in diesem Kommentar - sonst schlaegt die
   * Verifikations-Grep aus dem Handoff auf den Kommentar selbst an.)
   */

  /* ── Bootstrap-5.3-Variablen ueberschreiben ───────────────────────────
   * Die Utility-Klassen (.bg-*, .text-*, .border-*) lesen zur Laufzeit die
   * --bs-*-rgb-Variablen. Werden sie hier gesetzt, folgen alle Utilities
   * automatisch - auch solche, die erst zur Laufzeit aus JS zusammengesetzt
   * werden (z. B. `bg-${farbe}`) und per Grep nicht auffindbar waeren.
   * Buttons und Alerts lesen sie NICHT (Sass-generiert) und sind unten
   * einzeln ueberschrieben.
   */
  /* Konsequent per var() auf die Token oben verweisen statt die Hexwerte
   * zu wiederholen - sonst muesste ein kuenftiger Farbwechsel an zwei
   * Stellen greifen, genau das Risiko, das die Token-SSOT vermeiden soll. */
  --bs-primary:       var(--teal-600);      --bs-primary-rgb:   var(--teal-600-rgb);
  --bs-secondary:     var(--isoly-neutral); --bs-secondary-rgb: var(--isoly-neutral-rgb);
  --bs-success:       var(--isoly-success); --bs-success-rgb:   var(--teal-600-rgb);
  --bs-danger:        var(--isoly-danger);  --bs-danger-rgb:    var(--isoly-danger-rgb);
  --bs-warning:       var(--isoly-warning); --bs-warning-rgb:   var(--isoly-warning-rgb);
  --bs-info:          var(--isoly-info);    --bs-info-rgb:      var(--isoly-info-rgb);
  --bs-light:         var(--teal-50);       --bs-light-rgb:     var(--teal-50-rgb);
  --bs-dark:          var(--teal-900);      --bs-dark-rgb:      var(--teal-900-rgb);

  --bs-link-color:       var(--teal-600);  --bs-link-color-rgb:       var(--teal-600-rgb);
  --bs-link-hover-color: var(--teal-700);  --bs-link-hover-color-rgb: var(--teal-700-rgb);

  --bs-body-color: var(--teal-900);  --bs-body-color-rgb: var(--teal-900-rgb);
  --bs-body-bg:    var(--teal-50);   --bs-body-bg-rgb:    var(--teal-50-rgb);
  --bs-border-color: var(--isoly-border);

  /* .text-muted (62 Verwendungen) liest in Bootstrap 5.3 NICHT
   * --bs-secondary-rgb, sondern --bs-secondary-color - ohne diese Zeile
   * bliebe es Bootstrap-Neutralgrau statt Teal-400.
   * Teal-400: 5,10:1 auf Weiss, 4,76:1 auf Teal-50. */
  --bs-secondary-color: #5B736C;

  --bs-font-sans-serif: 'Quicksand', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
}

html {
  color-scheme: light;
}

body {
  background-color: var(--isoly-bg) !important;
  color: var(--isoly-text) !important;
  /* Quicksand wirkt in 400 zu duenn - Body-Schnitt ist 500 (Tokens-Doku) */
  font-family: 'Quicksand', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-weight: 500;
}

/* Explizite Textfarben - Bootstrap 5.3 CSS-Variablen koennen
   bei Override von --bs-primary Textfarben auf weiss setzen */
.card, .card-body, .modal-body {
  color: var(--isoly-text);
}

/* ── Modal-Header: Lime-Leiste mit Teal-Schrift (9,12:1) ───────────────
 *
 * UMGESTELLT 2026-07-31 auf Anwender-Entscheidung. Vorher Teal-900 mit
 * weisser Schrift. Anlass: Die App wurde auf Lime-Flaeche mit Teal-Schrift
 * umgestellt - obere Leiste und alle Dialoge -, und die Weboberflaechen
 * ziehen nach, damit App und Portal nicht auseinanderlaufen.
 *
 * Selbst nachgerechnet (WCAG, nicht aus der Tokens-Doku uebernommen):
 *   teal-900 auf lime-500   9,12:1   (Text-Schwelle 4,5:1 - reichlich)
 *   schwarz  auf lime-500  14,88:1   (Bootstrap-Standard des Schliesskreuzes)
 */
.modal-header {
  background-color: var(--isoly-cta-bg);
  color: var(--isoly-cta-fg);
}

.modal-header .modal-title {
  color: var(--isoly-cta-fg);
}

/* KEIN Weiss-Filter mehr. Er faerbte das Kreuz fuer die dunkle Leiste weiss;
 * auf Lime waere es mit 1,41:1 praktisch unsichtbar. Das Bootstrap-Original
 * ist schwarz und erreicht hier 14,88:1 - der Filter muss also nicht ersetzt,
 * sondern nur entfernt werden. */
.modal-header .btn-close {
  filter: none;
}

.form-label, label {
  color: var(--isoly-text);
}

/*
 * Bedienelemente: weisse Flaeche, sichtbare Begrenzung.
 *
 * Der Hintergrund kam aus --bs-body-bg und war damit teal-50 - also dieselbe
 * Farbe wie Seite und Karte (siehe die Herleitung bei .card). Ein Auswahlfeld,
 * das sich in nichts von seinem Untergrund unterscheidet, ist keins.
 *
 * WICHTIGER ALS DIE FLAECHE IST DER RAND. Ein Auswahlfeld ist ein
 * UI-Bestandteil; WCAG 1.4.11 verlangt fuer dessen Begrenzung 3,0:1 - nicht
 * die 1,22:1 einer Trennlinie. Gemessen am 2026-08-01 auf weisser Flaeche:
 *
 *   teal-150 (bisher)   1,22:1   ZU SCHWACH
 *   teal-300            1,65:1   ZU SCHWACH
 *   teal-400            5,10:1   ok
 *   Bootstrap-Standard  1,30:1   ZU SCHWACH
 *
 * Bemerkenswert: Auch Bootstraps eigene Vorgabe reisst die Schwelle. Der
 * Fehler ist also nicht hier entstanden - der teal-50-Kartengrund hat ihn nur
 * von "knapp" zu "unsichtbar" gemacht.
 *
 * --isoly-border BLEIBT UNVERAENDERT bei teal-150 - aber die erste Fassung
 * dieses Kommentars begruendete das falsch. Sie behauptete, das Token trage
 * "nur dekorative Trennlinien". Das stimmt nicht: Es ist auch der Rahmen von
 * .card, und auf dem Admin-Dashboard sind Karten teilweise ECHTE LINKS
 * (.stat-card-link) - also Bedienelemente, fuer die dieselbe 3,0:1-Schwelle
 * gilt, mit der hier argumentiert wird. Ein regel-check-Befund.
 *
 * Nachgesehen statt weiterbehauptet: Diese Kacheln tragen eine 4 Pixel starke
 * Akzentkante (--isoly-accent, 6,43:1) und einen Fokusring aus derselben
 * Farbe. Ihre Begrenzung ist damit erkennbar - WCAG 1.4.11 verlangt, dass die
 * zur IDENTIFIKATION noetige Information die Schwelle erreicht, nicht dass
 * jede Kante es tut. Der Fall ist also gedeckt, aber er ist gedeckt durch die
 * Kante, nicht durch den Rahmen.
 *
 * Es bleibt trotzdem bei einem EIGENEN Token: Fuer Trennlinien
 * (Abschnitts-Unterstriche, Strang-Trenner, Composer-Kante) waeren 5,10:1 zu
 * laut, und die sind der ueberwiegende Teil der Verwendungen.
 *
 * NACHTRAG TAB-02 (2026-08-02): Der Absatz oben stimmt weiterhin, aber sein
 * Reibungspunkt ist entfallen. `.card` haengt seit TAB-02 NICHT mehr an
 * --isoly-border, sondern an --isoly-border-card (teal-400, 4,76:1 gegen den
 * Seitengrund) - Herleitung dort.
 *
 * GENAU GESAGT, nicht ungefaehr: --isoly-border traegt seither Trennlinien UND
 * leise Innenkanten - Vorschaurahmen und Listentrenner im Dispatcher,
 * Sprechblasen und das gestrichelte Ablagefeld im Feedback-Verlauf, dazu ueber
 * --bs-border-color jede Bootstrap-Kante (Tabellenzeilen, Listengruppen,
 * Dropdowns). Was es NICHT mehr traegt, ist der Rahmen eines Bedienelements
 * oder einer Karte. Damit ist der Einwand von oben ausgeraeumt - aber die
 * bequeme Formulierung "nur dekorative Trennlinien" waere weiterhin falsch.
 *
 * Der .stat-card-link-Fall ist seither doppelt gedeckt: durch die 4-Pixel-
 * Akzentkante UND durch den Rahmen. An der Aussage oben aendert das nichts -
 * sie beschreibt, warum er schon vorher gedeckt war.
 */
.form-control, .form-select {
  color: var(--isoly-text);
  background-color: var(--isoly-surface);
  border-color: var(--isoly-border-control);
}

h1, h2, h3, h4, h5, h6 {
  color: var(--isoly-text);
  font-weight: 700;
}

h3, h4, h5, h6 {
  font-weight: 600;
}

p, li, td, th {
  color: inherit;
}

/* ── Navbar ─────────────────────────────────────────────────────────────
 *
 * LIME-LEISTE MIT TEAL-SCHRIFT. Anwender-Entscheidung 2026-07-31.
 *
 * Sie ERSETZT die Entscheidung vom 2026-07-29 (Teal-900 mit weisser Schrift).
 * Deren Begruendung steht hier, weil sie nicht falsch war, sondern ueberholt:
 *
 *   "Die Portale sind dichte Datentabellen-UIs, in denen eine Lime-Leiste
 *    dauerhaft dominieren wuerde; ausserdem faerbt der bestehende Logo-Filter
 *    die PNG-Logos WEISS - auf Lime waeren sie mit 1,41:1 unsichtbar."
 *
 * Was sich geaendert hat: Die App wurde auf Lime mit Teal-Schrift umgestellt,
 * obere Leiste und alle Dialoge. App und Weboberflaechen sollen dasselbe
 * Gesicht haben - das wiegt schwerer als die Sorge um die Dominanz.
 *
 * DER LOGO-EINWAND WAR TECHNISCH RICHTIG und ist deshalb MITGELOEST, nicht
 * uebergangen:
 *   - Admin-Panel und Kunden-Portal trugen isoly-logo-reverse.svg, die
 *     Marken-Variante fuer DUNKLE Flaechen (weiss + Lime). Auf Lime waere sie
 *     mit 1,41:1 verschwunden. Sie sind auf isoly-logo.svg umgestellt - die
 *     normale Variante des Markenpakets, gemacht fuer helle Flaechen.
 *   - Das Dispatcher-Portal traegt ein blaues Fremd-PNG, das per Filter weiss
 *     gefaerbt wurde. Es steht jetzt in einem weissen Kasten (Anwender-
 *     Entscheidung); der Filter entfaellt. Siehe dispatcher/css/dispatcher.css.
 *
 * Kontraste selbst nachgerechnet (WCAG), Flaeche lime-500:
 *   teal-900  9,12:1   teal-800  7,88:1   teal-700  6,38:1   teal-600  4,55:1
 *   weiss     1,41:1   <- deshalb muss ALLES Weisse hier weichen
 */

.navbar-isocal {
  background-color: var(--isoly-cta-bg) !important;
  box-shadow: 0 2px 4px rgba(0,0,0,0.15);
}

/* Die Schriftfarbe steht hier AUSDRUECKLICH und nicht implizit.
 *
 * Beinahe-Fehler beim Umstellen auf Lime: Das Markup trug Bootstraps Klasse
 * `navbar-dark`, und die setzt `--bs-navbar-brand-color: #fff`. Die Flaeche war
 * umgestellt, der Markenname blieb weiss - 1,41:1 auf Lime, also praktisch
 * unsichtbar. Aufgefallen ist es erst im regel-check.
 *
 * Behoben an zwei Stellen, und beide sind noetig:
 *   1. Im Markup steht jetzt `navbar-light` - semantisch richtig, die Leiste
 *      IST hell.
 *   2. Diese Zeile. Sie ist die tragende: Ohne sie haengt die Lesbarkeit an
 *      einer Fremdklasse, die jemand beim naechsten Umbau versehentlich
 *      zuruecksetzt - und der Fehler faellt niemandem auf, weil er nur die
 *      Marke betrifft, nicht die Bedienung.
 */
.navbar-isocal .navbar-brand {
  color: var(--isoly-cta-fg) !important;
  font-weight: 700;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* KEIN Filter hier - und seit 2026-07-31 auch keine Reverse-Variante mehr.
 *
 * Die Geschichte dieser Stelle in drei Schritten, weil jeder davon eine
 * Falle hinterlassen hat:
 *
 *   bis 2026-07-30  `filter: brightness(0) invert(1)` faerbte das blaue
 *                   ISOCAL-PNG fuer die dunkle Leiste weiss.
 *   ab  2026-07-30  isoly-logo-reverse.svg - die Marken-Variante fuer DUNKLE
 *                   Flaechen (weiss + Lime). Der Filter musste weg, er haette
 *                   das Lime im Wuerfel herausgebrannt.
 *   ab  2026-07-31  isoly-logo.svg - die NORMALE Variante, denn die Leiste ist
 *                   jetzt hell. Die Reverse-Fassung waere auf Lime mit 1,41:1
 *                   verschwunden; sie ist fuer dunkle Flaechen gemacht, und der
 *                   Name sagt es.
 *
 * Merksatz fuer die naechste Aenderung: Die Logo-Variante haengt an der
 * HELLIGKEIT DER FLAECHE, nicht am Geschmack. Wer die Leiste wieder abdunkelt,
 * muss beide Stellen zusammen anfassen - Farbe und Logo.
 *
 * Das Dispatcher-Portal traegt ein blaues Fremd-PNG und loest es anders: weisser
 * Kasten statt Filter (dispatcher/css/dispatcher.css). Seitenspezifischer Behelf
 * gehoert zur Seite (.claude/rules/frontend-shared-code.md).
 *
 * Hoehe 28px ergibt bei Seitenverhaeltnis 838:234 eine Breite von 100px und
 * haelt damit die Marken-Mindestbreite von 96px ein (Tokens-Doku, Abschnitt
 * Schutzraum). Nicht weiter verkleinern - darunter nur das Symbol verwenden.
 */
.navbar-isocal .navbar-brand img {
  height: 28px;
}

/* Ruhender Zustand teal-700 (6,38:1), aktiver teal-900 (9,12:1) und fett.
 * Der Unterschied liegt damit auf ZWEI Merkmalen - Farbe und Strichstaerke -,
 * nicht auf Farbe allein: Wer Farbunterschiede schlecht wahrnimmt, erkennt die
 * aktive Seite trotzdem. Beide Werte liegen ueber der Textschwelle von 4,5:1. */
.navbar-isocal .nav-link {
  color: var(--teal-700) !important;
  transition: color 0.15s;
}

.navbar-isocal .nav-link:hover {
  color: var(--isoly-cta-fg) !important;
}

/* Aktive Seite auf HELLER Flaeche -> das dunkelste Teal. Hier stand vorher
 * --isoly-accent-on-dark (Lime), richtig fuer die dunkle Leiste von damals -
 * auf der Lime-Leiste waere Lime auf Lime mit 1,00:1 vollstaendig unsichtbar.
 * Der Token-Name sagt es bereits: "on-dark". */
.navbar-isocal .nav-link.active {
  color: var(--isoly-cta-fg) !important;
  font-weight: 600;
}

/* Der Benutzername ist Beiwerk, aber lesbar: teal-700 statt einer
 * Deckkraft-Abstufung. Ein durchscheinendes Teal auf Lime laege je nach Wert
 * unter der Schwelle, ohne dass man es der Zeile ansieht. */
.navbar-isocal .nav-link-user {
  color: var(--teal-700) !important;
  font-size: 0.85rem;
}

/* ── Cards ──────────────────────────────────────────────────────────────
 * Radius 14px bewusst nur auf Karten/Modals/Alerts - Buttons, Inputs und
 * Badges behalten den Bootstrap-Radius (kleinstes Layout-Risiko).
 */

/*
 * KARTEN SIND WEISS - und waren es bis 2026-08-01 nicht.
 *
 * Anwender: "Die zweite obere Kachel ist eine Kontrast-Katastrophe. Grauer
 * Hintergrund auf grauem Hintergrund mit grauen Dropdowns."
 *
 * Gemessen war es genau das, und zwar dreifach:
 *
 *   Seite        --isoly-bg                        = teal-50
 *   Karte        --bs-card-bg -> --bs-body-bg      = teal-50
 *   Auswahlfeld  --bs-body-bg                      = teal-50
 *
 *   Karte gegen Seite      1,00:1   IDENTISCH
 *   Auswahlfeld gegen Karte 1,00:1  IDENTISCH
 *
 * Die Ursache ist eine Kette, die niemand beabsichtigt hat: Zeile 174 setzt
 * --bs-body-bg auf teal-50, damit der Seitengrund stimmt. Bootstrap 5.3 leitet
 * daraus aber AUCH --bs-card-bg und den Hintergrund der Formularelemente ab.
 * Drei Ebenen bekamen damit dieselbe Farbe; getrennt wurden sie nur durch
 * einen 1px-Rand mit 1,14:1.
 *
 * Dass es nicht frueher auffiel, hat einen verraeterischen Grund: Jede
 * SPAETER gebaute Flaeche - .fb-kachel, .fb-karte, .fb-abschnitt, .fb-kopf -
 * setzt `background: var(--isoly-surface)` von Hand. Sechsmal dieselbe
 * Umgehung desselben Fehlers, statt einmal die Ursache. Das Token sagt es
 * seit jeher - der Kommentar an seiner Definition lautet schlicht "Karten".
 *
 * Der Flaechenunterschied Weiss gegen teal-50 ist mit 1,07:1 klein - das ist
 * bei "Flaeche auf Flaeche" normal und wird von Rand und Schatten getragen.
 * Entscheidend ist, dass er ueberhaupt existiert: 1,07 statt 1,00.
 *
 * REICHWEITE: Diese Regel gilt fuer ALLE drei Oberflaechen. Sie stellt den
 * dokumentierten Sollzustand her, den die Einzelfluchten oben bereits
 * vorwegnehmen - keine neue Gestaltung, sondern das Ende der Umgehungen.
 */
/* KARTENRAND: eigenes Token seit TAB-02 (2026-08-02), auf Anwender-Hinweis
 * unmittelbar nach TAB-01.
 *
 * DER BEFUND, gemessen: Eine weisse Karte hebt sich vom teal-50-Seitengrund
 * mit 1,07:1 ab - also praktisch gar nicht. Die Begrenzung MUSS deshalb der
 * Rand leisten, und der lag mit teal-150 bei 1,14:1 gegen den Grund. Die
 * Karte war faktisch unbegrenzt; der vorhandene Schatten (8 % auf 3 px) traegt
 * auf so hellem Grund nichts bei.
 *
 * Der Rand liegt ZWISCHEN weisser Karte und getoentem Grund - beide Nachbarn
 * zaehlen, der schwaechere entscheidet:
 *
 *   Kandidat            gegen Weiss   gegen Grund   massgeblich
 *   teal-150 (bisher)      1,22          1,14          1,14   zu schwach
 *   teal-300               1,65          1,54          1,54   zu schwach
 *   teal-500               4,03          3,76          3,76   ok, aber gesaettigt
 *   teal-400               5,10          4,76          4,76   gewaehlt
 *   teal-600               6,43          5,99          5,99   zu dominant
 *
 * teal-500 waere der leichteste Wert ueber der Schwelle, ist aber ein
 * gesaettigtes Gruen - als Rahmen um jede Karte laese es sich als farbige
 * Auszeichnung lesen, nicht als Kante. teal-400 ist entsaettigt und traegt im
 * Projekt bereits die Rolle "sichtbare Begrenzung" (--isoly-border-control an
 * Formularfeldern).
 *
 * WARUM EIN EIGENES TOKEN UND NICHT --isoly-border GEAENDERT: Jenes haengt an
 * --bs-border-color und traegt damit JEDE Bootstrap-Kante - Tabellenzeilen,
 * Listengruppen, Dropdowns. Auf 4,76:1 angehoben wuerde aus jeder Tabelle ein
 * Gitter. Der Kommentar bei den Bedienelementen oben hatte das schon
 * entschieden ("fuer Trennlinien waeren 5,10:1 zu laut, und die sind der
 * ueberwiegende Teil der Verwendungen") - diese Entscheidung bleibt gueltig
 * und wird hier NICHT ueberstimmt, sondern ergaenzt.
 *
 * WARUM AUCH NICHT --isoly-border-control MITBENUTZT: Ein Kartenrahmen und die
 * Kante eines Eingabefeldes sind verschiedene Aufgaben. Getrennte Token heisst:
 * "Karten zu schwer" laesst sich in einer Zeile korrigieren, ohne die
 * Formularfelder mitzuziehen.
 *
 * NICHT BETROFFEN, absichtlich: die inneren Kanten von .card-header und
 * .card-footer (sie kommen aus --bs-card-border-color und bleiben leise) sowie
 * die Akzentkanten von .stat-card (links) und der Anmeldekarte (oben), die
 * ihre Seite ohnehin ueberschreiben.
 *
 * REICHWEITE: alle drei Oberflaechen - Admin, Kunden-Portal, Dispatcher.
 */
.card {
  background: var(--isoly-surface);
  box-shadow: 0 1px 3px rgba(0,0,0,0.08);
  border: 1px solid var(--isoly-border-card);
  border-radius: var(--isoly-radius);
}

.modal-content {
  border-radius: var(--isoly-radius);
}

.stat-card {
  border-left: 4px solid var(--isoly-accent);
  transition: transform 0.15s;
}

.stat-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}

.stat-card .stat-value {
  font-size: 2rem;
  font-weight: 700;
  color: var(--isoly-accent);   /* 6,43:1 auf weisser Karte */
}

.stat-card .stat-label {
  font-size: 0.875rem;
  color: var(--isoly-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  font-weight: 600;
}

/* Zweite Zahl in einer Kachel - z. B. "7 offen" unter der Gesamtzahl. */
.stat-card .stat-sub {
  font-size: 0.8rem;
  color: var(--isoly-text-muted);
  margin-top: 0.15rem;
}

/* Hervorhebung, solange etwas offen ist. Das Token traegt 5,25:1 auf Weiss
   (dokumentiert an seiner Definition) und liegt damit ueber der
   WCAG-Schwelle 4,5:1 fuer Fliesstext. */
.stat-card .stat-sub.hat-offene {
  color: var(--isoly-warning-text);
  font-weight: 600;
}

/* Kachel als Link (Admin-Dashboard).
   Der Anker bringt KEINE eigene Optik mit - Rahmen, Schatten und der
   Hover-Effekt stecken in der Karte darin. height:100% ist noetig, damit das
   h-100 der Karte eine Bezugshoehe hat und alle Kacheln einer Reihe gleich
   hoch bleiben, auch wenn eine davon drei Zeilen traegt.
   Ohne href - die Rolle darf das Ziel nicht oeffnen - bleibt der Anker ein
   gewoehnlicher Block: keine Zeigerhand, kein Tab-Stopp. */
.stat-card-link {
  display: block;
  height: 100%;
  text-decoration: none;
  color: inherit;
}

.stat-card-link:focus-visible {
  outline: 2px solid var(--isoly-accent);
  outline-offset: 2px;
  border-radius: var(--isoly-radius);
}

/* ── Buttons ────────────────────────────────────────────────────────────
 * Primaer = CTA: Lime-Flaeche mit Teal-900-Text (9,12:1). Bootstrap-Buttons
 * lesen --bs-primary NICHT (Sass-generierte --bs-btn-*), daher explizit.
 */

.btn-primary {
  background-color: var(--isoly-cta-bg) !important;
  border-color: var(--isoly-cta-bg) !important;
  color: var(--isoly-cta-fg) !important;
  font-weight: 600;
}

.btn-primary:hover, .btn-primary:focus {
  background-color: var(--isoly-cta-bg-hover) !important;
  border-color: var(--isoly-cta-bg-hover) !important;
  color: var(--isoly-cta-fg) !important;
}

.btn-primary:active {
  background-color: var(--isoly-cta-bg-hover) !important;
  border-color: var(--teal-900) !important;
  color: var(--isoly-cta-fg) !important;
}

/* Outline-Primaer steht auf hellem Grund -> Teal-600, nicht Lime (1,41:1) */
.btn-outline-primary {
  color: var(--isoly-accent) !important;
  border-color: var(--isoly-accent) !important;
}

.btn-outline-primary:hover {
  background-color: var(--isoly-accent) !important;
  border-color: var(--isoly-accent) !important;
  color: var(--isoly-on-brand) !important;
}

/* Ausgewaehltes Segment eines btn-check-Schalters = CTA-Lime, wie in der App
 * (UI-01, 2026-08-02).
 *
 * WARUM DAS EINE EIGENE REGEL BRAUCHT: Der Block oben faengt `.btn-primary`
 * und `.btn-outline-primary` ab, NICHT aber den `:checked`-Zustand. Der liest
 * die Sass-generierten `--bs-btn-active-*` und blieb deshalb auf Bootstraps
 * Vorgabe-Blau stehen - sichtbar geworden, als der erste Zwei-Segment-Schalter
 * gebaut wurde. Genau die Falle, die der Kommentar am Anfang dieses Blocks
 * bereits benennt; sie wiederholt sich fuer jede Zustandsvariante.
 *
 * Kontrast selbst nachgerechnet (WCAG, nicht aus der Tokens-Doku uebernommen):
 * teal-900 auf lime-500 = 9,12:1 - dieselbe Paarung wie beim Modal-Kopf. */
.btn-check:checked + .btn-outline-primary,
.btn-check:active + .btn-outline-primary {
  background-color: var(--isoly-cta-bg) !important;
  border-color: var(--isoly-cta-bg) !important;
  color: var(--isoly-cta-fg) !important;
  font-weight: 600;
}

.btn-check:focus-visible + .btn-outline-primary {
  outline: 2px solid var(--isoly-accent);
  outline-offset: 2px;
}

/* Sekundaer = dunkle Teal-Flaeche mit weissem Text (12,87:1) */
.btn-secondary {
  background-color: var(--teal-900) !important;
  border-color: var(--teal-900) !important;
  color: var(--isoly-on-brand) !important;
}

.btn-secondary:hover, .btn-secondary:focus, .btn-secondary:active {
  background-color: var(--teal-800) !important;
  border-color: var(--teal-800) !important;
  color: var(--isoly-on-brand) !important;
}

.btn-outline-secondary {
  color: var(--isoly-neutral) !important;
  border-color: var(--isoly-neutral) !important;
}

.btn-outline-secondary:hover {
  background-color: var(--isoly-neutral) !important;
  color: var(--isoly-on-brand) !important;
}

.btn-success {
  background-color: var(--isoly-success) !important;
  border-color: var(--isoly-success) !important;
  color: var(--isoly-on-brand) !important;
}

.btn-success:hover, .btn-success:focus {
  background-color: var(--teal-700) !important;
  border-color: var(--teal-700) !important;
}

.btn-outline-success {
  color: var(--isoly-success-text) !important;
  border-color: var(--isoly-success) !important;
}

.btn-outline-success:hover {
  background-color: var(--isoly-success) !important;
  color: var(--isoly-on-brand) !important;
}

.btn-danger {
  background-color: var(--isoly-danger) !important;
  border-color: var(--isoly-danger) !important;
  color: var(--isoly-on-brand) !important;
}

.btn-danger:hover, .btn-danger:focus {
  background-color: var(--isoly-danger-text) !important;
  border-color: var(--isoly-danger-text) !important;
}

.btn-outline-danger {
  color: var(--isoly-danger-text) !important;
  border-color: var(--isoly-danger) !important;
}

.btn-outline-danger:hover {
  background-color: var(--isoly-danger) !important;
  color: var(--isoly-on-brand) !important;
}

/* Warnung traegt DUNKLEN Text - weiss auf #E8A13C waere 2,19:1 */
.btn-warning {
  background-color: var(--isoly-warning) !important;
  border-color: var(--isoly-warning) !important;
  color: var(--teal-900) !important;
}

/* Hover-Herleitung: Die Warnflaeche traegt Teal-900-Text. Abdunkeln SENKT
 * diesen Kontrast (5,88:1 bei -0 %, 4,22:1 bei -12 % Helligkeit - die
 * AA-Grenze liegt bei -12 %). Gewaehlt ist -6 % Helligkeit bei gleichem
 * Farbton und gleicher Saettigung: sichtbarer Hover mit Abstand zur Grenze. */
.btn-warning:hover, .btn-warning:focus {
  background-color: var(--isoly-warning-hover) !important;
  border-color: var(--isoly-warning-hover) !important;
  color: var(--teal-900) !important;
}

.btn-outline-warning {
  color: var(--isoly-warning-text) !important;
  border-color: var(--isoly-warning) !important;
}

.btn-outline-warning:hover {
  background-color: var(--isoly-warning) !important;
  color: var(--teal-900) !important;
}

.btn-info {
  background-color: var(--isoly-info) !important;
  border-color: var(--isoly-info) !important;
  color: var(--isoly-on-brand) !important;
}

.btn-info:hover, .btn-info:focus {
  background-color: var(--isoly-info-text) !important;
  border-color: var(--isoly-info-text) !important;
  color: var(--isoly-on-brand) !important;
}

/* Fokus-Ring in Markenfarbe statt Bootstrap-Blau */
.btn:focus, .btn:focus-visible {
  box-shadow: 0 0 0 0.25rem rgba(var(--teal-500-rgb), 0.4);
}

/* ── Badges ─────────────────────────────────────────────────────────────
 * Badges sind Kleinschrift (0,72-0,85rem) -> es gilt das Fliesstext-Minimum
 * 4,5:1, NICHT die 3:1-Grossschrift-Schwelle.
 */

.badge-online, .bg-success {
  background-color: var(--isoly-success) !important;
}

.badge-offline {
  background-color: var(--isoly-neutral) !important;
}

/* Warn-Badges brauchen dunklen Text (Bootstrap setzt teils weiss) */
.bg-warning {
  color: var(--teal-900) !important;
}

/* ── Tables ─────────────────────────────────────────────────────────────── */

.table td, .table th {
  vertical-align: middle;
}

.table thead th {
  background-color: var(--teal-150);
  color: var(--teal-700);          /* 7,40:1 auf Teal-150 */
  font-weight: 600;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  border-bottom: 2px solid var(--isoly-accent);
}

.table tbody tr:hover {
  background-color: rgba(var(--teal-600-rgb), 0.04);
}

/* Bootstrap-Kontextzeile (Protokoll-Aenderungen) */
.table-info {
  --bs-table-bg: var(--teal-150);
  --bs-table-color: var(--teal-900);   /* 10,58:1 */
}

/* ── Login ──────────────────────────────────────────────────────────────── */

.login-container {
  max-width: 420px;
  margin: 60px auto;
}

.login-container .card {
  border-top: 4px solid var(--isoly-accent);
}

.login-logo {
  text-align: center;
  margin-bottom: 1.5rem;
}

.login-logo img {
  height: 48px;
}

.login-title {
  color: var(--teal-700);          /* 8,40:1 auf hellem Kartengrund */
  font-weight: 700;
}

/* ── Secrets/Codes ─────────────────────────────────────────────────────── */

.secret-display {
  font-family: 'Courier New', monospace;   /* Quicksand hat keinen Mono-Schnitt */
  font-size: 1.1rem;
  background: var(--isoly-warning-tint);
  border: 2px solid var(--isoly-warning);
  padding: 1rem;
  border-radius: 0.5rem;
  word-break: break-all;
  color: var(--isoly-text);
}

.secret-display .label {
  font-family: inherit;
  font-size: 0.8rem;
  color: var(--isoly-danger-text);   /* 4,84:1 auf Warn-Tint */
  font-weight: 600;
  display: block;
  margin-bottom: 0.25rem;
}

.code-display {
  font-family: 'Courier New', monospace;
  font-size: 1.5rem;
  letter-spacing: 0.15em;
}

/* ── Alerts ─────────────────────────────────────────────────────────────
 * Bootstrap-Alerts sind Sass-generiert und folgen den --bs-*-rgb nicht.
 * Hinweis: Die Tint-FLAECHEN von success und info liegen mit dE 5,4 nah
 * beieinander (beides helle Teal-Toene). Getragen wird die Unterscheidung
 * hier von Text (dE 18,1) und Rand (dE 20,2) - beide klar getrennt.
 */

.alert {
  border-radius: var(--isoly-radius);
}

/* Rahmen-Herleitung: gleicher Farbton wie die Funktionsfarbe, Saettigung auf
 * hoechstens 55 % begrenzt, Helligkeit gesenkt bis 3:1 gegen die eigene
 * Tint-Flaeche (Rahmen sind Nicht-Text, dort gilt 3:1, nicht 4,5:1).
 * Erreichte Werte je Rahmen gegen seine Flaeche: 3,03 / 3,02 / 3,00 / 3,00. */
.alert-success {
  --bs-alert-bg: var(--isoly-success-tint);
  --bs-alert-color: var(--isoly-success-text);
  --bs-alert-border-color: #2D9C74;
}

.alert-danger {
  --bs-alert-bg: var(--isoly-danger-tint);
  --bs-alert-color: var(--isoly-danger-text);
  --bs-alert-border-color: #CE6457;
}

.alert-warning {
  --bs-alert-bg: var(--isoly-warning-tint);
  --bs-alert-color: var(--isoly-warning-text);
  --bs-alert-border-color: #B47F34;
}

.alert-info {
  --bs-alert-bg: var(--isoly-info-tint);
  --bs-alert-color: var(--isoly-info-text);
  --bs-alert-border-color: #3097A5;
}

/* ── Tabs / Pills / Formularschalter ────────────────────────────────────
 * Diese drei ziehen ihre Farbe NICHT aus der a{}-Regel bzw. den Utilities
 * und waeren sonst die letzten verbliebenen Bootstrap-Blau-Stellen.
 */

/* REITER: nachgebessert 2026-08-02 (TAB-01) auf Anwender-Hinweis
 * "sehr schlecht zu erkennen, wenig Kontrast".
 *
 * NICHT die Schrift war das Problem - die war immer in Ordnung:
 *   inaktiv teal-600 auf Seitengrund 5,99:1, aktiv teal-900 12,87:1.
 * Es fehlte alles andere. Hier waren bis dahin NUR die Textfarben gesetzt
 * (siehe den Absatz darueber: sie waren "die letzten Bootstrap-Blau-Stellen"),
 * und die grauen Bootstrap-Raender blieben stehen - auf einem teal-50-Grund,
 * fuer den sie nie gedacht waren. Gemessen (WCAG, selbst gerechnet):
 *   Reiterrand   DEE2E6 (Bootstrap) auf Seitengrund : 1,21:1  (Ziel 3)
 *   Hover-Rand   E9ECEF (Bootstrap)                 : 1,11:1
 *   AKTIV-SIGNAL weisse Flaeche vs Grund : 1,07:1   <- praktisch unsichtbar
 *
 * Der zweite Fehler war ein Konstruktionsfehler: Der aktive Reiter markierte
 * sich ueber `border-bottom-color: weiss` - der Trick, bei dem er optisch in
 * die weisse Inhaltsflaeche darunter uebergeht. Auf diesen drei Seiten steht
 * darunter aber eine eigene Karte mit Abstand. Der Trick hatte nichts zum
 * Verschmelzen. Deshalb jetzt eine UNTERSTREICHUNG: die haengt nicht davon ab,
 * was unter dem Reiter liegt.
 *
 * WAS NICHT GEHT, hergeleitet statt behauptet: Die Leistenlinie soll >= 3:1
 * auf dem Grund liegen (also dunkel sein), die Unterstreichung >= 3:1 GEGEN
 * die Linie (also viel dunkler). Bei Linie = teal-400 (4,76:1) braeuchte die
 * Unterstreichung eine Leuchtdichte <= 0,0186; teal-900 hat 0,0316 und kommt
 * damit auf 2,52:1. Dunkler gibt der Token-Satz nichts her - Schwarz erreichte
 * 4,11:1, waere aber markenfremd.
 *
 * BEWUSST SO ENTSCHIEDEN: Die sichtbare Leiste ist mehr wert als die formale
 * Zahl. Das Aktiv-Signal traegt drei unabhaengige Merkmale:
 *   1. Unterstreichung teal-900 gegen den Grund : 12,01:1  (Ziel 3, erfuellt)
 *   2. Beschriftung aktiv gegen inaktiv         :  2,00:1
 *   3. Schriftschnitt 600 statt 400             : farbunabhaengig
 * Der Strich ist zudem dreimal so dick wie die Linie. Die Alternative waere
 * eine Leistenlinie mit 1,54:1 gewesen - formal besser, sichtbar schlechter.
 *
 * Gilt fuer alle drei Seiten mit Reitern: machines, benutzer, live-status.
 *
 * Bootstrap-Variablen statt Eigenschaften - dieselbe Technik wie bei
 * .nav-pills darunter; so greifen auch die abgeleiteten Regeln.
 */
.nav-tabs {
  --bs-nav-tabs-border-color: var(--isoly-border-control);   /* teal-400, 4,76:1 */
  --bs-nav-tabs-link-hover-border-color: transparent;
  --bs-nav-tabs-link-active-color: var(--teal-900);
  --bs-nav-tabs-link-active-bg: transparent;
  --bs-nav-tabs-link-active-border-color: transparent;
}

.nav-tabs .nav-link {
  color: var(--isoly-accent);
  border-color: transparent;
}

/* Hover meldet sich ueber die SCHRIFT, nicht ueber die Flaeche: Die Toenung
 * liegt bei 1,14:1 und traegt allein nichts. Der Farbwechsel der Beschriftung
 * ist unabhaengig davon sichtbar (teal-900 auf der Toenung: 10,58:1). */
.nav-tabs .nav-link:hover,
.nav-tabs .nav-link:focus-visible {
  color: var(--teal-900);
  background-color: var(--teal-150);
}

/* box-shadow statt dickerem Rand: veraendert die Geometrie NICHT, die Reiter
 * springen beim Wechsel also nicht. Der Schatten liegt ueber der Leistenlinie,
 * weil Bootstrap dem Reiter margin-bottom: -1px gibt. */
.nav-tabs .nav-link.active {
  color: var(--teal-900);
  font-weight: 600;
  box-shadow: inset 0 -3px 0 var(--teal-900);
}

.nav-pills {
  --bs-nav-pills-link-active-bg: var(--isoly-accent);
}

/* ── Checkboxen, Radios, Schalter: der UNGEPRUEFTE Zustand ─────────────
 *
 * WARUM DAS NOETIG WAR: --bs-body-bg steht auf teal-50 (Zeile 185), und
 * Bootstrap 5.3 leitet daraus --bs-form-check-bg ab. Eine ungeprueffte
 * Checkbox hatte damit EXAKT die Farbe der Flaeche, auf der sie sitzt -
 * erkennbar war nur ihr Rand, und der ist Bootstraps Vorgabe
 * rgba(0,0,0,.25): auf teal-50 verrechnet ein mittleres Grau, also 1,83:1.
 * WCAG 1.4.11 verlangt fuer Bedienelemente 3:1. Im Dialog "Portal-Zugang
 * bearbeiten" waren die Haken deshalb praktisch nicht zu sehen
 * (Anwender-Meldung 2026-08-03).
 *
 * Fuer .form-control/.form-select war genau das laengst behoben (Zeile 305) -
 * .form-check-input stand in jener Regel nur nicht mit dabei. Sie bekommt
 * deshalb dieselben zwei Token und sieht damit aus wie ein Eingabefeld:
 * weisses Feld, teal-400 Rand. Selbst nachgerechnet: 4,76:1 auf der
 * Dialogflaeche teal-50, 5,10:1 auf weisser Karte.
 *
 * :checked und :focus darunter gewinnen ueber ihre hoehere Spezifitaet.
 */
.form-check-input {
  background-color: var(--isoly-surface);
  border-color: var(--isoly-border-control);
}

/* Der Knopf eines Schalters ist ein eingebettetes SVG, und dort ist der
 * Farbwert ZWANGSLAEUFIG ein Literal: Eine Daten-URI ist ein eigenes
 * Dokument und sieht die Custom Properties der einbindenden Seite nicht.
 * Das ist dieselbe technische Grenze, aus der schon die Marken-SVGs unter
 * public/shared/ ausgenommen sind (.claude/rules/frontend-shared-code.md,
 * Abschnitt "Dokumentierte Ausnahmen") - dort ist dieser Fall nachgetragen.
 *
 * Der Wert in der URI IST teal-400 (Zeile 72), nur als %23-Schreibweise.
 * Wer das Token aendert, muss diese URI mitziehen - sonst laeuft der Knopf
 * gegen seinen eigenen Rand. Der Verifizierungs-Grep der Farb-Token-Regel
 * findet ihn NICHT: Er sucht nach "#", in einer Daten-URI steht "%23".
 *
 * :not(:checked) ist tragend, nicht kosmetisch: Ohne diese Einschraenkung
 * uebernaehme die Regel auch den GEPRUEFTEN Schalter. Dessen Knopf ist bei
 * Bootstrap weiss auf der Akzentbahn (6,43:1) - ein teal-400 Knopf auf
 * teal-600 waere kaum noch zu erkennen. So greift die Regel nur im
 * ungeprueften Zustand, und zwar auch im Fokus: Bootstrap faerbt den Knopf
 * dort in seinem eigenen Blau - 2,06:1 auf weisser Bahn und markenfremd
 * dazu. Die Fokus-Anzeige kommt ohnehin aus dem Ring weiter unten.
 *
 * Gerechnet: teal-400 auf weisser Bahn 5,10:1 (Ziel 3:1).
 */
.form-switch .form-check-input:not(:checked) {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%235B736C'/%3e%3c/svg%3e");
}

.form-check-input:checked {
  background-color: var(--isoly-accent);
  border-color: var(--isoly-accent);
}

.form-check-input:focus {
  border-color: var(--teal-500);
  box-shadow: 0 0 0 0.25rem rgba(var(--teal-500-rgb), 0.25);
}

/* ── Toast / Overlay ───────────────────────────────────────────────────── */

.toast-container {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 9999;
}

#loading-overlay {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(255,255,255,0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}

#loading-overlay .spinner-border {
  color: var(--isoly-accent) !important;
}

/* ── Links ──────────────────────────────────────────────────────────────
 * Teal-600 = Akzent auf hellem Grund (6,43:1 auf Weiss, 5,99:1 auf Teal-50).
 */

a {
  color: var(--isoly-accent);
}

a:hover {
  color: var(--teal-700);
}

/* ── Forms ──────────────────────────────────────────────────────────────── */

.form-control:focus, .form-select:focus {
  border-color: var(--teal-500);
  box-shadow: 0 0 0 0.2rem rgba(var(--teal-500-rgb), 0.25);
}

/* ══════════════════════════════════════════════════════════════════════════
   Lightbox — Bild gross anzeigen, ohne es herunterzuladen
   Markup erzeugt public/shared/lightbox.js selbst.

   Die Farbwerte hier sind DOKUMENTIERTE AUSNAHMEN der Token-Regel: Ein
   Verdunkelungs-Overlay und ein weisser Schliessen-Knopf darauf sind neutrale
   Flaechen ohne Markenbezug (.claude/rules/frontend-shared-code.md, Abschnitt
   "Dokumentierte Ausnahmen"). Ein Marken-Teal als Overlay wuerde das Bild
   einfaerben - genau das, was man beim Betrachten nicht will.
   ══════════════════════════════════════════════════════════════════════════ */

.lb-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .88);
  z-index: 2000;            /* ueber Bootstrap-Modals (1055) */
  padding: 24px;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.lb-overlay.lb-offen { display: flex; }

.lb-rahmen { position: relative; max-width: 94vw; max-height: 86vh; }

.lb-rahmen img {
  max-width: 94vw;
  max-height: 86vh;
  object-fit: contain;
  border-radius: 6px;
  display: block;
}

.lb-unterschrift {
  margin-top: 12px;
  text-align: center;
  color: rgba(255, 255, 255, .75);
  font-size: .9rem;
  max-width: 94vw;
  word-break: break-word;
}

.lb-schliessen {
  position: absolute;
  top: -14px;
  right: -14px;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 50%;
  background: #fff;
  color: #222;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .5);
}
.lb-schliessen:hover { background: #eee; }

/* Anklickbare Vorschau: zeigt, dass mehr dahintersteckt */
.lb-vorschau { cursor: zoom-in; transition: opacity .12s ease; }
.lb-vorschau:hover { opacity: .85; }

/* ══════════════════════════════════════════════════════════════════════════
   Feedback-Detailansicht — Gliederung
   Die Ansicht bringt Kopfdaten, Beschreibung, Verlauf, Anhaenge und Kontext
   untereinander. Ohne sichtbare Trennung liest sich das als eine lange Wand.
   ══════════════════════════════════════════════════════════════════════════ */

/* FIGUR UND GRUND statt staerkerer Rahmen.
 *
 * Der Anwender: "Die Sektionen haben kaum einen Kontrast." Gemessen stimmt das
 * genau - der Rahmen teal-150 erreicht auf weissem Grund 1,22:1, die Schwelle
 * fuer nicht-textliche Elemente liegt bei 3,0:1.
 *
 * Sein Vorschlag war eine Lime-Umrandung. Nachgerechnet loest die das NICHT:
 *   teal-150 (bisher)  1,22:1      lime-500  1,41:1      lime-600  1,73:1
 *   teal-300           1,65:1      teal-400  5,10:1      teal-600  6,43:1
 * Lime waere 0,19 besser als der Istzustand - sichtbar waere es damit nicht.
 *
 * Die Ursache ist nicht die Rahmenfarbe, sondern dass ALLES WEISS AUF WEISS
 * steht: Dialoggrund weiss, Abschnitte weiss, Rahmen fast weiss. Deshalb
 * bekommt der Dialog einen getoenten Grund und die Abschnitte werden zu
 * weissen Karten darauf - dasselbe Muster wie .card auf --isoly-bg im
 * uebrigen Panel.
 *
 * DIE UNBEQUEME ZAHL, die hier zuerst fehlte: Weiss gegen teal-50 sind
 * 1,07:1 - NIEDRIGER als der verworfene Rahmen mit 1,22:1. Ueber das
 * Farbverhaeltnis allein waere die neue Loesung also schlechter, und sie
 * auszulassen waere selektiv gewesen.
 *
 * Was sie trotzdem traegt, ist der SCHLAGSCHATTEN. Tiefe ist ein anderer
 * Wahrnehmungskanal als Helligkeitsunterschied: Eine Kante mit Schatten liest
 * das Auge als Ebene, unabhaengig davon, wie nah die beiden Flaechen
 * beieinanderliegen. Genau deshalb funktioniert das Kartenmuster in hellen
 * Paletten ueberhaupt - und deshalb waere ein staerkerer Rahmen (teal-400,
 * 5,10:1) zwar messbar besser, aber optisch ein Kasten statt einer Karte.
 *
 * Der Lime-Akzent kommt trotzdem - als KANTE, wo er auffaellt, statt als
 * Umrandung, wo er verschwaende. 4 Pixel gesaettigtes Lime an der linken Seite
 * sieht man; ein 1 Pixel breiter Rahmen ringsum nicht.
 */
#ticket-modal .modal-body { background: var(--isoly-bg); }

.fb-abschnitt {
  margin-top: 1.25rem;
  background: var(--isoly-surface);
  border-radius: var(--isoly-radius);
  border-left: 4px solid var(--isoly-cta-bg);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
  padding: 1rem 1.15rem;
}
.fb-abschnitt:first-child { margin-top: 0; }

/* Ueberschrift OHNE eigenen Balken - der Abschnitt traegt die Lime-Kante.
 *
 * Hier stand bis zur Umstellung ein Verlaufsbalken (Lime nach Teal) als
 * ::before-Pseudoelement, samt Herleitung. Er ist entfallen, weil zwei
 * Lime-Balken uebereinander - Kante am Abschnitt, Balken an der Ueberschrift -
 * miteinander konkurrierten, statt zu fuehren. Die Messwerte von damals gelten
 * unveraendert und stehen jetzt bei .fb-abschnitt, wo die Kante sitzt.
 *
 * Die Ueberschrift arbeitet stattdessen mit Groesse und einer Trennlinie
 * darunter - beides Mittel, die nicht von Farbkontrast abhaengen. */
.fb-abschnitt > h5 {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--isoly-text);
  margin: 0 0 .85rem;
  padding-bottom: .5rem;
  border-bottom: 1px solid var(--isoly-border);
}

/* Kopfdaten - eine weisse Karte wie die Abschnitte, nur ohne Lime-Kante.
 *
 * BEINAHE-FEHLER, vom regel-check gefunden: Hier stand kurzzeitig eine
 * Toenung mit der Begruendung "auf der weissen Karte traegt die Toenung". Das
 * war schlicht falsch - .fb-kopf liegt GAR NICHT in einem .fb-abschnitt. Die
 * Kopfdaten werden in feedback-tickets.js direkt in #detail-body eingesetzt,
 * nicht ueber abschnitt(). Und #detail-body traegt seit dieser Umstellung
 * selbst --isoly-bg. Toenung auf gleicher Toenung, ohne Rahmen, ohne Schatten:
 * der Block waere vollstaendig verschwunden - schlechter als der Zustand, den
 * die Umstellung beheben sollte.
 *
 * Die Lehre: Eine Aussage ueber die UMGEBUNG eines Elements gehoert am DOM
 * geprueft, nicht angenommen. Im CSS sieht man dem Selektor nicht an, wo er
 * landet.
 *
 * Keine Lime-Kante: Die Kopfdaten sind Nachschlagewerk, kein Abschnitt. Der
 * Unterschied soll sichtbar bleiben. */
.fb-kopf {
  background: var(--isoly-surface);
  border-radius: var(--isoly-radius);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
  padding: .8rem 1rem;
}
.fb-kopf dt { font-weight: 600; color: var(--isoly-text-muted); }
.fb-kopf dd { color: var(--isoly-text); }
.fb-kopf dl { margin-bottom: 0; }

/* Beschreibung: der Text des Melders, abgesetzt wie ein Zitat.
 * Die Lime-Kante wiederholt das Motiv der Abschnitte eine Ebene tiefer -
 * so ist erkennbar, dass hier jemand SPRICHT, nicht dass etwas aufgelistet
 * wird. */
.fb-beschreibung {
  background: var(--isoly-bg);
  border-left: 3px solid var(--isoly-cta-bg);
  border-radius: 0 var(--isoly-radius) var(--isoly-radius) 0;
  padding: .8rem 1rem;
}

/* ── Nachrichtenverlauf ──────────────────────────────────────────────── */

/* Kein eigener Rahmen mehr - der Abschnitt IST die Karte. */
.fb-strang { padding: 0; }

.fb-blase { max-width: 82%; border-radius: var(--isoly-radius); padding: .7rem .9rem; }

/* Der Anwender links auf heller Markenflaeche, der Bearbeiter rechts auf
   Weiss mit Rand: Die Richtung ist an Seite UND Farbe erkennbar - wer nur
   eines von beidem wahrnimmt, erkennt sie trotzdem. */
/* BEIDE Blasen sind gefuellt, seit die Abschnitte weisse Karten sind: Eine
 * weisse Blase auf weisser Karte waere nur noch am Rahmen erkennbar gewesen -
 * und der erreicht 1,22:1. Als Flaechen lesen sich beide als Form, unabhaengig
 * vom Kontrastverhaeltnis, und die Richtung bleibt an Seite UND Farbe
 * erkennbar. */
.fb-blase-melder     { background: var(--teal-150); color: var(--isoly-text); }
.fb-blase-bearbeiter { background: var(--isoly-bg); border: 1px solid var(--isoly-border); }

.fb-blase-kopf {
  display: flex;
  align-items: center;
  gap: .4rem;
  font-size: .8rem;
  color: var(--isoly-text-muted);
  margin-bottom: .35rem;
}

/* Statuswechsel: eine Linie durch den Strang, damit er als Einschnitt
   gelesen wird und nicht als weitere Nachricht */
.fb-status-zeile {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin: .9rem 0;
  color: var(--isoly-text-muted);
  font-size: .85rem;
}
.fb-status-zeile::before,
.fb-status-zeile::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--isoly-border);
}

/* Eingabebereich abgesetzt vom Verlauf */
.fb-composer { border-top: 2px solid var(--isoly-border); margin-top: 1rem; padding-top: 1rem; }

/* ── Initialen-Avatare ───────────────────────────────────────────────── */

/*
 * Ein Kreis mit bis zu zwei Initialen als visueller Anker beim Ueberfliegen.
 *
 * DIE FARBE IST SCHMUCK, DIE INITIALEN SIND DIE AUSSAGE. Bei drei Farben
 * teilen sich Personen zwangslaeufig eine - deshalb steht der Name immer
 * daneben, und der Avatar traegt `aria-hidden`, damit eine Vorlesehilfe ihn
 * nicht zusaetzlich buchstabiert.
 *
 * DIE PALETTE IST GEMESSEN, NICHT GEWAEHLT (2026-08-01):
 *
 *   - Nur die Marken-Rampe kommt in Frage. Die Funktionsfarben sind belegt:
 *     Ein roter Avatar saehe aus wie ein Fehler, ein gruener wie "erledigt".
 *   - teal-500 faellt raus: weisser Text erreicht darauf keine 4,5:1.
 *   - teal-600 faellt raus, obwohl der Kontrast stimmt - es ist mit dE 0,0
 *     EXAKT die Farbe der BEHOBEN-Plakette.
 *   - teal-800 faellt raus: nur dE 3,9 von teal-900 entfernt, in einer
 *     Dreier-Palette also verschenkt.
 *
 * Es bleiben (weisser Text / Abstand untereinander / Abstand zur naechsten
 * Statusfarbe):
 *
 *   teal-900  12,87:1    dE >= 8,8      dE 18,9 zu BEHOBEN
 *   teal-700   9,00:1                   dE 10,7 zu BEHOBEN
 *   teal-400   5,10:1                   dE 12,5 zu ABGELEHNT
 *
 * Verwechslung mit einer Statusplakette ist zusaetzlich durch die FORM
 * ausgeschlossen: Kreis mit zwei Buchstaben gegen Pille mit einem Wort.
 */
.fb-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;               /* darf im Flex-Kontext nicht schrumpfen */
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .02em;
  line-height: 1;
  color: var(--isoly-on-brand);
  user-select: none;            /* beim Kopieren einer Zeile nicht mitnehmen */
}
.fb-avatar-klein { width: 1.5rem; height: 1.5rem; font-size: .65rem; }

.fb-avatar-0 { background: var(--teal-900); }
.fb-avatar-1 { background: var(--teal-700); }
.fb-avatar-2 { background: var(--teal-400); }

/* ── Kennzahlen der Feedback-Uebersicht ──────────────────────────────── */

/*
 * Eine Reihe kleiner Kacheln ueber der Liste.
 *
 * WARUM `flex-wrap` UND KEIN FESTES RASTER: Die Zahl der Kacheln ist nicht
 * konstant - "ohne jede Antwort" erscheint nur, wenn es solche Meldungen gibt
 * (die Kachel ist die ehrliche Gegenzahl zum Mittelwert und waere bei 0 nur
 * Rauschen). Ein Raster mit fester Spaltenzahl liesse an dieser Stelle eine
 * Luecke.
 *
 * Die Kacheln sind ABSICHTLICH nicht anklickbar. Ein Filtersprung waere
 * naheliegend, aber die Kennzahlen gelten fuer den GESAMTEN Bestand, die
 * Filter nur fuer die Liste - ein Klick auf "12 neu" fuehrte zu einer Liste,
 * deren Kopfzahl dann etwas anderes zaehlt als die Kachel darueber. Das waere
 * eine eigene Entscheidung, kein Nebeneffekt.
 */
.fb-statistik {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
}

.fb-kachel {
  flex: 1 1 8.5rem;          /* mitwachsend, aber nie schmaler als lesbar */
  min-width: 8.5rem;
  background: var(--isoly-surface);
  border-radius: var(--isoly-radius);
  border-left: 4px solid var(--isoly-border);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
  padding: .7rem .9rem;
}
/* Die Reaktionszeit-Kacheln tragen Text statt einer Zahl und brauchen mehr
   Platz - sonst bricht "2 Tage 3 h" mitten im Wert um. */
.fb-kachel-weit { flex: 2 1 14rem; min-width: 14rem; }

.fb-kachel-wert {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--isoly-text);
}
.fb-kachel-titel {
  font-size: .8rem;
  font-weight: 600;
  color: var(--isoly-text);
  margin-top: .1rem;
}
.fb-kachel-unten {
  font-size: .75rem;
  color: var(--isoly-text-muted);
  margin-top: .15rem;
}

/* Zwei Kanten heben etwas hervor - dieselbe Sprache wie bei den Karten:
   Lime fuer "frisch da", Rot fuer "jemand wartet". Die Kante traegt die
   Aussage NICHT allein; die Beschriftung sagt dasselbe in Worten. */
.fb-kachel-neu     { border-left-color: var(--lime-500); }
.fb-kachel-warnung { border-left-color: var(--isoly-danger); }

/* ── Chat-Uebersicht der Meldungen ───────────────────────────────────── */

/*
 * Die Uebersicht ist eine Folge von Karten statt einer Tabelle - der Text der
 * Meldung ist direkt lesbar, der Verlauf klappt auf.
 *
 * DIE KARTE MUSS ALLES TRAGEN, was die acht Tabellenspalten zeigten (Nummer,
 * Typ, Titel, Melder, Instanz, Zeit, Anhaenge, Status). Nur deshalb ist es
 * vertretbar, die Tabelle ersatzlos aufzugeben statt einen zweiten Renderpfad
 * zu pflegen.
 *
 * Der Aufbau ist deshalb eine LESEREIHENFOLGE, keine Spaltenordnung:
 *   Kopf    - Kennungen und Zustand (Nummer, Typ, Status, Zeit)
 *   Titel   - worum es geht
 *   Quelle  - wer, wo, wie viele Anhaenge
 *   Text    - was zuletzt gesagt wurde
 *   Fuss    - aufklappen / bearbeiten
 */
.fb-liste { display: flex; flex-direction: column; gap: .85rem; }

.fb-karte {
  background: var(--isoly-surface);
  border-radius: var(--isoly-radius);
  border-left: 4px solid var(--isoly-border);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .10);
  padding: .9rem 1.1rem;
  cursor: pointer;
}
.fb-karte:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, .14); }

/* Unbeantwortet: die Kante wechselt auf Rot - dasselbe Signal wie die
 * Plakette, aber am Rand der ganzen Karte und damit beim Ueberfliegen einer
 * langen Liste sichtbar, ohne jede Zeile zu lesen. Die Plakette bleibt
 * trotzdem: Farbe allein darf nie der einzige Traeger einer Aussage sein. */
.fb-karte-offen { border-left-color: var(--isoly-danger); }

.fb-karte-kopf {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .4rem;
  margin-bottom: .45rem;
}
.fb-nr { color: var(--isoly-text-muted); font-size: .82rem; }
/* Die Zeit rutscht ans Ende der Zeile - sie ist Beiwerk, kein Kennzeichen. */
.fb-karte-zeit { margin-left: auto; color: var(--isoly-text-muted); font-size: .8rem; }

.fb-karte-titel {
  font-size: 1.02rem;
  font-weight: 700;
  color: var(--isoly-text);
  margin: 0 0 .2rem;
}
/* Flex, damit der Avatar neben dem Text steht und nicht darueber. Der Text
   darf umbrechen, der Kreis nicht schrumpfen - deshalb `flex: 0 0 auto` am
   Avatar und kein `white-space` hier. */
.fb-karte-quelle {
  display: flex;
  align-items: center;
  gap: .55rem;
  font-size: .82rem;
  color: var(--isoly-text-muted);
  margin-bottom: .5rem;
}

/* Wer den zitierten Beitrag gesagt hat. Steht bewusst UEBER dem Zitat und
   nicht in der Quellenzeile: Dort steht der Melder der Meldung, hier der
   Sprecher dieses einen Beitrags - nach der ersten Antwort sind das zwei
   verschiedene Personen. */
.fb-karte-sprecher {
  font-size: .8rem;
  font-weight: 600;
  color: var(--isoly-text);
  margin-bottom: .15rem;
}

/* Die Vorschau ist ABSICHTLICH nicht auf eine Zeilenzahl beschnitten: Gekuerzt
 * wird bereits in der Datenbank (VORSCHAU_ZEICHEN), und zweimal zu kuerzen
 * hiesse, dass die Auslassungspunkte auf einmal an einer anderen Stelle stehen
 * als der tatsaechliche Schnitt. Ein Rand nach links setzt sie als Zitat ab. */
.fb-karte-text {
  border-left: 3px solid var(--isoly-border);
  padding-left: .7rem;
  color: var(--isoly-text);
}
.fb-karte-text > :last-child { margin-bottom: 0; }

.fb-karte-fuss {
  display: flex;
  gap: 1rem;
  align-items: center;
  margin-top: .6rem;
}

/*
 * Beide Aktionen sehen aus wie Schaltflaechen, mit klarer Rangfolge:
 * "Verlauf anzeigen" als Umriss, "Bearbeiten" gefuellt.
 *
 * Hier stand bis AVATAR-02 reiner Text - "anzeigen" in der Akzentfarbe,
 * "Bearbeiten" sogar in --isoly-text-muted. Der Anwender hat sie deshalb nicht
 * als Aktionen erkannt. Ein Bedienelement, das aussieht wie eine Beschriftung,
 * ist keins.
 *
 * ── WARUM teal-600 UND NICHT DAS LIME-CTA-PAAR ────────────────────────────
 *
 * Gemessen wurden drei Varianten (2026-08-01):
 *
 *   gefuellt teal-600   Text 6,43:1, Flaeche 6,43:1 gegen die weisse Karte
 *   Lime-CTA            Text 9,12:1 - der beste Kontrast
 *   getoent teal-150    Text 10,58:1, ABER Flaeche nur 1,22:1
 *
 * Die getoente Variante faellt trotz des besten Textkontrasts heraus: Ihre
 * Flaeche hebt sich mit 1,22:1 nicht von der weissen Karte ab. Der Text waere
 * lesbar, die Schaltflaeche als solche nicht erkennbar - also genau das
 * verfehlt, worum es hier geht.
 *
 * Zwischen den beiden verbleibenden entscheidet die KONSISTENZ: Der
 * "Senden"-Knopf derselben Oberflaeche ist ein `btn-primary`, und
 * --bs-primary zeigt in diesem Projekt auf --teal-600. Zwei verschiedene
 * Primaerfarben fuer Knoepfe auf einer Seite waeren willkuerlich.
 *
 * ── DIE KOLLISION, DIE IN KAUF GENOMMEN WIRD ──────────────────────────────
 *
 * teal-600 ist mit dE 0,0 dieselbe Farbe wie die BEHOBEN-Plakette; das
 * Lime-Paar waere es fuer NEU. Innerhalb dieser Palette ist keine Variante
 * kollisionsfrei - jede Farbe traegt bereits Bedeutung.
 *
 * Warum das hier vertretbar ist und beim AVATAR nicht war: Eine Kollision
 * schadet, wenn die FARBE DAS EINZIGE SIGNAL ist. Der Avatar ist ein
 * unbeschrifteter Kreis - dort waere sie es gewesen. Diese Knoepfe tragen ein
 * Wort, stehen immer in der Fusszeile und haben eine eigene Form. Niemand
 * verwechselt einen Knopf "Bearbeiten" unten mit einer Plakette "BEHOBEN"
 * oben, auch wenn beide gruen sind.
 */
.fb-aufklappen,
.fb-bearbeiten {
  font-size: .85rem;
  font-weight: 600;
  line-height: 1.2;
  padding: .35em .8em;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
}

/* Umriss: die haeufige, unverbindliche Aktion. Rand und Text 6,43:1. */
.fb-aufklappen {
  background: none;
  border: 1px solid var(--isoly-accent);
  color: var(--isoly-accent);
}
/* :focus-visible mit :hover gekoppelt - so haelt es der uebrige Bestand
   (.btn-primary:hover, .btn-primary:focus u. a.). Ohne das bekaeme der
   Tastaturweg zwar den Standard-Fokusring, aber nicht dieselbe
   Farbrueckmeldung wie die Maus. Ein regel-check-HINWEIS. */
.fb-aufklappen:hover,
.fb-aufklappen:focus-visible {
  background: var(--isoly-accent);
  color: var(--isoly-on-brand);
}

/* Gefuellt: die Aktion, die den Ort wechselt. Hover teal-700 = 9,00:1. */
.fb-bearbeiten {
  margin-left: auto;
  border: 1px solid var(--isoly-accent);
  background: var(--isoly-accent);
  color: var(--isoly-on-brand);
}
.fb-bearbeiten:hover,
.fb-bearbeiten:focus-visible { background: var(--teal-700); border-color: var(--teal-700); }

.fb-pfeil { display: inline-block; font-size: .7rem; }

.fb-karte-strang {
  margin-top: .8rem;
  padding-top: .8rem;
  border-top: 1px solid var(--isoly-border);
}

/* ── Status- und Typ-Plaketten ───────────────────────────────────────── */

/*
 * ZWEI ACHSEN, ZWEI FORMEN - und das ist der eigentliche Entwurf, nicht die
 * Farbwahl.
 *
 * Eine Meldung traegt gleichzeitig einen BEARBEITUNGSSTATUS (wo sie im
 * Arbeitsablauf steht) und einen TYP (was fuer eine Meldung sie ist). Beides
 * gefuellt einzufaerben ergibt neun Farbflaechen nebeneinander und ist
 * schlechter lesbar als der farblose Zustand davor, nicht besser. Deshalb:
 *
 *   Status = gefuellte PILLE   (rund, gesaettigt)
 *   Typ    = getoente TAFEL    (eckig, blass, mit Marker links)
 *
 * An der Form sieht man ohne Lesen, welche Achse man vor sich hat. Die Farbe
 * unterscheidet dann INNERHALB der Achse.
 *
 * ── Warum der Typ nicht ebenfalls gesaettigt ist ──
 *
 * Nachgemessen (CIEDE2000, Werkzeug zuvor gegen die Testdaten von
 * Sharma/Wu/Dalal 2005 geprueft): Die blassen Toenungen unterscheiden sich
 * UNTEREINANDER nicht ausreichend - WUNSCH gegen FRAGE liegt bei dE 5,4, weit
 * unter den geforderten 15. Der Signaltraeger ist deshalb nicht die Flaeche,
 * sondern der gesaettigte TEXT: dort ist das schwaechste Paar dE 18,1.
 *
 * Genau darum uebernimmt der Marker links die Textfarbe (currentColor) und
 * nicht die gesaettigte Familienfarbe. Der zuerst geplante Weg - Marker in
 * --isoly-warning auf --isoly-warning-tint - erreicht 1,89:1 und waere bei
 * FACHAUSSAGE praktisch unsichtbar gewesen. Ueber currentColor gilt fuer den
 * Marker automatisch derselbe Wert wie fuer den Text, also >= 4,5:1 in allen
 * vier Faellen.
 *
 * ── Die gemessenen Werte ──
 *
 * Alle Farben stammen aus dem Token-Block oben; es entsteht KEIN neuer
 * Farbwert. Text auf Flaeche, Soll >= 4,5:1 (Plaketten sind Kleinschrift - die
 * 3:1-Schwelle fuer Grossschrift gilt hier NICHT):
 *
 *   NEU          lime-500  + teal-900       9,12:1
 *   ANGENOMMEN   info      + on-brand       4,51:1
 *   IN_ARBEIT    warning   + teal-900       5,88:1
 *   BEHOBEN      success   + on-brand       6,43:1
 *   ABGELEHNT    neutral   + on-brand       4,50:1
 *   unbeantwortet danger   + on-brand       4,51:1
 *   FEHLER/WUNSCH/FRAGE/FACHAUSSAGE: die -text/-tint-Paare, 4,51-4,54:1
 *
 * Unterscheidbarkeit innerhalb der Status-Gruppe: schwaechstes Paar
 * ANGENOMMEN/ABGELEHNT dE 16,3. Innerhalb der Typ-Gruppe (am Text gemessen):
 * WUNSCH/FRAGE dE 18,1.
 *
 * ── Zwei behobene Befunde des Ist-Zustands ──
 *
 * 1. NEU und BEHOBEN waren dieselbe Farbe, dE 0,0. Beide liefen ueber
 *    Bootstrap-Klassen, und der Token-Block bildet --bs-primary UND
 *    --bs-success auf --teal-600 ab. Die beiden Enden des Arbeitsablaufs sahen
 *    identisch aus. Das faellt niemandem als Fehler auf - man haelt es fuer
 *    Absicht.
 * 2. ANGENOMMEN trug "bg-info text-dark" und erreichte 2,85:1.
 *    --isoly-info ist fuer WEISSEN Text gerechnet (4,51:1).
 *
 * Deshalb eigene Klassen statt Bootstrap: Wer eine Bootstrap-Klasse waehlt,
 * waehlt eine Farbe, die er nicht sieht - sie steht drei Indirektionen
 * entfernt im Token-Block.
 *
 * 3. Das Kennzeichen "unbeantwortet" trug dasselbe Amber wie IN_ARBEIT
 *    (dE 0,0) - und beide stehen unmittelbar nebeneinander. Es ist eine dritte
 *    Achse (ein Merker, kein Zustand) und bekommt Rot: gegen alle fuenf Status
 *    ist das schwaechste Paar dE 31,9.
 */
.fb-badge {
  display: inline-block;
  padding: .28em .62em;
  font-size: .78rem;
  font-weight: 600;
  line-height: 1.25;
  white-space: nowrap;
  border-radius: 999px;          /* Pille - die Status-Form */
}

/* Status: gefuellt.
 *
 * NEU nimmt --lime-500/--teal-900 direkt und NICHT das CTA-Paar, obwohl das
 * heute dieselben Werte traegt: Das CTA-Paar bedeutet "hier klicken". Wer es
 * eines Tages verschiebt, verschoebe sonst stillschweigend einen Status mit. */
.fb-st-neu        { background: var(--lime-500);      color: var(--teal-900); }
.fb-st-angenommen { background: var(--isoly-info);    color: var(--isoly-on-brand); }
.fb-st-in-arbeit  { background: var(--isoly-warning); color: var(--teal-900); }
.fb-st-behoben    { background: var(--isoly-success); color: var(--isoly-on-brand); }
.fb-st-abgelehnt  { background: var(--isoly-neutral); color: var(--isoly-on-brand); }

/* Merker: der Anwender wartet auf Antwort. */
.fb-offen { background: var(--isoly-danger); color: var(--isoly-on-brand); }

/* Typ: getoente Tafel mit Marker. Eckig statt rund - der Formunterschied. */
.fb-typ {
  border-radius: 4px;
  border-left: 3px solid currentColor;
  padding-left: .5em;
}
.fb-ty-fehler      { background: var(--isoly-danger-tint);  color: var(--isoly-danger-text); }
.fb-ty-wunsch      { background: var(--isoly-success-tint); color: var(--isoly-success-text); }
.fb-ty-frage       { background: var(--isoly-info-tint);    color: var(--isoly-info-text); }
.fb-ty-fachaussage { background: var(--isoly-warning-tint); color: var(--isoly-warning-text); }

/* Unbekannter Wert - bewusst als Stoerung erkennbar, nicht als gueltiger Wert.
 *
 * Die Vorgaenger-Fassung fiel auf dieselbe Klasse zurueck wie ABGELEHNT. Ein
 * fehlerhafter Wert saehe damit aus wie eine regulaer abgelehnte Meldung, und
 * niemand kaeme auf die Idee nachzusehen. Der gestrichelte Rahmen sagt
 * "hier stimmt etwas nicht" - genau die Sorte stiller Verwechslung, die den
 * Job-Status schon einmal getroffen hat (sql/024). */
.fb-unbekannt {
  background: var(--isoly-surface);
  color: var(--isoly-text-muted);
  border: 1px dashed var(--isoly-border);
}

/* ── Anhaenge ────────────────────────────────────────────────────────── */

/* Kacheln: getoente Flaeche mit LIME-KANTE - der Wunsch des Anwenders, aber
 * als Kante statt als Umrandung.
 *
 * Eine Lime-Umrandung ringsum erreicht auf hellem Grund 1,41:1 und waere
 * praktisch unsichtbar; dieselben vier Pixel an EINER Seite sieht man. Die
 * Trennung von der weissen Karte darunter macht die Toenung, nicht die Kante -
 * die Kante ist der Akzent.
 *
 * overflow:hidden haelt das Vorschaubild in der abgerundeten Ecke. */
.fb-anhang {
  background: var(--isoly-bg);
  border-left: 4px solid var(--isoly-cta-bg);
  border-radius: 0 var(--isoly-radius) var(--isoly-radius) 0;
  overflow: hidden;
  height: 100%;
}
/* VOLLSTAENDIGE Anzeige: `contain` statt `cover`. Mit `cover` fuellte das Bild
   die Kachel und wurde dabei mittig beschnitten - bei einem hohen
   Handy-Screenshot sah man nur den Mittelstreifen, also gerade nicht das, was
   der Melder zeigen wollte.

   Die feste Hoehe bleibt trotzdem: `contain` zeigt das Bild vollstaendig
   INNERHALB der Kachel, der Rest bleibt Hintergrundflaeche. Das Raster bleibt
   damit gleichmaessig - die Kacheln unterschiedlich hoch werden zu lassen waere
   nicht noetig, um alles zu sehen.

   HERLEITUNG DER 220 PIXEL - der bindende Fall ist das Hochformat:

   Bei `contain` bestimmt die Hoehe die Darstellung, sobald das Bild hoeher als
   breit ist. Fuer ein Handy-Seitenverhaeltnis von 9:19,5 (verbreitetes Format
   aktueller Geraete) gilt: Breite = Hoehe * 9 / 19,5.

   ZIEL: mindestens 100 Pixel Bildbreite. Das ist eine Lesbarkeits-Entscheidung
   und als solche benannt - darunter ist ein Screenshot ein Farbfleck, in dem
   sich weder Gliederung noch Dialogfenster ausmachen lassen, und eine Vorschau,
   die nichts erkennen laesst, erfuellt ihren Zweck nicht.

   Daraus folgt die Hoehe:  100 * 19,5 / 9 = 216,7  ->  aufgerundet 220.

   Gegenprobe mit 220:
     9:19,5 (Hochformat Handy)   220 * 9/19,5 = 101,5 px   ->  Ziel erfuellt
     9:16   (aeltere Geraete)    220 * 9/16   = 123,8 px   ->  reichlich
     16:9   (Querformat)         Breite bindet, volle Kachelbreite ~300 px
   Randfall winziges Bild (z. B. 40x40): wird nicht hochskaliert, `contain`
   vergroessert nicht - es steht mittig auf der Flaeche.

   Der alte Wert 170 verfehlte das Ziel deutlich: 170 * 9/19,5 = 78,5 px. */
.fb-anhang-bild {
  width: 100%;
  height: 220px;
  object-fit: contain;
  display: block;
  /* Weiss, nicht die Kachelfarbe: Bei `contain` bleibt neben einem Hochformat
     Flaeche frei. Waere sie wie die Kachel getoent, verschwaemme das Bild mit
     ihr - so steht es wie in einem Passepartout. */
  background: var(--isoly-surface);
  border-bottom: 1px solid var(--isoly-border);
}

/* Die Klick-Signalisierung der Vorschau (`cursor: zoom-in` plus Hover) steht
 * weiter oben bei der Lightbox, wo sie hingehoert - seit FEEDBACK-05a.
 *
 * Hier stand kurzzeitig eine ZWEITE, fast gleiche Definition samt der
 * Behauptung, die Vorschau habe bisher nichts signalisiert. Beides war falsch.
 * Gefunden hat es der regel-check; entstanden ist es, weil meine Suche nach
 * bestehenden Vorkommen `shared/style.css` per `grep -v` ausschloss - also
 * ausgerechnet die Datei, in die geschrieben wurde. Die beiden Faelle wichen
 * schon in derselben Aenderung voneinander ab (`.12s ease` gegen
 * `.12s ease-in-out`) - genau das Fehlerbild aus der Warnsignal-Tabelle in
 * development-process.md. */

.fb-anhang-fuss { padding: .6rem .75rem; }

/* ── Tabellen: Sortierung und Schnellfilter (public/shared/tabelle.js) ──────
 *
 * Anlass war die Anwenderfrage nach 100 Kunden und 75 Benutzern je Kunde.
 * Die Regeln stehen im Kopf von tabelle.js; hier steht nur das Aussehen.
 *
 * Farben ausschliesslich ueber Tokens (frontend-shared-code.md,
 * "Farb-Token-SSOT"). Die Pfeile sind Unicode-Zeichen statt Hintergrundbilder:
 * Ein per background-image eingebundenes SVG haette keinen Zugriff auf die
 * Custom Properties der Seite - dieselbe Einschraenkung, die dort schon fuer
 * die Marken-Grafiken dokumentiert ist. */

.tabellen-filter {
  display: flex;
  align-items: center;
  gap: .75rem;
  margin-bottom: .5rem;
}

.tabellen-filter input[type="search"] {
  max-width: 22rem;
}

.tabellen-zaehler {
  white-space: nowrap;
}

/* Nur Koepfe, die tabelle.js als sortierbar markiert hat - so bleibt der
 * Zeiger dort aus, wo ein Klick nichts tut (Spalte "Aktionen"). */
th.sortierbar {
  cursor: pointer;
  user-select: none;
  position: relative;
  padding-right: 1.1rem;
}

th.sortierbar:hover {
  color: var(--isoly-accent);
}

/* Der Platz fuer den Pfeil wird IMMER freigehalten (padding-right oben), auch
 * ohne aktive Sortierung. Sonst zuckte die Spaltenbreite beim ersten Klick. */
th.sort-auf::after,
th.sort-ab::after {
  position: absolute;
  right: .25rem;
  color: var(--isoly-accent);
}

th.sort-auf::after { content: "\25B4"; }   /* ▴ */
th.sort-ab::after  { content: "\25BE"; }   /* ▾ */

/* ═══ Kachelwaehler der Kombinations-Dialoge (UI-24) ═══════════════════════
 *
 * Die App laesst die Profile ueber BILDER waehlen, nicht ueber Namen —
 * `docs/KATALOG-SERVERSEITE.md` fuehrt "Bildpflicht bei Formauswahl" als
 * ueberall gleiches fachliches Muster. Ein Auswahlfeld mit Namen wie
 * "V1 unten" verlangt vom Bearbeiter, sich die Form zu merken; die Kachel
 * zeigt sie.
 *
 * Die Klassen liegen hier und nicht in einer Seitendatei, weil das Portal
 * kein eigenes CSS-Verzeichnis hat — derselbe Weg, den die `.fb-*`-Klassen
 * des Admin-Panels schon gehen. Farben ausschliesslich ueber Tokens
 * (.claude/rules/frontend-shared-code.md, Farb-Token-SSOT).
 */

.kk-reihe {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}

.kk-kachel {
  width: 4.5rem;
  height: 4.5rem;
  padding: .25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 2px solid var(--teal-300);
  border-radius: .5rem;
  cursor: pointer;
}

.kk-kachel:hover { border-color: var(--teal-500); }

/* Der gewaehlte Zustand ist NICHT allein ueber die Farbe erkennbar: Die
   Kachel traegt zusaetzlich einen kraeftigeren Rahmen. Farbe allein waere
   fuer Rotgruenblinde die einzige Unterscheidung (WCAG 1.4.1). */
.kk-kachel[aria-pressed="true"] {
  border-color: var(--teal-600);
  border-width: 3px;
  background: var(--teal-150);
}

.kk-kachel img, .kk-kachel svg { max-width: 100%; max-height: 100%; }

/* Der Name der Auswahl steht UNTER der Reihe, wie in der App. */
.kk-name {
  margin-top: .35rem;
  font-size: .875rem;
  color: var(--teal-700);
}

.kk-vorschau {
  display: flex;
  justify-content: center;
  padding: .5rem 0 1rem;
}

.kk-vorschau img { max-width: 100%; max-height: 16rem; }

/* ═══ Verbindlichkeits-Chips der Einstellungs-Pflege ═══
 *   CHIP-FARBEN-01 (Farbe) · CHIP-RUHE-01 (Zustand)
 *
 * Die Farbe sagt den WERT, das SCHRIFTGEWICHT sagt den ZUSTAND:
 *   fett = zentral gesetzt · normal = Werksvorgabe (geerbt)
 *
 * HIER STAND EIN RAND, und seine Begruendung war richtig: Keine der
 * drei hellen Flaechen hebt sich allein vom weissen Karten-Grund ab
 * (1,22 bis 2,19:1 — selbst gerechnet), mit teal-900 waren es 12,87:1.
 *
 * Der Betreiber hat die randlose Form am Geraet gesichtet und
 * abgenommen; das Linienwerk wirkte „extrem unruhig" (App-Seite
 * 7f590c9, auf Betreiber-Zuruf). Die Ruhe der Flaeche wiegt schwerer
 * als die Abhebung — eine Abwaegung, keine Korrektur.
 *
 * ZUR WCAG-FRAGE — 1.4.11 HAT ZWEI KLAUSELN, und sie sind getrennt zu
 * beantworten:
 *
 * (a) INFORMATION, die nur ein Nicht-Text-Merkmal traegt. Hier traegt
 *     die Flaeche nichts allein — der WERT steht als Text im Chip
 *     (5,88 bis 12,87:1, alle vier ueber der 4,5er-Hausgrenze fuer
 *     Badges), der ZUSTAND im Schriftgewicht. Beides sind
 *     Text-Eigenschaften; die Klausel greift nicht.
 *
 * (b) GRENZEN UND ZUSTAENDE VON BEDIENELEMENTEN. Diese Klausel greift
 *     sehr wohl: Der Chip ist ein <button>, und im RUHEZUSTAND hebt
 *     ihn nichts mehr ab (1,22 bis 2,19:1). Hover und Fokus tragen
 *     ihn erst, wenn er schon gefunden ist.
 *
 * DIESER REST IST BEWUSST IN KAUF GENOMMEN, nicht uebersehen — und er
 * ist NICHT still entschieden: Die App-Seite hat ausdruecklich um
 * Meldung gebeten, falls eine harte Hausgrenze greift. Wir haben
 * geantwortet (Austausch-Repo, FERTIGMELDUNG vom 2026-08-31): Die
 * Hausregel in `frontend-shared-code.md` nennt 3:1 fuer „Raender und
 * Status-Punkte" — beides gibt es hier nicht mehr —, und der Rest ist
 * dort benannt statt verschwiegen. Wer ihn schliessen will, braucht
 * eine Flaeche mit 3:1 gegen Weiss; das waere eine andere Farbwelt und
 * damit eine beidseitige Entscheidung, keine CSS-Zeile.
 *
 * BEIDE GEWICHTE STEHEN EXPLIZIT. Bootstrap gibt `.badge` heute 700
 * mit; wir koennten uns darauf verlassen und nur das geerbte
 * herunterziehen. Bootstrap kommt hier aber vom CDN (5.3.2) — eine
 * Fassung, die auf 600 oder 500 ginge, schmaelerte den Abstand still.
 * Dieselbe Ueberlegung wie bei `KLASSE_JE_WERT`: lieber die Tabelle
 * vollstaendig als einen Zweig, der von fremder Vorgabe lebt.
 */
.es-vb {
  border: 0;
  font-weight: 700;
}
.es-vb-geerbt { font-weight: 400; }

/*
 * DER BEDIENWEG BLEIBT SICHTBAR — das ist der Rest, den der Wegfall
 * des Randes hinterlaesst: Der Chip ist ein <button>, und ohne Rand
 * hebt ihn nichts mehr vom Kartengrund ab. Das betrifft nicht die
 * Information (siehe oben), sondern die Auffindbarkeit als
 * Bedienelement.
 *
 * Der Umriss liegt deshalb AUSSERHALB der Flaeche (`outline-offset`)
 * und trifft damit den weissen Grund: teal-900 gegen Weiss = 12,87:1.
 * Auf der Flaeche selbst waere er bei `es-vb-fest` unsichtbar — dort
 * IST die Flaeche teal-900.
 */
/*
 * BEIDE OFFSETS SIND GLEICH, und das ist die Antwort auf die Frage,
 * warum sie es sein sollten: Es gab keinen Grund fuer den Unterschied.
 * Der erste Entwurf setzte 1px und 2px; gefragt, welche Fachlichkeit
 * dahintersteckt, war die ehrliche Antwort „keine".
 */
.es-vb:hover { outline: 2px solid var(--teal-900); outline-offset: 2px; }
.es-vb:focus-visible { outline: 2px solid var(--teal-900); outline-offset: 2px; }

.es-vb-frei     { background: var(--isoly-stufe-frei);     color: var(--teal-900); }
.es-vb-standard { background: var(--isoly-stufe-standard); color: var(--teal-900); }
.es-vb-fest     { background: var(--isoly-stufe-fest);     color: var(--isoly-on-brand); }
.es-vb-ohne     { background: var(--isoly-stufe-ohne);     color: var(--teal-900); }

/* ── Hinweis-Balken: ungespeicherte Arbeit (HINWEIS-BALKEN-01) ─────────
 *
 * Erbeten von der App-Seite mit Begründung: „Bei ungespeicherter Arbeit
 * ist AUFFÄLLIG das richtige Ziel; ein Zähler in der Fußleiste ist leicht
 * zu übersehen, wenn der Blick oben im Formular arbeitet."
 *
 * WARUM `--isoly-warning` UND NICHT `--isoly-stufe-standard`, obwohl
 * beide #E8A13C tragen: Der Kommentar am Stufen-Token oben begründet die
 * Trennung ausdrücklich damit, dass die Stufe „eine STUFEN-Farbe, keine
 * Warn-Semantik" ist. Hier liegt Warn-Semantik vor — also gehört der
 * Balken auf die Warnfarbe. Ein beidseitig abgestimmter Tausch der
 * Stufen-Farbe darf ihn NICHT mitfärben; genau dafür sind die zwei
 * Token getrennt.
 *
 * WARUM NICHT Bootstraps `.alert-warning`: Das ist bei uns die TINT-
 * Variante (Fläche #F6EDDF, Text #976012) — dezent, also das Gegenteil
 * des Ziels. Verlangt ist die volle Fläche.
 *
 * KONTRAST, selbst nachgerechnet (WCAG 2.1, nicht aus einer Doku
 * übernommen — `no-empirical-fixes.md`):
 *
 *   teal-900 auf warning   5,88:1  ✓ (Fließtext braucht 4,5:1)
 *   weiß     auf warning   2,19:1  ✗ — deshalb NIE weißer Text hier
 *   warning  auf weiß      2,19:1  ✗ als Abgrenzung (Nicht-Text: 3:1)
 *
 * Die Formel ist an ihren Randwerten gegengeprüft: #FFF/#000 = 21,00
 * und #FFF/#FFF = 1,00.
 *
 * DIE DRITTE ZEILE IST DER GRUND FÜR DEN RAND. Die Fläche hebt sich
 * mit 2,19:1 nicht vom weißen Dialoggrund ab. Ein Balken, der sich
 * nicht abhebt, ist keiner.
 *
 * DER RAND BLEIBT HIER, OBWOHL ER BEI DEN CHIPS GEFALLEN IST
 * (CHIP-RUHE-01, 2026-08-31). Hier stand die gleiche Lage bei den
 * Verbindlichkeits-Chips als Stütze — die trägt nicht mehr, und der
 * Unterschied ist der Eintrag wert:
 *
 *   Die Chips sind VIELE, klein und stehen dicht beieinander; ihr
 *   Linienwerk wirkte in der Sichtung „extrem unruhig". Und ihre
 *   Fläche trägt keine Information allein — Wert im Text, Zustand im
 *   Schriftgewicht.
 *
 *   Der Balken ist EINER, breit, und seine Fläche IST die Aussage:
 *   Sie soll auffallen, das war die ausdrückliche Bitte („AUFFÄLLIG
 *   ist das richtige Ziel"). Von Unruhe kann bei einem einzelnen
 *   Element keine Rede sein.
 *
 * Wer den Balken das nächste Mal anfasst, entfernt den Rand also
 * NICHT der Konsistenz halber — er wäre hier ein anderer Fall.
 */
.es-hinweis {
  background: var(--isoly-warning);
  color: var(--teal-900);
  border-bottom: 1px solid var(--teal-900);
  padding: 0.5rem 1rem;
  font-weight: 600;
}

