/**
 * @conda/ops-ui — Components
 * UI-Primitives für alle Operations-Hub-Module.
 */

/* ========== BUTTON ========== */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 8px 16px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-size: var(--font-size-base);
  font-weight: var(--font-weight-medium);
  cursor: pointer;
  transition: all var(--transition-fast);
  user-select: none;
  white-space: nowrap;
}
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-primary {
  background: var(--brand-cta);
  color: var(--text-on-cta);
}
.btn-primary:hover:not(:disabled) { background: var(--brand-cta-hover); }
.btn-primary:active:not(:disabled) { background: var(--brand-cta-hover); }
.btn-secondary {
  background: var(--bg-tertiary);
  color: var(--text-primary);
  border-color: var(--border);
}
.btn-secondary:hover:not(:disabled) { background: var(--bg-quaternary); border-color: var(--border-strong); }
.btn-ghost {
  background: transparent;
  color: var(--text-secondary);
}
.btn-ghost:hover:not(:disabled) { background: var(--bg-tertiary); color: var(--text-primary); }
.btn-danger {
  background: var(--danger);
  color: var(--text-on-brand);
}
.btn-danger:hover:not(:disabled) { filter: brightness(1.1); }
.btn-sm { padding: 5px 11px; font-size: var(--font-size-sm); }
.btn-lg { padding: 11px 22px; font-size: var(--font-size-md); }

/* ========== CARD ========== */
.card {
  background: var(--bg-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--elev-1);
}
.card-header { font-weight: var(--font-weight-medium); font-size: var(--font-size-md); margin-bottom: var(--space-3); }
.card-subheader { font-size: var(--font-size-xs); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: var(--space-2); }

/* ========== FORM ========== */
.form-row { margin-bottom: var(--space-4); }
.form-label {
  display: block;
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  margin-bottom: var(--space-1);
  font-weight: var(--font-weight-medium);
}
.form-label.required::after { content: ' *'; color: var(--danger); }
.form-input, .form-select, .form-textarea {
  width: 100%;
  background: var(--bg-primary);
  border: 1px solid var(--bd-input);
  border-radius: var(--radius-sm);
  padding: 9px 12px;
  color: var(--text-primary);
  font-size: var(--font-size-base);
  transition: border-color var(--transition-fast);
}
.form-input:focus, .form-select:focus, .form-textarea:focus {
  outline: none;
  border-color: var(--conda-sky);
  box-shadow: 0 0 0 1px var(--conda-sky);
}
.form-textarea { resize: vertical; min-height: 80px; }
/* --text-secondary statt --text-tertiary: Hilfetexte tragen Inhalt (etwa den
   Hinweis „nicht zugeordnet") und muessen AA erreichen — der Tertiaerton kommt
   auf der Karte nur auf 3.16. Keine optische Aenderung im Aufbau. */
.form-help { font-size: var(--font-size-xs); color: var(--text-secondary); margin-top: 4px; }
.form-error { font-size: var(--font-size-xs); color: var(--danger); margin-top: 4px; }
.form-row-inline { display: flex; gap: var(--space-3); }
.form-row-inline > .form-row { flex: 1; }

/* ========== TABLE ========== */
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--font-size-base);
}
.table th, .table td {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  border-bottom: 1px solid var(--border);
}
.table th {
  color: var(--text-secondary);
  font-weight: var(--font-weight-medium);
  font-size: var(--font-size-xs);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.table tbody tr:hover { background: var(--bg-tertiary); }
.table-mono td { font-family: var(--font-mono); font-size: var(--font-size-sm); }
.table .num { text-align: right; font-family: var(--font-mono); }

/* ========== STATUS-PILL ========== */
.pill {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--bg-tertiary);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
}
.pill-entwurf { color: var(--warning); }
.pill-eingereicht { color: var(--conda-sky); }
.pill-genehmigt { color: var(--success); }
.pill-abgelehnt { color: var(--danger); }
.pill-ausbezahlt { color: var(--text-secondary); }
.pill-storniert { color: var(--text-disabled); text-decoration: line-through; }

/* ========== BADGE (Country, Tag) ========== */
.badge {
  display: inline-block;
  padding: 1px 7px;
  border-radius: var(--radius-sm);
  background: var(--bg-tertiary);
  color: var(--conda-sky);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
}

/* ========== STEPPER (Wizard) ========== */
.stepper {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-bottom: var(--space-5);
  flex-wrap: wrap;
}
.step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 6px 12px;
  border-radius: var(--radius-sm);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.step.active { background: var(--brand); color: var(--text-on-brand); font-weight: var(--font-weight-medium); }
.step.done { color: var(--text-primary); }
.step-num {
  display: inline-flex;
  width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--bg-tertiary);
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  color: var(--text-secondary);
}
.step.active .step-num { background: var(--text-on-brand); color: var(--brand); }
.step.done .step-num { background: var(--success); color: var(--text-on-brand); }
.step-arrow { color: var(--text-disabled); font-size: var(--font-size-sm); }

/* ========== TOAST ========== */
.toast-container {
  position: fixed;
  bottom: var(--space-4);
  right: var(--space-4);
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  pointer-events: none;
}
.toast {
  background: var(--bg-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 16px;
  min-width: 280px;
  box-shadow: var(--elev-3);
  pointer-events: all;
  font-size: var(--font-size-sm);
  animation: toast-in 0.2s ease;
}
.toast-success { border-left: 3px solid var(--success); }
.toast-warning { border-left: 3px solid var(--warning); }
.toast-error { border-left: 3px solid var(--danger); }
.toast-info { border-left: 3px solid var(--info); }
@keyframes toast-in {
  from { opacity: 0; transform: translateX(20px); }
  to { opacity: 1; transform: translateX(0); }
}

/* ========== MODAL ========== */
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: var(--bg-overlay);
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Rand zum Fensterrand (FEINSCHLIFF C3). Ohne ihn klebte ein Dialog, der
     die volle Breite braucht, an beiden Kanten. Der Wert ist zugleich der
     Bezug fuer `min-width: min(400px, 100%)` unten: `100%` rechnet gegen
     den INHALTSBEREICH dieses Behaelters, nicht gegen `100vw` — damit ist
     der Rollbalken schon abgezogen. */
  padding: var(--space-4);
  animation: backdrop-in 0.15s ease;
}
@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
.modal {
  background: var(--bg-primary);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  /* ⚠️ `min(400px, 100%)` STATT `400px` (FEINSCHLIFF C3, gemessen 01.09.).
     Auf einem 360-px-Telefon zwang das feste Mindestmass jeden Dialog auf
     400 px: gemessen ragte er **207 px** ueber den rechten Rand hinaus, und
     die ganze Seite liess sich seitwaerts schieben. Betroffen war JEDER
     Dialog — Loeschen, Anlegen, Belege, Positionen.
     Auf breiten Fenstern aendert sich NICHTS: dort ist `100%` groesser als
     400 px, und `min()` waehlt weiterhin die 400. */
  min-width: min(400px, 100%);
  max-width: 600px;
  max-height: 80vh;
  overflow-y: auto;
  box-shadow: var(--elev-3);
}

/* ⚠️ BREITE VARIANTE FUER FORMULARDIALOGE (HERMES-2026-08-25-DIALOG-BREITE).
   780 px, weil `form-grid` ab 780 px traegt — der Wert kommt aus der Regel,
   nicht aus Geschmack. Der Standard oben bleibt bei 600 px: Ein Ja-Nein-Dialog
   wird nicht breiter, nur weil ein Formular es braucht.

   Gemessene Folge bei 780 px (Inhaltsbreite 723 px, also 780 minus 2 Rahmen,
   40 Polsterung und 15 Rollbalken; elf Abstaende a 16 px):
     f-3 ~ 169 px   f-4 ~ 230 px   f-6 ~ 353 px   f-12 ~ 723 px
   Vorher bei 600 px waren es 128 / 176 / 272 / 543. Der Unterschied bei `f-3`
   ist der zwischen unbequem und benutzbar.

   ⚠️ DIE MEDIENREGEL IST KEIN BEIWERK. Ohne sie laeuft ein 780-px-Dialog auf
   einem 700-px-Fenster links und rechts ueber den Rand, weil der Hintergrund
   per Flexbox zentriert. Ein Dialog, der auf einem Notebook nicht mehr auf den
   Schirm passt, waere ein Rueckschritt.

   ⚠️ NACHTRAG 01.09.: Die Untergrenze des Standards ist seit FEINSCHLIFF C3
   `min(400px, 100%)`. Damit gilt die Medienregel hier fuer die HOECHST-
   breite und die Untergrenze fuer die MINDESTbreite — vorher haette diese
   Regel den breiten Dialog auf einem Telefon zwar auf 100vw gedeckelt, das
   feste Mindestmass von 400 px haette ihn aber trotzdem ueberlaufen lassen. */
.modal-breit { max-width: 780px; }
@media (max-width: 820px) {
  .modal-breit { max-width: calc(100vw - var(--space-5) * 2); }
}

.modal-title { font-size: var(--font-size-md); font-weight: var(--font-weight-medium); margin: 0 0 var(--space-4); }
.modal-actions { display: flex; gap: var(--space-2); justify-content: flex-end; margin-top: var(--space-5); }

/* ========== AUTH-OVERLAY ========== */
.auth-overlay {
  position: fixed;
  inset: 0;
  background: var(--bg-primary);
  z-index: 1100;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: var(--space-4);
}
.spinner {
  width: 32px;
  height: 32px;
  border: 3px solid var(--border);
  border-top-color: var(--conda-green);
  border-radius: var(--radius-pill);
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ========== EMPTY STATE ========== */
.empty {
  text-align: center;
  padding: var(--space-12) var(--space-4);
  color: var(--text-secondary);
}
.empty-title { font-size: var(--font-size-md); color: var(--text-primary); margin-bottom: var(--space-2); }

/* ========== UTILITIES ========== */
.flex { display: flex; }
.flex-1 { flex: 1; }
.gap-2 { gap: var(--space-2); }
.gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); }
.between { display: flex; justify-content: space-between; align-items: center; }
.text-secondary { color: var(--text-secondary); }
.text-mono { font-family: var(--font-mono); }
.mb-2 { margin-bottom: var(--space-2); }
.mb-4 { margin-bottom: var(--space-4); }
.mt-4 { margin-top: var(--space-4); }
.hidden { display: none !important; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); border: 0;
}

/* ========== DEFINITIONSLISTE (Label/Wert in Detailansichten) ==========
   HERMES-2026-08-19-INVENTAR-UI, Mockup v1.0 Abschnitt 3.

   ⚠️ Diese Bausteine stehen hier und NICHT in `inventar.js` — sonst entstehen
   genau die modul-eigenen Primitives, die die Regel „nur Components aus
   @conda/ops-ui" verhindern soll (operations-hub.md §12, seit 24.04.). Eine
   Definitionsliste braucht jedes kuenftige Modul.

   Vorgeschichte: Das Inventar hat `kv`/`kv-key`/`kv-val` benutzt — Klassen, fuer
   die es NIE eine Regel gab. Ergebnis auf der Detailseite: „HerstellerApple",
   weil zwei Inline-Spans ohne Trenner nebeneinander standen. */
.dl { display: grid; grid-template-columns: minmax(130px, 180px) 1fr; }
.dl > dt {
  padding: 9px var(--space-4) 9px 0;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  border-bottom: 1px solid var(--border-subtle);
}
.dl > dd {
  padding: 9px 0;
  margin: 0;
  font-weight: var(--font-weight-medium);
  border-bottom: 1px solid var(--border-subtle);
  overflow-wrap: anywhere;
}
.dl > dt:last-of-type, .dl > dd:last-of-type { border-bottom: 0; }

/* „nicht erfasst" — bei 91 migrierten Geraeten ist Leere die Regel. Ein
   Gedankenstrich sieht aus wie ein Wert; Grau sagt, dass hier Arbeit wartet. */
.dl-empty { color: var(--text-tertiary); font-weight: var(--font-weight-regular); }

/* MAC, IP, Seriennummer: zeichenweise vergleichbar. In Proportionalschrift
   sehen `8b` und `b8` unnoetig aehnlich aus.

   ⚠️ KORREKTUR 25.08. — TECHNISCHE KENNUNGEN BRECHEN NIE UM.
   `.dl > dd` traegt `overflow-wrap: anywhere`; fuer Fliesstext ist das richtig
   und fuer Kennungen falsch. Gemessen im Sperr-Dialog: eine MAC brach als
   `02:00:00:00:00:0` / `1` um. **Eine halb umgebrochene MAC wird FALSCH
   GELESEN, und niemand merkt es, weil beide Haelften plausibel aussehen.** Ein
   Scrollbalken ist unbequem, aber ehrlich: man sieht, dass da noch etwas ist.
   Gilt fuer MAC, IP, Seriennummer, Vertragsnummer und IDs. */
.dl-mono {
  font-family: var(--font-mono);
  font-size: var(--font-size-sm);
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow-x: auto;
  overflow-wrap: normal;
  max-width: 100%;
}

/* ⚠️ KORREKTUR 25.08. — `dl-2` MIT MINDESTBREITE JE WERTESPALTE.
   Vorher stand hier `1fr` fuer die Werte. In einem 600-px-Dialog ergab das
   gemessene `160px 111.6px 160px 111.6px`: **112 Pixel fuer eine MAC-Adresse,
   die rund 130 braucht** — und fuer ganze Saetze, die zu zwoelfzeiligen
   Schluchten wurden.

   `minmax(220px, 1fr)` erzwingt die Grenze jetzt TECHNISCH, statt sie nur zu
   dokumentieren; unter 900 px faellt das Raster auf zwei Spalten zurueck. Die
   Regel dahinter (Register 25.08.): **Zu jedem Layout-Baustein gehoert die
   Angabe, ab welcher Breite er traegt.** Ein Raster ohne Mindestbreite je
   Spalte ist eine Falle, keine Vorlage — es rendert ueberall, aber nur an
   manchen Stellen lesbar.

   `dl-2` ist fuer BREITE DETAILSEITEN ab 900 px. Nicht in Dialogen, nicht in
   Karten darunter, nicht in Seitenleisten. Im Zweifel `dl`. */
.dl-2 {
  grid-template-columns:
    minmax(120px, 160px) minmax(220px, 1fr)
    minmax(120px, 160px) minmax(220px, 1fr);
}
@media (max-width: 900px) {
  .dl-2 { grid-template-columns: minmax(130px, 180px) 1fr; }
}

/* NEU 25.08. — Erklaerbereich unter einer Definitionsliste im Dialog.

   ⚠️ GANZE SAETZE GEHOEREN NICHT IN EIN RASTER. Ein Raster ist fuer
   Label-Wert-Paare; Erklaerungen gehoeren darunter als Fliesstext. Faustregel:
   **Passt der Wert nicht in fuenf Woerter, ist er kein Feldwert.**
   Die Liste sagt, WAS gilt — der Text darunter, WARUM. */
.dlg-erklaer {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}
.dlg-erklaer p { margin: 0 0 var(--space-2); }
.dlg-erklaer p:last-child { margin-bottom: 0; }
.dlg-erklaer b { color: var(--text-primary); font-weight: var(--font-weight-medium); }

/* ========== FORMULAR-RASTER (12 Spalten, mobil einspaltig) ========== */
.form-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: var(--space-4); }
.f-3 { grid-column: span 3; }
.f-4 { grid-column: span 4; }
.f-6 { grid-column: span 6; }
.f-12 { grid-column: span 12; }
@media (max-width: 780px) {
  .f-3, .f-4, .f-6 { grid-column: span 12; }
}

/* Feldgruppe. Der Legendentext traegt bei gesperrten Gruppen den Hinweis
   darauf — die Grenze soll sichtbar sein, nicht nur wirksam. */
.fieldset { border: 0; padding: 0; margin: 0 0 var(--space-5) 0; }
.fieldset > legend {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--brand);
  width: 100%;
  padding-bottom: var(--space-2);
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
}

/* Pflichtfeld-Stern und Formular-Fussleiste. Ein Formular ohne sichtbaren
   Ausstieg zwingt zum Browser-Zurueck — und das verliert die Eingaben. */
.req { color: var(--danger); margin-left: 2px; }
.form-footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border-subtle);
}

/* ========== PILLS: INVENTAR-STATUS UND HERKUNFT ==========
   Nur `color`, wie alle bestehenden Pills — `.pill` traegt Hintergrund und
   Form. Das Mockup schlug eigene Hintergrund-Literale vor; die Hausform ist
   farbig-auf-neutral, und neue Literalwerte waren ausgeschlossen. */
.pill-aktiv { color: var(--success); }
.pill-defekt { color: var(--danger); }
.pill-lager { color: var(--text-secondary); }
.pill-reparatur { color: var(--warning); }
.pill-ausgeschieden { color: var(--text-disabled); text-decoration: line-through; }
.pill-unbekannt { color: var(--text-tertiary); }
/* Herkunft: woher ein Datensatz kommt (manuell · discovery · import). */
.pill-manuell { color: var(--text-secondary); }
.pill-discovery { color: var(--conda-sky); }
.pill-import { color: var(--text-secondary); }

/* ========== FILTERLEISTE ==========
   Eine Zeile beschrifteter Auswahlfelder ueber einer Liste — so generisch wie
   die Tabelle darunter. */
.filterbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
  margin-bottom: var(--space-4);
}
.filter { display: flex; flex-direction: column; gap: 4px; }
.filter > span {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  font-weight: var(--font-weight-medium);
}
.filter-suche { flex: 1 1 220px; }

/* ========== HINWEISFLAECHE ==========
   Fuer Saetze, die den Zustand eines Datensatzes erklaeren (z. B. „dieses
   Geraet wurde einem anderen zugeordnet"). */
.notice {
  background: var(--bg-secondary);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
}

/* ========== MEHRFACHAUSWAHL (Kontrollkaestchen-Gruppe) ========== */
.checks { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
.check { display: flex; align-items: center; gap: 6px; font-size: var(--font-size-sm); }

/* ========== HAUPTAKTION AUF --brand ==========
   HERMES-2026-08-19-INVENTAR-UI, Roland-Entscheid: Die Hauptaktion gehoert auf
   `--brand` (#27614e). `--brand-cta` (Mint #9dd1c1) ist eine AKZENTFLAECHE —
   als Knopffarbe wirkt sie inaktiv.

   ⚠️ WARUM EINE EIGENE VARIANTE UND NICHT `.btn-primary` UMGEFAERBT:
   `.btn-primary` steht heute auf `--brand-cta` und wird von Spesen (5x) und
   Admin (3x) mitbenutzt — beide sind abgenommen und in diesem Auftrag
   ausdruecklich nicht im Scope. Ein Farbwechsel dort waere eine stille
   Umgestaltung fremder Module.

   Der Entscheid gilt aber allgemein. Sobald der Orchestrator es fuer die ganze
   App anordnet, ist es EINE Zeile: `.btn-primary` auf `--brand`/`--text-on-brand`
   setzen und diese Variante wieder entfernen. */
.btn-brand {
  background: var(--brand);
  color: var(--text-on-brand);
}
.btn-brand:hover:not(:disabled) { background: var(--brand-hover); }
.btn-brand:active:not(:disabled) { background: var(--brand-hover); }

/* ========== MODULKACHELN DER STARTSEITE ==========
   HERMES-2026-08-19-INVENTAR-UI (Nachtrag), Mockup v1.1.

   ⚠️ Der Modulstatus kommt aus `GET /api/modules`, NICHT aus dem Template.
   Vorher stand er als Text im Startseiten-Code und war einen Tag nach dem
   Go-live falsch: „V2 — GEPLANT" auf einer Kachel mit 91 Geraeten dahinter. */
.section-title {
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-bold);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin: 0 0 var(--space-3);
}
.mod-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
  gap: var(--space-4);
}
.mod {
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
  box-shadow: var(--elev-1);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: box-shadow var(--transition-fast), transform var(--transition-fast);
}
.mod:hover, .mod:focus-visible { box-shadow: var(--elev-2); transform: translateY(-1px); }
.mod-top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3); }
.mod-head { display: flex; gap: var(--space-3); align-items: center; }
.mod-icon {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: var(--bg-tertiary);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--brand);
  flex-shrink: 0;
}
.mod-name { font-size: var(--font-size-md); font-weight: var(--font-weight-medium); margin: 0; }
.mod-desc { color: var(--text-secondary); font-size: var(--font-size-sm); margin: 0; }

/* Drei Kennzahlen. Die ERSTE ist immer die Handlungszahl. */
.mod-stats {
  display: flex;
  gap: var(--space-5);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border-subtle);
  border-bottom: 1px solid var(--border-subtle);
}
.stat-num {
  font-size: var(--font-size-lg);
  font-weight: var(--font-weight-medium);
  line-height: 1.1;
  /* Tabellenziffern: sonst springt die Zahl beim Aktualisieren in der Breite. */
  font-variant-numeric: tabular-nums;
}
.stat-num.warn { color: var(--warning); }
.stat-num.mute { color: var(--text-tertiary); }
.stat-lbl {
  font-size: var(--font-size-xs);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.mod-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* ⚠️ SCHNELLAKTIONEN SIND GLEICHRANGIG (Mockup v1.1, Korrektur nach Rolands
   Einwand „als ob nur die gruenen Buttons aktiv waeren"). Beide legen etwas
   Neues an, keine ist der bevorzugte Weg. Primaer/Sekundaer liest sich hier
   nicht als „wichtiger", sondern als „aktiv/deaktiviert" — verstaerkt durch
   einen blassen grauen Rand. Deshalb: gleiche Form, Rand in --brand, die
   Rueckmeldung sitzt in der Interaktion (Fuellung beim Ueberfahren). */
.btn-quick {
  font: inherit;
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  padding: 6px 12px;
  border-radius: var(--radius-md);
  border: 1px solid var(--brand);
  background: var(--bg-primary);
  color: var(--brand);
  cursor: pointer;
}
.btn-quick:hover, .btn-quick:focus-visible { background: var(--brand); color: var(--text-on-brand); }
.btn-open {
  margin-left: auto;
  border: 0;
  background: transparent;
  color: var(--brand);
  font-weight: var(--font-weight-medium);
  padding: 6px 0;
  font-size: var(--font-size-sm);
}

/* Geplante Module: schmale gestrichelte Zeile statt voller Kachel. Der Platz
   gehoert dem, was man benutzen kann — und bei acht geplanten Modulen waeren
   acht leere Kacheln unertraeglich. */
.soon-wrap {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(272px, 1fr));
  gap: var(--space-3);
}
.mod-soon {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-4);
  color: var(--text-tertiary);
  font-size: var(--font-size-sm);
}
.mod-soon b { color: var(--text-secondary); font-weight: var(--font-weight-medium); }
.mod-soon .pill { margin-left: auto; }

.pill-neu { color: var(--info); }
.pill-plan { color: var(--text-tertiary); }

/* ========== KOPFZEILE: SYMBOL STATT ABKUERZUNG ==========
   „Benachr." war eine abgekuerzte Beschriftung. Regel daraus: Passt ein Wort
   nicht, ist entweder das Symbol richtig oder das Layout zu eng — nie die
   Abkuerzung. Das `aria-label` war bereits hinterlegt; sichtbar stand trotzdem
   ein abgeschnittenes Wort.

   ⚠️ HIER STEHT ABSICHTLICH KEINE NEUE REGEL: `.topbar-icon` und
   `.topbar-badge` existieren in `styles.css` samt Hover-Flaeche fuer die dunkle
   Kopfzeile und Zaehler in `--danger`. Ein zweiter Symbolknopf daneben waere
   genau die Doppelpflege, die dieser Auftrag abbaut. Geaendert wurde nur der
   INHALT des Knopfes: Glocken-SVG statt Text. */

/* ===========================================================================
   GERAETESPERRE (HERMES-2026-08-19-GERAETESPERRE)

   ⚠️ FARBE AUS DEM TOKEN, NIE ALS LITERALWERT. Das CONDA-Brandbook fuehrt kein
   Rot (gemessen in conda-ci-design/reference/colors.json: nur Gruen-, Blau- und
   Grautoene). Das ops-ui fuehrt --danger/--warning/--success/--info seit Beginn
   und benutzt sie ueberall — Fehlermeldungen, Pills, die Warnzahl auf den
   Kacheln. Ein Brandbook fuer Dokumente braucht keine Gefahrenfarbe, Software
   schon; die Abweichung ist dokumentiert, nicht neu erfunden.
   =========================================================================== */

/* Das Alarmsymbol in der Kopfzeile. Es erbt Hoehe, Radius und Padding von
   `.topbar-icon` — nur die Flaeche wird rot. Sichtbarkeit steuert `hidden` aus
   app.js; bei 0 Sperren ist das Symbol WEG, nicht grau. */
.topbar-alarm { background: var(--danger); gap: 6px; }
/* Der Hover von `.topbar-icon` setzt eine weisse Transluzenz — auf Rot ergaebe
   das ein blasses Rosa. Hier bleibt die Flaeche und wird nur etwas heller,
   dieselbe Bewegung wie bei `.btn-danger:hover`. */
.topbar-alarm:hover { background: var(--danger); filter: brightness(1.12); }
/* Die Zahl NEBEN dem Zeichen, nicht als ueberlagertes Badge (das truege auf
   roter Flaeche dieselbe Farbe wie der Grund). */
.topbar-alarm-zahl { font-size: 12px; font-weight: var(--font-weight-medium); }

/* Sperrzustand in der Geraetezeile — nur `color` plus Gewicht, wie alle Pills.
   `teils` ist ABSICHTLICH eine andere Farbe: auf einem Weg draussen, auf dem
   anderen drin. Wer das gleich einfaerbt, verliert genau die Information, um
   die es geht.

   Die Farben teilen sie mit `.pill-defekt` und `.pill-reparatur`, die Bedeutung
   nicht — deshalb eigene Klassen statt Wiederverwendung: „gesperrt" ist kein
   Gerätezustand, sondern eine Netzentscheidung, und beide koennen sich
   auseinanderentwickeln. Das zusaetzliche Gewicht ist der Unterschied, der
   heute schon sichtbar ist: eine Sperre ist lauter als ein Status. */
.pill-gesperrt { color: var(--danger); font-weight: var(--font-weight-medium); }
.pill-halb { color: var(--warning); font-weight: var(--font-weight-medium); }

/* Die Sperrspalte steht links vor dem Geraetenamen (Roland-Vorgabe) und soll
   der Namensspalte keinen Platz nehmen. */
.zelle-sperre { width: 1%; white-space: nowrap; }

/* Sperrzustand „unklar" — nach einer Zeitüberschreitung, die auch die Nachlese
   nicht auflösen konnte. Bewusst NICHT wie `teils` eingefärbt und trotzdem in
   der Warnfamilie: „halb gesperrt" ist eine Aussage, „nicht feststellbar" ist
   keine. Die gepunktete Unterstreichung ist der Unterschied ohne einen neuen
   Farbwert — das Brandbook führt ohnehin nur drei Warnfarben, und eine vierte
   zu erfinden wäre schlimmer als eine geteilte. */
.pill-unklar {
  color: var(--warning);
  font-weight: var(--font-weight-medium);
  text-decoration: underline dotted;
  text-underline-offset: 2px;
}

/* ===========================================================================
   WARTEZUSTAND DES SPERR-KNOPFS (HERMES-2026-08-25-SPERRE-WARTEZUSTAND)

   Vorlage: OUTPUTS/HERMES_Sperrbutton_Wartezustand_Mockup_v1.html, Abschnitt 4.
   Gemessener Anlass: Sperren dauerte live 31.518 ms, Entsperren 26.944 ms —
   beides mit EINER MAC. Eine halbe Minute Wartezeit auf einen Knopf, der
   "sofort sperren" heisst, fuehlt sich wie ein Fehler an; wer nach 15 Sekunden
   nochmal klickt, loest einen zweiten Vorgang gegen echte Hardware aus.

   ⚠️ FUENF HEX-LITERALE DER VORLAGE SIND IN TOKENS UEBERSETZT. Das Mockup
   schlug #faf9f6, #fff (2x), #faf6ec und #fbf0ef vor; die Auflage lautet "keine
   neuen Farben, keine Hex-Literale", und ein Test prueft es. Fuer die beiden
   getoenten Hintergruende der Meldezeile gibt es KEINEN Token (--warning-bg
   existiert nicht) — sie werden ein neutraler Ton, die Farbe traegt der Text.
   Dieselbe Uebersetzung wie bei den Inventar-Pills am 19.08.

   ⚠️ WIEDERVERWENDET, NICHT NEU DEFINIERT: `.spinner` (oben, 32 px) wird hier
   nur verkleinert; `.zelle-sperre` bleibt unveraendert; und der Zustand
   "gesperrt" nimmt `.btn-danger` statt einer eigenen `.btn-gesperrt` — das
   waere zeichengleiche Doppelpflege.
   =========================================================================== */

/* Der Knopf waehrend des Laufs. `cursor: progress` sagt "laeuft", nicht
   "verboten" — `not-allowed` waere die falsche Aussage. */
.btn-warten {
  border-color: var(--border);
  background: var(--bg-tertiary);
  color: var(--text-secondary);
  cursor: progress;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.btn-warten .spinner { width: 12px; height: 12px; border-width: 2px; }

/* Die laufende Zeile. Die Sperr-Spalte wird ABSICHTLICH nicht gedimmt — dort
   steht die Information, auf die gewartet wird. */
tr.laeuft { background: var(--bg-secondary); }
tr.laeuft td:not(.zelle-sperre) { opacity: .55; }

/* Der Sekundenzaehler. `tabular-nums` verhindert das Zappeln der Ziffern —
   eine Zahl, die bei jedem Takt die Breite wechselt, wirkt unruhiger als die
   Wartezeit selbst. */
.laufzeit {
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--text-tertiary);
  display: block;
  margin-top: 3px;
}

/* Teilerfolg: Rand in --warning auf weisser Flaeche. "Erneut versuchen" ist
   kein gefaehrlicher Knopf, aber auch kein neutraler. */
.btn-teils {
  border-color: var(--warning);
  background: var(--bg-primary);
  color: var(--warning);
  font-weight: var(--font-weight-medium);
}

/* Meldezeile unter der Tabelle. Sie verschwindet NICHT von selbst: Teilerfolg
   und `unklar` sind Zustaende, die jemand aufloesen muss. */
.zeilen-hinweis {
  font-size: 12px;
  padding: 8px var(--space-4) 10px;
  background: var(--bg-secondary);
  border-top: 1px solid var(--border-subtle);
  color: var(--warning);
}
.zeilen-hinweis.err { color: var(--danger); }

/* Bewegungsempfindlichkeit: Der Zaehler ist die Information, die Drehung nur
   Dekoration. Also steht der Spinner still und der Zaehler laeuft weiter. */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; opacity: .6; }
}

/* ========== GRUPPENKOPF (HERMES-2026-08-27-ADMIN-S2) ==========
   Der Gruppentitel war eine `.card-subheader`: xs, versal, sekundaerfarben —
   eine Beschriftungszeile, kein Titel. Er traegt jetzt Gewicht und ist zugleich
   die Schaltflaeche zum Auf- und Zuklappen; deshalb ein <button>, damit
   Tastatur und Screenreader ihn als das erkennen, was er tut. */
.gruppen-kopf {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-3) 0;
  background: none;
  border: 0;
  border-top: 1px solid var(--border-subtle);
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-bold);
  color: var(--text-primary);
}
.gruppen-kopf:hover { color: var(--conda-green); }
.gruppen-kopf:focus-visible { outline: 2px solid var(--conda-green); outline-offset: 2px; }
.gruppen-pfeil { display: inline-block; width: 1em; color: var(--text-secondary); }
.gruppen-anzahl { font-weight: var(--font-weight-regular); color: var(--text-secondary); font-size: var(--font-size-sm); }
.gruppen-werkzeuge { margin-left: auto; display: flex; gap: var(--space-2); }
.gruppen-zeile { display: flex; align-items: center; gap: var(--space-3); }
.gruppen-zeile > .gruppen-kopf { flex: 1; }
