html {
  font-size: 14px;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

html {
  position: relative;
  min-height: 100%;
}

body {
  margin-bottom: 60px;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
  color: var(--bs-secondary-color);
  text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
  text-align: start;
}

.letter-spacing {
  letter-spacing: 0.08em;
}

/* ── Seiten-Kopfzeile: Abstand zwischen Aktions-Buttons vergrößern ───────
   Betrifft NUR den Button-/Aktions-Container direkt neben der Seiten-
   Überschrift (H1/H2/H3) — nicht jede beliebige .gap-2/.gap-1-Stelle im
   Projekt (die Klassen werden an >270 Stellen für ganz andere Zwecke
   genutzt, z. B. Checkbox-Listen, Sortier-Chips, Formular-Reihen).
   :has() grenzt gezielt auf echte Kopfzeilen ein: die äußere Flex-Zeile
   muss eine Überschrift als direktes Kind haben, dann wird NUR deren
   direktes Geschwister-Element (der Button-Container) angepasst. */
.d-flex.justify-content-between.align-items-center:has(> h1, > h2, > h3) > .d-flex.gap-1,
.d-flex.justify-content-between.align-items-center:has(> h1, > h2, > h3) > .d-flex.gap-2 {
  gap: 1rem !important;
}

/* ── Aufmerksamkeits-Pulsieren für Auswahlfelder, die eine aktive Entscheidung
   brauchen ──────────────────────────────────────────────────────────────
   Signalisiert "hier ist eine Auswahl nötig, bitte treffen" (z. B. Ansprech-
   partner-Auswahl beim Rechnung-Upload, wenn ein Kunde mehrere Kontakte hat
   und keiner automatisch geraten wird). Kräftiges Orange statt Blau, damit es
   sich von den überall im UI vorhandenen blauen Rahmen/Buttons klar abhebt.
   Endet per JS, sobald der Nutzer selbst wählt. */
@keyframes attn-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(253, 126, 20, 0.55); background-color: #fff8ec; }
  50% { box-shadow: 0 0 0 6px rgba(253, 126, 20, 0.28); background-color: #ffe8cc; }
}
.attn-pulse {
  animation: attn-pulse 1s ease-in-out infinite;
  border: 2px solid #fd7e14 !important;
}

/* ── Globale Suche: Typ-Filter (dezenter Trichter-Button + Checkbox-Menü) ──
   Ersetzt eine frühere, immer sichtbare Chip-Leiste über den Ergebnissen
   (scheiterte an "keine 2 Zeilen" + "kein horizontales Scrollen" bei bis zu
   8 Treffertypen) — der Button ist IMMER sichtbar (nicht mehr an ein aktives
   Suchergebnis gekoppelt), sitzt als normales Flex-Geschwister neben dem
   Eingabefeld im Command-Palette-Overlay (siehe .gs-overlay-inputrow unten). */
.gs-filter-menu-item {
  cursor: pointer;
}
.gs-filter-menu-item:hover {
  background: #f1f3f5;
}
/* Person unter ihrer Firma: eingerückte Trefferzeile mit Verbindungswinkel.
   Die Einrückung allein reicht nicht — bei drei Zeilen je Treffer liest man sie nicht
   als Zugehörigkeit, sondern als Zufall. Der Winkel (::before) zeigt die Verbindung
   nach oben zur Firma. `.list-group-item` bringt position:relative schon mit. */
.gs-kind {
  padding-left: 2.6rem !important;
}
/* Firma und ihre Personen sind EIN Block: die Trennlinie dazwischen fällt weg. Ohne das
   zerschneidet sie den Verbindungswinkel, und der sieht aus, als käme er aus dem Nichts.
   Die Linie zwischen zwei Treffern ist in Bootstrap der UNTERE Rand der oberen Zeile —
   deshalb `:has(+ …)` an der Zeile darüber, nicht `border-top` am Kind. */
.list-group-item:has(+ .gs-kind) {
  border-bottom-color: transparent;
}
.gs-kind::before {
  content: "";
  position: absolute;
  left: 1.15rem;
  top: -0.35rem;
  height: 1.7rem;
  width: .85rem;
  border-left: 1px solid #ced4da;
  border-bottom: 1px solid #ced4da;
  border-bottom-left-radius: .35rem;
}

/* Reihenfolge der Treffertypen: ziehen (Maus) oder ↑/↓ (überall, insbesondere Touch —
   HTML5-Drag feuert dort nicht). Das Menü zeigt die Reihenfolge, in der die Treffer
   erscheinen; die Sortierung selbst liegt in global-search.js (typeOrder). */
.gs-filter-label { cursor: pointer; min-width: 0; }
.gs-filter-grip { color: #ced4da; cursor: grab; flex-shrink: 0; }
.gs-filter-menu-item:active .gs-filter-grip { cursor: grabbing; }
.gs-filter-menu-item.gs-filter-dragging { opacity: .4; }
/* Ziel-Zeile beim Ziehen: dorthin wandert der gezogene Typ. Innen liegender Rahmen,
   damit die Zeilenhöhe nicht springt. */
.gs-filter-menu-item.gs-filter-over {
  background: #e7f1ff;
  box-shadow: inset 0 0 0 1px #0d6efd;
}
/* Pfeile dezent, aber mit ausreichender Tippfläche — sie sind btn-link und damit von
   der 44-px-Touch-Regel weiter unten ausgenommen, sonst wüchse das Menü ins Riesige. */
.gs-filter-move .btn {
  color: #adb5bd;
  line-height: 1;
  min-width: 1.9rem;
  min-height: 1.9rem;
}
.gs-filter-move .btn:hover:not(:disabled) { color: #0d6efd; }
.gs-filter-move .btn:disabled { opacity: .25; }
.gs-filter-reset { color: #6c757d; }
/* Kreisförmiger, sichtbar abgesetzter Hintergrund statt eines reinen Icons ohne
   Fläche — sonst geht der Button im weißen Suchfeld optisch unter. */
#globalSearchFilterBtn {
  background: #e9ecef;
  color: #495057 !important;
  border-radius: 50%;
  transition: background-color .15s ease, color .15s ease;
}
#globalSearchFilterBtn:hover {
  background: #ced4da;
}
/* Aktiv = mindestens ein Typ ausgeblendet — Blau/gefüllt statt Grau, damit auf
   den ersten Blick klar ist, dass gerade gefiltert wird (per JS toggled,
   siehe render() in global-search.js). */
#globalSearchFilterBtn.gs-filter-active {
  background: #0d6efd;
  color: #fff !important;
}
#globalSearchFilterBtn.gs-filter-active:hover {
  background: #0b5ed7;
}
/* Command-Palette-Overlay der globalen Suche (Trigger-Icon in der Navbar,
   Markup in _Layout.cshtml #globalSearchOverlay). Gleiches Show/Hide-Pattern
   wie #hinweisOverlay (position:fixed;inset:0 + style.display-Toggle statt
   bootstrap.Modal) und optisch an .zeitpicker-overlay angelehnt (abgedunkelter
   Hintergrund + zentrierte Karte), aber näher am oberen Rand statt exakt
   mittig (Spotlight-/Cmd-K-Konvention) und deutlich breiter für die
   Ergebnisliste. z-index 2500 liegt über #hinweisOverlay (2000) und den
   Kalender-Toasts (2000/2200), aber unter dem bewusst gedeckelten
   Servicefall-Toast (2147483000). */
.gs-overlay {
  position: fixed; inset: 0; z-index: 2500;
  background: rgba(0, 0, 0, .5);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 1rem; padding-top: 12vh;
}
.gs-overlay-card {
  position: relative;
  background: #fff; border-radius: .75rem; width: 100%; max-width: 640px;
  box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, .35);
  /* KEIN overflow:hidden — das Filter-Menü hängt als absolut positioniertes
     Dropdown an dieser Karte und ist länger als sie, sobald die Suche wenige
     Treffer hat: es wurde dann an der Kartenkante abgeschnitten (im Feld
     gemeldet bei 2 Treffern, 208 px gekappt). Die Rundung, für die das
     overflow da war, stellen die beiden Kinder jetzt selbst her. */
  overflow: visible;
}
@media (max-width: 767.98px) {
  .gs-overlay { padding-top: 4vh; }
  .gs-overlay-card { max-width: 95vw; }
}
.gs-overlay-inputrow {
  display: flex; align-items: center; gap: .6rem;
  padding: .75rem 1rem; border-bottom: 1px solid #eef0f4;
}
.gs-overlay-input {
  flex: 1 1 auto; min-width: 0; border: 0; outline: 0; font-size: 1.05rem;
}
/* Filter-Menü jetzt relativ zur Karte positioniert (statt der früheren
   Navbar-weiten z-index-Skala 1050/1060) — die Karte selbst sitzt schon ganz
   oben im Stapel, ein kleiner lokaler z-index reicht. */
#globalSearchFilterMenu {
  position: absolute; right: 1rem; top: 3.4rem; z-index: 2;
  background: #fff; border-radius: .5rem; min-width: 250px; overflow: hidden;
  /* Auf niedrigen Fenstern darf das Menü selbst scrollen, statt unten aus dem
     Bild zu laufen — es wächst mit der Zahl der Treffertypen. */
  max-height: 70vh; overflow-y: auto;
}
#globalSearchResults {
  max-height: 60vh; overflow-y: auto;
  /* Rundet die Karte unten ab (die Karte selbst clippt nicht mehr, siehe dort).
     Die Eingabezeile oben braucht nichts: sie hat keinen eigenen Hintergrund. */
  border-bottom-left-radius: .75rem; border-bottom-right-radius: .75rem;
}
/* ── Morph-Animation Navbar-Suchfeld ⇄ Overlay (global-search.js) ──
   „Shared-Element-Transition": ein fliegendes Ghost-Element startet exakt über
   dem Navbar-Suchfeld (.gs-searchbox, dunkel) und wächst zur weißen Overlay-
   Eingabezeile — bzw. schrumpft beim Schließen zurück. Die echte Navbar-Box
   wird während des Flugs auf visibility:hidden gesetzt (sie „ist" ja gerade der
   Ghost). Kein transform-Scale (würde Text/Radius verzerren), sondern direkte
   Interpolation von top/left/width/height/padding/Farbe/Radius/Font.
   Backdrop faded via .gs-overlay--anim (nur während der Animation, sonst
   Instant-Toggle wie bisher). prefers-reduced-motion + Mobile/Strg+K → Instant. */
.gs-overlay--anim { transition: background-color .26s ease; }
.gs-ghost {
  position: fixed; z-index: 2600; box-sizing: border-box;
  display: flex; align-items: center; gap: .5rem;
  overflow: hidden; white-space: nowrap;
  /* Ruhe-Look = Navbar-Box auf dunkler Navbar (#1a1a2e + rgba(255,255,255,.08)). */
  background: #26263c; border: 1px solid rgba(255, 255, 255, .18);
  border-radius: .45rem; color: rgba(255, 255, 255, .65);
  font-size: .85rem; padding: .3rem .7rem;
  box-shadow: 0 .5rem 1.5rem rgba(0, 0, 0, .25);
  will-change: top, left, width, height;
  transition:
    top .27s cubic-bezier(.4, 0, .2, 1),
    left .27s cubic-bezier(.4, 0, .2, 1),
    width .27s cubic-bezier(.4, 0, .2, 1),
    height .27s cubic-bezier(.4, 0, .2, 1),
    padding .27s cubic-bezier(.4, 0, .2, 1),
    background-color .27s ease, color .2s ease,
    border-radius .27s ease, font-size .27s ease, box-shadow .27s ease;
}
.gs-ghost.gs-ghost--big {
  background: #fff; border-color: #eef0f4; color: #6c757d;
  font-size: 1.05rem; padding: .75rem 1rem; border-radius: .75rem; gap: .6rem;
  box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, .35);
}
.gs-ghost-ico { flex: 0 0 auto; }
.gs-ghost-text { flex: 1 1 auto; text-align: left; opacity: .85; }
@media (prefers-reduced-motion: reduce) {
  .gs-ghost, .gs-overlay--anim { transition: none; animation: none; }
}
/* Aktives (per Tastatur gewähltes) Suchergebnis: Bootstrap färbt den Hintergrund
   blau + den Haupttext weiß, aber die Untertitel (.text-muted) und farbigen Spans
   (.text-primary „Art.-Nr.", Bestand-Grün/Rot) behalten ihre Farbe → auf Blau
   unlesbar. Im aktiven Zustand alle aufhellen. NUR .active (blau) anfassen — der
   Hover-Zustand ist hellgrau und mit grauem Text bereits gut lesbar. */
#globalSearchResults .list-group-item.active .text-muted,
#globalSearchResults .list-group-item.active .text-primary,
#globalSearchResults .list-group-item.active .text-success,
#globalSearchResults .list-group-item.active .text-danger,
#globalSearchResults .list-group-item.active .text-warning {
  color: rgba(255, 255, 255, .9) !important;
}
/* Typ-Badge (u. a. „Firma" = blau) im aktiven blauen Zeilenhintergrund abgrenzen,
   sonst verschwimmt ein gleichfarbiges Badge mit dem Hintergrund. */
#globalSearchResults .list-group-item.active .badge {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .6);
}

.card {
  border-radius: 0.5rem;
}

.card-header {
  border-radius: 0.5rem 0.5rem 0 0 !important;
}

.table th {
  white-space: nowrap;
}

/* === Empty States === */
.empty-state {
  background: #fafbfc;
  border: 1px dashed #d8dde2;
  border-radius: 0.5rem;
  padding: 1.5rem;
}

.empty-state .display-3,
.empty-state .display-4 {
  opacity: 0.5;
}

/* === Kategorie-Badges für KundeDocument === */
.badge.kategorie-vertrag {
  background-color: #0d6efd;
  color: #fff;
}

.badge.kategorie-rechnung {
  background-color: #198754;
  color: #fff;
}

.badge.kategorie-sonstiges {
  background-color: #6c757d;
  color: #fff;
}

/* === Action-Buttons in Tabellen: konsistenter Hover === */
.table .btn-sm.btn-outline-primary:hover,
.table .btn-sm.btn-outline-success:hover,
.table .btn-sm.btn-outline-dark:hover,
.table .btn-sm.btn-outline-secondary:hover,
.table .btn-sm.btn-outline-danger:hover {
  box-shadow: 0 1px 3px rgba(0,0,0,0.12);
}

/* === Inline-Form in Geräte-Tabelle (Löschen-Button) === */
.table form.d-inline,
.table td form {
  display: inline-block;
  margin: 0;
}

/* === Card-Hover für klickbare Karten === */
a > .card.shadow-sm {
  transition: transform 0.08s ease, box-shadow 0.15s ease;
}

a > .card.shadow-sm:hover {
  transform: translateY(-2px);
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}

/* === Kalender-Legende === */
.legend-dot {
  display: inline-block;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  margin-right: 0.25rem;
  vertical-align: middle;
}

/* === FullCalendar Feinjustierung === */
.fc .fc-toolbar-title {
  font-size: 1.25rem;
  cursor: pointer;
  user-select: none;
  border-bottom: 1px dashed transparent;
}

.fc .fc-toolbar-title:hover {
  border-bottom-color: #6c757d;
}

.fc-event {
  cursor: pointer;
  font-size: 0.85rem;
}

.fc-event:hover {
  filter: brightness(1.1);
}

/* === Servicefall-Card Aufklapp-Indikator === */
.sc-toggle .sc-chevron {
  transition: transform 0.2s ease;
}

.sc-toggle[aria-expanded="true"] .sc-chevron {
  transform: rotate(180deg);
}

.sc-toggle:hover {
  background-color: rgba(0, 0, 0, 0.015);
}

/* ── Alert-Close-Button vertikal zentrieren ──────────────────────────────
   Bootstrap setzt .btn-close in .alert-dismissible absolut auf top-right mit
   eigenem Padding — bei reduzierten Alerts (py-2 etc.) wirkt das oben-rechts
   geklebt und verschoben. Hier vertikal mittig halten, damit das X sauber
   auf einer Linie mit dem Alert-Text sitzt. */
.alert-dismissible .btn-close {
  top: 50%;
  transform: translateY(-50%);
  padding: 0.5rem 1rem;
}

/* ── Scrollbars ausblenden ────────────────────────────────────────────────
   Browser-übergreifend alle vertikalen und horizontalen Scrollleisten
   visuell entfernen. Scrollen bleibt funktional (Mausrad, Touch, Tastatur).
   Wirkt auf Window-Scroll + alle Container mit overflow:auto/scroll. */
* {
  scrollbar-width: none;        /* Firefox 64+ */
  -ms-overflow-style: none;     /* IE 11 / Edge Legacy */
}
*::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;                /* Chromium, WebKit (Chrome/Edge/Safari) */
}

/* =====================================================================
   Wiederverwendbare Detail-Karten (kd-*) — ursprünglich inline auf der
   Kunden-Detailseite, hier global, damit auch die Lieferanten-Seite das
   gleiche kompakte Karten-Layout nutzt (Stammdaten, Ansprechpartner …).
   ===================================================================== */
.form-section-label {
  text-transform: uppercase;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--bs-secondary-color, #6c757d);
  font-weight: 600;
  margin-bottom: 0.75rem;
  padding: 0.15rem 0 0.15rem 0.65rem;
  border-left: 3px solid var(--bs-primary);
  line-height: 1.3;
}
.form-section-label i { color: var(--bs-primary); margin-right: 0.4rem !important; }
.form-section-label:not(:first-child) { margin-top: 1.6rem; }

.kd-card .card-header {
  background: var(--bs-light, #f8f9fa);
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  padding: 0.75rem 1rem;
}
.kd-card .card-header h5 { margin: 0; font-size: 1.05rem; color: var(--bs-body-color); }
.kd-card .card-header .badge.bg-light { background: rgba(0,0,0,0.08) !important; color: var(--bs-secondary-color); }
.kd-icon-primary { color: var(--bs-primary); }
.kd-icon-info { color: var(--bs-info); }
.kd-icon-success { color: var(--bs-success); }
.kd-icon-warning { color: var(--bs-warning); }
.kd-icon-secondary { color: var(--bs-secondary-color, #6c757d); }

.kd-row { padding: .75rem; border-radius: 10px; transition: background .12s ease; }
.kd-row:hover { background: #f8f9fa; }
.kd-row + .kd-row { border-top: 1px solid #eef0f4; }
/* Anklickbare Zeile (E-Mail-Verlauf → Nachricht öffnen). Die GANZE Zeile ist der Link;
   sie soll aber wie eine Zeile aussehen und nicht wie ein blau unterstrichener Textblock. */
a.kd-row-link { color: inherit; text-decoration: none; }
a.kd-row-link:hover { background: #eef4ff; color: inherit; }

.kd-avatar {
  width: 44px; height: 44px;
  border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-weight: 600; font-size: 0.95rem; color: #fff;
  background: #6c757d;
}
.kd-avatar.square { border-radius: 10px; }
/* Avatar als Link (Kontaktzeile → Personen-Akte): Farbe/Groesse bleiben, nur der Zeiger
   und ein leichtes Aufhellen zeigen, dass er anklickbar ist. */
a.kd-avatar { color: #fff; transition: filter .12s ease; }
a.kd-avatar:hover { filter: brightness(1.12); color: #fff; }
/* Name in der Kontaktzeile: unterstreicht erst beim Hovern, damit die Zeile ruhig bleibt. */
.kd-person-link:hover { text-decoration: underline !important; color: #0d6efd !important; }
.kd-meta { font-size: 0.82rem; color: #495057; }
.kd-meta a { color: #495057; text-decoration: none; }
.kd-meta a:hover { color: #0d6efd; text-decoration: underline; }
.kd-meta .ikoblock { display: inline-flex; align-items: center; gap: 0.25rem; margin-right: 0.85rem; }
.kd-meta .ikoblock i { color: #6c757d; }

/* Beschriftete Feld-Liste (Label | Wert) der Akten-Kopfkarten. Zweispaltiges Grid,
   das auf schmalen Screens stapelt; dt/dd liegen direkt im Grid (je eine Zelle).
   Global, weil Kunden- UND Personen-Akte sie nutzen. */
.kd-fields { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 0.5rem 1.1rem; align-items: baseline; }
.kd-fields dt { font-weight: 600; color: #6c757d; font-size: 0.85rem; display: flex; align-items: center; gap: 0.4rem; white-space: nowrap; margin: 0; }
.kd-fields dt i { color: var(--bs-primary); }
.kd-fields dd { margin: 0; min-width: 0; font-size: 0.9rem; overflow-wrap: anywhere; }
.kd-fields dd a { color: #212529; text-decoration: none; }
.kd-fields dd a:hover { color: #0d6efd; text-decoration: underline; }
@media (max-width: 575.98px) {
  .kd-fields { grid-template-columns: 1fr; gap: 0.1rem; }
  .kd-fields dt { margin-top: 0.55rem; }
}

/* Kopier-Knopf neben Telefon-/E-Mail-/Adress-Links (wwwroot/js/kopier-knopf.js) — das
   Skript ergänzt ihn generisch bei JEDEM passenden Link im ganzen DOM, keine Vorlage
   muss dafür Markup ändern. Klassen am Knopf sind btn btn-sm btn-link text-muted p-0
   (identisch zum bestehenden Notiz-Kopieren-Knopf, notiz-wert.js) — dieselbe Optik statt
   eines zweiten, selbst erfundenen Knopf-Stils. btn-link ist von der 44px-Touch-Regel
   weiter unten ausgenommen: der Link selbst bleibt das Tippziel zum Anrufen/Mailen/
   Navigieren, der Knopf ist nur ein kleiner Zusatz für die Zwischenablage. */
.qrv-copy-btn { line-height: 1; vertical-align: -1px; }
.qrv-copy-btn i { font-size: .85em; }
.qrv-copy-btn:hover, .qrv-copy-btn:focus-visible { color: var(--bs-primary) !important; }
.qrv-copy-btn.qrv-copy-btn-ok { color: #198754 !important; }
/* Nur mit echter Maus dauerhaft ausgeblendet, erscheint beim Überfahren/Fokussieren der
   Zeile — gleiches Muster wie die Aktions-Knöpfe der Aufgabenliste (Pages/Index.cshtml):
   sonst stehen an einer Kopfkarte mit drei Telefonnummern vier Symbole permanent herum.
   pointer-events:none verhindert einen unsichtbaren, aber klickbaren Knopf.
   Auf Touch (kein Hover) bleibt er dauerhaft sichtbar — dort gibt es kein "Überfahren". */
@media (hover: hover) and (pointer: fine) {
  .qrv-copy-btn { opacity: 0; pointer-events: none; transition: opacity .12s ease; }
  *:hover > .qrv-copy-btn,
  *:focus-within > .qrv-copy-btn,
  .qrv-copy-btn:focus-visible {
    opacity: 1;
    pointer-events: auto;
  }
}

/* Servicefall-Liste in den Akten: kompakte Zeilen-Karten statt Tabelle, linker
   Status-Akzent in Rot (offen) / Gruen (erledigt). Ebenfalls von beiden Akten genutzt. */
.kd-sc-list { display: flex; flex-direction: column; }
.kd-sc-row { padding: .65rem .85rem; border-left: 3px solid transparent; transition: background .12s ease; }
.kd-sc-row:hover { background: #f8f9fa; }
.kd-sc-row + .kd-sc-row { border-top: 1px solid #eef0f4; }
.kd-sc-row-offen { border-left-color: var(--bs-danger); }
.kd-sc-row-erledigt { border-left-color: var(--bs-success); opacity: .85; }
.kd-sc-device { color: #212529; }
.kd-sc-device:hover { color: var(--bs-primary); text-decoration: underline; }
.kd-sc-row-erledigt .kd-sc-device { color: #6c757d; }
/* SLA-Ampel (kleiner farbiger Punkt) - gleiche Optik wie ServiceCases/Index. */
.sla-ampel { display: inline-block; width: .6rem; height: .6rem; border-radius: 50%; margin-right: .35rem; flex-shrink: 0; vertical-align: middle; }

/* Fälligkeits-Balken einer Geräte-Zeile — dieselbe Technik wie .aufg-row der
   Planungsseite (Pages/Index.cshtml): rechts verankerter Farbverlauf + Randlinie,
   Füllstand/Deckkraft steigen zur Fälligkeit hin. Eigene Klassen (gd-due-*) statt
   aufg-fall-*, weil die Tage-Schwellen bewusst von der Planungsseite abweichen
   (siehe GeraeteFaelligkeitsBalken-Kommentar in Services/). --gd-fuellstand/--gd-staerke
   kommen als Inline-Style vom Server (InvariantCulture-formatiert).
   Drei Konsumenten seit 09/2026: Kunden/Details.cshtml (Geräte-Zeile),
   Devices/Index.cshtml (Geräte-Liste) und Kunden/Person.cshtml — deshalb global
   statt seitenlokal. Klasse heißt bewusst NICHT ".kd-row-faellig" o.ä. — sie
   kombiniert sich mit der jeweils eigenen Zeilen-Klasse der Seite (.kd-row bzw.
   .ap-row), ist selbst aber seitenneutral.

   --gd-ton ist bewusst UNABHÄNGIG von .faellig-row definiert (eigene Regel-Gruppe
   unten): das Endkunden-Portal (Portal/Devices.cshtml) zeigt Geräte als Bild-Karten,
   nicht als kompakte Liste — dort passt kein Flächen-Verlauf, nur ein farbiger
   Rand. Die Karte trägt dieselben gd-due-*-Klassen rein für den Farbton, ohne
   .faellig-row (kein Fill, kein z-index-Layering nötig). */
.gd-due-rot          { --gd-ton: 220 53 69; }
.gd-due-orange       { --gd-ton: 253 126 20; }
.gd-due-gelb         { --gd-ton: 255 193 7; }
.gd-due-gruen        { --gd-ton: 25 135 84; }
.gd-due-ueberfaellig  { --gd-ton: 0 0 0; }

.faellig-row { position: relative; border-left: 3px solid transparent; }
.faellig-row > * { position: relative; z-index: 1; }
/* Jedes Kind ist damit ein eigener Stapel-Kontext (z-index 1) — ein Dropdown darin ist
   gegenüber den Kindern der FOLGEZEILEN (ebenfalls 1, später im DOM) chancenlos: deren
   Knöpfe malen über das Menü, und die Maus trifft sie statt der Menüeinträge (im Feld
   gemeldet am ⋮-Menü der Geräte-Zeile). Solange ein Menü offen ist, hebt sich die
   Spalte auf die Dropdown-Ebene (--bs-dropdown-zindex, siehe „Stapel-Reihenfolge" —
   über den klebenden Leisten, unter Navbar/Modals). */
.faellig-row > *:has(.dropdown-menu.show) { z-index: 1025; }
/* Bewusst KEIN overflow:hidden auf der Zeile — der Balken (rechts verankert, Breite
   per var() begrenzt) kann sich ohnehin nicht über die Zeilengrenze hinaus ausdehnen,
   overflow:hidden würde aber ein Dropdown-Menü einer Aktionsspalte (ein Kind DIESER
   Zeile) mit abschneiden, sobald es unter der Zeile aufklappt. */
.faellig-row::before {
    content: ""; position: absolute; inset: 0; left: auto; border-radius: 10px;
    width: var(--gd-fuellstand, 0%);
    pointer-events: none; z-index: 0;
    background: linear-gradient(to left,
        rgb(var(--gd-ton, 108 117 125) / calc(var(--gd-deckung, .40) * var(--gd-staerke, 1))) 0%,
        rgb(var(--gd-ton, 108 117 125) / 0) 100%);
    box-shadow: inset 2px 0 0 rgb(var(--gd-ton, 108 117 125) / calc(var(--gd-deckung, .40) * var(--gd-staerke, 1) * .65));
}
.faellig-row.gd-due-rot          { border-left-color: var(--bs-danger); }
.faellig-row.gd-due-orange       { border-left-color: #fd7e14; }
.faellig-row.gd-due-gelb         { border-left-color: var(--bs-warning); }
.faellig-row.gd-due-gruen        { border-left-color: var(--bs-success); }
.faellig-row.gd-due-ueberfaellig { border-left-color: #000; }
/* Am Telefon ohne Flächen-Verlauf (09/2026): die Füllung ist prozentual zur Zeilenbreite
   (Mindestanteil 8 %) — auf einer 350-px-Zeile werden daraus ~28 px, und dort stand sie als
   halbtransparenter, abgerundeter „Pfeiler" am rechten Rand, hinter dem die Aktionsknöpfe
   klebten. Das las sich wie ein Darstellungsfehler, nicht wie eine Restzeit. Die Dringlichkeit
   tragen am Telefon weiter der farbige linke Rand und die Farbe des Datums; der Verlauf bleibt
   für die breite Zeile, wo er ein ruhiger Auslauf ist. */
@media (max-width: 767.98px) {
    .faellig-row::before { display: none; }
}

/* Portal-Gerätekarte (Endkunden-Selbstbedienung): nur der linke Kartenrand färbt
   sich, kein Hintergrund-Fill (Karte trägt Bild + Detailtabelle, ein Verlauf würde
   beides überlagern). */
.card.gd-due-rot, .card.gd-due-orange, .card.gd-due-gelb, .card.gd-due-gruen, .card.gd-due-ueberfaellig {
    border-left: 4px solid rgb(var(--gd-ton));
}

/* Servicefall-Board (Pages/ServiceCases/Board): Spalten nebeneinander, waagerecht
   scrollbar. Feste Spaltenbreite statt Flex-Verteilung — bei sechs Status waeren die
   Spalten sonst auf jedem Laptop zu schmal fuer die Kartentexte. */
.sc-board { display: flex; gap: .75rem; overflow-x: auto; padding-bottom: .5rem; align-items: flex-start; }
.sc-col { flex: 0 0 265px; background: #f8f9fa; border-radius: 12px; display: flex; flex-direction: column; max-height: calc(100vh - 210px); }
.sc-col-head {
  display: flex; align-items: center; gap: .5rem;
  padding: .6rem .75rem; border-top: 3px solid #6c757d; border-radius: 12px 12px 0 0;
  background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.04);
}
.sc-col-titel { font-weight: 600; font-size: .85rem; color: #212529; }
.sc-col-count { margin-left: auto; font-size: .75rem; font-weight: 600; color: #6c757d; background: #f1f3f5; border-radius: 1rem; padding: .05rem .5rem; }
.sc-col-body { padding: .5rem; overflow-y: auto; display: flex; flex-direction: column; gap: .5rem; min-height: 90px; }
.sc-col-body-ueber { background: rgba(13,110,253,.07); border-radius: 0 0 12px 12px; outline: 2px dashed rgba(13,110,253,.35); outline-offset: -4px; }
.sc-col-leer { color: #adb5bd; text-align: center; font-size: .85rem; padding: .75rem 0; }
.sc-karte {
  background: #fff; border-radius: 10px; padding: .6rem .7rem; cursor: grab;
  box-shadow: 0 1px 3px rgba(0,0,0,.06), 0 0 0 1px rgba(0,0,0,.04);
}
.sc-karte:active { cursor: grabbing; }
.sc-karte-zieht { opacity: .45; }
.sc-karte-wartet { opacity: .6; }          /* laeuft, waehrend der Server bestaetigt */
.sc-karte-titel { font-weight: 600; font-size: .85rem; color: #212529; overflow-wrap: anywhere; }
.sc-karte-meta { font-size: .76rem; color: #6c757d; margin-top: .1rem; overflow-wrap: anywhere; }
.sc-karte-text {
  font-size: .76rem; color: #495057; margin-top: .35rem;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.sc-karte-fuss { display: flex; align-items: center; gap: .6rem; margin-top: .45rem; font-size: .72rem; color: #868e96; }
.sc-karte-fuss a { color: #868e96; }
.sc-karte-fuss a:hover { color: #0d6efd; }
@media (max-width: 575.98px) { .sc-col { flex-basis: 82vw; } }

/* Aktivitaets-Historie (.kd-timeline) — Tages-Trenner, Icon-Punkt, Kopfzeile.
   Global, weil die Kundenakte (interaktive Variante mit Aktions-Knoepfen) und das geteilte
   Partial _Aktivitaeten.cshtml (Personen-/Geräteakte) dieselbe Optik zeigen sollen.
   Die NUR in der Kundenakte gebrauchten Regeln (.kd-tl-del, .kd-tl-neu-Animation) bleiben
   bewusst dort inline. */
.kd-timeline { padding-left: 0.25rem; padding-right: 0.4rem; }
.kd-tl-day {
  font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.06em;
  color: #6c757d; font-weight: 600;
  margin: 0 0 0.4rem 0; padding: 0.4rem 0 0.25rem 0;
  border-bottom: 1px dashed #dee2e6;
  /* Beim Scrollen oben andocken, damit das Datum des sichtbaren Bereichs stehen bleibt.
     Sticky relativ zum Scroll-Container; weisser Hintergrund + z-index, damit die
     durchlaufenden Eintraege nicht durchscheinen. */
  position: sticky; top: 0; z-index: 3; background: #fff;
}
.kd-tl-day:first-child { margin-top: 0; }
.kd-tl-row { display: flex; gap: 0.6rem; align-items: flex-start; padding: 0.4rem 0 0.4rem 0.1rem; }
.kd-tl-icon {
  flex-shrink: 0;
  width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-size: 0.85rem; line-height: 1;
  box-shadow: 0 0 0 2px #fff, 0 0 0 3px rgba(0,0,0,0.06);
}
.kd-tl-body { flex: 1 1 auto; min-width: 0; }
.kd-tl-head { display: flex; gap: 0.5rem; justify-content: space-between; align-items: center; }
.kd-tl-typ { font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; color: #495057; }
.kd-tl-time { font-size: 0.7rem; color: #adb5bd; font-variant-numeric: tabular-nums; }
.kd-tl-title { font-size: 0.88rem; font-weight: 500; color: #212529; }
.kd-tl-desc { font-size: 0.78rem; color: #6c757d; margin-top: 0.05rem; }
.kd-tl-link { font-size: 0.75rem; color: #0d6efd; text-decoration: none; display: inline-flex; align-items: center; margin-top: 0.15rem; }
.kd-tl-link:hover { text-decoration: underline; }

/* Aufgaben-Zeilen (_AufgabenListe.cshtml) — Haekchen links, Titel, Meta darunter.
   Erledigte werden zunaechst nur gedaempft und durchgestrichen (Puffer gegen versehentliches
   Mit-Abhaken der nachrueckenden Zeile) und verschwinden erst nach einer kurzen Wartezeit
   langsam aus der Liste (aufgabe-modal.js, AUFGABE_FADE_DELAY). */
.aufgabe-zeile { display: flex; align-items: flex-start; gap: .65rem; padding: .6rem .75rem; border-radius: 10px; overflow: hidden; transition: background .12s ease; }
.aufgabe-zeile:hover { background: #f8f9fa; }
.aufgabe-zeile + .aufgabe-zeile { border-top: 1px solid #eef0f4; }
.aufgabe-zeile .form-check { margin: 0; padding-left: 1.4rem; }
.aufgabe-zeile .form-check-input { width: 1.15rem; height: 1.15rem; margin-left: -1.4rem; cursor: pointer; }
.aufgabe-titel { font-weight: 500; overflow-wrap: anywhere; }
.aufgabe-erledigt .aufgabe-titel { text-decoration: line-through; color: #8a929a; }
.aufgabe-erledigt { opacity: .7; }
/* Ausblenden einer erledigten Zeile (JS friert die Hoehe vorher als px-Wert ein, siehe
   zeileAusblenden in aufgabe-modal.js — "auto" kann CSS nicht wegtransitionieren). Border
   dieser Zeile (falls sie NICHT die erste ist) blendet mit aus, sonst bliebe ein Strich auf
   0 Hoehe stehen; hoehere Spezifitaet (2 Klassen) + spaeter im Stylesheet schlaegt die
   "+ .aufgabe-zeile"-Regel oben. */
.aufgabe-zeile.aufgabe-ausblendend {
    /* Zwei Stufen: erst verblasst der Inhalt vollstaendig (1.4s), ERST DANACH zieht sich
       die Luecke zusammen (Verzoegerung = die volle Verblass-Dauer, damit beim Schrumpfen
       nichts mehr sichtbar ist und kein Text zusammengequetscht wird). Waehrend des
       Ausblendens nicht mehr anfassbar. */
    pointer-events: none;
    transition: opacity 1.4s ease,
                height .6s ease 1.4s,
                padding-top .6s ease 1.4s,
                padding-bottom .6s ease 1.4s,
                border-top-color .4s ease 1.3s;
    opacity: 0;
    padding-top: 0;
    padding-bottom: 0;
    border-top-color: transparent;
}
/* Ausblenden des ganzen Aufgaben-Segments (z. B. die Karte "Aufgaben" auf "Heute"), wenn
   die zuletzt übrige Zeile abgehakt wird — statt die Zeile erst einzeln auszublenden und
   danach separat die Karte, faengt bei der letzten Zeile SOFORT diese Animation an: exakt
   dieselben Zeitwerte wie .aufgabe-ausblendend oben (1.4s verblassen, dann 0.6s zusammen-
   ziehen), damit die Zeile als Kind der Karte einfach lautlos mitverblasst statt eine
   zweite, spuerbar eigene Animation hinterherzuschicken. Sonst bliebe eine leere
   Kartenhuelle stehen (die App wuerde sie erst beim naechsten Laden von selbst weglassen,
   siehe „Heute": die Aufgaben-Karte entfaellt serverseitig komplett, wenn nichts offen ist). */
[data-aufgaben-segment].aufgaben-segment-ausblendend {
    overflow: hidden;
    pointer-events: none;
    transition: opacity 1.4s ease,
                height .6s ease 1.4s,
                margin-bottom .6s ease 1.4s;
    opacity: 0;
}
/* Gruppen-Ueberschrift der Faelligkeits-Schubladen (Ueberfaellig/Heute/…). */
.aufgabe-gruppe-titel { font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 600; color: #6c757d; margin: 1rem 0 .35rem; }
.aufgabe-gruppe-titel:first-child { margin-top: 0; }
.aufgabe-gruppe-ueberfaellig { color: var(--bs-danger); }

/* Ziehbare Spaltenbreiten (table-resize.js): Greifer am rechten Rand jeder Kopfzelle. */
table[data-resizable] th.col-resizable { position: relative; }
table[data-resizable] th .col-resizer {
  position: absolute;
  top: 0;
  right: 0;
  width: 7px;
  height: 100%;
  cursor: col-resize;
  user-select: none;
  touch-action: none;
}
table[data-resizable] th .col-resizer:hover { background: rgba(13, 110, 253, .35); }
body.col-resizing { cursor: col-resize; user-select: none; }

/* Modus „nachbar" (Mail-Listen): der Greifer sitzt auf der Grenze zur nächsten Spalte. Ab md
   festes Tabellenlayout, damit Ziehen genau zwei Spalten betrifft und die Tabelle so breit
   bleibt wie ihre Karte; darunter bleibt das automatische Layout (die Liste scrollt dort in
   .table-responsive) und die Greifer entfallen. Bei festem Layout schneiden Zellen zu langen
   Text selbst ab, statt in die Nachbarspalte zu laufen — die Zellen der ersten Spalte (Häkchen)
   bleiben ausgenommen, sonst schneidet overflow den Fokusring des Kästchens ab. */
@media (min-width: 768px) {
  table[data-resizable="nachbar"] { table-layout: fixed; width: 100%; }
  table[data-resizable="nachbar"] th,
  table[data-resizable="nachbar"] td:not(:first-child) { overflow: hidden; text-overflow: ellipsis; }
  table[data-resizable="nachbar"] th { white-space: nowrap; }
}
@media (max-width: 767.98px) {
  table[data-resizable="nachbar"] th .col-resizer { display: none; }
}
/* Kleiner Trennstrich: ohne ihn sieht man dem Kopf nicht an, dass sich die Spalten ziehen lassen. */
table[data-resizable="nachbar"] th .col-resizer::after {
  content: "";
  position: absolute;
  top: 22%;
  bottom: 22%;
  right: 3px;
  width: 1px;
  background: rgba(0, 0, 0, .22);
}
table[data-resizable="nachbar"] th .col-resizer:hover::after { background: rgba(13, 110, 253, .9); }

/* Kategorie-Combobox (komm-list): das Eingabefeld ist absichtlich schmal (col-3),
   aber das Autocomplete-Dropdown soll lesbar breit sein — sonst bricht der Text
   („Ar bei t"). min-width lässt es nach rechts über das schmale Feld hinausragen. */
.komm-list .ac-dropdown { min-width: 18rem; }
.komm-list .ac-item .ac-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* E-Mail-Verlauf-Listen (Lieferanten-/Kunden-/Personen-Akte): „N weitere
   anzeigen" + Live-Suche über window.ListeBegrenztSuche (09/2026). Eine Zeile
   trägt praktisch immer .d-flex für ihr eigenes Layout — Bootstraps Utility
   setzt dafür display:flex !important und würde ein reines classList-Umschalten
   ohne eigenes !important sofort wieder überstimmen (dieselbe Falle wie bei
   .d-flex an einem Modal-Fuß). Der Compound-Selektor (zwei Klassen) hat höhere
   Spezifität als .d-flex allein und gewinnt deshalb unabhängig von der
   CSS-Ladereihenfolge. Generisch auf .kd-row gefasst, nicht auf eine
   Seiten-eigene Zeilen-Klasse — alle drei Akten nutzen dieselbe Zeilen-Basis. */
.kd-row.kd-verlauf-hidden { display: none !important; }

/* „N weitere anzeigen"-Knopf derselben Listen — Pille statt nacktem Text-Link
   (der wirkte wie ein vergessener <a>-Rest, nicht wie eine Bedienmöglichkeit).
   Passt sich der Kartenfläche an: zart umrandet im Ruhezustand, füllt sich beim
   Überfahren dezent — dieselbe Sprache wie die übrigen .btn-outline-* Knöpfe
   der App, nur als Pille wie die Zähler-Badges der Sprung-Leisten. */
.kd-mehr-btn {
    border-radius: 50rem;
    padding-inline: 1rem;
    color: #495057;
    border-color: #dee2e6;
    background: #fff;
}
.kd-mehr-btn:hover, .kd-mehr-btn:focus {
    background: #f1f3f5;
    color: #212529;
    border-color: #ced4da;
}
/* Der Pfeil dreht sich beim Aufklappen, statt Chevron-Down/-Up auszutauschen —
   eine Drehung liest sich als STETIGE Bewegung, ein Icon-Tausch wie ein Sprung. */
.kd-mehr-btn .kd-mehr-chevron {
    display: inline-block;
    transition: transform .15s ease;
}
.kd-mehr-btn.kd-mehr-btn-auf .kd-mehr-chevron { transform: rotate(180deg); }

/* ─── Mobil: markierte Tabellen als gestapelte Karten (statt horizontalem Scrollen) ───────
   Markup: <table class="… table-cards"> + an jeder <td data-label="Spaltenname">. Unter dem
   md-Breakpoint wird jede Tabellenzeile zu einer Karte, jede Zelle zu einer „Label: Wert"-
   Block-Zeile. Bewusst Block-Stack (nicht flex), damit auch mehrteilige Zellen (Kunde+Fahrzeug,
   Status-Badge, Aktions-Buttons) sauber umbrechen. Desktop bleibt unverändert. */
@media (max-width: 767.98px) {
    table.table-cards thead { display: none; }
    table.table-cards, table.table-cards tbody { display: block; width: 100%; }
    table.table-cards tr {
        display: block;
        border: 1px solid #e9ecef;
        border-radius: .6rem;
        margin-bottom: .6rem;
        padding: .5rem .85rem;
        background: #fff;
        box-shadow: 0 1px 2px rgba(0, 0, 0, .04);
    }
    table.table-cards td {
        display: block;
        border: none !important;
        padding: .15rem 0;
        text-align: left !important;
        white-space: normal;
    }
    table.table-cards td[data-label]::before {
        content: attr(data-label) ": ";
        font-weight: 600;
        color: #6c757d;
        margin-right: .35rem;
    }
    /* Aktions-Zelle (letzte Spalte, ohne data-label): Buttons nebeneinander, etwas Abstand oben. */
    table.table-cards td:last-child {
        padding-top: .5rem;
        display: flex;
        gap: .4rem;
        flex-wrap: wrap;
    }
}

/* ============================================================================
   Mobile-/Tablet-Feinschliff (additiv) — behebt die häufigsten Touch-Probleme
   app-weit, OHNE den Desktop (≥ md) anzufassen. Bewusst nicht-destruktiv:
   `flex-wrap` greift nur, wenn der Platz NICHT reicht (passt alles in eine
   Zeile, ändert sich nichts). Siehe Begründung pro Block.
   ============================================================================ */

/* Bis Tablet-Hochkant (≈ md): „Titel links / Aktionen rechts"-Kopfzeilen und
   Zeilen mit Aktions-Cluster (d-flex justify-content-between) dürfen umbrechen,
   statt rechts über den Rand zu laufen (= „seitlich scrollen" / „abgeschnitten").
   gap hält den Abstand beim Umbruch; bei space-between auf einer Zeile bleibt
   die Optik unverändert. */
@media (max-width: 991.98px) {
    .d-flex.justify-content-between { flex-wrap: wrap; gap: .5rem; }

    /* Nicht zu Karten umgebaute Tabellen: weiches Touch-Scrollen im Container
       (die Seite selbst läuft dank .table-responsive nicht über). */
    .table-responsive { -webkit-overflow-scrolling: touch; }
}

/* Nur Handy: größere Touch-Targets + mehr nutzbare Breite. */
@media (max-width: 767.98px) {
    /* Mindesthöhe fürs Antippen; Icon mittig. Text-Links (btn-link) und das
       Modal-X (btn-close) bleiben unverändert.
       44px statt der früheren 38px: das ist Apples Mindestmaß, und am Gerät ist
       genau das der Unterschied zwischen „trifft" und „trifft daneben", wenn man
       es einhändig in der Werkstatt bedient (08/2026 am iPhone nachgemessen —
       vorher lagen praktisch alle Knöpfe bei 38px). */
    .btn:not(.btn-link):not(.btn-close) {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .form-control, .form-select, .input-group-text { min-height: 44px; }

    /* Padding zurücknehmen → mehr Inhaltsbreite auf schmalen Displays. */
    .container, .container-fluid { padding-left: .75rem; padding-right: .75rem; }
    .card-body { padding: .9rem; }
    .modal-body { padding: 1rem; }

    /* Nicht zu Karten umgebaute Tabellen kompakter setzen → mehr Spalten passen
       rein, bevor seitlich gescrollt werden muss. (table-cards-Tabellen sind hier
       nicht betroffen — die werden ohnehin zu Blöcken gestapelt.) */
    .table { font-size: .85rem; }
    .table > :not(caption) > * > * { padding: .4rem .5rem; }
}
/* ── Registrierungs-/Setup-Assistent: Schritt-Anzeige (Stepper) ──
   Genutzt von Pages/PortalCentral/Register.cshtml + TenantSetup.cshtml. */
.reg-steps { display: flex; gap: .25rem; list-style: none; padding: 0; margin: 0 0 1.5rem; }
.reg-steps li { flex: 1 1 0; text-align: center; font-size: .8rem; color: #94a3b8; position: relative; }
.reg-steps li .reg-bubble {
    width: 2rem; height: 2rem; border-radius: 50%; margin: 0 auto .35rem;
    display: flex; align-items: center; justify-content: center; font-weight: 600;
    background: #e2e8f0; color: #64748b; transition: all .2s; position: relative; z-index: 1;
}
.reg-steps li.active .reg-bubble { background: #0d6efd; color: #fff; }
.reg-steps li.done .reg-bubble { background: #198754; color: #fff; }
.reg-steps li.active, .reg-steps li.done { color: #1e293b; font-weight: 600; }
.reg-steps li::before {
    content: ""; position: absolute; top: 1rem; left: -50%; width: 100%; height: 2px;
    background: #e2e8f0; z-index: 0;
}
.reg-steps li:first-child::before { display: none; }
.reg-steps li.done::before, .reg-steps li.active::before { background: #198754; }

/* ── Pflege-Akte: Reiter/Karteikarten (Devices/Edit im Pflege-Modus) ──
   Die unteren Akte-Bereiche (Medikation/Vitalwerte/Wunden/Besuche/Dokumente)
   liegen als Karten mit data-akte-tab vor; per JS wird je Reiter ein-/ausgeblendet.
   Im Reiter werden die Karten voll breit gestapelt (kein col-lg-8-Zentrieren). */
.pflege-tab-cards > [class*="col-"] {
    flex: 0 0 100% !important;
    max-width: 100% !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
}
/* Erste Karte im Reiter ohne doppelten Abstand (Tab-Leiste liefert den Abstand). */
.pflege-tab-cards > [class*="col-"].mt-4 { margin-top: 0 !important; margin-bottom: 1.25rem; }

/* ── Zeit-Picker (Scroll-Rad) — wwwroot/js/zeit-picker.js ──
   Ersetzt das native Browser-Widget für <input type="time"> global (Auto-Init in
   _Layout.cshtml, analog zum flatpickr-Auto-Init für Datumsfelder). Helles Theme, passend
   zum sonst hellen App-Look (Bootstrap-Weiß/Grau statt dunklem iOS-Chrome). */
/* Bewusst NICHT readonly (mehr) — das Feld ist direkt per Tastatur tippbar (Tab rein +
   "1130" eintippen), Klick öffnet zusätzlich das Rad. Daher kein Grau wie bei den
   readonly-Datumsfeldern; normales, editierbar aussehendes Input. */
.zeitpicker-input { cursor: pointer; }

.zeitpicker-overlay {
    position: fixed; inset: 0;
    /* Über #hinweisOverlay (_Layout.cshtml, z-index:2000 — der alle 60s pollende
       Ankündigungs-Popup): der Picker ist eine AKTIVE Nutzereingabe, die nie
       ungefragt von einem Hintergrund-Hinweis verdeckt werden darf (sähe aus wie
       „Picker reagiert nicht"). Unter der Command-Palette (2500), die der Nutzer
       bewusst per Strg+K zusätzlich öffnen kann. */
    z-index: 2100;
    background: rgba(0, 0, 0, .35);
    display: flex; align-items: center; justify-content: center;
    padding: 1rem;
}
.zeitpicker-sheet {
    background: #fff; border-radius: 14px; width: 100%; max-width: 280px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, .2);
    overflow: hidden;
}
/* Geburtstag-Picker (Tag/Monat/Jahr) braucht mehr Breite als Stunden/Minuten — Monatsnamen
   ("September") sind deutlich breiter als zweistellige Zahlen, dazu kommt die vierstellige
   Jahres-Spalte. Eigener Modifier statt den Zeit-Picker breiter zu machen, damit dessen
   kompakte Optik unverändert bleibt. Großzügig statt exakt ausgerechnet, als Sicherheitsmarge
   gegen das Sheet-eigene overflow:hidden. */
.zeitpicker-sheet--geburtstag { max-width: 440px; }
/* Nur beim Geburtstag-Picker: den weißen Freiraum links/rechts tatsächlich nutzen, statt die
   Spalten eng in der Mitte zu bündeln. Eigene Klasse (statt .zeitpicker-wheels direkt), damit
   der kompakte Zeit-Picker (Stunden:Minuten) unverändert eng zentriert bleibt. */
.zeitpicker-wheels--spread { justify-content: space-evenly; }
/* Flexbox statt Bootstraps float-basiertem .form-check — robuster in diesem per innerHTML
   erzeugten Overlay-Kontext (kein margin-left:-1.5em-Trick, der von einer bestimmten
   umgebenden Breite/Overflow-Situation abhängt und hier schief dargestellt wurde). */
.zeitpicker-noyear-row {
    display: flex; align-items: center; gap: .5rem;
    padding: .5rem 1rem; border-top: 1px solid #e9ecef;
}
.zeitpicker-noyear-row .form-check-input { flex-shrink: 0; margin: 0; }
.zeitpicker-wheels {
    position: relative; display: flex; align-items: center; justify-content: center;
    /* Abstand zwischen den Rad-Spalten — ohne gap saßen Tag/Monat/Jahr ohne jeden Zwischenraum
       aneinander ("touchten sich optisch"), besonders auffällig bei unterschiedlich breiten
       Monatsnamen (Mai vs. September). */
    gap: .5rem;
    padding: 0 1rem;
    /* Zeigt 7 Zeilen (3 über/unter der Mitte) — muss zu ITEM_HEIGHT (44px) in
       zeit-picker.js passen: 7 * 44 = 308. */
    height: 308px;
    /* Fade-out zu den Rändern hin, wie im nativen Rad. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 30%, #000 70%, transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 30%, #000 70%, transparent 100%);
}
/* position:relative + z-index:1 ist Absicht: .zeitpicker-band ist position:absolute und würde
   nach den normalen CSS-Stacking-Regeln sonst ÜBER den (unpositionierten) Zahlen-Spalten
   liegen und den ausgewählten Wert unsichtbar unter der Markierungsfläche verstecken. */
/* scroll-snap-type: PROXIMITY statt mandatory — "mandatory" ist ein bekannter Grund für
   genau das Symptom "Scrollen reagiert manchmal nicht": der Browser versucht bei JEDEM
   einzelnen Mausrad-Tick sofort auf den nächsten Snap-Punkt zurückzuziehen und kämpft dabei
   gegen die eingehenden Wheel-Deltas, was sich wie ein Hänger anfühlt. proximity snapt nur,
   wenn ohnehin schon nah dran — die eigentliche exakte Einrastung übernimmt weiterhin unser
   JS (debounce in zeit-picker.js). overscroll-behavior:contain verhindert zusätzlich, dass ein
   Erreichen des (dank Endlos-Kopien seltenen) echten Rands auf die Seite dahinter durchschlägt. */
.zeitpicker-wheel {
    position: relative; z-index: 1;
    height: 308px; overflow-y: scroll; scroll-snap-type: y proximity; overscroll-behavior: contain;
    scrollbar-width: none;
    /* Nicht schrumpfen: sonst quetscht der Flex-Container bei mehreren unterschiedlich
       breiten Spalten (Tag/Monat/Jahr) jede Spalte auf die verfügbare Restbreite zusammen,
       statt die Spalten in ihrer natürlichen Breite nebeneinander darzustellen. */
    flex-shrink: 0;
}
.zeitpicker-wheel::-webkit-scrollbar { display: none; }
.zeitpicker-wheel-pad { height: 132px; } /* 3 * ITEM_HEIGHT */
.zeitpicker-wheel-item {
    height: 44px; display: flex; align-items: center; justify-content: center;
    scroll-snap-align: center;
    padding: 0 .35rem;
    font-size: 1.7rem; font-variant-numeric: tabular-nums;
    color: #adb5bd; cursor: pointer;
    transition: color .15s, transform .15s;
    /* Monatsnamen dürfen nie umbrechen — sonst ragt eine zweite Zeile in die Nachbarspalte
       und sieht wie eine Berührung/Verschiebung der Nachbar-Werte aus. */
    white-space: nowrap;
}
/* Klick auf eine sichtbare Zahl dreht das Rad dorthin (zeit-picker.js: wheel-click-Handler
   ruft setValue). Hover-Feedback macht das erst erkennbar — ohne Cursor/Farbe sah es aus
   wie reiner Anzeigetext. */
.zeitpicker-wheel-item:hover { color: #495057; }
/* font-weight:700 macht den Text breiter als normalgewichtig — bei zentriertem Text
   (justify-content:center je Zeile) würde das die Zeile beim Einrasten sichtbar verschieben/
   "spreizen", wenn die SPALTE selbst variabel breit ist. Die eigentliche Lösung liegt daher in
   geburtstag-picker.js: Tag/Monat/Jahr-Spalten bekommen dort eine feste Mindestbreite
   (min-width, großzügig für den fett gerenderten Extremfall wie "September" bemessen) — damit
   ist Fett hier wieder gefahrlos möglich. */
.zeitpicker-wheel-item.is-selected { color: #000; font-weight: 700; }
.zeitpicker-wheel-item.is-selected:hover { color: #000; }
/* Ungültige Kombinationen (z. B. Tag 31 bei einem 30-Tage-Monat) werden ausgegraut und sind
   nicht anklickbar (siehe wheel-picker.js setDisabled + Klick-Guard) — verhindert, dass sich
   ein nicht existierendes Datum wie „31. Juni" auswählen lässt. */
.zeitpicker-wheel-item.is-disabled { color: #dee2e6; cursor: default; pointer-events: none; }
.zeitpicker-wheel-item.is-disabled.is-selected { color: #dee2e6; font-weight: 400; }
.zeitpicker-sep { color: #212529; font-size: 1.7rem; padding: 0 .4rem; }
.zeitpicker-band {
    position: absolute; left: 0; right: 0; top: 132px; height: 44px;
    background: #eef4ff; border-radius: 10px;
    pointer-events: none; z-index: 0;
}
.zeitpicker-actions {
    display: flex; justify-content: space-between; align-items: center;
    padding: .5rem .75rem .75rem; border-top: 1px solid #e9ecef;
}

/* ===== Searchable-Select (data-searchable, wwwroot/js/searchable-select.js) ─────
   Combobox-Widget: Original-<select> liegt visuell versteckt im Wrapper und dient
   nur als Form-Submit-Vehikel; das sichtbare <input> + Dropdown wird vom JS erzeugt.
   Global (nicht Seiten-scoped), da data-searchable auf mehreren Seiten verwendet
   wird (Kalender, Reifen/Edit, Rechnungen/Create, ...). */
.ss-wrap { position: relative; }
.ss-wrap .ss-native {
    /* Sichtbar versteckt — aber im Layout-/Form-Flow erhalten, damit
       name+value beim Submit greifen. display:none würde in manchen
       Browsern Validation-Hooks brechen. */
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0,0,0,0);
    white-space: nowrap;
    border: 0;
    pointer-events: none;
}
.ss-wrap .ss-input { padding-right: 1.75rem; }
.ss-wrap.ss-disabled .ss-input { background-color: #e9ecef; opacity: 1; }
.ss-clear {
    position: absolute;
    right: 0.4rem;
    top: 50%;
    transform: translateY(-50%);
    border: 0;
    background: transparent;
    color: #6c757d;
    font-size: 1.1rem;
    line-height: 1;
    padding: 0 0.35rem;
    border-radius: 0.25rem;
    cursor: pointer;
}
.ss-clear:hover { color: #dc3545; background: #f1f3f5; }
.ss-dropdown {
    position: absolute;
    left: 0; right: 0;
    z-index: 1080;
    max-height: 260px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid #ced4da;
    border-radius: 0.375rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.08);
    display: none;
    margin-top: 2px;
}
.ss-dropdown.open { display: block; }
/* Nach oben geklappt (zu wenig Platz unterhalb, siehe richteAus() in
   searchable-select.js). `bottom: 100%` setzt die Liste über das Eingabefeld;
   `top: auto` hebt die statische Position auf, die sie sonst nach unten zwingt. */
.ss-dropdown.ss-up {
    top: auto;
    bottom: 100%;
    margin-top: 0;
    margin-bottom: 2px;
}
.ss-item {
    padding: 0.35rem 0.75rem;
    cursor: pointer;
    font-size: 0.875rem;
    color: #212529;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ss-item:hover, .ss-item.active { background: #0d6efd; color: #fff; }
.ss-empty { padding: 0.5rem 0.75rem; color: #6c757d; font-size: 0.875rem; text-align: center; }
/* Überschrift einer <optgroup> im Dropdown. Bewusst nicht anklickbar (kein data-i im JS) —
   sie ordnet nur ein, etwa „Kunden" vs. „Ansprechpartner (Lieferant)". */
.ss-gruppe {
    padding: 0.35rem 0.75rem 0.15rem;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #6c757d;
    background: #f8f9fa;
    border-top: 1px solid rgba(0, 0, 0, .06);
}
.ss-dropdown .ss-gruppe:first-child { border-top: 0; }

/* Searchable-Select in einer Bootstrap-Input-Group: das von searchable-select.js
   erzeugte .ss-wrap liegt zwischen .input-group und dem sichtbaren .ss-input,
   deshalb greifen Bootstraps Default-Regeln (border-radius am direkten Kind)
   nicht. Wir machen .ss-wrap selbst zum Flex-Item und ziehen die rechte
   Border-Radius des .ss-input weg, damit die Naht zum Suffix-Button (z. B.
   der „Öffnen"-Icon-Button im Termin-Modal) bündig sitzt. */
.input-group > .ss-wrap { flex: 1 1 auto; width: 1%; min-width: 0; }
.input-group > .ss-wrap:not(:last-child) .ss-input {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}
.input-group > .ss-wrap:focus-within { z-index: 3; }

/* ─── Globale Suche: sichtbares Suchfeld in der Navbar (ab md) ───
   Button, gestylt wie ein Eingabefeld (Lupe + „Suchen…" + Strg+K-Badge) —
   öffnet beim Klick die Command-Palette (#globalSearchOverlay, Logik in
   global-search.js, gebunden über .js-global-search-trigger). Der Strg+K-
   Badge (.gs-trigger-hint) ist in navbar.css gestylt; Farben hier passen
   zur dunklen Navbar (#1a1a2e). Unter md übernimmt stattdessen der normale
   „Suchen"-Menü-Eintrag im Offcanvas-Drawer (nav-link, d-md-none). */
.gs-searchbox {
    background: rgba(255, 255, 255, .08);
    border: 1px solid rgba(255, 255, 255, .18);
    border-radius: .45rem;
    color: rgba(255, 255, 255, .6);
    font-size: .85rem;
    padding: .3rem .7rem;
    min-width: 130px;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.gs-searchbox:hover,
.gs-searchbox:focus-visible {
    background: rgba(255, 255, 255, .14);
    border-color: rgba(255, 255, 255, .35);
    color: rgba(255, 255, 255, .85);
}
.gs-searchbox .gs-searchbox-text {
    flex: 1 1 auto;
    text-align: left;
}
/* Ab lg ist Platz für ein breiteres Feld (dort wird auch der Strg+K-Badge
   eingeblendet, d-none d-lg-inline-block in _Layout.cshtml). */
@media (min-width: 992px) {
    .gs-searchbox { min-width: 200px; }
}

/* ============================================================================
   Kundenakte: Sticky-Unternavigation (.kd-subnav) — Pages/Kunden/Details.cshtml
   Horizontale Pill-Leiste, klebt beim Scrollen unter der fixen Top-Navbar.
   `--qrv-navbar-h` = real gemessene Navbar-Höhe (gesetzt in _Layout.cshtml);
   Fallback 58px, falls das Mess-Skript (noch) nicht gelaufen ist.
   Additiver Block am Dateiende — bewusst KEINE bestehenden Regeln angefasst.
   ============================================================================ */
.kd-subnav {
    position: sticky;
    /* Bündig unter der Navbar andocken (09/2026, Nutzer-Vorgabe) — kein Spalt mehr
       zur Navbar-Unterkante, sobald die Leiste beim Scrollen dort klebt. */
    top: var(--qrv-navbar-h, 58px);
    z-index: 1020; /* über Karten-Inhalten, unter der fixed-top-Navbar (1030) */
    display: flex;
    align-items: center;
    /* Pillen mittig in der Leiste. `safe` ist Pflicht, NICHT Kosmetik: reicht der
       Platz nicht (schmales Fenster, viele Sektionen), schaltet der Browser damit
       auf flex-start zurück — bei reinem `center` würden die ersten Pillen über
       den linken Rand des Scroll-Containers hinausgeschoben und wären nicht mehr
       erreichbar (Overflow nach links lässt sich nicht wegscrollen). Browser ohne
       `safe`-Keyword ignorieren die Zeile und bleiben links — bewusst kein
       `justify-content: center`-Fallback davor, der hätte genau den Bug. */
    justify-content: safe center;
    /* Die Kachel selbst nur so breit wie ihre Pillen, mittig auf der Seite. Das
       `max-width` ist der Gegenpart zum Scroll-Container: passt der Inhalt nicht
       mehr in die Spalte, hört die Kachel am Rand auf und wischt intern weiter,
       statt die Seite quer zu schieben. Ab hier trägt `safe center` (oben) nur
       noch diesen Überlauf-Fall — die Mittigkeit macht `margin-inline: auto`. */
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
    gap: .25rem;
    padding: .3rem .4rem;
    background: var(--bs-body-bg, #fff);
    /* Abhebung über die KANTE, nicht über die Fläche: die Leiste klebt und wandert
       beim Scrollen abwechselnd über den grauen Seitenhintergrund UND über weiße
       Karten — eine Flächenfarbe kann nur gegen eines von beidem kontrastieren.
       Sichtbare Hairline + zweistufiger Schatten (enger Kontaktschatten, weite
       tiefe Lage) tragen dagegen auf jedem Untergrund. Fläche bleibt deckend
       weiß: halbtransparent würde Text unter den Pillen durchscheinen lassen. */
    border: 1px solid rgba(15, 23, 42, .10);
    border-radius: 999px;
    --kd-subnav-schatten: 0 1px 2px rgba(15, 23, 42, .07),
                          0 10px 26px -8px rgba(15, 23, 42, .32);
    box-shadow: var(--kd-subnav-schatten);
    /* Mobil: eine Zeile, seitlich wischen statt umbrechen */
    overflow-x: auto;
    white-space: nowrap;
    scrollbar-width: none; /* Firefox: dezente Leiste ohne Scrollbar */
    -webkit-overflow-scrolling: touch;
}
.kd-subnav::-webkit-scrollbar { display: none; }
/* „Hier geht es weiter" — die Leiste wischt seitlich, ohne Scrollbalken sah man das
   nicht: bei sechs Sektionen ist sie in einem 360px-Fenster gut 1000px breit, die
   hinteren Pillen waren praktisch unentdeckbar. Der Hinweis ist ein INSET-Schatten,
   kein Verlauf über die Fläche: inset-Schatten scrollen nicht mit dem Inhalt und
   lassen Rahmen und Pillen-Rundung unangetastet (eine mask-image-Blende hätte auch
   den weißen Hintergrund und die Kante ausgefranst). Die Klassen setzt
   subnav-aktiv.js je nach Scroll-Position — steht die Leiste am Rand, verschwindet
   der Hinweis auf dieser Seite. */
.kd-subnav.kd-mehr-rechts { box-shadow: var(--kd-subnav-schatten), inset -16px 0 11px -11px rgba(15, 23, 42, .28); }
.kd-subnav.kd-mehr-links  { box-shadow: var(--kd-subnav-schatten), inset  16px 0 11px -11px rgba(15, 23, 42, .28); }
.kd-subnav.kd-mehr-links.kd-mehr-rechts {
    box-shadow: var(--kd-subnav-schatten),
                inset -16px 0 11px -11px rgba(15, 23, 42, .28),
                inset  16px 0 11px -11px rgba(15, 23, 42, .28);
}
.kd-subnav-link {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    flex-shrink: 0;
    padding: .28rem .7rem;
    border-radius: 999px;
    font-size: .82rem;
    font-weight: 500;
    color: #495057;
    text-decoration: none;
    transition: background .12s ease, color .12s ease;
}
.kd-subnav-link i { font-size: .85em; color: #8a929a; transition: color .12s ease; }
.kd-subnav-link:hover { background: #f1f3f5; color: #212529; }
.kd-subnav-link:hover i { color: #495057; }
.kd-subnav-link.active {
    background: #e7f1ff;
    color: #0a58ca;
    font-weight: 600;
}
.kd-subnav-link.active i { color: #0a58ca; }

/* ============================================================================
   Kompaktere Optik der Akte-Sprungleiste (Kunden/Personen/Lieferanten/Geräte) —
   Nutzer-Vorgabe 09/2026, aus mehreren im Chat gezeigten Varianten gewählt.
   Kleinere Schrift, aktives Ziel per Unterstrich statt Füllung (ruhiger, weil
   nur EIN Element Kontur trägt), Zähler als kleiner Punkt an der Pillen-Ecke
   statt eigener Badge-Pille hinter dem Label — mehr Ziele passen ohne seitliches
   Wischen. Bewusst auf ".kd-subnav" gescoped (nicht die nackte ".kd-subnav-link"-
   Klasse überschrieben): ".kd-tabbar" nutzt dieselbe Klasse für 2er-Umschalter
   ("Liste|Board", "Ansprechpartner|Firmen") — dort bleibt die gefüllte Pille die
   bessere Wahl, ein Unterstrich-Tab wäre für einen reinen Umschalter unpassend.
   Additiv, bewusst KEINE bestehende Regel angefasst. ============================ */
.kd-subnav { border-radius: 14px; }
.kd-subnav .kd-subnav-link {
    position: relative;
    padding: .3rem 1rem .4rem .55rem;
    border-radius: 8px 8px 0 0;
    border-bottom: 2px solid transparent;
    font-size: .76rem;
}
.kd-subnav .kd-subnav-link.active {
    background: transparent;
    border-bottom-color: #0a58ca;
}
/* Der Zähler sitzt jetzt an der oberen rechten Ecke der Pille statt als eigene
   Pille hinter dem Label — der rechte Innenabstand oben (1rem) reserviert extra
   Platz dafür, sonst liefe ein langes Label (z. B. „Ansprechpartner") unter den
   Punkt. Farbe bleibt neutral grau, auch wenn die Pille aktiv ist — Unterstrich
   + blaue Schrift/Icon signalisieren den Zustand bereits eindeutig. */
.kd-subnav .kd-subnav-link .badge {
    position: absolute;
    top: 2px;
    right: 2px;
    margin: 0; /* ms-1 im Markup zählt bei position:absolute sonst zum Offset dazu */
    min-width: 15px;
    height: 15px;
    padding: 0 3px;
    font-size: .62rem;
    line-height: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Sitzt die Sprung-Leiste direkt unter einer Kopf-Karte (Kunden-/Personen-/
   Lieferanten- UND seit 09/2026 auch Geräteseite, siehe .gsek-kopf weiter unten),
   wird sie zu deren sichtbarer Fortsetzung statt einer zweiten Karte mit grauem
   Rand ringsum dazwischen: kein Abstand, die Ecken an der gemeinsamen Kante
   werden eckig (nur die vier ÄUSSEREN Ecken beider zusammen bleiben rund — wirkt
   wie EIN Block mit zwei Zonen), Schatten deutlich leiser. Nutzer-Vorgabe 09/2026
   ("die Leiste steht so alleine"). Wird sie beim Scrollen sticky und die
   Kopf-Karte damit weggescrollt, bleiben die eckigen Oberkanten + der leise
   Schatten bestehen (die Regel hängt an der DOM-Nachbarschaft, nicht am
   Scroll-Zustand) — liest sich dann als ruhiger, flacher Streifen unter der
   Navbar statt als erneut voll gerundete, schwer beschattete Pille. */
.kd-kopf:has(+ .kd-subnav) {
    margin-bottom: 0;
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}
.kd-kopf + .kd-subnav {
    margin-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    border-top-color: #eef0f4;
    box-shadow: 0 6px 14px -10px rgba(15, 23, 42, .22);
}

/* ============================================================================
   Geräteseite (Devices/Edit): dieselbe "an die Kopf-Karte anschließen"-Regel wie
   oben, aber die Kopf-Karte dort ist bewusst KEIN row/col-lg-8-Bootstrap-Grid,
   sondern ein bare .kd-kopf-Geschwister von #gsekSubnav auf der obersten Ebene
   — position:sticky braucht dort zwingend diese Ebene als Containing-Block
   (sonst bricht die Leiste beim Scrollen ab, siehe Kommentar an #gsekSubnav in
   Devices/Edit.cshtml). .gsek-kopf reproduziert deshalb "col-lg-8" (66.667% ab
   992px) bzw. "col-xxl-10" für den Pflege-Modus (83.333% ab 1400px) von Hand statt
   über die Bootstrap-Klassen — ändert sich diese Breite auf der Seite, hier
   mitziehen. Die Inhalts-Karten darunter behalten ihr eigenes row/col-lg-8
   (unverändert, die brauchen die Sticky-Ebene nicht). */
.gsek-kopf {
    max-width: 66.6667%;
    margin-inline: auto;
}
@media (max-width: 991.98px) {
    .gsek-kopf { max-width: 100%; }
}
.gsek-kopf.gsek-kopf-breit {
    max-width: 83.3333%;
}
@media (max-width: 1399.98px) {
    .gsek-kopf.gsek-kopf-breit { max-width: 100%; }
}
/* .kd-subnav ist sonst content-breit (fit-content) bis 100% seines Containers —
   hier muss sie stattdessen exakt die Breite der Kopf-Karte übernehmen, sonst
   ragt sie (bei den 13 Zielen der Geräteseite oft breiter als 66.667%) seitlich
   über die schmalere Kopf-Karte hinaus und die "angeschlossene" Kante bricht. */
.gsek-kopf + .kd-subnav { max-width: 66.6667%; }
@media (max-width: 991.98px) {
    .gsek-kopf + .kd-subnav { max-width: 100%; }
}
.gsek-kopf.gsek-kopf-breit + .kd-subnav { max-width: 83.3333%; }
@media (max-width: 1399.98px) {
    .gsek-kopf.gsek-kopf-breit + .kd-subnav { max-width: 100%; }
}

/* Geräteseite: doppelter Abstand nach der "Verknüpft"-Karte — Nutzer-Vorgabe 09/2026
   ("Abstand zwischen den Kacheln unterschiedlich groß"). _VerknuepfungenSektion.cshtml
   bringt sein eigenes mb-4 mit (richtig so für Kunden-/Personen-Akte, die binden es
   BAR ein, ohne umschließenden Abstands-Wrapper). Auf der Geräteseite steckt dieselbe
   Karte zusätzlich in einem "col-lg-8 mx-auto mt-4"-Wrapper wie ihre Nachbarn (Notizen,
   Historie, Aufgaben, ...) — die Karten dort tragen selbst KEINEN eigenen Rand, nur der
   Wrapper. Weil die Wrapper Flex-Items der umschließenden .row sind (kein Margin-
   Collapsing zwischen Geschwistern, und das mb-4 der Karte kann durch den paddinglosen
   Wrapper nicht nach außen entweichen), addieren sich die beiden 1.5rem zu 3rem. Fix
   NUR über die eindeutige Anker-Id (#gsek-verknuepft existiert nur hier) — der Partial
   selbst bleibt unangetastet, Kunden-/Personen-Akte verlassen sich weiter auf sein
   eigenes mb-4. !important nötig: Bootstraps .mb-4-Utility ist selbst !important. */
#gsek-verknuepft { margin-bottom: 0 !important; }

/* Geräteseite, zweiter Anlauf: der Fix oben behob "Verknüpft", aber im Feld weiterhin
   ungleiche Abstände gemeldet ("immer noch") — das eigentliche, robustere Problem ist
   die Grundkonstruktion selbst. Jede Karte in .gsek-inhalt-row hängt an einem eigenen
   "col-lg-8 mx-auto mt-4"-Wrapper; als Flex-Items EINER .row collapsen deren Margins nie
   miteinander (anders als normale Blockgeschwister), UND ein mb-* an der Karte selbst
   kann durch den paddinglosen Wrapper nicht nach außen entweichen — beides zusammen
   macht den tatsächlichen Abstand abhängig davon, was die jeweilige Karte (oder ein
   eingebundenes Partial) zufällig an eigenem Rand mitbringt, statt von einer einzigen
   Regel. row-gap auf der Row selbst ist dagegen robust: EIN Wert für ALLE Zwischenräume,
   unabhängig vom Karten-Inhalt. Jedes einzelne mt-4 der Wrapper wird dafür neutralisiert
   (!important, da mt-4 selbst !important ist) — die Karten-eigenen mb-4/mb-0 spielen
   dadurch keine Rolle mehr für den Row-Abstand (die #gsek-verknuepft-Regel oben bleibt
   trotzdem nötig, sonst schlüge ihr mb-4 weiterhin oben drauf). */
.gsek-inhalt-row { row-gap: 1.5rem; }
.gsek-inhalt-row > .col-lg-8 { margin-top: 0 !important; }

/* Anker-Ziele der Unternav: beim Springen unter der Sticky-Leiste + Navbar
   landen, statt darunter verdeckt zu werden. scroll-margin wirkt NUR beim
   Anker-Scrollen — für alle anderen .kd-card-Nutzungen (z. B. Lieferant-Edit)
   ist die Regel wirkungslos-harmlos. */
.kd-card[id] {
    scroll-margin-top: calc(var(--qrv-navbar-h, 58px) + 3.6rem);
}

/* ============================================================================
   Kundenakte: Kopf-Karte (.kd-kopf) + einspaltiger Sektions-Stack (.kd-panels)
   Über der Sticky-Unternav steht eine feste Kopf-Karte (Identität + Status +
   Schnellaktionen). Darunter stapeln sich ALLE Sektionen einspaltig in voller
   Breite (keine Tabs) — die .kd-subnav ist ein reiner Anker-Scroll durch diese
   Stapel-Reihenfolge (JS in Details.cshtml). .kd-panels ist nur der Flow-
   Container; die EINZIGE Ausnahme vom Einspaltigen ist die Timeline-Spalte
   (.kd-panels-split, Block am Dateiende).
   ============================================================================ */
.kd-kopf {
    background: var(--bs-body-bg, #fff);
    border: 1px solid #eef0f4;
    border-radius: 14px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, .05);
    padding: 1rem 1.25rem;
    margin-bottom: 1rem;
}
.kd-kopf .kd-kopf-title { font-size: 1.5rem; font-weight: 600; line-height: 1.2; }
.kd-kopf-badges { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.kd-kopf-badges .badge { font-weight: 500; font-size: .78rem; padding: .35rem .6rem; }

/* ============================================================================
   Geräteseite: Anker-Ziele der Sticky-Unternav (#gsekSubnav in
   Pages/Devices/Edit.cshtml). Die Karten dort sind normale .card (nicht
   .kd-card), deshalb eine analoge, additive scroll-margin-Regel für die
   gsek-*-Sektions-ids — gleicher Versatz wie .kd-card[id] oben.
   Additiv am Dateiende — bewusst KEINE bestehenden Regeln angefasst.
   ============================================================================ */
[id^="gsek-"] {
    scroll-margin-top: calc(var(--qrv-navbar-h, 58px) + 3.6rem);
}

/* ============================================================================
   Kunden-Listen: Ansichts-Umschalter (.kd-tabbar) — Pages/Kunden/Ansprechpartner
   + Pages/Kunden/Firmen. Nicht-stickige Pill-Gruppe im Seitenkopf; die Pills
   selbst nutzen die vorhandenen .kd-subnav-link-Styles der Kundenakte (gleiche
   Optik), nur die Hülle ist neu — .kd-subnav wäre wegen position:sticky im
   normalen Seitenkopf ungeeignet.
   Additiv am Dateiende — bewusst KEINE bestehenden Regeln angefasst.
   ============================================================================ */
.kd-tabbar {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: .3rem .4rem;
    background: var(--bs-body-bg, #fff);
    border: 1px solid #eef0f4;
    border-radius: 999px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .05);
    white-space: nowrap;
}

/* ── Lange Nachrichten einklappen ─────────────────────────────────────────
   Der Großteil der Länge einer Mail ist meist die angehängte Vorgeschichte. Sie soll
   erreichbar bleiben, aber nicht die Blase fluten. Zwei Mechanismen:
   (1) `.mail-zitat` — der server-seitig abgetrennte zitierte Verlauf (Textmails),
   (2) `.is-gekuerzt` — Höhenbegrenzung mit Auslauf-Verlauf, gesetzt von mail-lesen.js
       für alles, was auch danach noch zu lang ist (und für HTML-Mails, wo sich nicht
       gefahrlos am Text schneiden lässt). */
.mail-gekuerzt-wrap { position: relative; }
.mail-gekuerzt-wrap.is-gekuerzt > .mail-gekuerzt-inhalt {
    max-height: 16rem;
    overflow: hidden;
    /* Der Auslauf-Verlauf unten muss sich am INHALT ausrichten, nicht am Rahmen — sonst liegt
       er über dem „Mehr anzeigen"-Knopf und blasst ihn aus. */
    position: relative;
}
/* Weicher Auslauf statt harter Kante — signalisiert „da kommt noch was". */
.mail-gekuerzt-wrap.is-gekuerzt > .mail-gekuerzt-inhalt::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 3rem;
    pointer-events: none;
    background: linear-gradient(to bottom, transparent, var(--mail-blase-bg, #fff));
}
.mail-mehr-btn {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-top: .35rem;
    padding: .1rem .5rem;
    font-size: .74rem;
    line-height: 1.6;
    color: #0d6efd;
    background: rgba(13, 110, 253, .07);
    border: 0;
    border-radius: 999px;
    cursor: pointer;
}
.mail-mehr-btn:hover { background: rgba(13, 110, 253, .14); }

/* Hintergrund fuer den Auslauf-Verlauf: die Nachricht steht auf der weissen Karte. */
:root { --mail-blase-bg: #fff; }

/* Zitierter Verlauf: standardmäßig zu, unauffällig, per Klick auf. */
.mail-zitat { margin-top: .5rem; }
.mail-zitat > summary {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    list-style: none;
    cursor: pointer;
    font-size: .74rem;
    color: #6c757d;
    padding: .1rem .5rem;
    background: rgba(0, 0, 0, .05);
    border-radius: 999px;
}
.mail-zitat > summary::-webkit-details-marker { display: none; }
.mail-zitat > summary::before { content: "⋯"; font-weight: 700; letter-spacing: .05em; }
.mail-zitat > summary:hover { background: rgba(0, 0, 0, .09); }
.mail-zitat[open] > summary::before { content: "×"; }
.mail-body-zitat {
    margin-top: .4rem;
    padding-left: .6rem;
    border-left: 2px solid rgba(0, 0, 0, .15);
    color: #6c757d;
    max-height: 24rem;
    overflow: auto;
}

/* Formatierte (bereinigte) HTML-Mail in der Sprechblase. Die Mail bringt eigene Inline-Styles
   mit, deshalb hier nur zähmen statt gestalten: Breite begrenzen, damit eine Tabellen-Signatur
   die Blase nicht sprengt, und Schriftgrößen auf ein verträgliches Maß bringen. */
.mail-body-html { font-size: .84rem; line-height: 1.45; overflow-x: auto; }
.mail-body-html * { max-width: 100%; }
.mail-body-html table { border-collapse: collapse; }
.mail-body-html td, .mail-body-html th { padding: 0 .25rem; vertical-align: top; }
/* Mails setzen gern riesige oder winzige Schriften — auf die Blase normalisieren. */
.mail-body-html font, .mail-body-html span, .mail-body-html p,
.mail-body-html td, .mail-body-html div { font-size: inherit !important; }
.mail-body-html p { margin: 0 0 .4rem; }
.mail-body-html blockquote {
    margin: .4rem 0 .4rem .5rem;
    padding-left: .6rem;
    border-left: 2px solid rgba(0, 0, 0, .15);
    color: #495057;
}
/* Bilder: eingebettete (cid:) zeigt der Server bereits als echte Quelle an; height:auto hält
   das Seitenverhältnis, wenn die Mail feste Pixelmaße gesetzt hat. */
.mail-body-html img { max-width: 100%; height: auto; }
/* Externe Bilder sind geparkt (data-mail-src), bis der Nutzer sie anfordert; nicht auflösbare
   Verweise werden ganz ausgeblendet statt als kaputtes Bildsymbol angezeigt. */
.mail-body-html img.mail-bild-extern,
.mail-body-html img.mail-bild-fehlt { display: none; }

/* Ein Bild MIT gesetzter Quelle, das trotzdem nicht laden konnte (toter Link, Server lehnt ab,
   Datei nachträglich verschwunden) — mail-lesen.js ersetzt das <img> bei "error" durch diesen
   Platzhalter. Ohne das zeigte der Browser sein natives, hässliches Broken-Image-Symbol samt
   Alt-Text; kein Mail-Programm, das etwas taugt, macht das. */
.mail-bild-defekt {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    background: rgba(0, 0, 0, .04);
    border: 1px dashed rgba(0, 0, 0, .18);
    border-radius: .35rem;
    padding: .15rem .5rem;
    font-size: .74rem;
    color: #6c757d;
    vertical-align: middle;
    line-height: 1.6;
}

.mail-bild-leiste {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin: 0 0 .4rem;
    padding: .3rem .5rem;
    border-radius: .4rem;
    background: #fff8e1;
    border: 1px solid #ffe082;
    font-size: .74rem;
    color: #7a5b00;
}

.mail-bild-btn {
    border: 1px solid #ffca28;
    background: #fff;
    color: #7a5b00;
    border-radius: .35rem;
    padding: .05rem .45rem;
    font-size: .72rem;
    font-weight: 600;
    line-height: 1.5;
}

.mail-bild-btn:hover { background: #fffdf5; }

@keyframes qrv-dreh { to { transform: rotate(360deg); } }

.mail-body {
    /* pre-wrap: der Abruf speichert nur den Text-Teil, Zeilenumbrüche der Mail bleiben
       erhalten. overflow-wrap bricht überlange Links/Wörter, damit die Blase nie über
       ihre Maximalbreite hinauswächst. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-size: .8rem;
    line-height: 1.45;
    margin: 0;
    /* Bewusst MONOSPACE: gespeichert wird reiner Text, und sowohl die eigenen Vorlagen als
       auch fremde Absender richten darin Werte mit Leerzeichen aus („Kunde:    Autohaus
       Sturne"). Proportional bricht genau diese Spalten auf. */
    font-family: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, "Liberation Mono", monospace;
}

/* ============================================================================
   Sticky-Unternav: Landemarkierung nach dem Klick auf eine Pille
   (Logik: wwwroot/js/subnav-ziel.js, im _Layout eingebunden).
   Klick auf eine Pille ⇒ die Karte, zu der sie springt, bekommt einen dezenten
   blauen Rahmen (bis zur nächsten Pille oder bis sie aus dem Bild scrollt).
   Bewusst `outline` statt `border`/`box-shadow`: outline liegt außerhalb des
   Layouts (die Karte springt nicht) und überschreibt weder den vorhandenen
   Karten-Schatten noch den Rand — die Ziele sind mal .kd-card, mal .kd-kopf,
   mal eine schlichte .card (Geräteseite).
   Additiver Block am Dateiende — bewusst KEINE bestehenden Regeln angefasst.
   ============================================================================ */
.kd-subnav-ziel {
    outline: 2px solid transparent;
    outline-offset: 3px;
    transition: outline-color .16s ease;
}

.kd-subnav-ziel-aktiv { outline-color: rgba(13, 110, 253, .38); }

@media (prefers-reduced-motion: reduce) {
    .kd-subnav-ziel { transition: none; }
}

/* ── Tabelle mit mitlaufendem Kopf (.tabelle-sticky am Scroll-Container) ──
   Ein `position: sticky` allein genügt NICHT: Bootstrap hebt Bedienelemente in
   den Zeilen über die Standard-Ebene — ein fokussiertes Feld in einer
   `.input-group` bekommt `z-index: 5`, Knöpfe einer `.btn-group` bei
   Hover/Fokus `z-index: 1`. Der Kopf muss also DARÜBER liegen, sonst schiebt
   sich beim Scrollen die gerade bearbeitete Zeile über die Spaltentitel
   (aufgefallen in der Lieferanten-Preisliste: Preisfeld angeklickt, gescrollt,
   Betrag lag über „eVP").

   Ebenso Pflicht: ein DECKENDER Hintergrund auf den `th` (nicht auf `thead` —
   bei `border-collapse: collapse` malt der Browser den thead-Hintergrund nicht
   zuverlässig) und der untere Rand als `inset`-Schatten, weil echte Ränder beim
   Scrollen mit der Zelle wegwandern. */
.tabelle-sticky thead th {
    position: sticky;
    top: 0;
    /* Über Bootstraps 5 (fokussiertes .input-group-Feld). */
    z-index: 6;
    background-color: #f1f3f5;
    box-shadow: inset 0 -1px 0 var(--bs-border-color);
}

/* ── Geöffnete Dropdowns über den klebenden Sprung-Leisten ──
   Ein aufgeklapptes Menü ist immer das oberste Bedienelement — es reagiert auf
   den nächsten Klick. Bootstrap gibt ihm z-index 1000, die Akten-Sprung-Leisten
   (.kd-subnav in Kunden-, Personen-, Geräte- und Lieferantenakte) liegen aber
   bei 1020: das „+ Neu"-Menü der Kopfkarte reichte hinter die Pillen, und der
   Klick auf den letzten Eintrag landete auf einer Pille statt im Menü.
   1025 hält es unter der fixed-top-Navbar (1030) und damit unter Modals/Toasts —
   nur die Leisten darunter werden überdeckt, und genau das ist gewollt. */
.dropdown-menu { --bs-dropdown-zindex: 1025; }

/* ============================================================================
   Kundenakte: Timeline als zweite, mitlaufende Spalte (.kd-panels-split)
   Pages/Kunden/Details.cshtml. Ab lg steht die Timeline-Karte rechts neben dem
   Sektions-Stack und klebt beim Scrollen unter der Sprung-Leiste — die Akte
   liest sich damit wie in Daylite: links arbeiten, rechts sieht man ohne
   Springen, was zuletzt passiert ist. Darunter (Tablet/Handy) bleibt alles beim
   einspaltigen Stapel, die Karte steht dann wie bisher am Ende.
   ============================================================================ */

/* Einspaltig (und als Grundwert): fester Scroll-Kasten wie bisher. */
.kd-tl-scroll { max-height: 520px; overflow-y: auto; }
.kd-tl-suche { width: 210px; }

@media (min-width: 992px) {
    .kd-panels-split {
        display: grid;
        /* Zweite Spalte: rund ein Drittel der Breite, damit Titel und Beschreibung
           eines Eintrags nebeneinander Platz haben — mit Untergrenze, sonst bliebe
           am lg-Rand (992 px) für die Formulare links zu wenig übrig, und mit
           Obergrenze, damit sie auf sehr breiten Schirmen nicht ausufert.
           minmax(0,…) links verhindert, dass breite Tabellen/Bilder in den Karten
           die Spalte über die Seitenbreite hinaus aufblasen. */
        grid-template-columns: minmax(0, 1fr) clamp(320px, 32%, 520px);
        column-gap: 1.5rem;
        /* Kein row-gap: der senkrechte Abstand kommt weiterhin vom mb-4 der
           Karten — sonst stünde alles doppelt so weit auseinander wie vorher. */
        row-gap: 0;
        align-items: start;
    }
    /* Ohne diese Zeile füllt der Auto-Flow Spalte 2 mit der jeweils nächsten
       Karte auf — ALLE Karten gehören nach links, nur die Timeline nicht. */
    .kd-panels-split > * { grid-column: 1; min-width: 0; }

    .kd-panels-split > .kd-tl-card {
        grid-column: 2;
        /* `1 / -1` funktioniert hier NICHT: die Zeilen sind implizit, -1 zeigt
           damit auf das Ende des expliziten Grids (= Linie 1) und der Span wäre
           eine einzige Zeile hoch. Der Span muss die Spalte über den ganzen
           Stapel aufziehen, denn er ist der Weg, den die klebende Karte fahren
           kann. Leere Zeilen darüber hinaus sind 0 hoch (row-gap: 0). */
        grid-row: 1 / span 200;
        align-self: start;
        position: sticky;
        /* Gleicher Versatz wie die scroll-margin der Sektions-Karten: knapp
           unter Navbar + Sprung-Leiste. */
        top: calc(var(--qrv-navbar-h, 58px) + 3.6rem);
        /* Höhe so, dass die Karte am Seitenende GENAU mit der letzten Karte der
           linken Spalte abschließt. Die Rechnung ist die Summe der Ränder oben
           und unten, nichts davon ist geschätzt:
             oben  3,6rem = der eigene sticky-Versatz unter der Sprung-Leiste
             unten 1,5rem = das mb-4 der Karte (bleibt stehen, es gehört zur
                            Zeilenhöhe des Grids — ohne es ragte die Spalte um
                            genau diesen Betrag tiefer als die linke Seite)
           macht 5,1rem, dazu Navbar und die fixierte Fußzeile, hinter der die im
           Viewport klebende Karte sonst endete (die letzten Timeline-Zeilen wären
           verdeckt). Nachgemessen am Seitenende: Unterkante beider Spalten gleich.
           Wer eine der beiden Konstanten oben ändert, ändert diese Summe mit. */
        max-height: calc(100vh - var(--qrv-navbar-h, 58px) - var(--qrv-footer-h, 26px) - 5.1rem);
        display: flex;
        flex-direction: column;
        min-height: 0;
    }
    /* Kopfzeile und Filter-Leiste behalten ihre Höhe … */
    .kd-panels-split > .kd-tl-card > * { flex: 0 0 auto; }

    /* Kopfzeile in der schmalen Spalte auf drei Zeilen umbrechen: Titel, dann die
       Drop-Zone über die volle Breite, dann Suche + Filter. Nebeneinander wie in
       der vollen Breite bräche die Drop-Zone sonst vierzeilig um. */
    .kd-panels-split > .kd-tl-card > .card-header { flex-wrap: wrap; row-gap: .5rem; }
    .kd-panels-split > .kd-tl-card > .card-header > .d-flex { width: 100%; }
    .kd-panels-split > .kd-tl-card .kd-dropzone { flex: 1 1 100%; }
    /* Der Hinweistext kürzt sich, statt die Zone zu sprengen. Er darf dabei SCHRUMPFEN,
       aber nicht wachsen (kein flex-grow): sonst füllt er die ganze Zone, und Symbol und
       Text stehen am linken Rand einer sonst leeren Fläche statt mittig — seit der Wegfall
       des „Auswählen"-Knopfes dort Platz gelassen hat, sieht man das sofort. */
    .kd-panels-split > .kd-tl-card .kd-dropzone > .small {
        flex: 0 1 auto;
        min-width: 0;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /* Suchfeld nimmt den Rest der letzten Zeile, der Filter-Knopf bleibt daneben. */
    .kd-panels-split > .kd-tl-card .kd-tl-suche { width: auto; flex: 1 1 140px; }
    /* … der Body darunter nimmt den Rest und gibt ihn nach innen weiter. */
    .kd-panels-split > .kd-tl-card > .kd-tl-body {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }
    /* Der Wrapper ist das Tausch-Ziel des Inline-Uploads (inline-upload.js) —
       die Klasse steht deshalb im Markup und kommt mit der frisch gerenderten
       Fassung zurück. */
    .kd-panels-split .kd-tl-wrap {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }
    /* min-height: 0 ist Pflicht, sonst wächst der Flex-Kasten mit seinem Inhalt
       aus der Karte heraus, statt in sich zu scrollen. */
    .kd-panels-split .kd-tl-scroll {
        flex: 1 1 auto;
        min-height: 0;
        max-height: none;
    }

    /* Upload-Zeilen (eine je gewählter Datei) stapeln in der schmalen Spalte.
       Die Bootstrap-Spalten im Zeilen-Template rechnen gegen die VIEWPORT-Breite
       (col-sm-4), nicht gegen die Spalte, in der sie stehen: bei einer 320-520px
       breiten Timeline blieben je Feld gut 100px übrig. Das Typ-Feld war damit
       nicht mehr lesbar, und sein Autocomplete-Dropdown brach den Vorschlag
       Buchstabe für Buchstabe um (im Feld gemeldet). Untereinander hat jedes
       Feld die volle Spaltenbreite. */
    .kd-panels-split > .kd-tl-card .kd-up-row > .row > [class*="col-"] {
        flex: 0 0 auto;
        width: 100%;
    }

    /* Lieferantenakte: EINE statische Upload-Zeile (kein Pro-Datei-Template wie
       oben) — gleiche Falle, gleiche Lösung, nur direkt am Zeilen-Div (#lfUploadRow,
       Pages/Lieferanten/Edit.cshtml). */
    .kd-panels-split > .kd-tl-card #lfUploadRow > [class*="col-"] {
        flex: 0 0 auto;
        width: 100%;
    }
}

/* ============================================================================
   Fußzeile immer im Bild (.app-footer) — Pages/Shared/_Layout.cshtml
   Die Zeile trägt die Versionsnummer samt Server-Ampel (grün/rot, /health-Poll)
   und die Rechtslinks; im Fluss war sie auf langen Seiten (Kundenakte, Listen)
   erst nach dem Scrollen ans Ende zu sehen.
   --qrv-footer-h = real gemessene Höhe (gesetzt in _Layout.cshtml), Fallback
   26px für den Moment vor dem ersten Messen bzw. bei abgeschaltetem JS.
   Additiver Block am Dateiende — bewusst KEINE bestehenden Regeln angefasst.
   ============================================================================ */
.app-footer {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Über Karten-Inhalten und den klebenden Sprung-Leisten (1020), aber UNTER
       den Aufklappmenüs (1025) und der fixed-top-Navbar (1030): ein Dropdown
       am unteren Fensterrand muss über der Fußzeile liegen, sonst wäre sein
       letzter Eintrag verdeckt — dieselbe Falle wie bei den Sprung-Leisten. */
    z-index: 1021;
    margin: 0;
    padding: 4px 0;
    line-height: 1.2;
    /* Deckend, sonst scrollt der Inhalt sichtbar durch die Zeile. Gleicher Ton
       wie der Seitenhintergrund (<body class="bg-light">). */
    background: #f8f9fa;
    border-top: 1px solid rgba(0, 0, 0, .08);
    /* Safe-Area iPhone: unterhalb der Zeile bleibt sonst die Home-Leiste drüber. */
    padding-bottom: calc(4px + env(safe-area-inset-bottom, 0px));
}
/* Der Platz für die aus dem Fluss genommene Zeile wird als padding-bottom am
   <body> reserviert — die Regel steht in navbar.css, weil die dort schon den
   Safe-Area-Inset setzt und später geladen wird (ein zweites body-Regelwerk
   hier würde stillschweigend verlieren). Reserviert wird GENAU die gemessene
   Höhe: die Seiten-Gesamthöhe bleibt damit identisch zu vorher (die Zeile
   belegte im Fluss dieselben Pixel), und die viewport-basierten Rechnungen in
   Kalender/Dashboard (calc(100dvh - …)) bekommen keinen neuen Scrollbalken. */
/* Beim Drucken gehört die Zeile wieder in den Fluss — eine fixierte Fußzeile
   legt sich sonst auf jeder Seite über den Inhalt. */
@media print {
    .app-footer { position: static; }
}

/* ============================================================================
   Toasts fahren vom unteren Bildschirmrand rein (_ToastMessages.cshtml,
   inline-upload.js, inline-save.js) — vorher saß der Container unter der
   Navbar und verdeckte dort regelmäßig das rechte Kopf-Dropdown/den
   Seitenkopf (im Feld gemeldet). Jetzt unten rechts über der Fußzeile, mit
   Slide-in-Animation statt reinem Fade: die Bootstrap-Toast-JS setzt beim
   Öffnen UND Schließen kurz die Klasse "showing" zusätzlich zu "show"
   (bootstrap.bundle.js, Toast.show/hide) — genau in diesem Fenster gilt der
   Transform-Startwert, danach (Öffnen) bzw. am Ende (Schließen) greift der
   Zielwert per Transition. Kein Transform am Container selbst (der bliebe
   dann während der Animation außerhalb seiner eigenen Klickfläche stehen).
   ============================================================================ */
.toast-container .toast.fade {
    transition: opacity .15s linear, transform .25s ease-out;
    transform: translateY(0);
}
.toast-container .toast.fade.showing {
    transform: translateY(24px);
}
@media (prefers-reduced-motion: reduce) {
    .toast-container .toast.fade {
        transition: opacity .15s linear;
        transform: none;
    }
}

/* ── Gleicher Abstand zwischen Seiteninhalt und Fußzeile, auf JEDER Seite ──
   Vorher entstand der Abstand aus dem Rand des jeweils letzten Elements einer
   Seite und war damit Zufall: Kartenstapel (Kalender, Akten) endeten mit 24px,
   Container ohne eigenen Rand (Dokumente, Einstellungen) klebten mit 0px an der
   Zeile, das Dashboard passt sich sogar selbst auf wenige Pixel Rest an.

   Der Abstand kommt jetzt aus EINEM leeren ::after am <main>, das nur einen
   margin-top trägt (Höhe 0). Der Trick ist das Zusammenfallen benachbarter
   Ränder: aneinanderstoßende Ränder bilden nicht die Summe, sondern das
   MAXIMUM — der Rand des letzten Seiten-Elements verschwindet also in diesem
   einen Abstand, statt sich auf ihn draufzusetzen. Eine Seite ohne eigenen Rand
   bekommt ihn, eine Seite mit mb-4 behält genau ihn. Ein unsichtbares Modal am
   Ende (display:none erzeugt keinen Kasten) stört dabei nicht.
   Deshalb hier KEIN padding und kein `display: flow-root` am <main>: beides
   unterbindet genau dieses Zusammenfallen, und die Abstände addierten sich. */
:root {
    /* EINE Stelle für diesen Abstand — auch die Selbst-Einpassung der Planung
       (Pages/Index.cshtml) liest ihn hier aus, statt eine eigene Zahl zu führen. */
    --qrv-inhalt-abstand: 1.5rem;
}
main::after {
    content: '';
    display: block;
    height: 0;
    margin-top: var(--qrv-inhalt-abstand, 1.5rem);
}
/* Eine Ausnahme: die Kundenakte. Dort ist der letzte Rand KEIN zusammenfallender
   Rand, sondern steckt als mb-4 der letzten Karte INNERHALB des Grid-Kastens (er
   hält zugleich die Timeline-Spalte bündig, siehe .kd-panels-split oben). Er
   trägt den Abstand also schon — ein zweiter würde sich hier wirklich addieren. */
main:has(> .kd-panels)::after {
    margin-top: 0;
}
/* Die Planung braucht KEINE Ausnahme, aber eine Erklärung: dort ist <main> selbst
   ein Flex-Container (die Dashboard-Zeile füllt die Resthöhe, siehe
   Pages/Index.cshtml). Ränder fallen in einem Flex-Container nicht zusammen —
   das ::after wird dort ein eigenes Flex-Element und nimmt seine 1,5rem als
   echten Platz, den die wachsende Zeile abgibt. Ergebnis ist derselbe Abstand,
   nur über einen anderen Weg. */

/* ============================================================================
   .min-w-0 — die fehlende Utility (08/2026)
   Das Markup benutzt sie an 62 Stellen in 20 Dateien, damit ein Flex-/Grid-Kind
   unter seine Inhaltsbreite schrumpfen darf und `text-truncate` daneben greift.
   Definiert war sie nirgends: Bootstrap 5.3 liefert nur min-vw-/min-vh-, und in
   site.css stand sie auch nicht. Ohne Definition bleibt `min-width: auto` stehen
   (der Default für Flex-Items) — die Zeile schrumpft nicht, sondern wird so
   breit wie ihr längster Text und schiebt die ganze Seite nach rechts.
   Auf dem Desktop fällt das kaum auf, auf dem Telefon sofort: die Kundenakte
   war 417px breit bei 390px Fenster, also 27px Seitwärts-Wackeln, und die
   Namen wurden nicht mit „…" gekürzt, sondern liefen aus dem Bild.
   !important wie bei allen Bootstrap-Utilities — sie steht bewusst über den
   Layout-Regeln der Komponenten, sonst wäre sie im Markup wirkungslos.
   ============================================================================ */
.min-w-0 { min-width: 0 !important; }

/* ============================================================================
   Bedienung am Telefon (< 768px) — 08/2026
   Am emulierten iPhone durchgemessen; beide Regeln unten sind Messwerte, keine
   Vermutungen. Sie gelten NUR unter dem md-Breakpoint, der Desktop bleibt exakt
   wie er ist.
   ============================================================================ */
@media (max-width: 767.98px) {

    /* --- 1. Kein Auto-Zoom beim Antippen eines Feldes ---
       iOS Safari zoomt die Seite hinein, sobald ein fokussiertes Feld unter 16px
       Schrift hat — danach steht die Seite verschoben und man muss von Hand
       zurückzoomen. Die App setzt html/body auf 14px, `form-control-sm` macht
       daraus 12,25px: gemessen an JEDEM Feld der Liste (Suche, Filter, Timeline).
       Die Klasse steckt an 700 Stellen im Markup — zentral ist der einzige Weg.
       16px ist die Schwelle, nicht ein Geschmackswert: ein Pixel weniger und
       Safari zoomt wieder. Höhe/Abstände der Felder bleiben unberührt (nur
       die Schrift wächst), Felder werden dadurch etwas höher — am Telefon
       willkommen. Kästchen/Radios sind ausgenommen, sie tragen keinen Text. */
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="file"]),
    select,
    textarea {
        /* !important ist hier Pflicht, kein Reflex: Bootstraps .form-control-sm /
           .form-select-sm sind KLASSEN-Selektoren und schlagen einen Element-Selektor.
           Ohne das blieben genau die kleinen Felder klein — im Messlauf übrig
           gebliebene 6 von 7 auf der Geräteliste. */
        font-size: 16px !important;
    }

    /* --- 2. Tippziele groß genug ---
       Gemessen: Sprung-Pillen der Akten 25px hoch, Icon-Knöpfe in Listen
       28x38px. Apple nennt 44x44px als Mindestmaß, und genau das ist der
       Unterschied zwischen „trifft" und „trifft daneben", wenn man das Gerät
       einhändig in der Werkstatt bedient.
       min-height statt height: nichts wird gestaucht, was schon größer ist. */
    /* Die Knopf-Mindesthöhe steht schon weiter oben im Touch-Block dieser Datei
       (dort mit den Ausnahmen für btn-link/btn-close) — hier nur, was ihr fehlt:
       die Sprung-Pillen der Akten (gemessen 25px hoch) und die Blätter-Links. */
    .kd-subnav-link,
    .page-link {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    /* Auch in der BREITE: Knöpfe, die nur ein Icon tragen, waren 28px schmal.
       Bewusst über dieselbe Ausnahme-Liste wie die Höhe statt über :has — ein
       Knopf mit Text ist ohnehin breiter als 44px, die Regel greift also genau
       dort, wo sie soll, und ein Icon mit unsichtbarer Beschriftung daneben
       (kein :only-child mehr) fällt nicht durchs Raster. Gegengemessen: die
       dichteste Reihe der App (fünf Icon-Knöpfe in der Kontaktzeile) passt in
       360px und bricht sonst wie bisher um. */
    .btn:not(.btn-link):not(.btn-close),
    .page-link {
        min-width: 44px;
    }
    /* Kästchen sind mit 1em (= 14px, die App setzt die Wurzel auf 14px) kaum zu
       treffen — feste 20px statt rem, damit die Wurzelgröße hier nicht durchschlägt. */
    .form-check-input,
    /* Die Aufgabenliste setzt ihre Kästchen selbst (.aufgabe-zeile .form-check-input,
       zwei Klassen = höhere Spezifität) — hier ausdrücklich mitgenommen, statt die
       Regel per !important durchzudrücken. Wer eine weitere Stelle mit eigener
       Kästchen-Größe baut, ergänzt sie hier. */
    .aufgabe-zeile .form-check-input {
        width: 20px;
        height: 20px;
    }
}

/* ── Filter-Pillen: dritter Zustand „ausgeblendet" — EINE Regel für alle Pill-Filter
   der App (Kundenakte-Timeline, Planung, künftige), statt einer Kopie je Seite.
   Bedienung überall gleich: 1. Klick isoliert (nur diese Kategorie, Bootstraps
   .active-Fülloptik), 2. Klick auf die GERADE isolierte Pille blendet sie aus
   (diese Klasse), 3. Klick setzt zurück auf neutral. !important ist hier Pflicht:
   Geräteklassen-Pillen tragen inline Farb-Styles (Tenant-Override), die sonst
   gewinnen würden — eine ausgeblendete Pille muss trotzdem rot/durchgestrichen
   erscheinen, unabhängig von ihrer eigentlichen Klassenfarbe. */
/* .btn. davor ist Pflicht, nicht Zierde: die Quelle-Pillen der Planung tragen eigene
   Farbklassen (.btn-outline-geplant/-vorschlag/-termin, _Layout.cshtml) deren :hover/
   :active-Zustand ebenfalls !important trägt und NACH site.css im <head> steht — bei
   gleicher Spezifität gewinnt sonst die Ladereihenfolge, nicht diese Regel. Die
   zusätzliche Klasse hebt die Spezifität an, damit .filter-pill-aus unabhängig von der
   Reihenfolge gewinnt (auch beim Hover). */
.btn.filter-pill-aus {
    border-color: #dc3545 !important;
    color: #dc3545 !important;
    background: #fff5f5 !important;
    text-decoration: line-through;
}
.btn.filter-pill-aus i { opacity: .55; }
/* color MUSS hier erneut stehen, nicht nur background: pro Property gewinnt bei
   gleicher Spezifität die Ladereihenfolge — ohne die eigene :hover-Deklaration würde
   die Textfarbe an die konkurrierende :hover-Regel verlieren (gleiche Spezifität wie
   die Basisregel oben) und auf Weiß/Blass umspringen, während der Rand/Hintergrund
   schon rot bleibt. Gegengeprüft per Screenshot. */
.btn.filter-pill-aus:hover {
    background: #ffe3e3 !important;
    color: #dc3545 !important;
    border-color: #dc3545 !important;
}
