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;
}
/* 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: 190px; 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; }
}

/* 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; }

/* 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-/Geraeteakte) 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 nur gedaempft und durchgestrichen, sie verschwinden NICHT aus der Liste:
   sonst haekt man beim Abarbeiten versehentlich die nachrueckende Zeile mit ab. */
.aufgabe-zeile { display: flex; align-items: flex-start; gap: .65rem; padding: .6rem .75rem; border-radius: 10px; 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; }
/* 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; }

/* 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; }

/* ─── 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) {
    /* WCAG-nahe Mindesthöhe; Icon mittig. `min-height` verbreitert KEINE
       Icon-Button-Cluster — nur die Tappfläche in der Höhe. Text-Links
       (btn-link) und das Modal-X (btn-close) bleiben unverändert. */
    .btn:not(.btn-link):not(.btn-close) {
        min-height: 38px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }
    .form-control, .form-select, .input-group-text { min-height: 38px; }

    /* 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;
    top: calc(var(--qrv-navbar-h, 58px) + 4px);
    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;
    box-shadow: 0 1px 2px rgba(15, 23, 42, .07),
                0 10px 26px -8px rgba(15, 23, 42, .32);
    /* 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; }
.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; }
/* 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 = sichtbarer Streifen unter Navbar/Leiste, MINUS des mb-4 der Karte
           (die 1,5rem stecken in den 6,1rem). Beides hängt zusammen: das mb-4
           bleibt stehen, damit die Karte am Seitenende bündig mit der letzten
           Karte der linken Spalte abschließt (der Abstand gehört zur Zeilenhöhe
           des Grids, ohne ihn ragte die Spalte um genau diese 1,5rem tiefer).
           Die Höhe muss dafür mitschrumpfen, sonst schöbe sich die Karte am
           Seitenende um denselben Betrag unter die Sprung-Leiste und der
           Karten-Kopf mit Suche/Filter verschwände dahinter. */
        max-height: calc(100vh - var(--qrv-navbar-h, 58px) - 6.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. */
    .kd-panels-split > .kd-tl-card .kd-dropzone > .small {
        flex: 1 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;
    }
}
