/* ══════════════════════════════════════════════════════════════════════════
   core/shell.css — Shared UI-Chrome
   Lädt vor allen Modul-CSS. Enthält globale Styles, die nicht zu einem
   einzelnen Modul gehören: Reset, Farben, Top-Nav, Buttons, Modal-Basis,
   Toast, Form-Field-Basis, Detail-Modal-Generika.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Reset & Base ─────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
  --brand:     #1a1a2e;
  --brand2:    #16213e;
  --accent:    #c8a96e;
  --accent2:   #b08d52;
  --bg:        #f4f6f9;
  --surface:   #ffffff;
  --border:    #e2e6ea;
  --text:      #1a1a2e;
  --text-muted:#6b7280;
  --danger:    #c0392b;
  --success:   #27ae60;
  --warning:   #e67e22;
  --radius:    8px;
  --shadow:    0 2px 16px rgba(0,0,0,0.08);
  /* Extended palette — referenced by v4 modules */
  --surface-2:    #f4f6f9;
  --bg-soft:      #f4f6f9;
  --accent-hover: #b08d52;
  --accent-soft:  #fdf3e3;

  /* ── Radien-Staffel ────────────────────────────────────────────────────
     `--radius: 8px` ist eine Desktop-Größe: bei 13px-Text und dichten
     Tabellen richtig, am Handy aber kantig. Grosse Flächen (Karten,
     schwebende Leisten, Sheets) brauchen mehr Rundung, damit sie als
     eigenständige Objekte gelesen werden statt als beschnittene Kästen.
     Nur die Staffel steht hier — WO sie gilt, entscheiden die Regeln. */
  --radius-lg:   14px;
  --radius-xl:   20px;
  --radius-pill: 999px;

  /* ── Schatten ──────────────────────────────────────────────────────────
     Zwei Ebenen statt einer: `--shadow` (flach, für Karten im Fluss) und
     `--shadow-float` für Elemente, die ÜBER dem Inhalt schweben. Der
     doppelte Schatten (enger Kontaktschatten + weiter Streuschatten) ist
     der Unterschied zwischen „aufgeklebt" und „schwebend". Beide in
     Markenfarbe getönt statt neutralem Schwarz — reines Schwarz wirkt auf
     dem hellgrauen --bg schmutzig. */
  --shadow-card:  0 1px 2px rgba(26,26,46,0.05), 0 4px 14px rgba(26,26,46,0.05);
  --shadow-float: 0 2px 6px rgba(26,26,46,0.07), 0 10px 30px rgba(26,26,46,0.16);

  /* ── Pastellige Status-Flächen ─────────────────────────────────────────
     --success/--danger/--warning sind Signalfarben: richtig für einen
     3px-Punkt oder eine Randlinie, falsch für eine 40px breite Fläche —
     dort schreien sie und lassen die Oberfläche billig wirken. Jede
     Statusfarbe bekommt deshalb ein Paar: `-soft` als Fläche (entsättigt,
     mintgrün / altrosa / sandgelb) und `-ink` als Schrift darauf. Das
     Paar ist so abgestimmt, dass Text auf Fläche > 7:1 Kontrast hat.
     Genutzt von `.status-chip` (weiter unten) und ab jetzt von Modulen,
     statt dort erneut Hex-Werte zu erfinden. */
  --success-soft: #e6f4ec;
  --success-ink:  #1b6b43;
  --danger-soft:  #f8e9e7;
  --danger-ink:   #98301f;
  --warning-soft: #fbf0df;
  --warning-ink:  #8f5510;
  --info-soft:    #e9eff7;
  --info-ink:     #2b4a75;
  --neutral-soft: #eef1f5;
  --neutral-ink:  #4b5563;
  /* Kräftigere Gold-Lasur als --accent-soft. --accent-soft (#fdf3e3) ist so
     hell, dass es auf weissem Grund praktisch verschwindet; für den aktiven
     Chip in der Bottom-Navigation braucht es eine sichtbare Stufe. */
  --accent-tint:  #f3e6cd;

  /* ── Typo-Skala ────────────────────────────────────────────────────────
     Die Basis steht global auf 13px — eine Dichte-Entscheidung für den
     Desktop, wo viel Fläche und ein Mauszeiger da sind. Am Handy fehlt
     dadurch jede Hierarchie: Seitentitel (20px), Abschnitt und Fliesstext
     liegen so eng beieinander, dass alles gleich laut wirkt.
     Vier semantische Stufen statt verstreuter Pixel; die Werte werden im
     Handy-Block (≤767px) angehoben, Desktop bleibt exakt wie bisher. */
  --fs-page-title: 20px;   /* .page-title — die eine Überschrift pro Ansicht */
  --fs-section:    16px;   /* Abschnitts-/Kartenüberschrift, Modal-Titel   */
  --fs-body:       13px;   /* Fliesstext, Tabellenzellen, Buttons         */
  --fs-label:      11px;   /* Feldbeschriftung, Meta, Versalien           */
  --lh-body:       1.5;

  /* ── Nachgezogene Aliase ───────────────────────────────────────────────
     Diese drei wurden von settings/bhbgv/kommunikation/tasks benutzt, waren
     aber NIRGENDS definiert (settings/view.css:61,950 sogar ohne Fallback →
     die Regel fiel dort komplett aus). Hier zentral nachgezogen. */
  --bg-subtle:    #f4f6f9;
  --primary:      #1a1a2e;
  --muted:        #6b7280;

  /* ── Layout-Maße ───────────────────────────────────────────────────────
     Vorher 3× hart als 56px verstreut (shell.css topnav, .update-popover,
     tickets calc()). Ab jetzt eine Quelle. */
  --topnav-h:     56px;
  --bottomnav-h:  56px;
  --panel-pad:    24px;
  /* Abstand der schwebenden Bottom-Navigation zum Bildschirmrand. Eigenes
     Token, weil DREI Dinge denselben Wert kennen müssen: die Leiste selbst,
     der Auslauf jedes Scroll-Containers und alles, was darüber einschwebt
     (Toasts, Update-Hinweis). Vorher war genau diese Kopplung die Quelle der
     beiden „etwas liegt unter der Leiste"-Fehler. */
  --bottomnav-gap: 10px;

  /* ── Touch & Safe-Area ─────────────────────────────────────────────────
     --tap = Apple/Google-Mindestmaß für Touch-Ziele. Vorher 23× hart als
     `min-height:44px` in 7 von 16 Modulen kopiert, in 9 Modulen gar nicht.
     --safe-b/-t = Notch bzw. Home-Indicator; im Projekt bisher 0 Vorkommen,
     für die Bottom-Navigation aber zwingend. */
  --tap:          44px;
  --safe-t:       env(safe-area-inset-top, 0px);
  --safe-b:       env(safe-area-inset-bottom, 0px);
  --safe-l:       env(safe-area-inset-left, 0px);
  --safe-r:       env(safe-area-inset-right, 0px);

  /* ── Abstands-Skala ────────────────────────────────────────────────────
     Ersetzt schrittweise die hart verstreuten 4/8/10/12/16/24px-Werte. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  /* ── z-index-Ebenen ────────────────────────────────────────────────────
     Bisher 10 unkoordinierte Werte zwischen 100 und 10000, quer über
     shell.css und Modul-CSS. Die Tokens bilden die IST-Reihenfolge ab,
     damit neue Overlays sich einordnen können statt zu überbieten. */
  --z-nav:       100;
  --z-dropdown:  200;
  --z-modal:     1000;
  --z-modal-hi:  9000;
  --z-banner:    9200;
  --z-drawer-bd: 9500;
  --z-drawer:    9600;
  --z-toast:     9999;
}

/* ── Mobile-Breakpoints (Referenz) ───────────────────────────────────────
   Projekt-Standard ist die Staffel 1023 / 767 / 479. Sie wird von 7 Modulen
   bereits verwendet (anfrage, bhbgv, dashboard, eigentuemer, help, kunden,
   user); die übrigen 9 nutzten Ad-hoc-Werte zwischen 560 und 1250.
   CSS kennt keine Variablen in Media-Queries — die Werte stehen deshalb
   weiterhin als Zahl in den Queries, aber ab jetzt einheitlich:

     ≤ 479px   Kleines Handy (iPhone SE)
     ≤ 767px   Handy         → Hamburger + Bottom-Nav, Karten statt Tabellen
     ≤ 1023px  Tablet        → gestapelte Split-Views, reduzierte Spalten

   ACHTUNG: 768/769 nicht mehr verwenden. Der Hamburger schaltet bei 767um;
   ein Modul mit `max-width:768px` erzeugt bei exakt 768px CSS-Pixeln
   (iPad hochkant) eine 1px-Zone mit Desktop-Nav und Mobile-Inhalt.
   ──────────────────────────────────────────────────────────────────────── */
body {
  font-family: 'Open Sans', Arial, sans-serif;
  /* Token statt Zahl — löst am Desktop weiterhin 13px auf, wird im
     Handy-Block angehoben. Bewusst KEIN line-height hier: das würde jedes
     Modul am Desktop verändern; die ruhigere Zeile gibt es nur mobil. */
  font-size: var(--fs-body);
  background: var(--bg);
  color: var(--text);
  /* 100dvh statt 100vh: Auf iOS-Safari und Android-Chrome zählt `vh` die
     eingeklappte URL-Leiste NICHT mit — die unteren ~60-100px lagen damit
     außerhalb des sichtbaren Bereichs, und weil hier `overflow:hidden` steht,
     konnte man nicht nachscrollen. `dvh` folgt der dynamischen Höhe.
     Fallback für alte Browser bleibt eine Zeile darüber stehen. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* ── Top Nav ──────────────────────────────────────────────────────────── */
#topnav {
  height: var(--topnav-h);
  background: var(--brand-surface);
  display: flex;
  align-items: center;
  padding: 0 24px;
  gap: 16px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
  flex-shrink: 0;
  z-index: 100;
}
/* Hamburger ist standardmäßig versteckt; erst unter 768px sichtbar. */
#nav-hamburger {
  display: none;
  background: transparent;
  border: 0;
  color: #fff;
  width: 38px;
  height: 38px;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  cursor: pointer;
  padding: 0;
}
#nav-hamburger:hover { background: rgba(255,255,255,0.10); }
@media (max-width: 767px) {
  #nav-hamburger { display: inline-flex; }
  /* Tabs auf Mobile in den Drawer auslagern */
  #topnav .nav-tabs { display: none; }
  #topnav { padding: 0 12px; gap: 10px; }
}
/* Mobile-Drawer (Slide-in von links) */
#mobile-drawer-backdrop {
  display: none;
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: 9500;
}
#mobile-drawer-backdrop.open { display: block; }
#mobile-drawer-backdrop:not([hidden]) { display: block; }
#mobile-drawer {
  display: none;
  position: fixed; top: 0; left: 0;
  width: 280px; max-width: 85vw; height: 100vh; height: 100dvh;
  background: var(--brand-surface);
  color: #fff;
  z-index: 9600;
  flex-direction: column;
  transform: translateX(-100%);
  transition: transform 0.25s ease;
}
#mobile-drawer.open,
#mobile-drawer:not([hidden]) { display: flex; }
#mobile-drawer.open { transform: translateX(0); }
#mobile-drawer .mobile-drawer-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px; border-bottom: 1px solid rgba(255,255,255,0.12);
}
#mobile-drawer .mobile-drawer-title { font-weight: 700; font-size: 14px; }
#mobile-drawer .mobile-drawer-close {
  background: transparent; border: 0; color: #fff;
  font-size: 22px; line-height: 1; cursor: pointer; padding: 4px 8px;
}
#mobile-drawer .mobile-drawer-body { padding: 8px; overflow-y: auto; flex: 1; }
#mobile-drawer .mobile-drawer-tabs { display: flex; flex-direction: column; gap: 4px; }
#mobile-drawer .mobile-drawer-tabs .nav-tab,
#mobile-drawer .mobile-drawer-tabs button {
  text-align: left;
  padding: 10px 14px;
  background: transparent;
  border: 0;
  color: rgba(255,255,255,0.80);
  font-size: 14px;
  font-weight: 600;
  border-radius: 6px;
  cursor: pointer;
  font-family: inherit;
}
#mobile-drawer .mobile-drawer-tabs .nav-tab.active,
#mobile-drawer .mobile-drawer-tabs .nav-tab:hover { background: rgba(255,255,255,0.10); color: #fff; }
#topnav img { height: 28px; }
#topnav .nav-divider { width: 1px; height: 28px; background: rgba(255,255,255,0.15); }
.nav-tabs { display: flex; gap: 4px; margin-left: 8px; }
.nav-tab {
  padding: 6px 16px;
  border-radius: 6px;
  border: none;
  background: transparent;
  color: rgba(255,255,255,0.65);
  font-family: 'Open Sans', sans-serif;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
}
.nav-tab:hover { background: rgba(255,255,255,0.1); color: #fff; }
.nav-tab.active { background: var(--accent); color: #fff; }
.nav-tab-icon {
  padding: 6px 10px;
  margin-right: 4px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.nav-tab-icon svg { opacity: 0.85; }
.nav-tab-icon:hover svg, .nav-tab-icon.active svg { opacity: 1; }

/* ── Nav-Gruppen mit Dropdown (Ticket #141) ───────────────────────────── */
.nav-group { position: relative; display: inline-flex; }
.nav-group-toggle { display: inline-flex; align-items: center; gap: 5px; }
.nav-group-caret { transition: transform 0.2s; opacity: 0.7; }
.nav-group:hover .nav-group-caret,
.nav-group:focus-within .nav-group-caret { transform: rotate(180deg); opacity: 1; }
/* Eltern-Tab leuchtet, wenn ein Gruppen-Mitglied offen ist (auch ≠ target) */
.nav-group.has-active > .nav-group-toggle { background: var(--accent); color: #fff; }
.nav-group-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 190px;
  background: var(--surface);
  border: 1px solid rgba(0,0,0,0.08);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  padding: 6px;
  display: none;
  flex-direction: column;
  gap: 2px;
  z-index: 200;
}
/* kleiner unsichtbarer Brücken-Bereich, damit der Hover zwischen Tab und Menü
   nicht abreißt */
.nav-group-menu::before {
  content: '';
  position: absolute;
  top: -6px; left: 0; right: 0; height: 6px;
}
.nav-group:hover .nav-group-menu,
.nav-group:focus-within .nav-group-menu { display: flex; }
.nav-group-item {
  text-align: left;
  padding: 9px 14px;
  border: 0;
  background: transparent;
  color: var(--brand);
  font-family: 'Open Sans', sans-serif;
  font-size: 13px;
  font-weight: 600;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
}
.nav-group-item:hover { background: rgba(0,0,0,0.05); }
.nav-group-item.active { background: var(--accent); color: #fff; }

/* Mobile-Drawer: Gruppen als Überschrift + eingerückte Kinder.
   Einrückung und Schnitt der Kinder stehen im MOBILE-DRAWER-Block am Ende
   der Datei (Führungslinie + 16px). Das frühere
   `padding-left: 28px !important` ist entfallen: es überstimmte genau jenen
   Block und riss Linie und Text weit auseinander — und ein !important war
   dafür nie nötig, beide Regeln stehen in derselben Datei. */
#mobile-drawer .nav-drawer-group-head { margin-top: 6px; opacity: 0.85; }

#topnav .spacer { flex: 1; }
.db-status {
  font-size: 11px;
  color: rgba(255,255,255,0.5);
  display: flex;
  align-items: center;
  gap: 6px;
}
.db-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--surface-3); }
.db-dot.ok { background: #2ecc71; }
.db-dot.err { background: #e74c3c; }

/* ── Main Layout & Panels ─────────────────────────────────────────────── */
#main { flex: 1; display: flex; overflow: hidden; min-height: 0; }

/* Modul-Container: jedes Modul rendert in einen [data-module-view]-Block.
   Nur das aktive Modul wird gezeigt — alle anderen sind komplett aus dem
   Layout entfernt. Modals der Module bleiben innerhalb dieser Wrapper, aber
   sind position:fixed → werden separat sichtbar. */
[data-module-view] {
  display: none;
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  flex-direction: column;
}
[data-module-view].active { display: flex; }

/* Modul-Panels (das eigentliche Modul-UI innerhalb des Wrappers) */
.panel { display: none; flex: 1; overflow: hidden; min-height: 0; }
.panel.active { display: flex; flex-direction: column; }

/* Sub-Panels innerhalb eines Moduls (z.B. bhbgv: list vs editor) */
.panel-sub { display: none; flex: 1; overflow: hidden; min-height: 0; }
.panel-sub.active { display: flex; flex-direction: column; }

/* ── Common headings & buttons ────────────────────────────────────────── */
.page-header { display: flex; align-items: center; justify-content: space-between; }
.page-title { font-size: var(--fs-page-title); font-weight: 700; color: var(--brand); }
.page-title span { color: inherit; }

.btn { display: inline-flex; align-items: center; gap: 6px; padding: 9px 18px; border: none; border-radius: var(--radius); font-family: 'Open Sans', sans-serif; font-size: var(--fs-body); font-weight: 600; cursor: pointer; transition: all 0.18s; text-decoration: none; }
.btn-primary { background: var(--accent); color: #fff; }
.btn-primary:hover { background: var(--accent2); }
.btn-outline { background: var(--surface); color: var(--brand); border: 1.5px solid var(--border); }
.btn-outline:hover { border-color: var(--accent); color: var(--accent); }
.btn-danger { background: var(--danger-soft); color: var(--danger); border: 1.5px solid var(--danger-line); }
.btn-danger:hover { background: var(--danger); color: #fff; }
.btn-sm { padding: 5px 12px; font-size: 12px; }
.btn-success { background: var(--success-soft); color: var(--success); border: 1.5px solid var(--success-line); }
.btn-success:hover { background: var(--success); color: #fff; }
.btn-warn { background: var(--danger); color: #fff; border: none; }
.btn-warn:hover { background: #a52a1c; }

/* ── Filter bar / pills ───────────────────────────────────────────────── */
.filter-bar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.filter-bar input[type=text] {
  padding: 8px 12px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  font-family: 'Open Sans', sans-serif;
  font-size: 13px;
  flex: 1 1 220px;
  min-width: 180px;
  max-width: 320px;
}
@media (max-width: 600px) {
  .filter-bar input[type=text] { max-width: none; width: 100%; }
}
.filter-bar input:focus { outline: none; border-color: var(--accent); }
.filter-pills { display: flex; gap: 6px; }
.pill {
  padding: 5px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  border: 1.5px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  transition: all 0.15s;
}
.pill.active { background: var(--brand-surface); color: #fff; border-color: var(--brand); }

/* ── Empty state ──────────────────────────────────────────────────────── */
.empty-state {
  text-align: center;
  padding: 64px 32px;
  color: var(--text-muted);
}
.empty-state svg { opacity: 0.3; margin-bottom: 16px; }
.empty-state h3 { font-size: var(--fs-section); margin-bottom: 8px; }

/* ── Form field base (genutzt von bhbgv, kunden, settings) ────────────── */
.field { margin-bottom: 14px; }
.field label {
  display: block;
  font-size: var(--fs-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  margin-bottom: 5px;
}
.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 8px 12px;
  border: 1.5px solid var(--border);
  border-radius: var(--radius);
  font-family: 'Open Sans', sans-serif;
  font-size: 13px;
  background: var(--surface);
  color: var(--text);
}
.field input:focus,
.field select:focus,
.field textarea:focus { outline: none; border-color: var(--accent); }
.field textarea { resize: vertical; min-height: 60px; }
.field-invalid input,
.field-invalid select,
.field-invalid textarea {
  border-color: var(--danger);
  background: var(--danger-soft);
}
.section-title {
  font-size: var(--fs-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--accent);
  margin: 18px 0 10px;
  border-bottom: 1.5px solid var(--border);
  padding-bottom: 6px;
}
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.row2 .field { margin-bottom: 0; }
.toggle-row { display: flex; align-items: center; gap: 8px; padding: 6px 0; }
.toggle { position: relative; width: 36px; height: 20px; cursor: pointer; }
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle .slider {
  position: absolute; inset: 0;
  background: var(--border);
  border-radius: 20px;
  transition: 0.2s;
}
.toggle .slider::before {
  content: ''; position: absolute;
  width: 16px; height: 16px;
  left: 2px; top: 2px;
  background: var(--surface); border-radius: 50%;
  transition: 0.2s;
}
.toggle input:checked + .slider { background: var(--accent); }
.toggle input:checked + .slider::before { transform: translateX(16px); }
.toggle-row label { font-size: 12px; color: var(--text); cursor: pointer; }

/* ── Globale Safety: HTML-Attribut `hidden` muss IMMER display:none erzwingen,
   sonst überschreiben spezifischere Modul-CSS-Regeln (z.B. display:flex auf
   einer Modal-Klasse) das Attribut, und Modals bleiben dauerhaft sichtbar.
   !important hier ist gerechtfertigt: hidden ist die HTML-Spec-Vorgabe. ── */
[hidden] { display: none !important; }

/* ── Modal base (geteilt von allen Modulen) ───────────────────────────── */
/* Bestehende Module (kunden, bhbgv, rechnungen, settings, ...) verwenden
   das `.open`-Klassen-Pattern für Sichtbarkeit. Eine globale
   `.modal-overlay:not([hidden])`-Regel würde diese Modals ungewollt zeigen,
   wenn sie nur eine `.open`-Klasse haben aber kein hidden-Attribut tragen. */
.modal-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 1000;
  align-items: center;
  justify-content: center;
  padding: 16px;
  overflow-y: auto;
}
.modal-overlay.open { display: flex; }
.modal {
  background: var(--surface);
  border-radius: 10px;
  padding: 28px;
  width: 440px;
  max-width: 90vw;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: 0 8px 40px rgba(0,0,0,0.2);
}
.modal h2 { font-size: var(--fs-section); font-weight: 700; margin-bottom: 6px; }
.modal p { font-size: var(--fs-body); color: var(--text-muted); margin-bottom: 20px; }
.modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
.modal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.modal-grid .field { margin-bottom: 0; }
.modal-grid .field.full { grid-column: 1 / -1; }

/* ── Detail-Modal Generika (genutzt von bhbgv und kunden) ─────────────── */
.detail-header {
  background: var(--brand-surface);
  color: #fff;
  padding: 16px 22px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
}
.detail-header-title { font-size: 15px; font-weight: 700; flex: 1; }
.detail-header-title span { font-weight: 300; opacity: 0.7; font-size: 12px; margin-left: 6px; }
.detail-header-close {
  background: none; border: none; color: rgba(255,255,255,0.6);
  font-size: 22px; cursor: pointer; line-height: 1; padding: 0 4px;
}
.detail-header-close:hover { color: #fff; }
.detail-body { display: flex; flex: 1; overflow: hidden; }
.detail-left {
  flex: 1;
  overflow-y: auto;
  padding: 22px 24px;
  border-right: 1px solid var(--border);
}
.detail-right {
  width: 300px;
  min-width: 260px;
  display: flex;
  flex-direction: column;
  background: var(--surface-2);
}
.detail-right-header {
  padding: 14px 16px 8px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--accent);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.kommentar-feed {
  flex: 1;
  overflow-y: auto;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.kommentar-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 9px 12px;
}
.kommentar-meta {
  font-size: 10.5px;
  color: var(--text-muted);
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  gap: 6px;
}
.kommentar-meta strong { color: var(--brand); }
.kommentar-meta .kommentar-del-btn {
  margin-left: auto;
  background: transparent;
  border: 0;
  font-size: 16px;
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0 4px;
  border-radius: 3px;
}
.kommentar-meta .kommentar-del-btn:hover { color: var(--danger, var(--danger-ink)); background: var(--danger-soft); }
.kommentar-text { font-size: 12.5px; line-height: 1.6; }
.kommentar-form {
  padding: 12px 14px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
}
.kommentar-form input {
  width: 100%;
  padding: 6px 9px;
  border: 1.5px solid var(--border);
  border-radius: 6px;
  font-family: 'Open Sans', sans-serif;
  font-size: 12px;
  margin-bottom: 6px;
}
.kommentar-form textarea {
  width: 100%;
  padding: 7px 9px;
  border: 1.5px solid var(--border);
  border-radius: 6px;
  font-family: 'Open Sans', sans-serif;
  font-size: 12px;
  resize: vertical;
  min-height: 64px;
  margin-bottom: 7px;
}
.kommentar-form input:focus,
.kommentar-form textarea:focus { outline: none; border-color: var(--accent); }
.detail-footer {
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  background: var(--surface);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  flex-wrap: wrap;
}
.detail-footer .spacer { flex: 1; }
.detail-status-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--text-muted);
}
.detail-status-row select {
  padding: 6px 10px;
  border: 1.5px solid var(--border);
  border-radius: 6px;
  font-family: 'Open Sans', sans-serif;
  font-size: 12px;
  background: var(--surface-2);
}
.detail-info-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 20px;
  margin-bottom: 16px;
}
.detail-info-item { font-size: 12px; }
.detail-info-item .lbl { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-muted); margin-bottom: 2px; }
.detail-info-item .val { color: var(--text); }
.pdf-version-list { display: flex; flex-direction: column; gap: 5px; margin-bottom: 6px; }
.pdf-version-item { display: flex; align-items: center; gap: 8px; font-size: 11px; }
.pdf-version-item a { color: var(--accent); text-decoration: none; }
.pdf-version-item a:hover { text-decoration: underline; }
.pdf-version-meta { color: var(--text-muted); font-size: 10px; }
.pdf-original-tag { font-size: 9.5px; color: var(--text-muted); background: var(--surface-3); border: 1px solid var(--border); border-radius: 3px; padding: 1px 5px; }
.detail-section-title {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--accent);
  border-bottom: 1.5px solid var(--border);
  padding-bottom: 5px;
  margin: 18px 0 12px;
}
.detail-total-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--brand-surface);
  color: #fff;
  border-radius: 6px;
  padding: 10px 16px;
  margin-top: 12px;
}
.detail-total-row .lbl { font-size: 11px; opacity: 0.75; }
.detail-total-row .val { font-size: 17px; font-weight: 700; }

/* ── Toast (Legacy #toast, behalten falls noch wo referenziert) ───────── */
#toast {
  position: fixed;
  bottom: 24px;
  right: 24px;
  background: var(--brand-surface);
  color: #fff;
  padding: 12px 20px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: 0 4px 20px rgba(0,0,0,0.2);
  transform: translateY(80px);
  opacity: 0;
  transition: all 0.3s;
  z-index: 2000;
}
#toast.show { transform: translateY(0); opacity: 1; }
#toast.success { background: var(--success); }
#toast.error   { background: var(--danger); }

/* ── Toast-Stack (Queue, neuestes oben, 15s pro Eintrag) ──────────────── */
#toast-stack {
  position: fixed;
  bottom: 20px;
  right: 20px;
  display: flex;
  flex-direction: column-reverse;   /* neueste Items nach OBEN stapeln */
  gap: 8px;
  z-index: 9999;
  max-width: min(420px, calc(100vw - 40px));
  pointer-events: none;             /* Stack-Container blockt nicht */
}
.toast-item {
  pointer-events: none;       /* Toasts blockieren keinen Click und sind nicht schließbar */
  display: flex;
  align-items: flex-start;
  gap: 10px;
  background: var(--brand-surface);
  color: #fff;
  padding: 11px 14px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  box-shadow: 0 4px 20px rgba(0,0,0,0.18);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.25s ease, transform 0.25s ease;
  cursor: default;
  max-width: 100%;
  word-wrap: break-word;
}
.toast-item.is-visible { opacity: 1; transform: translateY(0); pointer-events: auto; }
.toast-item.is-leaving { opacity: 0; transform: translateY(-8px); }
.toast-item.success { background: var(--success, #16a34a); }
.toast-item.error   { background: var(--danger,  #dc2626); }
.toast-item.warning { background: #d97706; }
.toast-item.info    { background: #2563eb; }
.toast-item .toast-text { flex: 1; min-width: 0; }
.toast-item .toast-copy {
  pointer-events: auto;
  background: transparent;
  border: 1px solid rgba(255,255,255,0.35);
  color: #fff;
  padding: 3px 6px;
  border-radius: 4px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  margin-top: -1px;
  transition: background 0.15s;
}
.toast-item .toast-copy:hover { background: rgba(255,255,255,0.18); }
.toast-item .toast-copy svg { display: block; }
.toast-item .toast-close {
  pointer-events: auto;
  background: transparent;
  border: 0;
  color: rgba(255,255,255,0.85);
  font-size: 18px;
  line-height: 1;
  padding: 0 0 0 6px;
  cursor: pointer;
  flex: 0 0 auto;
}
.toast-item .toast-close:hover { color: #fff; }
@media (max-width: 600px) {
  #toast-stack { left: 12px; right: 12px; max-width: none; }
}

/* ── WYSIWYG-Editor (modul-übergreifend) ────────────────────────────────
   Genutzt von bhbgv (Vertrag-Edit-Toolbar #wysiwyg-bar, Email-Modal,
   Baustein-Modal) und settings (E-Mail-Vorlage, Vorlage-Bausteine).
   Hier zentralisiert, damit Klassen konsistent sind und auch dann
   greifen, wenn ein Modul ohne bhbgv-CSS geladen wird.
──────────────────────────────────────────────────────────────────────── */
#wysiwyg-bar {
  display: none;
  max-width: 780px;
  margin: 0 auto 12px;
  background: var(--surface);
  border: 1.5px solid #e67e22;
  border-radius: 8px;
  padding: 8px 12px;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  box-shadow: 0 2px 8px rgba(230,126,34,0.15);
}
#wysiwyg-bar.visible { display: flex; }
.wysiwyg-hint {
  font-size: 11px;
  color: var(--warning-ink);
  font-weight: 600;
  margin-right: 8px;
  white-space: nowrap;
}
.wysiwyg-sep { width: 1px; height: 22px; background: var(--border); margin: 0 4px; }
.wb {
  width: 30px; height: 30px;
  border: none;
  border-radius: 5px;
  background: transparent;
  font-family: 'Open Sans', sans-serif;
  font-size: 13px;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s;
  color: var(--text);
}
.wb:hover { background: var(--surface-3); }
.wb.active { background: var(--brand-surface); color: #fff; }
.wb-bold { font-weight: 700; }
.wb-italic { font-style: italic; }
.wb-underline { text-decoration: underline; }
#wb-fontsize {
  padding: 3px 6px;
  border: 1.5px solid var(--border);
  border-radius: 5px;
  font-family: 'Open Sans', sans-serif;
  font-size: 12px;
  background: var(--surface-2);
  color: var(--text);
  cursor: pointer;
}
#wb-color {
  width: 30px; height: 30px;
  border: none; border-radius: 5px;
  padding: 2px;
  cursor: pointer;
  background: transparent;
}

/* Editor-Toolbar (settings/bhbgv-Baustein-Modal) + contenteditable Editor */
.settings-editor-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 2px;
  padding: 6px 8px;
  background: var(--surface-2);
  border: 1.5px solid var(--border);
  border-bottom: none;
  border-radius: 6px 6px 0 0;
}
.settings-editor {
  min-height: 280px;
  max-height: 480px;
  overflow-y: auto;
  padding: 12px 14px;
  border: 1.5px solid var(--border);
  border-radius: 0 0 6px 6px;
  background: var(--surface);
  font-size: 13px;
  line-height: 1.55;
  outline: none;
}
.settings-editor:focus { border-color: var(--accent); }

/* ── Profile-Menu (Welle 9) ───────────────────────────────────────────── */
.profile-menu-wrap {
  position: relative;
  display: flex;
  align-items: center;
  margin-left: 4px;
}
.profile-menu-wrap[hidden] { display: none; }

.profile-menu-btn {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  border: none;
  background: var(--accent);
  color: #fff;
  font-family: 'Open Sans', sans-serif;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  transition: background 0.18s, box-shadow 0.18s, transform 0.1s;
  line-height: 1;
}
.profile-menu-btn:hover { background: var(--accent2); }
.profile-menu-btn:focus-visible {
  outline: 2px solid var(--border);
  outline-offset: 2px;
}
.profile-menu-btn:active { transform: scale(0.96); }
.profile-menu-btn-icon { background: rgba(255,255,255,0.15); }
.profile-menu-btn-icon:hover { background: rgba(255,255,255,0.25); }
.profile-menu-btn-text { pointer-events: none; }

.profile-menu-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 200px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  padding: 6px 0;
  z-index: 1000;
}
.profile-menu-dropdown[hidden] { display: none; }

.profile-menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 14px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-family: 'Open Sans', sans-serif;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  text-align: left;
  transition: background 0.12s;
}
.profile-menu-item:hover,
.profile-menu-item:focus-visible {
  background: rgba(200, 169, 110, 0.10);
  outline: none;
}
.profile-menu-item-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  color: var(--text-muted);
  flex-shrink: 0;
}
.profile-menu-item:hover .profile-menu-item-icon { color: var(--accent); }

.profile-menu-divider {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}

/* Breakpoint war 720px — ein Ad-hoc-Wert, der zwischen 721 und 767px eine
   Zone erzeugte, in der der Topnav bereits im Handy-Modus stand, der
   Profil-Kreis aber noch in Desktop-Grösse. Jetzt auf der Projekt-Staffel. */
@media (max-width: 767px) {
  .profile-menu-btn {
    width: 32px;
    height: 32px;
    font-size: 11px;
  }
  .profile-menu-dropdown {
    min-width: 220px;
    right: -4px;
  }
}

/* ── Topbar Update-Warning Icon + Popover ─────────────────────────────────
   Sichtbar nur wenn User Permission `system.updates` hat UND ein Update
   für die Stripe-PHP-Library verfügbar ist. Position links vom DB-Status.
   Gesteuert in core/app.js via initUpdateWarning() / toggleUpdatePopover().
──────────────────────────────────────────────────────────────────────── */
.topbar-update-warning {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 6px 8px;
  border: none;
  background: transparent;
  color: var(--danger-ink);
  cursor: pointer;
  border-radius: 6px;
  transition: background 0.18s, color 0.18s;
  line-height: 1;
}
.topbar-update-warning:hover {
  background: rgba(220, 38, 38, 0.08);
}
.topbar-update-warning[hidden] {
  display: none;
}
@keyframes update-pulse {
  0%   { box-shadow: none;                                  color: var(--danger-ink); }
  50%  { box-shadow: 0 0 0 6px rgba(220, 38, 38, 0.25);     color: var(--danger-ink); }
  100% { box-shadow: none;                                  color: var(--danger-ink); }
}
.topbar-update-warning.is-pulsing {
  animation: update-pulse 1.2s ease-in-out infinite;
}

.update-popover {
  position: fixed;
  top: var(--topnav-h);
  right: 16px;
  z-index: 9999;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
  min-width: 300px;
  /* Nie breiter als der Viewport abzüglich der 16px Rand je Seite — sonst
     liefe das Popover auf sehr schmalen Handys links aus dem Bild. */
  min-width: min(300px, calc(100vw - 32px));
  max-width: min(400px, calc(100vw - 32px));
  padding: 0;
}
.update-popover[hidden] {
  display: none;
}
.update-popover-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
}
.update-popover-close {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font-size: 20px;
  line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
}
.update-popover-close:hover {
  color: var(--text);
}
.update-popover-body {
  padding: 14px 16px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text);
}
.update-popover-body code {
  background: var(--surface-2);
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 12px;
}
/* Direkt-Installation: Optik kommt von .btn/.btn-primary, hier nur Maße
   (volle Breite, Touch-Ziel ≥ 44px) und der gesperrte Ladezustand. */
.update-popover-body .update-install-btn {
  width: 100%;
  min-height: 44px;
  margin-top: 12px;
  justify-content: center;
}
.update-popover-body .update-install-btn:disabled {
  opacity: 0.65;
  cursor: progress;
}
.update-popover-body .update-install-error {
  margin-top: 8px;
  color: var(--danger-ink);
  font-size: 12px;
  overflow-wrap: anywhere;
}
.update-popover-body .update-install-error[hidden] {
  display: none;
}
.update-popover-body .update-install-ok {
  margin-top: 12px;
  color: var(--success-ink);
  font-weight: 600;
}
/* Dezenter Nebenlink (Release-Notes) — klein, aber mit 44px Tipphöhe. */
.update-popover-body .update-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: 4px;
  font-size: 12px;
  color: var(--text-muted);
  text-decoration: none;
}
.update-popover-body .update-link:hover {
  text-decoration: underline;
}

/* ── Environment-Rahmen (Test/Staging-Markierung) ──────────────────────────
   Wird von core/app.js bei boot() aktiviert, wenn die Backend-Route
   ?core=env != 'live' liefert. Default hidden via [hidden]-Attribut → Live
   bleibt komplett ohne Markierung.

   WARUM Rahmen statt Balken: Der frühere rote Balken oben kostete Höhe
   (am Handy inkl. Safe-Area bis zu ein Sechstel des Schirms), schob Topnav
   und Layout nach unten und brauchte dafür Sonderregeln. Ein leuchtender
   Rahmen um das ganze Fenster ist genauso unübersehbar, liegt aber als
   Overlay ÜBER der Seite und verbraucht 0px Layout-Fläche.

   • position:fixed + inset:0 → nimmt das Element aus dem Flex-Fluss des
     body, Topnav/Seitenleiste/Modals rechnen nichts mehr ein.
   • pointer-events:none → Klicks/Taps gehen IMMER durch, auch am Rand und
     auf dem Label (sonst wären Randbuttons und Scrollbars blockiert).
   • z-index über allem (Modals 1000/9000, Update-Popover 9999), damit der
     Rahmen auch bei offenem Dialog sichtbar bleibt — Fehlklick auf Live
     verhindern ist der ganze Zweck.
   • Safe-Area: Der Rahmen liegt bewusst an den physischen Bildschirm-
     kanten (auch hinter der Notch); er ist nur 2px + weicher Schein und
     verdeckt keine Inhalte. Das Label wird dagegen unter --safe-t gesetzt,
     damit es nicht unter Uhrzeit/Dynamic Island verschwindet.
   • Kein Pulsieren: eine Dauer-Animation eines vollflächigen box-shadow
     würde am Handy ständig neu zeichnen (Akku) — statisch reicht. */
.env-banner {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 2147483000;
  --env-rgb: 192, 57, 43;   /* Default rot; Staging überschreibt */
  box-shadow:
    inset 0 0 0 2px rgba(var(--env-rgb), .8),
    inset 0 0 18px rgba(var(--env-rgb), .35);
}
.env-banner[hidden] { display: none; }
.env-banner.env-test    { --env-rgb: 192, 57, 43; }   /* angenehmes Rot */
.env-banner.env-staging { --env-rgb: 211, 156, 51; }  /* gedämpftes Gelb-Orange */

/* Winziges Label oben mittig: sagt WAS markiert ist, ohne Fläche zu
   belegen. Halbtransparent, damit darunterliegende Topnav-Inhalte lesbar
   bleiben; pointer-events erbt `none` vom Rahmen. */
.env-banner-label {
  position: absolute;
  top: var(--safe-t);
  left: 50%;
  transform: translateX(-50%);
  padding: 1px 8px 2px;
  border-radius: 0 0 6px 6px;
  background: rgba(var(--env-rgb), .75);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: .14em;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: .85;
}
.env-banner.env-staging .env-banner-label { color: #1f1f1f; }
@media print {
  .env-banner { display: none !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE-FUNDAMENT  (v10.49.0)
   ─────────────────────────────────────────────────────────────────────────
   Zentrale Regeln für Handy-Bedienbarkeit. Ersetzt Blocker, die vorher in
   bis zu 16 Modulen einzeln (oder gar nicht) behandelt wurden.

   WICHTIG — Spezifität: Modul-CSS wird NACH shell.css geladen (core/app.js
   hängt alle modules/<id>/view.css als <link> ein). Eine Regel `.field input`
   hier würde deshalb von einer gleichnamigen Modul-Regel überstimmt. Die
   Regeln unten sind darum mit `body` geprefixt (Spezifität 0,1,2 statt
   0,1,1) — sie gewinnen gegen Modul-CSS, ohne dass `!important` nötig ist.
   Module, die bereits korrekt mit `!important` arbeiten (bhbgv, eigentuemer),
   behalten Vorrang — das ist gewollt, deren Werte stimmen.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Scroll-Fallback ──────────────────────────────────────────────────
   `[data-module-view]` und `.panel` stehen auf `overflow:hidden`; Scrollen
   muss jedes Modul selbst herstellen. Nur 7 von 16 tun das. Am Desktop fällt
   das kaum auf, am Handy ist der Inhalt IMMER länger als der Viewport —
   alles unterhalb der Faltung war schlicht nicht erreichbar (betraf tasks,
   tickets, objekte, buchungen, kalender, reinigung). */
@media (max-width: 767px) {
  body [data-module-view].active,
  body .panel.active {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }
}

/* ── 2. Touch-Ziele ──────────────────────────────────────────────────────
   `pointer:coarse` trifft Finger-Bedienung geräteunabhängig (auch Touch-
   Laptops), nicht nur schmale Viewports. Ersetzt 23 handkopierte
   `min-height:44px` und fixt die 9 Module, die es nie hatten. */
@media (pointer: coarse) {
  body .btn,
  body .btn-sm,
  body .wb,
  body .icon-btn,
  body .pill,
  body .nav-tab,
  body .detail-header-close,
  body .mobile-drawer-close {
    min-height: var(--tap);
    min-width: var(--tap);
  }
  /* Der Schalter selbst bleibt optisch 36×20, bekommt aber eine unsichtbare
     Trefferfläche über das umgebende .toggle-row. */
  body .toggle-row { min-height: var(--tap); }
  body .btn-sm { padding: 8px 14px; }
}

/* ── 3. Formulare & iOS-Auto-Zoom ────────────────────────────────────────
   iOS Safari zoomt beim Fokus in jedes Feld mit Schriftgröße < 16px. Die
   Basis steht auf 13px, also zoomte JEDES Formular im Projekt. Fünf Module
   patchten das lokal (teils mit !important), elf nicht.

   WARUM DIESE REGEL AUSNAHMSWEISE `!important` TRÄGT:
   Der erste Anlauf zählte Feldtypen einzeln auf und verliess sich auf den
   `body`-Prefix (Spezifität 0,1,2). Das reichte nicht: Modul-CSS lädt NACH
   shell.css, und eine Modul-Regel mit gleicher Spezifität gewinnt den
   Gleichstand allein durch die Reihenfolge. In `settings` zoomte iOS
   deshalb bei jedem Bereichswechsel hinein (Sidebar-Dropdown auf 13px), in
   `buchungen` ebenso — beide mussten es lokal nachziehen. Bei 16 Modulen
   ist das kein Einzelfall, sondern ein Konstruktionsfehler.

   16px ist hier keine Gestaltungsentscheidung, über die ein Modul anders
   urteilen dürfte, sondern die Schwelle, ab der ein Betriebssystem AUFHÖRT,
   ungefragt in die Seite zu zoomen. Der Zoom bleibt danach stehen, die
   Ansicht ist verrutscht, und der Nutzer muss von Hand herauszoomen — ein
   Bedienfehler, kein Geschmacksthema. Genau dafür ist `!important` da.

   Bewusst NUR auf `font-size`: `min-height` ist eine Layout-Entscheidung,
   die ein Modul begründet anders treffen darf, und bleibt überstimmbar.

   Die :not()-Kette statt einer Typ-Liste, weil die alte Aufzählung
   `url`, `time`, `datetime-local`, `month`, `week` und `<input>` ganz ohne
   type-Attribut übersah — alles Felder, die iOS ebenfalls zoomt. Ausgenommen
   sind nur Eingaben ohne Textcursor (Schalter, Regler, Knöpfe): dort gibt
   es nichts zu zoomen, und 16px würde ihre Grösse verändern. */
@media (max-width: 767px) {
  body input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=color]):not([type=file]):not([type=submit]):not([type=button]):not([type=reset]):not([type=image]):not([type=hidden]),
  body select,
  body textarea {
    font-size: 16px !important;
  }
  body .field input,
  body .field select,
  body .field textarea,
  body .filter-bar input[type=text],
  body input[type=text],
  body input[type=email],
  body input[type=tel],
  body input[type=number],
  body input[type=date],
  body input[type=search],
  body input[type=password],
  body select,
  body textarea {
    min-height: var(--tap);
  }
  body textarea { min-height: 88px; }
  /* Zweispaltige Formularzeilen stapeln */
  body .row2,
  body .modal-grid { grid-template-columns: 1fr; }
}

/* ── 4. Modals als Vollbild ──────────────────────────────────────────────
   `.modal` stand auf `width:440px; padding:28px` — auf einem 375px-Display
   blieben 281px nutzbare Breite. Unter 600px wird das Modal zur Vollseite.
   `.modal-fullscreen-mobile` ist die Opt-in-Klasse für die ~12 Modals, die
   eigene Klassen statt `.modal` verwenden (bu-detail-modal, tickets-modal,
   rechnungen-modal, …). */
@media (max-width: 600px) {
  body .modal-overlay { padding: 0; align-items: stretch; }
  body .modal,
  body .modal-fullscreen-mobile {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    height: 100dvh;
    max-height: 100dvh;
    border-radius: 0;
    padding: var(--space-4);
    /* Safe-Area OBEN (Ticket #169): Ein Vollbild-Modal beginnt bei y=0, also
       in der installierten PWA UNTER der iOS-Statusleiste (~47–59px). Ohne
       diesen Abstand lagen Titel und ✕ jedes generischen Modals hinter
       Uhrzeit/Akku — „oberste Leiste nicht sichtbar". Module, die das
       Padding per ID auf 0 setzen und einen eigenen dunklen Kopf haben,
       tragen --safe-t dort selbst (bhbgv #detail-modal macht es vor). */
    padding-top: calc(var(--space-4) + var(--safe-t));
    padding-bottom: calc(var(--space-4) + var(--safe-b));
    overflow-y: auto;
  }
  /* Aktionsleiste bleibt am unteren Rand erreichbar, statt ans Ende eines
     langen Formulars zu rutschen. */
  body .modal .modal-actions {
    position: sticky;
    bottom: 0;
    background: var(--surface);
    padding: var(--space-3) 0 0;
    margin-top: var(--space-4);
    border-top: 1px solid var(--border);
  }
  body .modal .modal-actions .btn { flex: 1; justify-content: center; }
}

/* ── 5. Detail-Ansichten stapeln ─────────────────────────────────────────
   `.detail-right` stand auf `width:300px; min-width:260px` neben einem
   `flex:1`-Geschwister. In einem 96vw-Modal auf 375px blieben der linken
   Spalte ~90px. Betraf buchungen, tickets, tasks (bhbgv/kunden lösten es
   selbst per ID-Selektor). */
@media (max-width: 1023px) {
  body .detail-body { flex-direction: column; overflow-y: auto; }
  body .detail-left {
    border-right: 0;
    border-bottom: 1px solid var(--border);
    overflow-y: visible;
    padding: var(--space-4);
  }
  body .detail-right {
    width: 100%;
    min-width: 0;
    flex-shrink: 0;
  }
}

/* ── 6. Datentabellen: Basis ─────────────────────────────────────────────
   shell.css hatte urspruenglich KEINEN Tabellen-Style; 31 eigene Klassen in
   den Modulen, davon vier mit eigenem Karten-Umbau und der Rest mit
   horizontalem Wischen.

   Verwendung im Modul:
     1. Der <table> zusaetzlich die Klasse `data-table` geben.
     2. Im Renderer jedem <td> ein `data-label="Spaltenname"` mitgeben.

   Das Karten-Layout fuer das Handy steht geschlossen im Block
   „LISTEN AM HANDY" am Dateiende — bewusst an EINER Stelle. Hier stand
   frueher ein zweiter, konkurrierender Satz Regeln; wo beide auf dieselbe
   Zelle trafen, fielen Werte aus der Anzeige. */
.data-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

@media (max-width: 767px) {
  body .data-table-wrap {
    overflow-x: hidden;      /* im Kartenmodus gibt es nichts zu wischen */
    max-width: 100%;
  }
  body .data-table {
    display: block;
    width: 100%;
    max-width: 100%;
    min-width: 0;          /* hebt .rein-table{min-width:760px} u.ae. auf */
    border-collapse: separate;
    table-layout: auto;
  }
  body .data-table thead { display: none; }
  body .data-table thead th { position: static; }
  body .data-table tbody { display: block; width: 100%; max-width: 100%; }
}

/* ── 7. Scroll-Sperre hinter dem Drawer ──────────────────────────────────
   core/app.js setzt seit jeher `body.drawer-open` — es gab dafür nur nie
   eine CSS-Regel. Ohne sie scrollt der Inhalt unter dem offenen Menü mit. */
body.drawer-open { overflow: hidden; }
body.drawer-open #main { overflow: hidden; }

/* ── 7b. Body-Sperre für Overlays (App.scrollLock) ───────────────────────
   `overflow:hidden` allein reicht auf iOS nicht: Ein Wisch auf einer nicht
   scrollbaren Fläche des Overlays (Kopf, Fußleiste, Rand) wandert an die
   Seite dahinter weiter — daher „im Hintergrund scrollt es, aber nicht das
   Vordergründige". `App.scrollLock(true)` setzt zusätzlich
   `position:fixed; top:-<scrollY>px` per Style-Attribut; hier stehen nur die
   Eigenschaften, die keine gemerkte Zahl brauchen.
   `overscroll-behavior:none` nimmt dem Body zusätzlich das Gummiband.
   Bewusst KEIN `touch-action` auf dem Body: das wäre der Schnitt über die
   ganze Seite und nähme dem Anwender das Aufziehen der Vertragsvorschau. */
body.scroll-gesperrt {
  overflow: hidden;
  overscroll-behavior: none;
}

/* ── 8. Panel-Innenabstand ───────────────────────────────────────────────
   Sechs Module setzten Padding selbst und reduzierten es im eigenen
   Mobile-Block auf denselben Wert. Ab jetzt eine Variable. */
@media (max-width: 767px) {
  :root { --panel-pad: 14px; }
}
@media (max-width: 479px) {
  :root { --panel-pad: 10px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   BOTTOM-NAVIGATION  (nur Handy) — schwebende helle Leiste
   ─────────────────────────────────────────────────────────────────────────
   Gerendert von core/app.js → renderBottomNav() aus derselben Datenquelle
   wie Topnav und Drawer.

   WARUM DER UMBAU:
   Vorher war die Leiste ein dunkler Kasten über die volle Breite, der bündig
   am Bildschirmrand klebte, und der aktive Eintrag unterschied sich nur
   durch die Schriftfarbe (dünnes Gold auf Marineblau). Drei Probleme:
     1. Zwei schwere dunkle Balken (Topnav + Bottomnav) klammerten den
        Inhalt ein — das Fenster für den eigentlichen Inhalt wirkte eng.
     2. Farbe allein ist ein schwacher Aktiv-Zustand. Gold auf Dunkelblau
        hat wenig Kontrast, bei Sonne draussen sieht man ihn kaum.
     3. Rand an Rand ohne Rundung liest sich als Systemleiste, nicht als
        Teil der App.

   JETZT: eine helle Pille, die mit Abstand zum Rand über dem Inhalt liegt,
   und ein aktiver Eintrag mit eigener Fläche (Chip) statt nur eingefärbter
   Schrift. Zwischen Pille und Inhalt bleibt der Seitenhintergrund sichtbar
   — genau das lässt sie als eigene Schicht lesen statt als angewachsenen
   Bildschirmrand.

   ACHTUNG BEIM ÄNDERN — hier gab es zweimal denselben Fehler:
   Die Leiste ist `position:fixed`, verkürzt #main also NICHT von selbst.
   Der Platz dafür wird an EINER Stelle reserviert — `#main` im Block
   „KORREKTUR v10.51.3" — und von Toast und Update-Hinweis mit derselben
   Summe nachvollzogen:
     --bottomnav-h + --bottomnav-gap + --safe-b.
   Wer hier eine Höhe ändert, muss dort nichts anfassen: die Tokens
   erledigen das. Wer die Reservierung in die Module zurückverlegt, baut
   den Fehler neu — die Begründung steht in jenem Block.
   ══════════════════════════════════════════════════════════════════════════ */
#bottomnav { display: none; }

@media (max-width: 767px) {
  /* Vor der Anmeldung setzt renderBottomNav() `hidden` — die UA-Regel dafür
     hat aber weniger Gewicht als `body #bottomnav` weiter unten. Ohne diese
     Zeile stünde am Login eine Leiste mit einem einsamen „Mehr". */
  body #bottomnav[hidden] { display: none; }

  body #bottomnav {
    display: flex;
    align-items: stretch;
    /* Schwebend: seitlich eingerückt, über der Home-Indicator-Zone. Die
       Safe-Area-Insets stecken IM Abstand, nicht im Padding — sonst wäre die
       Pille im Querformat unter dem Notch abgeschnitten. */
    position: fixed;
    /* BUENDIG UNTEN, nicht schwebend.
       Die schwebende Pille sah am Simulator gut aus, weil dort
       env(safe-area-inset-bottom) = 0 ist. Auf einem iPhone mit
       Home-Indicator kommen ~34px dazu: Zusammen mit dem 10px-Abstand
       entstand ein ~44px hoher heller Streifen unter der Leiste, den der
       Auftraggeber dreimal als Fehler gemeldet hat. Zu Recht — auf einem
       echten Geraet geht die Rechnung nicht auf.
       Jetzt sitzt die Leiste am physischen Rand und faerbt die
       Home-Indicator-Zone selbst ein. Die Rundung bleibt oben erhalten,
       damit sie weiter als eigene Ebene ueber dem Inhalt liest. */
    left:   0;
    right:  0;
    bottom: 0;
    height: calc(var(--bottomnav-h) + var(--safe-b));
    padding-bottom: var(--safe-b);
    padding-left:  calc(6px + var(--safe-l));
    padding-right: calc(6px + var(--safe-r));
    /* Padding, aber bewusst KEIN gap: auf 375px bleiben pro Eintrag nur
       ~66px, und „Posteingang" braucht davon fast alles. Die Rundung des
       aktiven Chips trennt die Einträge ausreichend. */
    padding-top: 6px;
    background: var(--surface);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    box-shadow: 0 -2px 18px rgba(26,26,46,0.10), 0 -1px 0 rgba(26,26,46,0.06);
    z-index: var(--z-nav);
  }
  /* Leichte Milchglas-Schicht, wo der Browser sie kann. Regulär liegt unter
     der Pille nur der Seitenhintergrund (der Auslauf an #main hält den
     Inhalt frei) — sichtbar wird die Transluzenz beim Überziehen am
     Scroll-Ende, wenn iOS den Inhalt kurz darunter schiebt. Sie nimmt der
     weissen Fläche ausserdem die Härte. Ohne Unterstützung bleibt es bei
     der deckenden Fläche darüber — kein Funktionsverlust. */
  @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    body #bottomnav {
      background: color-mix(in srgb, var(--surface) 86%, transparent);
      -webkit-backdrop-filter: blur(18px) saturate(170%);
      backdrop-filter: blur(18px) saturate(170%);
    }
  }

  body #bottomnav .bottomnav-item {
    position: relative;            /* Anker für die Zähler-Blase */
    flex: 1 1 0;
    min-width: 0;
    min-height: var(--tap);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 4px 1px;
    background: transparent;
    border: 0;
    border-radius: var(--radius-lg);
    color: var(--text-muted);
    font-family: inherit;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.01em;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background 0.18s, color 0.18s;
  }
  /* Icon etwas kleiner als vorher (21→20) und Schrift etwas grösser: das
     Verhältnis war kopflastig, das Label wirkte wie eine Fussnote. */
  body #bottomnav .bottomnav-item svg { width: 20px; height: 20px; flex-shrink: 0; }
  body #bottomnav .bottomnav-item .bottomnav-label {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.1;
  }

  /* ── Aktiv = Chip, nicht nur Farbe ─────────────────────────────────────
     Gold-Lasur als Fläche, Markenblau als Schrift: der Chip ist auf Weiss
     eindeutig zu sehen, und Text auf ihm hat ~15:1 Kontrast — anders als
     dünnes Gold auf Dunkelblau vorher.
     Bewusst KEIN fetterer Schnitt im aktiven Zustand: die Beschriftung
     würde dadurch breiter als der Eintrag und bräche in „Postein…" ab.
     Fläche und Farbe markieren den Zustand deutlich genug. */
  body #bottomnav .bottomnav-item.active {
    background: var(--accent-tint);
    color: var(--brand);
  }
  body #bottomnav .bottomnav-item:active { background: rgba(26,26,46,0.06); }

  /* Zähler-Blase (offene Tickets, ungelesene Nachrichten). Sitzt am Icon,
     nicht am Chip — sie soll auch im inaktiven Zustand auffallen. Weisser
     Ring, damit sie sich vom Icon darunter absetzt. */
  body #bottomnav .bottomnav-badge {
    position: absolute;
    top: 2px;
    left: 50%;
    margin-left: 3px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: var(--radius-pill);
    background: var(--danger);
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    box-shadow: 0 0 0 2px var(--surface);
  }

  /* Toasts und alles andere Schwebende halten Abstand zur Leiste. */
  body #toast-stack {
    bottom: calc(var(--bottomnav-h) + var(--safe-b) + var(--space-3));
    right: var(--space-3);
    left: var(--space-3);
  }
}

/* Auf sehr kleinen Geräten (≤359px) bleiben pro Eintrag ~62px. Icon und
   Schrift eine Stufe kleiner, damit die Beschriftung nicht abgeschnitten
   wird — lieber klein und lesbar als „Postei…". */
@media (max-width: 359px) {
  body #bottomnav { padding: 5px; }
  body #bottomnav .bottomnav-item { font-size: 9.5px; }
  body #bottomnav .bottomnav-item svg { width: 19px; height: 19px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   PWA — Update-Banner & iOS-Installationshinweis
   Gesteuert von core/pwa.js.
   ══════════════════════════════════════════════════════════════════════════ */
#pwa-update-banner, #push-verloren-banner {
  position: fixed;
  left: 50%;
  bottom: calc(var(--space-4) + var(--safe-b));
  transform: translate(-50%, 140%);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: min(560px, calc(100vw - 24px));
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-4);
  background: var(--brand-surface);
  color: #fff;
  border-radius: 999px;
  box-shadow: 0 8px 30px rgba(0,0,0,0.28);
  z-index: var(--z-toast);
  font-size: 13px;
  transition: transform 0.32s cubic-bezier(.2,.8,.3,1);
}
#pwa-update-banner.sichtbar, #push-verloren-banner.sichtbar { transform: translate(-50%, 0); }
#pwa-update-banner .pwa-update-text, #push-verloren-banner .pwa-update-text { flex: 1; min-width: 0; }
#pwa-update-banner .pwa-update-btn, #push-verloren-banner .pwa-update-btn {
  flex-shrink: 0;
  background: var(--accent);
  color: #fff;
  border: 0;
  border-radius: 999px;
  padding: 8px 16px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
}
#pwa-update-banner .pwa-update-btn:hover, #push-verloren-banner .pwa-update-btn:hover { background: var(--accent2); }
#pwa-update-banner .pwa-update-spaeter, #push-verloren-banner .pwa-update-spaeter {
  flex-shrink: 0;
  background: transparent;
  border: 0;
  color: rgba(255,255,255,0.6);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0 6px;
}
#pwa-update-banner .pwa-update-spaeter:hover, #push-verloren-banner .pwa-update-spaeter:hover { color: #fff; }

/* Breakpoint war 600px, jetzt 767px: die Bottom-Navigation erscheint bei
   767px: Zwischen 601 und 767px schwebte der Hinweis deshalb HINTER der
   Leiste. Ausserdem übernimmt er jetzt Radius und Einrückung der Leiste, so
   dass beide als Elemente derselben Schicht gelesen werden. */
@media (max-width: 767px) {
  #pwa-update-banner, #push-verloren-banner {
    left:  calc(var(--space-3) + var(--safe-l));
    right: calc(var(--space-3) + var(--safe-r));
    max-width: none;
    transform: translateY(200%);
    border-radius: var(--radius-lg);
  }
  #pwa-update-banner.sichtbar, #push-verloren-banner.sichtbar { transform: translateY(0); }
  /* Genau eine Leistenhöhe + Abstand darüber — dieselbe Summe wie der
     Auslauf der Scroll-Container. */
  body #pwa-update-banner, body #push-verloren-banner {
    bottom: calc(var(--bottomnav-h) + var(--safe-b) + var(--space-3));
  }
  /* Touch-Ziele ≥ 44 px (Projektregel). */
  #pwa-update-banner .pwa-update-btn, #push-verloren-banner .pwa-update-btn,
  #pwa-update-banner .pwa-update-spaeter, #push-verloren-banner .pwa-update-spaeter {
    min-height: 44px;
    min-width: 44px;
  }
}

/* ── iOS: „Zum Home-Bildschirm" ──────────────────────────────────────────
   Safari kennt kein Installations-Prompt, und ohne Installation gibt es auf
   iOS keine Push-Benachrichtigungen. Deshalb eine echte Anleitung. */
#pwa-ios-hinweis {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: var(--z-toast);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  opacity: 0;
  transition: opacity 0.25s;
}
#pwa-ios-hinweis.sichtbar { opacity: 1; }
#pwa-ios-hinweis .pwa-ios-inner {
  position: relative;
  width: 100%;
  max-width: 460px;
  background: var(--surface);
  color: var(--text);
  border-radius: 16px 16px 0 0;
  padding: var(--space-5) var(--space-4);
  padding-bottom: calc(var(--space-5) + var(--safe-b));
  transform: translateY(100%);
  transition: transform 0.3s cubic-bezier(.2,.8,.3,1);
}
#pwa-ios-hinweis.sichtbar .pwa-ios-inner { transform: translateY(0); }
#pwa-ios-hinweis .pwa-ios-close {
  position: absolute;
  top: 10px; right: 12px;
  background: transparent; border: 0;
  font-size: 26px; line-height: 1;
  color: var(--text-muted);
  cursor: pointer;
  min-width: var(--tap); min-height: var(--tap);
}
#pwa-ios-hinweis .pwa-ios-titel {
  font-size: 17px; font-weight: 700;
  margin-bottom: var(--space-4);
  color: var(--brand);
}
#pwa-ios-hinweis .pwa-ios-schritte {
  margin: 0 0 var(--space-4);
  padding-left: 20px;
  font-size: 14px;
  line-height: 1.85;
}
#pwa-ios-hinweis .pwa-ios-fuss {
  font-size: 12px;
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}

/* ══════════════════════════════════════════════════════════════════════════
   SAFE-AREA OBEN  (Notch / Dynamic Island)  — Korrektur zu v10.51.0
   ─────────────────────────────────────────────────────────────────────────
   `viewport-fit=cover` in index.php lässt die Seite bis unter die Status-
   leiste laufen. Das ist gewollt (sonst bleiben oben schwarze Balken), heißt
   aber: Der oberste Balken muss den Bereich hinter Uhrzeit/Akku SELBST
   einfärben und seinen Inhalt darunter schieben. Sonst steht die Uhrzeit
   auf dem Logo.

   Seit dem Umbau des Env-Banners zum Overlay-Rahmen (position:fixed,
   0px Layout-Fläche) ist #topnav auf ALLEN Instanzen der oberste Balken —
   er füllt die Safe-Area also immer selbst; keine Fallunterscheidung per
   :has() mehr nötig.
   ══════════════════════════════════════════════════════════════════════════ */

#topnav {
  padding-top: var(--safe-t);
  height: calc(var(--topnav-h) + var(--safe-t));
}

/* Der Drawer liegt über allem und braucht den Abstand immer selbst. */
#mobile-drawer .mobile-drawer-header {
  padding-top: calc(14px + var(--safe-t));
}

/* Seitliche Safe-Area (Querformat auf Geräten mit Notch).
   Die Bottom-Navigation steht hier nicht mehr: sie ist seit dem Umbau auf
   die schwebende Pille `position:fixed` und hat --safe-l/-r bereits in
   ihren left/right-Werten. Ein zusätzliches Padding hätte den Inhalt der
   Pille doppelt eingerückt. */
@media (max-width: 1023px) and (orientation: landscape) {
  #topnav { padding-left: calc(12px + var(--safe-l)); padding-right: calc(12px + var(--safe-r)); }
}

/* ══════════════════════════════════════════════════════════════════════════
   MOBILE-DRAWER — Lesbarkeit & Hierarchie
   ─────────────────────────────────────────────────────────────────────────
   Vorher war das eine flache Liste, in der Gruppen ("Verträge", "Objekte")
   genauso aussahen wie ihre Kinder — man sah nicht, was wozu gehört, und
   jeder Eintrag war gleich laut. Jetzt: Gruppen als leise Abschnitts-
   überschrift, Kinder eingerückt an einer Führungslinie, aktiver Eintrag
   eindeutig markiert.
   ══════════════════════════════════════════════════════════════════════════ */
#mobile-drawer {
  width: 300px;
  max-width: 78vw;         /* vorher 85vw — nahm auf 390px fast den ganzen Schirm */
  /* Leichter Verlauf statt einer Fläche: gibt dem Panel Tiefe.
     Bewusst NICHT --brand2: das ist im Dunkel-Modus eine helle Farbe (es
     wird als Text und als Akzentstreifen benutzt). Als Verlaufsende ergab
     das ein Schubfach, das nach unten ins Hellgraue lief — mit weisser
     Schrift darauf. --drawer-end ist eine reine Flächenfarbe und in beiden
     Modi dunkel. */
  background: linear-gradient(165deg, var(--brand-surface) 0%, var(--drawer-end) 100%);
  /* Abgerundete Aussenkante + Schlagschatten: der Drawer liest sich als
     Blatt, das über die Seite geschoben wird, nicht als abgeschnittener
     Bildschirmteil. */
  border-radius: 0 var(--radius-xl) var(--radius-xl) 0;
  box-shadow: 12px 0 40px rgba(0,0,0,0.35);
  padding-left: var(--safe-l);   /* Querformat mit Notch links */
}
#mobile-drawer .mobile-drawer-body {
  padding: var(--space-2) var(--space-3) calc(var(--space-4) + var(--safe-b));
}
/* 3px statt 1px: die Einträge waren so dicht gestapelt, dass die aktive
   Fläche an die Nachbarn stiess und die Liste als Block wirkte. */
#mobile-drawer .mobile-drawer-tabs { gap: 3px; }

/* ── Kopfzeile: Marke statt Systemtitel ───────────────────────────────────
   „Menü" war so gross wie die Menüpunkte selbst und konkurrierte mit ihnen.
   Jetzt eine leise Versalien-Beschriftung — der Inhalt darunter führt. */
#mobile-drawer .mobile-drawer-header {
  border-bottom: 0;              /* Trennlinien vermeiden, Abstand trennt */
  padding-bottom: var(--space-2);
}
#mobile-drawer .mobile-drawer-title {
  font-size: var(--fs-label);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: rgba(255,255,255,0.45);
}
#mobile-drawer .mobile-drawer-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  margin-right: -8px;            /* optisch bündig, Trefferfläche bleibt 44px */
  border-radius: var(--radius-lg);
  font-size: 26px;
  color: rgba(255,255,255,0.65);
}
#mobile-drawer .mobile-drawer-close:active { background: rgba(255,255,255,0.10); }

/* Alle Einträge mit ruhigem Rhythmus. 15px statt 14px: der Drawer ist die
   Hauptnavigation am Handy, seine Ziele sind die wichtigsten Treffer auf
   dem Schirm und dürfen nicht kleiner sein als Fliesstext. */
#mobile-drawer .mobile-drawer-tabs .nav-tab,
#mobile-drawer .mobile-drawer-tabs button {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  font-size: 15px;
  font-weight: 500;
  line-height: 1.25;
  border-radius: var(--radius-lg);
  color: rgba(255,255,255,0.82);
}
#mobile-drawer .mobile-drawer-tabs .nav-tab:active { background: rgba(255,255,255,0.10); }
#mobile-drawer .mobile-drawer-tabs .nav-tab-icon-wrap {
  display: inline-flex;
  flex-shrink: 0;
  width: 18px;
  opacity: 0.7;
}

/* ── Gruppenkopf: Abschnittsüberschrift, nicht Menüpunkt ──────────────────
   Bleibt klickbar (führt zum Gruppen-Ziel), tritt optisch aber zurück,
   damit die eigentlichen Ziele darunter die Aufmerksamkeit bekommen. */
#mobile-drawer .mobile-drawer-tabs .nav-drawer-group-head {
  margin-top: var(--space-4);
  padding: 6px 12px 4px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1.1px;
  color: rgba(255,255,255,0.40);
  border-radius: 0;
  min-height: 0;           /* Überschrift braucht kein 44px-Touch-Ziel */
}
#mobile-drawer .mobile-drawer-tabs .nav-drawer-group-head:hover,
#mobile-drawer .mobile-drawer-tabs .nav-drawer-group-head.active {
  background: transparent;
  color: rgba(255,255,255,0.55);
}

/* ── Gruppenkinder: eingerückt an einer Führungslinie ─────────────────── */
#mobile-drawer .mobile-drawer-tabs .nav-drawer-group-item {
  position: relative;
  margin-left: 12px;
  padding-left: 16px;
}
#mobile-drawer .mobile-drawer-tabs .nav-drawer-group-item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 2px;
  bottom: 2px;
  width: 1.5px;
  background: rgba(255,255,255,0.12);
  border-radius: 1px;
}

/* ── Aktiver Eintrag: eindeutig, ohne zu schreien ─────────────────────── */
#mobile-drawer .mobile-drawer-tabs .nav-tab.active {
  background: rgba(200,169,110,0.18);
  color: #fff;
  font-weight: 700;
}
/* Solo-Einträge (ohne Gruppe) haben keine Führungslinie, an der sich der
   Aktiv-Zustand festmachen könnte — sie bekommen denselben goldenen
   Markierungsstrich links, damit „aktiv" überall gleich aussieht. */
#mobile-drawer .mobile-drawer-tabs .nav-tab.active:not(.nav-drawer-group-item) {
  position: relative;
  box-shadow: inset 3px 0 0 var(--accent);
}
#mobile-drawer .mobile-drawer-tabs .nav-drawer-group-item.active::before {
  background: var(--accent);
  width: 3px;
}
#mobile-drawer .mobile-drawer-tabs .nav-tab.active .nav-tab-icon-wrap { opacity: 1; }

/* ══════════════════════════════════════════════════════════════════════════
   NICHTS DARF UNTER DER LEISTE LIEGEN  — Korrektur zu v10.51.0
   ─────────────────────────────────────────────────────────────────────────
   Zwei getrennte Probleme, die am Handy gleich aussehen:

   1. WEISSER STREIFEN unter der Bottom-Navigation.
      `body` steht auf `height:100dvh`. Steht darunter noch etwas Platz
      (Home-Indicator-Bereich), scheint dort der Hintergrund von <html>
      durch — und der war nie gesetzt, also weiß. Sichtbar wurde das erst
      mit `viewport-fit=cover`.

   2. VERDECKTER INHALT: Der „+ Ticket"-Knopf (#app-footer, z-index 9200)
      schwebt über allem. Wenn ein Formular oder Modal exakt am unteren
      Rand endet, liegen dessen letzte Schaltflächen darunter. Scrollende
      Bereiche brauchen deshalb unten Auslauf.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Kein durchscheinender Hintergrund mehr ────────────────────────────
   <html> bekommt die Farbe dessen, was am unteren Rand tatsächlich zu sehen
   ist. Solange die Bottom-Navigation ein dunkler, randbündiger Balken war,
   musste das --brand sein. Seit sie als helle Pille darüber SCHWEBT, liegt
   am Rand der Seitenhintergrund — --brand würde dort einen dunklen Streifen
   erzeugen, also genau den Fehler, den diese Regel verhindern soll.
   Deshalb jetzt durchgehend --bg. */
html { background: var(--bg); }
@media (max-width: 767px) {
  /* Sicherstellen, dass nichts unter der Viewport-Höhe wegläuft. */
  body { height: 100dvh; max-height: 100dvh; overflow: hidden; }
}

/* ── 2. Auslauf am unteren Rand ───────────────────────────────────────────
   Der Auslauf sitzt seit der Chrome-Überarbeitung an #main, nicht mehr an
   den Modul-Containern — siehe den Block „KORREKTUR v10.51.3" weiter unten.
   Hier bleiben nur die Vollbild-Modals, die ausserhalb von #main schweben
   und ihren Abstand deshalb selbst mitbringen müssen. */
@media (max-width: 767px) {
  body .modal,
  body .modal-fullscreen-mobile {
    padding-bottom: calc(var(--tap) + var(--space-4) + var(--safe-b));
  }
}

/* Auf ganz kleinen Geräten den schwebenden Knopf kompakter halten, damit er
   weniger Fläche verdeckt. */
@media (max-width: 479px) {
  body #footer-ticket-btn { padding: 0 12px; font-size: 12px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   KORREKTUR v10.51.2 — Leiste klebt am Rand, „+ Ticket" wandert ins Menü
   ─────────────────────────────────────────────────────────────────────────
   1. WEISSER STREIFEN, zweiter Anlauf.
      Erster Versuch war, die Leiste im Flex-Fluss zu lassen und <html>
      einzufärben. Auf dem iPhone blieb der Streifen — in der installierten
      PWA schließt `100dvh` die untere Safe-Area offenbar NICHT ein, die
      Leiste endete also oberhalb des physischen Bildschirmrands.
      Jetzt: `position:fixed; bottom:0`. Damit klebt sie garantiert unten,
      unabhängig davon, wie der Browser `dvh` auslegt. Der Preis ist, dass
      sie Inhalt überdecken KÖNNTE — deshalb bekommen alle scrollenden
      Modul-Container unten exakt ihre Höhe als Auslauf.

   2. Der schwebende „+ Ticket"-Knopf ist am Handy weg. Er lag über den
      Schaltflächen jedes Formulars. Auf kleinen Schirmen ist ein FAB neben
      einer Bottom-Navigation ohnehin einer zu viel — die Funktion sitzt
      jetzt im Menü (siehe core/app.js → renderMobileDrawerTabs).
   ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   KORREKTUR v10.51.3 — der Auslauf gehört der Hülle, nicht den Modulen
   ─────────────────────────────────────────────────────────────────────────
   Dritter Anlauf für „nichts darf unter der Leiste liegen". Die beiden
   ersten scheiterten nicht am Wert, sondern an der Zuständigkeit.

   Bisher reservierten `body [data-module-view].active` und
   `body .panel.active` den Platz — Spezifität 0,2,1. Stylt ein Modul sein
   Panel per ID (`#panel-buchungen`, `#panel-bhbgv` …), gewinnt die ID
   (1,0,0), das padding-bottom fällt ersatzlos weg und die letzte Zeile des
   Formulars liegt hinter der Leiste. Dass es meistens trotzdem klappte,
   lag nur daran, dass die zweite Regel auf dem data-Attribut zusätzlich
   griff — also an der Struktur des jeweiligen Moduls, nicht an einer
   Zusicherung. In drei Modulen musste der Abstand schon einzeln
   wiederhergestellt werden.

   Die Ursache ist konzeptionell: Wie viel Platz die App-Hülle für ihre
   eigene Navigation braucht, ist keine Information, die 16 Module kennen
   und respektieren müssen. Sie gehört der Hülle.

   Deshalb sitzt die Reservierung jetzt an `#main` — dem Container, in dem
   ALLE Modulansichten stecken. Das hat drei Vorteile:
     • Ein Modul kann sie nicht versehentlich überschreiben: es müsste
       dafür selbst `#main` ansprechen, und das tut keines (ausser bhbgv
       im Druck-Stylesheet, wo keine Bottom-Navigation existiert).
     • Sie wirkt unabhängig davon, WELCHES Element im Modul scrollt —
       auch bei Modulen mit eigener Kopf-/Fusszeilen-Struktur, deren
       klebende Fusszeile von einem padding im Scrollbereich gar nicht
       erreicht worden wäre.
     • Sie gilt genau einmal. Vorher konnten sich Panel- und
       Modul-Container-Regel zu doppeltem Abstand addieren.

   `#main` steht auf `overflow:hidden`; Überlauf wird an der PADDING-Box
   beschnitten, das reservierte Feld bleibt also frei — dort liegt sichtbar
   der Seitenhintergrund, über dem die Leiste schwebt.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  /* Position und Grösse der Leiste stehen im BOTTOM-NAVIGATION-Block weiter
     oben — hier steht nur, was der Inhalt daraus folgern muss.
     Auslauf = Leistenhöhe + Abstand zum Rand + Safe-Area + etwas Luft.
     Diese vierteilige Summe ist die EINE Stelle, an der die Geometrie der
     schwebenden Leiste in den Inhalt übersetzt wird. Wer sie ändert, muss
     Toast und Update-Hinweis mitändern — sonst entsteht der alte Fehler
     neu, nur an anderer Stelle. */
  body #main {
    padding-bottom: calc(var(--bottomnav-h) + var(--safe-b) + var(--space-3));
  }
  /* Panels selbst brauchen jetzt KEIN eigenes padding-bottom mehr. Falls ein
     Modul dennoch eines setzt, ist das nur zusätzliche Luft, kein Fehler. */

  /* Vollbild-Modals liegen ausserhalb von #main (position:fixed) und
     überdecken die Leiste ohnehin (z-index 1000 > --z-nav 100). Sie
     brauchen deshalb nur die Safe-Area, nicht die volle Leistenhöhe. */
  body .modal,
  body .modal-fullscreen-mobile {
    padding-bottom: calc(var(--space-4) + var(--safe-b));
  }
  body .modal .modal-actions { padding-right: 0; }

  /* Schwebender Ticket-Knopf: am Handy aus. Wandert ins Menü. */
  body #app-footer { display: none; }
}

/* ── Menü-Fussbereich: „Neues Ticket" ─────────────────────────────────────
   Ersetzt den schwebenden „+ Ticket"-Knopf, der am Handy ausgeblendet ist.
   Bisher stand er als goldene Zeile unter einer Trennlinie — optisch ein
   Menüpunkt wie jeder andere, nur andersfarbig. Er ist aber keine
   Navigation, sondern eine AKTION. Deshalb jetzt als eigenständige,
   umrandete Fläche mit deutlichem Abstand: die Form sagt „Knopf", die
   Position am Ende sagt „nachgeordnet".
   Der Abstand ersetzt die Trennlinie — Linien sind im Drawer sonst nirgends
   mehr nötig. */
#mobile-drawer .mobile-drawer-trenner {
  height: var(--space-5);
  background: transparent;
  margin: 0;
}
#mobile-drawer .mobile-drawer-tabs .mobile-drawer-ticket {
  justify-content: center;
  gap: var(--space-2);
  color: var(--accent);
  font-weight: 700;
  border: 1px solid rgba(200,169,110,0.40);
  border-radius: var(--radius-pill);
  padding: 12px 14px;
}
#mobile-drawer .mobile-drawer-tabs .mobile-drawer-ticket:active {
  background: rgba(200,169,110,0.18);
}
#mobile-drawer .mobile-drawer-tabs .mobile-drawer-ticket .nav-tab-icon-wrap { opacity: 1; }

/* ══════════════════════════════════════════════════════════════════════════
   HANDY-GESTALTUNG — Typo-Skala, schlanker Topnav, weiche Flächen
   (Chrome-Überarbeitung nach Nutzertest „unübersichtlich")
   ─────────────────────────────────────────────────────────────────────────
   Alles in diesem Block gilt AUSSCHLIESSLICH ≤767px. Der Desktop behält
   Dichte und Maße unverändert — die Tokens lösen dort auf ihre bisherigen
   Werte auf.

   Die Leitidee stammt aus dem Referenzbild des Auftraggebers: der Inhalt
   führt, das Gerüst tritt zurück. Konkret heißt das drei Dinge:
     • weniger Chrome (schmalerer Topnav, weniger Icons darin),
     • mehr Kontrast in der Schrift (echte Größenstufen statt 13px überall),
     • Gruppierung durch Abstand und Fläche statt durch Linien und Rahmen.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Typo-Skala am Handy ──────────────────────────────────────────────
   Warum überhaupt anheben: 13px ist auf 375px CSS-Pixeln bei Armlänge etwa
   so groß wie 9pt auf Papier. Und weil ALLES 13px war, gab es keine
   Hierarchie — der Blick fand keinen Einstieg und las die Seite als graue
   Masse. Genau das meint „unübersichtlich".

   Die Stufen sind bewusst weit auseinandergezogen (25 / 17 / 14.5 / 11.5):
   am Handy ist wenig Platz für Zwischentöne, jede Stufe muss auf einen
   Blick als andere Ebene erkennbar sein.

   14.5px statt 16px für den Fliesstext ist Absicht: 16px hätte die
   Tabellen-Karten der Module um ~10 % höher gemacht. Formularfelder stehen
   davon unberührt weiterhin auf 16px — das erzwingt iOS gegen Auto-Zoom
   (siehe MOBILE-FUNDAMENT, Abschnitt 3). */
@media (max-width: 767px) {
  :root {
    --fs-page-title: 25px;
    --fs-section:    17px;
    --fs-body:       14.5px;
    --fs-label:      11.5px;
    --lh-body:       1.55;
    /* Topnav von 56 auf 48: fünf Bedienelemente in einem 56px-Balken waren
       auf 375px ein Sechstel der Höhe für Dinge, die man selten braucht.
       Das Token wird auch von .update-popover und dem Tickets-Modul für
       ihre calc()-Positionen gelesen — deshalb hier zentral, nicht am
       #topnav hart. */
    --topnav-h: 48px;
  }
  body { line-height: var(--lh-body); }

  /* Der Seitentitel bekommt Luft und wird ruhig gesetzt: enger Zeilenfall,
     leicht negative Laufweite — bei 25px wirkt die Open-Sans-Defaultweite
     sonst auseinandergefallen. */
  body .page-title {
    font-size: var(--fs-page-title);
    line-height: 1.2;
    letter-spacing: -0.015em;
  }
  /* Titel und Aktionen stehen am Handy fast nie nebeneinander in eine Zeile.
     Statt sie zu quetschen: umbrechen lassen, Titel oben, Aktionen darunter
     linksbündig — das ergibt den Weissraum, der den Titel wirken lässt. */
  body .page-header {
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: flex-start;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
  }
  body .page-header .page-title { flex: 1 1 100%; }

  /* Abschnittsüberschrift ohne Trennlinie: die Linie war am Desktop ein
     dezenter Taktgeber, am Handy ein weiterer Strich in einer ohnehin
     strichlastigen Ansicht. Abstand oben trennt genauso deutlich. */
  body .section-title {
    border-bottom: 0;
    padding-bottom: 0;
    margin: var(--space-5) 0 var(--space-3);
  }
}

@media (max-width: 479px) {
  :root {
    --fs-page-title: 22px;   /* auf 320-375px sonst zweizeilige Titel */
    --fs-section:    16px;
  }
}

/* ── 2. Topnav am Handy: nur noch drei Dinge ─────────────────────────────
   Vorher: Hamburger · Logo · Trennstrich · Warn-Icon · Ticket-Icon ·
   Profil-Initialen · DB-Statuspunkt. Auf 375px Breite geteilt durch sieben
   Elemente bleibt jedem ~50px — und keines davon ist etwas, das man beim
   Öffnen der App braucht.

   Was bleibt und warum:
     • Hamburger — der einzige Weg zur vollständigen Navigation.
     • Logo — Ortsangabe, welche App/Instanz man vor sich hat.
     • Ticket-Icon — trägt Zähler-Blasen, ist also eine Benachrichtigung.
     • Profil — Abmelden, Push, Installation; Konvention oben rechts.
   Was geht und warum:
     • Trennstrich — trennte den Logo-Bereich von den Tabs, und die Tabs
       sind am Handy ohnehin ausgeblendet. Er trennte also nichts mehr.
     • DB-Statuspunkt — Diagnose für den Betrieb, keine Nutzerinformation.
       Am Desktop bleibt er, dort stört er niemanden.
   Nicht angefasst: das Warn-Icon (#update-warning-btn). Es erscheint nur
   mit Berechtigung UND vorliegendem Update — also praktisch nie, und dann
   ist es genau die Sorte Meldung, die man sehen soll.

   Entfallen ist ausserdem die Regel `#topnav #testinstanz-btn {display:
   inline-flex}`: ticket-widget.js schiebt den Knopf nach #nav-tabs-right,
   und dessen Container ist am Handy display:none — ein Kind kann nicht
   sichtbar sein, wenn der Elternknoten es nicht ist. Die Regel hat also nie
   etwas bewirkt. Die Funktion ist am Handy trotzdem erreichbar: der
   TESTINSTANZ-Balken selbst ist der Auslöser (ticket-widget.js →
   hookTestinstanzBanner), und „Neues Ticket" steht im Menü. */
@media (max-width: 767px) {
  body #topnav {
    /* Safe-Area-Insets im Padding, damit die Regel auch im Querformat mit
       Notch gilt und die allgemeine Landscape-Regel nicht überstimmt. */
    padding-left:  calc(6px + var(--safe-l));
    padding-right: calc(8px + var(--safe-r));
    gap: 4px;
    /* Kein Schlagschatten: Marineblau auf hellgrauem Inhalt trennt bereits
       maximal. Der Schatten war eine Desktop-Geste, am Handy addierte er
       nur Schwere zu einem ohnehin dunklen Kopfbereich. */
    box-shadow: none;
  }
  body #topnav .nav-divider { display: none; }
  body #topnav .db-status   { display: none; }

  /* Trefferflächen auf 44px bringen, OHNE die Elemente optisch zu
     vergrössern. Beim Hamburger geht das direkt (transparente Fläche),
     bei Profil-Kreis und Ticket-Icon nur über eine unsichtbare Hülle —
     ein 44px-Goldkreis wäre am Handy ein Klotz, und #ticket-btn bekommt
     sein Padding per Inline-Style aus ticket-widget.js, ist also von hier
     ohne !important nicht änderbar. */
  body #nav-hamburger {
    width: var(--tap);
    height: var(--tap);
    border-radius: var(--radius-lg);
  }
  body #topnav .profile-menu-btn,
  body #topnav #ticket-btn { position: relative; }
  body #topnav .profile-menu-btn::after,
  body #topnav #ticket-btn::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--tap);
    height: var(--tap);
    transform: translate(-50%, -50%);
  }
  /* Das Logo trägt am Handy die Markenaussage allein — eine Spur grösser. */
  body #topnav img { height: 26px; }
}

/* ── 3. Weiche Flächen statt Rahmen ──────────────────────────────────────
   Die Karten, zu denen `.data-table` am Handy umbaut, hatten Rahmen UND
   Schatten UND eckige 8px-Radien. Drei Abgrenzungsmittel für dieselbe
   Aufgabe: das ergibt das „gekachelte" Bild, das schwer wirkt. Jetzt trägt
   der Schatten allein, der Radius wird grosszügig, und die Zeilentrenner
   INNERHALB einer Karte werden so leise wie möglich — sie sollen Zeilen
   gliedern, nicht die Karte zerschneiden. */
@media (max-width: 767px) {
  body .data-table tr {
    border: 0;
    border-radius: var(--radius-lg);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-3);
    box-shadow: var(--shadow-card);
  }
  body .data-table td { border-bottom-color: rgba(26,26,46,0.06); }
  body .data-table td::before { font-size: var(--fs-label); }

  /* Knöpfe und Pillen runder: bei --tap-Höhe (44px) wirken 8px Radius wie
     abgeschnittene Ecken. Formulierfelder bleiben bei --radius — dort ist
     die eckigere Form die bessere Lesehilfe für die Eingabezeile. */
  body .btn { border-radius: var(--radius-lg); padding: 10px 18px; }
  /* .btn-sm muss NACH .btn stehen: beide haben dieselbe Spezifitaet, sonst
     verliert die kleinere Variante und jeder Knopf waere gleich gross. */
  body .btn-sm { border-radius: var(--radius); padding: 8px 14px; font-size: 13px; }
  body .pill { border-radius: var(--radius-pill); padding: 8px 14px; font-size: 13px; }
  /* Filterpillen wandern seitlich aus dem Bild, statt in eine zweite Zeile
     umzubrechen — so bleibt die Kopfzone einer Ansicht immer gleich hoch. */
  body .filter-pills {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  body .filter-pills::-webkit-scrollbar { display: none; }
  body .filter-pills .pill { flex: 0 0 auto; }
}

/* ── 4. Status-Chip als gemeinsame Form ──────────────────────────────────
   Angebot an die Module, keine Vorschrift: wer heute einen Status mit
   eigener Hex-Farbe und eigenem Radius rendert, kann auf `.status-chip`
   umstellen und bekommt Fläche, Schrift und Kontrast konsistent.
   Die `-soft`/`-ink`-Paare sind so gewählt, dass die Fläche neben weiss
   ruhig bleibt und die Schrift darauf trotzdem über 7:1 Kontrast hat —
   pastellig heisst hier nicht blass. */
.status-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-label);
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
  background: var(--neutral-soft);
  color: var(--neutral-ink);
}
.status-chip.is-ok      { background: var(--success-soft); color: var(--success-ink); }
.status-chip.is-warn    { background: var(--warning-soft); color: var(--warning-ink); }
.status-chip.is-danger  { background: var(--danger-soft);  color: var(--danger-ink);  }
.status-chip.is-info    { background: var(--info-soft);    color: var(--info-ink);    }
.status-chip.is-accent  { background: var(--accent-tint);  color: var(--brand);       }
/* Optionaler Punkt vorweg — für Listen, in denen nur die Farbe zählt. */
.status-chip .status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}


/* ══════════════════════════════════════════════════════════════════════════
   KORREKTUR — aufgeblaehte Suchfelder
   ─────────────────────────────────────────────────────────────────────────
   `.filter-bar input[type=text]` traegt `flex: 1 1 220px`. In einer ZEILE
   ist 220px die Breite — richtig. Mehrere Module stellen die Leiste am Handy
   aber auf `flex-direction: column` (kunden, objekte, eigentuemer). In einer
   SPALTE ist dieselbe Zahl die HOEHE, und `flex-grow: 1` streckt sie auf den
   restlichen Platz. Ergebnis: ein 400px hohes leeres Suchfeld auf jedem
   dieser Bildschirme.

   Am Handy hat ein Eingabefeld eine feste Hoehe und volle Breite. Punkt.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  body .filter-bar {
    flex-direction: column;
    align-items: stretch;
  }
  body .filter-bar input,
  body .filter-bar select,
  body .filter-bar input[type=text] {
    flex: 0 0 auto;          /* NICHT wachsen — sonst wird flex-basis zur Hoehe */
    width: 100%;
    max-width: none;
    min-width: 0;
    height: var(--tap);
    min-height: var(--tap);
  }
  /* Dieselbe Falle in Modul-eigenen Leisten: jedes Feld in einem
     Spalten-Flexcontainer darf nur seine natuerliche Hoehe haben. */
  body .panel.active input[type=text],
  body .panel.active input[type=search],
  body .panel.active input[type=email],
  body .panel.active input[type=tel],
  body .panel.active select {
    flex-grow: 0;
    flex-shrink: 0;
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   LISTEN AM HANDY — Neufassung von .data-table
   ─────────────────────────────────────────────────────────────────────────
   Der erste Wurf war falsch. Er hat jede Tabellenzeile in eine Karte
   verwandelt, in der ALLE Spalten als Beschriftung/Wert-Paare untereinander
   standen. Bei acht Spalten sind das acht Zeilen à ~50px — ein einziger
   Vertrag fuellte den kompletten Bildschirm. Das ist keine Uebersicht,
   sondern eine gekippte Tabelle. Der Auftraggeber hat es zu Recht
   auseinandergenommen.

   Eine Listenzeile am Telefon zeigt das, wonach man sucht — nicht alles,
   was die Datenbank hat:
     • Zeile 1: der Titel (erste Spalte), gross und ohne Beschriftung
     • Zeile 2: bis zu drei Kennwerte nebeneinander, Beschriftung klein
       darueber statt daneben — das spart die halbe Breite
     • Alles ab der fuenften Spalte: ausgeblendet. Es steht im Detail.
     • Aktionen: rechts unten, ueber die volle Breite

   Ergebnis: ~110px statt ~400px pro Eintrag, also vier bis fuenf Eintraege
   pro Bildschirm statt einem.

   FEINSTEUERUNG durch die Module (optional, keine Pflicht):
     .col-title    diese Zelle ist die Ueberschrift (statt der ersten)
     .col-kennwert diese Zelle gehoert in die Kennwert-Zeile
     .col-weg      diese Zelle am Handy nie zeigen
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  /* Zeile = Karte im Raster, zwei Spalten fuer die Kennwerte. */
  body .data-table tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 var(--space-3);
    align-items: start;
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    padding: var(--space-3);
    margin-bottom: var(--space-2);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
  }

  /* Alle Zellen: Beschriftung klein DARUEBER, nicht daneben. */
  body .data-table td {
    display: block;
    padding: 4px 0;
    border: 0;
    min-width: 0;
    max-width: 100%;
    text-align: left;
    font-size: 13.5px;
    line-height: 1.35;
    /* Lange Werte (E-Mail-Adressen, Firmierungen) muessen umbrechen duerfen,
       sonst sprengen sie die Rasterspalte und der Text laeuft aus dem Bild. */
    overflow-wrap: anywhere;
    white-space: normal;
  }
  body .data-table td::before {
    content: attr(data-label);
    display: block;
    flex: none;
    margin-bottom: 1px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    color: var(--text-muted);
    text-align: left;
  }

  /* ── Ueberschrift: erste Zelle, volle Breite, ohne Beschriftung ──────── */
  body .data-table td:first-of-type,
  body .data-table td.col-title {
    grid-column: 1 / -1;
    order: -2;
    padding: 0 0 2px;
    font-size: 15.5px;
    font-weight: 700;
    line-height: 1.25;
    color: var(--text);
  }
  body .data-table td:first-of-type::before,
  body .data-table td.col-title::before { content: none; }

  /* Traegt eine Zelle ausdruecklich .col-title, gibt die erste ihren
     Sonderstatus wieder ab und reiht sich bei den Kennwerten ein. */
  body .data-table tr:has(.col-title) td:first-of-type {
    grid-column: auto;
    font-size: 13.5px;
    font-weight: 400;
    padding: 4px 0;
  }
  body .data-table tr:has(.col-title) td:first-of-type::before {
    content: attr(data-label);
  }

  /* ── Ab der fuenften Spalte: nicht am Handy. Steht im Detail. ────────── */
  body .data-table td:nth-of-type(n+5),
  body .data-table td.col-weg { display: none; }
  /* Ausdruecklich angeforderte Kennwerte bleiben, egal an welcher Stelle. */
  body .data-table td.col-kennwert { display: block; }

  /* ── Aktionen: immer sichtbar, rechts unten ──────────────────────────── */
  body .data-table td.col-aktionen:not(:empty),
  body .data-table td.col-actions:not(:empty) {
    display: flex;
    grid-column: 1 / -1;
    order: 9;
    justify-content: flex-end;
    align-items: center;
    gap: var(--space-2);
    padding: 6px 0 0;
    margin-top: 4px;
    border-top: 1px solid var(--bg);
  }
  /* Aktionsknoepfe in Listen kompakt halten — sie sind Beiwerk, die Karte
     selbst ist das Ziel. Ohne das kostet jede Zeile ~50px fuer einen Knopf,
     der weiss auf weiss ohnehin kaum zu sehen ist. */
  body .data-table td.col-aktionen .btn,
  body .data-table td.col-actions .btn {
    min-height: 34px;
    padding: 5px 12px;
    font-size: 12.5px;
  }
  body .data-table td.col-aktionen::before,
  body .data-table td.col-actions::before { content: none; }

  /* Zellen ohne Beschriftung brauchen auch keinen Platz dafuer. */
  body .data-table td:not([data-label])::before { content: none; }

  /* Statuszellen duerfen ihren Chip behalten, ohne Zeilenumbruch. */
  /* KEIN nowrap: Ein langer Statustext sprengt sonst die Rasterspalte, und
     die rechte Spalte — typischerweise der Betrag — rutscht aus dem Bild. */
  body .data-table td.col-status,
  body .data-table td.col-typ { white-space: normal; }

  /* Der Status ist ein Zustand, kein Kennwert — er gehoert direkt unter den
     Titel und braucht keine eigene Beschriftung, das Wort steht im Chip. */
  body .data-table td.col-status {
    order: -1;
    grid-column: 1 / -1;
    padding-top: 2px;
    padding-bottom: 6px;
  }
  body .data-table td.col-status::before { content: none; }
}

/* Sehr kleines Handy: Kennwerte untereinander statt nebeneinander. */
@media (max-width: 359px) {
  body .data-table tr { grid-template-columns: 1fr; }
}


/* ══════════════════════════════════════════════════════════════════════════
   KORREKTUREN v10.53.1 — nach zweitem Geraetetest
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {

  /* ── 1. Fremde Zaehler-Blase in der Bottom-Nav ────────────────────────────
     kommunikation haengt `.komm-nav-badge` an JEDEN Button mit
     data-module="kommunikation" — auch an den Bottom-Nav-Eintrag. Dort gab es
     dafuer keine Positionsregel: Die Blase stand als drittes Flex-Kind UNTER
     dem Label und zerschnitt den Schriftzug „Posteingang".
     Hier bekommt sie im Nav-Kontext dieselbe Verankerung wie .bottomnav-badge:
     absolut, oben rechts am Icon. */
  body #bottomnav .komm-nav-badge {
    position: absolute;
    top: 2px;
    left: 50%;
    margin-left: 3px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: var(--radius-pill, 999px);
    background: var(--danger);
    color: #fff;
    font-size: 9px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    box-shadow: 0 0 0 2px var(--surface);
  }

  /* ── 2. Navigationsleiste weg, solange getippt wird ──────────────────────
     Bei offener Tastatur schob visualViewport die fixierte Leiste mitten ins
     Bild — Composer, Leiste, weisse Luecke, Tastatur uebereinander. Waehrend
     des Tippens navigiert niemand; die Leiste verschwindet, der Composer
     sitzt direkt auf der Tastatur. kommunikation misst die Leistenhoehe
     dynamisch (offsetParent-Pruefung) und rechnet dann automatisch mit 0. */
  body.komm-tippt #bottomnav { display: none; }

  /* ── 3. Die RIESEN-LUECKEN zwischen den Filterfeldern ────────────────────
     Der eigentliche Fehler hinter den leeren Kaesten auf kunden/objekte/
     eigentuemer — und der vorige Fix hat ihn nur halb erwischt: `flex-grow:0`
     wurde gesetzt, aber die FLEX-BASIS blieb. `flex: 1 1 220px` traegt seine
     220px in der Spaltenrichtung als HOEHE — auch ohne grow. Erst
     `flex-basis: auto` macht die Felder wieder so hoch wie ihr Inhalt.
     Gilt fuer Felder UND fuer Wrapper-Divs wie `.obj-filter-grow`, die
     dieselbe Flex-Kurzform tragen. */
  /* Ueber #main, nicht ueber Klassen: Modul-Regeln wie
     `.obj-filter-bar .obj-filter-grow { flex: 1 1 220px }` haben (0,2,0) —
     eine Klassen-Kette hier verlor den Vergleich (im Harness gemessen:
     Wrapper blieb 220px hoch). Die ID gewinnt gegen jede Klassenkombination,
     ohne !important. */
  #main .panel.active input,
  #main .panel.active select,
  #main .filter-bar > *,
  #main [class*="filter-bar"] > * {
    flex-basis: auto;
    flex-grow: 0;
  }

  /* ── 4. Datumsfelder ragen rechts aus der Karte (anfrage) ────────────────
     iOS gibt input[type=date] eine intrinsische Mindestbreite, die width:100%
     ueberstimmt — die Felder standen ~26px ueber den Kartenrand. */
  body input[type=date],
  body input[type=datetime-local],
  body input[type=time] {
    min-width: 0;
    max-width: 100%;
    -webkit-appearance: none;
    appearance: none;
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   KORREKTUR v10.54.5 — der graue Streifen unter der Leiste, Ursache belegt
   ─────────────────────────────────────────────────────────────────────────
   Messung vom Geraet (Diagnosezeile): ih:894, navDelta:0 — die Leiste sitzt
   EXAKT buendig am unteren Viewport-Rand. Das Display ist aber hoeher: iOS
   gibt der installierten PWA einen verkuerzten Layout-Viewport und haelt die
   Home-Bar-Zone (~60pt) komplett ausserhalb. Kein Element der Seite kann
   dort je hinzeichnen — position:fixed endet am Viewport, nicht am Display.
   Das Einzige, was iOS in dieser Zone rendert, ist der HTML-Hintergrund.
   Der stand auf --bg (grau): genau der Streifen, der drei Fix-Runden lang
   „unter der Leiste" sichtbar blieb.
   Loesung: Die Zone bekommt die Flaechenfarbe der Leiste und verschmilzt
   optisch mit ihr. Mehr ist physisch nicht moeglich. */
@media (max-width: 767px) {
  html { background: var(--surface); }
}

/* ── Kanalwahl „Nachricht schreiben" (core/app.js: App.kommStart) ──────────
   Hängt am <body>, nicht am aufrufenden Fenster: Buchungs- und Vertrags-
   Overlay haben eigene Stapelordnung und `overflow` — ein Menü darin wäre
   abgeschnitten oder läge dahinter. Position setzt JS, weil es nach oben
   aufklappen muss (der Knopf sitzt in der Fußzeile). */
.app-kanalmenue {
  position: fixed;
  z-index: 12000;
  min-width: 280px;
  max-width: 380px;
  padding: 6px;
  border: 1px solid var(--border, var(--border-strong));
  border-radius: 10px;
  background: var(--surface, var(--surface));
  box-shadow: 0 10px 32px rgba(0,0,0,.20);
  font-size: 13px;
}
.app-kanalmenue-kopf {
  padding: 6px 9px 8px;
  font-weight: 600;
  color: var(--text-muted, var(--text-muted));
  font-size: 12px;
}
.app-kanalmenue-laden,
.app-kanalmenue-fehler {
  padding: 8px 9px;
  color: var(--text-muted, var(--text-muted));
}
.app-kanalmenue-eintrag {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 9px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.app-kanalmenue-eintrag:hover:not([disabled]),
.app-kanalmenue-eintrag:focus-visible {
  background: var(--hover, var(--surface-2));
  outline: none;
}
/* Nicht möglich: gedämpft, aber LESBAR — der Grund darunter ist der
   eigentliche Nutzen dieser Zeile und darf nicht weggeblendet werden. */
.app-kanalmenue-eintrag[disabled] { cursor: default; opacity: .75; }
.app-kanalmenue-symbol { font-size: 15px; line-height: 1.35; flex: none; }
.app-kanalmenue-text   { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.app-kanalmenue-text small {
  font-size: 11px;
  color: var(--text-muted, var(--text-muted));
  overflow-wrap: anywhere;
}

/* ── Kontakt-Symbole im Detail-Kopf (core/app.js: App.kontaktLeiste) ──────
   Stufe 1 „Kommunikation als EIN Ort": kleine QUADRATISCHE Knöpfe oben rechts
   in Buchungs-, Vertrags- und Kundendetail. Dezent: Sie erben die Schriftfarbe
   des Kopfes (hell auf dunklem Kopf, dunkel auf hellem) und zeigen ihre
   Markenfarbe erst beim Überfahren — dann auf heller Fläche, damit Booking-
   Blau auch auf dem dunklen Kopf lesbar bleibt.
   `nowrap` + `flex:none`: Die Symbole brechen nie um und werden nie gequetscht. */
.app-kontaktleiste {
  display: inline-flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  flex: none;
}
.app-kontaktleiste[hidden] { display: none !important; }
.app-kontakt-btn {
  --kl-farbe: var(--accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
  border-radius: 8px;
  background: color-mix(in srgb, currentColor 5%, transparent);
  color: inherit;
  opacity: .82;
  cursor: pointer;
  transition: color .12s, background-color .12s, border-color .12s, opacity .12s;
  -webkit-tap-highlight-color: transparent;
}
.app-kontakt-btn svg { width: 18px; height: 18px; flex: none; }
.app-kontakt-btn:hover,
.app-kontakt-btn:focus-visible {
  opacity: 1;
  color: var(--kl-farbe);
  border-color: var(--kl-farbe);
  background: var(--surface);
  outline: none;
}
.app-kontakt-btn:focus-visible { box-shadow: 0 0 0 2px color-mix(in srgb, var(--kl-farbe) 35%, transparent); }
/* Markenfarben der Fremddienste (Logo-Farben, keine Projekt-Tokens). */
.app-kontakt-whatsapp { --kl-farbe: #1da851; }
.app-kontakt-airbnb   { --kl-farbe: #e0565b; }
.app-kontakt-booking  { --kl-farbe: #1f4fa0; }
/* Touch: volles Mindestmaß (Projektregel ≥ 44px). */
@media (max-width: 767px), (pointer: coarse) {
  .app-kontakt-btn { width: var(--tap); height: var(--tap); border-radius: 10px; }
  .app-kontakt-btn svg { width: 20px; height: 20px; }
}
/* Innerhalb der Kommunikation (eingebettete Buchung bzw. eingebettetes
   Dokument in der Info-Spalte) wären die Symbole ein Sprung an den Ort, an
   dem man schon ist — und der Rückbau des geliehenen Fensters liefe mitten
   in der eigenen Ansicht. */
.komm-layout .app-kontaktleiste { display: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   REDESIGN v5 — Seitenleiste (Desktop), „Mehr"-Seite (Handy), Avatar
   ─────────────────────────────────────────────────────────────────────────
   Eingriff 01 (Desktop ≥1024px): Die waagrechte Top-Tab-Leiste entfällt,
   eine senkrechte Seitenleiste (--d-side, Definition in core/desktop.css)
   übernimmt. Sechzehn Module passen in keine waagrechte Leiste — senkrecht
   ist die Liste beliebig lang, gruppierbar und beschriftbar.
   Screen 08 (Handy <768px): Der Slide-in-Drawer wird durch die echte Seite
   #/mehr ersetzt; der Hamburger entfällt dort.
   Tablet 768–1023px: bestehendes Verhalten (Topnav-Tabs) bleibt unberührt.
   Farben ausschließlich über Token (--brand-surface, --accent, rgba-Weiß-
   Stufen wie im bisherigen Topnav-Stil) — kein neuer Hex-Wert.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Avatar mit Initialen (Seitenleiste + Mehr-Seite) ───────────────────── */
.nav-avatar {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent, #fff);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  line-height: 1;
}

/* ── Seitenleiste: unterhalb 1024px nicht vorhanden ─────────────────────── */
#d-sidenav { display: none; }

@media (min-width: 1024px) {
  /* Top-Tabs entfallen — die Seitenleiste übernimmt. Die rechten Icon-Tabs
     (Einstellungen, Hilfe) stehen jetzt in der Gruppe VERWALTUNG; nur der
     Testinstanz-Knopf (kein data-module) bleibt oben rechts erhalten. */
  #topnav #nav-tabs-left { display: none; }
  #topnav #nav-tabs-right > [data-module] { display: none; }
  #topnav .nav-divider { display: none; }

  #d-sidenav {
    display: flex;
    flex-direction: column;
    /* Breite BEWUSST als feste Zahl, nicht als var(--d-side):
       Chrome animiert eine Eigenschaft nicht, deren Wert aus einer
       Custom Property stammt — die Breite blieb dabei sogar auf dem alten
       Wert stehen, die Schiene liess sich gar nicht mehr zuwischen. Die
       Variable bleibt für alles andere die Quelle (z. B. die Position des
       Update-Popovers weiter unten); hier stehen die beiden Endwerte
       ausgeschrieben, damit der Übergang wirklich läuft. */
    /* `flex: none` statt einer flex-basis: die Breite steuert allein `width`.
       Ein Übergang auf `flex-basis` fror den Wert hier fest, statt ihn zu
       animieren — die Leiste blieb dann auf der alten Breite stehen, und
       `getAnimations()` meldete nicht einmal eine laufende Animation. Mit
       `flex: none` gibt es nur noch eine Grösse, die sich ändert, und der
       Übergang läuft sauber. */
    flex: none;
    width: 236px;
    min-height: 0;
    /* Ohne diese Zeile bleibt die Leiste bei 236px stehen: als Flex-Element
       hat sie `min-width: auto`, und ihr Mindestinhalt sind die 212px breiten
       Modulzeilen (die genau deshalb fest sind, damit die Icons beim Wischen
       stehen bleiben). Die automatische Mindestbreite hebelt dann jede
       kleinere Breite aus — die Schiene liesse sich nicht mehr zuwischen. */
    min-width: 0;
    background: var(--brand-surface);
    color: rgba(255,255,255,0.82);
    /* ── Wisch-Animation ────────────────────────────────────────────────
       Die Leiste wird auf- und zugewischt, nicht hart umgeschaltet. Der
       Trick steckt in `overflow-x: hidden`: der Inhalt behält in BEIDEN
       Zuständen seine volle Breite (siehe `.d-side-item` unten) und wird
       vom schmaler werdenden Rand beschnitten. Dadurch bleibt jedes Icon
       exakt an seinem Platz — es gibt kein zweites Layout, das erst
       einrasten müsste.
       `width` und `flex-basis` beziehen ihren Wert aus `--d-side`; die
       Variable selbst lässt sich nicht animieren, die daraus errechnete
       Breite sehr wohl. */
    overflow-x: hidden;
    transition: width 0.24s ease;
  }
  /* Wer Bewegung abbestellt hat, bekommt den harten Wechsel. */
  @media (prefers-reduced-motion: reduce) {
    #d-sidenav { transition: none; }
  }

  /* Logo statt Schriftzug — dieselbe Datei wie früher im Topnav. Nur die
     helle Fassung, weil die Leiste in beiden Farbmodi dunkel ist
     (Begründung am Markup in index.php). */
  #d-sidenav .d-side-logo {
    flex: none;
    height: 22px;
    width: auto;
    max-width: 150px;
    object-fit: contain;
    cursor: pointer;
  }

  /* Marke oben — Ortsangabe der Leiste. Zugleich die Zeile, in der rechts
     der Umschalter sitzt (siehe unten), deshalb Flex statt Blockelement.
     Linker Innenabstand 12px, damit das Logo bündig über den Modul-Icons
     steht und beim Zuwischen nicht wandert. */
  #d-sidenav .d-side-marke {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 16px 12px 10px;
    /* FESTE Höhe, in beiden Zuständen dieselbe. Ohne sie rutschte der
       Umschalter beim Zuwischen unter das Logo, die Zeile wuchs um 28px —
       und die gesamte Modulliste darunter wanderte mit. Genau das darf
       nicht passieren. */
    height: 58px;
    box-sizing: border-box;
    overflow: hidden;
  }

  /* ── Umschalter: Leiste ⇄ Schiene ──────────────────────────────────
     Der einzige Weg zwischen den beiden Zuständen, deshalb auf JEDER Seite
     vorhanden (früher entschied das Modul über sein module.json). 32px ist
     die Trefferflächen-Untergrenze aus dem Entwurf — dieselbe wie in der
     Werkzeugzeile unten. `margin-left:auto` schiebt ihn an die rechte Kante,
     ohne dass die Marke ihre Ausrichtung verliert. */
  #d-sidenav .d-side-toggle {
    flex: none;
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: var(--radius);
    background: transparent;
    color: rgba(255,255,255,0.65);
    cursor: pointer;
  }
  #d-sidenav .d-side-toggle:hover {
    background: rgba(255,255,255,0.10);
    color: #fff;
  }
  #d-sidenav .d-side-toggle:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
  }
  #d-sidenav .d-side-toggle svg { width: 15px; height: 15px; }
  /* Anker: verankert (Leiste fest offen) = Akzentfarbe, gelöst = gedimmt. */
  body:not(.d-rail) #d-sidenav .d-side-toggle { color: var(--accent); }

  /* Modulgruppen: Label in Mono-Versalien, Einträge mit Icon. */
  #d-sidenav .d-side-nav {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    /* Muss ausdrücklich dastehen: die Zeilen sind fest 212px breit und ragen
       in der 60px-Schiene über den Rand. `overflow-y: auto` allein macht die
       Waagrechte automatisch mit — und dann liegt ein Rollbalken quer unter
       der Modulliste. */
    overflow-x: hidden;
    padding: 2px 12px 12px;
  }
  #d-sidenav .d-side-gruppe {
    margin: 14px 6px 5px;
    font-family: ui-monospace, 'SFMono-Regular', monospace;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: rgba(255,255,255,0.40);
  }
  #d-sidenav .d-side-item {
    display: flex;
    align-items: center;
    gap: 10px;
    /* FESTE Breite, nicht 100 % — sonst schrumpfte die Zeile beim Zuwischen
       mit, der Modultext bekäme „…" und das Icon rückte nach. So bleibt die
       Zeile 212px breit (236 minus 2×12 Innenabstand) und wird schlicht vom
       Rand beschnitten: das Icon steht in beiden Zuständen an derselben
       Stelle. `flex: none` verhindert, dass Flexbox sie doch noch staucht. */
    width: 212px;
    flex: none;
    position: relative;   /* Anker für die Zähler-Blase in der Schiene */
    padding: 8px 10px;
    margin-bottom: 1px;
    border: 0;
    border-radius: var(--radius);
    background: transparent;
    color: rgba(255,255,255,0.78);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    text-align: left;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }
  #d-sidenav .d-side-item svg { flex: none; width: 18px; height: 18px; opacity: 0.75; }
  #d-sidenav .d-side-item:hover { background: rgba(255,255,255,0.08); color: #fff; }
  /* Aktiv: getönte Fläche + Gold-Marker links — derselbe Marker wie im
     Drawer und bei „ungelesen" am Handy. */
  #d-sidenav .d-side-item.active {
    background: rgba(255,255,255,0.10);
    color: #fff;
    box-shadow: inset 3px 0 0 var(--accent);
  }
  #d-sidenav .d-side-item.active svg { opacity: 1; color: var(--accent); }
  #d-sidenav .d-side-label {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Zähler in der Zeile: dieselbe Blase, die kommunikation an jedes
     [data-module="kommunikation"] hängt (Grundform in dessen view.css). */
  #d-sidenav .komm-nav-badge {
    flex: none;
    margin-left: auto;
  }

  /* Profil unten fixiert: Avatar, Name, Rolle → öffnet das bestehende
     Profil-Menü (Dropdown verankert am Topnav, core/profile-menu.js). */
  #d-sidenav .d-side-fuss {
    flex: none;
    border-top: 1px solid rgba(255,255,255,0.10);
    padding: 8px 12px calc(8px + var(--safe-b));
  }
  #d-sidenav .d-side-profil {
    display: flex;
    align-items: center;
    gap: 10px;
    /* Feste Breite wie die Modulzeilen — der Avatar darf beim Zuwischen
       nicht nachrücken; der Name daneben wird einfach beschnitten. */
    width: 212px;
    flex: none;
    padding: 7px 10px;
    border: 0;
    border-radius: var(--radius);
    background: transparent;
    color: #fff;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
  }
  #d-sidenav .d-side-profil:hover { background: rgba(255,255,255,0.08); }
  #d-sidenav .d-side-profil .nav-avatar { width: 32px; height: 32px; font-size: 11.5px; }
  #d-sidenav .d-side-profil-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
  #d-sidenav .d-side-profil-name {
    font-size: 12.5px;
    font-weight: 700;
    line-height: 1.2;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #d-sidenav .d-side-profil-rolle { font-size: 11px; color: rgba(255,255,255,0.55); }
}

/* ── „Mehr"-Seite ─────────────────────────────────────────────────────────
   Grundgerüst breakpoint-unabhängig: die Seite ist primär ein Handy-Ziel,
   muss aber auch funktionieren, wenn #/mehr am Tablet/Desktop aufgerufen
   wird. Die Werte entsprechen .m-nav-list/.m-nav-item aus core/mobile.css
   (dort nur <768px definiert) — gleiche Optik, keine Konflikte. */
#mehr-seite.active { display: block; overflow-y: auto; -webkit-overflow-scrolling: touch; }
#mehr-seite .mehr-inhalt {
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
  padding: var(--space-4) var(--space-3) var(--space-6);
}
#mehr-seite .mehr-titel {
  margin: var(--space-2) 2px var(--space-4);
  font-size: var(--fs-page-title);
  font-weight: 700;
  color: var(--brand);
  line-height: 1.2;
}
#mehr-seite .mehr-profil {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: var(--tap);
  padding: 10px var(--space-3);
  margin-bottom: var(--space-4);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  box-shadow: var(--shadow-card);
  color: var(--text);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
#mehr-seite .mehr-profil:active { background: var(--surface-3); }
#mehr-seite .mehr-profil-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
#mehr-seite .mehr-profil-name {
  font-size: 15px;
  font-weight: 700;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#mehr-seite .mehr-profil-rolle { font-size: 12px; color: var(--text-muted); }
#mehr-seite .m-abschnitt {
  margin: var(--space-4) 2px var(--space-2);
  font-family: ui-monospace, 'SFMono-Regular', monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent2);
}
#mehr-seite .m-nav-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}
#mehr-seite .m-nav-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 52px;
  padding: 8px var(--space-3);
  border: 0;
  border-bottom: 1px solid var(--border-light, var(--border));
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
#mehr-seite .m-nav-item:last-child { border-bottom: 0; }
#mehr-seite .m-nav-item:active { background: var(--surface-3); }
#mehr-seite .m-nav-icon {
  width: 20px;
  flex-shrink: 0;
  display: inline-flex;
  color: var(--accent2);
}
#mehr-seite .m-nav-txt { flex: 1; min-width: 0; }
#mehr-seite .m-row-chevron { flex-shrink: 0; order: 3; color: var(--border-strong, var(--border)); font-size: 17px; }
/* Die Kommunikations-Blase hängt sich als letztes Kind an — vor den Chevron
   einreihen (Flex-order), Grundform kommt aus kommunikation/view.css. */
#mehr-seite .m-nav-item .komm-nav-badge { order: 2; margin-left: auto; }
/* Aktion statt Navigation: „Neues Ticket" wie zuvor im Drawer — umrandete
   Fläche in Akzentfarbe, deutlich abgesetzt von den Listen. */
#mehr-seite .mehr-ticket {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--tap);
  margin-top: var(--space-5);
  padding: 12px 14px;
  border: 1px solid rgba(200,169,110,0.55);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--accent2);
  font-family: inherit;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
}
#mehr-seite .mehr-ticket:active { background: var(--accent-soft); }

/* ── Hamburger + Drawer entfallen am Handy ────────────────────────────────
   Der fünfte Bottom-Nav-Tab („Mehr") ist jetzt der Weg zur vollständigen
   Navigation. Diese Regel steht NACH der Einschalt-Regel weiter oben
   (Zeile ~205) und gewinnt durch die Reihenfolge. Der Drawer selbst bleibt
   im Markup/JS erhalten (kein Einstiegspunkt mehr — bewusst rückbaubar). */
@media (max-width: 767px) {
  body #nav-hamburger { display: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   KORREKTURWELLE F1 — EINE KOPFZEILE, NICHT ZWEI
   ─────────────────────────────────────────────────────────────────────────
   Gemessen im Entwurf (quality/design/BEFUND-ABWEICHUNGEN.md):

   • DESKTOP (≥1024px): Es gibt KEIN durchgehendes dunkles Band über die
     volle Breite. Die Seitenleiste beginnt bei y=0 und läuft über die volle
     Höhe; rechts daneben startet sofort der Inhalt mit seinem eigenen Kopf.
     → #topnav entfällt. Alles, was dort hing (Ticket-Knopf, Update-Warnung,
       Testinstanz-Knopf, DB-Punkt, Profil-Menü), wandert in die Seitenleiste
       — als DERSELBE DOM-Knoten, verschoben von core/app.js
       (werkzeugeEinordnen). Kein Neubau, also kein verlorener Handler.

   • HANDY (<768px): Marken-Band + Modulüberschrift standen übereinander und
     kosteten auf einem 390px-Gerät rund 180px. → Der Topnav TRÄGT jetzt den
     Modulnamen (statt des Logos), die modul-eigene .page-title entfällt.

   • TABLET (768–1023px): unverändert. Dort gibt es weder Schiene noch
     Seitenleiste — der Topnav ist die Navigation und bleibt, wie er ist.

   Diese Regeln stehen bewusst am DATEIENDE: `#topnav { display:none }` hat
   dieselbe Spezifität wie der Safe-Area-Block weiter oben und gewinnt nur
   durch die Reihenfolge.
   ══════════════════════════════════════════════════════════════════════════ */

/* Der Modulname im Kopf ist ein reines Handy-Element. */
.topnav-titel { display: none; }

/* ── Desktop: keine obere Leiste ───────────────────────────────────────── */
@media (min-width: 1024px) {
  #topnav { display: none; }

  /* Das Environment-Banner ist KEIN Navigationselement — es warnt vor
     versehentlichen Live-Aktionen und bleibt deshalb oben über die volle
     Breite stehen, auch ohne Topnav. (Es liegt im Body-Flex vor #main und
     braucht dafür keine eigene Regel; der Kommentar hält nur fest, dass das
     Absicht ist.) */

  /* Die Leiste beginnt jetzt an der Oberkante des Fensters und trägt die
     Safe-Area selbst — vorher tat das der Topnav. */
  #d-sidenav { padding-top: var(--safe-t); }

  /* Vor dem Login setzt core/app.js `aside.hidden = true` (keine sichtbaren
     Module → die Leiste wäre eine leere dunkle Spalte neben dem Login).
     Das Attribut allein reichte nicht: die UA-Regel `[hidden]{display:none}`
     verliert gegen den ID-Selektor `#d-sidenav{display:flex}` weiter oben.
     Mit dem Attribut IM Selektor gewinnt sie wieder — ohne !important. */
  #d-sidenav[hidden] { display: none; }

  /* ── Werkzeugzeile über dem Profil ──────────────────────────────────
     Eine Zeile schmaler Symbolknöpfe. Sie ist bewusst KEIN Nachbau: die
     Knoten kommen per appendChild aus dem Topnav, samt Event-Handlern,
     Inline-Styles und Zähler-Blasen aus core/ticket-widget.js. */
  #d-sidenav .d-side-werkzeuge {
    display: flex;
    align-items: center;
    /* Umbrechend statt umschaltend: in 60px passt nur ein Symbol pro Zeile,
       in 236px alle vier. Beim Wischen wandern sie von selbst untereinander
       — ein zweites Layout, das erst einrasten müsste, gibt es nicht. */
    flex-wrap: wrap;
    gap: 2px;
    padding: 2px 6px 6px;
    min-height: 0;
  }
  /* Leer (vor dem Login) → kein Luftpolster über dem Profil. */
  #d-sidenav .d-side-werkzeuge:empty { display: none; }

  /* Gemeinsame Trefferfläche: 32px ist die Untergrenze aus dem Entwurf. */
  #d-sidenav .d-side-werkzeuge > button,
  #d-sidenav .d-side-werkzeuge > .db-status {
    min-width: 32px;
    min-height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius);
  }
  #d-sidenav .d-side-werkzeuge > button:hover {
    background: rgba(255,255,255,0.10);
  }
  /* WICHTIG: Die Zeile darüber setzt display per ID-Selektor und würde damit
     `hidden` überstimmen — der Update-Warnknopf (bis ein Update vorliegt)
     und der Testinstanz-Knopf (bis nach dem Login) wären dauerhaft sichtbar.
     Deshalb dieselbe Kette plus Attribut, das gewinnt wieder. */
  #d-sidenav .d-side-werkzeuge > [hidden] { display: none; }
  /* Der DB-Punkt ist Diagnose, kein Knopf: er bekommt keine Hover-Fläche,
     nur seinen Platz am Ende der Zeile. */
  #d-sidenav .d-side-werkzeuge > .db-status { margin-left: auto; }

  /* Die Warn-Icon-Animation setzt in ihren Keyframes selbst eine Textfarbe
     (--danger-ink). Auf dem dunklen Leistengrund ist die im hellen Modus zu
     dunkel — deshalb hier eigene Keyframes, die NUR den Schein animieren,
     und eine feste Signalfarbe aus dem Token-Satz. */
  #d-sidenav .d-side-werkzeuge .topbar-update-warning { color: var(--danger); }
  #d-sidenav .d-side-werkzeuge .topbar-update-warning.is-pulsing {
    animation-name: d-werkzeug-puls;
  }

  /* Der Testinstanz-Knopf trägt in der Topnav die Klasse .nav-tab (Pillen-
     Padding). In der Schiene stört das — Maße kommen aus der Zeile. */
  #d-sidenav .d-side-werkzeuge .nav-tab,
  #d-sidenav .d-side-werkzeuge .nav-tab-icon {
    padding: 0;
    margin: 0;
    background: transparent;
  }

  /* Das Update-Popover hängt am Knopf, und der steht jetzt unten links.
     Es ist position:fixed — also neben die Leiste, auf Höhe des Auslösers. */
  .update-popover {
    top: auto;
    right: auto;
    bottom: var(--space-4);
    left: calc(var(--d-side, 236px) + var(--space-4));
  }

  /* ── Profil-Menü in der Leiste ──────────────────────────────────────
     core/profile-menu.js baut Knopf + Dropdown in EINEN Wrapper und hängt
     ihn in den Topnav. Der Wrapper wandert mit in die Leiste; sichtbarer
     Auslöser bleibt die Profilzeile (.d-side-profil), die den echten Knopf
     synthetisch klickt. Der Knopf selbst wird also versteckt — nicht
     entfernt, sonst verlöre das Menü seinen Anker und seinen Handler. */
  #d-sidenav .profile-menu-wrap {
    position: static;
    display: block;
    margin: 0;
  }
  /* Vor dem Login trägt der Wrapper `hidden`. Der ID-Selektor oben würde die
     UA-Regel überstimmen — deshalb dieselbe Spezifität plus Attribut. */
  #d-sidenav .profile-menu-wrap[hidden] { display: none; }
  #d-sidenav .profile-menu-btn { display: none; }
  /* position:FIXED, nicht absolute. Die Leiste steht in #main, und #main
     trägt `overflow:hidden` — ein absolut positioniertes Menü würde an der
     Leistenkante abgeschnitten, in der 60px-Schiene also fast vollständig.
     Fest am Fenster verankert (unten links, wo auch der Auslöser sitzt)
     kann nichts clippen, in beiden Leisten-Zuständen. */
  #d-sidenav .profile-menu-dropdown {
    position: fixed;
    top: auto;
    right: auto;
    bottom: calc(var(--space-2) + var(--safe-b));
    left: var(--space-2);
    min-width: 210px;
  }
}

@keyframes d-werkzeug-puls {
  0%, 100% { box-shadow: none; }
  50%      { box-shadow: 0 0 0 6px rgba(255,255,255,0.14); }
}


/* ══════════════════════════════════════════════════════════════════════════
   DIE SCHIENE — DER EINGEKLAPPTE ZUSTAND DER SEITENLEISTE
   ─────────────────────────────────────────────────────────────────────────
   `body.d-rail` ist die 60px-Icon-Schiene aus dem Entwurf: nur Icons,
   zentriert, `padding:13px 0`, Grund `--brand-surface` (#1a1a2e hell /
   #15151e dunkel — beides aus dem Token).

   WER die Klasse setzt, hat sich geändert: bis v5 tat das core/app.js beim
   Modulwechsel, abgeleitet aus `"layout": "rail"` im module.json — die
   Leiste war in Kommunikation und Kalender zwangsweise schmal und liess
   sich nicht aufklappen. Jetzt entscheidet ausschliesslich der Anwender
   über den Umschalter oben in der Leiste (#d-side-toggle); die Wahl gilt
   modulübergreifend und liegt in localStorage['fw-sidenav-eingeklappt'].
   Standard ohne gespeicherte Wahl: aufgeklappt, 236px, auf JEDER Seite.

   Das Maß --d-side: 60px steht in core/desktop.css bei den übrigen
   Layout-Tokens.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 1024px) {

  /* Marke wird zum kleinen Zeichen, der Umschalter rutscht darunter: in
     60px stehen Zeichen und Knopf nicht nebeneinander, ohne dass einer von
     beiden unter seine Trefferfläche fällt. Der Markentext bleibt im DOM
     (Vorlesehilfen), nur die Darstellung schrumpft. */
  /* ── Grundsatz dieses Blocks ────────────────────────────────────────────
     Die Schiene ordnet NICHTS um. Sie ist derselbe Inhalt, nur schmaler
     beschnitten — deshalb bleiben Innenabstände, Zeilenhöhen und
     Symbolgrössen unverändert, und kein Icon wandert beim Umschalten.
     Alles, was hier noch steht, ist entweder nur sichtbar/unsichtbar oder
     betrifft Elemente, die sonst aus dem Bild fielen.                    */

  /* Das Logo bleibt, wo es ist; nur der Umschalter rutscht darunter —
     nebeneinander passen beide nicht in 60px, ohne dass einer unter seine
     Trefferfläche fällt. */
  /* Die beiden Endwerte des Wischens (Gegenstück zu den 236px im
     Grundzustand — siehe die Begründung dort, warum das keine Variable ist). */
  body.d-rail #d-sidenav { width: 60px; }

  /* In 60px ist der Schriftzug nur noch ein Stummel — deshalb weicht er dem
     Umschalter, der hier der einzige Weg zurück ist. Die Zeilenhöhe bleibt
     dieselbe (58px, siehe Grundzustand), damit darunter nichts wandert. */
  body.d-rail:not(.d-side-schwebt) #d-sidenav .d-side-logo { display: none; }
  body.d-rail:not(.d-side-schwebt) #d-sidenav .d-side-marke {
    justify-content: center;
    padding: 16px 0 10px;
  }
  body.d-rail:not(.d-side-schwebt) #d-sidenav .d-side-toggle {
    margin-left: 0;
  }

  /* ── Gelöste Leiste: aufklappen beim Darüberfahren (seit v5.57.0) ──────
     `body.d-side-schwebt` setzt core/app.js, solange der Mauszeiger (oder
     der Tastaturfokus) in der Schiene steht. Die Leiste wird dann wieder
     236px breit, aber ÜBER den Inhalt gelegt: der negative Rand rechts
     hält ihren Platz im Flex-Fluss bei 60px (236 − 176). Breite und Rand
     laufen mit derselben Dauer und Kurve, ihre Summe bleibt in jedem Frame
     60px — der Inhalt daneben bewegt sich also nicht, und kein Modul muss
     neu rechnen. `--d-side` bleibt 60px, weil `body.d-rail` stehen bleibt. */
  body.d-rail #d-sidenav {
    position: relative;
    margin-right: 0;
    /* Schliessen (dieser Übergang gilt beim Entfernen von d-side-schwebt):
       kurz, damit der Inhalt sofort wieder frei ist. */
    transition: width 0.14s ease-in, margin-right 0.14s ease-in, box-shadow 0.14s ease-in;
  }
  body.d-rail.d-side-schwebt #d-sidenav {
    width: 236px;
    margin-right: -176px;
    z-index: 60;
    box-shadow: 8px 0 28px rgba(0,0,0,0.35);
    /* Öffnen: zügig, aber weich auslaufend. Breite und Rand mit DERSELBEN
       Kurve, sonst stimmt die 60px-Summe zwischendurch nicht. */
    transition: width 0.18s ease-out, margin-right 0.18s ease-out, box-shadow 0.18s ease-out;
  }
  @media (prefers-reduced-motion: reduce) {
    body.d-rail #d-sidenav,
    body.d-rail.d-side-schwebt #d-sidenav { transition: none; }
  }

  /* Gruppen-Überschriften: `visibility`, NICHT `display` — damit die
     Zwischenräume ihre Höhe behalten. Mit `display:none` rutschte die
     gesamte Modulliste nach oben, und genau das wäre ein wandernder
     Icon-Satz. */
  body.d-rail:not(.d-side-schwebt) #d-sidenav .d-side-gruppe { visibility: hidden; }

  /* Beschriftungen dagegen ganz weg. Sie nur vom Rand beschneiden zu lassen
     sah aus wie ein Textfehler — in der Schiene stand von jedem Modul noch
     ein angeschnittener Buchstabe („D", „Ka", „Re" …). In der Schiene sind
     nur Symbole zu sehen, so wie vorher auch.
     Das verschiebt KEIN Icon: das Symbol ist das erste Kind der Zeile, sein
     Platz hängt am linken Innenabstand, nicht an dem, was rechts davon
     steht oder eben nicht steht. */
  body.d-rail:not(.d-side-schwebt) #d-sidenav .d-side-label,
  body.d-rail:not(.d-side-schwebt) #d-sidenav .d-side-profil-txt { display: none; }

  /* Die Zähler-Blase sitzt am rechten Ende der Zeile und läge damit
     ausserhalb der 60px. In der Schiene wandert sie an das Icon — und wird
     dabei kleiner.

     Die Rechnung: Die Zeile beginnt 12px vom Rand, ihr Innenabstand links
     ist 10px, das Symbol ist 18px breit — es sitzt also bei 22–40px. In
     voller Grösse (17px hoch, 5px Polsterung, zweistellig ≈ 22px breit) und
     bei 30px Versatz begann die Blase erst bei 42px und stiess bei 64px an;
     abgeschnitten wurde sie an der Kante bei 60px.
     Jetzt: Versatz 19px (= knapp im Symbol), rund 16px breit → 31–47px.
     Auch dreistellig bleibt sie mit ~19px Breite innerhalb der Schiene. */
  body.d-rail:not(.d-side-schwebt) #d-sidenav .komm-nav-badge {
    position: absolute;
    top: 0;
    left: 19px;
    margin-left: 0;
    min-width: 13px;
    height: 13px;
    padding: 0 3px;
    border-radius: 7px;
    font-size: 8.5px;
    /* Ein schmaler Ring in der Leistenfarbe trennt die Blase optisch vom
       Symbol darunter — ohne ihn verschwimmen Rot und Icon ineinander. */
    box-shadow: 0 0 0 1.5px var(--brand-surface);
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   KORREKTURWELLE F1 (Handy) — DER TOPNAV IST DIE MODUL-KOPFZEILE
   ─────────────────────────────────────────────────────────────────────────
   Vorher: Marken-Band „FEWORLD LIVING" (48px) + darunter noch einmal ein
   25px-Modultitel mit Luft ringsum. Zwei Kopfzeilen für dieselbe Aussage.

   Jetzt: eine Kopfzeile, 56px (Maß von .m-head aus core/mobile.css), links
   der Modulname, rechts höchstens zwei Symbolknöpfe. Den Titel setzt
   core/app.js beim Modulwechsel (setzeKopfTitel).

   Bottom-Navigation und „Mehr"-Seite sind davon unberührt: sie hängen an
   #bottomnav bzw. #mehr-seite und lesen weder Titel noch Logo.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  :root {
    /* 48 → 56: Der Balken trägt jetzt den Seitentitel und ersetzt damit die
       Überschrift, die vorher darunter stand. Unterm Strich bleibt das ein
       deutlicher Gewinn, weil Titelzeile + Abstand darunter entfallen. */
    --topnav-h: 56px;
  }

  /* Das Logo hat seine Aufgabe abgegeben — der Titel sagt jetzt, wo man
     ist. (Welche Instanz man vor sich hat, sagt das Env-Banner darüber.) */
  body #topnav img { display: none; }

  body #topnav {
    padding-left: calc(var(--space-3) + var(--safe-l));
  }

  body #topnav .topnav-titel {
    display: block;
    flex: 1;
    min-width: 0;
    font-size: var(--fs-page-title);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.01em;
    color: var(--on-brand);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Der Ticket-Knopf ist am Handy der dritte Knopf und damit einer zu viel.
     Er bleibt nur sichtbar, wenn er wirklich etwas MELDET — dann ist er
     keine Navigation, sondern eine Benachrichtigung. Ohne Blase führt der
     Weg über „Mehr" → Tickets bzw. „+ Neues Ticket".
     !important ist hier nötig: core/ticket-widget.js schreibt
     `display:inline-flex` als Inline-Style bei jedem Zähler-Update. */
  body #topnav #ticket-btn { display: none !important; }
  body #topnav #ticket-btn:has(.ticket-badge:not([hidden])) {
    display: inline-flex !important;
  }

  /* ── Die modul-eigene Überschrift entfällt ──────────────────────────
     Sie steht jetzt oben im Balken. Ausgenommen sind die Einstellungen:
     dort benennt .page-title nicht das Modul, sondern den UNTERbereich
     („E-Mail Vorlage", „Objektdatenbank") — der Balken sagt nur
     „Einstellungen", also wäre das ein echter Informationsverlust. */
  body .page-header > .page-title { display: none; }
  body .settings-section .page-header > .page-title { display: block; }

  /* Ohne Titel ist die Kopfzeile oft nur noch eine Aktionszeile — sie darf
     dann nicht mehr den Abstand eines Seitenkopfs beanspruchen. Enthält sie
     gar nichts Sichtbares mehr, verschwindet sie ganz. */
  body .page-header:not(:has(> :not(.page-title))) { display: none; }
  /* … ausser in den Einstellungen: dort steht der Titel weiterhin, also
     muss auch sein Kopf stehen bleiben. */
  body .settings-section .page-header { display: flex; }
}

/* ══════════════════════════════════════════════════════════════════════════
   TICKET #169 — LEISTEN AM HANDY: DIE BÜHNE ZWISCHEN DEN LEISTEN
   ─────────────────────────────────────────────────────────────────────────
   Befund: Detailansichten (Vertrag, Buchung, …) sind `.modal-overlay`-Fenster
   mit `position:fixed; inset:0` — sie legen sich über Topnav UND Bottom-
   Navigation. Am Handy sieht der Anwender in Verträgen/Buchungen deshalb
   „immer" weder die obere noch die untere Leiste, sobald er etwas öffnet.
   Die Hülle hatte dafür bisher keine Alternative: Ein Modul konnte nur
   Vollbild (beide Leisten weg) oder gar kein Fenster.

   Jetzt bietet die Hülle die ZWEITE Form an — die Push-Ansicht: Das Fenster
   füllt genau die Bühne zwischen den Leisten; Topnav (Modulname, Profil)
   und Bottom-Navigation bleiben sichtbar und bedienbar. Opt-in per Klasse
   `modal-push-mobile` am `.modal-overlay` (analog zu `modal-fullscreen-
   mobile`). Ab 768px ändert die Klasse NICHTS — Tablet und Desktop behalten
   ihr zentriertes bzw. Spalten-Fenster.

   WARUM `position:absolute` IM MODUL-CONTAINER STATT `fixed` MIT RECHNEREI:
   Die Bühne ist schlicht die Fläche, die das Flex-Layout dem aktiven
   `[data-module-view]` lässt — unter Env-Banner + Topnav, über dem Auslauf,
   den `#main` für die Bottom-Navigation reserviert (Block „KORREKTUR
   v10.51.3"). Ein `fixed`-Fenster müsste diese Geometrie nachrechnen
   (Banner-Höhe? Safe-Area? Topnav 48 oder 56?) und läge bei jeder Änderung
   daneben. Ein `absolute`-Kind des Modul-Containers erbt sie automatisch:
   `[data-module-view]` steht auf `overflow:hidden` und beschneidet auf
   seinen Inhaltskasten — exakt die Bühne. `position:relative` bekommt der
   Container nur, wenn er tatsächlich eine Push-Ansicht enthält (`:has`),
   damit absolut positionierte Elemente anderer Module ihren Bezug behalten.

   Bedingungen für ein Modul, das die Klasse setzt (Fenster-Höhe ist jetzt
   die Bühne, nicht der Bildschirm):
     • keine eigenen `height:100dvh` / `max-height:100dvh` am `.modal`
       (Fenster füllt die Bühne über `height:100%`),
     • kein `--safe-t` im Fenster-Kopf — die Statusleiste deckt der Topnav,
     • Aktionsleisten nicht als `.m-actionbar.solo` (bottom:0 läge hinter
       der Bottom-Navigation), sondern als `.m-actionbar` (sitzt darüber),
     • ein Zurück-/Schliessen-Element bleibt Pflicht (Bottom-Nav wechselt
       das Modul, schliesst aber kein Fenster).
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  body [data-module-view]:has(.modal-overlay.modal-push-mobile) {
    position: relative;
  }
  /* Browser ohne `:has()` (Firefox < 121, Safari < 15.4) würden die Regel
     oben verwerfen — das Push-Fenster läge dann wieder über beiden Leisten.
     Rückfall: Bezug für die Module setzen, die die Push-Ansicht nutzen
     (bhbgv, buchungen — ihre absoluten Kinder haben eigene Bezugselemente). */
  @supports not selector(:has(a)) {
    body [data-module-view="bhbgv"],
    body [data-module-view="buchungen"] {
      position: relative;
    }
  }
  body .modal-overlay.modal-push-mobile {
    position: absolute;
    inset: 0;
    /* Push-Seite, kein Dialog: kein abgedunkelter Hintergrund, kein Rand. */
    padding: 0;
    align-items: stretch;
    justify-content: stretch;
    background: var(--bg);
    overflow: hidden;              /* gescrollt wird im Fenster, nicht hier */
  }
  body .modal-overlay.modal-push-mobile > .modal,
  body .modal-overlay.modal-push-mobile > .modal-fullscreen-mobile {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    height: 100%;
    max-height: 100%;
    margin: 0;
    border-radius: 0;
    box-shadow: none;
    /* Kein Safe-Area-Zuschlag: oben sitzt der Topnav, unten die Bottom-
       Navigation — beide tragen ihre Safe-Area selbst. */
    padding: 0;
    padding-bottom: var(--space-4);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  /* Ein Fenster, das eine feste .m-actionbar mitbringt, hält sich den Platz
     dafür frei (dieselbe Summe wie .m-hat-actionbar in core/mobile.css). */
  body .modal-overlay.modal-push-mobile > .modal.m-hat-actionbar,
  body .modal-overlay.modal-push-mobile > .modal-fullscreen-mobile.m-hat-actionbar {
    padding-bottom: calc(66px + var(--bottomnav-h) + var(--safe-b));
  }
}


/* ══════════════════════════════════════════════════════════════════════════
   INFO-i — Feldbeschreibungen als Blase (v5.60.0)
   ─────────────────────────────────────────────────────────────────────────
   Logik und Benutzung: core/info.js. Ein Hinweis mit `.info-text` wird zur
   versteckten Quelle (`.info-quelle`), daneben an der Beschriftung steht ein
   kleines „i". Das „i" ist bewusst leise (grau, halbtransparent) — es soll
   da sein, aber nicht im Vordergrund stehen. Die sichtbare Grösse bleibt
   15px; die Trefferfläche wächst unsichtbar über ::after (Touch: ≥44px).
   ══════════════════════════════════════════════════════════════════════════ */
.info-quelle { display: none !important; }

.info-i {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 15px;
  height: 15px;
  margin: 0 0 0 5px;
  padding: 0;
  border: 1px solid currentColor;
  border-radius: 50%;
  background: transparent;
  color: var(--text-muted);
  opacity: 0.55;
  font: italic 700 10px/1 Georgia, 'Times New Roman', serif;
  letter-spacing: 0;
  text-transform: none;
  vertical-align: 1px;
  cursor: help;
  transition: opacity 0.12s, color 0.12s;
  -webkit-tap-highlight-color: transparent;
}
.info-i::after {
  content: '';
  position: absolute;
  inset: -8px;
}
@media (pointer: coarse) {
  .info-i::after { inset: -15px; }
}
.info-i:hover,
.info-i:focus-visible,
.info-i[aria-expanded="true"] {
  opacity: 1;
  color: var(--accent);
}
.info-i:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Ohne Beschriftung: das „i" steht allein an der Stelle des Hinweises. */
.info-i--inline { margin: 2px 0 0; }

.info-blase {
  position: fixed;
  z-index: 100000;
  max-width: min(320px, calc(100vw - 32px));
  padding: 9px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
  font-size: 12.5px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.45;
  letter-spacing: normal;
  text-transform: none;
  text-align: left;
  white-space: normal;
  overflow-wrap: anywhere;
  opacity: 0;
  transition: opacity 0.12s;
}
.info-blase.ist-offen { opacity: 1; }
.info-blase[hidden] { display: none; }
.info-blase a { color: var(--accent); }
.info-blase code { font-size: 11.5px; }
@media (prefers-reduced-motion: reduce) {
  .info-blase, .info-i { transition: none; }
}

/* ── Reiniger-Meldung ─────────────────────────────────────────────────────
   Meldungsleiste „n Meldungen von Reinigern offen" (core/reiniger-meldung.js),
   Design-Canvas D10/D11.

   EINE Form für alle Geräte statt zweier: eine 40px hohe Leiste GANZ OBEN im
   Body. Auf der Testinstanz steht sie damit ÜBER dem roten „TESTINSTANZ"-Band
   — eine Meldung aus dem Cockpit ist dringender als der Hinweis, auf welcher
   Instanz man gerade arbeitet. Sie liegt im FLUSS (body ist eine Flex-Spalte)
   und verdeckt deshalb nichts, sondern schiebt Banner, Kopf und Inhalt um
   ihre eigene Höhe nach unten.

   Am Handy bleibt derselbe Knoten stehen, nur kürzer: Kurztexte, das Wort
   „offen" und die Beschriftung des Links verschwinden per CSS. Dadurch gibt
   es beim Drehen des Geräts nichts neu zu zeichnen.

   Alle Farben über Tokens → der Dunkelmodus kommt ohne zweiten Block mit;
   --warning-soft/--warning-ink lösen exakt auf die Canvas-Werte auf
   (hell #fbf0df/#8f5510, dunkel #362b1b/#e5ba7d).

   Der frühere Knopf neben dem Testinstanz-Knopf ist entfallen.            */
.rm-bar {
  flex: none;
  position: relative;
  z-index: 9300;              /* über dem Env-Banner (9200) — sie steht darüber */
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  height: 40px;
  padding: 0 var(--space-4);
  border: 0;
  border-bottom: 1px solid var(--border);
  background: var(--warning-soft);
  color: var(--warning-ink);
  font-family: inherit;
  font-size: 12.5px;
  line-height: 1.2;
  text-align: left;
  cursor: pointer;
}
.rm-bar-icon { flex: none; display: flex; }
.rm-bar-titel {
  flex: none;
  font-weight: 600;
  white-space: nowrap;
}
/* Kurztexte der letzten zwei Meldungen: leiser als die Zahl, und das Erste,
   was bei knapper Breite weggekürzt wird (min-width:0 + Ellipse) — die Zahl
   und der Link müssen immer vollständig lesbar bleiben. */
.rm-bar-kurz {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-weight: 400;
  font-size: 12px;
  opacity: .85;
}
.rm-bar-fuell { flex: 1 1 0; min-width: 0; }
.rm-bar-link {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-weight: 700;
  font-size: 12px;
  white-space: nowrap;
}
.rm-bar:hover .rm-bar-link-text { text-decoration: underline; }
.rm-bar:focus-visible { outline: 2px solid var(--warning-ink); outline-offset: -2px; }

/* ── Handy (<768px) ──────────────────────────────────────────────────────
   Nur noch „n Meldungen von Reinigern" + Pfeil, EINE Zeile, volle Breite als
   Tippziel (≥44px). Weil die Leiste hier der oberste Balken ist, färbt SIE
   die Safe-Area ein (`viewport-fit=cover`) — Env-Banner und Topnav dürfen sie
   dann nicht ein zweites Mal reservieren. Statt deren Regeln zu duplizieren,
   wird bei ihnen `--safe-t` lokal auf 0 gesetzt: das entschärft in einem Zug
   das padding-top UND den Farbverlauf-Stopp des Env-Banners. */
@media (max-width: 767px) {
  .rm-bar {
    height: auto;
    min-height: calc(var(--tap) + var(--safe-t));
    padding: 0 var(--space-3);
    padding-top: var(--safe-t);
    padding-left: calc(var(--space-3) + var(--safe-l));
    padding-right: calc(var(--space-3) + var(--safe-r));
    gap: var(--space-2);
    font-size: 12px;
  }
  .rm-bar-kurz,
  .rm-bar-offen,
  .rm-bar-link-text { display: none; }
  .rm-bar-titel {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .rm-bar-fuell { display: none; }
  body:has(#reiniger-meldung-bar) #env-banner,
  body:has(#reiniger-meldung-bar) #topnav { --safe-t: 0px; }
}
@media print {
  .rm-bar { display: none; }
}

/* ── Modul-Schalter (Login-Host, PLAN §3.5 C0.4) ──────────────────────────
   core/app.js modulSchalterAnwenden(): Elemente mit data-modul="<id>", deren
   Modul der Mandant nicht gebucht hat. !important, damit style.display-
   Umschalter der Module (Rechte, Reiter) sie nicht wieder einblenden. */
.modul-nicht-gebucht { display: none !important; }

/* Hinweis „Testphase abgelaufen" — steht im Fluss vor #main (body ist eine
   Flex-Spalte), überdeckt also weder Kopfzeile noch Inhalt. */
.trial-banner {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: 40px;
  padding: 8px var(--space-4);
  border-bottom: 1px solid var(--border);
  background: var(--warning-soft);
  color: var(--warning-ink);
  font-size: 12.5px;
  line-height: 1.35;
}
.trial-banner-text { min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 767px) {
  .trial-banner {
    padding: 6px var(--space-3);
    padding-left: calc(var(--space-3) + var(--safe-l, 0px));
    padding-right: calc(var(--space-3) + var(--safe-r, 0px));
    font-size: 12px;
  }
}
@media print {
  .trial-banner { display: none; }
}

/* ── Support-Zugang „Als Mandant öffnen“ (Login-Host, PLAN §6.9) ──────────
   core/support.js. Oranger Balken im Fluss vor #main (wie der Hinweis
   „Testphase abgelaufen“ — kein Überlappen der Kopfzeile), dazu ein oranger
   Rahmen über den Environment-Rahmen (#env-banner.env-support). Feste
   Farben in beiden Farbmodi: die Markierung soll immer gleich auffallen. */
.env-banner.env-support { --env-rgb: 234, 88, 12; }
.support-balken {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2, 8px);
  min-height: 44px;
  padding: 4px var(--space-4, 16px);
  background: #c2410c;
  color: #fff;
  font-size: 13px;
  line-height: 1.3;
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .18);
}
.support-balken-icon { display: inline-flex; flex: none; }
.support-balken-text { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.support-balken-mandant { font-weight: 700; }
.support-balken-zusatz { opacity: .9; }
.support-balken-zeit {
  flex: none;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, .18);
  white-space: nowrap;
}
.support-balken-knapp .support-balken-zeit { background: #7f1d1d; }
.support-balken-ende {
  flex: none;
  min-height: 36px;
  min-width: 44px;
  padding: 0 14px;
  border: 1px solid rgba(255, 255, 255, .75);
  border-radius: 8px;
  background: transparent;
  color: #fff;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.support-balken-ende:hover { background: rgba(255, 255, 255, .15); }
.support-balken-ende:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.support-balken-ende:disabled { opacity: .6; cursor: default; }
@media (max-width: 767px) {
  .support-balken {
    padding: 4px var(--space-3, 12px);
    padding-left: calc(var(--space-3, 12px) + var(--safe-l, 0px));
    padding-right: calc(var(--space-3, 12px) + var(--safe-r, 0px));
    font-size: 12.5px;
  }
  .support-balken-ende { min-height: 44px; }
}
@media (max-width: 479px) {
  .support-balken-icon, .support-balken-zusatz { display: none; }
}
@media print {
  .support-balken { display: none; }
}

/* Bestätigungs-Dialog „Wirklich Daten für <Mandant> ändern?“ */
.support-dlg-huelle {
  position: fixed;
  inset: 0;
  z-index: 2147483100;            /* über Modals und dem Env-Rahmen */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: var(--overlay, rgba(0, 0, 0, .5));
}
.support-dlg {
  width: 100%;
  max-width: 440px;
  max-height: calc(100dvh - 32px);
  overflow: auto;
  box-sizing: border-box;
  padding: 22px 22px 18px;
  border-radius: 14px;
  border-top: 5px solid #c2410c;
  background: var(--surface, #fff);
  color: var(--text, #1a1a2e);
  box-shadow: 0 12px 40px rgba(0, 0, 0, .3);
}
.support-dlg-titel { margin: 0 0 8px; font-size: 18px; line-height: 1.3; }
.support-dlg-text { margin: 0 0 18px; color: var(--text-muted, #5b6070); line-height: 1.45; }
.support-dlg-knoepfe { display: flex; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.support-dlg-knoepfe .btn { min-height: 44px; }
.support-dlg-ja { background: #c2410c; border-color: #c2410c; color: #fff; }
.support-dlg-ja:hover { background: #9a3412; border-color: #9a3412; }
@media (max-width: 479px) {
  .support-dlg-huelle {
    align-items: flex-end;
    padding: 0 0 calc(8px + var(--safe-b, 0px));
  }
  .support-dlg { max-width: none; border-radius: 14px 14px 0 0; }
  .support-dlg-knoepfe { flex-direction: column-reverse; }
  .support-dlg-knoepfe .btn { width: 100%; justify-content: center; text-align: center; }
}
