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;
}

/* ── Links: kein Blau, keine Unterstreichung — beim Darüberfahren fett (Nutzerwunsch 10/2026) ──
   Bootstraps Grund-Link (`a`) ist blau und unterstrichen; hier trägt er die Farbe des umgebenden
   Textes, ohne Linie. Fett beim Darüberfahren/Tastatur-Fokus ist ein gezeichnetes Fett
   (-webkit-text-stroke in Textfarbe, alle Browser): echtes font-weight macht den Text breiter und
   ließe alles dahinter springen — das ließe sich nur mit einer unsichtbaren Kopie JEDES Linktexts
   verhindern (Markup an jeder Stelle). Stärke an EINER Stelle: --link-fett, auch von Nicht-Links
   mit Link-Verhalten genutzt (Klapp-Trigger, <summary>).
   Ausgenommen sind Bausteine mit eigenem Hover-Aussehen: Knopf, Navigations-/Reiter-Link,
   Sprung-Leiste (.kd-subnav-link), Menüeintrag, Blätter-Link, Listenzeile, Logo. Reiter, Blättern
   und der aktive Punkt der Sprung-Leiste behalten ihr Blau — das sind Bedienelemente („hier bist
   du"), keine Textlinks. `.btn-link` ist ein Knopf in Link-Optik und folgt den Links.
   App-eigene Hover-Regeln („a:hover { color: #0d6efd; text-decoration: underline }") sind
   entfernt; neue bitte nicht wieder anlegen, sonst kommt dort das Blau zurück. Ausnahme mit
   Absicht: Links in EMPFANGENEN Mails (.mail-body-html, weiter unten). */
:root { --link-fett: .4px; }
a { color: inherit; text-decoration: none; }
a:not(.btn, .nav-link, .kd-subnav-link, .dropdown-item, .page-link, .list-group-item, .navbar-brand):hover,
a:not(.btn, .nav-link, .kd-subnav-link, .dropdown-item, .page-link, .list-group-item, .navbar-brand):focus-visible,
.btn-link:hover,
.btn-link:focus-visible {
  -webkit-text-stroke: var(--link-fett) currentColor;
}
/* Quellenangabe der Karten (Leaflet: „© OpenStreetMap"): Leaflets eigenes CSS färbt sie blau
   und unterstreicht beim Darüberfahren — mit höherer Spezifität, weil leaflet.css NACH site.css lädt. */
.leaflet-container .leaflet-control-attribution a[href] { color: inherit; }
.leaflet-container .leaflet-control-attribution a[href]:hover { text-decoration: none; }
.btn-link {
  --bs-btn-color: currentColor;
  --bs-btn-hover-color: currentColor;
  --bs-btn-active-color: currentColor;
  text-decoration: none;
}

/* ── 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;
}

/* ── Seiten-Kopfzeile + Akten-Kopfkarte: Knöpfe in der Größe von .btn-sm ──
   Nutzer-Vorgabe 10/2026 („der Kalender-Knopf auf ‚Heute' hat eine gute Größe — auf
   allen Seiten"). Zentral statt btn-sm an ~40 Seitenköpfen: dieselbe Kopfzeilen-Erkennung
   wie oben, zusätzlich mit der Überschrift eine Ebene tiefer (Titel + Untertitel in
   einem div, z. B. Heute, Tourenplanung, E-Mail) und die Kopfkarte der Akten (.kd-kopf).
   Über die Bootstrap-Variablen, damit Hover/Fokus/Dropdown-Pfeil unverändert bleiben.
   Eigene Köpfe (Kalender .cal-header, Auslastung) tragen btn-sm im Markup. Die
   44-px-Tippziele auf Touch-Geräten (weiter unten) gelten unverändert. */
:is(.d-flex.justify-content-between.align-items-center:has(> h1, > h2, > h3, > div > h1, > div > h2, > div > h3), .kd-kopf)
  .btn:not(.btn-lg, .btn-link, .btn-close) {
  --bs-btn-padding-y: .25rem;
  --bs-btn-padding-x: .5rem;
  --bs-btn-font-size: .875rem;
  --bs-btn-border-radius: var(--bs-border-radius-sm);
}

/* ── 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;
}
/* Trefferzeile: EINE Zeile (Name + kurze Angabe), der Suchbegriff markiert; eine zweite Zeile nur als
   Fundstelle, wenn der Begriff in einem sonst unsichtbaren Feld steht (10/2026, global-search.js). */
.gs-mark {
  background: #fff1a8; color: inherit; padding: 0; border-radius: 2px;
}
#globalSearchResults .list-group-item.active .gs-mark {
  background: rgba(255, 255, 255, .35); color: #fff;
}
.gs-fundstelle { color: #495057; }
/* ⓘ an der Zeile (nur ohne Maus, Telefon): 44 px Tippfläche, ragt in das Zeilen-Padding. In px, nicht
   rem — am Telefon rechnet die App mit kleinerer Grundschrift, 2.75rem waren dort nur 38 px. */
.gs-info {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin: -.5rem -.75rem -.5rem 0;
  color: #6c757d; font-size: 1.1rem; cursor: pointer;
}
.gs-treffer.gs-details-offen .gs-info { color: #0d6efd; }
@media (hover: none) {
  #globalSearchResults a.gs-treffer { min-height: 44px; }
}
/* Treffer und seine aufgeklappten Details sind EIN Block: keine Trennlinie dazwischen, gleicher Grund
   (außer der per Tastatur gewählte, der bleibt blau). */
#globalSearchResults .gs-treffer.gs-details-offen { border-bottom-color: transparent; }
#globalSearchResults .gs-treffer.gs-details-offen:not(.active) { background: #f8f9fa; }
/* Details unter der Zeile — weich auf- und zugeklappt (grid-template-rows 0fr ⇄ 1fr: animiert die echte
   Höhe, ohne sie zu kennen), eingerückt bis unter den Namen (Badge 60 px + Abstand, --gs-einzug). Zu: keine
   Höhe, kein Innenabstand, kein Rand — sonst stünde während des Zuklappens eine doppelte Linie da.
   Der Einzug ist Innenabstand von .gs-details-innen, nicht des Kastens: der Ansprechpartner-Baum ragt
   nach links hinein, und overflow:hidden schneidet erst am Rand des Innenabstands ab.
   Rechts nur .25rem am Kasten, den Rest (.75rem) tragen Felder und Personenzeilen selbst: das ⓘ einer Person
   ragt in diesen Rest hinein und steht so bündig unter dem ⓘ der Firma — ragte es über .gs-details-innen
   hinaus, wäre der Kasten seitlich scrollbar, und ein scrollIntoView (Playwrights Tippen) schob ihn 12 px
   nach links. */
.gs-details-inline {
  --gs-einzug: calc(1rem + 60px + .5rem);
  display: grid; grid-template-rows: 0fr; opacity: 0;
  background: #f8f9fa; font-size: .85rem;
  padding: 0 .25rem 0 0;
  border-bottom-width: 0;
  transition: grid-template-rows .22s ease, opacity .22s ease, padding .22s ease;
}
.gs-details-inline.offen {
  grid-template-rows: 1fr; opacity: 1;
  padding-top: .1rem; padding-bottom: .6rem; border-bottom-width: 1px;
}
.gs-details-innen { overflow: hidden; min-height: 0; padding-left: var(--gs-einzug); }
.gs-details-inline.gs-details-kind { --gs-einzug: calc(2.6rem + 60px + .5rem); }
@media (prefers-reduced-motion: reduce) {
  .gs-details-inline { transition: none; }
}
/* Felder der Details: Symbol + Wert, Anschrift und lange Texte je eine Zeile (.gs-vs-zeile), Kurzes daneben. */
.gs-vs-felder { display: flex; flex-wrap: wrap; gap: .25rem 1.25rem; padding-top: .15rem; }
.gs-details-innen > .gs-vs-felder, .gs-vs-mehr { padding-right: .75rem; }
.gs-vs-feld { display: inline-flex; align-items: baseline; gap: .45rem; min-width: 0; overflow-wrap: anywhere; }
.gs-vs-feld.gs-vs-zeile { flex-basis: 100%; }
.gs-vs-feld > .bi { flex-shrink: 0; width: 1em; text-align: center; color: #6c757d; }
.gs-vs-name { flex-shrink: 0; color: #6c757d; font-size: .78rem; }
/* Telefon, Mobil, E-Mail und „Karte" als runde Knöpfe unter der Anschrift (global-search.js, erreichbarHtml).
   a.… hebt die Spezifität über die globale Link-Regel (gezeichnetes Fett): ein Knopf zeigt das Darüberfahren
   an seiner Fläche. Telefon: 44 px Tippziel. */
.gs-vs-knoepfe { flex-basis: 100%; display: flex; flex-wrap: wrap; gap: .4rem; padding-top: .2rem; min-width: 0; }
a.gs-vs-knopf {
  display: inline-flex; align-items: center; gap: .4rem; max-width: 100%; min-height: 1.85rem;
  padding: .2rem .75rem; border: 1px solid #dee2e6; border-radius: 50rem;
  background: #fff; color: #212529; font-size: .8rem; line-height: 1.2;
  transition: background-color .15s ease, border-color .15s ease;
}
a.gs-vs-knopf:hover, a.gs-vs-knopf:focus-visible { background: #e9ecef; border-color: #adb5bd; -webkit-text-stroke: 0; }
.gs-vs-knopf > .bi { flex-shrink: 0; color: #6c757d; }
.gs-vs-knopf > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (hover: none) {
  a.gs-vs-knopf { min-height: 44px; padding: .3rem .9rem; }
}
/* Ansprechpartner in den Details: wie die Personen-Zeilen unter einer Firma (.gs-kind) — Abzeichen
   „Kontakt" an derselben Stelle (2.6rem vom Zeilenrand), Winkel bei 1.15rem. Anders als dort läuft die
   Linie durch (├ … └): beim ersten Eintrag nach oben bis an den Rand der Details (dort schneidet
   overflow:hidden sie ab) und von dort per ::before des Kastens in die Firmenzeile hinein. */
.gs-vs-baum {
  list-style: none; padding: 0; margin: .25rem 0 0 calc(1.15rem - var(--gs-einzug));
}
.gs-vs-baum li { position: relative; padding: 0 .75rem 0 1.45rem; }
.gs-vs-baum li > a, .gs-vs-baum li > span { position: relative; display: flex; align-items: center; gap: .5rem; min-height: 1.75rem; }
@media (hover: none) {
  .gs-vs-baum li > a { min-height: 44px; }
}
.gs-vs-baum .badge { font-size: .75rem; min-width: 60px; white-space: nowrap; flex-shrink: 0; -webkit-text-stroke: 0; }
.gs-vs-person { min-width: 0; overflow-wrap: anywhere; }
/* Der Winkel hängt an der Personenzeile, nicht am <li>: klappen darunter ihre Kontaktdaten auf, wird das <li>
   höher, und bottom:50% des <li> zöge den Winkel in deren Mitte. */
.gs-vs-baum li > :first-child::before {
  content: ""; position: absolute; left: -1.45rem; top: -.35rem; bottom: 50%; width: .85rem;
  border-left: 1px solid #ced4da; border-bottom: 1px solid #ced4da; border-bottom-left-radius: .35rem;
}
.gs-vs-baum li:first-child > :first-child::before { top: -100vh; }
.gs-vs-baum li:not(:last-child)::after {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; border-left: 1px solid #ced4da;
}
/* z-index: die Firmenzeile darüber hebt Bootstrap beim Darüberfahren auf 1 — sie deckte das obere Stück der
   Linie sonst zu (gleicher Wert, die Details stehen später im DOM). Nicht höher: das Filter-Menü liegt auf 2. */
.gs-details-inline.gs-details-baum { z-index: 1; }
.gs-details-inline.gs-details-baum::before {
  content: ""; position: absolute; left: 1.15rem; top: -.35rem; height: .5rem; border-left: 1px solid #ced4da;
}
.gs-vs-mehr { color: #6c757d; margin: .15rem 0 0 calc(2.6rem - var(--gs-einzug)); }
/* Kontaktdaten eines Ansprechpartners (global-search.js, personAufklappen): weich wie die Details der Firma,
   eingerückt bis unter seinen Namen (Abzeichen 60 px + Abstand). */
.gs-vs-pdetails {
  display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows .22s ease, opacity .22s ease, padding .22s ease;
}
.gs-vs-pdetails.offen { grid-template-rows: 1fr; opacity: 1; padding-bottom: .4rem; }
.gs-vs-pdetails-innen { overflow: hidden; min-height: 0; padding-left: calc(60px + .5rem); }
@media (prefers-reduced-motion: reduce) {
  .gs-vs-pdetails { transition: none; }
}
/* ⓘ an der Person (nur ohne Maus, Telefon) — wie das der Firmenzeile, 44 px Tippfläche. */
.gs-p-info {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin: 0 -.75rem 0 auto;
  color: #6c757d; font-size: 1.1rem; cursor: pointer;
}
.gs-vs-p-offen .gs-p-info { color: #0d6efd; }

/* Vorschlagslisten unter Suchfeldern — das Muster `list-group position-absolute w-100 shadow-sm`
   (Empfänger der Bestellung, Kunde bei Gerät/Einsatzbericht/Aufgabe, Ersatzteil-Suchen; neun
   Stellen). Bootstrap gibt nur den EINTRÄGEN Hintergrund und Rahmen, dem Container nicht: wo
   max-height den letzten Eintrag anschneidet, lag dessen halbe Zeile ohne Abschluss direkt auf
   dem Text der Seite darunter, beides verschwamm (10/2026, Bestellungen/Neu). Jetzt ein
   geschlossener Kasten mit eigenem Rand und deutlichem Schatten; die Einträge verlieren ihre
   Seitenränder wie bei `.list-group-flush`. `!important` beim Schatten, weil `.shadow-sm` ihn
   selbst trägt. Die Scrollposition setzen die Render-Funktionen der Seiten zurück. */
.list-group.position-absolute {
  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid var(--bs-border-color);
  box-shadow: 0 .5rem 1.5rem rgba(15, 23, 42, .16), 0 .125rem .375rem rgba(15, 23, 42, .08) !important;
  margin-top: .25rem;
}
.list-group.position-absolute > .list-group-item {
  border-width: 0 0 var(--bs-border-width);
  border-radius: 0;
}
.list-group.position-absolute > .list-group-item:last-child {
  border-bottom-width: 0;
}

/* 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; }

/* ── Geräteverbund-Rahmen (10/2026) — EIN Aussehen für Kundenakte (Geräteliste) und Geräteakte:
   gemeinsamer Rahmen mit Kopfzeile in der Farbe „seines" Verbunds (--vb; mehrere Verbünde eines Kunden
   bekommen in der Kundenakte verschiedene Farben). KEIN overflow:hidden am Rahmen: das „…"-Menü der
   Zeilen ist ein Bootstrap-Dropdown und würde abgeschnitten — die Ecken runden Kopf und letzte Zeile
   selbst. Die .kd-row-Sonderregeln für die Kundenakte stehen dort. */
.kd-verbund-rahmen {
  border: 1.5px solid var(--vb, #0d6efd);
  border-radius: 12px;
  margin: .4rem 0;
  background: color-mix(in srgb, var(--vb, #0d6efd) 3%, #fff);
}
.kd-verbund-kopf {
  display: flex; align-items: center; flex-wrap: wrap; gap: .25rem .6rem;
  padding: .4rem .75rem;
  border-radius: 10.5px 10.5px 0 0;
  border-bottom: 1px solid color-mix(in srgb, var(--vb, #0d6efd) 25%, #fff);
  background: color-mix(in srgb, var(--vb, #0d6efd) 10%, #fff);
  color: var(--vb, #0d6efd);
  font-size: .82rem; font-weight: 600;
}
.kd-verbund-kopf .kd-verbund-meta { color: #495057; font-weight: 400; }
/* „Verbund bearbeiten" rechts in der Kopfzeile (Link in der Kundenakte, Knopf in der Geräteakte). */
.kd-verbund-kopf a, .kd-verbund-kopf .kd-verbund-aktion {
  color: inherit; margin-left: auto; font-weight: 600; font-size: inherit;
}
.kd-verbund-fuss {
  padding: .35rem .75rem; font-size: .78rem; color: #6c757d;
  border-top: 1px solid color-mix(in srgb, var(--vb, #0d6efd) 15%, #fff);
}

/* Geräteakte, „Geräte am Standort": eine Zeile je Gerät, das geöffnete hervorgehoben — im Verbund-Rahmen
   in dessen Farbe. Die ganze Zeile ist der Link (.stretched-link am Namen, daher position: relative). */
.kd-geraet-zeile {
  position: relative;
  display: flex; align-items: center; flex-wrap: wrap; gap: .25rem .6rem;
  padding: .45rem .75rem; font-size: .875rem;
}
.kd-geraet-zeile + .kd-geraet-zeile { border-top: 1px solid #eef0f4; }
.kd-geraet-zeile:not(.ist-dieses):hover { background: #f8f9fa; }
.kd-geraet-zeile.ist-dieses { background: #eef4ff; border-radius: 8px; }
.kd-geraet-zeile .kd-geraet-meta { color: #495057; }
.kd-verbund-rahmen .kd-geraet-zeile + .kd-geraet-zeile { border-top-color: color-mix(in srgb, var(--vb, #0d6efd) 15%, #fff); }
.kd-verbund-rahmen .kd-geraet-zeile:not(.ist-dieses):hover { background: color-mix(in srgb, var(--vb, #0d6efd) 5%, #fff); }
.kd-verbund-rahmen .kd-geraet-zeile.ist-dieses { background: color-mix(in srgb, var(--vb, #0d6efd) 8%, #fff); border-radius: 0; }
/* Ohne Fußzeile ist die letzte Zeile der untere Rand des Rahmens: ihre Hover-Fläche folgt dessen Ecken. */
.kd-verbund-rahmen > .kd-geraet-zeile:last-child { border-radius: 0 0 10.5px 10.5px; }
.kd-geraete-mehr { padding: .45rem .75rem; font-size: .8rem; }
/* „Außerdem: 12 Geräte an 4 anderen Standorten · anzeigen" — klappt die übrigen Standorte auf (Bootstrap-
   Collapse). Der Pfeil dreht über aria-expanded, das Wort wechselt per CSS; „anzeigen" verhält sich wie ein
   Link (gezeichnetes Fett beim Darüberfahren, --link-fett). */
.kd-geraete-aufklapp {
  display: flex; align-items: center; flex-wrap: wrap; gap: .25rem .4rem; width: 100%;
  padding: .45rem .75rem; border: 0; border-radius: 8px; background: none;
  color: #6c757d; font-size: .8rem; text-align: left;
}
.kd-geraete-aufklapp:hover { background: #f8f9fa; }
.kd-geraete-aufklapp .kd-geraete-wort { color: var(--bs-body-color); font-weight: 600; }
.kd-geraete-aufklapp:hover .kd-geraete-wort,
.kd-geraete-aufklapp:focus-visible .kd-geraete-wort { -webkit-text-stroke: var(--link-fett) currentColor; }
.kd-geraete-pfeil { display: inline-block; transition: transform .15s ease; }
.kd-geraete-aufklapp[aria-expanded="true"] .kd-geraete-pfeil { transform: rotate(180deg); }
.kd-geraete-aufklapp .kg-auf,
.kd-geraete-aufklapp[aria-expanded="true"] .kg-zu { display: none; }
.kd-geraete-aufklapp[aria-expanded="true"] .kg-auf { display: inline; }
.kd-geraete-standort {
  display: flex; align-items: center; flex-wrap: wrap; gap: .25rem .4rem;
  padding: .6rem .75rem .2rem; font-size: .8rem; color: #6c757d;
}
@media (max-width: 767.98px) {
  .kd-geraet-zeile, .kd-geraete-aufklapp { min-height: 44px; }   /* Tippziel */
}

/* ── Einklappbare Karte: der Pfeil im Kopf dreht mit, sobald sie offen ist (Bootstrap setzt
   aria-expanded am Auslöser). Vorher inline in der Standorte-Karte der Kundenakte — und galt damit
   nur dort, wo diese Karte gerendert wurde. */
.kd-collapse-chev .kd-collapse-icon { display: inline-block; transition: transform .15s; }
.kd-collapse-chev[aria-expanded="true"] .kd-collapse-icon { transform: rotate(180deg); }

/* ── Ansprechpartner eingeklappt, Details per Hover (10/2026) — Kunden- UND Lieferantenakte ──
   Liste mit .kd-kontakte-klapp; je Eintrag (.kd-row) bleiben Name, Plaketten, Funktion und Knöpfe
   sichtbar, die Details (.kd-kontakt-details > .kd-kontakt-details-inner) klappen auf, sobald die Maus
   auf dem Eintrag ruht oder der Tastatur-Fokus darin steht.
   - Nur auf Geräten mit echter Maus (hover: hover + pointer: fine) — am Telefon gibt es kein Hover,
     dort bleibt alles offen.
   - Weich über grid-template-rows 0fr → 1fr (eine feste max-height schnitte lange Einträge ab); der
     Wrapper trägt overflow:hidden, nicht .kd-meta selbst (dessen d-flex !important schlüge display:grid).
   - Aufklappen nach 120 ms, Zuklappen erst nach 300 ms: beim Überstreichen der Liste springt nichts,
     und wer nach unten wechselt, bleibt im neuen (schon offenen) Eintrag, wenn der alte zuklappt.
   - Während einer Suche (.kd-suche-aktiv an der Liste) ist alles offen — man soll sehen, WARUM ein
     Eintrag passt.
   - Der Pfeil hinter der Funktion (.kd-kontakt-mehr, data-kontakt-anheften) zeigt, dass es mehr gibt;
     ein Klick heftet die Karte offen an (.ist-angeheftet), ein zweiter löst sie (js/kontakte-anheften.js). */
.kd-kontakte-klapp .kd-kontakt-mehr { display: none; }   /* ohne Maus (Telefon) gibt es nichts aufzuklappen */
@media (hover: hover) and (pointer: fine) {
  .kd-kontakte-klapp .kd-kontakt-details {
    display: grid; grid-template-rows: 0fr; opacity: 0;
    transition: grid-template-rows .2s ease .3s, opacity .15s ease .3s;
  }
  .kd-kontakte-klapp .kd-kontakt-details > .kd-kontakt-details-inner { overflow: hidden; min-height: 0; }
  .kd-kontakte-klapp .kd-row:hover .kd-kontakt-details,
  .kd-kontakte-klapp .kd-row:focus-within .kd-kontakt-details,
  .kd-kontakte-klapp .kd-row.ist-angeheftet .kd-kontakt-details,
  .kd-kontakte-klapp.kd-suche-aktiv .kd-kontakt-details {
    grid-template-rows: 1fr; opacity: 1;
    transition-delay: .12s;
  }
  .kd-kontakte-klapp .kd-kontakt-mehr {
    display: inline-flex; align-items: center; justify-content: center;
    margin-left: .25rem; padding: .1rem .3rem; border: 0; border-radius: .35rem;
    background: transparent; color: #adb5bd; font-size: .7rem; line-height: 1; vertical-align: middle;
  }
  .kd-kontakte-klapp .kd-kontakt-mehr:hover { background: #e9ecef; color: #495057; }
  .kd-kontakte-klapp .kd-kontakt-mehr i { display: inline-block; transition: transform .2s ease .12s; }
  .kd-kontakte-klapp .kd-row:hover .kd-kontakt-mehr i,
  .kd-kontakte-klapp .kd-row:focus-within .kd-kontakt-mehr i,
  .kd-kontakte-klapp .kd-row.ist-angeheftet .kd-kontakt-mehr i { transform: rotate(180deg); }
  .kd-kontakte-klapp .kd-row.ist-angeheftet .kd-kontakt-mehr { color: var(--bs-primary); background: var(--bs-primary-bg-subtle); }
  .kd-kontakte-klapp.kd-suche-aktiv .kd-kontakt-mehr { display: none; }
}

.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; }
.kd-meta { font-size: 0.82rem; color: #495057; }
.kd-meta a { color: #495057; text-decoration: none; }
.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; }
@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-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, seit 10/2026 auch
   Devices/EigeneGeraete.cshtml (#leihListe) — 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; }
@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; display: inline-flex; align-items: center; margin-top: 0.15rem; }

/* Zusammengefasster Stopp (Standortbesuch / Geräte-Verbund) in der Timeline — gleiche
   Darstellung wie die Gruppen-Zeile auf „Heute" (.mt-gruppe-*): ein Anzahl-Chip, der zugleich
   der Aufklapp-Schalter ist, und darunter die Geräte. Farben wie dort: Besuch = info (blau-
   türkis), Verbund = primary. Als <button>, der wie ein Badge aussieht (kein Button-Rahmen). */
.kd-tl-gchip {
  display: inline-flex; align-items: center;
  margin-left: .35rem; padding: .08rem .5rem;
  font-size: .72rem; font-weight: 600; line-height: 1.4;
  border: 1px solid transparent; border-radius: 50rem;
  cursor: pointer; vertical-align: middle;
}
.kd-tl-gchip-verbund {
  color: var(--bs-primary); background: var(--bs-primary-bg-subtle); border-color: var(--bs-primary-border-subtle);
}
.kd-tl-gchip-besuch {
  color: var(--bs-info-text-emphasis, var(--bs-info)); background: var(--bs-info-bg-subtle); border-color: var(--bs-info-border-subtle);
}
.kd-tl-gchip-chevron { font-size: .6rem; transition: transform .18s ease; display: inline-block; }
/* Bootstrap setzt aria-expanded am Toggle — der Pfeil dreht mit, damit der Zustand auch ohne
   Blick auf die Liste erkennbar ist. */
.kd-tl-gchip[aria-expanded="true"] .kd-tl-gchip-chevron { transform: rotate(180deg); }
.kd-tl-geraete {
  margin-top: .3rem; padding: .1rem .55rem;
  background: #f8fafc; border: 1px solid #eef2f7; border-radius: .5rem;
}
.kd-tl-geraet { display: flex; align-items: center; gap: .55rem; padding: .3rem 0; }
.kd-tl-geraet + .kd-tl-geraet { border-top: 1px solid #eef2f7; }
.kd-tl-geraet-avatar {
  flex-shrink: 0; width: 28px; height: 28px; border-radius: .35rem;
  color: #fff; font-size: .58rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.kd-tl-geraet-text { flex: 1 1 auto; min-width: 0; }
.kd-tl-geraet-name { font-size: .82rem; font-weight: 600; color: #0f172a; }
.kd-tl-geraet-name a { color: inherit; text-decoration: none; }
.kd-tl-geraet-sn { font-size: .72rem; color: #64748b; }

/* 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+Gerät,
   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; }

/* ── 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, Bestellungen, ...). */
.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; }
}

/* ============================================================================
   Sticky-Unternavigation (.kd-subnav) — seit 10/2026 von keiner Seite mehr genutzt
   (alle Akten haben Karteikarten-Reiter, .akte-karteireiter); die Link-Optik .kd-subnav-link nutzen weiter die Umschalter (.kd-tabbar, _TagesNav).
   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-/
   Lieferantenakte; die Geräteseite hat seit 10/2026 Reiter, siehe 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): bis 10/2026 schloss auch hier eine Sprungleiste an die Kopf-Karte an (Regel oben,
   dazu `.gsek-kopf + .kd-subnav { width: auto }`, weil die Leiste dort die volle Breite des Kopfs brauchte). Seit
   10/2026 hat die Geräteakte Karteikarten-Reiter (.akte-karteireiter, weiter unten), die am Inhaltskasten hängen. */

/* Geräteakte, Kopf: die anderen Geräte des Verbunds als Schnellsprung (11/2026) — in der Farbe des eigenen
   Verbund-Rahmens in „Geräte am Standort" (.kd-verbund-rahmen, erste Farbe der Palette), das Klassen-Kürzel
   als kleines Abzeichen darin. */
.kopf-verbund { display: inline-flex; flex-wrap: wrap; align-items: center; gap: .25rem .5rem; }
.kopf-verbund-wort { font-size: .75em; font-weight: 600; color: #0d6efd; }
.kopf-verbund-chip {
    display: inline-flex; align-items: center; gap: .35em;
    color: #0a58ca; background: color-mix(in srgb, #0d6efd 8%, #fff);
    border: 1px solid color-mix(in srgb, #0d6efd 35%, #fff);
}
.kopf-verbund-chip:hover { color: #084298; background: color-mix(in srgb, #0d6efd 15%, #fff); }
.kopf-verbund-chip .badge { font-size: .9em; padding: .15em .4em; }
.kopf-verbund-chip.ist-stillgelegt { opacity: .55; text-decoration: line-through !important; }

/* 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-12 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-12 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). */
/* Abschnitte paarweise nebeneinander (10/2026, Nutzer: „die Segmente nicht so groß, ggf. immer 2 nebeneinander" —
   seit der vollen Seitenbreite waren die Felder sehr lang gezogen). Ab xl zwei Spalten, darunter wie bisher
   untereinander. align-items:start — ein kurzer Abschnitt (Servicetechniker) wird nicht auf die Höhe seines
   Nachbarn gezogen. Die Karten tragen mb-4 (Bootstrap, !important) — im Raster macht der gap den Abstand.
   Genutzt von Geräteakte und „Neues Gerät"; die Kästen unter dem Geräte-Formular laufen über .gsek-inhalt-row mit
   col-xl-6 (dieselbe Breitenstufe). Ein Formular mit nur EINEM Kasten („Neuer Lieferant") nicht hier hinein — dann
   bliebe die rechte Hälfte leer; dort stehen die Teile IM Kasten nebeneinander (row/col-xl-6). */
.akte-paare { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; align-items: start; margin-bottom: 1.5rem; }
.akte-paare > .card { margin-bottom: 0 !important; }
@media (min-width: 1200px) {
    .akte-paare { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Zwei unabhängige Spalten (Geräteakte, 10/2026, Nutzer: „links Gerätedaten, Wartung, Anstehende Termine — rechts
   Ansprechpartner und der Rest"): jede Spalte stapelt ihre Kästen für sich (kein gemeinsames Zeilenraster, also keine
   Lücken neben einem langen Nachbarn). Unter xl stehen erst die linken, dann die rechten Kästen untereinander. Die
   Abstände macht der gap; die mb-4 der Karten (Bootstrap, !important) und des Verknüpft-Partials fallen hier weg. */
.akte-spalten { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.5rem; align-items: start; margin-bottom: 1.5rem; }
.akte-spalte { display: flex; flex-direction: column; gap: 1.5rem; min-width: 0; }
.akte-spalte > .card,
.akte-spalte > .akte-block > .card,
.akte-spalte > form > .card:last-of-type { margin-bottom: 0 !important; }   /* last-of-type: am Formular-Ende hängt das Antiforgery-Feld */
@media (min-width: 1200px) {
    .akte-spalten { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Reiter der Geräteakte als Karteikarten (10/2026, Nutzer-Vorgabe „Karteikarten, linksbündig"; akte-reiter.js).
   Die Leiste .akte-karteireiter steht unmittelbar über dem Kasten .akte-reiter-inhalt: der aktive Reiter ist weiß wie
   der Kasten und hat keine Unterkante — er geht nahtlos in ihn über; die übrigen liegen grau dahinter. Die Grundlinie
   ist ein INSET-Schatten an Leiste und Reitern statt eines Rahmens mit margin-bottom:-1px (Bootstraps nav-tabs): die
   Leiste wischt am Telefon seitlich (overflow-x), und ein Überlappen über den eigenen Rand schnitte der Überlauf ab.
   Die Leiste klebt unter der Navbar; ihre Fläche hat die Farbe der Seite (body.bg-light), damit beim Scrollen kein
   Inhalt zwischen den Reitern durchscheint. */
.akte-karteireiter {
    --akte-reiter-rand: #dee2e6;
    --akte-reiter-gap: .25rem;
    /* Was passiert, wenn nicht alle Reiter in die Zeile passen (akte-reiter.js liest den Wert — der Breakpoint steht
       nur hier): „wischen" = die Leiste wischt seitlich, mit Pfeilen am Rand; „mehr" (ab xl, unten) = die hinteren
       Reiter wandern ins Menü „Mehr ▾". */
    --akte-reiter-modus: wischen;
    position: sticky;
    top: var(--qrv-navbar-h, 58px);
    z-index: 1020;   /* wie die Sprungleisten: über Karten-Inhalten, unter Navbar (1030) und Dialogen */
    display: flex;
    align-items: flex-end;
    gap: var(--akte-reiter-gap);
    padding-top: .5rem;
    background: var(--bs-light, #f8f9fa);
    box-shadow: inset 0 -1px 0 var(--akte-reiter-rand);
    overflow-x: auto;
    white-space: nowrap;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.akte-karteireiter::-webkit-scrollbar { display: none; }
.akte-karteireiter-link {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    flex-shrink: 0;
    padding: .55rem 1rem;
    font-size: .9rem;
    font-weight: 500;
    color: #495057;
    background: #e9ecef;
    border: 1px solid var(--akte-reiter-rand);
    border-bottom: 0;
    border-radius: .6rem .6rem 0 0;
    box-shadow: inset 0 -1px 0 var(--akte-reiter-rand);
    text-decoration: none;
    transition: background .12s ease, color .12s ease;
}
.akte-karteireiter-link i { color: #6c757d; }
.akte-karteireiter-link:hover { background: #f1f3f5; color: #212529; }
.akte-karteireiter-link.active { background: #fff; color: #0a58ca; font-weight: 600; box-shadow: none; }
.akte-karteireiter-link.active i { color: #0a58ca; }
.akte-karteireiter-link .badge { font-weight: 500; font-size: .72rem; }
@media (max-width: 767.98px) {
    .akte-karteireiter-link { min-height: 44px; }   /* Tippziel am Telefon */
}
/* Die Kundenakte hat bis zu elf Reiter (mit „Stammdaten", Servicefällen, Bestellungen): bei 1280 px stand der letzte
   halb hinter dem Rand (gemessen). Unter 1600 px rücken die Reiter enger zusammen, damit die übliche Zahl einzeilig
   sichtbar bleibt; reicht es trotzdem nicht (schmale Fenster, Tablet, Telefon), wischt die Leiste und zeigt am Rand
   einen Schatten (unten, .akte-mehr-rechts/-links aus akte-reiter.js). */
@media (max-width: 1599.98px) {
    .akte-karteireiter { --akte-reiter-gap: .2rem; }
    .akte-karteireiter-link { padding-left: .6rem; padding-right: .6rem; font-size: .85rem; gap: .35rem; }
}
/* „Hier geht es weiter" zeigen seit 11/2026 die Pfeile am Rand (.akte-reiter-pfeil, unten). Der frühere Schatten an der
   Leiste ist weg — er schaute über dem Pfeil im oberen Innenabstand der Leiste als grauer Fleck hervor. Die Klassen
   .akte-mehr-rechts/-links setzt akte-reiter.js weiter (Zustand für Tests und die Pfeile). */
/* Am PC (ab xl) wischt man nicht: was nicht in die Zeile passt, steht im Menü „Mehr ▾" am Ende (11/2026, Nutzer-Wahl
   unter drei Entwürfen — wischen ließ Reiter hinter dem Rand verschwinden, eine zweite Zeile trennte den aktiven Reiter
   vom Kasten). Der aktive Reiter bleibt immer in der Zeile. akte-reiter.js räumt um. */
@media (min-width: 1200px) {
    .akte-karteireiter { --akte-reiter-modus: mehr; }
}
.akte-karteireiter-link.akte-reiter-im-mehr { display: none !important; }
.akte-reiter-mehr { display: flex; flex-shrink: 0; }
.akte-reiter-mehr > .akte-karteireiter-link { font-family: inherit; line-height: inherit; }
.akte-reiter-mehr-pfeil { font-size: .7rem; }
.akte-reiter-mehr .dropdown-menu { z-index: 1025; min-width: 13rem; }
.akte-reiter-mehr .dropdown-item i { color: #6c757d; }
.akte-reiter-mehr .dropdown-item .badge { margin-left: auto; font-weight: 500; font-size: .72rem; }
/* Wischt die Leiste (unter xl), zeigt ein kleiner Pfeil am Rand, dass es weitergeht — ein Klick schiebt sie weiter (11/2026,
   Nutzer-Wunsch; vorher nur ein Schatten, mit der Maus kam man an verdeckte Reiter kaum heran). Die Pfeile kleben am Rand
   der wischenden Leiste (sticky) und nehmen keinen Platz ein: der negative Rand hebt Breite und Abstand auf, sonst
   änderte ihr Ein-/Ausblenden die Breite der Leiste. Der Verlauf deckt den Reiter darunter ab. */
.akte-reiter-pfeil {
    position: sticky;
    z-index: 2;
    flex: 0 0 2rem;
    width: 2rem;
    align-self: stretch;
    display: flex;
    align-items: center;
    padding: 0 .2rem;
    border: 0;
    color: #495057;
    font-size: 1rem;
    box-shadow: inset 0 -1px 0 var(--akte-reiter-rand);   /* Grundlinie läuft unter dem Pfeil weiter */
    cursor: pointer;
}
.akte-reiter-pfeil:hover { color: #0a58ca; }
.akte-reiter-pfeil-links {
    left: 0;
    margin-right: calc(-2rem - var(--akte-reiter-gap));
    justify-content: flex-start;
    background: linear-gradient(to right, var(--bs-light, #f8f9fa) 55%, rgba(248, 249, 250, 0));
}
.akte-reiter-pfeil-rechts {
    right: 0;
    margin-left: calc(-2rem - var(--akte-reiter-gap));
    justify-content: flex-end;
    background: linear-gradient(to left, var(--bs-light, #f8f9fa) 55%, rgba(248, 249, 250, 0));
}
.akte-reiter-inhalt {
    background: #fff;
    border: 1px solid #dee2e6;
    border-top: 0;
    border-radius: 0 0 .75rem .75rem;
    padding: 1.25rem;
    margin-bottom: 1.5rem;
    /* Wechsel bei klebender Leiste: der Kasten beginnt direkt unter Navbar + Leiste. */
    scroll-margin-top: calc(var(--qrv-navbar-h, 58px) + 3.3rem);
}
@media (max-width: 575.98px) {
    .akte-reiter-inhalt { padding: .75rem; }
}
/* Reiter „Gerätedaten": links die Daten, rechts schmaler die Bilder (10/2026, Nutzer: „die Bilder rechts in der Spalte
   als Vorschau") — schon ab lg, weil die Bildspalte keine langen Felder trägt; darunter untereinander. Nebeneinander
   enden beide Kästen auf gleicher Höhe (Nutzer: „die Beschreibung größer, dass die Kacheln gleich enden"): die Spalten
   strecken sich auf die Zeilenhöhe, die Karten füllen ihre Spalte, und das Beschreibungsfeld nimmt den Rest der
   Gerätedaten-Karte ein. Untereinander bleibt es beim zweizeiligen Feld. */
@media (min-width: 992px) {
    .akte-spalten.akte-spalten-bild { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); align-items: stretch; }
    .akte-spalten-bild > .akte-spalte > .card { flex: 1 1 auto; }
    .akte-spalten-bild .akte-karte-fuellen { display: flex; flex-direction: column; }
    .akte-spalten-bild .akte-feld-fuellt { flex: 1 1 auto; display: flex; flex-direction: column; }
    .akte-spalten-bild .akte-feld-fuellt > textarea { flex: 1 1 auto; }
}
/* Vorschau und Platzhalter haben IMMER dieselbe feste Höhe (Nutzer: „das Bild immer in der Größe als Vorschau"):
   quer, hochkant oder winzig — das Bild wird in den Rahmen eingepasst, der Kasten springt beim Hochladen nicht. */
.geraet-bild-vorschau { display: block; }
.geraet-bild-vorschau img {
    display: block;
    width: 100%;
    height: 22rem;
    object-fit: contain;   /* ganzes Bild, auch hochkant — kein Beschnitt */
    background: #f8f9fa;
    border: 1px solid #dee2e6;
    border-radius: .5rem;
}
.geraet-bild-leer {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    height: 22rem;
    color: #6c757d;
    background: #f8f9fa;
    border: 1px dashed #ced4da;
    border-radius: .5rem;
}
.geraet-bild-leer i { font-size: 2rem; }
.geraet-bilder-raster { display: grid; grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr)); gap: .5rem; }
.geraet-bilder-raster img {
    display: block;
    width: 100%;
    aspect-ratio: 1;
    object-fit: cover;
    border: 1px solid #dee2e6;
    border-radius: .4rem;
}
/* Im Kasten machen die Abstände der Reiter selbst den Rand: kein mb-4 der letzten Karte, kein Rand der Spalten. */
.akte-reiter-pane > .card:last-child,
.akte-reiter-pane > .akte-spalten:last-child { margin-bottom: 0 !important; }
/* Kunden- und Lieferantenakte tragen seit 10/2026 dieselben Reiter (Nutzer-Wunsch „der Geräteansicht anpassen").
   Steht in einem zweispaltigen Reiter nur EINE Spalte (Ansprechpartner ohne Standorte-Karte), nimmt sie die ganze
   Breite — sonst bliebe ab xl die rechte Hälfte leer. */
.akte-spalten > .akte-spalte:only-child { grid-column: 1 / -1; }
/* Die Timeline ist dort ein eigener Reiter (Nutzer-Entscheidung, vorher die mitlaufende zweite Spalte): ihre Liste
   läuft mit der Seite wie die Historie der Geräteakte, statt im 520-px-Kasten zu scrollen. */
.akte-reiter-pane .kd-tl-scroll { max-height: none; }
.gsek-inhalt-row { row-gap: 1.5rem; }
.gsek-inhalt-row > .col-12 { 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);
}

/* ============================================================================
   Akten: Kopf-Karte (.kd-kopf) + einspaltiger Sektions-Stack (.kd-panels)
   Seit 10/2026 nutzt keine Akte mehr den Stapel: Kunden-, Lieferanten-, Personen-
   und Geräteakte zeigen ihre Karten in Reitern (.akte-karteireiter, akte-reiter.js).
   Ü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; }
/* Detailstreifen (Stammdaten-Felder) unter der Kopfzeile — Kunden- und Lieferantenakte zeigen ihre
   Stammdaten in der Kopf-Karte, die Stammdaten-Karte nur zum Bearbeiten. */
.kd-kopf-details { border-top: 1px solid #eef0f4; margin-top: .9rem; padding-top: .85rem; }
.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 in den Reitern (#gsekReiter 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: .15rem;
    padding: .15rem .2rem;
    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;
}
/* In der Höhe eines .btn-sm (≈31px statt ≈40px) — die Leiste steht immer im Seitenkopf
   neben den dort kleinen Knöpfen (Nutzer-Vorgabe 10/2026). */
.kd-tabbar .kd-subnav-link { padding: .16rem .6rem; font-size: .8rem; }

/* ── 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; }
/* Ausnahme von der Link-Regel oben: in EMPFANGENEN Mails bleiben Links blau unterstrichen — fremder
   Inhalt, ein Link muss dort als Link erkennbar sein (versehentliche Klicks, Phishing). Eigene
   Farben aus der Mail (inline style) schlagen das weiterhin. */
.mail-body-html a, .mail-body a { color: var(--bs-link-color); text-decoration: underline; }
.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; }

/* ============================================================================
   Akte mit Timeline daneben (.akte-mit-timeline, Kundenakte, 11/2026) — Nutzer-Wunsch:
   Dokumente ablegen, ohne erst den Reiter „Timeline" zu öffnen; im Entwurf gewählt:
   „die linke Seite bündig zum letzten Reiter". Ab xl zwei Spalten: links Reiterleiste
   + Kasten (.akte-links), genau so breit wie die Leiste, rechts die Timeline
   (.akte-timeline-spalte) mit dem Rest, mindestens 400 px. Reicht die Breite nicht für
   alle Reiter (gemessen mit 9 Reitern: unter ~1730 px Fenster), bleibt die Timeline bei
   400 px und die hinteren Reiter wandern ins Menü „Mehr" (akte-reiter.js). Die linke
   Spalte bleibt dabei so breit wie die VOLLE Leiste (--akte-links-breite, vom Skript
   gemessen) — sonst würde sie mit jedem Reiter im Menü schmaler und die Timeline
   breiter, bis wieder alles passt. Unter xl ist die Timeline wieder ein Reiter.
   (Bis 10/2026 stand hier die alte mitlaufende Spalte .kd-panels-split der Akten
   ohne Reiter; die Regeln für das Innere der Karte sind von dort übernommen.)
   ============================================================================ */

/* Reiter über der Timeline-Spalte: nur neben den Reitern (ab xl, unten) — darunter ist die Timeline selbst ein Reiter. */
.akte-timeline-reiter { display: none; }
/* Einspaltig (und als Grundwert): fester Scroll-Kasten. */
.kd-tl-scroll { max-height: 520px; overflow-y: auto; }
.kd-tl-suche { width: 210px; }

@media (min-width: 1200px) {
    .akte-mit-timeline {
        display: grid;
        /* Grid gibt erst der linken Spalte bis zu ihrer Grenze (die Leiste), den Rest bekommt 1fr — dessen 400 px
           stehen vorher fest, die linke Spalte schrumpft also zuerst. Untergrenze links 2/3 (Lieferantenakte, 11/2026):
           wenige Reiter (5) machten den Kasten sonst schmal und die Timeline über 1000 px breit. Liegt die Grenze über
           der Leiste, gilt sie (minmax mit max < min = min); die Kundenakte mit 9 Reitern bleibt bündig zum letzten. */
        grid-template-columns: minmax(min(66%, calc(100% - 400px - 1.5rem)), var(--akte-links-breite, max-content)) minmax(400px, 1fr);
        column-gap: 1.5rem;
        align-items: start;
    }
    /* stretch: die linke Spalte reicht über die ganze Höhe — sonst klebte die Reiterleiste nur so lange, wie der
       Kasten hoch ist. */
    .akte-mit-timeline > .akte-links { min-width: 0; align-self: stretch; }
    /* Der Inhalt zählt NICHT für die Breite der linken Spalte: lange Zeilen und Tabellen setzten sie sonst fest. */
    .akte-mit-timeline .akte-reiter-inhalt { contain: inline-size; }
    .akte-mit-timeline .akte-reiter-daneben { display: none !important; }
    /* Steht immer da — auch vor dem Skript und ohne es (hidden kommt vom Server, weil sie darunter ein Reiter ist). */
    .akte-mit-timeline > .akte-timeline-spalte[hidden] { display: block !important; }
    .akte-mit-timeline > .akte-timeline-spalte {
        position: sticky;
        /* = Oberkante der Reiterleiste; der Reiter der Spalte hat wie die Leiste links .5rem Innenabstand oben — so
           stehen beide Reiter und beide Kästen auf gleicher Höhe, geklebt wie ungeklebt. */
        top: var(--qrv-navbar-h, 58px);
        min-width: 0;
        margin-bottom: 1.5rem;   /* wie der Kasten links: beide enden am Seitenende auf gleicher Höhe */
    }
    /* Reiter über der Spalte (11/2026, Nutzer-Wunsch): sieht aus wie die Leiste links mit einem aktiven Reiter, der
       nahtlos in den Kasten übergeht — der Kasten hat oben keinen Rand und keine Rundung (wie .akte-reiter-inhalt),
       die Grundlinie zeichnet die Leiste. Nicht klebend (die Spalte klebt schon), nichts zum Anklicken. */
    .akte-mit-timeline .akte-timeline-reiter {
        display: flex;
        position: static;
        overflow: visible;
        background: transparent;
    }
    .akte-mit-timeline .akte-timeline-reiter .akte-karteireiter-link { cursor: default; }
    .akte-timeline-spalte .kd-tl-card {
        border-top: 0;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        box-shadow: none !important;   /* .shadow-sm ist !important; links hat der Kasten auch keinen Schatten */
    }
    .akte-timeline-spalte .kd-tl-card > .card-header { border-radius: 0; background: #fff; }
    /* Der Titel steht im Reiter — in der Karte nur noch für Vorlesegeräte (wie .visually-hidden). */
    .akte-mit-timeline .akte-tl-titel {
        position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important;
        margin: -1px !important; overflow: hidden !important; clip: rect(0, 0, 0, 0) !important; white-space: nowrap !important; border: 0 !important;
    }
    .akte-mit-timeline .akte-tl-nur-titel { display: none !important; }
    .akte-timeline-spalte .kd-tl-card {
        /* Bis zur Fußzeile und über dem Hinweis-Stapel unten rechts (--qrv-stapel-h, site.js) — sonst läge
           „Neuer Eingang" über den letzten Einträgen. Abzug: oben .5rem Versatz, unten der Rand von 1,5rem. */
        max-height: calc(100vh - var(--qrv-navbar-h, 58px) - var(--qrv-footer-h, 26px) - var(--qrv-stapel-h, 0px) - 2rem);
        display: flex;
        flex-direction: column;
        min-height: 0;
    }
    /* Kopfzeile und Filter-Leiste behalten ihre Höhe … */
    .akte-timeline-spalte .kd-tl-card > * { flex: 0 0 auto; }
    /* Kopfzeile in der schmalen Spalte auf drei Zeilen: Titel, dann die Ablagefläche über die volle Breite, dann
       Suche + Filter. Nebeneinander bräche die Ablagefläche vierzeilig um. */
    .akte-timeline-spalte .kd-tl-card > .card-header { flex-wrap: wrap; row-gap: .5rem; }
    .akte-timeline-spalte .kd-tl-card > .card-header > .d-flex { width: 100%; }
    .akte-timeline-spalte .kd-tl-card .kd-dropzone { flex: 1 1 100%; }
    /* Der Hinweistext kürzt sich, statt die Fläche zu sprengen — schrumpfen ja, wachsen nein, sonst stünden Symbol
       und Text am linken Rand einer sonst leeren Fläche statt mittig. */
    .akte-timeline-spalte .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. */
    .akte-timeline-spalte .kd-tl-card .kd-tl-suche { width: auto; flex: 1 1 140px; }
    /* … der Body darunter nimmt den Rest und gibt ihn nach innen weiter. */
    .akte-timeline-spalte .kd-tl-card > .kd-tl-body {
        flex: 1 1 auto;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }
    /* Tausch-Ziel des Inline-Uploads (inline-upload.js) — die Klasse kommt mit der frisch gerenderten Fassung zurück. */
    .akte-timeline-spalte .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. */
    .akte-timeline-spalte .kd-tl-scroll {
        flex: 1 1 auto;
        min-height: 0;
        max-height: none;
    }
    /* Upload-Zeilen (eine je gewählter Datei) stapeln: die Bootstrap-Spalten im Zeilen-Template rechnen gegen die
       Fensterbreite (col-sm-4), nicht gegen die Spalte — je Feld blieben gut 100 px, das Typ-Feld wäre unlesbar. */
    .akte-timeline-spalte .kd-tl-card .kd-up-row > .row > [class*="col-"] {
        flex: 0 0 auto;
        width: 100%;
    }
    /* Lieferantenakte: ihre Upload-Zeile (Datei · Bezeichnung · Kategorie · Knopf, col-md-* gegen die Fensterbreite)
       stünde in der Spalte vierspaltig gequetscht — Datei und Knopf über die volle Breite, die zwei Felder daneben. */
    .akte-timeline-spalte #lfUploadRow > .col-md-4,
    .akte-timeline-spalte #lfUploadRow > .col-md-2 { flex: 0 0 auto; width: 100%; }
    .akte-timeline-spalte #lfUploadRow > .col-md-3 { flex: 0 0 auto; width: 50%; }
}
/* Unter xl ist die Timeline ein Reiter: sie steht dann unter der Leiste und sieht aus wie der Kasten. */
@media (max-width: 1199.98px) {
    /* Leiste, Kasten und Timeline in EINEM Block — sonst klebte die Leiste nicht, solange die Timeline offen ist
       (eine klebende Leiste bleibt in ihrem Elternelement). */
    .akte-mit-timeline > .akte-links { display: contents; }
    .akte-mit-timeline > .akte-timeline-spalte {
        background: #fff;
        border: 1px solid #dee2e6;
        border-top: 0;
        border-radius: 0 0 .75rem .75rem;
        padding: 1.25rem;
        margin-bottom: 1.5rem;
        scroll-margin-top: calc(var(--qrv-navbar-h, 58px) + 3.3rem);
    }
}
@media (max-width: 575.98px) {
    .akte-mit-timeline > .akte-timeline-spalte { padding: .75rem; }
}
/* Steht der offene Reiter außerhalb des Kastens (die Timeline unter xl), fällt der leere Kasten zusammen — er bleibt
   aber im Dokument, weil die Dialoge der Akte darin stehen (ein display:none ließe sie nicht mehr aufgehen). Klasse
   von akte-reiter.js. */
.akte-reiter-inhalt.akte-reiter-inhalt-leer { padding: 0; border: 0; margin: 0; }

/* ============================================================================
   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;
}
/* Ebenso die Akte mit Timeline daneben (11/2026), solange sie ein Raster ist (ab xl): Kasten und Timeline-Spalte
   tragen ihren Rand von 1,5rem INNERHALB des Rasters, er fällt nicht mit diesem ::after zusammen — die Seite war
   dadurch 24 px länger als das Fenster und scrollte, obwohl die Spalte genau bis an den Rand reicht. Darunter ist sie
   ein gewöhnlicher Block, dort fallen die Ränder zusammen. */
@media (min-width: 1200px) {
    main:has(.akte-mit-timeline)::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;
    }
    /* … und der Text rückt mit: Bootstraps Einzug (1.5em) ist für ein 1em-Kästchen bemessen,
       neben dem 20px-Kästchen klebte die Beschriftung daran (gemessen in den Einstellungen,
       10/2026). Schalter (form-switch) haben ihren eigenen, breiteren Einzug. */
    .form-check:not(.form-switch):not(.form-check-reverse) {
        padding-left: 28px;
    }
    .form-check:not(.form-switch):not(.form-check-reverse) .form-check-input {
        margin-left: -28px;
    }
}

/* ── 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;
}

/* ───── Offline-Ausgang (wwwroot/js/offline-ausgang.js) ─────
   Leiste über der Fußzeile: wartende/abgelehnte Offline-Einträge und kurze Meldungen.
   z-index 1022 = über der Fußzeile (1021), unter Dropdowns (1025), Navbar (1030) und Dialogen
   (1050+). Die Hülle fängt keine Klicks ab, nur die Meldungen darin. */
.qrv-ausgang {
    position: fixed;
    left: 0;
    right: 0;
    bottom: calc(var(--qrv-footer-h, 26px) + 8px);
    z-index: 1022;
    padding: 0 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    pointer-events: none;
}
.qrv-ausgang[hidden] { display: none; }
.qrv-ausgang .alert {
    pointer-events: auto;
    width: 100%;
    max-width: 38rem;
}

/* ── Dialoge: Standardbreite und Größe ziehen (10/2026, Nutzer-Wunsch „größer und in der Größe
   veränderbar") ─────────────────────────────────────────────────────────────────────────────
   Normale Dialoge sind am Computer deutlich breiter als Bootstraps 500 px; .modal-sm/-lg/-xl setzen
   die Variable am .modal-dialog selbst und bleiben dadurch, wie sie sind. Die kurze Rückfrage
   („Wirklich …?", #siteConfirmModal) behält 500 px — ein Zweizeiler in 800 px wirkt verloren.
   Der Ziehgriff kommt aus modal-groesse.js (nur Maus, ab md). */
@media (min-width: 768px) { .modal { --bs-modal-width: 640px; } }
@media (min-width: 992px) { .modal { --bs-modal-width: 800px; } }
.modal-dialog.modal-rueckfrage { --bs-modal-width: 500px; }

.modal-groesse-griff {
    position: absolute;
    right: 1px;
    bottom: 1px;
    width: 14px;
    height: 14px;
    z-index: 2;
    cursor: nwse-resize;
    touch-action: none;
    opacity: .45;
    color: var(--bs-secondary-color, #6c757d);
    background: linear-gradient(135deg,
        transparent 0 50%, currentColor 50% 57%, transparent 57% 68%,
        currentColor 68% 75%, transparent 75% 86%, currentColor 86% 93%, transparent 93%);
    border-bottom-right-radius: var(--bs-modal-inner-border-radius, .5rem);
}
.modal-groesse-griff:hover { opacity: .9; }
body.modal-groesse-zieht,
body.modal-groesse-zieht * { cursor: nwse-resize !important; user-select: none !important; }
@media (max-width: 767.98px), (pointer: coarse) {
    .modal-groesse-griff { display: none; }
}

/* Übergabeschein (Pages/Devices/Uebergabeschein): Zeichenflächen für Empfänger und Firma — gestrichelt mit
   „Hier unterschreiben", nach dem ersten Strich neutral (.hat-tinte setzt uebergabeschein.js). */
.leihschein-zone {
    position: relative;
    border: 2px dashed var(--bs-primary, #0d6efd);
    background: rgba(var(--bs-primary-rgb, 13,110,253), .05);
}
.leihschein-zone canvas { display: block; width: 100%; height: 200px; touch-action: none; }
/* Am Bildschirm sind die Felder breit (volle Seitenbreite) — dann auch höher, damit es sich gut unterschreibt. */
@media (min-width: 992px) { .leihschein-zone canvas { height: 260px; } }
.leihschein-zone.hat-tinte { border-style: solid; border-color: var(--bs-border-color, #dee2e6); background: #fff; }
.leihschein-wasserzeichen {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    pointer-events: none; user-select: none;
    color: var(--bs-primary, #0d6efd); opacity: .45; font-weight: 600;
}
.leihschein-zone.hat-tinte .leihschein-wasserzeichen { display: none; }
.leihschein-loeschen { position: absolute; top: 6px; right: 6px; }

/* Übersicht „Eigene Geräte“ (Pages/Devices/EigeneGeraete): Belegung der nächsten 6 Wochen als Leiste, ein Feld je Tag.
   Montags ein dunklerer linker Rand als Wochen-Markierung. Farben wie die Abzeichen der Seite. */
.leih-leiste { display: grid; grid-template-columns: repeat(42, minmax(0, 1fr)); gap: 1px; }
.leih-tag { height: 10px; min-width: 4px; border-radius: 1px; background: var(--bs-secondary-bg, #e9ecef); }
span.leih-tag.d-inline-block { width: 14px; vertical-align: -1px; }
.leih-tag-montag { box-shadow: inset 2px 0 0 rgba(0, 0, 0, .35); }
.leih-tag-aus { background: #ffc107; }
.leih-tag-offen { background: repeating-linear-gradient(135deg, #ffc107 0 3px, #fff3cd 3px 6px); }
.leih-tag-res { background: #0dcaf0; }
.leih-tag-konflikt { background: #dc3545; }
.leih-tag-wagen { background: #c5b3ee; }
/* Eigene Geräte („gehört uns", 10/2026): EIN Zeichen überall — Haus-Symbol in Violett, der Ton des Menüpunkts
   „Betrieb". Im Wagen ist ein Gerät bei uns, nicht „ausgegeben" (GeraeteAusgabe.ImWagen). */
.eigen-ton { color: #6f42c1; }
.badge-imwagen { background: #efe9fb; color: #59359a; }

/* Spur „Eigene Geräte & Fahrzeuge" in der Planung (Pages/Index.cshtml, eigene-spur.js, 10/2026): Kopfzeile über den
   eigenen Zeilen, zugeklappt per Klasse an #aufgabenListe. !important, weil .aufg-row d-flex trägt. */
.aufg-eigene-kopf { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; width: 100%; text-align: left;
    font: inherit; color: inherit; padding: .55rem .75rem; border: 0; border-bottom: 1px solid var(--bs-border-color);
    border-left: 4px solid #6f42c1; background: #f7f4fd; }
.aufg-eigene-kopf.aufg-eigene-ueb { border-left-color: var(--bs-danger); background: #fff5f6; }
.aufg-eigene-chev { transition: transform .15s; }
.aufg-eigene-kopf[aria-expanded="true"] .aufg-eigene-chev { transform: rotate(90deg); }
#aufgabenListe.aufg-eigene-zu .aufg-row[data-eigen="1"] { display: none !important; }
.aufg-eigene-nur { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; padding: .3rem .75rem;
    font-size: .875rem; background: #e7f1ff; border-bottom: 1px solid var(--bs-border-color); }
.aufg-eigene-nur[hidden] { display: none !important; }
#aufgabenListe.aufg-nur-wagen .aufg-row:not(.aufg-wagen-treffer),
#aufgabenListe.aufg-nur-wagen .aufg-kunden-kopf,
#aufgabenListe.aufg-nur-wagen #aufgUeberfaelligHinweis { display: none !important; }
.aufg-kunden-kopf { padding: .45rem .75rem; font-size: .875rem; color: var(--bs-secondary-color);
    border-bottom: 1px solid var(--bs-border-color); background: var(--bs-tertiary-bg, #f8f9fa); }
.aufg-ort-chip { display: inline-flex; align-items: center; gap: .25rem; margin-right: .85rem; padding: 0 .55rem;
    font: inherit; line-height: 1.6; color: inherit; background: #fff; border: 1px solid var(--bs-border-color); border-radius: 1rem; }
.aufg-ort-chip:hover, .aufg-ort-chip:focus-visible { border-color: #6f42c1; }
@media (max-width: 767.98px) { .aufg-ort-chip { padding: .3rem .65rem; } }

/* „Wo ist was" (_WoIstWas.cshtml): kompakt gestapelt (rechte Seite der Planung), voll als Raster (Seite „Eigene Geräte
   & Fahrzeuge"). Hervorhebung beim Darüberfahren links: .wo-markiert / .wo-karte-markiert (eigene-spur.js). */
.wo-ist-was { display: flex; flex-direction: column; gap: .75rem; padding: .75rem; }
.wo-ist-was.wo-voll { display: grid; grid-template-columns: repeat(auto-fill, minmax(26rem, 1fr)); align-items: start; gap: 1rem; padding: 0; }
@media (max-width: 575.98px) { .wo-ist-was.wo-voll { grid-template-columns: minmax(0, 1fr); } }
.wo-karte { background: #fff; border: 1px solid var(--bs-border-color); border-radius: .5rem; overflow: hidden;
    transition: box-shadow .12s, border-color .12s; }
.wo-karte-markiert { border-color: #0d6efd; box-shadow: 0 0 0 3px rgba(13, 110, 253, .18); }
.wo-kopf { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; padding: .55rem .75rem;
    border-bottom: 1px solid var(--bs-border-color-translucent); }
.wo-kopf-gruppe { background: var(--bs-tertiary-bg, #f8f9fa); }
.wo-zeile { display: flex; align-items: center; gap: .5rem; padding: .4rem .75rem; border-bottom: 1px solid #f1f3f5; }
.wo-zeile:last-child { border-bottom: 0; }
.wo-markiert { background: rgba(13, 110, 253, .10); box-shadow: inset 3px 0 0 #0d6efd; }
.wo-fristen { flex-shrink: 0; }
.wo-zeile-fristen { margin-top: .2rem; }
.wo-rest { padding: .35rem .75rem; font-size: .8rem; color: var(--bs-secondary-color); }
.wo-gruppe-name { padding: .45rem .75rem 0; font-weight: 600; font-size: .9rem; }
.wo-fuss { padding: .4rem .75rem; border-top: 1px solid var(--bs-border-color-translucent); }
/* Kasten „Fahrzeuge": je Wagen ein Abschnitt, getrennt durch eine Linie; hervorgehoben (eigene-spur.js) innen, weil der
   Kasten überstehende Schatten abschneidet (overflow: hidden). */
.wo-wagen + .wo-wagen { border-top: 1px solid var(--bs-border-color); }
.wo-wagen-kopf { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; padding: .5rem .75rem .15rem; }
.wo-wagen-fuss { padding: .3rem .75rem .55rem; }
.wo-wagen.wo-karte-markiert { box-shadow: inset 0 0 0 2px rgba(13, 110, 253, .55); background: rgba(13, 110, 253, .04); }
/* Umräumen auf „Nach Ort" (wo-umraeumen.js): ziehbare Zeilen, beim Ziehen gestrichelte Ziele, das Ziel unter dem Zeiger
   violett hinterlegt. Leere data-Attribute (Razor bei null) zählen nicht. */
.wo-zeile[data-wo-ziehbar="1"] { cursor: grab; }
.wo-gezogen { opacity: .45; }
.wo-zieht [data-wo-ziel]:not([data-wo-ziel=""]) { outline: 2px dashed rgba(111, 66, 193, .45); outline-offset: -3px; }
.wo-ziel-ueber { background-color: rgba(111, 66, 193, .08); outline-color: #6f42c1 !important; }
.wo-verschieben-knopf, .wo-ausgeben-knopf { padding: .1rem .4rem; }
.wo-ausgeben-knopf { white-space: nowrap; }
@media (max-width: 767.98px) { .wo-verschieben-knopf, .wo-ausgeben-knopf { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; } }
/* Wartungs-Fälligkeit als Füllstand (.faellig-row, Nutzer 10/2026: „der grüne Füllstand wie in der Planung") — in der
   eckigen Liste ohne abgerundetes Füllungsende, Deckkraft je Stufe wie in der Geräteliste (Devices/Index). */
#leihListe .faellig-row::before { border-radius: 0; }
#leihListe .gd-due-rot          { --gd-deckung: .42; }
#leihListe .gd-due-orange       { --gd-deckung: .43; }
#leihListe .gd-due-gelb         { --gd-deckung: .48; }
#leihListe .gd-due-gruen        { --gd-deckung: .36; }
#leihListe .gd-due-ueberfaellig { --gd-deckung: .50; }

/* Fristen eines Geräts (Firmenfahrzeuge, _GeraeteFristen): der Farbton kommt aus der gd-due-*-Klasse (--gd-ton,
   oben beim Fälligkeits-Balken) — keine zweite Farbtabelle. Leise getönt, Überfälliges und die nächsten 14 Tage
   kräftig, damit sie in einer langen Liste auffallen. */
.frist-badge {
    display: inline-flex; align-items: center; font-size: .78rem; line-height: 1.2; padding: .2rem .45rem;
    border-radius: .375rem; white-space: nowrap; color: var(--bs-body-color);
    background: rgb(var(--gd-ton, 108 117 125) / .14); border: 1px solid rgb(var(--gd-ton, 108 117 125) / .5);
}
.frist-badge.gd-due-rot, .frist-badge.gd-due-ueberfaellig { background: rgb(var(--gd-ton)); color: #fff; }

/* Mehrfachauswahl der Geräteliste (geraete-mehrfachauswahl.js): das Label ist die Tippfläche — 44 px hoch und oben
   bündig wie der Klassen-Avatar daneben (.ap-avatar, 44 px), sonst rutschte das Häkchen in hohen Telefon-Zeilen in die
   Mitte; über dem Hover-Schleier der Fälligkeitszeile (z-index 0). */
.geraet-auswahl-ziel {
    display: flex; align-items: center; justify-content: center; align-self: flex-start;
    min-width: 1.75rem; height: 44px; margin: 0 -.5rem 0 -.25rem; padding: 0 .25rem;
    cursor: pointer; position: relative; z-index: 1;
}
.geraet-auswahl-ziel .form-check-input { width: 1.15rem; height: 1.15rem; cursor: pointer; }

/* Kundenakte, Reiter „Geräte": Überschrift je Standort (11/2026, Standort am Gerät) — nur bei mehr als einem Standort
   mit Geräten. Das Kästchen wählt alle (sichtbaren) Geräte des Standorts. Am Telefon steht die Anschrift unter dem
   Namen (die Zahl bleibt in der ersten Zeile). */
.kd-standort-kopf { display: flex; align-items: center; gap: .6rem; margin: 1.1rem 0 .4rem; font-size: .9rem; }
.kd-standort-kopf:first-child { margin-top: 0; }
.kd-standort-kopf .kd-standort-name { font-weight: 600; color: var(--bs-body-color); }
.kd-standort-kopf .kd-standort-adresse { color: var(--bs-secondary-color); }
.kd-standort-kopf .kd-standort-linie { flex: 1; border-top: 1px solid var(--bs-border-color); min-width: 1rem; }
@media (max-width: 575.98px) {
    .kd-standort-kopf { flex-wrap: wrap; row-gap: 0; }
    .kd-standort-kopf .kd-standort-adresse { order: 9; flex-basis: 100%; padding-left: 3.4rem; font-size: .85rem; }
    .kd-standort-kopf .kd-standort-linie { display: none; }
}
/* Dialog „Standort ändern": das gewählte Ziel getönt. */
#standortAendernModal .st-ziel { cursor: pointer; }
#standortAendernModal .st-ziel.active-ziel { background: var(--bs-primary-bg-subtle); border-color: var(--bs-primary-border-subtle); }
