/* ═══════════════════════════════════════════════════════════════════════
   Base: Reset + wiederverwendbare Komponenten (shell-unabhängig)
   ═══════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* `hidden` muss IMMER verstecken.
   Der Browser bringt dafür nur `[hidden] { display: none }` mit — dieselbe
   Spezifität wie jede eigene Klasse. Jede Regel wie `.foo { display: flex }`
   gewinnt daher, weil sie später kommt, und das Element bleibt trotz
   gesetztem Attribut stehen. Genau so blieb der Knopf „Fahrzeug & Zeitraum
   ändern" sichtbar, bevor überhaupt etwas ausgewählt war. Statt die Falle
   an jeder Stelle einzeln zu entschärfen, gilt sie hier ein für alle Mal. */
[hidden] { display: none !important; }

html { height: 100%; }
body {
  height: 100dvh;
  font-family: var(--font);
  font-size: var(--fs-md);
  background: var(--bg0);
  color: var(--text);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  /* Doppeltipp-Zoom auf iOS verhindern; Mapbox regelt sein eigenes
     Pinch-Zoom auf der Canvas unabhängig davon selbst. */
  touch-action: manipulation;
}

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input, select { font: inherit; }
a { color: inherit; text-decoration: none; }
ul { list-style: none; }
svg { display: block; flex-shrink: 0; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--bg3); border-radius: var(--r-full); }
::-webkit-scrollbar-track { background: transparent; }

/* Telemetrie-Zahlen laufen nicht (Tachometer, Koordinaten) */
.num { font-variant-numeric: tabular-nums; }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  padding: 12px var(--s4);
  border-radius: var(--r-md);
  font-size: var(--fs-md); font-weight: 600;
  transition: background var(--t-fast), color var(--t-fast), border-color var(--t-fast), opacity var(--t-fast);
  white-space: nowrap;
  /* 48px = Material-Mindestmaß für Touch-Ziele; 40px waren gerade auf dem
     Handy mit Handschuhen kaum sicher zu treffen. */
  min-height: 48px;
}
.btn:disabled { opacity: .45; cursor: not-allowed; }

.btn--primary { background: var(--accent); color: var(--accent-ink); }
.btn--primary:hover:not(:disabled) { background: var(--accent-strong); }

.btn--ghost { background: var(--bg2); color: var(--text-dim); border: 1px solid var(--border); }
.btn--ghost:hover:not(:disabled) { background: var(--bg3); color: var(--text); }

.btn--danger { background: var(--err-dim); color: var(--err); }
.btn--danger:hover:not(:disabled) { background: rgba(248, 113, 113, .25); }

.btn--icon {
  padding: 0; width: 48px; min-width: 48px; height: 48px;
  border-radius: var(--r-md);
  color: var(--text-muted);
}
.btn--icon:hover { background: var(--bg2); color: var(--text); }
/* Kompakte Variante für enge Kopfzeilen (z. B. neben einer .section-label
   im Fahrtenbuch-Fahrzeugpanel) — die volle 48px-Touch-Zielgröße wirkte dort
   neben der kleinen Beschriftung unförmig breit. */
.btn--icon-sm { width: 32px; min-width: 32px; height: 32px; }

.btn--sm { min-height: 40px; padding: 8px var(--s3); font-size: var(--fs-sm); }

/* ── Formfelder ──────────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field > label {
  font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .7px;
  color: var(--text-muted);
}
.input,
.field input, .field select {
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text);
  padding: 12px var(--s3);
  min-height: 48px;
  outline: none;
  transition: border-color var(--t-fast);
  width: 100%;
  min-width: 0;
}
.input:focus,
.field input:focus, .field select:focus { border-color: var(--accent); }
/* Gesperrte Formulare (s. "Nur ansehen", vehicleEdit.js lockFormIfReadOnly)
   waren sonst optisch nicht von normalen Feldern zu unterscheiden — nur
   .btn:disabled hatte bisher eine eigene Optik, Inputs/Selects keine. */
.field input:disabled, .field select:disabled,
.input:disabled { opacity: .5; cursor: not-allowed; }
.input::placeholder,
.field input::placeholder { color: var(--text-muted); }

/* Symbolauswahl im Fahrzeug-Dialog: Buttons statt <select>, damit die
   Symbole selbst sichtbar sind. Grid bricht auf schmalen Sheets um. */
/* Zusammengeklappt zeigt die Auswahl nur das gewählte Symbol — 17 Kacheln
   nahmen im Dialog sonst fünf Reihen ein. */
.iconpick-wrap.is-collapsed .iconpick { display: none; }
.iconpick-current {
  display: none; align-items: center; gap: var(--s3);
  padding: 8px 10px;
  background: var(--bg2); border: 1px solid var(--border);
  border-radius: var(--r-md);
  width: 100%; cursor: pointer; color: var(--text);
  min-height: 48px;
}
.iconpick-wrap.is-collapsed .iconpick-current { display: flex; }
.iconpick-current__name { flex: 1; text-align: left; font-size: var(--fs-sm); font-weight: 600; }
.iconpick-current__hint { font-size: var(--fs-xs); color: var(--text-muted); }
.iconpick-current svg { color: var(--accent); }
.iconpick-current:disabled, .iconpick__opt:disabled { opacity: .5; cursor: not-allowed; }

.iconpick {
  display: grid; gap: var(--s2);
  grid-template-columns: repeat(auto-fill, minmax(66px, 1fr));
}
.iconpick__opt {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 6px 3px;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: border-color var(--t-fast), color var(--t-fast), background var(--t-fast);
}
.iconpick__opt span {
  font-size: 10px; line-height: 1.15; text-align: center;
  /* lange Einzelwörter („Mähdrescher“) umbrechen statt abschneiden; hyphens
     trennt an Silben (html lang="de"), anywhere ist nur die Notbremse */
  hyphens: auto;
  overflow-wrap: anywhere;
}
.iconpick__opt:hover { color: var(--text); border-color: var(--text-muted); }
.iconpick__opt.is-on {
  color: var(--accent); border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.field select {
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%237e93ac' stroke-width='1.8' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
  padding-right: 34px;
}
/* Native Date/Time-Picker-Icons auf Dunkel invertieren */
input[type=date]::-webkit-calendar-picker-indicator,
input[type=time]::-webkit-calendar-picker-indicator { filter: invert(.7); }

/* ── Durchsuchbares Select (searchSelect.js) ────────────────────────── */
.ssel { position: relative; }
.ssel__native { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.ssel__input {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%237e93ac' stroke-width='1.8' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
  padding-right: 34px;
}
.ssel__input::placeholder { color: var(--text-muted); opacity: 1; }
/* An document.body angehängt (s. searchSelect.js), NICHT ins .ssel
   verschachtelt — sonst schneidet jeder scrollbare/gefilterte Vorfahre
   (allen voran .dialog__body: overflow-y:auto) die Liste an dessen Rand ab.
   Bei einem einfeldrigen Dialog wie "Fahrzeug aktivieren" reichte die
   sichtbare Höhe oft nicht mal bis zur Knopfzeile — die Liste durfte optisch
   nie über den Formularbereich hinausragen. Position/Breite kommen deshalb
   dynamisch aus reposition() (JS), nicht aus CSS. */
.ssel__list {
  position: fixed;
  background: var(--bg2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  box-shadow: var(--e2);
  max-height: 240px; overflow-y: auto;
  z-index: var(--z-toast);
}
.ssel__opt { display: block; width: 100%; text-align: left; padding: 10px var(--s3); font-size: var(--fs-sm); }
.ssel__opt:hover, .ssel__opt.is-selected { background: var(--bg3); }
.ssel__empty { padding: 10px var(--s3); font-size: var(--fs-sm); color: var(--text-muted); }
.ssel__more {
  padding: 8px var(--s3); font-size: var(--fs-xs); color: var(--text-muted);
  border-top: 1px solid var(--border);
}

/* ── Karten & Panels ─────────────────────────────────────────────────── */
.card {
  background: var(--bg1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--e1);
}
.glass {
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--e2);
}

.section-label {
  font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .8px;
  color: var(--text-muted);
}

/* ── Badges & Status ─────────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 10px;
  border-radius: var(--r-full);
  font-size: var(--fs-xs); font-weight: 700;
}
.badge--ok   { background: var(--ok-dim);   color: var(--ok); }
.badge--warn { background: var(--warn-dim); color: var(--warn); }
.badge--err  { background: var(--err-dim);  color: var(--err); }
.badge--idle { background: var(--idle-dim); color: var(--text-dim); }

.dot { width: 9px; height: 9px; border-radius: var(--r-full); flex-shrink: 0; }
.dot--online  { background: var(--ok); box-shadow: 0 0 8px var(--ok); }
.dot--offline { background: var(--idle); }
.dot--unknown { background: var(--warn); }

/* Live-Verbindungsanzeige */
.live-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); font-weight: 700; color: var(--text-muted);
}
.live-chip .dot { width: 7px; height: 7px; }
.live-chip.is-live { color: var(--ok); }
.live-chip.is-live .dot { background: var(--ok); animation: livePulse 2s ease infinite; }
.live-chip:not(.is-live) .dot { background: var(--warn); }
@keyframes livePulse { 50% { opacity: .4; } }

/* ── Fahrzeug-Marker (Mapbox) ────────────────────────────────────────── */
/* WICHTIG: Das Root-Element (.vmark-wrap) wird von Mapbox per transform
   positioniert — es darf selbst NIE transform/transition tragen.
   Alle Effekte liegen auf dem inneren Kreis (.vmark). */
/* KEIN position hier — .mapboxgl-marker (position:absolute) muss gelten,
   sonst landen die Marker im Layout-Fluss statt an der Koordinate. */
.vmark-wrap {
  width: 38px; height: 38px;
  cursor: pointer;
  border: none; background: none; padding: 0;
}
.vmark {
  position: relative;
  width: 100%; height: 100%;
  border-radius: var(--r-full);
  border: 3px solid #fff;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--e2);
  color: #fff;
  transition: transform var(--t-fast), border-color var(--t-fast);
}
.vmark-wrap:hover .vmark { transform: scale(1.12); }
.vmark--online  { background: var(--accent-strong); }
.vmark--offline { background: var(--idle); }
.vmark--unknown { background: var(--warn); }
.vmark--selected { transform: scale(1.15); }
/* Der Auswahlring hatte die Akzentfarbe fest verdrahtet — ein offline stehendes
   Fahrzeug leuchtete beim Antippen grün und sah dadurch gesund aus. Ring und
   Schein folgen jetzt dem Status. */
.vmark--selected.vmark--online {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 45%, transparent), var(--e2);
}
.vmark--selected.vmark--offline {
  border-color: var(--err);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--err) 50%, transparent), var(--e2);
}
.vmark--selected.vmark--unknown {
  border-color: var(--warn);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 50%, transparent), var(--e2);
}
.vmark--moving::after {
  content: ''; position: absolute; inset: -8px;
  border-radius: var(--r-full);
  border: 2px solid var(--accent);
  animation: markPulse 1.6s ease-out infinite;
}
@keyframes markPulse {
  from { transform: scale(.7); opacity: .8; }
  to   { transform: scale(1.2); opacity: 0; }
}

/* Label über dem Marker: Name (+ Geschwindigkeit wenn online).
   Passt beides nicht in eine Zeile, bricht das Label um (max-width). */
.vmark-label {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap;
  column-gap: 6px; row-gap: 1px;
  width: max-content;
  max-width: 150px;
  text-align: center;
  line-height: 1.3;
  background: var(--glass-bg);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 3px 10px;
  font-size: var(--fs-xs); font-weight: 700;
  color: var(--text);
  pointer-events: none;
  box-shadow: var(--e1);
}
.vmark-label__name { white-space: normal; overflow-wrap: break-word; max-width: 130px; }
/* Mehrere Beacons am selben Fahrzeug/Punkt (s. setBeaconMarkers in mapkit.js)
   sitzen enger nebeneinander als Fahrzeuge — mit voller Label-Breite (130px)
   ragen die Namensschilder ineinander. Schmalerer Name UND Zeilenumbruch
   (statt Überlappung) für diesen Fall. */
.vmark-label--tight .vmark-label__name { max-width: 64px; }
.vmark-label__speed { color: var(--ok); white-space: nowrap; }
.vmark-label__speed:empty { display: none; }

/* Cluster-Marker: fasst nahe Fahrzeuge beim Rauszoomen zusammen */
.vcluster {
  width: 46px; height: 46px;
  border-radius: var(--r-full);
  background: var(--accent-strong);
  border: 3px solid #fff;
  color: #fff;
  font-size: 16px; font-weight: 800;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--e2);
  cursor: pointer;
  padding: 0;
}
.vcluster:hover { background: var(--accent); color: var(--accent-ink); }

/* ── Fahrzeugliste (Desktop-Panel + Mobile-Sheet identisch) ─────────── */
.vlist { overflow-y: auto; overscroll-behavior: contain; }
.vitem {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; text-align: left;
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--border);
  transition: background var(--t-fast);
  min-height: 60px;
}
.vitem:hover, .vitem.is-selected { background: var(--bg2); }
.vitem.is-selected { box-shadow: inset 3px 0 0 var(--accent); }
.vitem__icon {
  display: inline-flex; flex: 0 0 auto;
  color: var(--text-muted); margin-left: -2px;
}
.vitem.is-selected .vitem__icon { color: var(--accent); }
.vitem__body { flex: 1; min-width: 0; }
.vitem__name {
  display: block;
  font-size: var(--fs-md); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Eigene Zeile fürs Kennzeichen — vorher lief es dem Namen als reiner
   Inline-Text hinterher und wanderte bei langen Namen mitten in den
   Fließtext der Kilometerzeile statt sauber darunterzustehen. */
.vitem__platerow { display: block; margin-top: 3px; }
.vitem__ign {
  display: inline-flex; vertical-align: -2px;
  margin-left: 5px; color: var(--ok);
}
.vitem__meta { display: block; font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px; }
.vitem__plate {
  display: inline-block; margin-right: 6px; padding: 1px 6px;
  border: 1px solid var(--border-strong); border-radius: 4px;
  font-weight: 700; color: var(--text-dim); letter-spacing: .3px;
}

/* Beacon-Zeile in derselben Liste (erbt .vitem) — gedimmt, wenn länger keine
   Sichtung mehr einging (s. STALE_AFTER_MS in views/beacons.js). */
.beacon-item.is-stale { opacity: .55; }
.beacon-item.is-stale:hover { opacity: 1; }

/* Fahrzeuge/Beacons — Umschalter oben in der Kartenliste UND auf der
   Flotten-Seite (dieselbe Klasse, zwei Einsatzorte). */
.list-modes { display: flex; gap: var(--s2); padding: 0 0 var(--s3); }
.list-modes__tab {
  display: flex; align-items: center; gap: 6px;
  padding: 7px var(--s3);
  border-radius: var(--r-full);
  font-size: var(--fs-xs); font-weight: 700;
  color: var(--text-muted); background: var(--bg2);
  transition: background var(--t-fast), color var(--t-fast);
}
.list-modes__tab:hover { color: var(--text-dim); }
.list-modes__tab.is-on { color: var(--accent-ink); background: var(--accent); }

/* ── Fahrzeug-Untergruppen (Listen-Sektionen) ───────────────────────── */
.vgroup__head {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s3) var(--s4) var(--s2);
  font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .6px;
  color: var(--text-muted);
  background: var(--bg1);
  position: sticky; top: 0; z-index: 1;
}
.vgroup__dot { width: 8px; height: 8px; border-radius: var(--r-full); flex-shrink: 0; }
.vgroup__count { color: var(--text-muted); font-weight: 600; }
.vitem__speed { font-size: var(--fs-sm); font-weight: 700; color: var(--ok); white-space: nowrap; }
.vitem__speed.is-idle { color: var(--text-muted); font-weight: 500; }

/* ── Detail-Statistiken ──────────────────────────────────────────────── */
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.stat {
  background: var(--bg2);
  border-radius: var(--r-md);
  padding: var(--s3);
}
.stat__label { font-size: var(--fs-xs); color: var(--text-muted); margin-bottom: 4px; }
.stat__value { font-size: var(--fs-lg); font-weight: 700; }
.stat__value small { font-size: var(--fs-xs); font-weight: 500; color: var(--text-muted); }

/* ── Tabellen ────────────────────────────────────────────────────────── */
.table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.table th {
  text-align: left; padding: var(--s3) var(--s4);
  font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .6px;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
}
.table td { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--border); }
.table tr:last-child td { border-bottom: none; }
.table tbody tr { transition: background var(--t-fast); }
.table tbody tr:hover { background: var(--bg2); }

/* ── Flottenübersicht (Tabelle über alle Fahrzeuge) ─────────────────────── */
.fleet-table__wrap { overflow-x: auto; }
.fleet-table { white-space: nowrap; }
.fleet-table tbody tr { cursor: pointer; }
.fleet-table th[data-sort] { cursor: pointer; user-select: none; }
.fleet-table th[data-sort]:hover { color: var(--text); }
.fleet-table th.is-sorted { color: var(--text); }
.fleet-sort-arrow { display: inline-flex; vertical-align: -2px; margin-left: 3px; transition: transform var(--t-fast); }
.fleet-sort-arrow.is-desc { transform: rotate(180deg); }
.fleet-table__actions { display: flex; gap: 6px; justify-content: flex-end; }
.fleet-table__count { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; align-self: center; margin-left: auto; }
.fleet-pager {
  display: flex; align-items: center; justify-content: center; gap: var(--s4);
  padding: var(--s4) 0; font-size: var(--fs-sm); color: var(--text-muted);
}
.fleet-map-modal { width: 100%; height: 360px; border-radius: 0 0 var(--r-lg) var(--r-lg); overflow: hidden; }

/* ── Fortschrittsbalken ──────────────────────────────────────────────── */
.bar { background: var(--bg3); border-radius: var(--r-full); height: 7px; overflow: hidden; }
.bar__fill { height: 100%; border-radius: var(--r-full); transition: width .5s ease; }
.bar__fill--ok   { background: var(--ok); }
.bar__fill--warn { background: var(--warn); }
.bar__fill--err  { background: var(--err); }

/* ── Zustände: Laden / Leer / Fehler ─────────────────────────────────── */
.spinner {
  width: 20px; height: 20px;
  border: 2.5px solid var(--bg3);
  border-top-color: var(--accent);
  border-radius: var(--r-full);
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s3); padding: var(--s7) var(--s4);
  color: var(--text-muted); font-size: var(--fs-sm);
  text-align: center;
}
.state svg { color: var(--bg3); }
.state--error { color: var(--err); }

.skeleton {
  background: linear-gradient(90deg, var(--bg2) 25%, var(--bg3) 50%, var(--bg2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite;
  border-radius: var(--r-md);
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* ── Toast ───────────────────────────────────────────────────────────── */
#toasts {
  position: fixed; bottom: calc(var(--s5) + env(safe-area-inset-bottom));
  left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; gap: var(--s2);
  z-index: var(--z-toast);
  pointer-events: none;
}
.toast {
  background: var(--bg3);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  padding: var(--s3) var(--s5);
  font-size: var(--fs-sm); font-weight: 600;
  box-shadow: var(--e3);
  animation: toastIn var(--t-med) both;
  display: flex; align-items: center; gap: var(--s2);
}
.toast--err { border-color: var(--err); color: var(--err); }
.toast--ok  { border-color: var(--ok); color: var(--ok); }
@keyframes toastIn { from { opacity: 0; transform: translateY(8px); } }

/* ── Dialog (ersetzt confirm()) ──────────────────────────────────────── */
.dialog-backdrop {
  position: fixed; inset: 0;
  background: rgba(4, 8, 16, .7);
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
  z-index: var(--z-modal);
  display: flex; align-items: center; justify-content: center;
  padding: var(--s5);
  animation: fadeIn var(--t-fast) both;
}
.dialog {
  background: var(--bg1);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  padding: var(--s6);
  max-width: 380px; width: 100%;
  box-shadow: var(--e3);
  /* Lange Dialoge (Fahrzeug bearbeiten) sprengten auf dem Handy den Schirm —
     die Knöpfe lagen weit unterhalb und waren nur durch langes Scrollen
     erreichbar. Jetzt scrollt NUR der Inhalt, Titel und Knöpfe bleiben stehen. */
  max-height: min(92dvh, 900px);
  display: flex; flex-direction: column;
  padding-bottom: 0;
}
/* Flex statt Fliesstext: Titel mit vorangestelltem Symbol brachen sonst um —
   das Symbol stand allein in der ersten Zeile, der Text darunter. */
.dialog__title {
  font-size: var(--fs-lg); font-weight: 700; margin-bottom: var(--s2); flex-shrink: 0;
  display: flex; align-items: center; gap: var(--s2);
}
.dialog__title svg { flex: none; }
.dialog__text  { font-size: var(--fs-sm); color: var(--text-dim); margin-bottom: var(--s5); }
.dialog__body  { overflow-y: auto; overscroll-behavior: contain; flex: 1; min-height: 0; }
.dialog__actions {
  display: flex; gap: var(--s2); justify-content: flex-end;
  flex-shrink: 0;
  padding: var(--s4) 0 var(--s6);
  background: var(--bg1);
  /* auf dem Handy Platz für die Home-Leiste */
  padding-bottom: max(var(--s6), env(safe-area-inset-bottom));
}
/* Trennlinie nur, wenn darüber ein scrollbarer Bereich liegt — kurze
   Bestätigungsdialoge sollen keinen Strich bekommen. */
.dialog__body ~ .dialog__actions { border-top: 1px solid var(--border); }
@keyframes fadeIn { from { opacity: 0; } }

/* ── Wochentags-Auswahl (Geofence-Kalender) ──────────────────────────── */
.daypick { display: flex; gap: var(--s1); }
.daypick button {
  flex: 1; min-height: 36px;
  background: var(--bg2);
  border: 1.5px solid var(--border);
  border-radius: var(--r-sm);
  font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-muted);
  transition: all var(--t-fast);
}
.daypick button.is-on {
  background: var(--accent); border-color: var(--accent);
  color: var(--accent-ink); font-weight: 700;
}

/* ── Fahrzeug-Checkliste (Geofence-Geltungsbereich) ──────────────────── */
.geo-device-list {
  max-height: 180px; overflow-y: auto;
  background: var(--bg2);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--s2);
}
.geo-device-item {
  display: flex; align-items: center; gap: var(--s2);
  padding: 8px var(--s2);
  font-size: var(--fs-sm);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.geo-device-item:hover { background: var(--bg3); }
.geo-device-item input { accent-color: var(--accent); width: 16px; height: 16px; }

/* ── Benutzereinstellungen ───────────────────────────────────────────── */
.acc-card { padding: var(--s5); }
.acc-card + .acc-card { margin-top: var(--s4); }
.acc-push-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-sm);
}
.acc-push-row:last-child { border-bottom: none; }
.acc-push-row > span:first-child { display: flex; align-items: center; gap: var(--s2); flex: 1; }

/* Desktop-Detailpanel: alles nach der Kennzahlen-Grid (Signal-Zeitpunkt,
   Warnungen, Zusatzfelder, Weitere Infos) — abhängig vom Fahrzeug ist nur ein
   Teil davon da. Ein gemeinsamer gap statt einzelner margin-top je Block hält
   den Abstand gleich, egal welche Kombination gerade sichtbar ist (dieselbe
   Lösung wie .vstat im Mobile-Popup). */
.vstat-extra { display: flex; flex-direction: column; gap: var(--s3); margin-top: var(--s3); }
.sim-row { display: flex; justify-content: space-between; align-items: baseline; font-size: var(--fs-sm); padding: 3px 0; }
.sim-row > span:first-child { color: var(--text-muted); }

/* ── SIM-Daten-Seite: Kennzahlen-Leiste + Tabelle (s. views/sim.js) ──────
   Massenverwaltung für bis zu 400 Tracker: dieselbe Tabellen-Bauweise wie
   die Flottenübersicht, dazu klickbare Kennzahl-Kacheln als Schnellfilter —
   auf einen Blick sichtbar, wie viele SIMs noch ausreichend Volumen haben
   oder bald neu verhandelt werden müssen, ohne die ganze Liste zu scannen. */
.sim-kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s3); margin-bottom: var(--s4);
}
.sim-kpi {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: var(--s3) var(--s4);
  background: var(--bg1); border: 1px solid var(--border); border-left: 3px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--e1);
  text-align: left; cursor: pointer;
  transition: transform var(--t-fast), border-color var(--t-fast);
}
.sim-kpi:hover { transform: translateY(-1px); }
.sim-kpi.is-active { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 8%, var(--bg1)); }
.sim-kpi__icon { color: var(--text-muted); }
.sim-kpi__num { font-size: var(--fs-2xl); font-weight: 800; line-height: 1; }
.sim-kpi__label { font-size: var(--fs-xs); color: var(--text-muted); }
.sim-kpi--ok      { border-left-color: var(--ok); }
.sim-kpi--ok .sim-kpi__icon      { color: var(--ok); }
.sim-kpi--warn    { border-left-color: var(--warn); }
.sim-kpi--warn .sim-kpi__icon    { color: var(--warn); }
.sim-kpi--err     { border-left-color: var(--err); }
.sim-kpi--err .sim-kpi__icon     { color: var(--err); }
.sim-kpi--idle .sim-kpi__icon    { color: var(--text-dim); }

.sim-vol { display: flex; flex-direction: column; gap: 4px; min-width: 160px; }
.sim-vol__text { font-size: var(--fs-xs); color: var(--text-muted); }
.sim-muted { font-size: var(--fs-xs); color: var(--text-muted); }
.sim-pending { font-size: var(--fs-sm); color: var(--text-muted); font-style: italic; }
.sim-hint { display: flex; align-items: center; gap: var(--s2); color: var(--text-muted); font-size: var(--fs-sm); padding: var(--s2) 0; }
.sim-hint svg { color: var(--warn); flex-shrink: 0; }

.sim-table tbody tr[data-id] { cursor: pointer; }
.sim-table tbody tr.is-expanded { background: var(--bg2); }
.sim-detail-row td { padding: 0 var(--s4) var(--s4); background: var(--bg1); }
.sim-detail { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5); padding-top: var(--s3); }
@media (max-width: 720px) { .sim-detail { grid-template-columns: 1fr; } }

/* ── Mapbox-Anpassungen ──────────────────────────────────────────────── */
.mapboxgl-ctrl-group {
  background: var(--glass-bg) !important;
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border) !important;
  border-radius: var(--r-md) !important;
  overflow: hidden;
}
.mapboxgl-ctrl-group button {
  background: transparent !important;
  /* Mapbox liefert 29px — auf dem Handy zu klein zum sicheren Treffen. */
  width: 42px !important; height: 42px !important;
}
/* Mapbox setzt die Symbol-Klasse auf den BUTTON selbst, nicht auf ein Kind.
   Ohne das bleiben Plus/Minus/Kompass in den vergrößerten Buttons winzig. */
.mapboxgl-ctrl-group button.mapboxgl-ctrl-icon,
.mapboxgl-ctrl-group button .mapboxgl-ctrl-icon {
  background-size: 24px 24px;
  background-position: center;
  background-repeat: no-repeat;
}
.mapboxgl-ctrl-group button + button { border-top: 1px solid var(--border) !important; }
.mapboxgl-ctrl-icon { filter: invert(.85); }
.mapboxgl-ctrl-attrib { background: rgba(10, 15, 26, .6) !important; }
.mapboxgl-ctrl-attrib a { color: var(--text-muted) !important; }

/* ── Druck (PDF-Export für Berichte) ─────────────────────────────────────
   War kaputt: .print-area hing per position:absolute an .dsk-view, dessen
   Vorfahr .dsk-main aber overflow:hidden auf genau eine Bildschirmhöhe
   gesetzt hat — alles, was länger als ein Screen war, wurde beim Drucken
   abgeschnitten (bei 40 Berichtszeilen blieben ~13 übrig, Rest weg). Dazu
   blieb die Sidebar-Spalte des Grids reserviert (unsichtbar, aber weiter
   224px breit), der Report stand also mit Lücke rechts daneben. UND: nur
   Zellfarbe wurde auf Schwarz gestellt, der Hintergrund (.card/.table) blieb
   im dunklen Bildschirm-Theme — Schwarz auf Dunkelblau, praktisch unlesbar
   auf Papier. Jetzt: eigene helle Kachel-Optik statt Theme-Variablen, und
   die Desktop-Shell (Grid+Sidebar+geclipptes Main) wird fürs Drucken
   komplett aufgelöst statt nur versteckt. */
@media print {
  body { background: #fff; color: #000; overflow: visible; height: auto; }
  body * { visibility: hidden; }
  .print-area, .print-area * { visibility: visible; }
  .print-area {
    position: static;
    width: 100%; margin: 0; padding: 20px;
    background: #fff; color: #000;
    box-shadow: none; border: none;
  }
  .print-area .table th, .print-area .table td { color: #000; border-color: #ccc; }
  .print-area .table th { background: #f0f0f0; }
  .no-print { display: none !important; }
  /* Die Bildschirmtabelle in #repResult wird für den Druck nicht mehr
     wiederverwendet (s. reports.js printReport() — eigener .print-doc
     stattdessen). Unsichtbar bleibt sie trotzdem im Fluss und schiebt den
     echten Druckinhalt als Leerraum nach unten, wenn man sie nicht komplett
     aus dem Layout nimmt. */
  #repResult { display: none !important; }

  /* #app komplett ausblenden statt nur "aufzulösen". Alle Druck-Container
     (.print-area/.print-doc/.cal-print-log) werden inzwischen direkt an
     document.body gehängt (s. ensurePrintEl() in reports.js/calendar.js/
     parking.js/logbook.js) — liegen also NIE innerhalb von #app. Die frühere
     Lösung ("auflösen": display:block + height:auto, Sidebar weg,
     overflow:visible) machte #app zwar unsichtbar, ließ es aber in seiner
     vollen, ungeclippten Höhe im Fluss stehen — GENAU der Fehler, der oben
     für #repResult einzeln erkannt und gefixt wurde, hier aber für die
     GESAMTE App-Shell erneut zuschlug: Karte, Kalenderraster, Alarmlisten
     etc. blieben als unsichtbarer Platzhalter vor dem eigentlichen
     Druckinhalt stehen und erzeugten je nach Ansicht mehrere komplett leere
     Seiten, bevor der PDF-Export überhaupt begann. Gemessen: #app allein kam
     in der Alarme-Ansicht schon auf ~1160px Leerraum vor dem Park-Log. */
  #app { display: none !important; }
}

/* ── Druck-Briefkopf-Design ("print-doc") ─────────────────────────────────
   Gemeinsames Aussehen für gedruckte Berichte: Logo+Titel-Kopf mit
   Akzentlinie, KPI-Kacheln für die wichtigsten Zahlen auf einen Blick,
   gestreifte Tabelle mit dunkler Kopfzeile, Fußnote. War vorher einfach die
   Bildschirmtabelle in Schwarzweiß — sieht jetzt wie ein eigenständiges
   Dokument aus, nicht wie ein Screenshot. Eigener Container statt Wiederver-
   wendung der Bildschirmansicht (s. reports.js printReport()), direkt an
   body gehängt — dieselbe robuste Lösung wie beim Kalender-Log, umgeht
   dadurch die Layout-Fallstricke der App-Shell von vornherein statt sie zu
   reparieren. */
.print-doc { display: none; }
@media print {
  .print-doc { display: block; color: #111; font-family: var(--font); }

  .print-doc__head {
    display: flex; align-items: flex-end; justify-content: space-between;
    gap: 24px; padding-bottom: 14px; margin-bottom: 16px;
    border-bottom: 3px solid #233612;
  }
  .print-doc__brand { display: flex; align-items: center; gap: 10px; }
  .print-doc__brand svg, .print-doc__brand img { width: 32px; height: 32px; flex-shrink: 0; }
  .print-doc__brand b { font-size: 14px; letter-spacing: .2px; }
  .print-doc__brand b span { color: #3e5f1f; }
  .print-doc__title { text-align: right; }
  .print-doc__title h1 { font-size: 20px; margin: 0 0 3px; }
  .print-doc__title .sub { font-size: 11px; color: #555; }

  .print-doc__meta { font-size: 11px; color: #333; line-height: 1.6; margin-bottom: 16px; }
  .print-doc__meta b { color: #000; }

  .print-doc__kpis {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(108px, 1fr));
    gap: 10px; margin-bottom: 20px;
  }
  .print-doc__kpi {
    border: 1px solid #ddd; border-left: 3px solid #3e5f1f;
    border-radius: 4px; padding: 8px 10px; background: #fafafa;
  }
  .print-doc__kpi span {
    display: block; font-size: 9px; text-transform: uppercase;
    letter-spacing: .5px; color: #666; margin-bottom: 3px;
  }
  .print-doc__kpi b { display: block; font-size: 17px; color: #111; }
  .print-doc__kpi--warn { border-left-color: #dc2626; background: #fdf4f4; }
  .print-doc__kpi--warn b { color: #b91c1c; }

  .print-doc h2 { font-size: 13px; margin: 18px 0 8px; padding-bottom: 4px; border-bottom: 1px solid #ddd; }
  .print-doc table { width: 100%; border-collapse: collapse; font-size: 10.5px; margin-bottom: 12px; }
  .print-doc th {
    text-align: left; padding: 7px 8px; background: #233612; color: #fff;
    font-size: 9px; text-transform: uppercase; letter-spacing: .4px; font-weight: 600;
  }
  .print-doc td { padding: 6px 8px; border-bottom: 1px solid #e5e5e5; }
  .print-doc tbody tr:nth-child(even) td { background: #f6f7f6; }
  .print-doc tr.is-warn td { background: #fdecec; }
  .print-doc td.num, .print-doc th.num { text-align: right; font-variant-numeric: tabular-nums; }

  .print-doc__foot {
    margin-top: 16px; padding-top: 10px; border-top: 1px solid #ddd;
    font-size: 9.5px; color: #777; display: flex; justify-content: space-between; gap: 12px;
  }
}

/* ── Kalender-Log-Export (gerichtlich verwertbares PDF, s. calendar.js
   exportLog()) — läuft über denselben Druckdialog-Weg wie oben, aber mit
   eigenem Inhalt statt der Bildschirmansicht: Hunderte Positionszeilen
   wären in der schmalen Detail-Spalte unbrauchbar, deshalb ein eigener
   Container, der am Bildschirm unsichtbar bleibt und nur beim Drucken
   erscheint (die .print-area-Sichtbarkeitsregel oben übernimmt den Rest). */
.cal-print-log { display: none; }
@media print {
  .cal-print-log { display: block; color: #000; font-family: var(--font); }
  .cal-print-log h1 { font-size: 20px; margin: 0 0 6px; }
  .cal-print-log h2 { font-size: 14px; margin: 18px 0 6px; }
  .cal-print-log .cal-print-meta { font-size: 12px; color: #333; line-height: 1.6; margin-bottom: 14px; }
  .cal-print-log .cal-print-summary {
    display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px 24px;
    font-size: 11px; color: #333; margin-bottom: 6px;
  }
  .cal-print-log .cal-print-summary b { display: block; font-size: 15px; color: #000; margin-top: 2px; }
  .cal-print-log table { width: 100%; border-collapse: collapse; font-size: 10.5px; margin-bottom: 10px; }
  .cal-print-log th, .cal-print-log td { border: 1px solid #ccc; padding: 3px 6px; text-align: left; }
  .cal-print-log th { background: #eee; }
  .cal-print-log tr.seg--no_ignition { background: #fbe2e2; }
  /* Stromtrennung ohne erkennbare Bewegung ist unauffällig (gelb) — MIT
     Bewegung ist der eigentliche Manipulationsverdacht und muss selbst beim
     schnellen Überfliegen des gedruckten Logs sofort auffallen (kräftiges Rot,
     fett). */
  .cal-print-log tr.seg--power_off { background: #fff3d6; }
  .cal-print-log tr.seg--power_off-moved { background: #f8d7d7; font-weight: 700; color: #9a1c1c; }
  .cal-print-log .cal-print-foot { font-size: 10px; color: #666; margin-top: 6px; }
  /* Kopfzeile jeder Tabelle wiederholt sich auf Folgeseiten, Zeilen brechen
     nicht mitten durch — sonst reißt eine Ortung über zwei Seiten auseinander. */
  .cal-print-log thead { display: table-header-group; }
  .cal-print-log tr { break-inside: avoid; }
}

/* ── Alarm-Log ───────────────────────────────────────────────────────────
   Zeilen sind bewusst kompakt: der Log wird lang, und auf Mobile soll man
   viele Ereignisse ohne Scrollen überblicken. */

/* Log/Alarm-Log — als Tab-Leiste mit Unterstrich statt als Pillen-Reihe wie
   der übrige .seg-Filter: das ist die PRIMÄRE Weiche der Seite (welche
   Ereignisse überhaupt infrage kommen), keine gleichrangige Verfeinerung
   wie "nur ungelesen" — sollte sich davon auch optisch abheben. */
.alert-modes {
  display: flex; gap: var(--s5);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--s4);
}
.alert-modes__tab {
  display: flex; align-items: center; gap: 7px;
  padding: 2px 2px var(--s3);
  font-size: var(--fs-sm); font-weight: 700;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color var(--t-fast), border-color var(--t-fast);
}
.alert-modes__tab:hover { color: var(--text-dim); }
.alert-modes__tab.is-on { color: var(--accent); border-color: var(--accent); }

.alert-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); flex-wrap: wrap;
}
.alert-actions { display: flex; gap: var(--s2); flex-wrap: wrap; }
.alert-badge {
  display: inline-block; margin-left: 6px; padding: 1px 7px;
  border-radius: 999px; background: var(--accent); color: #06210f;
  font-size: var(--fs-xs); font-weight: 800; letter-spacing: 0;
}
/* Umfang (alle/ein Fahrzeug/eine Gruppe) + Ungelesen-Umschalter */
.alert-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3); flex-wrap: wrap;
  margin-bottom: var(--s3);
}
.alert-scope { max-width: 220px; min-height: 38px; padding: 6px var(--s3); font-size: var(--fs-sm); }

/* Segmentierter Filter */
.seg { display: flex; gap: 4px; background: var(--bg2); border-radius: var(--r-md); padding: 4px; }
.seg__btn {
  flex: 1; min-height: 32px; padding: 5px 8px;
  border-radius: calc(var(--r-md) - 3px);
  color: var(--text-muted); font-size: var(--fs-xs); font-weight: 600;
  cursor: pointer; transition: background var(--t-fast), color var(--t-fast);
  white-space: nowrap;
}
.seg__btn.is-on { background: var(--bg); color: var(--text); }

.alert-list { display: flex; flex-direction: column; }
.alert-pager {
  display: flex; align-items: center; justify-content: center; gap: var(--s4);
  padding-top: var(--s4);
  font-size: var(--fs-sm); color: var(--text-muted);
}
.alert-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: 9px 2px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
}
.alert-row:last-child { border-bottom: 0; }
.alert-row__icon {
  display: inline-flex; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 50%;
  align-items: center; justify-content: center;
  background: var(--bg2); color: var(--text-muted);
}
.alert-row--warn  .alert-row__icon { color: #fbbf24; }
.alert-row--alarm .alert-row__icon { color: var(--err); background: color-mix(in srgb, var(--err) 18%, transparent); }
.alert-row__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.alert-row__msg  { font-size: var(--fs-sm); line-height: 1.25; }
.alert-row__meta { font-size: var(--fs-xs); color: var(--text-muted); }
.alert-row__map  { flex: 0 0 auto; }
/* Ungelesenes bleibt an der linken Kante markiert, ohne die Zeile umzubauen */
.alert-row.is-unread { box-shadow: inset 3px 0 0 var(--accent); padding-left: 8px; }
.alert-row.is-unread .alert-row__msg { font-weight: 600; }

/* ── Eigener Standort ────────────────────────────────────────────────────
   Bewusst ohne Richtungspfeil/-kegel: der Punkt zeigt nur, WO man ist.
   Die Fahrtrichtung steckt in der Kartendrehung, ein zusätzlicher Pfeil wäre
   doppelt. Gezeichnet wird der Punkt selbst (nicht Mapbox' eingebauter),
   damit er zwischen zwei Messungen weich wandern kann — siehe
   addUserLocation() in mapkit.js. */
.user-dot {
  width: 26px; height: 26px;
  border-radius: 50%;
  background: #1a73e8;
  border: 4px solid #fff;
  box-shadow: 0 0 0 1px rgba(0,0,0,.25), 0 2px 6px rgba(0,0,0,.45);
  position: relative;
}
/* ruhiger Puls, damit der Punkt auf der Satellitenkarte auffindbar bleibt */
.user-dot::after {
  content: ''; position: absolute; inset: -13px;
  border-radius: 50%;
  background: rgba(26, 115, 232, .22);
  animation: userPulse 2.4s ease-out infinite;
}
@keyframes userPulse {
  0%   { transform: scale(.6); opacity: .9; }
  100% { transform: scale(1.5); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .user-dot::after { animation: none; }
}

/* Beacon-Marker (s. setBeaconMarkers() in mapkit.js) — nutzt dieselben
   .vmark-wrap/.vmark/.vmark-label-Klassen wie der Fahrzeug-Marker weiter
   oben, nur mit eigenem Icon (Tag statt Fahrzeug) statt vehicleIcon(). */
.vmark--lowbat { border-color: var(--warn); }

/* Leine zwischen Fahrzeug- und Beacon-Marker (s. setBeaconMarkers) — macht
   sichtbar, dass beide zusammengehören/zusammen unterwegs sind, statt es nur
   aus der Nähe zu erraten. Nur für frisch gesehene, einem Fahrzeug
   zugeordnete Beacons gezeichnet (nie für verwaiste/veraltete). */
.beacon-tether { pointer-events: none; }
/* Sattes Gelb statt der Akzentfarbe (dieselbe wie DAY_ROUTE_STYLE in
   views/history.js) — Grün ging im Satelliten-Kartenstil über Feldern/Wäldern
   sichtbar unter, Gelb hebt sich von Grün, Braun und Grau gleichermaßen ab.
   Gilt gleichermaßen für Desktop und mobil (gemeinsames Stylesheet). */
.beacon-tether__line {
  stroke: #ffd400;
  stroke-width: 4;
  stroke-dasharray: 5 4;
  stroke-linecap: round;
  opacity: 1;
}

/* Beacon-Verlauf (s. views/beaconHistory.js) — schlichte Punkte statt volle
   Marker: es sind potenziell hunderte Sichtungen, keine handvoll Fahrzeuge. */
.bhist-dot {
  width: 12px; height: 12px;
  border-radius: var(--r-full);
  background: var(--text-muted);
  border: 2px solid var(--bg1);
  box-shadow: var(--e1);
  cursor: pointer;
}
.bhist-dot--last {
  width: 16px; height: 16px;
  background: var(--accent-strong);
  border-color: #fff;
}

/* Standort-Knopf (eigener Control, s. addUserLocation in mapkit.js).
   Symbol kommt als Inline-SVG aus dem JS — CSS-Masken wurden von Mapbox'
   eigenen Button-Styles überschrieben. */
.mapboxgl-ctrl-group .user-loc-btn,
.user-loc-btn {
  /* Höhere Spezifität als Mapbox' `.mapboxgl-ctrl-group button`, sonst
     gewinnt deren `display:block` und das Symbol klebt oben links statt
     mittig. */
  display: flex !important;
  align-items: center; justify-content: center;
  width: 42px; height: 42px;
  padding: 0;
  background: none; border: none; cursor: pointer;
  /* Die Karten-Controls sind hier dunkel gestylt (s. .mapboxgl-ctrl-group),
     das Symbol muss also hell sein — nicht das Mapbox-Standardgrau. */
  color: var(--text-dim);
  transition: color var(--t-fast);
}
.user-loc-btn:hover { color: var(--text); }
.user-loc-btn > svg { display: block; margin: auto; }
/* Folgt der Knopf einem anderen, bekommt er oben eine 1px-Trennlinie
   (s. `.mapboxgl-ctrl-group button + button`). Die Flex-Zentrierung rechnet
   nur mit der Inhaltsfläche, das Symbol säße dadurch 1px zu tief — hier
   ausgeglichen. */
.mapboxgl-ctrl-group button + .user-loc-btn { padding-bottom: 1px; }
.user-loc-btn.is-on { color: #1a73e8; }
/* aktiver Zustand zusätzlich als Puls, damit er auch ohne Farbsehen auffällt */
.user-loc-btn.is-on svg { animation: locPulse 2s ease-in-out infinite; }
@keyframes locPulse { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) {
  .user-loc-btn.is-on svg { animation: none; }
}

/* Popup an den Richtungspfeilen der Fahrspur — bewusst winzig, es soll die
   Spur nicht verdecken. */
.trail-popup .mapboxgl-popup-content {
  background: var(--bg1);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  box-shadow: var(--e2);
  padding: 7px 10px;
}
.trail-popup .mapboxgl-popup-tip { border-top-color: var(--bg1); border-bottom-color: var(--bg1); }
.trail-popup__in {
  display: flex; flex-direction: column; gap: 1px;
  font-size: var(--fs-xs); color: var(--text); white-space: nowrap;
}
.trail-popup__in b { font-size: var(--fs-xs); color: var(--text-dim); }
.trail-popup__in .num:first-of-type { font-size: var(--fs-sm); font-weight: 700; }

/* Verwaltung der Fahrzeug-Zusatzfelder */
.vfield-list { display: flex; flex-direction: column; }
.vfield-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--border);
}
.vfield-row:last-child { border-bottom: 0; }
/* Beschriftung und Schalter nebeneinander statt untereinander — gestapelt
   sah die Zeile aus wie zwei Einträge. */
.vfield-row__body {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: var(--s3);
}
.vfield-row__body .vfield-row__label { flex: 1; min-width: 0; }
@media (max-width: 560px) {
  .vfield-row__body { flex-direction: column; align-items: stretch; gap: 6px; }
}
.vfield-row__label {
  background: var(--bg2); border: 1px solid var(--border);
  border-radius: var(--r-md); color: var(--text);
  padding: 10px var(--s3); min-height: 44px; width: 100%; min-width: 0; outline: none;
}
.vfield-row__label:focus { border-color: var(--accent); }
.vfield-row__flag {
  display: flex; align-items: center; gap: 7px; flex: none;
  font-size: var(--fs-xs); color: var(--text-muted); cursor: pointer;
  white-space: nowrap;
}
.vfield-row__flag input { width: 17px; height: 17px; accent-color: var(--accent); }

/* Verwaltung der Fahrzeuggruppen */
.vg-list { display: flex; flex-direction: column; }
.vg-row {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s2) 0;
  border-bottom: 1px solid var(--border);
}
.vg-row:last-child { border-bottom: 0; }
.vg-row__order { display: flex; flex-direction: column; gap: 2px; flex: none; padding-top: 2px; }
.vg-order-arrow { display: inline-flex; transform: rotate(180deg); }
.vg-order-arrow.is-up { transform: rotate(0deg); }
.vg-row__body { flex: 1; min-width: 0; }
.vg-row__top { display: flex; align-items: center; gap: var(--s3); }
.vg-row__label {
  flex: 1; min-width: 0;
  background: var(--bg2); border: 1px solid var(--border);
  border-radius: var(--r-md); color: var(--text);
  padding: 10px var(--s3); min-height: 44px; outline: none;
}
.vg-row__label:focus { border-color: var(--accent); }
.vg-count {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap;
}

.vg-color {
  display: inline-block; flex: none; width: 28px; height: 28px;
  border-radius: var(--r-full); background: var(--sw, var(--accent));
  border: 2px solid var(--border-strong); cursor: pointer;
}
.vg-color--pick { width: 32px; height: 32px; }

.vg-palette { display: flex; flex-wrap: wrap; gap: var(--s2); margin-top: var(--s3); }
.vg-palette--new { padding: 0 0 var(--s2); }
.vg-swatch {
  width: 26px; height: 26px; border-radius: var(--r-full);
  background: var(--sw); border: 2px solid transparent; cursor: pointer; padding: 0;
}
.vg-swatch.is-selected { border-color: var(--text); }
.vg-swatch--custom {
  position: relative; overflow: hidden;
  border: 2px dashed var(--border-strong);
}
.vg-swatch--custom input[type=color] {
  position: absolute; inset: -4px; width: calc(100% + 8px); height: calc(100% + 8px);
  border: 0; padding: 0; cursor: pointer;
}

.vg-new { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--s3); }
.vg-new .input { flex: 1; min-width: 0; }
@media (max-width: 560px) {
  .set-card__foot--form:has(.vg-new) { flex-wrap: wrap; }
  .vg-new { width: 100%; }
}

/* Beacon-Verwaltung (Flotten-Seite) — gleiches Grundmuster wie .vg-row */
.bcn-list { display: flex; flex-direction: column; }
.bcn-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--border);
}
.bcn-row:last-child { border-bottom: 0; }
.bcn-row__body { flex: 1; min-width: 0; }
.bcn-row__top { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.bcn-row__meta {
  display: flex; align-items: center; gap: 6px;
  margin-top: 3px; font-size: var(--fs-xs); color: var(--text-muted);
}

/* Zusatzfelder in Popup und Detailansicht */
.vfields { display: grid; grid-template-columns: auto 1fr; gap: 2px var(--s3); font-size: var(--fs-xs); }
.vfields > span { color: var(--text-muted); }

/* Gemeinsame Popup-Hülle (Fahrzeug UND Beacon) — scrollt selbst, statt den
   Kartenausschnitt zu sprengen, wenn viele Werte/Warnungen zusammenkommen. */
.veh-popup { display: flex; flex-direction: column; gap: var(--s2); max-height: 40vh; overflow-y: auto; }

/* ── Kompaktes Status-Raster (Karten-Popup) ───────────────────────────────
   Ursprünglich Fahrzeug/Mobile-only in mobile.css — jetzt hier, weil Beacons
   dasselbe Raster auch auf dem Desktop brauchen (dort lädt mobile.css nicht). */
.vstat { display: flex; flex-direction: column; gap: 6px; }
.vstat__top { display: flex; gap: 6px; flex-wrap: wrap; }
.vstat__top .badge { padding: 2px 8px; }
.vstat__grid {
  display: grid; grid-template-columns: auto 1fr; gap: 2px var(--s3);
  font-size: var(--fs-xs); align-items: baseline;
}
.vstat__grid > span { color: var(--text-muted); }
.vstat__grid > b { text-align: right; font-weight: 700; }
/* Icon je Statuszeile — macht die Werte per Symbol statt nur per Text
   erfassbar (gleiche Sprache wie die Fahrzeug-/Beacon-Liste). */
.vstat__row { display: inline-flex; align-items: center; gap: 5px; }
.vstat__ico { display: inline-flex; flex-shrink: 0; color: var(--text-muted); }
.vstat__ico svg { width: 13px; height: 13px; }

/* ── Beacon-Popup (Karte) ──────────────────────────────────────────────────
   Gleiche Bauform wie das Fahrzeug-Popup (.veh-popup… in mobile.css): Icon in
   Statusfarbe + Name + Unterzeile im Kopf, Bearbeiten-Knopf rechts daneben,
   darunter .vstat mit Badges und Icon-Raster (s. o.). Eigene Klassen, damit
   nichts am bestehenden Fahrzeug-Popup hängt, aber optisch bewusst identisch. */
.beacon-popup__head {
  display: flex; align-items: flex-start; gap: var(--s2);
  padding-right: 132px; /* Teilen- + Bearbeiten- + 44×44-Schließen-Button */
  padding-bottom: var(--s2);
  border-bottom: 1px solid var(--border);
}
.beacon-popup__icon {
  flex-shrink: 0; width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm);
  background: var(--bg2);
  border: 1px solid var(--border);
  color: var(--text-muted);
}
.beacon-popup__icon svg { width: 16px; height: 16px; }
.beacon-popup__icon--fresh {
  color: var(--accent);
  border-color: var(--accent-dim);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg2));
}
.beacon-popup__title { min-width: 0; flex: 1; }
.beacon-popup__name { font-size: var(--fs-sm); font-weight: 700; }
.beacon-popup__addr { font-size: 11px; line-height: 1.3; color: var(--text-muted); margin-top: 1px; }
.beacon-popup__edit, .beacon-popup__share {
  position: absolute; top: 0; right: 44px;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  border-radius: var(--r-md);
  transition: color var(--t-fast), background var(--t-fast);
}
.beacon-popup__share { right: 88px; }
.beacon-popup__edit:hover, .beacon-popup__share:hover,
.beacon-popup__edit:focus-visible, .beacon-popup__share:focus-visible { color: var(--accent); background: var(--bg2); }
.beacon-popup__notes {
  margin-top: var(--s2); padding-top: var(--s2);
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs); color: var(--text-dim); font-style: italic;
}

/* Mapbox-Popup-Chrome auf Dark Theme, wie beim Fahrzeug-Popup. */
.beacon-mappopup .mapboxgl-popup-content {
  background: var(--bg1);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--e3);
  padding: var(--s2);
  animation: veh-popup-in var(--t-med) both;
}
.beacon-mappopup.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip,
.beacon-mappopup.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip,
.beacon-mappopup.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip { border-top-color: var(--bg1); }
.beacon-mappopup.mapboxgl-popup-anchor-top .mapboxgl-popup-tip,
.beacon-mappopup.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip,
.beacon-mappopup.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip { border-bottom-color: var(--bg1); }
.beacon-mappopup.mapboxgl-popup-anchor-left .mapboxgl-popup-tip { border-right-color: var(--bg1); }
.beacon-mappopup.mapboxgl-popup-anchor-right .mapboxgl-popup-tip { border-left-color: var(--bg1); }
.beacon-mappopup .mapboxgl-popup-close-button {
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  color: var(--text-muted);
  font-size: 20px;
  right: 0; top: 0;
  border-radius: var(--r-md);
  transition: color var(--t-fast), background var(--t-fast);
}
.beacon-mappopup .mapboxgl-popup-close-button:hover,
.beacon-mappopup .mapboxgl-popup-close-button:focus-visible { color: var(--text); background: var(--bg2); }
@keyframes veh-popup-in {
  from { opacity: 0; transform: translateY(4px) scale(.98); }
}
.vfields > b { text-align: right; font-weight: 600; }
/* "Weitere Infos" — eingeklappte technische Rohwerte (Akku, Funkzelle) */
.vextra { border-top: 1px solid var(--border); padding-top: var(--s2); }
.vextra summary {
  cursor: pointer; font-size: var(--fs-xs); font-weight: 600;
  color: var(--text-muted); list-style: none; user-select: none;
}
.vextra summary::-webkit-details-marker { display: none; }
.vextra summary::before { content: '▸ '; }
.vextra[open] summary::before { content: '▾ '; }
.vextra .vfields { margin-top: var(--s2); }
.vextra__stamp { margin-top: var(--s2); font-size: var(--fs-xs); color: var(--text-muted); }

/* Bordspannungs-Zeile in "Weitere Infos" — eigene Zeile statt Teil der
   generischen .vfields-Liste, weil sie klickbar ist (öffnet den
   Spannungsverlauf, s. views/voltageHistory.js). */
.vfields__volt {
  display: flex; align-items: center; gap: 6px; width: 100%;
  margin-top: 4px; padding: 2px 0; background: none; border: none;
  font-size: var(--fs-xs); color: var(--text-muted); cursor: pointer;
  text-align: left;
}
.vfields__volt span { flex: 1; }
.vfields__volt b { font-weight: 600; color: var(--text); }
.vfields__volt svg { color: var(--accent); flex-shrink: 0; }
.vfields__volt:hover { color: var(--text); }

/* Akkustand in der Fahrzeugliste */
.vitem__battery {
  display: inline-flex; align-items: center; gap: 3px; margin-right: 6px;
  color: var(--text-muted);
}
.vitem__battery.is-low { color: var(--err); font-weight: 600; }

/* Zusatzfeld-Werte in der Fahrzeugliste (nur ausdrücklich markierte Felder) */
.vitem__extra {
  display: inline-block; margin-right: 6px; padding: 1px 6px;
  border-radius: var(--r-sm);
  background: var(--bg3); color: var(--text-dim);
  font-size: var(--fs-xs); font-weight: 600;
}

/* ── Aktivitätskalender ──────────────────────────────────────────────── */
/* Kalender links, Detail-Spalte rechts — auf dem Handy (kein Platz daneben)
   untereinander gestapelt, s. .mob-scroll-Kontext in dem das schon steckt. */
.cal-layout { display: flex; flex-direction: column; gap: var(--s4); align-items: flex-start; }
.cal-main { width: 100%; min-width: 0; }

.cal-head {
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  padding: var(--s4);
  border-bottom: 1px solid var(--border);
}
.cal-head__title { font-size: var(--fs-lg); font-weight: 700; min-width: 150px; }
.cal-sum { margin-left: auto; display: flex; gap: var(--s4); font-size: var(--fs-xs); color: var(--text-muted); }
.cal-sum b { color: var(--text); font-size: var(--fs-sm); }

.cal-grid {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: 4px; padding: var(--s4);
}
.cal-dow {
  text-align: center; font-size: var(--fs-xs); font-weight: 700;
  color: var(--text-muted); padding-bottom: 2px;
}
.cal-cell {
  min-height: 74px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 5px 6px;
  display: flex; flex-direction: column; gap: 1px;
  background: var(--bg1);
  /* .cal-cell ist ein <button> (anklickbar, Tastatur-fokussierbar) —
     Browser-Grundstil zurücksetzen, Optik bleibt wie bei einem <div>. */
  appearance: none; text-align: left; font: inherit; color: inherit; cursor: pointer;
  transition: border-color var(--t-fast), background var(--t-fast);
}
.cal-cell.is-empty { border: none; background: none; cursor: default; }
/* Tage mit Aktivität heben sich ab, damit man das Muster auf einen Blick sieht */
.cal-cell.has-data { background: color-mix(in srgb, var(--accent) 12%, var(--bg1)); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.cal-cell.is-today { outline: 2px solid var(--accent); outline-offset: -2px; }
.cal-cell.is-selected { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 22%, var(--bg1)); }
.cal-cell__day { display: flex; align-items: center; gap: 4px; font-size: var(--fs-xs); color: var(--text-muted); font-weight: 700; }
.cal-cell__km  { font-size: var(--fs-sm); font-weight: 700; }
.cal-cell__h   { font-size: var(--fs-xs); color: var(--text-dim); }
/* Bewegung ohne Zündung an dem Tag — dieselbe Signalfarbe wie überall sonst
   im Popup/den Listen (s. .nofix--alarm), damit sie sofort auffällt. */
.cal-cell__flag { display: inline-flex; color: var(--err); }

/* ── Detail-Spalte: Fahrzeugliste ODER chronologische Abschnitte eines
   angeklickten Tages ─────────────────────────────────────────────────── */
.cal-detail {
  width: 100%; min-width: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--bg1);
  overflow: hidden;
}
.cal-detail__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: var(--s4);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-md); font-weight: 700;
  text-transform: capitalize;
  flex-wrap: wrap;
}
.cal-detail__head-actions { display: flex; align-items: center; gap: var(--s3); font-weight: 400; }
/* Kompakte Variante von .chk für die Kopfzeile — Einzelortungen im
   PDF-Export ein-/ausschließen (s. calendar.js exportLog()). */
.cal-detail__head-actions .chk { padding: 0; min-height: auto; gap: 6px; }
.cal-detail__head-actions .chk input { width: 15px; height: 15px; }
.cal-detail__head-actions .chk span { font-size: var(--fs-xs); color: var(--text-muted); font-weight: 600; }
.cal-detail__list { display: flex; flex-direction: column; }
.cal-detail__veh {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; text-align: left;
  padding: 10px var(--s4);
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
  min-height: 48px;
}
.cal-detail__veh:last-child { border-bottom: none; }
.cal-detail__veh:hover { background: var(--bg2); color: var(--text); }
.cal-detail__veh-icon { display: inline-flex; color: var(--text-muted); flex-shrink: 0; }
.cal-detail__veh-name { flex: 1; min-width: 0; font-size: var(--fs-sm); font-weight: 600;
                         white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-detail__veh-stats { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }

.cal-detail__segs { display: flex; flex-direction: column; padding: var(--s2); gap: 2px; }
.cal-seg {
  display: flex; align-items: center; gap: var(--s3);
  padding: 10px var(--s3);
  border-radius: var(--r-md);
}
.cal-seg__icon { display: inline-flex; flex-shrink: 0; }
.cal-seg--ignition     .cal-seg__icon { color: var(--ok); }
.cal-seg--no_ignition  .cal-seg__icon { color: var(--err); }
.cal-seg--no_ignition  { background: var(--err-dim); }
/* Getrennte Stromversorgung OHNE erkennbare Bewegung: auffällig, aber nicht
   alarmierend (Warnfarbe). MIT Bewegung ist der eigentliche Verdachtsfall —
   deutlich kräftiger, damit er in der Zeitleiste nicht zwischen den
   normalen Fahrt-Abschnitten untergeht. */
.cal-seg--power        .cal-seg__icon { color: var(--warn); }
.cal-seg--power        { background: var(--warn-dim); }
.cal-seg--power-moved  .cal-seg__icon { color: var(--err); }
.cal-seg--power-moved  { background: var(--err-dim); outline: 1px solid var(--err); }
.cal-seg--power-moved  .cal-seg__label { color: var(--err); }
.cal-seg__label { flex: 1; min-width: 0; font-size: var(--fs-sm); font-weight: 600; }
.cal-seg__time  { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
.cal-seg__km    { font-size: var(--fs-sm); font-weight: 700; white-space: nowrap; min-width: 64px; text-align: right; }

/* Fahrzeugspalte */
.cal-veh {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; text-align: left;
  padding: 10px var(--s4);
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
  min-height: 48px;
}
.cal-veh:hover { background: var(--bg2); color: var(--text); }
.cal-veh.is-on { background: var(--bg2); color: var(--text); box-shadow: inset 3px 0 0 var(--accent); }
.cal-veh__icon { display: inline-flex; color: var(--text-muted); }
.cal-veh.is-on .cal-veh__icon { color: var(--accent); }
.cal-veh__name { flex: 1; min-width: 0; font-size: var(--fs-sm); font-weight: 600;
                 white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-group__head {
  padding: var(--s3) var(--s4) 4px;
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .7px; color: var(--text-muted);
  display: flex; align-items: center; gap: 6px;
}

/* Auf schmalen Schirmen kompakter, sonst passt eine Woche nicht nebeneinander */
@media (max-width: 560px) {
  .cal-grid { gap: 3px; padding: var(--s3); }
  .cal-cell { min-height: 58px; padding: 3px 4px; }
  .cal-cell__km { font-size: var(--fs-xs); }
  .cal-cell__h  { font-size: 10px; }
}

/* ── Fahrtenbuch ─────────────────────────────────────────────────────── */
.lb-veh__badge {
  flex-shrink: 0; min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--r-full); font-size: 10px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
}
.lb-veh__badge--warn { background: var(--warn); color: #1a1400; }
.lb-veh__badge--ok   { background: var(--ok-dim); color: var(--ok); }

.lb-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s3); margin-bottom: var(--s3); }
.lb-head h2 { font-size: var(--fs-lg); margin: 0; min-width: 0; overflow-wrap: anywhere; }
.lb-head .num { color: var(--text-muted); font-size: var(--fs-sm); }

.lb-empty { text-align: center; padding: var(--s5) var(--s4); }
.lb-empty__hint {
  max-width: 440px; margin: var(--s2) auto var(--s4); font-size: var(--fs-sm);
  color: var(--text-muted); line-height: 1.5;
}

.lb-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s2);
  padding-bottom: var(--s3); margin-bottom: var(--s3); border-bottom: 1px solid var(--border);
}
.lb-toolbar__group { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.lb-toolbar__group--export { margin-left: auto; }
.lb-toolbar__month { font-weight: 700; font-size: var(--fs-sm); min-width: 130px; text-align: center; }
@media (max-width: 480px) {
  .lb-toolbar__group--export { margin-left: 0; width: 100%; }
  .lb-toolbar__group--export .btn { flex: 1; }
}

.lb-trip-list { display: flex; flex-direction: column; gap: var(--s2); }
.lb-trip {
  display: grid; grid-template-columns: auto auto auto 1fr auto; align-items: center;
  gap: var(--s3); width: 100%; text-align: left;
  padding: 10px var(--s3); border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg1);
}
.lb-trip:hover { border-color: var(--accent); }
.lb-trip--none    { box-shadow: inset 3px 0 0 var(--warn); }
.lb-trip--business{ box-shadow: inset 3px 0 0 var(--accent); }
.lb-trip--private { box-shadow: inset 3px 0 0 var(--text-muted); }
.lb-trip--commute { box-shadow: inset 3px 0 0 var(--ok); }
.lb-trip__time { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
.lb-trip__km   { font-size: var(--fs-sm); font-weight: 700; white-space: nowrap; }
.lb-trip__cat  { display: flex; align-items: center; gap: 4px; font-size: var(--fs-xs); font-weight: 700; white-space: nowrap; color: var(--warn); }
.lb-trip--business .lb-trip__cat,
.lb-trip--private  .lb-trip__cat,
.lb-trip--commute  .lb-trip__cat { color: var(--text-muted); font-weight: 600; }
.lb-trip__purpose { min-width: 0; font-size: var(--fs-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-trip__odo  { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
@media (max-width: 720px) {
  .lb-trip { grid-template-columns: 1fr auto; grid-template-areas: "time km" "cat purpose" "odo odo"; row-gap: 4px; }
  .lb-trip__time { grid-area: time; } .lb-trip__km { grid-area: km; }
  .lb-trip__cat { grid-area: cat; } .lb-trip__purpose { grid-area: purpose; }
  .lb-trip__odo { grid-area: odo; }
}

.lb-disclaimer {
  display: flex; align-items: flex-start; gap: 8px; min-width: 0;
  margin-top: var(--s3); padding: var(--s3);
  border: 1px solid var(--warn); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--warn) 10%, transparent);
  font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.5; text-align: left;
}
.lb-disclaimer svg { flex-shrink: 0; margin-top: 2px; color: var(--warn); }
.lb-disclaimer span { min-width: 0; flex: 1; overflow-wrap: break-word; }

/* ── Alarm-Regeln ────────────────────────────────────────────────────── */
.rule-list { display: flex; flex-direction: column; gap: var(--s2); }
.rule {
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: var(--s3); background: var(--bg1);
}
.rule.is-off { opacity: .55; }
.rule__head { display: flex; align-items: center; gap: var(--s2); }
.rule__name { flex: 1; min-width: 0; font-weight: 700; font-size: var(--fs-sm);
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rule__ch {
  font-size: var(--fs-xs); color: var(--accent); font-weight: 700;
  padding: 2px 8px; border-radius: var(--r-full);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  white-space: nowrap;
}
.rule__meta { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 3px; }
.rule__off  { color: var(--warn); }
.rule__hint {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-xs); color: var(--text-muted);
}
.rule__hint--err { color: var(--err); }
.rule-grid { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.rule--clickable { cursor: pointer; transition: border-color var(--t-fast); }
.rule--clickable:hover { border-color: var(--accent); }

/* Schmale Screens: Name bekommt die erste Zeile ganz für sich, Badge/Buttons
   rutschen als Gruppe drunter — sonst quetscht sich der Name bei mehreren
   Elementen (Badge + 2 Buttons) auf ein paar Buchstaben zusammen. */
@media (max-width: 560px) {
  .rule__head { flex-wrap: wrap; }
  .rule__name { flex-basis: 100%; }
}

/* Fahrzeugsuche über den Parklisten — eigene, kontextunabhängige Klasse
   (nicht .dsk-search/.mob-search wiederverwendet, die sind laut Kopfkommentar
   in desktop.css/mobile.css bewusst auf ihre jeweilige Shell gescoped, dieses
   Suchfeld läuft aber identisch in beiden). */
.park-search { position: relative; margin-bottom: var(--s4); }
.park-search svg {
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 15px; height: 15px; color: var(--text-muted);
}
.park-search input {
  width: 100%; background: var(--bg2); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 9px var(--s3) 9px 36px;
  color: var(--text); font-size: var(--fs-sm); outline: none;
  transition: border-color var(--t-fast);
}
.park-search input:focus { border-color: var(--accent); }

/* ── Park-Log Zeitstrahl ─────────────────────────────────────────────── */
.park-timeline { display: flex; flex-direction: column; gap: 2px; }
.park-timeline__row {
  display: flex; justify-content: space-between; gap: var(--s3);
  padding: 7px var(--s3); border-radius: var(--r-sm);
  font-size: var(--fs-sm); background: var(--bg2);
}
.park-timeline__row--warn { background: var(--warn-dim); color: var(--warn); font-weight: 600; }

/* Mehrfachauswahl (Ereignisse, Fahrzeuge) */
.chk-list {
  display: flex; flex-direction: column; gap: 2px;
  max-height: 190px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: var(--s2);
}
.chk {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-sm); cursor: pointer; padding: 5px 4px; min-height: 34px;
}
.chk input { width: 18px; height: 18px; accent-color: var(--accent); flex-shrink: 0; }
.chk span { min-width: 0; }

/* ── Parkverstoß-Meldung (Startseite) ─────────────────────────────────────
   Bewusst auffällig und nicht wegklickbar ohne Bestätigung: Das ist die
   Meldung, wegen der jemand nachts aufsteht. */
.viol {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) var(--s4);
  border: 1px solid var(--err);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--err) 16%, var(--bg1));
  box-shadow: var(--e2);
}
.viol + .viol { margin-top: var(--s2); }
.viol__icon { color: var(--err); flex-shrink: 0; margin-top: 1px; }
.viol__body { flex: 1; min-width: 0; }
.viol__title { font-weight: 800; font-size: var(--fs-sm); }
.viol__facts {
  display: flex; flex-wrap: wrap; gap: 3px var(--s3);
  margin-top: 3px; font-size: var(--fs-xs); color: var(--text-dim);
}
.viol__facts b { color: var(--text); }
.viol__warn { color: var(--err); font-weight: 700; }
.viol__when { margin-top: 3px; font-size: var(--fs-xs); color: var(--text-muted); }

/* Überlagert die Karte oben, ohne die Bedienelemente zu verdecken */
.viol-wrap {
  position: absolute; z-index: var(--z-panel);
  left: var(--s4); right: var(--s4);
  top: calc(var(--s4) + env(safe-area-inset-top));
  max-height: 45%; overflow-y: auto;
}
.viol-wrap:empty { display: none; }
@media (max-width: 560px) {
  .viol { padding: var(--s3); }
  .viol__facts { gap: 2px var(--s2); }
}
/* Offline in Signalfarbe — ein stiller grauer Hinweis wurde übersehen */
.vitem__speed.is-offline { color: var(--err); font-weight: 700; }

/* Hinweis auf fehlenden GPS-Empfang: Die angezeigte Position ist dann die
   letzte gültige Ortung, nicht der aktuelle Standort. Muss deutlich sein —
   eine stumm falsche Position ist schlimmer als gar keine. */
.nofix {
  display: flex; align-items: flex-start; gap: 6px;
  font-size: var(--fs-xs); line-height: 1.3;
  color: var(--warn);
  background: var(--warn-dim);
  border-radius: var(--r-sm);
  padding: 6px 8px;
}
.nofix svg { flex-shrink: 0; margin-top: 1px; }
/* Bewegung ohne Zündung wiegt schwerer als ein fehlender GPS-Fix — das ist
   der Diebstahl-/Transportfall und muss sich vom gelben Hinweis absetzen. */
.nofix--alarm { color: var(--err); background: var(--err-dim); }
.nofix--alarm b { color: var(--err); }
.vitem__nofix { display: inline-flex; vertical-align: -2px; margin-left: 5px; color: var(--warn); }
/* Marker ohne aktuellen GPS-Empfang: gestrichelter Rand statt durchgezogen */
.vmark--nofix { border-style: dashed; border-color: var(--warn); }

/* ── Verstoßhistorie je Fahrzeug ─────────────────────────────────────────
   Marco will zu EINEM Fahrzeug dessen komplette Vorgeschichte sehen, nicht
   eine gemischte Liste aller Meldungen. Deshalb je Fahrzeug eine Zeile mit
   Bilanz, die sich aufklappen lässt. */
.viol-hist { display: flex; flex-direction: column; gap: var(--s2); }

.viol-veh {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg1);
  overflow: hidden;
}
.viol-veh__head {
  width: 100%;
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4);
  background: none; border: none; text-align: left;
  color: var(--text); cursor: pointer;
}
.viol-veh__head:hover { background: var(--bg2); }
.viol-veh__chev {
  display: flex; flex-shrink: 0; color: var(--text-muted);
  transition: transform var(--t-fast);
}
.viol-veh.is-open .viol-veh__chev { transform: rotate(180deg); }
.viol-veh__name { font-weight: 600; font-size: var(--fs-sm); }
.viol-veh__sum {
  margin-left: auto; text-align: right;
  font-size: var(--fs-xs); color: var(--text-muted);
}
.viol-veh__sum b { color: var(--text-dim); }
.viol-veh__body { border-top: 1px solid var(--border); }

/* Hinweis auf eine Empfangslücke in der Historie. Die gestrichelte Linie auf
   der Karte wäre ohne Erklärung nicht einzuordnen — sie sieht wie eine
   gefahrene Strecke aus, obwohl auf ihr nichts gemessen wurde. */
.gap-note {
  display: flex; gap: var(--s3); align-items: flex-start;
  margin: var(--s3) var(--s4) 0;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--warn);
  border-radius: var(--r-md);
  background: var(--warn-dim);
  font-size: var(--fs-xs); line-height: 1.4; color: var(--text-dim);
}
.gap-note svg { flex-shrink: 0; color: var(--warn); margin-top: 1px; }
.gap-note b { color: var(--text); }
/* Bei mehreren Lücken jede einzeln aufführen — „an 2 Stellen" allein sagt
   nicht, WANN das Fahrzeug nicht erfasst war. */
.gap-note__list { margin-top: 6px; display: flex; flex-direction: column; gap: 2px; }
.gap-note__list li { font-variant-numeric: tabular-nums; color: var(--text); }

/* Getrennte Stromversorgung ist schwerwiegender als eine bloße Empfangslücke
   (mögliche Manipulation, s. Park-Log) — daher in Fehlerfarbe statt Warnfarbe. */
.gap-note--power {
  border-color: var(--err);
  background: var(--err-dim);
}
.gap-note--power svg { color: var(--err); }

/* Betriebsstunden-Summe über der Fahrtenliste — informativ, keine Warnung. */
.trip-summary {
  display: flex; gap: var(--s3); align-items: center;
  margin: var(--s3) var(--s4) 0;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--bg2);
  font-size: var(--fs-xs); color: var(--text-dim);
}
.trip-summary svg { flex-shrink: 0; color: var(--accent); }
.trip-summary b { color: var(--text); }

/* Werte, die keine Betriebsleistung sind, sondern ein Warnzeichen —
   z. B. Kilometer ohne Zündung (Transport oder unbefugte Nutzung). */
.is-warn { color: var(--err); font-weight: 700; }
.vitem__noign {
  display: inline-flex; align-items: center; gap: 3px;
  margin-left: 6px; color: var(--err); font-weight: 700;
  white-space: nowrap;
}
.vitem__noign svg { flex-shrink: 0; }

/* ── Schnellwahl der Fahrten in der Historie ─────────────────────────────
   „Alle" plus eine Schaltfläche je Fahrt. Beim Vergleichen mehrerer Fahrten
   will man springen können, ohne durch die Liste zu scrollen. */
.trip-chips {
  display: flex; flex-wrap: wrap; gap: var(--s2);
  padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--border);
}
.trip-chip {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--bg2);
  color: var(--text-dim);
  font-size: var(--fs-xs); font-weight: 600;
  white-space: nowrap;
}
.trip-chip:hover { border-color: var(--accent); color: var(--text); }
.trip-chip.is-active {
  background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
}
.trip-chip__n {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 16px; height: 16px; padding: 0 3px;
  border-radius: var(--r-full);
  background: var(--bg3); color: var(--text-muted);
  font-size: 10px; font-weight: 800;
}
.trip-chip.is-active .trip-chip__n { background: rgba(0, 0, 0, .22); color: var(--accent-ink); }

/* Erklärung unter einem Eingabefeld. Bei Feldern, die man nur alle paar
   Monate anfasst, ist der Hinweis wichtiger als die Kompaktheit. */
.field__hint {
  display: block; margin-top: 4px;
  font-size: var(--fs-xs); line-height: 1.35; color: var(--text-muted);
}


/* ── Anmeldung in zwei Schritten ────────────────────────────────────────
   Freiwillig, deshalb steht die Erklärung vor der Auswahl: Wer nicht weiss,
   wozu das gut ist, schaltet es nicht ein. */
.acc-hint { font-size: var(--fs-sm); color: var(--text-muted); line-height: 1.5; }
.acc-actions { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s3); }
.acc-err {
  margin-top: var(--s3); padding: var(--s2) var(--s3);
  border-radius: var(--r-sm); background: var(--err-dim);
  color: var(--err); font-size: var(--fs-xs);
}
.btn--danger-text { color: var(--err); }

.twofa-choice { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s3); }
.twofa-opt {
  display: flex; flex-direction: column; gap: 3px;
  padding: var(--s3) var(--s4); text-align: left;
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--bg2); color: var(--text);
}
.twofa-opt:hover { border-color: var(--accent); }
.twofa-opt__title { font-size: var(--fs-sm); font-weight: 700; }
.twofa-opt__hint  { font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.35; }

.twofa-on {
  display: flex; gap: var(--s3); align-items: flex-start;
  padding: var(--s3) var(--s4);
  border: 1px solid var(--ok); border-radius: var(--r-md); background: var(--ok-dim);
}
.twofa-on__icon { display: flex; flex-shrink: 0; color: var(--accent); margin-top: 1px; }
.twofa-on__head { font-size: var(--fs-sm); font-weight: 600; }
.twofa-on__meta { margin-top: 3px; font-size: var(--fs-xs); color: var(--text-muted); }
.twofa-on__meta b { color: var(--text-dim); }
.twofa-on__meta.is-warn, .twofa-on__meta.is-warn b { color: var(--warn); }

/* QR-Code: qrencode liefert ein SVG mit fester cm-Grösse — hier auf die
   Dialogbreite zwingen, sonst sprengt es auf dem Handy das Layout. */
.qrbox {
  display: flex; justify-content: center;
  margin: var(--s3) 0;
  padding: var(--s3);
  background: #fff; border-radius: var(--r-md);
}
.qrbox svg { width: 200px; height: 200px; max-width: 100%; }

.secretbox {
  margin-top: var(--s2); padding: var(--s3);
  border: 1px dashed var(--border); border-radius: var(--r-md);
  background: var(--bg2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--fs-sm); letter-spacing: .12em; text-align: center;
  word-break: break-all;
}

/* Ersatzcodes werden abgeschrieben — grosszügig und in zwei Spalten. */
.reccodes {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--s2);
}
.reccodes span {
  padding: 9px var(--s3);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg2);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--fs-sm); font-weight: 700; letter-spacing: .08em; text-align: center;
}

/* ── Benutzerverwaltung ──────────────────────────────────────────────────
   Nur der Verwalter sieht diesen Block. Die Liste ist bewusst luftig: Es
   sind wenige Einträge, dafür trägt jeder eine Aussage über Zugriff — wer
   was sehen darf, soll man im Vorbeigehen erfassen können. */

.dialog--wide { max-width: 520px; }
/* Fahrzeug bearbeiten: früher ein einziger 380px schmaler Stapel aus zwölf
   Feldern ohne jede Gliederung — mit den .dlg-sec-Abschnitten (s. u.) und
   einer zweiten Spalte auf breiteren Schirmen deutlich kürzer und lesbarer. */
.dialog--vehicle { max-width: 640px; }
.dialog--xwide { max-width: 800px; }

/* ── Spannungsverlauf-Diagramm ─────────────────────────────────────────
   Handgebautes Inline-SVG (kein Chart-Package im Projekt, s. views/
   voltageHistory.js). */
.volt-zoom-ctrl { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.volt-zoom-btn { width: 30px; min-width: 30px; height: 30px; }
.volt-chart-wrap { margin-top: var(--s3); }
.volt-chart { width: 100%; height: auto; display: block; }
/* Das verschachtelte Plot-<svg> selbst trägt Mausrad/Ziehen fürs
   Zoomen/Verschieben — nicht eine eigene Deckfläche darüber, sonst würden
   die Hover-Tooltips (<title>) auf den einzelnen Messpunkten blockiert.
   touch-action:none verhindert, dass der Browser ein Ziehen auf dem
   Touchscreen als Seiten-Scroll interpretiert. */
.volt-zoom-surface { touch-action: none; cursor: default; }
.volt-zoom-surface.is-zoomed { cursor: grab; }
.volt-zoom-surface.is-zoomed.is-dragging { cursor: grabbing; }
.volt-band { opacity: .5; }
.volt-band--charge { fill: var(--ok-dim); }
.volt-band--rest   { fill: var(--bg2); }
.volt-grid { stroke: var(--border); stroke-width: 1; }
.volt-axis-x, .volt-axis-y { fill: var(--text-muted); font-size: 9px; }
.volt-line--on  { stroke: var(--accent); stroke-width: 2; }
.volt-line--off { stroke: var(--text-muted); stroke-width: 1.5; opacity: .7; }
.volt-line--gap { stroke: var(--text-muted); stroke-width: 1; stroke-dasharray: 3 3; opacity: .5; }
.volt-point--on  { fill: var(--accent); }
.volt-point--off { fill: var(--text-muted); opacity: .7; }
.volt-legend {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s4); margin-top: var(--s3);
  font-size: var(--fs-xs); color: var(--text-muted);
}
.volt-legend span { display: flex; align-items: center; gap: 6px; }
.volt-legend__swatch { display: inline-block; width: 14px; height: 3px; border-radius: 2px; }
.volt-legend__swatch--on   { background: var(--accent); height: 3px; }
.volt-legend__swatch--off  { background: var(--text-muted); height: 3px; }
.volt-legend__swatch--band-charge { background: var(--ok-dim); width: 14px; height: 10px; }
.volt-legend__swatch--band-rest   { background: var(--bg2); width: 14px; height: 10px; border: 1px solid var(--border); }
.volt-legend__swatch--gap {
  background: none; height: 0; border-top: 2px dashed var(--text-muted);
}

.usr-head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s4);
}
.usr-head .btn { flex: none; }
.usr-head .section-label { margin: 0; }

.usr-warn {
  display: flex; align-items: flex-start; gap: var(--s2);
  margin-top: var(--s4); padding: var(--s3);
  border: 1px solid var(--warn); border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--warn) 12%, transparent);
  font-size: var(--fs-sm); line-height: 1.45;
}
.usr-warn svg { flex: none; color: var(--warn); margin-top: 1px; }

.usr-list { display: flex; flex-direction: column; gap: var(--s2); }

.usr-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3); border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg2);
}
/* Ein gesperrtes Konto muss auf den ersten Blick anders aussehen als ein
   aktives — sonst übersieht man beim Durchgehen, wer gerade nicht hineinkommt. */
.usr-row.is-disabled { opacity: .62; }
.usr-row__main { min-width: 0; flex: 1; }
.usr-row__head { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.usr-row__mail {
  font-size: var(--fs-xs); color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.usr-row__groups { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.usr-row__side { display: flex; align-items: center; gap: var(--s1); flex: none; }
.usr-row__count {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-xs); color: var(--text-muted);
  padding-right: var(--s2); white-space: nowrap;
}

.usr-flag {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 6px; border-radius: 999px;
  background: var(--bg3); color: var(--text-muted);
}
.usr-flag--off { background: var(--err-dim); color: var(--err); }
.usr-flag--perm { background: var(--accent-dim, var(--bg3)); color: var(--accent); }

.usr-chip--all { background: var(--ok-dim); border-color: var(--ok); color: var(--ok); font-weight: 600; }

.usr-chip {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; padding: 2px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--chip, var(--accent)) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--chip, var(--accent)) 45%, transparent);
  color: var(--text);
}
.usr-chip--none {
  background: none; border-color: var(--warn); color: var(--warn); font-weight: 600;
}

.usr-groups { display: flex; flex-direction: column; gap: var(--s2); }
.usr-group {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg2); cursor: pointer;
}
.usr-group:has(input:checked) {
  border-color: color-mix(in srgb, var(--chip) 70%, transparent);
  background: color-mix(in srgb, var(--chip) 10%, var(--bg2));
}
.usr-group input { flex: none; }
.usr-group__dot {
  width: 10px; height: 10px; border-radius: 3px; flex: none;
  background: var(--chip);
}
.usr-group__name { flex: 1; min-width: 0; font-size: var(--fs-sm); }
.usr-group__count { font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }

/* Einzel-Fahrzeug-Freigabe (zusätzlich zu Gruppen) — eigene, scrollbare Liste,
   da eine Flotte deutlich mehr Zeilen als Fahrzeuggruppen haben kann. */
.usr-devices {
  display: flex; flex-direction: column; gap: var(--s2);
  max-height: 220px; overflow-y: auto;
  padding: var(--s2); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.usr-device {
  display: flex; align-items: center; gap: var(--s2);
  padding: 6px var(--s2); border-radius: var(--r-sm); cursor: pointer;
}
.usr-device:has(input:checked) { background: var(--bg3); }
.usr-device input { flex: none; }
.usr-device__name { flex: 1; min-width: 0; font-size: var(--fs-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Seiten-Sichtbarkeit — kompaktes Grid statt Einzelzeilen, zehn Einträge
   sollen nicht die halbe Dialoghöhe fressen. */
.usr-pages {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: var(--s2);
}
.usr-page {
  display: flex; align-items: center; gap: 6px;
  padding: 6px var(--s2); border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg2); cursor: pointer; font-size: var(--fs-sm);
}
.usr-page:has(input:checked) { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, var(--bg2)); }
.usr-page input { flex: none; }

.usr-switch {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) 0; cursor: pointer;
}
.usr-switch + .usr-switch { border-top: 1px solid var(--border); }
.usr-switch input { flex: none; margin-top: 3px; }
.usr-switch b { display: block; font-size: var(--fs-sm); }
.usr-switch small {
  display: block; margin-top: 2px;
  font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.4;
}

/* ── Einstellungsseite ───────────────────────────────────────────────────
   Abschnitte statt Kartenhalde: links eine Leiste, rechts genau EIN
   Abschnitt. Vorher standen Profil, Passwort, zweiter Faktor, Push, Benutzer
   und Zusatzfelder gleichzeitig da, verteilt auf zwei ungleich lange
   Spalten — man musste die Seite lesen, um etwas zu finden. */

.set {
  display: grid;
  grid-template-columns: 216px minmax(0, 1fr);
  gap: var(--s6);
  align-items: start;
}
@media (max-width: 900px) {
  .set { grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
}

.set__rail { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.set__tab {
  display: flex; align-items: center; gap: var(--s3);
  width: 100%; padding: 10px var(--s3);
  border: 0; border-radius: var(--r-md);
  background: none; color: var(--text-dim);
  font-family: inherit; font-size: var(--fs-md); font-weight: 600;
  text-align: left; cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast);
}
.set__tab svg { flex: none; opacity: .8; }
.set__tab:hover { background: var(--bg2); color: var(--text); }
.set__tab.is-active { background: var(--accent-dim); color: var(--accent); }
.set__tab.is-active svg { opacity: 1; }

/* Auf schmalen Schirmen wird aus der Leiste eine waagerechte Reihe, die bei
   Bedarf seitlich scrollt — eine gestapelte Liste würde dort den halben
   Bildschirm fressen, bevor der Inhalt beginnt.
   Muss NACH der Grundregel stehen: gleiche Spezifität, sonst gewinnt das
   width:100% von oben und man sieht nur den ersten Reiter. */
@media (max-width: 900px) {
  .set__rail {
    flex-direction: row; gap: var(--s1);
    overflow-x: auto; scrollbar-width: none;
    padding-bottom: var(--s1);
  }
  .set__rail::-webkit-scrollbar { display: none; }
  .set__tab { flex: none; width: auto; padding: 8px var(--s3); }
  .set__tab span { white-space: nowrap; }
}

.set__content { min-width: 0; max-width: 780px; }
.set__sec { display: none; }
.set__sec.is-active { display: block; animation: fadeIn var(--t-med) both; }

.set__legal {
  display: flex; align-items: center; justify-content: center; gap: var(--s2);
  margin-top: var(--s5);
  font-size: var(--fs-sm); color: var(--text-muted);
}
.set__legal a { color: var(--text-dim); text-decoration: none; padding: 4px; border-radius: var(--r-sm); }
.set__legal a:hover { color: var(--accent); text-decoration: underline; }

/* ── Karte innerhalb eines Abschnitts ── */

.set-card {
  background: var(--bg1);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--e1);
  overflow: hidden;
}
.set-card + .set-card { margin-top: var(--s4); }

.set-card__head { padding: var(--s5) var(--s5) 0; }
.set-card__head h2 { margin: 0; font-size: var(--fs-lg); font-weight: 700; }
.set-card__head p {
  margin: 4px 0 0; max-width: 62ch;
  font-size: var(--fs-sm); color: var(--text-muted); line-height: 1.5;
}
.set-card__head--action {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4);
}
.set-card__head--action .btn { flex: none; }
@media (max-width: 560px) {
  .set-card__head--action { flex-direction: column; align-items: stretch; }
}

.set-card__body { padding: var(--s4) var(--s5) var(--s5); }

.set-card__foot {
  display: flex; justify-content: flex-end; gap: var(--s2);
  padding: var(--s3) var(--s5);
  border-top: 1px solid var(--border);
  background: rgba(10, 15, 26, .4);
}
/* Fusszeile, die selbst ein Eingabefeld trägt (neues Zusatzfeld anlegen) */
.set-card__foot--form { justify-content: stretch; gap: var(--s2); }
.set-card__foot--form input { flex: 1; min-width: 0; }
.set-card__foot--form .btn { flex: none; }

.set-empty { font-size: var(--fs-sm); color: var(--text-muted); margin: 0; }

/* ── Formularzeile: Beschriftung links, Feld rechts ──────────────────────
   Ein Feld ohne Erklärung ist in Einstellungen fast immer eine
   unbeantwortete Frage — deshalb hat jede Zeile Platz für einen Halbsatz. */

.set-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 280px);
  gap: var(--s4);
  align-items: center;
  padding: var(--s3) 0;
}
.set-row + .set-row { border-top: 1px solid var(--border); }
.set-row__label b { display: block; font-size: var(--fs-md); font-weight: 600; }
.set-row__label small {
  display: block; margin-top: 2px;
  font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.45;
}
.set-row__field input { width: 100%; }
@media (max-width: 640px) {
  .set-row { grid-template-columns: minmax(0, 1fr); gap: var(--s2); align-items: stretch; }
}

/* ── Push-Geräte ── */

.set-push-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) 0;
  font-size: var(--fs-sm);
}
.set-push-row + .set-push-row { border-top: 1px solid var(--border); }
.set-push-row > span:first-child { display: flex; align-items: center; gap: var(--s2); flex: 1; }

/* ── Abschnitte innerhalb eines Dialogs ──────────────────────────────────
   Der Alarmregel-Dialog war ein durchgehender Stapel aus Feldern ohne
   Gliederung: Name, Ereignisse, Wochentage, Zeiten, Fahrzeuge, E-Mail,
   Haken — alles gleich gewichtet in einem 380px schmalen Kasten. Jetzt
   gruppiert nach der Frage, die der Abschnitt beantwortet. */

.dlg-sec + .dlg-sec {
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--border);
}
.dlg-sec__head { margin-bottom: var(--s2); }
.dlg-sec__head h3 { font-size: var(--fs-md); font-weight: 700; margin: 0; }
.dlg-sec__head p {
  margin: 2px 0 0;
  font-size: var(--fs-xs); color: var(--text-muted); line-height: 1.45;
}

/* Zwei Felder nebeneinander innerhalb eines Abschnitts (Name+Kennzeichen,
   Kilometer+Stunden, Geräte-ID+Bordnetz, ...) — auf dem Handy wieder
   gestapelt, dort ist neben zwei Feldern kein Platz für Beschriftungen. */
.dlg-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
@media (max-width: 560px) {
  .dlg-grid-2 { grid-template-columns: 1fr; }
}

/* Auswahlraster für Ereignisse und Fahrzeuge. Eine einspaltige Liste in
   einem Scrollkasten versteckte die Hälfte der Einträge. */
.pickgrid {
  display: grid; gap: var(--s1);
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  /* Hoch genug, dass die zwölf Ereignisarten vollständig hineinpassen — bei
     210px wurde die letzte Reihe angeschnitten und sah aus wie ein Fehler,
     nicht wie ein Rollbereich. Für längere Fahrzeuglisten bleibt es scrollbar. */
  max-height: 320px; overflow-y: auto;
  padding: 2px;
}
.pick {
  display: flex; align-items: center; gap: var(--s2);
  padding: 7px var(--s2);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg2);
  font-size: var(--fs-sm); cursor: pointer;
}
.pick:has(input:checked) {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--bg2));
}
.pick input { width: 17px; height: 17px; accent-color: var(--accent); flex: none; }
.pick span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.dlg-times {
  display: flex; gap: var(--s3); margin-top: var(--s2); flex-wrap: wrap;
}
.dlg-times label {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--fs-sm); color: var(--text-muted);
}
.dlg-times input { width: auto; min-width: 120px; }
