/* Design tokens */
:root {
    --color-bg: #F8FAFC;
    --color-surface: #FFFFFF;
    --color-border: #E5E8EC;
    --color-text: #1B2B3A;
    --color-text-muted: #6B7785;
    /* UI-Konsistenz-Refactoring (Nutzer-Feedback 2026-08-13): Vaults bespoke Sidebar (_sidebar.twig)
       faerbte inaktive Nav-/Listeneintraege bisher mit --color-text-muted, waehrend die geteilte
       Kombi-Shell-Facetten-Sidebar (.asset-list-filter-item, Assets/Tickets/Kunden/...) mangels
       eigener color-Regel implizit --color-text erbte - sichtbar kontrastreicher. Eigenes Token
       statt direkt --color-text, damit alle Sidebar-Nav-Texte app-weit denselben, absichtlich
       benannten Bezug nutzen (leichter pruef-/aenderbar als ein impliziter Vererbungs-Zufall). */
    --sidebar-text-color: var(--color-text);
    --color-accent: #0559C9;
    --color-accent-hover: #04449E;
    --color-accent-soft: rgba(5, 89, 201, 0.08);
    --color-accent-glow: rgba(5, 89, 201, 0.35);
    /* Sehr dezente Einfaerbung fuer Sidebar-Hintergrund, Tabellen-Zebrastreifen und den
       Body-Hintergrund-Verlauf (siehe .sidebar/.data-table/body weiter unten) - bewusst ein
       eigenes, viel schwaecheres Token als --color-accent-soft (das ist fuer Badges/aktive
       Zustaende gedacht und waere hier flaechig viel zu kraeftig). */
    --color-accent-tint: rgba(5, 89, 201, 0.05);
    /* Eigenes Token nur fuer die Sidebar (siehe .sidebar weiter unten) - bewusst getrennt von
       --color-accent-tint (Tabellen-Zebrastreifen/Body-Verlauf/Avatar-Dropzone-Hover): ohne eigene
       persoenliche Akzentfarbe (Standardzustand nach "Zurücksetzen"/Systemstandard) soll die
       Sidebar rein #FFFFFF/--color-surface bleiben, waehrend die Zebrastreifen weiterhin die
       dezente Standard-Einfaerbung zeigen. Wird nur bei einer gespeicherten Akzentfarbe auf einen
       echten Tint-Wert gesetzt (siehe AccentColor::cssVariables()). */
    --sidebar-tint: transparent;
    --status-active: #15A35E;
    --status-active-soft: rgba(21, 163, 94, 0.1);
    --status-warning: #E8A33D;
    --status-warning-soft: rgba(232, 163, 61, 0.12);
    --status-neutral: #8A96A3;
    --status-neutral-soft: rgba(138, 150, 163, 0.14);
    --status-new: #d2c8fa;
    --status-new-text: #4A3B91;
    --status-new-soft: rgba(210, 200, 250, 0.22);
    --color-danger-bg: #FFF5F5;
    --color-danger-bg-strong: #FDEDED;
    --color-danger-border: #F5C2C2;
    --color-danger-text: #9B2C2C;
    --status-hold: #6B5B95;
    --status-hold-soft: rgba(107, 91, 149, 0.12);
    --status-callback: #0C8599;
    --status-callback-soft: rgba(12, 133, 153, 0.12);
    --status-scheduled: #4F5FAD;
    --status-scheduled-soft: rgba(79, 95, 173, 0.12);
    --status-absent: #C4463D;
    --status-absent-soft: rgba(196, 70, 61, 0.12);
    /* Erweitertes Farbschema fuer Eintragstyp-Header (Ticket-Verlauf, siehe ticket-new-design.css)
       und Zeitrahl-Bloecke (Dispatch Tages-/Wochenansicht weiter unten) - EIN gemeinsamer Satz
       Tokens fuer beide Ansichten, damit derselbe Eintragstyp app-weit dieselbe Farbe traegt.
       Farbwerte 1:1 aus dem Tailwind-Farbschema (z.B. indigo-50/500/700), *-tint sind die helle
       Kartenkopf-Flaeche, die Basisfarbe ist Text/Icon (Ticket-Verlauf) bzw. die solide
       Balkenfarbe (Zeitrahl). */
    --entrytype-push: #6366F1;
    --entrytype-push-text: #4338CA;
    --entrytype-push-tint: #EEF2FF;
    --entrytype-appointment: #6B21A8;
    --entrytype-appointment-tint: #F3E8FF;
    --entrytype-callback: #B45309;
    --entrytype-callback-tint: #FEF3C7;
    --entrytype-email: #334155;
    --entrytype-email-tint: #F1F5F9;
    --entrytype-service: #1D4ED8;
    --entrytype-service-tint: #EFF6FF;
    --calendar-weekend-bg: rgba(138, 150, 163, 0.10);
    --calendar-holiday-bg: rgba(59, 130, 246, 0.12);
    --calendar-offhours-bg: rgba(0, 0, 0, 0.06);
    --color-critical: #7A1220;
    --color-critical-soft: rgba(122, 18, 32, 0.12);
    --radius-sm: 6px;
    --radius-md: 10px;
    --radius-lg: 14px;
    --shadow-card: 0 1px 2px rgba(16, 24, 40, 0.04), 0 1px 6px rgba(16, 24, 40, 0.06);
    --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    /* Nur fuer .mono (technische Daten: IP/MAC/Seriennummern/OTP-Codes/Ticket-IDs) - eigenes Token
       statt der ~28 bereits bestehenden Ad-hoc-"monospace"-Deklarationen quer durch diese Datei,
       die unangetastet bleiben. Gilt in allen Themes, sichtbar wird der Unterschied nur dort, wo
       .mono tatsaechlich angewendet wird. */
    --font-mono: "JetBrains Mono", "Fira Code", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    /* Nur fuer .code-block (siehe CoreDesk Academy) - absichtlich fest dunkel in beiden Themes
       (wie ein Code-Editor), nicht an --color-bg/-surface gekoppelt. */
    --code-bg: #0F172A;
    --code-text: #E2E8F0;
    --code-comment: #7B8A9E;
    --code-keyword: #7DB4FF;
    --code-string: #9CDB8F;
    --code-number: #F0B979;
}

/* ============================================================================
   BREAKPOINT-VERTRAG - gilt ab sofort fuer JEDE neue UI-Aenderung an CoreDesk,
   nicht nur fuer die mobile Hub-/Leistung-schreiben-Optimierung, die diesen
   Vertrag eingefuehrt hat:
       Phone:   max-width: 599px
       Tablet:  min-width: 600px UND max-width: 1023px
       Desktop: min-width: 1024px
   CSS-Custom-Properties koennen keine @media-Feature-Werte liefern (kein
   Praeprozessor in diesem Projekt) - dies ist daher eine numerische Konvention,
   keine technisch erzwungene. Die ca. 30 bereits bestehenden Ad-hoc-@media-Regeln
   in dieser Datei (420/480/640/768/900/1024px usw.) werden NICHT rueckwirkend auf
   diese drei Zahlen migriert - nur neuer Code folgt ab jetzt diesem Vertrag.
   ============================================================================ */

/* Lokal gehostet (kein Google-Fonts-CDN zur Laufzeit) - vermeidet die IP-Uebertragung an Google,
   die in Deutschland als DSGVO-relevant gilt, wenn Schriften direkt von fonts.gstatic.com geladen werden. */
@font-face {
    font-family: "Inter";
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url("/fonts/Inter.woff2") format("woff2");
}

/* UniFi-inspiriertes dunkles Theme: dunkles Anthrazit/Navy, kein reines Schwarz */
[data-theme="dark"] {
    --color-bg: #11151C;
    --color-surface: #1A2029;
    --color-border: #2B3340;
    --color-text: #E7EAEE;
    --color-text-muted: #8E99A8;
    --sidebar-text-color: var(--color-text);
    --color-accent: #4C8DFF;
    --color-accent-hover: #6BA1FF;
    --color-accent-soft: rgba(76, 141, 255, 0.16);
    --color-accent-glow: rgba(76, 141, 255, 0.45);
    --color-accent-tint: rgba(76, 141, 255, 0.07);
    --sidebar-tint: transparent;
    --status-active: #34D399;
    --status-active-soft: rgba(52, 211, 153, 0.14);
    --status-warning: #FBBF24;
    --status-warning-soft: rgba(251, 191, 36, 0.14);
    --status-neutral: #9CA7B4;
    --status-neutral-soft: rgba(156, 167, 180, 0.16);
    --status-new: #b8a8f5;
    --status-new-text: #E7E2FB;
    --status-new-soft: rgba(184, 168, 245, 0.22);
    --color-danger-bg: #271A1B;
    --color-danger-bg-strong: #32201F;
    --color-danger-border: #5C2E2E;
    --color-danger-text: #F2A8A8;
    --status-hold: #A78BDB;
    --status-hold-soft: rgba(167, 139, 219, 0.16);
    --status-callback: #22D3EE;
    --status-callback-soft: rgba(34, 211, 238, 0.16);
    --status-scheduled: #9DA8F0;
    --status-scheduled-soft: rgba(157, 168, 240, 0.16);
    --status-absent: #E5645A;
    --status-absent-soft: rgba(229, 100, 90, 0.16);
    /* Nur die *-tint-Flaechen werden im Dark Mode gedunkelt (gleiche Konvention wie --tdv2-*-tint
       in ticket-new-design.css) - Text-/Icon-/Balkenfarben bleiben bewusst unveraendert. */
    --entrytype-push-tint: #1E1B4B;
    --entrytype-appointment-tint: #3B0764;
    --entrytype-callback-tint: #451A03;
    --entrytype-email-tint: #0F172A;
    --entrytype-service-tint: #172554;
    --calendar-weekend-bg: rgba(156, 167, 180, 0.12);
    --calendar-holiday-bg: rgba(96, 165, 250, 0.16);
    --calendar-offhours-bg: rgba(255, 255, 255, 0.05);
    --color-critical: #FF6B6B;
    --color-critical-soft: rgba(255, 107, 107, 0.18);
    --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 6px rgba(0, 0, 0, 0.25);
}

/* UniFi Portal Clone (Ubiquiti-Style) - dritter, ueber einen globalen Admin-Schalter aktivierbarer
   Theme-Wert (siehe SettingsRepository::globalTheme(), public/index.php Theme-Aufloesung). Belegt
   dieselben Variablennamen wie [data-theme="dark"], damit die gesamte App (fast alles konsumiert
   bereits var(--color-*)) automatisch die neue Palette uebernimmt, ohne dass jedes Template
   einzeln angefasst werden muss. Tokens, die der Nutzer nicht explizit vorgegeben hat (Danger/
   Critical/Hold/Callback/Entrytype-Tints/Kalender-Hintergruende), werden bewusst 1:1 vom
   bestehenden Dark-Theme uebernommen statt neu erfunden - sie sind Teil der Sekundaerpalette,
   nicht der Ubiquiti-Markenfarben, und sollen zur bestehenden Dark-Stimmung passen.
   --shadow-card wird hier direkt auf "none" gesetzt statt
   ueber Dutzende Einzel-Selektoren - deckt automatisch alle ~49 Stellen ab, die bereits
   var(--shadow-card) nutzen (siehe .card weiter unten). */
[data-theme="unifi"] {
    --color-bg: #0B0F19;
    --color-surface: #111827;
    --color-border: #1F293D;
    --color-text: #F8FAFC;
    --color-text-muted: #94A3B8;
    --sidebar-text-color: var(--color-text);
    --color-accent: #0284C7;
    --color-accent-hover: #1D4ED8;
    --color-accent-soft: rgba(2, 132, 199, 0.16);
    --color-accent-glow: rgba(2, 132, 199, 0.45);
    --color-accent-tint: rgba(2, 132, 199, 0.07);
    --sidebar-tint: transparent;
    --status-active: #10B981;
    --status-active-soft: rgba(16, 185, 129, 0.14);
    --status-warning: #F59E0B;
    --status-warning-soft: rgba(245, 158, 11, 0.14);
    --status-neutral: #94A3B8;
    --status-neutral-soft: rgba(148, 163, 184, 0.16);
    --status-new: #b8a8f5;
    --status-new-text: #E7E2FB;
    --status-new-soft: rgba(184, 168, 245, 0.22);
    --color-danger-bg: #271A1B;
    --color-danger-bg-strong: #32201F;
    --color-danger-border: #5C2E2E;
    --color-danger-text: #F2A8A8;
    --status-hold: #A78BDB;
    --status-hold-soft: rgba(167, 139, 219, 0.16);
    --status-callback: #22D3EE;
    --status-callback-soft: rgba(34, 211, 238, 0.16);
    --status-scheduled: #9DA8F0;
    --status-scheduled-soft: rgba(157, 168, 240, 0.16);
    --status-absent: #E5645A;
    --status-absent-soft: rgba(229, 100, 90, 0.16);
    --entrytype-push-tint: #1E1B4B;
    --entrytype-appointment-tint: #3B0764;
    --entrytype-callback-tint: #451A03;
    --entrytype-email-tint: #0F172A;
    --entrytype-service-tint: #172554;
    --calendar-weekend-bg: rgba(156, 167, 180, 0.12);
    --calendar-holiday-bg: rgba(96, 165, 250, 0.16);
    --calendar-offhours-bg: rgba(255, 255, 255, 0.05);
    --color-critical: #FF6B6B;
    --color-critical-soft: rgba(255, 107, 107, 0.18);
    --shadow-card: none;
}

/* .mono - gezielt auf technischen Daten angewendet (Vault Passwort/OTP, Asset MAC/IP/Serial,
   Ticket-IDs), NICHT rueckwirkend auf die ~28 bestehenden Ad-hoc-monospace-Deklarationen
   angewendet (die bleiben unangetastet und funktionieren weiter). */
.mono {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
}

/* Seamless-Feinschliff nur im UniFi-Theme: .modal-box hat einen hartcodierten Schatten +
   Radius (nicht ueber --shadow-card/--radius-* tokenisiert, siehe Recherche), daher hier
   gezielt statt ueber das Token geloest. Globale duenne Scrollbar-Regel ergaenzt die bislang nur
   punktuell vorhandenen Scrollbar-Styles (.sidebar-nav/.vault-mdlist) um einen App-weiten Fallback. */
[data-theme="unifi"] .modal-box {
    box-shadow: none;
    border-radius: var(--radius-md);
    border: 1px solid var(--color-border);
}

[data-theme="unifi"] * {
    scrollbar-width: thin;
    scrollbar-color: var(--color-border) transparent;
}

[data-theme="unifi"] *::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

[data-theme="unifi"] *::-webkit-scrollbar-track {
    background: transparent;
}

[data-theme="unifi"] *::-webkit-scrollbar-thumb {
    background: var(--color-border);
    border-radius: 4px;
}

* {
    box-sizing: border-box;
}

/* Traegt --color-bg auch dort, wo body transparent durchscheint (siehe Gradient unten) - ohne
   das wuerde am hellen/oberen linken Verlaufsende der Browser-Standardhintergrund (weiss)
   durchscheinen statt des Theme-Farbtons, was im Dark Mode falsch aussaehe.
   overflow-x:hidden verhindert einen bekannten iOS-Safari-Bug: mehrere fixed-positionierte
   Elemente (.tdv2-slideover, .modal-box, Toast-Container) zentrieren sich per
   "left:50%; transform:translate(-50%, ...)" - Safari bezieht bei der Scroll-Breiten-Berechnung
   teils die ungetransformte Layout-Position mit ein, wodurch sich die gesamte Seite auf dem
   iPhone minimal horizontal wegziehen liess, obwohl visuell kein Overflow sichtbar war (Chromium-
   basierte Test-Tools reproduzieren das nicht). Kein Feature verlaesst sich auf Dokument-weites
   horizontales Scrollen - alle bewusst scrollbaren Bereiche (.card, .htimeline etc.) setzen ihr
   eigenes overflow-x:auto auf einem inneren Element und bleiben davon unberuehrt. */
html {
    background: var(--color-bg);
    overflow-x: hidden;
}

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: 13px;
    color: var(--color-text);
    /* KEIN eigenes overflow-x:hidden hier (auch wenn body sich in der Vergangenheit an html's
       Regel "angehaengt" hat): sobald sowohl html als auch body ein nicht-visible overflow
       deklarieren, hoert der Browser auf, body's Overflow auf den Viewport zu propagieren, und
       body wird selbst zu einer eigenen (nie tatsaechlich scrollenden) Scroll-Box. .sidebar's
       position:sticky bindet sich dann an diese falsche, unbewegliche Box statt an die echte
       Scroll-Ebene (html) - die Sidebar scrollt dann sichtbar mit statt zu kleben. Der iOS-Safari-
       Fix unten auf html allein reicht fuer das horizontale Clipping voellig aus. */
    /* Sehr dezenter Verlauf von oben links (transparent, zeigt --color-bg durch) nach unten
       rechts (leicht mit der Akzentfarbe eingefaerbt) - analog zum Personio-Referenz-Screenshot.
       Passt sich automatisch an eine persoenliche Akzentfarbe an (siehe App\Support\AccentColor). */
    background: linear-gradient(to bottom right, transparent 0%, var(--color-accent-tint) 100%);
    /* fixed statt scroll: der Verlauf bleibt relativ zum sichtbaren Viewport, statt sich ueber die
       komplette (potenziell sehr lange) Scroll-Hoehe der Seite zu strecken und dabei fast unsichtbar
       zu werden - siehe lange Listen-Ansichten wie Tickets/Kunden. */
    background-attachment: fixed;
    -webkit-font-smoothing: antialiased;
}

a {
    color: var(--color-accent);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

h1, h2, h3 {
    font-weight: 600;
    margin: 0 0 16px;
}

h1 { font-size: 17px; }
h2 { font-size: 14px; }

hr {
    border: none;
    border-top: 1px solid var(--color-border);
    margin: 28px 0;
}

.icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

/* App shell: sidebar + main content */
.app-shell {
    display: flex;
    min-height: 100vh;
}

.sidebar {
    width: 64px;
    flex-shrink: 0;
    /* Zwei gestapelte Hintergrund-Layer statt eines gemischten Farbwerts: die obere,
       halbtransparente Ebene (--sidebar-tint) legt sich verlaesslich ueber die deckende
       --color-surface-Ebene darunter - passt sich damit automatisch an Light/Dark-Theme UND an
       eine persoenliche Akzentfarbe an, ohne dass hier ein fixer Mischwert gepflegt werden muss.
       Eigenes Token statt --color-accent-tint (siehe :root-Kommentar) - ohne gespeicherte
       Akzentfarbe ist --sidebar-tint transparent, die Sidebar bleibt dann reines
       --color-surface/#FFFFFF statt immer einen leichten Farbstich zu zeigen. */
    background: linear-gradient(var(--sidebar-tint), var(--sidebar-tint)), var(--color-surface);
    border-right: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 16px 0;
    /* Bleibt beim Scrollen langer Seiten (z.B. Ticket-Detail, Kundenliste) im Viewport sichtbar,
       statt mit dem Hauptinhalt aus dem Bild zu laufen. align-self hebt den impliziten
       align-items:stretch von .app-shell auf, sonst waere die Sidebar so hoch wie .main und
       position:sticky haette nichts zum "Kleben". */
    position: sticky;
    top: 0;
    align-self: flex-start;
    height: 100vh;
    overflow: hidden;
    /* position:sticky erzeugt (anders als position:relative mit z-index:auto) immer einen eigenen
       Stacking-Context. Ohne explizites z-index hier wird dieser Context beim Vergleich mit dem
       spaeter im DOM stehenden .main-Bereich als "auto" behandelt und faellt hinter dessen Inhalt
       zurueck - die ueber die 64px-Breite hinausragenden [data-tooltip]-Tooltips (siehe oben)
       wurden dadurch vom Content ueberdeckt statt sichtbar darueber zu liegen. z-index:10 reicht,
       um vor .main zu liegen, bleibt aber deutlich unter Modal-/Overlay-Ebenen (z-index 100+). */
    z-index: 10;
}

.sidebar-nav {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    flex-grow: 1;
    width: 100%;
    /* min-height:0 ist noetig, damit ein flex-grow-Kind in einer flex-column ueberhaupt schrumpfen
       und scrollen darf (sonst wird die Box auf ihren Inhaltsumfang aufgeblasen, siehe .sidebar-
       footer weiter unten, das dadurch bei kurzen Viewports/vielen Icons aus dem sichtbaren Bereich
       gedrueckt wurde - z.B. Theme-Umschalter/Admin-Zahnrad nicht mehr erreichbar). overflow-y:auto
       scrollt nur diese Icon-Liste, .sidebar-footer bleibt davon unberuehrt immer sichtbar. */
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
}

.sidebar-nav::-webkit-scrollbar {
    width: 4px;
}

.sidebar-nav::-webkit-scrollbar-thumb {
    background: var(--color-border);
    border-radius: 4px;
}

/* CSS erzwingt bei overflow-y:auto automatisch auch overflow-x:auto/hidden (Spec-Verhalten) -
   damit wuerden die per position:absolute ueberlaufenden [data-tooltip]-Tooltips (siehe oben)
   innerhalb dieser Liste stumm abgeschnitten. Fuer .sidebar-nav daher deaktiviert; ein JS-Tooltip
   (siehe public/js/sidebar-tooltip.js) uebernimmt hier stattdessen ueber position:fixed - exakt
   das bereits etablierte Baukastenprinzip aus public/js/avatar-tooltip.js. .sidebar-footer
   (Theme-Umschalter etc.) ist trotz eigener Begruendung ("nicht geclippt") vom selben Problem
   betroffen: .sidebar selbst hat overflow:hidden (fuer position:sticky, siehe dort), was den
   laengeren "Dark Theme"-Tooltip am rechten Rand abschnitt - daher hier ebenfalls deaktiviert und
   auf denselben JS-Tooltip umgestellt. */
.sidebar-nav [data-tooltip]:hover::after,
.sidebar-nav [data-tooltip]:focus-visible::after,
.sidebar-footer [data-tooltip]:hover::after,
.sidebar-footer [data-tooltip]:focus-visible::after {
    display: none;
}

.nav-item {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    font-weight: 500;
}

.nav-item:hover {
    background: var(--color-bg);
    text-decoration: none;
    color: var(--color-text);
}

.nav-item.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.nav-item-back {
    margin-bottom: 8px;
    color: var(--color-accent);
}

.nav-item-back:hover {
    color: var(--color-accent);
    background: var(--color-accent-soft);
}

.nav-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 8px;
    background: var(--color-danger-text);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
}

/* Trennt das wachsende vertikale Menue (Kundenkontext, Admin) optisch in thematische
   Gruppen - sonst verschwimmt eine lange Icon-Liste ohne jede Struktur. */
.nav-divider {
    width: 28px;
    height: 1px;
    background: var(--color-border);
    margin: 6px 0;
    flex-shrink: 0;
}

/* Text bleibt fuer Screenreader im DOM, wird aber visuell durch das Tooltip (data-tooltip) ersetzt */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
}

[data-tooltip] {
    position: relative;
}

[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
    content: attr(data-tooltip);
    position: absolute;
    left: 100%;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 12px;
    background: var(--color-text);
    color: var(--color-bg);
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 500;
    white-space: nowrap;
    box-shadow: var(--shadow-card);
    z-index: 200;
    pointer-events: none;
}

.sidebar-footer {
    border-top: 1px solid var(--color-border);
    padding-top: 14px;
    margin-top: 14px;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
}

.theme-switch {
    display: inline-flex;
    flex-direction: column;
    gap: 2px;
    padding: 3px;
    background: var(--color-bg);
    border-radius: 999px;
    border: 1px solid var(--color-border);
}

.theme-switch-option {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    color: var(--color-text-muted);
}

.theme-switch-option:hover {
    text-decoration: none;
}

.theme-switch-option.is-active {
    background: var(--color-surface);
    color: var(--color-accent);
    box-shadow: var(--shadow-card);
}

.theme-switch-option .icon {
    width: 14px;
    height: 14px;
}

/* Horizontale Variante fuer Kontexte ohne Sidebar (z.B. Kanban-Workspace-Topbar,
   siehe projects/workspace.twig) - identische Optik/Tokens wie die vertikale Sidebar-Variante
   oben, nur die Stapelrichtung dreht sich. */
.theme-switch--horizontal {
    flex-direction: row;
}

.lang-switch {
    display: flex;
    gap: 6px;
}

.lang-switch a {
    color: var(--color-text-muted);
    font-size: 11px;
    font-weight: 600;
}

.lang-switch a.is-active {
    color: var(--color-accent);
}

.logout-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    font-weight: 500;
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-size: 13px;
}

.logout-link:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

/* Versionsnummer unter dem Abmelden-Button - bewusst sehr klein/unauffaellig, dient nur als
   Referenz beim Support ("welche Version laeuft hier"), nicht als Werbeflaeche. */
.sidebar-version {
    font-size: 10px;
    color: var(--color-text-muted);
    text-align: center;
    margin-top: 4px;
    cursor: default;
}

.main {
    flex-grow: 1;
    min-width: 0;
}

.topbar {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 18px 32px;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
}

.topbar h1 {
    margin: 0;
}

.topbar-right {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    flex: 0 0 auto;
}

.topbar-left {
    display: flex;
    align-items: center;
    gap: 16px;
    min-width: 0;
    flex: 0 1 auto;
}

/* Nicht mehr grid-zentriert unabhaengig von der Ueberschriften-Laenge (fruehere Regel) - stattdessen
   wie im Ticket-Ansicht-2.0-Mockup: die Suche folgt direkt nach dem linken Kopfbereich, statt
   diesem bei laengeren Kopfzeilen (z.B. Ticket-Titel+Status+Prio-Pills, siehe .tdv2-header) exakt
   die Haelfte der Topbar wegzunehmen. margin-left:auto schiebt sie (und alles danach) so weit wie
   moeglich nach rechts, max-width begrenzt ihre Breite auf die des Finder-Buttons. */
.topbar-center {
    display: flex;
    align-items: center;
    flex: 0 1 380px;
    max-width: 380px;
    margin-left: auto;
}

/* CoreDesk Finder - globale OmniSearch/Command-Palette (Strg+K), siehe public/js/finder.js.
   Optik an .list-search (rechte Seiten-Suchen, z.B. "Finde Einstellungen...") angeglichen, nur
   breiter, damit sie in der zentrierten Topbar-Spalte nicht verloren wirkt. */
.finder-trigger {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    width: 380px;
    max-width: 100%;
    padding: 8px 12px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 8px;
    color: var(--color-text-muted);
    font-size: 13px;
    cursor: pointer;
    transition: box-shadow 0.15s ease, border-color 0.15s ease;
}

.finder-trigger span {
    flex-grow: 1;
    text-align: left;
}

.finder-trigger:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

.finder-trigger:hover,
.finder-trigger:focus-visible {
    border-color: var(--color-accent);
    box-shadow: 0 0 8px var(--color-accent-glow);
    outline: none;
}

[data-theme="dark"] .finder-trigger {
    box-shadow: 0 0 8px var(--color-accent-glow);
    border-color: var(--color-accent);
}

[data-theme="dark"] .finder-trigger:hover,
[data-theme="dark"] .finder-trigger:focus-visible {
    box-shadow: 0 0 12px var(--color-accent-glow);
}

/* Wiederverwendbarer leuchtender Akzent-Effekt (gleiches Rezept wie .finder-trigger oben), aber
   PERMANENT statt nur bei Hover/Focus - fuer Elemente, die dauerhaft aus der Flaeche hervorstechen
   sollen (z.B. der "Fokus View"-Link im Ticket UND der gleichnamige Fokus-Modus-Umschalter im
   CoreDesk-Hub-Board, siehe #hubFocusBtn in home.twig). !important auf color/border-color, weil
   sowohl Module mit eigener ".xyz a { color: inherit; }"-Regel (Ticket-Ansicht 2.0) als auch
   gleich-spezifische, spaeter geladene Klassen (z.B. .hub-toggle-btn) sonst gewinnen wuerden -
   gilt bewusst fuer <a> UND <button>, nicht nur Links.  */
.glow-accent {
    color: var(--color-accent) !important;
    border-color: var(--color-accent) !important;
    box-shadow: 0 0 8px var(--color-accent-glow);
    transition: box-shadow 0.15s ease;
}

.glow-accent:hover {
    box-shadow: 0 0 12px var(--color-accent-glow);
    background: var(--color-accent-soft);
}

.finder-trigger kbd {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    padding: 1px 5px;
    color: var(--color-text-muted);
}

.finder-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(15, 23, 42, 0.3);
    backdrop-filter: blur(4px);
    align-items: flex-start;
    justify-content: center;
    padding: 10vh 16px 16px;
}

.finder-backdrop.is-open {
    display: flex;
}

.finder-box {
    width: 100%;
    max-width: 900px;
    height: 550px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: 0 20px 60px rgba(15, 23, 42, 0.35);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transform: scale(1);
    transition: transform 100ms ease;
}

.finder-box.is-wiggling {
    transform: scale(1.02);
}

.finder-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 16px;
    height: 56px;
    border-bottom: 1px solid var(--color-border);
    flex-shrink: 0;
    color: var(--color-text-muted);
}

.finder-header input {
    flex: 1;
    border: none;
    background: transparent;
    font-size: 15px;
    color: var(--color-text);
    padding: 0;
}

.finder-header input:focus {
    outline: none;
    box-shadow: none;
}

.finder-close-btn {
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
    padding: 6px;
    border-radius: var(--radius-sm);
    display: inline-flex;
}

.finder-close-btn:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

.finder-body {
    display: flex;
    flex: 1;
    min-height: 0;
    background: var(--color-bg);
}

.finder-results {
    width: 50%;
    border-right: 1px solid var(--color-border);
    overflow-y: auto;
    background: var(--color-surface);
    padding: 8px;
}

.finder-empty-hint {
    padding: 24px 12px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 12px;
}

.finder-result-item {
    display: block;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    cursor: pointer;
    margin-bottom: 4px;
}

.finder-result-item:hover {
    background: var(--color-bg);
}

.finder-result-item.is-active {
    background: var(--color-accent-soft);
    border-color: var(--color-accent);
}

.finder-result-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 4px;
}

.finder-result-id {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
}

.finder-result-item.is-active .finder-result-id {
    color: var(--color-accent);
}

.finder-result-subject {
    font-size: 13px;
    font-weight: 500;
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.finder-result-customer {
    font-size: 11px;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.finder-preview {
    width: 50%;
    overflow-y: auto;
    padding: 16px;
}

.finder-preview-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    color: var(--color-text-muted);
    text-align: center;
    gap: 8px;
}

.finder-preview-empty p {
    font-size: 12px;
    margin: 0;
}

.finder-preview-content {
    display: flex;
    flex-direction: column;
    gap: 12px;
    height: 100%;
}

.finder-preview-meta {
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 12px;
}

.finder-preview-meta-label {
    font-size: 11px;
    color: var(--color-text-muted);
}

.finder-preview-meta h3 {
    font-size: 15px;
    margin: 4px 0;
    color: var(--color-text);
}

.finder-preview-meta-row {
    display: flex;
    gap: 12px;
    font-size: 12px;
    color: var(--color-text-muted);
    flex-wrap: wrap;
}

.finder-preview-entry-wrap {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.finder-preview-entry-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    width: max-content;
    font-size: 11px;
    font-weight: 700;
    color: var(--status-active);
    background: var(--status-active-soft);
    border: 1px solid var(--status-active);
    border-radius: var(--radius-sm);
    padding: 4px 10px;
    margin-bottom: 8px;
}

.finder-preview-entry {
    flex: 1;
    overflow-y: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 12px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    color: var(--color-text);
    white-space: pre-wrap;
    line-height: 1.6;
}

.finder-preview-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--color-border);
    flex-shrink: 0;
}

.finder-footer {
    height: 36px;
    background: var(--color-bg);
    border-top: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 16px;
    font-size: 11px;
    color: var(--color-text-muted);
    gap: 16px;
    flex-shrink: 0;
}

.finder-footer > span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.finder-footer kbd {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    padding: 1px 5px;
    color: var(--color-text);
}

/* CoreDesk Quick-Punch (siehe Migration 153): kleinere, zentrierte Command-Palette nach demselben
   Backdrop-Muster wie CoreDesk Finder (.finder-backdrop/.finder-box) - bewusst eigene Klassen
   statt Wiederverwendung, da die Palette kein Suchfeld/keine Ergebnisliste hat, sondern eine
   feste Kachel-Grid-Flaeche. */
.qp-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(15, 23, 42, 0.3);
    backdrop-filter: blur(4px);
    align-items: flex-start;
    justify-content: center;
    padding: 12vh 16px 16px;
}

.qp-backdrop.is-open {
    display: flex;
}

.qp-box {
    width: 100%;
    max-width: 520px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: 0 20px 60px rgba(15, 23, 42, 0.35);
    overflow: hidden;
}

.qp-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 16px;
    height: 56px;
    border-bottom: 1px solid var(--color-border);
}

.qp-header h2 {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 15px;
    margin: 0;
}

.qp-tile-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
    padding: 16px;
}

.qp-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 16px 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg);
    cursor: pointer;
    transition: background 100ms ease, border-color 100ms ease;
}

.qp-tile:hover {
    background: rgba(99, 102, 241, 0.10);
    border-color: var(--entrytype-push);
}

.qp-tile:disabled {
    opacity: 0.5;
    cursor: default;
}

.qp-tile-emoji {
    font-size: 24px;
}

.qp-tile-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
    text-align: center;
}

.qp-tile-duration {
    font-size: 11px;
    color: var(--color-text-muted);
}

.qp-empty-hint {
    grid-column: 1 / -1;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 13px;
    padding: 16px 0;
}

/* Finder-Filterleiste (Zeitraum-Chips + Historie-Checkbox), zwischen Suchzeile und Split-View. */
.finder-filter-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 10px;
    padding: 10px 16px;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
    flex-shrink: 0;
}

.finder-filter-bar--content {
    border-bottom: 1px solid var(--color-border);
}

.finder-time-filters,
.finder-content-filters {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.finder-filter-label {
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.finder-time-chip,
.finder-content-chip {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    font-size: 11px;
    font-weight: 500;
    padding: 4px 12px;
    border-radius: 16px;
    cursor: pointer;
}

.finder-time-chip.is-active,
.finder-content-chip.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}

.finder-date-chip-wrap {
    position: relative;
    display: inline-flex;
}

.finder-date-popover {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 10;
    width: 260px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: 0 12px 24px rgba(15, 23, 42, 0.15);
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.finder-date-popover[hidden] {
    display: none;
}

.finder-date-presets {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
}

.finder-date-preset-btn {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    color: var(--color-text);
    font-size: 11px;
    font-weight: 500;
    padding: 4px 10px;
    border-radius: 14px;
    cursor: pointer;
}

.finder-date-preset-btn:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.finder-date-range-row {
    display: flex;
    gap: 10px;
}

.finder-date-range-row label {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 11px;
    color: var(--color-text-muted);
    flex: 1;
}

.finder-date-range-row input[type="date"] {
    font-size: 12px;
    padding: 5px 6px;
}

.finder-date-apply-btn {
    width: 100%;
    justify-content: center;
    font-size: 12px;
    padding: 8px;
}

/* Kategorie-Gruppen in der Ergebnisliste (client-seitig gerendert, siehe finder.js). */
.finder-category-title {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-light, var(--color-text-muted));
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 8px 12px 4px;
}

.finder-type-badge {
    font-size: 10px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 4px;
    background: var(--status-neutral-soft);
    color: var(--status-neutral);
}

.finder-type-badge--password {
    background: var(--color-danger-bg-strong);
    color: var(--color-danger-text);
}

/* Asset-Vorschau: schlichte 2-spaltige Detail-Tabelle. */
.finder-preview-asset-table {
    width: 100%;
    border-collapse: collapse;
}

.finder-preview-asset-table td {
    padding: 8px 4px;
    border-bottom: 1px solid var(--color-border);
    font-size: 13px;
    color: var(--color-text);
}

.finder-preview-asset-table td:first-child {
    font-weight: 600;
    color: var(--color-text-muted);
    width: 40%;
}

/* Passwort-Vorschau: je eine Zeile fuer Benutzername/Kennwort mit eigenem Kopieren-Button. */
.finder-password-row {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.finder-password-row-label {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-bottom: 2px;
}

.finder-password-masked {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 2px;
    color: var(--color-text);
}

/* CoreDesk Vault - globales Security Dashboard + manipulationssicheres Audit-Log, siehe
   templates/vault/*.twig und VaultDashboardController. 2/3-1/3-Layout: Hauptinhalt links,
   In-Page-Navigation (Security Dashboard/Audit Logs) rechts. */
.vault-layout-grid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 24px;
    align-items: start;
}

.vault-side-nav {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px;
    position: sticky;
    top: 0;
}

.vault-side-nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.vault-side-nav-item:hover {
    background: var(--color-bg);
}

.vault-side-nav-item.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.vault-metrics-grid {
    display: grid;
    grid-template-columns: 1fr 2fr;
    gap: 24px;
}

.vault-metric-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.vault-big-stat {
    font-size: 40px;
    font-weight: 800;
    color: var(--color-text);
    line-height: 1;
    margin: 12px 0;
}

.vault-segmented-progress {
    height: 8px;
    background: var(--color-border);
    border-radius: 4px;
    overflow: hidden;
    display: flex;
    margin-bottom: 10px;
}

.vault-seg-secure {
    background: var(--status-active);
    height: 100%;
}

.vault-seg-risk {
    background: var(--color-danger-text);
    height: 100%;
}

.vault-progress-legend {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    font-weight: 600;
}

.vault-legend-secure {
    color: var(--status-active);
}

.vault-legend-risk {
    color: var(--color-danger-text);
}

.vault-risk-indicators {
    display: flex;
    justify-content: space-around;
    align-items: flex-start;
    text-align: center;
}

.vault-ring-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    max-width: 110px;
}

.vault-risk-ring {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    border: 4px solid var(--color-border);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    font-weight: 700;
}

.vault-risk-ring--weak {
    border-color: var(--status-active);
    color: var(--status-active);
}

.vault-risk-ring--old {
    border-color: var(--status-warning);
    color: var(--status-warning);
}

.vault-risk-ring--breach {
    border-color: var(--color-danger-text);
    color: var(--color-danger-text);
}

.vault-ring-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.vault-strength-bar {
    height: 6px;
    background: var(--color-border);
    border-radius: 3px;
    width: 90px;
    overflow: hidden;
}

.vault-strength-bar-fill {
    height: 100%;
}

.vault-strength-bar-fill--weak {
    background: var(--color-danger-text);
}

.vault-strength-bar-fill--strong {
    background: var(--status-active);
}

.vault-info-banner {
    background: var(--status-active-soft);
    border: 1px solid var(--status-active);
    color: var(--status-active);
    padding: 12px 18px;
    border-radius: var(--radius-md);
    font-size: 13px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 10px;
}

.hash-tag {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    color: var(--color-text-muted);
    background: var(--color-bg);
    padding: 2px 6px;
    border-radius: 4px;
}

/* CoreDesk Signal - globales, technikerbezogenes Activity Center (Benachrichtigungs-Glocke im
   Header, siehe public/js/signal-bell.js). .nav-badge wird 1:1 aus der Sidebar-Inbox-Glocke
   wiederverwendet (position:absolute, braucht einen position:relative-Elternteil - hier .signal-bell). */
.signal-bell-wrap {
    position: relative;
}

.signal-bell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.signal-bell:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

.signal-dropdown {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: 340px;
    max-height: 420px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    z-index: 100;
    overflow: hidden;
}

.signal-dropdown.is-open {
    display: flex;
    flex-direction: column;
}

.signal-dropdown-list {
    overflow-y: auto;
    max-height: 360px;
}

.signal-item {
    display: block;
    padding: 10px 16px;
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text);
    font-size: 13px;
    line-height: 1.5;
}

.signal-item:hover {
    background: var(--color-bg);
    text-decoration: none;
}

.signal-item--unread {
    background: var(--color-accent-soft);
}

.signal-item-time {
    display: block;
    font-size: 11px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.signal-dropdown-empty {
    padding: 24px 16px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 13px;
}

.signal-dropdown-markall {
    border: none;
    border-top: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-accent);
    font-size: 12px;
    font-weight: 600;
    padding: 10px;
    cursor: pointer;
    flex-shrink: 0;
}

.signal-dropdown-markall:hover {
    background: var(--color-bg);
}

/* Techniker-Menue in der globalen Topbar (neben der Glocke) - Avatar + Name/Position sichtbar
   (nicht nur als Tooltip wie bisher in der Sidebar-Footer), Dropdown per reinem CSS-Hover statt
   JS-Toggle (wie beim Signal-Glocken-Dropdown), da nur schlichte Platzhalter-Links enthalten sind. */
.topbar-user-menu {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding-left: 14px;
    padding-bottom: 14px;
    margin-left: 4px;
    margin-bottom: -14px;
    border-left: 1px solid var(--color-border);
    cursor: default;
}

/* 4px groesser als der Standard-.tech-avatar (22px) - bei 3-stelligen Kuerzeln (z.B. "LHA")
   wirkte die Standardgroesse zu eng am Rand. */
.topbar-user-menu .tech-avatar {
    width: 26px;
    height: 26px;
    font-size: 11px;
}

.topbar-user-info {
    display: flex;
    flex-direction: column;
    line-height: 1.3;
}

.topbar-user-name {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text);
}

.topbar-user-role {
    font-size: 10px;
    color: var(--color-text-muted);
}

.topbar-user-dropdown {
    display: none;
    flex-direction: column;
    position: absolute;
    top: 100%;
    right: 0;
    min-width: 190px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    z-index: 100;
    padding: 6px;
}

.topbar-user-menu:hover .topbar-user-dropdown {
    display: flex;
}

/* Klick-Umschaltung zusaetzlich zum Hover (siehe mobile-chrome.js): :hover ist auf Touch-Geraeten
   nicht zuverlaessig ausloesbar - ohne diese Klasse war das Dropdown (und damit auch Logout) auf
   dem iPhone praktisch nicht erreichbar, da die Sidebar mit dem alternativen Logout-Button dort
   ausgeblendet ist (siehe Breakpoint-Vertrag). */
.topbar-user-menu.is-open .topbar-user-dropdown {
    display: flex;
}

.topbar-user-dropdown a {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    color: var(--color-text);
}

.topbar-user-dropdown a:hover {
    background: var(--color-bg);
    text-decoration: none;
}

.topbar-user-dropdown-highlight {
    color: var(--status-active) !important;
    font-weight: 600;
}

.topbar-user-dropdown-logout {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 10px;
    border: none;
    border-radius: var(--radius-sm);
    background: none;
    font-family: inherit;
    font-size: 12px;
    color: var(--color-danger-text);
    cursor: pointer;
    text-align: left;
}

.topbar-user-dropdown-logout:hover {
    background: var(--color-bg);
}

/* Logo-Wordmark nur auf der Hub-Startseite (siehe home.twig page_heading-Override) - die kleine
   "CD"-Marke in der Sidebar bleibt der globale Navigationsanker auf allen anderen Seiten. */
.hub-logo-wordmark {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: var(--color-text);
}

.hub-logo-wordmark-accent {
    color: var(--color-accent);
}

.content {
    padding: 24px 28px;
    max-width: none;
}

/* Gesperrter Kunde: dezente Toenung statt grellem Rot, fuer alle Kunden-Unterseiten */
.main.is-customer-locked {
    background: var(--color-danger-bg);
}

.main.is-customer-locked .topbar {
    background: var(--color-danger-bg);
}

.lock-banner {
    background: var(--color-danger-bg-strong);
    border: 1px solid var(--color-danger-border);
    color: var(--color-danger-text);
    padding: 12px 18px;
    border-radius: var(--radius-sm);
    margin-bottom: 20px;
    font-size: 13px;
}

.lock-banner strong {
    letter-spacing: 0.02em;
}

.btn:disabled,
.btn[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
    filter: grayscale(0.4);
}

.btn:disabled:hover,
.btn[disabled]:hover {
    background: var(--color-surface);
}

/* Sende-Spinner fuer Formular-Buttons (Nutzer-Feedback 2026-08-07, zuerst am "E-Mail senden"-
   Button im Ticket) - generische .btn-Modifier-Klasse statt eines email-spezifischen Namens, damit
   sie sich fuer jeden weiteren "gegen Doppel-Klick absichern"-Button wiederverwenden laesst. Ring
   nutzt currentColor statt eines hart codierten Weiss, damit sowohl .btn-primary (weisser Text auf
   Akzentfarbe) als auch ein normaler .btn (Textfarbe) automatisch passend eingefaerbt sind - auch
   in Dark Mode ohne eigene Ueberschreibung. Ueberschreibt bewusst die pauschale Ausgrau-Optik von
   .btn:disabled oben, da "wird gerade aktiv gesendet" etwas anderes signalisieren soll als
   "gerade nicht verfuegbar". */
.btn.is-sending:disabled {
    opacity: 0.85;
    filter: none;
    cursor: default;
}

/* Der Mauszeiger steht im Sende-Moment fast immer noch genau auf dem Button (Nutzer hat gerade
   geklickt) - .btn:disabled:hover oben faerbt den Hintergrund dann auf --color-surface (weiss) um,
   was bei .btn-primary weissen Text auf weissem Grund ergibt (unsichtbar). Gleiche Spezifitaet wie
   .btn:disabled:hover (3 Klassen/Pseudoklassen), gewinnt per Reihenfolge (steht spaeter). */
.btn-primary.is-sending:disabled,
.btn-primary.is-sending:disabled:hover {
    background: var(--color-accent);
}

.btn.is-sending::before {
    content: '';
    display: inline-block;
    width: 13px;
    height: 13px;
    margin-right: 7px;
    vertical-align: -2px;
    border-radius: 50%;
    border: 2px solid color-mix(in srgb, currentColor 30%, transparent);
    border-top-color: currentColor;
    animation: btn-spin 0.6s linear infinite;
}

@keyframes btn-spin {
    to { transform: rotate(360deg); }
}

.field input:disabled,
.field select:disabled,
.field textarea:disabled {
    background: var(--color-bg);
    color: var(--color-text-muted);
    cursor: not-allowed;
}

/* Cards */
.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 24px;
    margin-bottom: 20px;
    /* Lange Tabelleninhalte scrollen innerhalb der Karte statt die Seite zu sprengen - overflow-y
       bleibt bewusst "visible" (statt des impliziten "auto"), damit absolut positionierte Popover/
       Dropdowns innerhalb der Karte (z.B. .legend-info-popover) nicht am Kartenrand abgeschnitten
       werden. */
    overflow-x: auto;
    overflow-y: visible;
}

/* Dark-Theme: Karten bekommen statt des flachen --color-border-Randes einen dezenten
   Farbverlauf-Rand (zweischichtiger background-Trick: innere Flaeche in padding-box, Verlauf in
   border-box - der transparente 1px-Rand macht den Verlauf im Randring sichtbar). Nutzt bewusst
   --color-accent/--color-accent-hover statt fester RGB-Werte, damit eine vom Mitarbeiter gewaehlte
   individuelle Akzentfarbe (Einstellungen > UI-Design, siehe AccentColor::cssVariables()) auch hier
   greift statt beim Standard-Blau zu bleiben. */
[data-theme="dark"] .card {
    border: 1px solid transparent;
    background:
        linear-gradient(rgb(19, 20, 22), rgb(19, 20, 22)) padding-box padding-box,
        linear-gradient(135deg, var(--color-accent) 10%, var(--color-accent-hover) 60%, var(--color-accent-hover) 100%) border-box border-box;
}

/* UniFi-Theme bewusst OHNE den gluehenden Farbverlauf-Rand von [data-theme="dark"] .card oben -
   das widerspricht der geforderten "messerscharfen 1px-Linie, keine Schatten"-Optik. Flache,
   einfarbige Grenze statt Glow-Effekt. */
[data-theme="unifi"] .card {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
}

/* Gedeckte Variante fuer optisch zurueckhaltende, von der eigentlichen Bearbeitung getrennte
   Karten (z.B. die "Ticket loeschen"-Karte unter der Kostenrechnung, siehe tickets/show.twig) -
   nutzt bewusst dieselbe --color-bg-Variable wie der Seitenhintergrund statt eines neuen Tons,
   damit die Karte optisch zurueckhaltend/"eingesenkt" wirkt statt wie eine normale weisse Karte
   hervorzustechen, in Light- wie Dark-Theme gleichermassen. */
.card--muted {
    background: var(--color-bg);
    box-shadow: none;
}

/* Gebaenderter Karten-Kopf/-Fuss (Referenz: td-2.html-Mockup) - bleibt bis zu den Kartenraendern
   ausgedehnt (negative Margin gleicht die .card-Innenabstaende aus), fuer die Beschreibungs- und
   die Ticket-Verlauf-Karte auf der Ticket-Detailseite. Andere Karten (Sidebar etc.) behalten ihre
   bestehende .sidebar-section-title-Optik unveraendert bei, die dem Mockup dort bereits entspricht. */
.ticket-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: -24px -24px 20px;
    padding: 16px 24px;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
}

.ticket-card-header h2,
.ticket-card-header h3 {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text);
}

.ticket-card-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 20px -24px -24px;
    padding: 12px 24px;
    background: var(--color-bg);
    border-top: 1px solid var(--color-border);
    border-radius: 0 0 var(--radius-md) var(--radius-md);
    font-size: 12px;
    color: var(--color-text-muted);
}

/* Segmented-Control-Filter im Ticket-Verlauf-Kartenkopf ("Alle/Leistungen/Kommentare/E-Mails") -
   reines Anzeige-Toggle per data-kind-Attribut, siehe filterTimeline() in tickets/show.twig. */
.timeline-filter {
    display: flex;
    gap: 2px;
    background: var(--color-border);
    padding: 3px;
    border-radius: 8px;
}

.timeline-filter-btn {
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    font-size: 12px;
    font-weight: 600;
    padding: 5px 12px;
    border-radius: 6px;
    cursor: pointer;
}

.timeline-filter-btn.is-active {
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.08);
}

/* Text-Kuerzung fuer lange Inhalte (URLs, Notizen, Tag-Listen) - Wert bleibt vollstaendig
   im DOM/title-Attribut erhalten, nur die Anzeige wird gekuerzt. */
.truncate {
    display: inline-block;
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: bottom;
}

.truncate-sm {
    max-width: 140px;
}

/* Dynamischer Begruessungs-Header im CoreDesk Hub (siehe hub.html-Vorlage,
   DashboardController::resolveGreeting()) - Begruessung + Motivations-Satz links, persoenliche
   Tages-Zeiterfassung rechts. */
.hub-greeting-card {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 20px;
    margin-bottom: 24px;
    position: relative;
    overflow: hidden;
}

/* Dezente Deko-Blase oben rechts (rein kosmetisch, global auf allen Breakpoints) - liegt hinter
   Begruessungstext und Status-Widget dank z-index:1 auf den beiden Kind-Elementen unten. */
.hub-greeting-card::before {
    content: '';
    position: absolute;
    top: -50px;
    right: -50px;
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: var(--color-accent-soft);
    filter: blur(20px);
    pointer-events: none;
    z-index: 0;
}

.hub-greeting-card > .hub-greeting-text,
.hub-greeting-card > .hub-status-widget {
    position: relative;
    z-index: 1;
}

.hub-greeting-title {
    font-size: 24px;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin: 0 0 4px;
}

.hub-greeting-subtitle {
    font-size: 13px;
    color: var(--color-text-muted);
    margin: 0;
}

.hub-greeting-warning {
    font-size: 13px;
    font-weight: 600;
    color: #A66A1E;
    background: var(--status-warning-soft);
    border: 1px solid var(--status-warning-soft);
    border-radius: var(--radius-sm);
    padding: 6px 10px;
    margin: 8px 0 0;
    display: inline-block;
}

.hub-sla-badge {
    display: inline-block;
    background: var(--status-active-soft);
    color: var(--status-active);
    border: 1px solid var(--status-active-soft);
    padding: 1px 6px;
    border-radius: var(--radius-sm);
    font-size: 11px;
    font-weight: 700;
}

/* Adaptives "Mein Status"-Widget (siehe DashboardController::buildHubStatus()) - ein Widget-
   Slot mit festem Aufbau (Icon/Status/Zeitraum/Aktion), der Farbe (--hub-status-accent/-soft,
   per Inline-Style je Zustand gesetzt) und Inhalt an den aktiven Abwesenheitstyp anpasst, statt
   fuer jeden Typ eine eigene Karte zu zeigen. Ersetzt die alte .hub-time-progress-Familie. */
.hub-status-widget {
    width: 100%;
    max-width: 322px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 16px 18px 14px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    position: relative;
    overflow: hidden;
}

.hub-status-widget::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background: var(--hub-status-accent);
}

.hub-status-top {
    display: flex;
    align-items: center;
    gap: 11px;
}

.hub-status-icon {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    background: var(--hub-status-accent-soft);
    color: var(--hub-status-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.hub-status-icon-svg {
    width: 19px;
    height: 19px;
}

.hub-status-label-wrap {
    min-width: 0;
}

.hub-status-label {
    font-size: 13px;
    font-weight: 800;
    line-height: 1.2;
}

.hub-status-primary {
    font-size: 12px;
    color: var(--color-text-muted);
    font-weight: 600;
    line-height: 1.3;
    margin-top: 1px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.hub-status-secondary {
    font-size: 12px;
    color: var(--color-text-muted);
    line-height: 1.5;
}

/* Monat-Zeile im Utilization-Widget (siehe home.twig hub-status-widget) - Farbe zeigt per Inline-
   Style den Abstand zur Ziel-Auslastungsquote (Ampel-Schema wie sonst im Hub ueblich), daher hier
   nur Typografie. */
.hub-status-caption {
    font-size: 11.5px;
    font-weight: 600;
    margin-top: 6px;
}

.hub-status-progress-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-subtle, var(--color-text-muted));
    margin-bottom: 3px;
}

.hub-status-track {
    height: 6px;
    border-radius: 999px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    overflow: hidden;
}

.hub-status-fill {
    height: 100%;
    border-radius: 999px;
    background: var(--hub-status-accent);
    transition: width 0.3s;
}

.hub-status-foot {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    margin-top: 2px;
    padding-top: 10px;
    border-top: 1px solid var(--color-border);
}

.hub-status-action {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 700;
    color: var(--hub-status-accent);
    white-space: nowrap;
}

.hub-status-privacy {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10.5px;
    color: var(--color-text-muted);
    font-weight: 600;
    white-space: nowrap;
}

/* Abschnitts-Header ueber dem Kalender-Panel (siehe hub.html-Vorlage "Zentrale
   Team-Auslastung") - reiner Text-Block, keine eigene Karte. */
.hub-section-header {
    margin-bottom: 16px;
}

.hub-section-header h2 {
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin: 0;
}

.hub-section-header p {
    font-size: 13px;
    color: var(--color-text-muted);
    margin: 4px 0 0;
}

/* Dreispaltige Reihe "Attention"-Widget + Live-Team-Praesenz + Ehrentage & Meilensteine
   (Relations-Modul, nur wenn aktiviert) unterhalb des Kalenders - Attention etwas breiter, da
   Kunde+Betreff mehr Platz brauchen als die beiden kompakteren Listen-Widgets. */
.hub-attention-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1.4fr;
    gap: 16px;
    align-items: start;
    margin-bottom: 24px;
}

@media (max-width: 1200px) {
    .hub-attention-row {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 900px) {
    .hub-attention-row {
        grid-template-columns: 1fr;
    }
}

/* "Attention"-Widget (DashboardController::buildAttentionTickets()/TicketRepository::
   attentionForUser()) - ausschliesslich eigene Tickets, deren vertragliche SLA-Frist bereits
   ueberschritten ist. Jede Zeile ist per Definition hochkritisch, daher durchgehend die
   Gefahren-Palette (--color-danger-*, sonst fuer Fehlermeldungen genutzt) statt einer abgestuften
   Ampel - die Karte soll auf den ersten Blick als "hier muss sofort gehandelt werden" lesbar sein.
   Ohne offene SLA-Verstoesse bleibt die Karte neutral (siehe .attention-card ohne --alert). */
.attention-card--alert {
    background: var(--color-danger-bg);
    border-color: var(--color-danger-border);
}

.attention-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 10px;
    margin-bottom: 12px;
}

.attention-card--alert .attention-header {
    color: var(--color-danger-text);
    border-bottom-color: var(--color-danger-border);
}

.attention-count-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--color-danger-text);
    color: var(--color-danger-bg);
    font-size: 11px;
    font-weight: 700;
    text-transform: none;
    letter-spacing: normal;
}

/* Attention-Widget, gruppiert nach Ursache (UI/UX-Redesign 2026-08-01, Nutzer-Feedback: "die ersten
   beiden roten eintraege sind ueberfaellige tickets, die beiden gruenen sind termine, die nicht in
   leistung umgewandelt wurden - muessen wir gruppieren"). Statt einer durchmischten Einheitsliste
   bekommt jede Ursache (SLA-Bruch vs. offene Nachbuchung) einen eigenen Mini-Abschnitt mit
   Eyebrow-Header + eigenem Zaehler - die Zeilen selbst bleiben bewusst neutral (--color-bg statt
   vollflaechig eingefaerbt), das Farbsignal sitzt konzentriert im Akzentbalken links und im
   Zeit-Chip rechts. So bleibt die Karte bei vielen Eintraegen lesbar statt zu einer "roten Wand"
   zu werden, im UniFi-typischen ruhigen, dichten Listenstil. */
.attn-groups {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.attn-group-header {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: 7px;
}

.attn-group--danger .attn-group-header { color: var(--color-danger-text); }
.attn-group--success .attn-group-header { color: var(--status-active); }

.attn-group-count {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 5px;
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    text-transform: none;
    letter-spacing: normal;
}

.attn-group--danger .attn-group-count {
    background: var(--color-danger-bg-strong);
    color: var(--color-danger-text);
}

.attn-group--success .attn-group-count {
    background: var(--status-active-soft);
    color: var(--status-active);
}

.attn-rows {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.attn-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 10px 7px 12px;
    border-radius: var(--radius-md);
    background: var(--color-bg);
    color: var(--color-text);
    min-width: 0;
    overflow: hidden;
    transition: background .15s ease, box-shadow .15s ease, transform .1s ease;
}

.attn-row:hover {
    box-shadow: 0 1px 4px rgba(16, 24, 40, 0.1);
    text-decoration: none;
    transform: translateX(1px);
}

.attn-row-bar {
    position: absolute;
    left: 0;
    top: 4px;
    bottom: 4px;
    width: 3px;
    border-radius: 999px;
}

.attn-group--danger .attn-row-bar { background: var(--color-danger-text); }
.attn-group--success .attn-row-bar { background: var(--status-active); }

.attn-row-body {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
    flex: 1 1 auto;
}

.attn-row-customer {
    font-weight: 700;
    font-size: 12.5px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.attn-row-subject {
    font-size: 11.5px;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.attn-row-badge {
    flex: 0 0 auto;
    font-size: 10px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    padding: 3px 7px;
    border-radius: 999px;
}

.attn-group--danger .attn-row-badge {
    background: var(--color-danger-bg-strong);
    color: var(--color-danger-text);
}

.attn-group--success .attn-row-badge {
    background: var(--status-active-soft);
    color: var(--status-active);
}

/* Kompakte Live-Team-Praesenzanzeige (siehe hub.html-Vorlage "Live Team-Status",
   DashboardController::buildTeamPresence()) - nur noch Avatare in einem umbrechenden Cluster statt
   einer Zeile pro Mitarbeiter mit Name+Status-Pill: bei groesseren Abteilungen (10+ Mitarbeiter)
   wurde die Zeilenliste sonst zu lang/unuebersichtlich. Der Status ist bereits ueber die
   Hintergrundfarbe des Avatars selbst kodiert (.tech-avatar--{status}, siehe avatar.twig), Name
   und Detailstatus bleiben ueber den bestehenden Rich-Tooltip beim Hover erreichbar. */
.team-presence-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 10px;
    margin-bottom: 12px;
}

/* Mobiler Kurz-Titel + "Alle"-Link (siehe Mockup-Screenshot Team-Praesenz-Karte) - auf Desktop
   bleibt der bestehende Titel "Live Team-Status (Heute)" unveraendert, nur auf Mobil wird auf
   die kompakte Karten-Kopfzeile mit Verlinkung zu /dispatch umgeschaltet (siehe Breakpoint
   weiter unten). */
.team-presence-header-mobile, .team-presence-all-link { display: none; }

.team-presence-all-link {
    align-items: center;
    gap: 2px;
    color: var(--color-accent);
    text-transform: none;
    font-weight: 700;
}

.team-presence-all-link .icon, .team-presence-all-link svg { width: 14px; height: 14px; }

.team-presence-avatars {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    max-height: 220px;
    overflow-y: auto;
}

/* CoreDesk Relations & Milestones (Add-on-Modul, siehe RelationsMilestonesResolver) - Hub-Widget
   "Ehrentage & Meilensteine" unterhalb der Team-Praesenzleiste. */
.milestones-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-weight: 700;
    font-size: 13px;
    margin-bottom: 10px;
}

.milestones-settings-link {
    display: inline-flex;
    color: var(--color-text-muted);
}

.milestones-settings-link:hover {
    color: var(--color-text);
}

.milestones-tabs {
    display: flex;
    gap: 6px;
    margin-bottom: 12px;
}

.milestones-tab {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-muted);
    border-radius: var(--radius-pill, 999px);
    padding: 4px 12px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
}

.milestones-tab.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}

.milestones-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.milestones-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.milestones-dot {
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: 5px;
    background: var(--color-text-muted);
}

.milestones-dot--birthday { background: #E11D48; }
.milestones-dot--anniversary { background: #D97706; }
.milestones-dot--customer_birthday { background: #2563EB; }

.milestones-row-main {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.milestones-row-name {
    font-weight: 700;
    font-size: 12.5px;
}

.milestones-row-sub {
    font-size: 11.5px;
    color: var(--color-text-muted);
    font-weight: 400;
}

.milestones-hint {
    font-size: 11px;
    color: var(--color-accent);
    font-weight: 600;
}

/* CoreDesk Hub-Widget "Kunden-Geburtstage" (Nutzer-Wunsch 2026-08-10) - eigenstaendige Kachel neben
   Team-Praesenz/Ehrentage-&-Meilensteine/People-Widget. Heutige Geburtstage sind als hervorgehobene
   Zeilen mit Gratulieren-Aktion oben, "Anstehend" darunter wiederverwendet .milestones-list/-row
   1:1 (derselbe --customer_birthday-Dot-Ton wie im Ehrentage-Widget). */
.hub-birthday-today-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 10px;
}

.hub-birthday-today-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: 1px solid var(--color-accent);
    border-radius: var(--radius-md);
    background: var(--color-accent-soft);
}

.hub-birthday-today-row svg { flex: 0 0 auto; color: var(--color-accent); }

.hub-birthday-row-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.hub-birthday-row-name { font-weight: 700; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-birthday-row-sub { font-size: 11.5px; color: var(--color-text-muted); }

.hub-birthday-greet-btn { flex: 0 0 auto; padding: 4px 10px; font-size: 12px; }

.hub-birthday-upcoming-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-muted);
    margin: 4px 0 8px;
}

/* CoreDesk Hub-Widget "People Quick View" (nur Exec/Teamlead, siehe PeopleHubWidgetResolver) -
   wiederverwendet bewusst dieselben .milestones-header/-settings-link Klassen fuer Kopfzeile/Link,
   nur die Sektions-/Zeilen-Darstellung ist eigenstaendig (3 gestapelte Mini-Listen statt einer). */
.people-widget-sections {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.people-widget-section-title {
    display: block;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin-bottom: 6px;
}

.people-widget-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 6px 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    margin-bottom: 6px;
}

.people-widget-row:last-child {
    margin-bottom: 0;
}

a.people-widget-row--link {
    text-decoration: none;
    color: inherit;
}

a.people-widget-row--link:hover {
    border-color: var(--color-accent);
}

.people-widget-row-name {
    font-weight: 700;
    font-size: 12.5px;
}

.people-widget-row-sub {
    font-size: 11.5px;
    color: var(--color-text-muted);
    font-weight: 400;
    white-space: nowrap;
}

.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: 16px;
    margin-bottom: 24px;
}

.stat-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 18px 20px;
}

.stat-card .stat-value {
    font-size: 28px;
    font-weight: 700;
    line-height: 1.2;
}

.stat-card .stat-label {
    font-size: 12px;
    color: var(--color-text-muted);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* Buttons */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 7px 14px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
}

.btn[hidden] { display: none; }

/* Asset-Bearbeiten-Formular: "Fertig"-Link zurueck zur Ansicht (siehe assets/edit.twig) - war ein
   blanker Textlink, jetzt oberhalb der Formular-Karte wie ein regulaerer .btn abgesetzt. */
.asset-edit-topbar { margin-bottom: 12px; }

.btn:hover {
    background: var(--color-bg);
    text-decoration: none;
}

.btn-primary {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #FFFFFF;
}

.btn-primary:hover {
    background: var(--color-accent-hover);
    border-color: var(--color-accent-hover);
}

.btn-success {
    background: #38CC65;
    border-color: #38CC65;
    color: #FFFFFF;
}

.btn-success:hover {
    background: #2EB356;
    border-color: #2EB356;
}

.btn-danger {
    background: transparent;
    border-color: #C0392B;
    color: #C0392B;
}

.btn-danger:hover {
    background: rgba(192, 57, 43, 0.1);
}

/* Dezente Pill-Schaltflaechen fuer Zeilen-Aktionen (Bearbeiten/Verschieben/Loeschen in
   Leistungskarten, Kommentaren, Terminen, Admin-Listen) - global vereinheitlicht statt
   wiederholter Inline-Styles pro Stelle. */
.btn-action {
    padding: 4px 10px;
    font-size: 11px;
    font-weight: 400;
    color: var(--color-text-muted);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.05);
    transition: color 150ms ease, border-color 150ms ease;
}

.btn-action:hover {
    color: var(--color-text);
    background: var(--color-surface);
}

.btn-action--danger {
    color: var(--color-danger-text);
}

.btn-action--danger:hover {
    color: var(--color-danger-text);
    filter: brightness(0.85);
}

.flash {
    padding: 10px 14px;
    margin-bottom: 12px;
    border-radius: 8px;
    font-size: 13px;
}

.flash--success {
    background: var(--status-active-soft);
    color: var(--status-active);
}

.flash--error {
    background: rgba(192, 57, 43, 0.1);
    color: #C0392B;
}

.broadcast-severity {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
}

.broadcast-severity--info {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.broadcast-severity--warning {
    background: var(--status-warning-soft);
    color: #A66A1E;
}

.broadcast-severity--critical {
    background: var(--color-danger-bg-strong);
    color: var(--color-danger-text);
}

.flash--warning {
    background: var(--status-warning-soft);
    color: #A66A1E;
}

.flash--info {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.field-hint {
    font-size: 11.5px;
    color: var(--color-text-muted);
    margin: 2px 0 8px;
    line-height: 1.5;
}

.tdv2-assignment-banner p {
    margin: 4px 0 10px;
}

.tdv2-assignment-candidates {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.tdv2-assignment-candidate-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border-radius: var(--radius-md);
    border: 1px solid #A66A1E;
    background: #fff;
    color: #A66A1E;
    font-weight: 600;
    font-size: 13px;
    cursor: pointer;
}

.tdv2-assignment-candidate-btn:hover {
    background: var(--status-warning-soft);
}

/* Forms */
/* App-weiter Basis-Stil fuer alle Select/Input/Textarea-Elemente, die ausserhalb von .field
   liegen (z.B. Assets/Domains/Checkliste-Zeilen in der Ticket-Sidebar). Bewusst als Tag-Selektor
   mit niedriger Spezifitaet, damit .field/.info-grid/.secret-cell/.ticket-subject-input & Co.
   (alle mit Klassen-Selektoren, hoehere Spezifitaet) unveraendert ihre eigenen Regeln behalten. */
select,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="date"],
input[type="datetime-local"],
input[type="time"],
input[type="number"],
input[type="tel"],
input[type="url"],
textarea {
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 8px 10px;
    font-family: inherit;
    font-size: 13px;
    color: var(--color-text);
    background: var(--color-bg);
}

select:focus,
input[type="text"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
input[type="date"]:focus,
input[type="datetime-local"]:focus,
input[type="time"]:focus,
input[type="number"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
textarea:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

.field {
    margin-bottom: 18px;
}

.field label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    margin-bottom: 6px;
}

.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="date"],
.field input[type="time"],
.field input[type="datetime-local"],
.field input[type="number"],
.field input[type="tel"],
.field input[type="url"],
.field select,
.field textarea {
    width: 100%;
    max-width: 420px;
    padding: 8px 12px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    font-family: inherit;
    font-size: 13px;
    color: var(--color-text);
    background: var(--color-bg);
}

.field input:focus,
.field select:focus,
.field textarea:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

/* Read-only Ersatz fuer ein .field-Eingabeelement (z.B. Mailprovider im Kunden-Portal, siehe
   customer_portal/index.twig) - Kunde soll den Wert sehen, aber nicht aendern koennen (bewusst
   admin-verwaltet). Gleiche Massgeblichkeit wie die echten Inputs daneben, damit die Zeile nicht
   optisch aus der Reihe faellt, nur ohne Eingabe-Erscheinungsbild (kein Fokus-Ring, gedaempfte
   Textfarbe, Schloss-Icon als Hinweis). */
.field-locked {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    max-width: 420px;
    padding: 8px 12px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    font-size: 13px;
    color: var(--color-text-muted);
    background: var(--color-surface);
}

.field-locked svg {
    width: 13px;
    height: 13px;
    flex: 0 0 auto;
}

.field-error {
    color: #C0392B;
    font-size: 12px;
    margin-top: 6px;
}

/* App-weiter Save-Spinner fuer Formular-Buttons (Doppelklick-/Doppelbuchungsschutz) - gleiche
   Optik wie .tdv2-btn-spinner in ticket-new-design.css, aber global verfuegbar statt auf den
   Ticket-Detail-Scope beschraenkt. */
.btn-spinner { display: inline-block; width: 14px; height: 14px; border-radius: 50%; border: 2px solid rgba(255,255,255,.4); border-top-color: #fff; animation: btn-spin .6s linear infinite; flex-shrink: 0; margin-right: 6px; vertical-align: -2px; }
@keyframes btn-spin { to { transform: rotate(360deg); } }

.form-actions {
    display: flex;
    gap: 10px;
    margin-top: 20px;
    padding-top: 16px;
    border-top: 1px solid var(--color-border);
}

/* Pro-Feld-Autosave-Checkmark (Assets, siehe public/js/asset-autosave.js) - analog zu
   .tdv2-prop-saved im neuen Ticket-Design, hier aber generisch fuers Label jedes .field-Blocks. */
.asset-prop-saved {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-left: 8px;
    font-size: 10px;
    font-weight: 700;
    color: var(--color-success, #16a34a);
    opacity: 0;
    transition: opacity .3s;
}
.asset-prop-saved.show { opacity: 1; }
.asset-prop-saved svg { width: 10px; height: 10px; }

/* Sanftes Einblenden fuer dynamisch sichtbar werdende Felder (z.B. Abrechnungsmodell beim
   Typwechsel auf Service Request, siehe tickets/form.twig). */
@keyframes fieldFadeIn {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: translateY(0); }
}
.field-fade-in {
    animation: fieldFadeIn .2s ease;
}

/* App-weites Checkbox-Grundstyling - bislang nutzte jede Checkbox im Projekt den reinen
   Browser-Standard (nur "style=width:auto" inline). accent-color faerbt die native Checkbox in
   der Akzentfarbe ein, ohne dass irgendein Template angefasst werden muss. */
input[type="checkbox"] {
    width: 16px;
    height: 16px;
    accent-color: var(--color-accent);
    border-radius: 4px;
    cursor: pointer;
}

/* iOS-artiger Toggle-Switch (Referenz: td-2.html-Mockup) - nur fuer die zwei Regel-Schalter auf
   der Ticket-Detailseite ("Pauschal abrechnen"/"Kunden-E-Mail Benachrichtigung"), NICHT als
   App-weiter Ersatz fuer normale Checkboxen (Checkliste, "Ohne Berechnung" etc. bleiben normale
   Checkboxen). Reiner CSS-Sibling-Trick (versteckte Checkbox + gestylter Sibling-Track), kein JS
   noetig - analog zum bereits etablierten Peer-Checked-Muster aus dem Mockup, nur ohne Tailwind. */
.toggle-switch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    cursor: pointer;
}

/* Ueberschreibt ".field label { display:block; ... }" (Zeile ~1826) - dessen Element+Klasse-
   Selektor ist spezifischer als die einfache ".toggle-switch"-Klasse allein, wodurch ein
   Toggle-Switch innerhalb eines ".field"-Wrappers (z.B. admin/absence_types.twig) sonst platt
   auf display:block faellt und der Schalter unsichtbar/funktionslos wirkt. */
.field label.toggle-switch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0;
}

.toggle-switch input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.toggle-switch-track {
    position: relative;
    flex-shrink: 0;
    width: 36px;
    height: 20px;
    background: var(--color-border);
    border-radius: 999px;
    transition: background 150ms ease;
}

.toggle-switch-track::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 16px;
    height: 16px;
    background: var(--color-surface);
    border-radius: 50%;
    box-shadow: 0 1px 2px rgba(16, 24, 40, 0.15);
    transition: transform 150ms ease;
}

.toggle-switch input:checked ~ .toggle-switch-track {
    background: var(--color-accent);
}

.toggle-switch input:checked ~ .toggle-switch-track::after {
    transform: translateX(16px);
}

.toggle-switch input:focus-visible ~ .toggle-switch-track {
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

/* Pillenfoermiges, farbiges Select fuer Status/Prioritaet (Referenz: td-2.html-Mockup) -
   nutzt dieselben Farbtoken wie .badge--{status}/.badge--priority_{prio} (siehe weiter unten),
   damit keine neuen Farben eingefuehrt werden muessen. Modifier-Klasse wird serverseitig anhand
   des aktuellen Werts gesetzt und bei jedem onchange per JS aktualisiert (siehe tickets/show.twig),
   reine Optik - Formularverhalten (name=/onchange=toggleHoldReasonField) bleibt unveraendert.
   select.select-pill statt nur .select-pill, um zuverlaessig ueber die allgemeineren
   .field select/.info-grid select-Regeln zu gewinnen (hoehere Spezifitaet). */
select.select-pill {
    display: inline-block;
    width: auto;
    max-width: none;
    border: 1px solid transparent;
    border-radius: 999px;
    padding: 4px 28px 4px 12px;
    font-size: 12px;
    font-weight: 700;
    cursor: pointer;
}

select.select-pill.select-pill--open,
select.select-pill.select-pill--priority_normal { background: var(--color-accent-soft); color: var(--color-accent); border-color: var(--color-accent-soft); }
select.select-pill.select-pill--in_progress { background: var(--status-active-soft); color: var(--status-active); border-color: var(--status-active-soft); }
select.select-pill.select-pill--waiting_customer { background: var(--status-warning-soft); color: #A66A1E; border-color: var(--status-warning-soft); }
select.select-pill.select-pill--closed { background: var(--status-neutral-soft); color: var(--status-neutral); border-color: var(--status-neutral-soft); }
select.select-pill.select-pill--on_hold { background: var(--status-hold-soft); color: var(--status-hold); border-color: var(--status-hold-soft); }
select.select-pill.select-pill--callback_requested { background: var(--status-callback-soft); color: var(--status-callback); border-color: var(--status-callback-soft); }
select.select-pill.select-pill--priority_urgent { background: var(--color-critical-soft); color: var(--color-critical); border-color: var(--color-critical-soft); }
select.select-pill.select-pill--priority_high { background: var(--color-danger-bg-strong); color: var(--color-danger-text); border-color: var(--color-danger-bg-strong); }
select.select-pill.select-pill--priority_low { background: var(--status-neutral-soft); color: var(--status-neutral); border-color: var(--status-neutral-soft); }
/* CoreDesk Campaigns Hub-Matrix (Phase 3) - pending/skipped reusen den neutralen Ticket-Ton,
   done reust den aktiven/gruenen Ton (--in_progress ist bereits oben definiert und passt 1:1). */
select.select-pill.select-pill--pending,
select.select-pill.select-pill--skipped { background: var(--status-neutral-soft); color: var(--status-neutral); border-color: var(--status-neutral-soft); }
select.select-pill.select-pill--done { background: var(--status-active-soft); color: var(--status-active); border-color: var(--status-active-soft); }

select.select-pill:focus {
    outline: none;
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

/* Formatierungshinweis ueber Textareas, deren Inhalt tatsaechlich per Markdown.php gerendert
   wird (Wiki-Seiten, Changelog-Beschreibung, Zeiterfassungs-Kundennotiz) - bewusst NICHT auf
   jeder Textarea in der App, da die meisten Freitextfelder nirgends als Markdown geparst werden
   und der Hinweis dort irrefuehrend waere. */
.markdown-hint {
    font-size: 12px;
    color: var(--color-text-muted);
    margin: -2px 0 6px;
}

/* Dezenter Info-Hinweis (siehe _time_entry_fields.twig: Pausen-Sync-Hinweis) - wiederverwendbare
   generische Klasse statt Tailwind-Utility-Klassen (CoreDesk nutzt kein Tailwind), nutzt die
   bestehende Akzentfarbe fuer einen "informativ, nicht warnend"-Ton. */
.info-badge {
    display: flex;
    align-items: center;
    gap: 6px;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    border-radius: 6px;
    padding: 8px 12px;
    font-size: 12px;
    margin: 6px 0 14px;
}

.info-badge .icon {
    flex-shrink: 0;
}

/* Ticket-Beschreibung: bewusst als eigene Klasse statt ".field textarea" global zu aendern -
   die gemeinsame Regel gilt auch fuer Kunden/Projekte/Netzwerke/Assets-Formulare und soll dort
   bei max-width:420px bleiben. Die Beschreibung ist meist der laengste Freitext im Ticket-
   Formular und soll die volle Card-Breite nutzen statt gegenueber der Betreffzeile optisch
   abzufallen. */
.field textarea.field-textarea-full {
    max-width: none;
}

.field-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 16px;
}

/* [hidden] hat dieselbe Selektor-Spezifitaet wie .field-grid - ohne diese Regel gewinnt
   "display:grid" den Kaskaden-Gleichstand (Autor-Regeln stehen im Stylesheet nach der
   UA-Standardregel) und die Custom-Zeitraum-Felder blieben trotz hidden-Attribut sichtbar
   (Kunden-Servicebericht-Modal, Nutzer-Wunsch 2026-08-09). */
.field-grid[hidden] {
    display: none;
}

.field-grid .field input,
.field-grid .field select {
    max-width: none;
}

.field-section-title {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin: 4px 0 14px;
    padding-top: 14px;
    border-top: 1px solid var(--color-border);
}

.field-section-title:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

@media (max-width: 640px) {
    .field-grid {
        grid-template-columns: 1fr;
    }
}

/* Netzwerk-Formular: Smart-Toggle-Modus (Vorlagen-Auswahl -> Automatisch/Manuell) +
   Auto-Berechnung-Zubehoer (Reset-Button, "automatisch ermittelt"-Hinweis neben Feld-Labels).
   Generisch genug fuer .field-Formulare, aber bisher nur von templates/networks/_fields.twig
   genutzt. */
.field-hint {
    margin: 4px 0 0;
    font-size: 12px;
    color: var(--color-text-muted);
}

.network-auto-summary {
    margin-bottom: 20px;
    padding: 14px 16px;
    border-radius: var(--radius-md, 8px);
    background: var(--color-accent-soft, rgba(37, 99, 235, 0.06));
    border: 1px solid var(--color-border);
}

.network-auto-summary-title {
    margin: 0 0 8px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

.network-auto-summary-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 4px 0;
    font-size: 13px;
}

.network-auto-summary-label {
    color: var(--color-text-muted);
}

#network-detail-fields-wrapper {
    transition: opacity 0.2s ease;
}

#network-detail-fields-wrapper[hidden] {
    display: none;
}

.field-section-title-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
}

.field-section-title-row .field-section-title {
    flex: 1;
}

.network-reset-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: none;
    border: none;
    color: var(--color-text-muted);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    padding: 0 0 14px;
    white-space: nowrap;
}

.network-reset-btn:hover {
    color: var(--color-accent);
}

.field-label-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-bottom: 6px;
}

.field-label-row label {
    margin-bottom: 0;
}

/* Label + "i"-Info-Button als eine Gruppe links, damit der Button unmittelbar hinter dem
   Label-Text steht statt (space-between-bedingt) am rechten Rand neben einem Auto-Hinweis. */
.field-label-with-info {
    display: flex;
    align-items: center;
    gap: 6px;
}

.field-auto-hint {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    font-weight: 600;
    color: var(--color-accent);
    white-space: nowrap;
}

/* Ohne diese Regel gewinnt die obige Klassen-Regel gegen das [hidden]-Attribut (gleiche
   Spezifitaet, aber die Klassen-Regel steht spaeter in der Kaskade) - der Hinweis waere dadurch
   staendig sichtbar statt nur nach einer echten Auto-Befuellung. */
.field-auto-hint[hidden] {
    display: none;
}

/* "i"-Info-Button neben einem Feld-Label - oeffnet ueber public/js/avatar-tooltip.js denselben
   reichhaltigen Hover-Tooltip wie Avatare (.field-info-trigger/.field-info-content, siehe dort).
   Pilot am VLAN/Port-Feld im Netzwerk-Formular, generisch genug fuer weitere Felder. */
.field-info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    color: var(--color-text-muted);
    cursor: help;
    padding: 0;
    flex-shrink: 0;
}

.field-info-btn:hover,
.field-info-btn:focus-visible {
    border-color: var(--color-accent);
    color: var(--color-accent);
    outline: none;
}

.field-info-btn .icon-xs {
    width: 11px;
    height: 11px;
}

.field-info-content {
    display: none;
}

.field-info-content strong {
    display: block;
    margin-bottom: 3px;
}

/* Dezenter Verweis auf das passende CoreDesk-Academy-Kapitel - bislang nur auf den Netzwerk-
   Seiten genutzt (siehe Feedback-Memory "Maintain Academy on every change"), generisch genug um
   spaeter auf weiteren Modul-Seiten neben dem Zurueck-Link zu erscheinen. */
.academy-help-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-left: 14px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.academy-help-link:hover {
    color: var(--color-accent);
}

.academy-help-link .icon-xs {
    width: 13px;
    height: 13px;
}

/* Wie .field-grid, aber fuer eine variable Anzahl Felder (2-3) statt fix 2 Spalten - genutzt vom
   Kunden-Self-Service-Portal (Stammdaten-Aktualisierung), wo Zeilen mal 2, mal 3 Felder haben. */
.field-row {
    display: flex;
    gap: 0 16px;
}

.field-row .field {
    flex: 1;
    min-width: 0;
}

.field-row .field input,
.field-row .field select {
    max-width: none;
}

@media (max-width: 640px) {
    .field-row {
        flex-direction: column;
        gap: 0;
    }
}

/* Gestylter Ersatz fuer <input type="file"> (Nutzer-Feedback: nativer Datei-Button passt nicht ins
   Design) - der eigentliche <input type="file" hidden> bleibt unveraendert (Formular-Semantik,
   Server sieht ganz normal $_FILES), dieses <label for="..."> daneben triggert per nativer
   Browser-Semantik den Datei-Dialog beim Klick, kein JS dafuer noetig. Nur der angezeigte
   Dateiname (.file-picker-name) wird per kleinem "change"-Listener aktualisiert. */
.file-picker {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    width: 100%;
    max-width: 420px;
    padding: 8px 12px;
    border: 1px dashed var(--color-border);
    border-radius: 8px;
    background: var(--color-bg);
    font-size: 13px;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: border-color 150ms ease, background 150ms ease;
}

.file-picker:hover,
.file-picker:focus-within {
    border-color: var(--color-accent);
    background: var(--color-accent-tint);
}

.file-picker-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.file-picker-name.has-file {
    color: var(--color-text);
    font-weight: 600;
}

.file-picker-browse {
    flex: 0 0 auto;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 4px 8px;
}

/* Logo + "Logo ersetzen" in einer Tabellenzelle verschmolzen (Nutzer-Feedback: Zeile mit separater
   Upload-Spalte zu ueberladen) - Kamera-Overlay erscheint nur bei :hover/:focus-within, dahinter
   liegt derselbe <label for="hidden-file-input">-Trick wie bei .file-picker, hier aber onchange
   direkt this.form.submit() statt nur den Dateinamen anzuzeigen (Klick = sofortiger Ersatz, kein
   zweiter Bestaetigungs-Klick auf einen separaten "Hochladen"-Button noetig). */
.isp-logo-cell {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 8px;
    background: var(--color-surface-alt, var(--color-bg));
    border: 1px solid var(--color-border);
    overflow: hidden;
}

.isp-logo-cell-img {
    width: 32px;
    height: 32px;
    object-fit: contain;
}

.isp-logo-cell-fallback {
    color: var(--color-text-muted);
    display: flex;
}

.isp-logo-cell-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    width: 100%;
    height: 100%;
    cursor: pointer;
}

.isp-logo-cell-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 42, 0.6);
    color: #fff;
    opacity: 0;
    cursor: pointer;
    transition: opacity 150ms ease;
    pointer-events: none;
}

.isp-logo-cell:hover .isp-logo-cell-overlay,
.isp-logo-cell:focus-within .isp-logo-cell-overlay {
    opacity: 1;
}

/* Eine Ansprechpartner-Kachel im Kunden-Self-Service-Portal (bestehend oder neu hinzugefuegt,
   Nutzer-Wunsch 2026-08-12: eigene Kachel statt eines zusammenhaengenden Blocks) - durch eine
   Trennlinie optisch abgesetzt, damit bei mehreren Ansprechpartnern klar ist, wo eine Kachel
   endet und die naechste beginnt. .is-marked-for-removal (siehe wireDepartToggle() im Template)
   faerbt die Kachel rot ein, sobald der "Ausgeschieden"-Button geklickt wurde - Kunde sieht sofort,
   welche Kachel zur Loeschung vorgemerkt ist, ohne dass server-seitig etwas geloescht wird (die
   Einreichung landet wie jede andere Aenderung erst als 'pending'-Antrag zur Admin-Freigabe).
   Farbabstufungen ueber dieselben --color-danger-*-Tokens wie sonst in der App (z.B.
   .status-dot--danger), keine neuen Rot-Werte erfunden. */
.portal-contact-row {
    padding: 16px 0;
    border-top: 1px solid var(--color-border);
    border-radius: 8px;
    transition: background-color .15s ease;
}

.portal-contact-row:first-child {
    padding-top: 0;
    border-top: none;
}

.portal-contact-row.is-marked-for-removal {
    background: var(--color-danger-bg);
    padding: 12px 12px 16px;
    border-top-color: transparent;
}

.portal-contact-row.is-marked-for-removal .portal-contact-row-fields {
    opacity: .55;
    pointer-events: none;
}

.portal-contact-row-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.portal-contact-row-name {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 14px;
    font-weight: 700;
    color: var(--color-text);
}

.portal-contact-row-name svg {
    width: 14px;
    height: 14px;
    color: var(--color-text-muted);
    flex: 0 0 auto;
}

.portal-contact-badge {
    display: inline-flex;
    align-items: center;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .02em;
}

.portal-contact-depart-btn {
    flex: 0 0 auto;
    font-size: 12.5px;
    color: var(--color-danger-text);
    border-color: var(--color-danger-border);
}

.portal-contact-depart-btn:hover {
    background: var(--color-danger-bg-strong);
}

.portal-contact-row-marked-hint {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 10px;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--color-danger-text);
}

/* Ohne diese Regel gewinnt der obige Klassen-Selektor (hoehere Spezifitaet) gegen das
   [hidden]-Attribut - die Zeile waere dadurch permanent sichtbar statt nur nach Klick auf den
   Ausgeschieden-Button (Nutzer-Fund beim eigenen Testrender 2026-08-12). */
.portal-contact-row-marked-hint[hidden] {
    display: none;
}

.portal-contact-row-marked-hint svg {
    width: 13px;
    height: 13px;
    flex: 0 0 auto;
}

/* Betreiber-Hinweisbadge im Portal-Header (Nutzer-Wunsch 2026-08-12: "Bereitgestellt ueber Ihr
   IT-Service-Portal") - dezent gehalten (kein Akzentton), damit der Firmenname des IT-Systemhauses
   davor die visuelle Hauptrolle behaelt. */
.portal-operator-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 10px;
    border-radius: 999px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    font-size: 11px;
    font-weight: 600;
}

/* Redesign der Stammdaten-Aktualisierungsseite (Nutzer-Wunsch 2026-08-13): eigenstaendige
   Header-Card statt schmaler Textzeile, Info-Box mit Pruefhinweis, 2-spaltiges Grid fuer
   Firmenprofil + IT-Infrastruktur und ein fixierter Sticky-Footer fuer die Absende-Aktion - vorher
   wirkte die Seite durch starr gestapelte volle-Breite-Kacheln unuebersichtlich lang. */
.portal-header-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 16px;
}

.portal-header-card-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 4px;
}

.portal-header-logo {
    max-height: 32px;
    max-width: 200px;
    object-fit: contain;
}

.portal-header-card-title {
    margin: 0;
    font-size: 20px;
    font-weight: 700;
}

.portal-header-card-customer {
    margin: 0;
    color: var(--color-accent);
    font-size: 14px;
    font-weight: 600;
}

.portal-info-box {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin-bottom: 20px;
    padding: 12px 14px;
    border-radius: var(--radius-md);
    background: var(--color-accent-soft);
    color: var(--color-text);
}

.portal-info-box svg {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    margin-top: 1px;
    color: var(--color-accent);
}

.portal-info-box p {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.5;
}

.portal-form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-items: start;
    gap: 16px;
    margin-bottom: 16px;
}

@media (max-width: 860px) {
    .portal-form-grid {
        grid-template-columns: 1fr;
    }
}

/* Dashed-Card statt schlichtem Button (Nutzer-Wunsch 2026-08-13) - liest sich als "Platzhalter am
   Listenende" statt als beilaeufige Formular-Aktion, aehnlich einer Datei-Upload-Dropzone. */
.portal-add-contact-card {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    margin-top: 12px;
    padding: 12px;
    border: 1.5px dashed var(--color-border);
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-text-muted);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: border-color .15s ease, color .15s ease, background-color .15s ease;
}

.portal-add-contact-card:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
    background: var(--color-accent-soft);
}

/* Fixierte Absende-Leiste (Nutzer-Wunsch 2026-08-13) - der Submit-Button geht bei einem langen
   Formular (viele Ansprechpartner) sonst leicht "aus dem Blick"; extra Bottom-Padding auf
   .portal-content-wrap (siehe Template) haelt den letzten Karteninhalt frei von der Leiste. */
.portal-sticky-footer {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    background: var(--color-surface);
    border-top: 1px solid var(--color-border);
    box-shadow: 0 -4px 16px rgba(0, 0, 0, .08);
}

.portal-sticky-footer-inner {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    max-width: 1040px;
    margin: 0 auto;
    padding: 14px 16px;
}

/* Arbeitsbereich-Wechsler im Kunden-Self-Service-Portal (Kontext-Wechsel fuer externe
   Dienstleister mit mehreren Firmenzugaengen, Migration 166/168) - eigene, schlanke Popover-
   Styles statt der tdv2-impact-* Klassen, da customer_portal/index.twig nur app.css laedt und
   nicht ticket-new-design.css. */
.portal-workspace-switch-wrap {
    position: relative;
    display: inline-block;
}

.portal-workspace-switch-popover {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    min-width: 240px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    z-index: 60;
    padding: 6px;
}

.portal-workspace-switch-wrap.is-open .portal-workspace-switch-popover {
    display: block;
}

.portal-workspace-switch-item {
    display: block;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    color: var(--color-text);
    text-decoration: none;
}

.portal-workspace-switch-item:hover {
    background: var(--color-surface-hover, rgba(0, 0, 0, 0.04));
}

.form-error-banner {
    background: rgba(192, 57, 43, 0.08);
    border: 1px solid rgba(192, 57, 43, 0.25);
    color: #C0392B;
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    margin-bottom: 18px;
}

.inline-form {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Tables */
table.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}

table.data-table th {
    text-align: left;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    font-weight: 600;
    padding: 9px 14px;
    border-bottom: 1px solid var(--color-border);
}

table.data-table td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--color-border);
}

/* Zebra-Streifen (jede zweite Zeile leicht eingefaerbt, analog Personio) - MUSS vor der
   :hover-Regel darunter stehen: bei gleicher Selektor-Spezifitaet gewinnt die spaeter im
   Stylesheet stehende Regel, Hover soll auch auf geraden Zeilen sichtbar bleiben. */
table.data-table tbody tr:nth-child(even) {
    background: var(--color-accent-tint);
}

table.data-table tbody tr:hover {
    background: var(--color-bg);
}

/* Ehemalige Mitarbeiter (contacts.is_active = 0, Migration 139) bleiben in der Kontaktliste sichtbar,
   werden aber optisch zurueckgenommen statt kommentarlos zu verschwinden. */
table.data-table tbody tr.is-muted {
    opacity: 0.55;
}

table.data-table tbody tr:last-child td {
    border-bottom: none;
}

.empty-state {
    color: var(--color-text-muted);
    padding: 20px 0;
}

/* Nutzer-Fund 2026-08-17 beim UniFi-Redesign von Einstellungen & Profil: .text-muted wird app-weit
   auf Hunderten Hint-/Subtitle-Texten verwendet, hatte aber nirgends eine tatsaechliche Basisregel
   - der Text erbte bislang stillschweigend die normale Textfarbe statt gedaempft zu wirken. Reiner
   Bugfix (die Klasse war seit jeher als "gedaempft" gedacht), kein neues Verhalten. */
.text-muted {
    color: var(--color-text-muted);
}

/* Projekte-Widget im Context-Dock: feste Spaltenbreiten statt Auto-Layout, sonst zieht die
   ungebremste Budget-Spalte den gesamten Restplatz an sich (laeuft bis zum Kartenrand) waehrend
   Projekt/Kunde per altem col-truncate-Trick (max-width:1px) auf wenige Pixel zusammengequetscht
   werden - der Link-Text wird dann so stark abgeschnitten, dass er nicht mehr wie ein Link
   aussieht. Mit table-layout:fixed bestimmen diese Breiten die Spalten unabhaengig vom Inhalt. */
.dashboard-project-table {
    table-layout: fixed;
    width: 100%;
}

.dashboard-project-table th,
.dashboard-project-table td {
    padding: 8px 10px;
    font-size: 12px;
}

.dashboard-project-table th:nth-child(1),
.dashboard-project-table td:nth-child(1) {
    width: 38%;
}

.dashboard-project-table th:nth-child(2),
.dashboard-project-table td:nth-child(2) {
    width: 30%;
}

.dashboard-project-table th:nth-child(3),
.dashboard-project-table td:nth-child(3) {
    width: 32%;
}

.dashboard-project-table .col-truncate {
    max-width: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Status badges */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.badge .icon {
    width: 12px;
    height: 12px;
}

.badge .badge-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
}

.badge--open { background: var(--color-accent-soft); color: var(--color-accent); }
.badge--open .badge-dot { background: var(--color-accent); }

.badge--in_progress { background: var(--status-active-soft); color: var(--status-active); }
.badge--in_progress .badge-dot { background: var(--status-active); }

.badge--waiting_customer { background: var(--status-warning-soft); color: #A66A1E; }
.badge--waiting_customer .badge-dot { background: var(--status-warning); }

.badge--closed { background: var(--status-neutral-soft); color: var(--status-neutral); }
.badge--closed .badge-dot { background: var(--status-neutral); }

.badge--on_hold { background: var(--status-hold-soft); color: var(--status-hold); }
.badge--on_hold .badge-dot { background: var(--status-hold); }

/* Ultra-kompakte Status-Spalte (Nutzer-Feedback 2026-08-05, "STAT"-Spalte nimmt zu viel
   horizontalen Platz ein, Linear.app-Vorbild): Icon-Kreis statt breitem Text-Badge, Farben aus
   denselben --status-*-Tokens wie .badge--* oben uebernommen (keine neue Palette). Tooltip via
   natives title-Attribut am Element - dieselbe Konvention wie ueberall sonst in der App, kein
   neues Tooltip-System noetig. Wiederverwendet in tickets/index.twig UND projects/_table_view.twig. */
.status-icon-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 999px;
    flex-shrink: 0;
    cursor: help;
}

.status-icon-badge--open { background: var(--color-accent-soft); color: var(--color-accent); }
.status-icon-badge--in_progress { background: var(--status-active-soft); color: var(--status-active); }
.status-icon-badge--waiting_customer { background: var(--status-warning-soft); color: #A66A1E; }
.status-icon-badge--closed { background: var(--status-neutral-soft); color: var(--status-neutral); }
.status-icon-badge--on_hold { background: var(--status-hold-soft); color: var(--status-hold); }
.status-icon-badge--callback_requested { background: var(--status-callback-soft); color: var(--status-callback); }

/* Admin > E-Mail-Log (Migration 199) - Versand-Status je Zeile */
.badge--sent { background: var(--status-active-soft); color: var(--status-active); }
.badge--sent .badge-dot { background: var(--status-active); }

.badge--failed { background: var(--color-critical-soft); color: var(--color-critical); }
.badge--failed .badge-dot { background: var(--color-critical); }

/* Phasen-Abrechnungs-Typ (Anforderung "Abrechnungs-Typ: Dropdown/Badge") - dezente Farbunterscheidung
   Pauschal (fester Betrag, warmer Ton) vs. Nach Aufwand (variabel, neutraler Akzentton). */
.badge--flat_rate { background: var(--status-warning-soft); color: #A66A1E; }
.badge--hourly { background: var(--color-accent-soft); color: var(--color-accent); }
.badge--hybrid { background: var(--status-callback-soft); color: var(--status-callback); }

/* Domain & Infrastructure Asset Manager: dringende (<=7 Tage) Ablauf-Warnung braucht einen
   staerkeren Rotton als das generische badge--closed - reuse des bereits vorhandenen "harte
   SLA-Verletzung"-Tokens statt einer neuen Farbe. */
.badge--domain_urgent { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }
.badge--domain_urgent .badge-dot { background: var(--color-danger-text); }

/* CMS-Typ-Pills: farbcodierte Text-Badges statt Marken-Logos (kein neues SVG-Asset noetig,
   siehe Domain-Asset-Manager-Plan). badge-dot braucht wie bei allen anderen Badge-Typen eine
   eigene Hintergrundfarbe - sonst bleibt der Punkt transparent/unsichtbar. */
.badge--cms-wordpress { background: rgba(5, 89, 201, 0.10); color: var(--color-accent); }
.badge--cms-wordpress .badge-dot { background: var(--color-accent); }
.badge--cms-typo3 { background: var(--status-warning-soft); color: #A66A1E; }
.badge--cms-typo3 .badge-dot { background: #A66A1E; }
.badge--cms-shopify { background: var(--status-active-soft); color: var(--status-active); }
.badge--cms-shopify .badge-dot { background: var(--status-active); }
.badge--cms-other { background: var(--status-neutral-soft); color: var(--status-neutral); }
.badge--cms-other .badge-dot { background: var(--status-neutral); }

/* Eigene Infrastruktur vs. Kunden-Hosting bei Dritten - gleiche Farblogik wie die CMS-Pills. */
.badge--hosting-own { background: rgba(5, 89, 201, 0.10); color: var(--color-accent); }
.badge--hosting-external { background: var(--status-warning-soft); color: #A66A1E; }

/* Server-Architektur-Unterklassifizierung (Bare Metal / VM / Cloud-Managed, Migration 192) -
   gleiche farbcodierte Text-Pill-Konvention wie CMS/Hosting oben. */
.badge--arch-bare_metal { background: rgba(5, 89, 201, 0.10); color: var(--color-accent); }
.badge--arch-bare_metal .badge-dot { background: var(--color-accent); }
.badge--arch-virtual_machine { background: var(--status-active-soft); color: var(--status-active); }
.badge--arch-virtual_machine .badge-dot { background: var(--status-active); }
.badge--arch-cloud_managed { background: var(--status-callback-soft); color: var(--status-callback); }
.badge--arch-cloud_managed .badge-dot { background: var(--status-callback); }

/* Generische Ampel-Badges (gut/mittel/kritisch/neutral) fuer Status-Uebersichtsseiten wie
   ITIL-Status - bewusst nicht domain-spezifisch benannt, damit sie auch anderswo (Gap-Analysen,
   Reifegrad-Checks) wiederverwendbar sind. */
.badge--good { background: var(--status-active-soft); color: var(--status-active); }
.badge--good .badge-dot { background: var(--status-active); }
.badge--warn { background: var(--status-warning-soft); color: #A66A1E; }
.badge--warn .badge-dot { background: var(--status-warning); }
.badge--critical { background: var(--color-critical-soft); color: var(--color-critical); }
.badge--critical .badge-dot { background: var(--color-critical); }
.badge--neutral { background: var(--status-neutral-soft); color: var(--status-neutral); }
.badge--neutral .badge-dot { background: var(--status-neutral); }

/* Randlose Status-Tabelle (z.B. ITIL-Status): nur horizontale Trennlinien statt Gitter, damit sie
   auf Mobilgeraeten nicht per Scroll-Krampf gelesen werden muss - schmale Spalten bleiben nowrap,
   die Beschreibungsspalte bricht stattdessen mehrzeilig um. */
table.itil-table { width: 100%; border-collapse: collapse; font-size: 13px; }
table.itil-table th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); font-weight: 600; padding: 10px 14px; border-bottom: 1px solid var(--color-border); }
table.itil-table td { padding: 12px 14px; border-bottom: 1px solid var(--color-border); vertical-align: top; line-height: 1.5; }
table.itil-table tbody tr:last-child td { border-bottom: none; }
table.itil-table tbody tr:hover { background: var(--color-bg); }
table.itil-table td.itil-col-narrow { white-space: nowrap; width: 1%; }
table.itil-table td.itil-col-title { font-weight: 600; white-space: nowrap; width: 1%; }
table.itil-table td.itil-col-body { color: var(--color-text-muted); }

@media (max-width: 700px) {
    table.itil-table thead { display: none; }
    table.itil-table, table.itil-table tbody, table.itil-table tr, table.itil-table td { display: block; width: auto; }
    table.itil-table tr { padding: 12px 0; border-bottom: 1px solid var(--color-border); }
    table.itil-table tbody tr:last-child { border-bottom: none; }
    table.itil-table td { border-bottom: none; padding: 3px 0; white-space: normal; width: auto; }
    table.itil-table td.itil-col-title { font-size: 14px; }
    table.itil-table td.itil-col-body::before { content: attr(data-label); display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); margin-top: 6px; margin-bottom: 2px; }
}

/* Generischer Opt-in-Modifier fuer beliebige .data-table-Instanzen (z.B. Asset-Sync-Betriebslog):
   auf schmalen Viewports werden Zeilen zu gestapelten Karten statt seitwaerts zu scrollen. Bewusst
   NICHT global auf .data-table angewendet (78 Templates nutzen die Klasse) - nur Tabellen mit
   dieser Zusatzklasse + data-label-Attributen auf den <td>s bekommen das Verhalten. */
@media (max-width: 700px) {
    table.data-table--stack-mobile thead { display: none; }
    table.data-table--stack-mobile, table.data-table--stack-mobile tbody, table.data-table--stack-mobile tr, table.data-table--stack-mobile td { display: block; width: auto; }
    table.data-table--stack-mobile tr { padding: 12px 0; border-bottom: 1px solid var(--color-border); }
    table.data-table--stack-mobile tbody tr:last-child { border-bottom: none; }
    table.data-table--stack-mobile td { border-bottom: none; padding: 3px 0; white-space: normal; }
    table.data-table--stack-mobile td:first-child { font-weight: 600; font-size: 14px; }
    table.data-table--stack-mobile td:not(:first-child)::before { content: attr(data-label); display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--color-text-muted); margin-top: 6px; margin-bottom: 2px; }
}

/* Segmented control fuer die Architektur-Auswahl im Asset-Formular (bare_metal/virtual_machine/
   cloud_managed) - kleines, generisches 3-Optionen-Umschalter-Widget im UniFi-Stil. */
.segmented-control {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 8px);
    overflow: hidden;
    background: var(--color-surface-alt, #f1f5f9);
}
.segmented-option {
    border: none;
    background: transparent;
    padding: 6px 14px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background .15s ease, color .15s ease;
}
.segmented-option + .segmented-option { border-left: 1px solid var(--color-border); }
.segmented-option:hover { color: var(--color-text); }
.segmented-option.is-active { background: var(--color-accent); color: #fff; }

/* Emphasized Host-Auswahl-Feld, wenn Architektur=virtual_machine ("Laeuft auf Hypervisor/Host"
   ist dann das wichtigste Feld der ganzen Sektion). */
.is-emphasized .field label { color: var(--color-accent); font-weight: 700; }
.is-emphasized select, .is-emphasized input { border-color: var(--color-accent); }

/* Child-VM-Dashboard: dunkle "Console-Box" fuer die aggregierten VM-Kennzahlen (Gesamt-VMs,
   Summe vCPU, Summe vRAM) auf einem Bare-Metal-Host, UniFi-Dashboard-Stil. */
.asset-vm-totals-box {
    display: flex;
    gap: 24px;
    background: var(--color-console-bg, #0f172a);
    border-radius: var(--radius-md, 8px);
    padding: 16px 20px;
    margin-bottom: 16px;
}
.asset-vm-totals-item { display: flex; flex-direction: column; gap: 2px; }
.asset-vm-totals-value { font-size: 22px; font-weight: 700; color: #fff; font-variant-numeric: tabular-nums; }
.asset-vm-totals-label { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: rgba(255,255,255,.6); }

/* ============================================================================
   Domain-Tabelle -> Mobile-Karten (Table-to-Card-Hybrid, Phone-Breakpoint gem.
   Breakpoint-Vertrag oben in dieser Datei: max-width 599px). Die Desktop-Tabelle
   bleibt strukturell unveraendert und nur per CSS ausgeblendet (nicht aus dem DOM
   entfernt) - dadurch funktioniert der bestehende tr[data-domain-id="..."]-
   Selektor in openDomainModal() (templates/domains/index.twig) unveraendert auch
   beim Bearbeiten ueber eine Mobil-Karte, kein JS-Umbau noetig.
   ============================================================================ */
.domain-mobile-list { display: none; }

@media (max-width: 599px) {
    .domain-split-view { display: none; }

    .domain-mobile-list {
        display: flex;
        flex-direction: column;
        gap: 12px;
    }

    .domain-mobile-card {
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-md);
        padding: 14px;
    }

    .dmc-head {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: 10px;
        padding-bottom: 10px;
        margin-bottom: 10px;
        border-bottom: 1px solid var(--color-border);
    }

    .dmc-domain-name {
        font-size: 15px;
        font-weight: 700;
        color: var(--color-text);
        max-width: 62%;
    }

    .dmc-grid {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 14px;
        font-size: 12.5px;
    }

    .dmc-col { min-width: 0; }

    .dmc-label {
        display: block;
        font-size: 10.5px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: .03em;
        color: var(--color-text-muted);
        margin-bottom: 3px;
    }

    .dmc-val {
        display: block;
        font-weight: 500;
        color: var(--color-text);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .dmc-vault-link {
        display: inline-flex;
        align-items: center;
        gap: 5px;
        min-height: 44px;
        color: var(--color-accent);
        text-decoration: none;
        font-weight: 500;
    }

    .dmc-vault-link:hover { text-decoration: underline; }

    .dmc-accordion { margin-top: 14px; }

    .dmc-accordion-toggle {
        width: 100%;
        min-height: 44px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        background: var(--color-bg);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-sm);
        padding: 0 12px;
        font-size: 12px;
        font-weight: 600;
        color: var(--color-text-muted);
        cursor: pointer;
    }

    .dmc-accordion-toggle .icon { transition: transform .15s ease; }
    .domain-mobile-card.is-expanded .dmc-accordion-toggle .icon { transform: rotate(180deg); }

    /* Animiertes Auf-/Zuklappen ueber CSS-Grid-Trick (0fr -> 1fr), gleiches Muster wie
       .inside-crm-card-body-outer in der CoreDesk-Inside-Mobilansicht. */
    .dmc-accordion-body {
        display: grid;
        grid-template-rows: 0fr;
        transition: grid-template-rows .22s ease;
    }

    .domain-mobile-card.is-expanded .dmc-accordion-body {
        grid-template-rows: 1fr;
    }

    .dmc-accordion-body > .dmc-accordion-inner {
        overflow: hidden;
        min-height: 0;
    }

    .dmc-dns-row {
        display: flex;
        justify-content: space-between;
        gap: 8px;
        padding: 9px 12px;
        font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
        font-size: 12px;
        color: var(--color-text);
    }

    .dmc-dns-row:not(:last-child) { border-bottom: 1px solid var(--color-border); }
    .dmc-dns-key { color: var(--color-text-muted); font-family: inherit; }

    .dmc-actions {
        display: flex;
        gap: 8px;
        margin-top: 12px;
    }

    .dmc-actions .btn {
        flex: 1;
        min-height: 44px;
        justify-content: center;
    }

    .dmc-delete-form { flex: 1; display: flex; }
    .dmc-delete-form .btn { width: 100%; }
}

/* ============================================================================
   Domain-Uebersicht -> Master-Detail Split View (Tablet/Desktop, UniFi-Stil).
   Ersetzt die vorherige breite Tabelle mit vielen Spalten + horizontalem
   Scrollen: die Master-Tabelle links zeigt nur noch 4 Kernspalten (Domain/
   Registrar/CMS/Ablaufdatum, ohne Scrollbalken), alle Tiefeninformationen
   wandern in ein Detail-Panel rechts, das per Klick auf eine Zeile befuellt
   wird (siehe selectDomainRow() in templates/domains/index.twig - liest die
   bereits vorhandenen data-*-Attribute der <tr>, kein Fetch noetig). Ab
   1024px (Breakpoint-Vertrag) steht das Detail-Panel sticky daneben, darunter
   (600-1023px) stapelt es sich volle Breite unter die Tabelle, da 380px fix
   + Master dort zu eng waeren. Farben ausschliesslich ueber die bestehenden
   --color-*-Tokens (kein neues Farbschema), die dunkle DNS-Konsole nutzt die
   bereits vorhandenen --code-bg/--code-text-Tokens (siehe :root-Kommentar
   "absichtlich fest dunkel in beiden Themes").
   ============================================================================ */
/* Nahtloses Master-Detail-Layout (siehe body.page-seamless-split weiter unten) - vormals eine
   eigene schwebende Box mit Rand/Radius/Schatten, jetzt exakt dieselbe Fuellflaechen-Technik wie
   .vault-mdview: kein eigener Rand/Radius/Schatten mehr, stattdessen flex:1 innerhalb der
   100vh-Spalte aus .content. .domain-split-master/.domain-split-detail scrollen deshalb jetzt
   eigenstaendig (overflow-y:auto), da die frueher unbegrenzte Seite jetzt hoehenbeschraenkt ist. */
.domain-split-view {
    display: flex;
    align-items: stretch;
    flex: 1;
    min-height: 0;
    background: var(--color-surface);
}

.domain-split-master {
    flex: 1;
    min-width: 0;
    border-right: 1px solid var(--color-border);
    overflow-y: auto;
}

.domain-split-table { width: 100%; border-collapse: collapse; text-align: left; }
.domain-split-table th {
    background: var(--color-bg);
    color: var(--color-text-muted);
    padding: 11px 16px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    border-bottom: 1px solid var(--color-border);
}
.domain-split-table td { padding: 13px 16px; border-bottom: 1px solid var(--color-border); vertical-align: middle; }
.domain-split-table tr:last-child td { border-bottom: none; }
.domain-split-row { cursor: pointer; }
.domain-split-row:hover td { background: var(--color-bg); }
.domain-split-row.is-selected td { background: var(--color-accent-soft); }
.domain-split-muted { color: var(--color-text-muted); }
.domain-split-chevron { text-align: right; color: var(--color-text-muted); }

.domain-split-detail {
    width: 380px;
    flex-shrink: 0;
    overflow-y: auto;
}

.domain-detail-header {
    padding: 16px 20px;
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
    background: var(--color-bg);
}

.domain-detail-eyebrow { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-text-muted); display: block; margin-bottom: 3px; }
.domain-detail-name { font-size: 17px; font-weight: 700; color: var(--color-text); word-break: break-word; }
.domain-detail-body { padding: 18px 20px; display: flex; flex-direction: column; gap: 18px; }
.domain-detail-section-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-text-muted); margin-bottom: 8px; }
.domain-detail-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 13px; }
.domain-detail-label { font-size: 11.5px; color: var(--color-text-muted); display: block; margin-bottom: 2px; }
.domain-detail-value { font-weight: 600; color: var(--color-text); }

.domain-detail-vault-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 11px 12px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--color-text);
    font-weight: 600;
    font-size: 13px;
    min-height: 44px;
}
.domain-detail-vault-card:hover { border-color: var(--color-accent); }
.domain-detail-vault-open { color: var(--color-accent); font-weight: 600; font-size: 12.5px; white-space: nowrap; }

.domain-detail-console { background: var(--code-bg); border-radius: var(--radius-sm); overflow: hidden; }
.domain-detail-console-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 8px 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: var(--code-text); }
.domain-detail-console-row:not(:last-child) { border-bottom: 1px solid rgba(255,255,255,0.08); }
.domain-detail-console-key { color: #94A3B8; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.03em; white-space: nowrap; }
.domain-detail-console-val { text-align: right; overflow-wrap: anywhere; }

.domain-detail-notes { font-size: 13px; color: var(--color-text); background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 10px 12px; white-space: pre-wrap; overflow-wrap: anywhere; }

.domain-detail-actions { display: flex; gap: 8px; padding: 16px 20px; border-top: 1px solid var(--color-border); }
.domain-detail-actions .btn { flex: 1; min-height: 40px; justify-content: center; }
.domain-detail-delete-form { flex: 1; display: flex; }
.domain-detail-delete-form .btn { width: 100%; }

@media (max-width: 1023px) {
    .domain-split-view { flex-direction: column; }
    .domain-split-master { border-right: none; border-bottom: 1px solid var(--color-border); }
    .domain-split-detail { width: 100%; }
}

/* Muss NACH der obigen unguarded .domain-split-view-Regel stehen, sonst gewinnt die
   spaetere Regel im Cascade und ueberschreibt das display:none aus der fruehen
   Phone-Media-Query weiter oben (Tabelle-zu-Karten-Abschnitt) - CSS-Spezifitaet ist
   hier gleich, es entscheidet die Reihenfolge im Quelltext. */
@media (max-width: 599px) {
    .domain-split-view { display: none; }
}

.badge--callback_requested { background: var(--status-callback-soft); color: var(--status-callback); }
.badge--callback_requested .badge-dot { background: var(--status-callback); }

.badge--scheduled { background: var(--status-callback-soft); color: var(--status-callback); }
.badge--scheduled .badge-dot { background: var(--status-callback); }

.badge--locked { background: var(--color-danger-bg-strong); color: var(--color-danger-text); margin-left: 8px; }
.badge--locked .badge-dot { background: var(--color-danger-text); }

/* Admin > E-Mail-Routing: Match-Type-Badges (Phase 5, siehe EmailInboundRouteRepository) */
.badge--route-alias { background: rgba(124, 58, 237, 0.12); color: #7C3AED; }
.badge--route-pattern { background: var(--color-accent-soft); color: var(--color-accent); }
.badge--route-system { background: var(--status-neutral-soft); color: var(--status-neutral); }

.admin-split-2-1 {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 20px;
    align-items: start;
}

@media (max-width: 900px) {
    .admin-split-2-1 { grid-template-columns: 1fr; }
}

.email-route-form { display: flex; flex-direction: column; gap: 20px; margin-top: 12px; }

.email-route-form-section {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding-top: 16px;
    border-top: 1px solid var(--color-border);
}

.email-route-form-section[hidden] { display: none; }

.email-route-form-section:first-child { padding-top: 0; border-top: none; }

.email-route-form-section-title {
    margin: 0;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
}

.field-block { display: flex; flex-direction: column; gap: 4px; }
.field-block[hidden] { display: none; }

.field-block label {
    font-size: 13px;
    font-weight: 500;
    color: var(--color-text-muted);
}

.email-route-sim-card {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 12px;
    padding: 12px;
    background: var(--color-surface-muted, #f8fafc);
    border: 1px solid var(--color-border);
    border-radius: 8px;
}

.email-route-sim-card[hidden] { display: none; }

.email-route-sim-badges { display: flex; flex-wrap: wrap; gap: 8px; }

.email-route-sim-details { font-size: 13px; }

.email-route-sim-details summary {
    cursor: pointer;
    list-style: none;
    color: var(--color-accent);
    font-weight: 500;
}

.email-route-sim-details summary::-webkit-details-marker { display: none; }

.email-route-sim-details summary::before {
    content: '▸';
    display: inline-block;
    margin-right: 6px;
    transition: transform 0.15s ease;
}

.email-route-sim-details[open] summary::before { transform: rotate(90deg); }

.email-route-sim-sample-list {
    list-style: none;
    margin: 10px 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.email-route-sim-sample-list li {
    padding: 8px 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    font-size: 12.5px;
}

.email-route-sim-sample-list a {
    color: var(--color-text);
    font-weight: 500;
    text-decoration: none;
}

.email-route-sim-sample-list a:hover { color: var(--color-accent); text-decoration: underline; }

.email-route-sim-sample-customer {
    display: block;
    margin-top: 2px;
    color: var(--color-text-muted);
}

.email-route-submit { width: 100%; margin-top: 4px; }

.badge--premium { background: var(--status-warning-soft); color: var(--status-warning); margin-left: 8px; }
.badge--premium .icon-sm { width: 12px; height: 12px; }

.badge--active { background: var(--status-active-soft); color: var(--status-active); }
.badge--active .badge-dot { background: var(--status-active); }

.badge--in_stock { background: var(--status-callback-soft); color: var(--status-callback); }
.badge--in_stock .badge-dot { background: var(--status-callback); }

.badge--in_repair { background: var(--status-hold-soft); color: var(--status-hold); }
.badge--in_repair .badge-dot { background: var(--status-hold); }

.badge--lost { background: var(--status-warning-soft); color: #A66A1E; }
.badge--lost .badge-dot { background: var(--status-warning); }

.badge--stolen { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }
.badge--stolen .badge-dot { background: var(--color-danger-text); }

.badge--retired { background: var(--status-neutral-soft); color: var(--status-neutral); }
.badge--retired .badge-dot { background: var(--status-neutral); }

/* Projekt-Meilenstein-Status (siehe projects/show.twig) - eigene Modifiers statt Wiederverwendung
   von z.B. badge--closed/badge--active, da "pending/on_track/at_risk/done" eine eigene, vom
   Ticket-Status losgeloeste Bedeutungswelt ist. */
.badge--pending { background: var(--status-neutral-soft); color: var(--status-neutral); }
.badge--pending .badge-dot { background: var(--status-neutral); }

.badge--on_track { background: var(--status-active-soft); color: var(--status-active); }
.badge--on_track .badge-dot { background: var(--status-active); }

.badge--at_risk { background: var(--status-warning-soft); color: #A66A1E; }
.badge--at_risk .badge-dot { background: var(--status-warning); }

.badge--done { background: var(--color-accent-soft); color: var(--color-accent); }
.badge--done .badge-dot { background: var(--color-accent); }

/* Sichtbarkeits-Badge fuer Projekt-Wiki-Seiten (siehe project_wiki/index.twig) - kein badge-dot,
   stattdessen ein Augen-Icon, da hier keine Statusfarbe im engeren Sinn abgebildet wird, sondern
   ein einfaches sichtbar/nicht-sichtbar. */
.badge--visible { background: var(--status-active-soft); color: var(--status-active); }
.badge--internal { background: var(--status-neutral-soft); color: var(--color-text-muted); }

/* Ticket-Prioritaet: farblicher Tag-Chip, damit die Prioritaet auch fuer den Techniker auf den
   ersten Blick sichtbar ist (Listen, Gallery, Dashboard, Ticket-Detail). */
.badge--priority_urgent { background: var(--color-critical-soft); color: var(--color-critical); }
.badge--priority_high { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }
.badge--priority_normal { background: var(--color-accent-soft); color: var(--color-accent); }
.badge--priority_low { background: var(--status-neutral-soft); color: var(--status-neutral); }

/* Projekt-Zuordnung in der Ticket-Tabelle (Nutzer-Feedback 2026-08-07: "fehlt eine Spalte, ob
   das Ticket einem Projekt zugewiesen ist") - klickbarer Link-Chip statt reiner Text/Icon-
   Anzeige, da direkt zum Projekt (/customers/{id}/projects/{projectId}) navigiert wird. */
.badge--project { background: var(--color-accent-soft); color: var(--color-accent); text-decoration: none; }
.badge--project:hover { background: var(--color-accent); color: #fff; }

/* Asset gallery */
.asset-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
}

/* Tresor-Karten brauchen mehr Breite als einfache Geraete-/Kunden-Karten (Felder, Buttons) */
.vault-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 16px;
}

.vault-card {
    display: block;
}

.asset-card {
    display: block;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 18px;
    text-align: left;
    color: var(--color-text);
}

.asset-card:hover {
    border-color: var(--color-accent);
    text-decoration: none;
}

.asset-card.is-inactive {
    opacity: 0.55;
}

.asset-card .asset-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}

/* Dezentes Hintergrund-Quadrat um das Typ-Icon - schafft sofortige visuelle
   Unterscheidung zwischen Geraetetypen (Access Point vs. Router vs. Drucker),
   statt eines nackten, gleichfarbigen Icons wie zuvor. */
.asset-card .asset-icon-square {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: var(--radius-sm);
    background: var(--color-accent-soft);
    flex-shrink: 0;
}

.asset-card .asset-icon {
    width: 19px;
    height: 19px;
    color: var(--color-accent);
    flex-shrink: 0;
}

.asset-card .asset-card-header-text {
    flex: 1;
    min-width: 0;
}

.asset-card .asset-card-header .status-dot {
    flex-shrink: 0;
    margin-left: auto;
}

.asset-card .asset-name {
    font-weight: 600;
    font-size: 13px;
    margin-bottom: 5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.asset-card .asset-card-header .asset-name {
    margin-bottom: 0;
}

.asset-card .asset-subtitle {
    font-size: 11px;
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-top: 2px;
}

.asset-card .asset-meta {
    display: flex;
    align-items: center;
    font-size: 12px;
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    margin-bottom: 3px;
}

/* Typ/Standort/Mitarbeiter als graue Pills nebeneinander statt gestapelter
   Zeilen - spart vertikalen Platz in der Karte. */
.asset-card .asset-pill-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-bottom: 12px;
}

.asset-card .asset-pill-row .tag-badge {
    margin: 0;
}

.asset-card .asset-pill-row .icon-xs {
    width: 10px;
    height: 10px;
}

/* Monospace-IP mit direktem 1-Klick-Copy-Button statt reinem Anzeigetext. */
.asset-card .asset-ip-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border);
}

.asset-card .asset-ip-row .mono {
    font-size: 12px;
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.asset-card .asset-ip-row .icon-btn {
    width: 24px;
    height: 24px;
    flex-shrink: 0;
}

.asset-card .asset-ip-row .icon-btn .icon {
    width: 13px;
    height: 13px;
}

/* ============================================================================
   Geraete-Listenansicht (Liste) im UniFi-Controller-Stil: randlose Sidebar mit
   Such-/Checkbox-Facetten links, dichte Tabelle rechts - ergaenzt die bereits
   bestehenden randlosen page-vault-seamless/page-seamless-split-Seiten
   (Vault/Domains) um eine dritte Variante mit Facetten statt Master-Detail.
   Wird nur aktiv, wenn view=list mit Ergebnissen gerendert wird (siehe
   templates/assets/index.twig body_class-Block).
   ============================================================================ */
.asset-list-shell {
    display: flex;
    height: 100%;
    flex: 1;
    min-height: 0;
    background: var(--color-surface);
}

.asset-list-sidebar {
    width: 240px;
    flex-shrink: 0;
    border-right: 1px solid var(--color-border);
    background: var(--color-bg);
    padding: 16px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 18px;
    transition: width .15s ease, padding .15s ease;
}

.asset-list-sidebar-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding-bottom: 10px;
    margin-bottom: 2px;
    border-bottom: 1px solid var(--color-border);
}

.asset-list-sidebar-title {
    font-size: 15px;
    font-weight: 700;
}

/* Nutzer-Feedback 2026-08-07: bei der Ticket-Uebersicht (einziger Verwender mit einem zweiten
   Kind-Element neben der Suche, siehe .ticket-quick-filters in tickets/index.twig) fielen die
   Schnellfilter-Pills wegen flex-wrap:nowrap aus dieser Zeile heraus - .asset-list-sidebar hat nur
   overflow-y:auto gesetzt, wodurch der Browser overflow-x automatisch ebenfalls auf 'auto'
   erzwingt (CSS-Overflow-Spec: ein "visible"-Wert auf einer Achse wird zu "auto", sobald die
   andere Achse nicht "visible" ist) - die ueberstehenden Pills wurden dadurch am Sidebar-Rand
   abgeschnitten statt sichtbar umzubrechen. flex-direction:column stapelt die Suche und (falls
   vorhanden) das zweite Element sauber untereinander; alle anderen 11 Verwender dieser Klasse
   haben ohnehin nur die Suche als einziges Kind, fuer die macht row vs. column keinen Unterschied. */
.asset-list-sidebar-search-row {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
}

/* Nutzer-Feedback 2026-08-13: .list-search sah in der Kombi-Shell-Sidebar (Assets/Tickets/Kunden/
   ...) trotz identischer Border-/Radius-/Background-Tokens sichtbar "duenner" aus als Vaults
   eigenes Suchfeld (.vault-sidebar-search) - Ursache war ein anderes Box-Modell: .list-search legt
   Border/Radius/Padding auf den <input> selbst und blendet das Icon absolut ueberlappend ein
   (34px Gesamthoehe), waehrend .vault-sidebar-search Icon+Input als Flex-Kinder in einer
   gepolsterten Aussen-Box anordnet (48px Gesamthoehe). Diese Regeln uebernehmen exakt Vaults
   Box-Modell nur innerhalb der Sidebar-Suchzeile, ohne die App-weit genutzte Basis-.list-search
   (z.B. Toolbar-Suchfelder ausserhalb von Sidebars) anzutasten. */
.asset-list-sidebar-search-row .list-search {
    flex: 1;
    max-width: none;
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

.asset-list-sidebar-search-row .list-search .icon {
    position: static;
    transform: none;
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
}

.asset-list-sidebar-search-row .list-search input {
    /* padding: 8px 10px (nicht 0) - exakt der Wert, den .vault-sidebar-search input effektiv
       zeigt (dort nicht selbst gesetzt, sondern durchgereicht von der globalen input[type="text"]-
       Basisregel weiter oben) - explizit statt implizit, damit diese Uebereinstimmung nicht von
       einem unangetasteten Fallthrough anderswo in der Datei abhaengt. */
    padding: 8px 10px;
    border: none;
    border-radius: 0;
    background: none;
}

.asset-list-sidebar-search-row .list-search input:focus {
    box-shadow: none;
}

.asset-list-sidebar-collapse {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    flex-shrink: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
}

.asset-list-sidebar-collapse:hover {
    color: var(--color-text);
}

.asset-list-sidebar-internal-link {
    width: 100%;
    justify-content: center;
    text-align: center;
}

/* Selektor faengt sowohl .icon als auch die verbreitetere Groessen-Modifier-Klasse .icon-sm ab -
   icons.render(name, class) ERSETZT die Default-Klasse "icon" komplett statt sie zu ergaenzen
   (siehe partials/icons.twig), .icon-sm-Aufrufe (assets/customers/tickets identisch) hatten die
   Rotation dadurch bisher nie tatsaechlich ausgeloest (Nutzer-Feedback 2026-08-02: Pfeil zeigte im
   ausgeklappten Zustand faelschlich nach rechts statt links). Globale Basis-Regel fuer ALLE
   Asset-List-Shell-/Kombi-Shell-Sidebars: eingeklappt = Pfeil zeigt nach rechts (zum Ausklappen),
   ausgeklappt = Pfeil zeigt nach links (zum Einklappen). */
.asset-list-sidebar-collapse svg {
    width: 14px;
    height: 14px;
    transform: rotate(180deg);
    transition: transform .15s ease;
}

/* Direktes-Kind-Combinator (">"), NICHT Nachfahren-Selektor (Nutzer-Feedback 2026-08-07): auf
   Kunden-Unterseiten steckt eine zweite, eigene .asset-list-shell (Tickets/Assets/Kontakte/...)
   INNERHALB der aeusseren Kunden-Navigations-Shell (#customer-shell, ebenfalls .asset-list-shell).
   Ein reiner Nachfahren-Selektor traf hier faelschlich auch die innere, gar nicht eingeklappte
   .asset-list-sidebar mit - Einklappen der Kunden-Navigation liess so nebenbei auch die
   Ticket-/Asset-Facetten-Sidebar verschwinden. In jeder Kombi-Shell-Seite ist .asset-list-sidebar
   ohnehin immer ein direktes Kind ihrer eigenen .asset-list-shell (siehe /admin/ui-design
   Abschnitt 20), der Combinator schraenkt also nur echte Fehltreffer ein. */
.asset-list-shell.is-sidebar-collapsed > .asset-list-sidebar {
    width: 0;
    padding-left: 0;
    padding-right: 0;
    overflow: hidden;
    border-right: none;
}

/* Bleibt bewusst ein ungescopter Nachfahren-Selektor (anders als die .asset-list-sidebar-Regel
   oben): der Toggle-Button selbst sitzt je nach Seite an unterschiedlichen Stellen im Baum
   (direktes Kind der Shell bei /admin/billing, in .asset-list-sidebar-head bei der Kunden-
   Navigation, im .asset-list-table-toolbar-left bei Tickets/Assets/... - siehe /admin/ui-design
   Abschnitt 20). Ein zu enger Selektor wuerde die Pfeil-Rotation auf mehreren Seiten stumm
   brechen. Faelschliches Mit-Rotieren des Icons einer NICHT eingeklappten verschachtelten Sidebar
   ist rein kosmetisch (kein Funktionsverlust wie bei der width:0-Regel oben) und daher hier
   akzeptiert. */
.asset-list-shell.is-sidebar-collapsed .asset-list-sidebar-collapse svg {
    transform: rotate(0deg);
}

.asset-list-filter-section h4 {
    margin: 0 0 6px;
    padding-bottom: 6px;
    font-size: 11px;
    font-weight: 700;
    /* War "uppercase", auf Nutzer-Feedback 2026-08-01 auf "capitalize" geaendert - gilt fuer ALLE
       Asset-List-Shell-Sidebars (Basis-Regel), also auch /assets?view=list. */
    text-transform: capitalize;
    letter-spacing: .04em;
    color: var(--color-text-muted);
    /* Duenne graue Trennlinie unter jeder Facetten-Ueberschrift (Kundentyp/Status/...) - ebenfalls
       Nutzer-Feedback 2026-08-01, gilt fuer ALLE Asset-List-Shell-Sidebars. */
    border-bottom: 1px solid var(--color-border);
}

/* Academy-Verweis am Ende der Sidebar statt neben dem Titel (Nutzer-Feedback 2026-08-01) -
   margin-top:auto in einer flex-column-Sidebar. Gilt fuer alle Asset-List-Shell-Sidebars, seit der
   Kombi-Shell-Migration (2026-08-02) auch fuer das Abrechnungs-Modul (vormals eigene
   .billing-module-sidebar-help-Klasse mit identischem Trick). */
.asset-list-sidebar-help {
    margin-top: auto;
    margin-left: 0;
    padding-top: 14px;
    border-top: 1px solid var(--color-border);
}

.asset-list-filter-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 5px 6px;
    margin: 0 -6px;
    font-size: 13px;
    /* War implizit (kein color hier, geerbt von --color-text) - jetzt explizit dasselbe Token wie
       Vaults eigene Sidebar (siehe --sidebar-text-color-Definition oben), damit beide Sidebar-
       Familien nachweisbar denselben Bezug nutzen statt sich nur zufaellig gleich zu verhalten. */
    color: var(--sidebar-text-color);
    cursor: pointer;
    border-radius: var(--radius-sm);
}

.asset-list-filter-item:hover {
    background: var(--color-surface);
}

.asset-list-filter-item-label {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.asset-list-filter-item-label .icon-sm {
    flex-shrink: 0;
    color: var(--color-text-muted);
}

.asset-list-filter-item-label span:last-child {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.asset-list-filter-count {
    font-size: 12px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

/* Einklappbare Facetten-Gruppen (Nutzer-Feedback 2026-08-07, UniFi-Vorbild) - .asset-list-filter-
   section-head ergaenzt die bestehende .asset-list-filter-section h4-Basisregel (Trennlinie,
   Farbe etc. bleiben unveraendert) nur um Flex-Layout + Cursor fuer den Chevron. */
.asset-list-filter-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    user-select: none;
}

.asset-list-filter-section-chevron {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--color-text-muted);
    transition: transform .15s ease;
}

.asset-list-filter-section.is-collapsed .asset-list-filter-section-chevron {
    transform: rotate(-90deg);
}

.asset-list-filter-section.is-collapsed .asset-list-filter-section-body {
    display: none;
}

/* "Filter zuruecksetzen" ganz unten in der Sidebar statt direkt unter der Suche (Nutzer-Feedback
   2026-08-07) - margin-top:auto in der flex-column-Sidebar, gleicher Trick wie .asset-list-sidebar-
   help (Academy-Link). Eigener Wrapper statt der Regel direkt auf .asset-list-filter-reset, da
   diese Klasse auf anderen Seiten (z.B. /contacts) bewusst oben unter der Suche bleibt. */
.asset-list-sidebar-filter-footer {
    margin-top: auto;
    padding-top: 14px;
    border-top: 1px solid var(--color-border);
}

.asset-list-table-pane {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.asset-list-table-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 16px;
    border-bottom: 1px solid var(--color-border);
    flex-shrink: 0;
}

.asset-list-table-toolbar-left,
.asset-list-table-toolbar-right {
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Nur im Ticket Hub belegt (siehe .hub-toolbar-search-slot) - der rechte Toolbar-Block bekommt hier
   den gesamten freien Platz nach dem Sidebar-Toggle, damit sich das Suchfeld darin zentrieren kann. */
.asset-list-table-toolbar:has(.hub-toolbar-search-slot) .asset-list-table-toolbar-right {
    flex: 1;
}

/* Ticket-Toolbar rechts ist inzwischen die vollste im ganzen Kombi-Shell-System (Spalten/Massen-
   Loeschen/Geschlossene einblenden/Focus View, je nach Rolle bis zu 4 Elemente) - bei schmalerem
   Fenster lief "Focus View" bereits ueber den rechten Viewport-Rand hinaus (Nutzer-Feedback
   2026-08-07). Nur fuer die Ticket-Seite (.ticket-list-shell) gelockert, um die Basis-Regel
   (.asset-list-table-toolbar-right, kein flex-wrap) fuer alle anderen Kombi-Shell-Seiten
   unveraendert zu lassen. */
.ticket-list-shell .asset-list-table-toolbar-right {
    flex-wrap: wrap;
    justify-content: flex-end;
    row-gap: 8px;
}

.asset-list-table-scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
}

/* Nutzer-Fund 2026-08-16 ("STAT"-Spalte der Ticket-Liste): overflow:auto auf dieser Scrollbox
   schneidet das generische, position:absolute-basierte [data-tooltip]::after (siehe oben) an ihrem
   Rand ab - z.B. der Status-Icon-Tooltip (siehe partials/status_icon.twig) wirkte dadurch
   "angeschnitten". Deaktiviert, ein JS-Tooltip (siehe public/js/table-tooltip.js) uebernimmt
   stattdessen ueber position:fixed - exakt dasselbe Baukastenprinzip wie bei .sidebar-nav
   (public/js/sidebar-tooltip.js). .ptbl-table-scroll (Projekt-Tabellenansicht) hat denselben
   overflow-bedingten Clip und wird vom selben Skript mit abgedeckt.
   .asset-list-table-scroll(-Nachfahre) hat position:relative NICHT auf jedem [data-tooltip]-Element
   selbst - ::after erbt daher deren Positionierung, das Ausblenden hier reicht aus. */
.asset-list-table-scroll [data-tooltip]:hover::after,
.asset-list-table-scroll [data-tooltip]:focus-visible::after,
.ptbl-table-scroll [data-tooltip]:hover::after,
.ptbl-table-scroll [data-tooltip]:focus-visible::after {
    display: none;
}

.table-tooltip {
    position: fixed;
    z-index: 300;
    display: none;
    background: var(--color-text);
    color: var(--color-bg);
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 500;
    white-space: nowrap;
    box-shadow: var(--shadow-card);
    pointer-events: none;
}

.table-tooltip.is-visible {
    display: block;
}

table.asset-list-table {
    width: 100%;
    border-collapse: collapse;
}

table.asset-list-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--color-surface);
    text-align: left;
    padding: 10px 16px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

table.asset-list-table th.is-sortable {
    cursor: pointer;
    user-select: none;
    display: table-cell;
}

table.asset-list-table th.is-sortable:hover {
    color: var(--color-text);
}

table.asset-list-table th.is-sortable .th-sort-icon {
    display: inline-flex;
    vertical-align: middle;
    margin-left: 3px;
    opacity: 0;
    transition: opacity .1s ease, transform .1s ease;
}

table.asset-list-table th.is-sortable:hover .th-sort-icon {
    opacity: .4;
}

table.asset-list-table th.is-sort-active .th-sort-icon {
    opacity: 1;
    color: var(--color-accent);
}

table.asset-list-table th.is-sort-active.is-sort-desc .th-sort-icon {
    transform: rotate(180deg);
}

table.asset-list-table td {
    padding: 9px 16px;
    font-size: 13px;
    border-bottom: 1px solid var(--color-border);
    vertical-align: middle;
    white-space: nowrap;
    /* UniFi-Feintuning (Nutzer-Feedback 2026-08-08): Tabellen-Ziffern (Zeiten, Betraege, IDs)
       sollen gleich breit sein, damit Spalten sauber untereinander stehen, und eine
       durchgestrichene Null tragen wie im UniFi Netzwerk-Controller. Bewusst NICHT die im
       Feedback vorgeschlagenen festen Hex-Farben/letter-spacing/!important uebernommen - die
       wuerden das bestehende --color-text/--color-text-muted-Token-System und damit den Dark
       Mode fuer JEDE .asset-list-table-Tabelle (30+ Stellen app-weit) aushebeln, ohne echten
       Referenz-Screenshot zur Verifikation (siehe Memory "Verify reference-video claims"). */
    font-variant-numeric: tabular-nums slashed-zero;
}

table.asset-list-table tbody tr:last-child td {
    border-bottom: none;
}

table.asset-list-table tbody tr:hover {
    background: var(--color-bg);
}

.asset-list-type-cell {
    display: flex;
    align-items: center;
    color: var(--color-text-muted);
}

.asset-list-status-cell {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* ============================================================================
   Sidebar-/Tabellen-Politur (Nutzer-Feedback 2026-08-01) - urspruenglich nur unter der
   ".kombi-shell"-Modifier-Klasse (nur /customers?view=list), auf expliziten Wunsch als Basis-Regel
   auf ALLE Asset-List-Shell-Seiten ausgeweitet, also auch /assets?view=list (siehe
   .asset-list-filter-section h4 weiter oben fuer die capitalize-Regel). Referenz-Implementierung
   fuer die sticky erste Spalte: .unifi-filter-table--sticky-cols (Abrechnungs-Modul), hier nur auf
   eine Spalte reduziert.
   ============================================================================ */
table.asset-list-table th:first-child,
table.asset-list-table td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--color-surface);
}

table.asset-list-table thead th:first-child {
    z-index: 3;
}

table.asset-list-table tbody tr:hover td:first-child {
    background: var(--color-bg);
}

/* Ticket Hub (Nutzer-Feedback 2026-08-02: "globaler Kombi-Shell-Standard") - nutzt dieselbe
   asset-list-table-Basis wie /assets und /customers. Das Abrechnungs-Modul (admin/_billing_tab_*.twig)
   wurde ab 2026-08-02 in vier Phasen ebenfalls auf .asset-list-table umgestellt (vorher
   .unifi-filter-table) - siehe .asset-list-table--billing-sticky-cols weiter unten fuer die
   Checkbox-Spalten-Variante.
   Der Ticket Hub braucht zusaetzlich zu #/Titel eine dritte, NUR fuer Admins sichtbare
   Checkbox-Spalte davor (Massen-Loeschen) - die urspruengliche :nth-child(2)-Variante ging von
   einer festen Spaltenposition aus und brach dadurch je nach Rolle (Checkbox-Spalte vorhanden
   oder nicht). Ab Nutzer-Feedback 2026-08-06 deshalb klassenbasiert statt positionsbasiert: jede
   sticky Zelle traegt .tk-sticky-col direkt (siehe templates/tickets/index.twig), die konkrete
   links-Position kommt als Inline-Style von Twig (haengt von der bedingten Checkbox-Spalte ab). */
table.asset-list-table--ticket-sticky-cols th.tk-sticky-col,
table.asset-list-table--ticket-sticky-cols td.tk-sticky-col {
    position: sticky;
    z-index: 2;
    background: var(--color-surface);
}

table.asset-list-table--ticket-sticky-cols thead th.tk-sticky-col {
    z-index: 3;
}

table.asset-list-table--ticket-sticky-cols tbody tr:hover td.tk-sticky-col {
    background: var(--color-bg);
}

/* Nutzer-Feedback 2026-08-07: in der Ticket-Tabelle soll beim Hover NUR die fixierte Spalte
   grau werden (wie hier), nicht die gesamte Zeile - die Basis-Regel .asset-list-table tbody
   tr:hover faerbt sonst die gesamte <tr> ein, was bei einer so breiten Tabelle unruhig wirkt.
   Andere Kombi-Shell-Tabellen (Assets/Kunden/...) behalten den vollen Zeilen-Hover. */
table.asset-list-table--ticket-sticky-cols tbody tr:hover {
    background: transparent;
}

/* Letzte sticky Spalte (Titel) bekommt den Trennschatten + eine Mindestbreite, die frueheren
   Spalten (Checkbox/#) bleiben auf ihre Inhaltsbreite begrenzt. */
table.asset-list-table--ticket-sticky-cols th.tk-sticky-col--subject,
table.asset-list-table--ticket-sticky-cols td.tk-sticky-col--subject {
    min-width: 220px;
    box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.15);
}

/* ============================================================================
   UniFi-Stil dynamisches Spalten-Layout (Nutzer-Feedback 2026-08-07, siehe
   ticket-table-columns.js): table-layout:fixed macht Spaltenbreiten per Drag steuerbar, zu lange
   Zellinhalte werden abgeschnitten ("...") statt umzubrechen oder die Spalte zu sprengen. Nur fuer
   die Ticket-Tabelle scoped (asset-list-table--ticket-sticky-cols), damit /assets, /customers &
   Co. ihr bisheriges auto-Layout behalten. Etwas dichteres Zeilen-Padding (6px statt 9-10px) fuer
   mehr Datendichte, klare Trennlinien kommen bereits aus der bestehenden border-bottom-Basisregel.
   ============================================================================ */
table.asset-list-table--ticket-sticky-cols {
    table-layout: fixed;
}

table.asset-list-table--ticket-sticky-cols th,
table.asset-list-table--ticket-sticky-cols td {
    position: relative;
    overflow: hidden;
    text-overflow: ellipsis;
    padding-top: 6px;
    padding-bottom: 6px;
}

/* Ziehbarer Rand am rechten Spaltenrand - 6px breiter Treffer-Bereich innerhalb der Zelle (nicht
   ueberstehend, sonst wuerde das eigene overflow:hidden der Zelle den Griff abschneiden). */
.tk-col-resizer {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 6px;
    cursor: col-resize;
    z-index: 4;
}

.tk-col-resizer:hover,
body.tk-col-resizing .tk-col-resizer {
    background: var(--color-accent);
    opacity: .5;
}

body.tk-col-resizing {
    cursor: col-resize;
    user-select: none;
}

/* "Spalten"-Menue (Toolbar rechts) - reines Checkbox-Menue im bestehenden .dropdown-menu-Baukasten
   (siehe .btn-group-dropdown weiter unten), oeffnet/schliesst per :focus-within ohne eigenes JS. */
.ticket-columns-menu {
    min-width: 200px;
}

.ticket-columns-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    border-radius: 6px;
    font-size: 13px;
    color: var(--color-text);
    cursor: pointer;
}

.ticket-columns-menu-item:hover {
    background: var(--color-accent-tint);
}

/* Rich-Hover-Tooltip auf dem Betreff (siehe ticket-table-columns.js, 300ms Delay vor dem Lazy-
   Fetch). Gleiche invertierende Tooltip-Optik wie .sidebar-nav-tooltip (background:var(--color-text)/
   color:var(--color-bg) - kontrastiert in beiden Themes automatisch korrekt gegen die Seite), hier
   aber mehrzeilig mit fester max-width statt einzeiligem nowrap. Trennlinie per color-mix() statt
   fixem rgba(255,255,255,...), da der Tooltip im Dark Theme hell statt dunkel wird (--color-text/
   --color-bg tauschen dort die Rollen) - eine feste weisse Linie waere dort fast unsichtbar. */
.tk-subject-tooltip {
    position: fixed;
    z-index: 300;
    display: none;
    max-width: 360px;
    background: var(--color-text);
    color: var(--color-bg);
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    line-height: 1.45;
    box-shadow: var(--shadow-card);
    pointer-events: none;
}

.tk-subject-tooltip.is-visible {
    display: block;
}

.tk-subject-tooltip-title {
    font-weight: 700;
    font-size: 13px;
    margin-bottom: 3px;
    overflow-wrap: anywhere;
}

.tk-subject-tooltip-meta {
    opacity: .8;
    margin-bottom: 8px;
}

.tk-subject-tooltip-label {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    opacity: .65;
    margin-bottom: 3px;
}

.tk-subject-tooltip-body {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.tk-subject-tooltip-foot {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid color-mix(in srgb, currentColor 25%, transparent);
    opacity: .75;
}

/* Abrechnungs-Modul "Historie & Berichte" (admin/_billing_tab_runs.twig): erste Spalte ist eine
   Checkbox (Sammel-ZIP-Download), zweite Spalte der Kundenname - gleicher Sticky-Mechanismus wie
   .asset-list-table--ticket-sticky-cols oben, nur mit breiterer erster Spalte fuer die Checkbox. */
table.asset-list-table--billing-sticky-cols th:first-child,
table.asset-list-table--billing-sticky-cols td:first-child {
    width: 42px;
    min-width: 42px;
    max-width: 42px;
}

table.asset-list-table--billing-sticky-cols th:nth-child(2),
table.asset-list-table--billing-sticky-cols td:nth-child(2) {
    position: sticky;
    left: 42px;
    z-index: 2;
    min-width: 180px;
    background: var(--color-surface);
    box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.15);
}

table.asset-list-table--billing-sticky-cols thead th:nth-child(2) {
    z-index: 3;
}

table.asset-list-table--billing-sticky-cols tbody tr:hover td:nth-child(2) {
    background: var(--color-bg);
}

/* ============================================================================
   Kunden-Liste (/customers?view=list) im Kombi-Shell-Pro-Standard (Nutzer-Feedback 2026-08-08,
   Referenz-Implementierung: templates/tickets/index.twig). Die sticky erste Spalte (Name) braucht
   HIER keine .tk-sticky-col-Klasse wie bei Tickets - anders als dort gibt es keine bedingte
   Checkbox-Spalte davor, die erste Spalte ist immer die Name-Spalte. Die bereits bestehende
   generische ":first-child"-Basisregel weiter oben ("Sidebar-/Tabellen-Politur") deckt Sticky-
   Position/Z-Index/Hover-Hintergrund schon vollstaendig ab - hier nur die Kombi-Shell-Pro-
   Ergaenzungen: Trennschatten + Mindestbreite sowie table-layout:fixed fuer Spalten-Resizing per
   Drag (siehe customer-table-columns.js, identischer Mechanismus wie ticket-table-columns.js).
   ============================================================================ */
table.asset-list-table--customer-sticky-cols th:first-child,
table.asset-list-table--customer-sticky-cols td:first-child {
    min-width: 200px;
    box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.15);
}

table.asset-list-table--customer-sticky-cols {
    table-layout: fixed;
}

table.asset-list-table--customer-sticky-cols th,
table.asset-list-table--customer-sticky-cols td {
    position: relative;
    overflow: hidden;
    text-overflow: ellipsis;
    padding-top: 6px;
    padding-bottom: 6px;
}

/* Gleicher Grund wie bei .is-overdue/.is-new-ticket in der Ticket-Tabelle (siehe dort): die sticky
   erste Spalte hat einen eigenen opaken Hintergrund, der den flieder Zeilen-Verlauf der eigenen-
   Firma-Zeile (.customer-row--internal) sonst ausgerechnet in dieser einen Zelle uebermalt. */
table.asset-list-table--customer-sticky-cols tbody tr.customer-row--internal td:first-child {
    background: linear-gradient(90deg, var(--color-accent-soft), transparent 140%);
}

table.asset-list-table--customer-sticky-cols tbody tr.customer-row--internal:hover td:first-child {
    background: var(--color-bg);
}

/* Regie-Abrechnungs-Monitor (admin/_billing_tab_ready_to_bill.twig, Nutzer-Feedback 2026-08-02):
   dezentes gruenes Highlight fuer Zeilen im Abschnitt "Erledigte Tickets" - color-mix() ueber den
   bestehenden --status-active-Token statt eines neuen, hart codierten Gruentons, funktioniert
   dadurch automatisch in allen drei Themes (light/dark/unifi). Linker Akzentrand statt vollflaechig
   kraeftiger Farbe fuer den "edlen, dezenten" Look aus der Anfrage. */
table.asset-list-table tr.rtb-row--ready td {
    background: color-mix(in srgb, var(--status-active) 8%, var(--color-surface));
}

table.asset-list-table tr.rtb-row--ready:hover td {
    background: color-mix(in srgb, var(--status-active) 14%, var(--color-surface));
}

table.asset-list-table tr.rtb-row--ready td:first-child {
    border-left: 3px solid var(--status-active);
    background: color-mix(in srgb, var(--status-active) 8%, var(--color-surface));
}

table.asset-list-table tr.rtb-row--ready:hover td:first-child {
    background: color-mix(in srgb, var(--status-active) 14%, var(--color-surface));
}

.rtb-section-title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    padding: 14px 16px 8px;
}

.rtb-filter-bar {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
}

.rtb-filter-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.rtb-filter-field input,
.rtb-filter-field select {
    min-width: 140px;
}

/* Sortierte Spalten-Ueberschrift hervorheben - dieselbe Klasse (is-sorted), die
   ticket-hub-board.js bereits setzt; .unifi-filter-table hat dieselbe Regel unter eigenem
   Namespace (siehe dort), hier ergaenzt fuer die neue asset-list-table-Basis. */
table.asset-list-table th.is-sorted {
    color: var(--color-accent);
}

.asset-list-filter-reset {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 0;
    margin: -4px 0 2px;
    border: none;
    background: none;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-accent);
    cursor: pointer;
}

/* "display: inline-flex" oben ueberstimmt sonst das [hidden]-Attribut (Autoren-CSS schlaegt immer
   die UA-Stylesheet-Regel "[hidden]{display:none}", unabhaengig von Selektor-Spezifitaet) - ohne
   diese Regel bliebe der Button trotz .hidden=true sichtbar. */
.asset-list-filter-reset[hidden] {
    display: none;
}

.asset-list-filter-reset:hover {
    text-decoration: underline;
}

/* Projekt-Ticket-Tabelle (Phasen-Accordion, Linear/Monday.com-Stil) - templates/projects/
   _table_view.twig, public/js/project-table-view.js. Baut auf .asset-list-table (Zeilen 4283ff.)
   auf, ausschliesslich bestehende --color-* / --status-*-Tokens, kein neuer Farbwert. */
.ptbl-filterbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 10px 12px;
    margin-bottom: 16px;
}

.ptbl-filterbar-left,
.ptbl-filterbar-right {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.ptbl-filter-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    font-size: 12px;
    font-weight: 600;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
    white-space: nowrap;
}

.ptbl-filter-btn:hover {
    border-color: var(--color-accent);
    color: var(--color-text);
}

.ptbl-filter-btn.is-active {
    background: var(--color-accent-soft);
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.ptbl-collapse-all-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 4px;
    font-size: 12px;
    font-weight: 600;
    border: none;
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    white-space: nowrap;
}

.ptbl-collapse-all-btn:hover {
    color: var(--color-accent);
}

.ptbl-search-wrap {
    position: relative;
    flex: 1;
    min-width: 180px;
    max-width: 320px;
    display: flex;
    align-items: center;
}

.ptbl-search-wrap svg,
.ptbl-search-wrap .icon {
    position: absolute;
    left: 10px;
    color: var(--color-text-muted);
    pointer-events: none;
}

/* Element+Klasse (statt reiner Klassen-Selektor) noetig, um die globale
   input[type="text"]-Reset-Regel (Zeile ~2684, Spezifitaet Element+Attribut schlaegt eine reine
   Klasse) zu ueberstimmen - sonst gewinnt deren padding-left:10px und das Suchfeld-Icon liegt auf
   dem ersten Buchstaben des Platzhaltertexts. */
input.ptbl-search-input {
    width: 100%;
    padding: 6px 10px 6px 30px;
    font-size: 12.5px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
}

.ptbl-search-input:focus {
    outline: none;
    border-color: var(--color-accent);
}

.ptbl-groups {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ptbl-group {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--color-surface);
}

.ptbl-phase-header-row {
    display: flex;
    align-items: center;
    gap: 4px;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
}

.ptbl-phase-header {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 14px;
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
}

.ptbl-phase-actions {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: 4px;
    padding-right: 12px;
}

.ptbl-phase-header-left {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.ptbl-chevron {
    flex-shrink: 0;
    transition: transform .15s ease;
    color: var(--color-text-muted);
}

.ptbl-group.is-collapsed .ptbl-chevron {
    transform: rotate(-90deg);
}

.ptbl-group.is-collapsed .ptbl-group-body {
    display: none;
}

.ptbl-phase-name {
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ptbl-phase-count-badge {
    font-size: 11px;
    font-weight: 500;
    color: var(--color-text-muted);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    padding: 2px 8px;
    border-radius: 999px;
    white-space: nowrap;
}

.ptbl-phase-kpis {
    display: flex;
    align-items: center;
    gap: 14px;
    font-size: 12px;
    flex-shrink: 0;
}

.ptbl-phase-kpi {
    color: var(--color-text-muted);
    white-space: nowrap;
}

.ptbl-phase-kpi--cost {
    color: var(--color-text);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.ptbl-phase-kpi--percent {
    font-weight: 700;
    color: var(--color-accent);
    font-variant-numeric: tabular-nums;
}

.ptbl-mini-progress {
    display: inline-block;
    width: 90px;
    height: 6px;
    border-radius: 999px;
    background: var(--color-border);
    overflow: hidden;
    vertical-align: middle;
}

.ptbl-mini-progress-fill {
    display: block;
    height: 100%;
    background: var(--color-accent);
    border-radius: 999px;
}

.ptbl-table-scroll {
    overflow-x: auto;
}

table.ptbl-table td,
table.ptbl-table th {
    padding: 7px 12px;
}

.ptbl-col-id {
    width: 64px;
}

.ptbl-col-badge {
    width: 110px;
}

.ptbl-col-assignee {
    width: 70px;
}

.ptbl-col-date {
    width: 100px;
}

.ptbl-col-status {
    /* Ultra-kompakt (Nutzer-Feedback 2026-08-05) - war 150px fuer den frueheren breiten Text-Badge,
       jetzt nur noch der 24px-Icon-Kreis, siehe partials/status_icon.twig. */
    width: 40px;
}

.ptbl-col-drag {
    width: 28px;
    padding-left: 10px !important;
    padding-right: 0 !important;
}

.ptbl-drag-handle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    color: var(--color-text-muted);
    cursor: grab;
    border-radius: var(--radius-sm);
}

.ptbl-drag-handle:hover {
    color: var(--color-text);
    background: var(--color-bg);
}

.ptbl-drag-handle:active {
    cursor: grabbing;
}

tr.is-dragging,
.ptbl-mobile-card.is-dragging {
    opacity: .4;
}

/* Drop-Zone-Highlight auf der gesamten Phasen-Karte (Header + Body) - auch im eingeklappten
   Zustand sichtbar/treffbar, damit man Tickets in eine zugeklappte Phase ziehen kann. */
.ptbl-group.is-drag-over {
    border-color: var(--color-accent);
    border-style: dashed;
    background: var(--color-accent-tint);
}

.ptbl-group.is-drag-over .ptbl-phase-header,
.ptbl-group.is-drag-over .ptbl-phase-header-row {
    background: transparent;
}

.ptbl-empty-drop-hint {
    display: none;
    margin: 10px 14px 14px;
    padding: 18px 14px;
    text-align: center;
    font-size: 12.5px;
    color: var(--color-text-muted);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
}

/* .is-empty wird serverseitig gesetzt, wenn eine Phase 0 Tickets hat, und clientseitig per
   project-table-view.js live nachgefuehrt, sobald ein Ticket per Drag-and-Drop weg-/hinein-
   gezogen wird - Tabelle/Mobile-Liste bleiben dabei immer im DOM (nur leer), damit die Drop-Zone
   fertige tbody/.ptbl-mobile-list-Container zum Einhaengen hat. */
.ptbl-group-body.is-empty .ptbl-table-scroll,
.ptbl-group-body.is-empty .ptbl-mobile-list {
    display: none;
}

.ptbl-group-body.is-empty .ptbl-empty-drop-hint {
    display: block;
}

.ptbl-subject-cell {
    max-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ptbl-mobile-list {
    display: none;
    flex-direction: column;
}

.ptbl-mobile-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--color-border);
}

.ptbl-mobile-card:last-child {
    border-bottom: none;
}

.ptbl-mobile-card.is-overdue {
    border-left: 3px solid var(--color-danger-text);
    background: var(--color-danger-bg);
}

.ptbl-mobile-card-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.ptbl-mobile-card-left {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* touch-action:none verhindert, dass der Browser den Griff als Scroll-/Zoom-Geste
   interpretiert, bevor project-table-view.js per touchmove(preventDefault) uebernimmt. */
.ptbl-mobile-card .ptbl-drag-handle {
    touch-action: none;
}

.ptbl-touch-ghost {
    position: fixed;
    z-index: 2000;
    top: 0;
    left: 0;
    pointer-events: none;
    opacity: .92;
    box-shadow: var(--shadow-card), 0 8px 24px rgba(0, 0, 0, .18);
    transform: rotate(-1deg);
}

.ptbl-mobile-card-subject {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
}

.ptbl-mobile-card-bottom {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.ptbl-mobile-card-bottom .ptbl-status-chip {
    margin-left: auto;
}

@media (max-width: 599px) {
    .ptbl-table-scroll {
        display: none;
    }

    .ptbl-mobile-list {
        display: flex;
    }
}

@media (max-width: 900px) {
    .asset-list-shell {
        flex-direction: column;
        height: auto;
    }

    .asset-list-sidebar {
        width: 100%;
        border-right: none;
        border-bottom: 1px solid var(--color-border);
    }

    .asset-list-shell.is-sidebar-collapsed > .asset-list-sidebar {
        width: 100%;
        padding: 16px;
        overflow: visible;
        border-bottom: 1px solid var(--color-border);
    }

    .asset-list-sidebar-collapse {
        display: none;
    }
}

.status-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    margin-right: 5px;
    vertical-align: middle;
}

/* Eigenstaendiger Punkt in einer Tabellenzelle (kein folgender Text) - etwas groesser und ohne
   den fuer Inline-Text gedachten rechten Abstand, Bedeutung kommt per title-Tooltip statt Label. */
.status-dot--cell {
    width: 9px;
    height: 9px;
    margin-right: 0;
    cursor: default;
}

.status-dot--active { background: var(--status-active); }
.status-dot--warning { background: var(--status-warning); }
.status-dot--in_stock { background: var(--status-callback); }
.status-dot--in_repair { background: var(--status-hold); }
.status-dot--lost { background: var(--status-warning); }
.status-dot--stolen { background: var(--color-danger-text); }
.status-dot--retired { background: var(--status-neutral); }
.status-dot--open { background: var(--color-accent); }
.status-dot--in_progress { background: var(--status-active); }
.status-dot--waiting_customer { background: var(--status-warning); }
.status-dot--on_hold { background: var(--status-hold); }
.status-dot--callback_requested { background: var(--status-callback); }
.status-dot--scheduled { background: var(--status-scheduled); }
.status-dot--closed { background: var(--status-neutral); }
.status-dot--customer_appointment { background: var(--entrytype-appointment); }
.status-dot--tentative { background: var(--status-neutral); }
.status-dot--booked_service { background: var(--entrytype-service); }
.status-dot--callback { background: var(--entrytype-callback); }
.status-dot--absent { background: var(--status-absent); }
.status-dot--outside_hours { background: var(--status-absent-soft); border: 1px solid var(--status-absent); }
.status-dot--holiday { background: var(--calendar-holiday-bg); border: 1px solid var(--color-accent); }
.status-dot--weekend { background: var(--calendar-weekend-bg); border: 1px solid var(--color-text-muted); }
.status-dot--locked { background: var(--color-danger-text); }

/* Prioritaets-Punkte (gleiche Farbskala wie .badge--priority_*, nur als reiner Punkt statt
   Badge - fuer kompakte UniFi-Style Tabellenzeilen, Tooltip via title-Attribut). */
.status-dot--priority_urgent { background: var(--color-critical); }
.status-dot--priority_high { background: var(--color-danger-text); }
.status-dot--priority_normal { background: var(--color-accent); }
.status-dot--priority_low { background: var(--status-neutral); }

/* View toggle (list/gallery) */
.view-toggle {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.view-toggle a {
    padding: 7px 14px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-surface);
}

.view-toggle a:hover {
    text-decoration: none;
    background: var(--color-bg);
}

.view-toggle a.is-active {
    background: var(--color-accent);
    color: #FFFFFF;
}

.view-toggle a + a {
    border-left: 1px solid var(--color-border);
}

/* HR-Personalakte: obere Tab-Leiste (Informationen/Gehalt/Abwesenheit/Dokumente) - gleiches
   Segmented-Control-Muster wie .view-toggle, aber mit <button> statt <a> fuer client-seitiges
   Umschalten ohne Seitenreload. */
.hr-tabs {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
    margin-bottom: 16px;
}

.hr-tabs button {
    padding: 7px 14px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-surface);
    border: none;
    cursor: pointer;
}

.hr-tabs button:hover {
    background: var(--color-bg);
}

.hr-tabs button.is-active {
    background: var(--color-accent);
    color: #FFFFFF;
}

.hr-tabs button + button {
    border-left: 1px solid var(--color-border);
}

/* Projekt-Controlling-Filter-Chips (admin/_billing_tab_projects.twig) - rein clientseitiges
   Ein-/Ausblenden der bereits gerenderten Tabellenzeilen (siehe billing-module-tabs.js), keine
   generische ".is-hidden { display:none }"-Utility-Klasse im Projekt vorhanden (ist hier immer
   an die jeweilige Komponente gekoppelt). */
tr[data-filter-status].is-hidden {
    display: none;
}

/* Linke Untermenue-Navigation innerhalb des "Informationen"-Tabs. */
.hr-subnav {
    display: flex;
    gap: 24px;
    align-items: flex-start;
}

.hr-subnav-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 200px;
    flex-shrink: 0;
}

.hr-subnav-list button {
    text-align: left;
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    font-size: 13px;
    cursor: pointer;
}

.hr-subnav-list button:hover {
    background: var(--color-bg);
}

.hr-subnav-list button.is-active {
    background: var(--color-accent);
    color: #FFFFFF;
}

.hr-subnav-content {
    flex: 1;
    min-width: 0;
}

@media (max-width: 720px) {
    .hr-subnav {
        flex-direction: column;
    }

    .hr-subnav-list {
        flex-direction: row;
        flex-wrap: wrap;
        min-width: 0;
    }
}

.toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 16px;
}

/* Such-/Filter-/Sortier-Leiste, einheitlich fuer alle Hauptmodule */
.list-controls {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.list-search {
    position: relative;
    flex: 1;
    min-width: 180px;
    max-width: 320px;
}

.list-search input {
    width: 100%;
    padding: 8px 12px 8px 34px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-size: 13px;
    background: var(--color-surface);
    color: var(--color-text);
    font-family: inherit;
}

.list-search input:focus {
    outline: none;
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

.list-search .icon {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    width: 14px;
    height: 14px;
    color: var(--color-text-muted);
    pointer-events: none;
}

.list-controls select {
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    font-size: 13px;
    background: var(--color-bg);
    color: var(--color-text);
    font-family: inherit;
}

.list-controls select:focus {
    outline: none;
    border-color: var(--color-accent);
}

.icon-sm {
    width: 14px;
    height: 14px;
}

.icon-xs {
    width: 11px;
    height: 11px;
}

.asset-card .card-stats {
    display: flex;
    justify-content: flex-start;
    gap: 14px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--color-border);
    font-size: 12px;
    color: var(--color-text-muted);
}

.asset-card .card-stats span {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

/* Credentials */
.credential-item {
    padding: 14px 0;
    border-bottom: 1px solid var(--color-border);
}

.credential-item:last-of-type {
    border-bottom: none;
}

.credential-label {
    font-weight: 600;
    margin-bottom: 4px;
}

.credential-password-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 8px 0;
}

.credential-password-input {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    max-width: 200px;
}

/* Read-only detail view */
.detail-grid {
    display: grid;
    grid-template-columns: 160px 1fr;
    row-gap: 14px;
    column-gap: 16px;
    font-size: 13px;
}

.detail-row {
    display: contents;
}

.detail-label {
    color: var(--color-text-muted);
    font-weight: 600;
}

/* ============================================================================
   Auth (Login) - zweigeteilter Shopware-Stil: links ein vollflaechiges Sichtfeld (eigenes
   Hintergrundbild oder neutraler Farbverlauf als Fallback, siehe layout-auth.twig), rechts das
   eigentliche Formular. Beide Seiten weiss/neutral gehalten (UniFi-Look), nur der Akzent bleibt
   CoreDesk-Blau - White-Labeling ersetzt Logo/Hintergrund ueber die Branding-Uploads, nicht die
   Farbpalette selbst.
   ============================================================================ */
.auth-shell {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg);
    padding: 40px 24px;
}

/* Fly-out-Animation bei erfolgreichem Login (siehe public/js/login.js) - dem Shopware-Admin-Login
   nachempfunden. */
.auth-shell.is-leaving {
    animation: authFlyOut 480ms ease-in forwards;
}

@keyframes authFlyOut {
    to {
        transform: translateY(-60px) scale(0.96);
        opacity: 0;
    }
}

.auth-split {
    display: flex;
    width: 100%;
    max-width: 1140px;
    min-height: 620px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}

.auth-visual {
    flex: 1 1 54%;
    position: relative;
    display: flex;
    align-items: flex-end;
    padding: 40px;
    background: linear-gradient(135deg, var(--color-accent) 0%, #0A2A54 100%);
    background-size: cover;
    background-position: center;
    color: #FFFFFF;
}

.auth-visual-text h2 {
    margin: 0 0 4px;
    font-size: 34px;
    font-weight: 700;
}

.auth-visual-text p {
    margin: 0;
    font-size: 18px;
    opacity: 0.85;
}

/* Betreiber-Branding in der linken Auth-Flaeche (Nutzer-Wunsch 2026-08-13, Kunden-Portal-Login) -
   ersetzt dort testweise den generischen Willkommens-Text durch Logo/Wortmarke des IT-Systemhauses,
   siehe auth_visual_text-Block in layout-auth.twig. */
.auth-visual-logo {
    display: block;
    max-height: 40px;
    max-width: 220px;
    margin-bottom: 20px;
    object-fit: contain;
}

.auth-visual-wordmark {
    display: block;
    margin-bottom: 20px;
    font-size: 22px;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.auth-form-security-hint {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 16px;
    color: var(--color-text-subtle, var(--color-text-muted));
    font-size: 12px;
}

.auth-form-security-hint svg {
    width: 13px;
    height: 13px;
    flex: 0 0 auto;
}

.auth-form-panel {
    flex: 1 1 46%;
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 56px 64px;
}

.auth-badge {
    position: absolute;
    top: 24px;
    right: 24px;
    width: 48px;
    height: 48px;
    border-radius: 12px;
    background: var(--color-accent);
    color: #FFFFFF;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 15px;
    font-weight: 700;
    overflow: hidden;
}

.auth-badge img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.auth-form-title {
    margin: 0 0 28px;
    font-size: 20px;
    font-weight: 700;
}

.auth-form-panel .btn-primary {
    width: 100%;
    justify-content: center;
    margin-top: 10px;
}

.auth-form-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-top: 16px;
    font-size: 12px;
}

.auth-lang-switch {
    text-align: center;
    margin-top: 32px;
    font-size: 12px;
}

.auth-lang-switch a {
    color: var(--color-text-muted);
    font-weight: 600;
}

.auth-lang-switch a.is-active {
    color: var(--color-accent);
}

@media (max-width: 860px) {
    .auth-visual {
        display: none;
    }

    .auth-form-panel {
        padding: 48px 32px;
    }
}

/* ============================================================================
   Einstellungen-Uebersicht (templates/admin/index.twig) - kategorisierte Kachel-Uebersicht nach
   Shopware-Vorbild: die KATEGORIEN stehen in 3 Spalten nebeneinander, innerhalb einer Kategorie
   ist jede Einstellung nur eine schlichte Icon+Text-Zeile (keine eigene Card), damit die Liste
   auch mit vielen Eintraegen kompakt und ueberblickbar bleibt.
   ============================================================================ */
.admin-settings-wrap {
    max-width: 1200px;
    margin: 0 auto;
}

.settings-categories {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 36px 32px;
}

.settings-category-title {
    margin: 0 0 12px;
    font-size: 14px;
    font-weight: 700;
    color: var(--color-text);
}

/* Service-Katalog (Nutzer-Wunsch 2026-08-17): Bundle-Karte auf der Uebersichtsseite, groesser/
   praesenter als .settings-tile - ist der einzige Einstiegspunkt in den jeweiligen Workflow, kein
   dichtes Listen-Item. */
.service-catalog-grid {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.service-catalog-card {
    display: flex;
    align-items: center;
    gap: 16px;
    max-width: 640px;
    padding: 18px 20px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    text-decoration: none;
    color: var(--color-text);
    transition: border-color .12s ease, box-shadow .12s ease;
}

.service-catalog-card:hover {
    border-color: var(--color-accent);
    box-shadow: var(--shadow-card-hover, var(--shadow-card));
}

.service-catalog-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.service-catalog-card-body {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.service-catalog-card-title {
    font-size: 14px;
    font-weight: 700;
    color: var(--color-text);
}

.service-catalog-card-desc {
    font-size: 12.5px;
    color: var(--color-text-muted);
}

.service-catalog-card-arrow {
    margin-left: auto;
    flex-shrink: 0;
    color: var(--color-text-muted);
}

.settings-grid {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.settings-tile {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    border-radius: var(--radius-sm);
    color: var(--color-text);
    font-size: 13px;
    text-decoration: none;
}

.settings-tile:hover {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.settings-tile-icon {
    display: flex;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.settings-tile:hover .settings-tile-icon {
    color: var(--color-accent);
}

@media (max-width: 900px) {
    .settings-categories {
        grid-template-columns: 1fr;
    }
}

/* ============================================================================
   Control Center (templates/admin/index.twig) - Hero mit integrierter Suche + Tab-Leiste
   (Allgemein & Abrechnung / People / System / Module & Add-ons), nach ccc.html-Mockup. Die
   Kategorie-Listen innerhalb der Tabs bleiben bewusst auf .settings-grid/.settings-tile (siehe
   oben) statt einer neuen Zeilen-Komponente - nur die umschliessende .cc-setting-card ist neu.
   ============================================================================ */
.cc-hero {
    position: relative;
    overflow: hidden;
    border-radius: 24px;
    padding: 32px;
    background: linear-gradient(135deg, var(--color-accent), #1e3a8a);
    color: #fff;
    margin-bottom: 24px;
}

.cc-hero::before {
    content: '';
    position: absolute;
    right: -50px;
    bottom: -50px;
    width: 220px;
    height: 220px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.12);
    filter: blur(20px);
    pointer-events: none;
}

.cc-hero-inner {
    position: relative;
    z-index: 1;
    max-width: 560px;
}

.cc-hero-title {
    margin: 0;
    font-size: 24px;
    font-weight: 800;
    letter-spacing: -0.01em;
}

.cc-hero-subtitle {
    margin: 6px 0 20px;
    font-size: 13.5px;
    color: rgba(255, 255, 255, 0.82);
}

.cc-hero-search {
    position: relative;
    display: flex;
    align-items: center;
}

.cc-hero-search svg {
    position: absolute;
    left: 16px;
    color: var(--color-text-muted);
    pointer-events: none;
}

.cc-hero-search input {
    width: 100%;
    border: 0;
    border-radius: 16px;
    padding: 14px 16px 14px 44px;
    font-size: 14px;
    font-weight: 500;
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.15);
}

.cc-hero-search input:focus {
    outline: 3px solid rgba(255, 255, 255, 0.35);
}

.cc-tabs {
    display: flex;
    gap: 24px;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 24px;
    overflow-x: auto;
}

.cc-tab-btn {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 12px 2px;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    font-size: 13.5px;
    font-weight: 700;
    color: var(--color-text-muted);
    cursor: pointer;
    white-space: nowrap;
}

.cc-tab-btn:hover {
    color: var(--color-text);
}

.cc-tab-btn.is-active {
    color: var(--color-accent);
    border-bottom-color: var(--color-accent);
}

.cc-tab-panel {
    display: none;
}

.cc-tab-panel.is-active {
    display: block;
}

.cc-columns {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 20px;
    align-items: start;
}

.cc-columns--1 {
    grid-template-columns: minmax(0, 1fr);
    max-width: 420px;
}

@media (max-width: 900px) {
    .cc-columns {
        grid-template-columns: 1fr;
    }
}

.cc-setting-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 18px;
}

.cc-setting-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-bottom: 10px;
    margin-bottom: 6px;
    border-bottom: 1px solid var(--color-border);
}

.cc-setting-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-md);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    flex-shrink: 0;
}

.cc-setting-card-title {
    margin: 0;
    font-size: 12px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text);
}

.cc-modules-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 20px;
}

@media (max-width: 900px) {
    .cc-modules-grid {
        grid-template-columns: 1fr;
    }
}

.cc-module-card {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 18px;
}

.cc-module-card.is-planned {
    background: var(--color-bg);
}

.cc-module-card-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.cc-module-card-identity {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

.cc-module-card-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: var(--radius-md);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    flex-shrink: 0;
}

.cc-module-card.is-planned .cc-module-card-icon {
    background: var(--status-neutral-soft);
    color: var(--status-neutral);
}

.cc-module-card-name {
    margin: 0;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--color-text);
}

.cc-module-card-tagline {
    margin: 2px 0 0;
    font-size: 12px;
    color: var(--color-text-muted);
}

.cc-module-card-desc {
    margin: 14px 0 0;
    font-size: 12.5px;
    line-height: 1.55;
    color: var(--color-text-muted);
}

.cc-module-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--color-border);
    font-size: 12px;
    color: var(--color-text-muted);
}

.cc-module-card-foot a {
    display: flex;
    align-items: center;
    gap: 4px;
    font-weight: 700;
    color: var(--color-accent);
    text-decoration: none;
}

/* ============================================================================
   Rollen & Berechtigungen (templates/admin/roles.twig) - Master-Detail-Layout: 30% Rollenliste
   links, 70% Berechtigungs-Panels rechts (JS blendet nur das aktive Panel ein, alle sind
   serverseitig vorgerendert - siehe public/js/roles-admin.js).
   ============================================================================ */
.roles-layout {
    display: flex;
    align-items: stretch;
    min-height: 480px;
}

.roles-list {
    flex: 0 0 30%;
    max-width: 320px;
    border-right: 1px solid var(--color-border);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.roles-list-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
    cursor: pointer;
}

.roles-list-item:hover {
    background: var(--color-accent-soft);
}

.roles-list-item.is-active {
    background: var(--color-accent);
    color: #fff;
}

.roles-list-item.is-active .icon-btn {
    color: #fff;
}

.roles-permissions {
    flex: 1 1 70%;
    padding: 20px;
    min-width: 0;
}

.roles-category-card {
    margin-bottom: 16px;
}

.roles-category-card:last-child {
    margin-bottom: 0;
}

.roles-category-card h3 {
    margin: 0 0 12px;
    font-size: 14px;
}

.roles-tier-group {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.roles-tier-option {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    cursor: pointer;
}

.roles-tier-option input[type="radio"] {
    margin-top: 3px;
    flex-shrink: 0;
}

.roles-tier-option-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 13px;
}

.roles-tier-option-text small {
    color: var(--color-text-muted);
    font-weight: 400;
}

.roles-self-service-warning {
    margin-top: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.roles-flag-toggle {
    margin-bottom: 14px;
}

.roles-flag-toggle:last-child {
    margin-bottom: 0;
}

.roles-flag-toggle strong {
    display: block;
    font-size: 13px;
}

.roles-flag-toggle small {
    display: block;
    color: var(--color-text-muted);
    font-weight: 400;
    font-size: 12px;
}

@media (max-width: 900px) {
    .roles-layout {
        flex-direction: column;
    }

    .roles-list {
        flex: 0 0 auto;
        max-width: none;
        border-right: none;
        border-bottom: 1px solid var(--color-border);
        flex-direction: row;
        flex-wrap: wrap;
    }
}

/* ============================================================================
   Einstellungen & Profil (templates/settings.twig) - vereinte Selbstbedienungsseite (Profil,
   Sicherheit, Darstellung, Sprache, Praeferenzen). Einspaltig, zentriert, UniFi-OS-Underline-
   Tabs oben (Nutzer-Wunsch 2026-08-17 "im echten UniFi OS / Site Manager Style").
   ============================================================================ */
.profile-wrap {
    max-width: 760px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 24px;
}

/* Flache Underline-Tabs statt der wiederverwendbaren .control-period-toggle-Pille (die bleibt
   fuer CoreDesk Control/Executive-Analytics unveraendert) - eigene Balkenlinie statt Container-
   Hintergrund, aktiver Tab per Textfarbe + 2px-Unterstrich direkt auf der Trennlinie. */
.settings-tabs {
    display: flex;
    align-items: center;
    gap: 28px;
    max-width: 760px;
    margin: 0 auto 32px;
    border-bottom: 1px solid var(--color-border);
}

.settings-tab {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 12px 2px;
    margin-bottom: -1px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
    border-bottom: 2px solid transparent;
    transition: color .12s ease, border-color .12s ease;
}

.settings-tab:hover {
    color: var(--color-text);
    text-decoration: none;
}

.settings-tab.is-active {
    color: var(--color-accent);
    border-bottom-color: var(--color-accent);
}

/* Kartenkopf-Typografie vereinheitlicht: Titel etwas praesenter als der App-weite Standard-h2
   (14px), Unterschrift direkt darunter eng gefuehrt statt mit dem Standard-Absatzabstand. Bewusst
   nur unter .profile-wrap skopiert - .card wird app-weit genutzt und soll ausserhalb dieser Seite
   unveraendert bleiben. */
.profile-wrap .card h2 {
    font-size: 16px;
    font-weight: 600;
}

.profile-wrap .card h2 + p.text-muted {
    margin-top: 2px;
    margin-bottom: 16px;
    font-size: 12px;
}

/* MFA-Einrichtung im Profil (Mein Profil > Sicherheit) - QR-Code + Secret-Fallback + Bestaetigungs-
   code, identisch zum erzwungenen Setup-Flow bei /login/setup-mfa, nur als eingeloggter
   Self-Service-Block statt Vollbild-Formular. */
.mfa-setup-block {
    margin-top: 12px;
    padding: 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-width: 320px;
}

.mfa-setup-block svg { width: 180px; height: 180px; }

/* Secret-Fallback-Text (Base32) ist ~52 Zeichen am Stueck ohne Leerzeichen - ohne diese Klasse
   sprengt er die Kartenbreite (max-width:320px oben) statt umzubrechen. Betrifft sowohl dieses
   Self-Service-Setup als auch den identischen erzwungenen Flow bei /login/setup-mfa. */
.mfa-secret {
    font-family: monospace;
    font-size: 12.5px;
    word-break: break-all;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
}

.mfa-recovery-codes-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    margin-top: 16px;
    font-family: monospace;
    font-size: 15px;
}

.mfa-recovery-codes-grid code {
    padding: 8px 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    text-align: center;
}

/* Live-Vorschau der Hub-Begruessung (Mein Profil > Personalisierung) - zeigt alle drei
   Tageszeit-Phrasen des aktuell gewaehlten Modus, das gerade aktive Zeitfenster wird hervorgehoben
   (dieselben Fenster wie DashboardController::classicGreeting()/phraseForHour()). Wird per JS bei
   jeder Aenderung (Modus, Region, eigene Phrasen) neu berechnet, ohne Server-Roundtrip. */
.greeting-preview {
    margin-top: 20px;
    padding: 14px 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg);
}

.greeting-preview-title {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    margin-bottom: 10px;
}

.greeting-preview-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
}

.greeting-preview-row + .greeting-preview-row {
    margin-top: 2px;
}

.greeting-preview-row.is-active {
    background: var(--color-accent-soft);
}

.greeting-preview-label {
    font-size: 12px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.greeting-preview-phrase {
    font-size: 14px;
    font-weight: 600;
    text-align: right;
    overflow-wrap: anywhere;
}

.greeting-preview-row.is-empty .greeting-preview-phrase {
    color: var(--color-text-muted);
    font-weight: 400;
}

.greeting-preview-row.is-active .greeting-preview-phrase {
    color: var(--color-accent);
}

.greeting-preview-now-badge {
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-accent);
    background: var(--color-surface);
    border: 1px solid var(--color-accent);
    border-radius: 999px;
    padding: 1px 7px;
    margin-left: 8px;
    flex-shrink: 0;
}

/* Groesserer Segment-Umschalter (Design/Sprache) - bewusst eigenstaendig von der kleinen
   Topbar-.theme-switch-Pille, da hier mehr visuelles Gewicht gewuenscht ist (Icon + Label
   nebeneinander liegende Karten statt schlichter Icon-Buttons). */
.theme-choice-group {
    display: flex;
    gap: 12px;
}

.theme-choice-option {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    flex: 1;
    padding: 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    color: var(--color-text-muted);
    text-decoration: none;
    font-size: 13px;
    font-weight: 600;
    text-align: center;
}

.theme-choice-option:hover {
    border-color: var(--color-accent);
    color: var(--color-text);
}

.theme-choice-option.is-active {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

/* Akzentfarben-Swatches (Profil > Erscheinungsbild, siehe App\Support\AccentColor) - runde
   Farbkacheln analog zum Personio-Customization-Panel: feste Preset-Farben plus ein letztes
   "Custom"-Swatch, das einen nativen <input type="color"> ueberlagert (siehe profile.twig). */
.accent-swatch-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 10px;
}

.accent-swatch {
    position: relative;
    width: 36px;
    height: 36px;
    border-radius: 999px;
    border: 2px solid transparent;
    padding: 0;
    cursor: pointer;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}

.accent-swatch.is-active {
    border-color: var(--color-text);
    box-shadow: 0 0 0 2px var(--color-surface), 0 0 0 4px var(--color-text);
}

.accent-swatch--custom {
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-bg);
    color: var(--color-text-muted);
}

.accent-swatch--custom.is-active {
    color: #fff;
}

.accent-swatch--custom input[type="color"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    border: none;
    padding: 0;
}

/* CoreDesk Backup: Speicherziel-Auswahl-Grid (admin/backup_destinations.twig). Nur ein Ziel
   (OneDrive) ist derzeit ein echter Link, alle anderen sind ausgegraute Platzhalter fuer
   kuenftige Treiber - .storage-card--disabled entzieht Hover/Cursor/Farbe komplett. */
.storage-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 12px;
}

.storage-card {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    text-decoration: none;
    color: var(--color-text);
    transition: border-color 0.15s ease, background 0.15s ease;
}

a.storage-card:hover {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
}

.storage-card--disabled {
    cursor: not-allowed;
    opacity: 0.55;
}

.storage-icon-wrapper {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

a.storage-card:hover .storage-icon-wrapper {
    background: var(--color-accent);
    color: #FFFFFF;
}

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

.storage-name {
    font-size: 14px;
    font-weight: 500;
}

.storage-status {
    font-size: 12px;
    color: var(--color-text-muted);
}

.storage-status--active {
    color: var(--status-active);
    font-weight: 600;
}

.storage-footer-note {
    font-size: 13px;
    color: var(--color-text-muted);
    line-height: 1.5;
    border-top: 1px solid var(--color-border);
    padding-top: 16px;
    margin-top: 20px;
}

.dashboard-tile-empty {
    color: var(--color-text-muted);
    font-size: 13px;
}

.tile-info-block {
    margin-bottom: 18px;
}

.tile-info-block:last-child {
    margin-bottom: 0;
}

.tile-info-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    font-weight: 600;
    margin-bottom: 6px;
}

.tile-info-value {
    font-size: 14px;
}

.tile-info-value small {
    display: block;
    color: var(--color-text-muted);
    font-size: 12px;
    margin-top: 2px;
}

.call-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--color-accent);
    font-weight: 600;
}

.call-link .icon {
    width: 14px;
    height: 14px;
}

.maps-route-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
    padding: 10px 14px;
    min-height: 44px;
    border-radius: var(--radius-md);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-size: 13px;
    font-weight: 600;
    text-decoration: none;
    transition: background-color 0.15s ease, transform 0.1s ease;
    -webkit-tap-highlight-color: transparent;
}

.maps-route-link .icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.maps-route-link:hover {
    background: var(--color-accent);
    color: #fff;
}

.maps-route-link:active {
    transform: scale(0.97);
}

/* Wiki / Markdown */
.markdown-body h1, .markdown-body h2, .markdown-body h3 {
    margin: 24px 0 12px;
}

.markdown-body h1:first-child, .markdown-body h2:first-child, .markdown-body h3:first-child {
    margin-top: 0;
}

.markdown-body p {
    margin: 0 0 14px;
    line-height: 1.6;
}

.markdown-body ul, .markdown-body ol {
    margin: 0 0 14px;
    padding-left: 22px;
}

.markdown-body li {
    margin-bottom: 4px;
}

.markdown-body code {
    background: var(--color-bg);
    padding: 2px 5px;
    border-radius: 4px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
}

.markdown-body pre {
    background: var(--color-bg);
    padding: 14px;
    border-radius: var(--radius-sm);
    overflow-x: auto;
    margin: 0 0 14px;
}

.markdown-body pre code {
    background: none;
    padding: 0;
}

.markdown-body blockquote {
    border-left: 3px solid var(--color-border);
    padding-left: 14px;
    color: var(--color-text-muted);
    margin: 0 0 14px;
}

.markdown-body table {
    border-collapse: collapse;
    margin-bottom: 14px;
}

.markdown-body table th, .markdown-body table td {
    border: 1px solid var(--color-border);
    padding: 6px 10px;
}

/* Verwaltung > Feature-Konzepte (templates/admin/_feature_concepts_content.twig) - dokumentiert
   Architektur/Berechnungslogik fuer noch nicht (oder teilweise) gebaute Features, z.B. CoreDesk
   People Health Radar. Eigenes Praefix statt Wiederverwendung von .card-Unterklassen, damit
   spaetere Konzept-Sektionen sich nicht mit anderen Modulen ueberschneiden. */
.concept-pattern-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    margin: 12px 0;
}

.concept-pattern-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 6px;
}

.concept-pattern-letter {
    width: 24px;
    height: 24px;
    border-radius: var(--radius-sm);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: 700;
    font-size: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.concept-formula {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 12px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    overflow-x: auto;
    margin: 8px 0;
    white-space: pre;
}

/* .callout ersetzt das fruehere, auf Feature-Konzepte beschraenkte .concept-callout (immer
   warnungsfarben) - jetzt mit neutralem Akzent-Default + .is-warning-Modifier, gemeinsam genutzt
   von Feature-Konzepten UND CoreDesk Academy (siehe unten). */
.callout {
    display: flex;
    gap: 12px;
    border: 1px solid var(--color-accent-glow);
    background: var(--color-accent-soft);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    margin: 16px 0;
    font-size: 13px;
}

.callout svg { flex-shrink: 0; margin-top: 1px; color: var(--color-accent); }
.callout strong { display: block; margin-bottom: 2px; }

.callout.is-warning {
    border-color: var(--status-warning);
    background: var(--status-warning-soft);
}

.callout.is-warning svg { color: #A66A1E; }
[data-theme="dark"] .callout.is-warning svg { color: var(--status-warning); }

/* .is-critical - Zero-Risk-Gate-Sperren im Abrechnungs-Simulator (siehe billing-simulator.js):
   nutzt bewusst dieselbe --color-danger-* Palette wie Fehlermeldungen, nicht die Warnungsfarbe -
   eine kritische Satz-Luecke ist keine Warnung, sondern ein Grund, den scharfen Lauf zu verweigern. */
.callout.is-critical {
    border-color: var(--color-danger-border);
    background: var(--color-danger-bg-strong);
}

.callout.is-critical svg,
.callout.is-critical strong { color: var(--color-danger-text); }

/* .is-tip - positive/hilfreiche Hinweise (z.B. Academy-Kapitel "Abrechnung", Nutzer-Feedback
   2026-08-02: gruene Tipp-Boxen fuer Sofort-Abrechnung), dieselbe --status-active-Palette wie
   status-dot--active/.doc-process-card.is-approve - EIGENE Farbfamilie statt .is-warning/-critical,
   da ein Tipp keine Warnung ist. */
.callout.is-tip {
    border-color: var(--status-active);
    background: var(--status-active-soft);
}

.callout.is-tip svg,
.callout.is-tip strong { color: var(--status-active); }

/* Kontextuelle Hilfe-Tooltips (Nutzer-Feedback 2026-08-02: "?"-Icons an Abrechnungs-Buttons/
   -Ueberschriften, ersparen das Nachschlagen in der Academy) - reines CSS-Popover ueber
   :hover/:focus-within, kein JS, keine neue Bibliothek (siehe partials/help_tip.twig). Bewusst
   dunkle, immer-dunkle Bubble unabhaengig vom aktuellen Theme (wie ein systemeigener Tooltip) statt
   der hellen .callout-Flaeche - Tooltips sollen sich klar vom Seiteninhalt abheben, nicht wie ein
   weiterer Inhaltsblock wirken. */
.help-tip {
    position: relative;
    display: inline-flex;
    align-items: center;
    vertical-align: middle;
    margin-left: 5px;
}

.help-tip-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    padding: 0;
    border: none;
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: color .12s ease;
}

.help-tip-icon:hover,
.help-tip-icon:focus-visible { color: var(--color-accent); }

.help-tip--warning .help-tip-icon,
.help-tip--critical .help-tip-icon { color: var(--status-warning); }
.help-tip--critical .help-tip-icon { color: var(--color-danger-text); }

.help-tip-bubble {
    position: absolute;
    left: 0;
    top: calc(100% + 8px);
    z-index: 50;
    width: max-content;
    max-width: 260px;
    padding: 10px;
    border-radius: var(--radius-md);
    background: #0f172a;
    color: #f1f5f9;
    font-size: 12px;
    font-weight: 500;
    line-height: 1.5;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.28);
    opacity: 0;
    visibility: hidden;
    transform: translateY(2px);
    transition: opacity .12s ease, transform .12s ease, visibility .12s;
    pointer-events: none;
}

.help-tip:hover .help-tip-bubble,
.help-tip:focus-within .help-tip-bubble {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.help-tip--critical .help-tip-bubble { border: 1px solid var(--color-danger-text); }
.help-tip--warning .help-tip-bubble { border: 1px solid var(--status-warning); }

/* Modifier fuer Trigger nahe am rechten Rand (z.B. Toolbar-Ende) - Bubble oeffnet nach links statt
   nach rechts, sonst laeuft sie bei .help-tip-bubble{left:0} auf schmaleren Fenstern ueber den
   Viewport-/Tabellenrand hinaus. */
.help-tip--align-right .help-tip-bubble { left: auto; right: 0; }

/* Rueckwaerts-Alias fuer die Feature-Konzepte-Ueberschrift-Zeile (.concept-callout-title) -
   dieselbe Optik wie bisher, nur ueber die neue .callout-Basis erreichbar. */
.concept-callout-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 700;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #A66A1E;
    margin-bottom: 8px;
}
[data-theme="dark"] .concept-callout-title { color: var(--status-warning); }

/* ================================================================================
   CoreDesk Academy (templates/academy/*) - internes Bedienungs-/Konfigurations-
   handbuch fuer Mitarbeiter und Admins. Komponenten-Klassen 1:1 aus dem geprueften
   Artefakt "CoreDesk Projects - Erweiterungskonzept" uebernommen (dessen Farb-/
   Radius-/Schatten-Tokens waren bereits identisch zu den echten app.css-Variablen
   oben), ergaenzt um Dark-Mode-Anpassungen wo noetig.
   ================================================================================ */

.doc-hero {
    padding: 32px 0 24px;
    border-bottom: 1px solid var(--color-border);
    background: linear-gradient(180deg, var(--color-accent-soft), transparent 140%);
    margin: -24px -24px 0;
    padding-left: 24px;
    padding-right: 24px;
}

.doc-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-accent);
    background: var(--color-accent-soft);
    padding: 4px 10px;
    border-radius: 999px;
    margin-bottom: 14px;
}

.doc-hero h1 { font-size: 26px; max-width: 720px; margin: 0 0 6px; }
.doc-hero p.lead { font-size: 14px; color: var(--color-text-muted); max-width: 660px; margin: 0; }

.toc-bar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    margin: 0 -24px;
}

.toc-inner {
    display: flex;
    gap: 4px;
    overflow-x: auto;
    padding: 10px 24px;
    scrollbar-width: thin;
}

.toc-inner a {
    flex-shrink: 0;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--color-text-muted);
    padding: 7px 12px;
    border-radius: 999px;
    white-space: nowrap;
    text-decoration: none;
    transition: background 0.12s ease, color 0.12s ease;
}
.toc-inner a:hover { background: var(--color-bg); color: var(--color-text); }
.toc-inner a.is-active { background: var(--color-accent-soft); color: var(--color-accent); }

.doc-section { padding: 32px 0 8px; border-bottom: 1px solid var(--color-border); scroll-margin-top: 64px; }
.doc-section:last-child { border-bottom: none; }
.section-kicker { font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 8px; }
.section-head { max-width: 760px; margin-bottom: 20px; }
.section-head h2 { font-size: 19px; margin: 0 0 6px; }
.section-head p { color: var(--color-text-muted); font-size: 13.5px; margin: 0; }

/* Vier-Schritte-Prozesskarten (Kapitel "Abrechnung & Ticket-/Leistungsfreigabe", Nutzer-Feedback
   2026-08-02: "schwer lesbare Textwueste" - visueller Ablauf-Ueberblick ganz oben statt reinem
   Fliesstext). Bewusst eigene, benannte Karten statt Wiederverwendung von .er-grid (Datenmodell-
   Kontext) oder .billing-stepper (kompakte Inline-Leiste im Abrechnungs-Modul selbst) - hier
   groessere, nebeneinander stehende Karten mit farblich unterschiedlichem Akzentstreifen je Schritt
   (Modifier-Klassen .is-approve/.is-adhoc/.is-run/.is-archive), analog zur farblichen Semantik von
   .type-pill weiter unten. Bewusst keine Emoji (Nutzer-Wunsch nannte 🟢⚡🔄📁), sondern dasselbe
   SVG-Icon-System wie der Rest der App (partials/icons.twig) - Emoji kommen sonst nirgends in der
   Academy vor und wuerden gegen die etablierte visuelle Sprache brechen. */
.doc-process-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 12px;
    margin: 20px 0 28px;
}

@media (max-width: 900px) { .doc-process-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .doc-process-grid { grid-template-columns: 1fr; } }

.doc-process-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 16px 14px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    box-shadow: var(--shadow-card);
    border-top: 3px solid var(--doc-process-accent, var(--color-accent));
}

.doc-process-card.is-approve { --doc-process-accent: var(--status-active); }
.doc-process-card.is-adhoc { --doc-process-accent: var(--color-accent); }
.doc-process-card.is-run { --doc-process-accent: var(--status-callback); }
.doc-process-card.is-archive { --doc-process-accent: var(--status-neutral); }

.doc-process-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    font-size: 11.5px;
    font-weight: 700;
    color: #fff;
    background: var(--doc-process-accent, var(--color-accent));
}

.doc-process-card svg { color: var(--doc-process-accent, var(--color-accent)); margin-top: 2px; }
.doc-process-card strong { font-size: 13.5px; }
.doc-process-card span { font-size: 12px; color: var(--color-text-muted); line-height: 1.4; }

/* CSS-only Tab-Navigation (Nutzer-Feedback 2026-08-02: 3 klare Abschnitte statt der bisherigen
   8-Punkte-TOC-Leiste). Radio-Input+Label-Trick statt JS - die Academy-Seiten sind bewusst
   vollstaendig ohne eigenes JS gehalten (siehe layout-academy.twig, kein <script>-Include), ein
   Tab-Wechsel ist hier reiner Anzeige-Zustand ohne serverseitige Daten, dafuer reicht CSS. Radios
   sitzen unmittelbar VOR .doc-tab-nav und .doc-tab-panels im Markup (direkte Geschwister noetig,
   der allgemeine Geschwister-Selektor ~ verlangt das). */
.doc-tab-radio {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.doc-tab-nav {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    gap: 4px;
    padding: 10px 0;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    margin: 0 -24px 0;
    padding-left: 24px;
    padding-right: 24px;
    overflow-x: auto;
}

.doc-tab-label {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text-muted);
    padding: 8px 14px;
    border-radius: 999px;
    cursor: pointer;
    white-space: nowrap;
    transition: background .12s ease, color .12s ease;
}

.doc-tab-label:hover { background: var(--color-bg); color: var(--color-text); }

.doc-tab-panel { display: none; }

#doc-tab-workflow:checked ~ .doc-tab-nav label[for="doc-tab-workflow"],
#doc-tab-storno:checked ~ .doc-tab-nav label[for="doc-tab-storno"],
#doc-tab-practices:checked ~ .doc-tab-nav label[for="doc-tab-practices"] {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

#doc-tab-workflow:checked ~ .doc-tab-panels #doc-panel-workflow,
#doc-tab-storno:checked ~ .doc-tab-panels #doc-panel-storno,
#doc-tab-practices:checked ~ .doc-tab-panels #doc-panel-practices {
    display: block;
}

/* Tastatur-Shortcut-Spickzettel (Nutzer-Feedback 2026-08-02) - nutzt 1:1 dieselbe .approval-shortcut
   kbd-Optik wie die echte Shortcuts-Leiste im Freigabe-Tab (siehe admin/_billing_shortcuts_bar.twig),
   damit die Doku exakt wie das echte UI-Element aussieht statt einer abweichenden Nachbildung. */
.doc-shortcut-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 10px 16px;
    margin: 14px 0 6px;
    padding: 14px 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg);
}

/* Datenmodell-/Architektur-Uebersichten (Entity-Karten) */
.er-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 20px; }
@media (max-width: 900px) { .er-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 600px) { .er-grid { grid-template-columns: 1fr; } }
.er-table { border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface); overflow: hidden; box-shadow: var(--shadow-card); }
.er-table.is-existing { opacity: 0.72; }
.er-table-head { padding: 9px 12px; font-weight: 700; font-size: 12.5px; background: var(--color-bg); border-bottom: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; }
.er-table.is-new .er-table-head { background: var(--color-accent-soft); color: var(--color-accent); }
.er-tag { font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-muted); }
.er-table-rows { padding: 6px 0; }
.er-row { padding: 4px 12px; font-size: 11.5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--color-text-muted); display: flex; gap: 6px; }
.er-row b { color: var(--color-text); font-weight: 600; }
.er-row .fk { color: var(--color-accent); }

/* Code-Bloecke (Konfigurationsbeispiele, Ausschnitte) */
.code-block { background: var(--code-bg); border-radius: var(--radius-md); overflow: hidden; margin: 14px 0; box-shadow: var(--shadow-card); border: 1px solid rgba(255,255,255,0.06); }
.code-block-head { display: flex; align-items: center; justify-content: space-between; padding: 8px 14px; border-bottom: 1px solid rgba(255,255,255,0.08); font-size: 11px; color: #94A3B8; font-weight: 600; letter-spacing: 0.03em; }
.code-block pre { margin: 0; padding: 16px; overflow-x: auto; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.3px; line-height: 1.65; color: var(--code-text); }
.tok-kw { color: var(--code-keyword); font-weight: 600; }
.tok-str { color: var(--code-string); }
.tok-num { color: var(--code-number); }
.tok-com { color: var(--code-comment); font-style: italic; }

/* Klassischer schwarzer Terminal-Look (z.B. Sonar-Kopieren-Befehle in customers/settings.twig) -
   bewusst eigene Klasse statt .code-block, das den dunkelblauen IDE-Look der Academy hat. */
.code-terminal { display: flex; align-items: center; gap: 6px; margin: 4px 0 8px; background: #000; border-radius: var(--radius-sm); padding: 8px 10px; }
.code-terminal code { flex: 1; background: none; padding: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; color: #33FF33; word-break: break-all; }
.code-terminal .icon-btn { color: #33FF33; }
.code-terminal .icon-btn:hover { color: #6dff6d; }

/* Frage/Antwort- und Spezifikations-Tabellen */
table.spec-table { width: 100%; border-collapse: collapse; font-size: 12.5px; margin: 10px 0; }
table.spec-table th { text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-muted); padding: 6px 10px; border-bottom: 1px solid var(--color-border); }
table.spec-table td { padding: 8px 10px; border-bottom: 1px solid var(--color-border); vertical-align: top; }
table.spec-table tr:last-child td { border-bottom: none; }
table.spec-table code { background: var(--color-bg); padding: 1px 5px; border-radius: 4px; }

/* Kleine Bedien-Mockups innerhalb eines Academy-Kapitels (z.B. vereinfachte Ticket-Karte,
   vereinfachter Kalender-Tag) - eigenes, bewusst schlankes Praefix, analog .concept-demo-card,
   nur fuer Faelle ohne 1:1 wiederverwendbares Original-CSS. */
.academy-demo-row { display: flex; gap: 14px; overflow-x: auto; padding-bottom: 6px; margin: 14px 0; }
.academy-demo-card { background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 12px; flex-shrink: 0; }

/* ITIL-Typ-Pills (Kapitel "Ticket-Typen, SLA & Abrechnungsmodelle") - dieselbe Farbsemantik wie
   die produktive .tdv2-type-badge (Incident/Service-Request), zusaetzlich um "Projekt" ergaenzt
   und hier bewusst eigenstaendig gehalten, da ticket-new-design.css im Academy-Layout nicht
   eingebunden ist. */
.type-pill { display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; border-radius: 999px; font-size: 12px; font-weight: 700; }
.type-pill--incident { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }
.type-pill--service_request { background: var(--color-accent-soft); color: var(--color-accent); }
.type-pill--project { background: var(--status-callback-soft); color: var(--status-callback); }

.tt-proto-inline-note { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--color-text-muted); background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 8px 12px; margin: -6px 0 18px; }
.tt-proto-status { border-top: 1px solid var(--color-border); margin-top: 4px; padding-top: 14px; }
.tt-proto-status-title { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--color-text-muted); margin-bottom: 10px; }
.tt-proto-status-row { display: flex; align-items: center; justify-content: space-between; font-size: 12.5px; padding: 5px 0; }
.tt-proto-status-row span { color: var(--color-text-muted); }
.tt-proto-status-row strong { font-weight: 600; }

/* Eigenstaendige Academy-Seitenhuelle (layout-academy.twig) - bewusst OHNE die Haupt-Sidebar
   der App, damit die Academy wie ein eigenstaendiger Bereich wirkt (siehe templates/guest/*.twig
   fuer das gleiche Muster bei Gastseiten). */
.academy-shell { max-width: 980px; margin: 0 auto; padding: 24px 16px 48px; }
.academy-topbar { display: flex; align-items: center; justify-content: space-between; padding: 4px 0 20px; margin-bottom: 8px; border-bottom: 1px solid var(--color-border); }
.academy-content { display: flex; flex-direction: column; }

.academy-search { position: relative; margin-top: 20px; }
.academy-search-input-wrap { display: flex; align-items: center; gap: 10px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 10px 14px; color: var(--color-text-muted); }
.academy-search-input { flex: 1; border: none; background: transparent; font-size: 14px; color: var(--color-text); outline: none; }
.academy-search-input::placeholder { color: var(--color-text-muted); }
.academy-search-results { display: none; position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 20; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); box-shadow: var(--shadow-card); max-height: 360px; overflow-y: auto; }
.academy-search-results.is-active { display: block; }
.academy-search-result { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 14px; text-decoration: none; color: inherit; border-bottom: 1px solid var(--color-border); }
.academy-search-result:last-child { border-bottom: none; }
.academy-search-result:hover { background: var(--color-bg); }
.academy-search-result-title { font-size: 13.5px; font-weight: 600; }
.academy-search-result-meta { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.academy-search-result-track { font-size: 11.5px; color: var(--color-text-muted); }
.academy-search-empty { padding: 14px; font-size: 13px; color: var(--color-text-muted); text-align: center; }

.concept-radar-demo {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin: 16px 0;
    max-width: 500px;
}

.concept-demo-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 14px;
    text-align: center;
}

.concept-demo-score {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11.5px;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 999px;
    display: inline-block;
    margin-top: 8px;
}

.concept-ai-bubble {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-accent);
    border-radius: var(--radius-sm);
    padding: 14px 16px;
    font-size: 13px;
}

/* Kunden-Wiki: Tiptap-Editor (templates/wiki/form.twig, public/js/wiki-editor.js) - der
   Container traegt zusaetzlich .markdown-body, damit Bearbeiten und Ansehen (wiki/show.twig)
   optisch identisch aussehen; hier nur der Editor-eigene Rahmen/Toolbar-Chrome. */
.wiki-editor-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: 6px;
    border: 1px solid var(--color-border);
    border-bottom: none;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: var(--color-bg);
}

.wiki-editor-toolbar-btn {
    min-width: 30px;
    height: 30px;
    padding: 0 8px;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-text);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.wiki-editor-toolbar-btn:hover {
    background: var(--color-accent-soft);
}

.wiki-editor-toolbar-btn.is-active {
    background: var(--color-accent);
    color: #fff;
}

.wiki-editor-toolbar-divider {
    width: 1px;
    height: 20px;
    background: var(--color-border);
    margin: 0 4px;
}

.tiptap-editor-content {
    border: 1px solid var(--color-border);
    border-radius: 0 0 var(--radius-sm) var(--radius-sm);
    padding: 14px 16px;
    min-height: 320px;
    max-height: 640px;
    overflow-y: auto;
}

/* min-height sitzt bewusst NICHT nur auf .tiptap-editor-content (dem Wrapper), sondern auch auf
   dem eigentlichen kontenteditierbaren Tiptap-Kindelement selbst - Tiptap haengt .ProseMirror als
   eigenes Kind in den Wrapper ein, das ohne eigene Mindesthoehe nur so hoch wie sein Inhalt ist
   (bei leerem/kurzem Artikel oft nur eine Zeile). Der grosse leere Bereich darunter GEHOERT zwar
   optisch zur Editor-Box, liegt aber ausserhalb des klickbaren/fokussierbaren .ProseMirror-Bereichs
   - ein Klick dort fokussiert nichts, Tippen bleibt wirkungslos (Nutzer-Bugreport: "wenn ich in die
   textarea tippe, passiert nichts"). 290px = 320px Wrapper-Mindesthoehe minus 2x14px Padding. */
.tiptap-editor-content .ProseMirror {
    outline: none;
    min-height: 290px;
}

/* Eingebettete Artikel-Bilder (Drag&Drop/Paste-Upload, siehe wiki-editor.js/WikiImageStorage) -
   sowohl im Editor selbst als auch in der spaeteren Artikel-Ansicht (.markdown-body img, wiki/
   codex show.twig), damit Bearbeiten und Ansehen optisch konsistent bleiben. */
.tiptap-editor-content img,
.markdown-body img {
    max-width: 100%;
    height: auto;
    border-radius: 8px;
    display: block;
    margin: 10px 0;
}
.markdown-body img { cursor: zoom-in; }

.wiki-editor-upload-status {
    display: none;
    margin-bottom: 8px;
    padding: 7px 11px;
    border-radius: var(--radius-sm);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-size: 12.5px;
    font-weight: 600;
}
.wiki-editor-upload-status.is-error {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

/* CoreDesk Codex: globales Unternehmens-Wiki (templates/codex/*, CodexController) - Kombi-Shell
   1:1 wie /admin/billing bzw. /customers?view=list (.asset-list-shell/.asset-list-sidebar/
   .asset-list-table-pane), Kategorie-Links nutzen bewusst dieselben .customer-sidebar-link*-
   Klassen wie die Kunden-Sidebar (siehe layout.twig) statt einer dritten Sidebar-Optik. */
.codex-new-category-form {
    display: flex;
    gap: 6px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid var(--color-border);
}
.codex-new-category-form input {
    flex: 1;
    min-width: 0;
    padding: 7px 9px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    background: var(--color-surface);
    color: var(--color-text);
}
.codex-new-category-form button {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    flex-shrink: 0;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
}
.codex-new-category-form button:hover { color: var(--color-accent); border-color: var(--color-accent); }
.customer-shell.is-sidebar-collapsed .codex-new-category-form,
.asset-list-shell.is-sidebar-collapsed .codex-new-category-form { display: none; }

.codex-overview-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
.codex-overview-section-title { display: flex; align-items: center; gap: 8px; margin: 0 0 12px; font-size: 13px; font-weight: 800; color: var(--color-text); }
.codex-article-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.codex-article-list li { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--color-border); font-size: 13px; }
.codex-article-list li:last-child { border-bottom: none; }
.codex-article-list-meta { display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; font-size: 11.5px; color: var(--color-text-muted); white-space: nowrap; }
.codex-article-meta { display: flex; align-items: center; gap: 8px; margin: -4px 0 16px; font-size: 12px; color: var(--color-text-muted); }

/* Lightbox fuers Vergroessern eingebetteter Artikel-Bilder per Klick (wiki-image-lightbox.js) -
   gleiches Verdunkeln-Backdrop-Muster wie .billing-flyout-bg. */
.wiki-lightbox-overlay {
    position: fixed; inset: 0; z-index: 950;
    display: flex; align-items: center; justify-content: center;
    padding: 40px;
    background: rgba(16,24,40,.75);
    opacity: 0; pointer-events: none;
    transition: opacity .2s ease;
    cursor: zoom-out;
}
.wiki-lightbox-overlay.show { opacity: 1; pointer-events: auto; }
.wiki-lightbox-img { max-width: 100%; max-height: 100%; border-radius: 8px; box-shadow: var(--shadow-lg, 0 20px 60px rgba(0,0,0,.4)); }

/* Vault */
.vault-entry-name {
    display: flex;
    align-items: center;
    gap: 10px;
}

.vault-favicon {
    width: 18px;
    height: 18px;
    border-radius: 4px;
    flex-shrink: 0;
    object-fit: contain;
}

.vault-favicon-fallback {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--color-text-muted);
}

/* CoreDesk Vault - Apple-Passwoerter-Stil Master-Detail-Ansicht (siehe templates/vault/index.twig +
   public/js/vault-passwords-view.js), ersetzt Tabelle/Galerie als Standard-Browsing-Ansicht. Nutzt
   die gleiche Flex-Zweispalten-Technik wie .finder-body/.finder-results/.finder-preview, aber als
   volle Seite statt Modal - deshalb eigene Klassennamen statt Wiederverwendung. */
/* Nahtloses Vollflaechen-Layout: body.page-seamless-split macht .main zu einer exakt
   100vh hohen Flex-Spalte (Topbar behaelt ihre natuerliche, variable Hoehe je nach
   Kunde-Namenslaenge/Umbruch - ein fixer calc(100vh - Npx)-Wert waere hier fragil), .content
   verliert sein globales Padding und wird selbst zur flex:1-Fuellflaeche fuer die jeweilige
   Master-Detail-Struktur (Vault: .vault-mdview, Domains: .domain-split-view). Dadurch
   verschwindet die schwebende Card-Optik komplett und die Ansicht verhaelt sich wie ein
   eigenstaendiges App-Fenster mit Scroll ausschliesslich in den inneren Spalten. Generalisierte
   Klasse (vormals Vault-exklusiv "page-vault-seamless") - von jeder Master-Detail-Ansicht
   wiederverwendbar, die denselben nahtlosen Vollflaechen-Look braucht. */
body.page-seamless-split .main {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

body.page-seamless-split .topbar {
    flex-shrink: 0;
}

body.page-seamless-split .content {
    flex: 1;
    min-height: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

/* Leerer-Tresor-Zustand und Fehlerbanner brauchen weiterhin Innenabstand, da hier (anders
   als .vault-mdview) keine eigene Flaeche bis an Sidebar/Topbar heranreicht. */
.vault-seamless-pad {
    padding: 24px 28px;
}

/* Passwork-artige Sidebar (Nutzer-Feedback 2026-08-06) - ersetzt die vormals volle Breite
   einnehmende .vault-side-nav-Kopfleiste. .vault-shell ist der neue aeusserste Flex-Container
   innerhalb von .content, .vault-shell-main haelt entweder den Leer-Zustand
   (.vault-seamless-pad) oder die bestehende Master-Detail-Ansicht (.vault-mdview) - Layout-
   Vorbild ist .archive-sidebar (CoreDesk Archiv), selbe Tokens statt neuer Custom Properties. */
.vault-shell {
    display: flex;
    flex: 1;
    min-height: 0;
}

.vault-shell-main {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    min-width: 0;
}

.vault-sidebar {
    width: 220px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 16px 12px;
    border-right: 1px solid var(--color-border);
    background: var(--color-bg);
    overflow-y: auto;
}

.vault-sidebar-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
}

.vault-sidebar-search svg { flex: 0 0 auto; width: 15px; height: 15px; }

.vault-sidebar-search input {
    border: none;
    background: none;
    outline: none;
    font-size: 13px;
    width: 100%;
    color: var(--color-text);
}

/* Autocomplete-Dropdown fuer die Sidebar-Suche (Nutzer-Feedback 2026-08-07) - liest dieselben
   bereits im DOM gerenderten .vault-mdlist-item-Eintraege wie die bestehende Inline-Filterung
   (kein zusaetzlicher Server-Roundtrip), zeigt aber ergaenzend eine feste Trefferliste ueber den
   Rest der Sidebar, damit ein Treffer per Klick/Pfeiltasten+Enter sofort ausgewaehlt werden kann -
   unabhaengig davon, ob Tag-/Kunden-Filter oder Schnellzugriff ihn in der Liste darunter gerade
   ausblenden wuerden (ein Klick setzt diese bewusst zurueck, siehe vault/index.twig). */
.vault-search-dropdown {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 20;
    max-height: 320px;
    overflow-y: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md, 0 4px 16px rgba(0,0,0,.15));
    padding: 4px;
}

.vault-search-dropdown-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: var(--color-text);
}

.vault-search-dropdown-item:hover,
.vault-search-dropdown-item.is-active {
    background: var(--color-accent-soft);
}

.vault-search-dropdown-item-avatar {
    flex: 0 0 auto;
    width: 24px;
    height: 24px;
    border-radius: var(--radius-sm);
    background: var(--color-bg-hover, color-mix(in srgb, var(--color-text) 8%, transparent));
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
    overflow: hidden;
}

.vault-search-dropdown-item-avatar img { width: 100%; height: 100%; object-fit: contain; }

.vault-search-dropdown-item-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
}

.vault-search-dropdown-item-text strong {
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-search-dropdown-item-text span {
    font-size: 11px;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-search-dropdown-empty {
    padding: 10px 8px;
    font-size: 12.5px;
    color: var(--color-text-muted);
}

.vault-search-dropdown-more {
    padding: 6px 8px;
    font-size: 11px;
    color: var(--color-text-muted);
    text-align: center;
}

.vault-quick-nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.vault-quick-nav-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 8px;
    border: none;
    background: transparent;
    border-radius: var(--radius-sm);
    font-size: 13px;
    /* War --color-text-muted (Nutzer-Feedback 2026-08-13: zu blass/kontrastarm im Vergleich zur
       geteilten Kombi-Shell-Facetten-Sidebar, siehe --sidebar-text-color-Definition oben). */
    color: var(--sidebar-text-color);
    cursor: pointer;
    text-align: left;
}

.vault-quick-nav-item svg { flex: 0 0 auto; }
.vault-quick-nav-item span:not(.vault-quick-nav-badge) { flex: 1; }

.vault-quick-nav-item:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); }

.vault-quick-nav-item.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: 600;
}

.vault-quick-nav-badge {
    flex: 0 0 auto !important;
    background: var(--status-warning-soft);
    color: var(--status-warning);
    font-size: 10px;
    font-weight: 700;
    padding: 1px 6px;
    border-radius: 999px;
}

.vault-sidebar-section {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding-top: 8px;
    border-top: 1px solid var(--color-border);
}

/* Schriftgroesse/Transform/Letter-Spacing an .asset-list-filter-section h4 angeglichen (Nutzer-
   Feedback 2026-08-13, "einheitliche Sektions-Labels app-weit") - das ist die Kombi-Shell-
   Basisregel, der alle anderen Modul-Sidebars bereits folgen; nur Vaults eigene Sidebar war hier
   driftet. Farbe (--color-text-muted) war bereits identisch, daher unveraendert. */
.vault-sidebar-section-title {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
    text-transform: capitalize;
    letter-spacing: .04em;
    padding: 0 8px 2px;
}

.vault-sidebar-select {
    width: 100%;
    padding: 6px 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 12.5px;
}

.vault-sidebar-list {
    display: flex;
    flex-direction: column;
    gap: 1px;
    max-height: 220px;
    overflow-y: auto;
}

.vault-sidebar-list-item {
    display: block;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    color: var(--sidebar-text-color);
    text-decoration: none;
}

.vault-sidebar-list-item:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); text-decoration: none; }

.vault-sidebar-list-item.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: 600;
}

.vault-sidebar-footer {
    margin-top: auto;
    padding-top: 12px;
    border-top: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.vault-sidebar-footer a {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
    font-size: 12.5px;
    color: var(--sidebar-text-color);
    text-decoration: none;
}

.vault-sidebar-footer a:hover { background: color-mix(in srgb, var(--color-text) 5%, transparent); color: var(--color-text); text-decoration: none; }

/* Favoriten-Stern (Nutzer-Feedback 2026-08-06) - Umriss-Stern in Ruhe, gefuellt+amber sobald
   favorisiert. Wiederverwendet dasselbe icons.twig-'star'-Pfad-Icon wie der CSAT-Stern in
   CoreDesk Control, hier nur ueber fill:currentColor umgeschaltet statt einer zweiten SVG-Variante. */
.vault-favorite-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border: none;
    background: none;
    border-radius: var(--radius-sm);
    color: var(--color-text-muted);
    cursor: pointer;
}

.vault-favorite-btn:hover { background: color-mix(in srgb, var(--color-text) 8%, transparent); color: var(--status-warning); }

.vault-favorite-btn.is-favorite {
    color: var(--status-warning);
}

.vault-favorite-btn.is-favorite svg {
    fill: currentColor;
}

.vault-mdview {
    display: flex;
    flex: 1;
    min-height: 0;
    background: var(--color-surface);
}

.vault-mdlist-pane {
    width: 320px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--color-border);
    background: var(--color-bg);
    min-height: 0;
}

.vault-mdlist-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
    padding: 16px 14px 10px;
}

.vault-mdlist-title {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.vault-mdlist-title strong {
    font-size: 15px;
    font-weight: 700;
}

.vault-mdlist-count {
    font-size: 11.5px;
    color: var(--color-text-muted);
}

.vault-mdlist-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.icon-btn.icon-btn-primary {
    border-color: var(--color-accent);
    background: var(--color-accent);
    color: #fff;
}

.icon-btn.icon-btn-primary:hover {
    background: var(--color-accent);
    filter: brightness(1.1);
    color: #fff;
}

.icon-btn.icon-btn-danger {
    border-color: var(--color-danger-border);
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

.icon-btn.icon-btn-danger:hover {
    background: var(--color-danger-bg-strong);
}

.vault-mdlist-sort {
    padding: 5px 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-size: 12px;
    background: var(--color-surface);
    color: var(--color-text);
    font-family: inherit;
    height: 28px;
}

.vault-mdlist-search {
    /* .list-search setzt flex:1 fuer den urspruenglichen horizontalen Toolbar-Kontext - hier
       im vertikalen .vault-mdlist-pane (flex-direction:column) wuerde das stattdessen die
       Hoehe auffressen und Tags/Liste nach unten druecken, deshalb explizit zurueckgesetzt. */
    flex: 0 0 auto;
    max-width: none;
    padding: 0 14px;
    /* Abstand als margin statt padding-bottom: padding-bottom vergroessert die Box, an der
       sich das absolut positionierte .icon (top:50%) ausrichtet, und zieht es dadurch tiefer
       als die Eingabezeile - mit margin bleibt die Box exakt so hoch wie das Input selbst. */
    margin-bottom: 10px;
}

.vault-mdlist-search .icon {
    /* .list-search .icon setzt left:10px relativ zum Padding-Rand des Containers - der ist
       unabhaengig vom eigenen padding-Wert immer am Border-Rand verankert. Das eigene
       padding:0 14px oben rueckt aber das Input (normaler Flow) um 14px ein, das Icon
       (absolut positioniert) folgt dem nicht automatisch mit - deshalb hier explizit um
       das Container-Padding nachjustiert (14px + urspruengliche 10px = 24px). */
    left: 24px;
}

.vault-mdlist-tags {
    padding: 0 14px 10px;
    flex-wrap: wrap;
}

.vault-mdlist {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
    padding: 4px 8px 12px;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    align-content: flex-start;
    justify-content: flex-start;
    /* Nutzer-Feedback 2026-08-13: Eintraege wirkten ohne Zwischenraum "gequetscht", da nur das
       Item-eigene Padding fuer Abstand sorgte - ein echter Gap zwischen den Zeilen macht die
       Liste zusaetzlich zum groesseren Item-Padding unten deutlich luftiger. */
    gap: 8px;
}

.vault-mdlist-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 16px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    color: inherit;
    text-decoration: none;
}

.vault-mdlist-item:hover {
    background: var(--color-surface);
}

.vault-mdlist-item.is-active {
    background: var(--color-accent-soft);
}

.vault-mdlist-item.is-hidden {
    display: none;
}

/* Groesse in der Liste (32px) und im Detail-Header (48px, .--lg) folgt der UniFi-Design-Vorgabe
   2026-08-02 ("Icon-Badges vereinheitlichen") - beide Kontexte teilen dieselbe Basis-Klasse, nur
   die Groesse/der Radius unterscheiden sich ueber den Modifier. */
/* Hellblau getoentes Badge (var(--color-accent-soft)/--color-accent) statt dunkelgrauem Block
   (Nutzer-Feedback 2026-08-02, exaktes Mockup) - gilt fuer Icon UND Initialbuchstaben-Fallback;
   ein geladenes Favicon (echtes Bild, eigene Farben) ueberdeckt es ohnehin per .vault-avatar-square
   img{background:#fff}. */
.vault-avatar-square {
    width: 32px;
    height: 32px;
    flex-shrink: 0;
    border-radius: var(--radius-sm);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 13px;
    overflow: hidden;
}

.vault-avatar-square img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #fff;
}

.vault-avatar-square--lg {
    width: 48px;
    height: 48px;
    font-size: 18px;
    border-radius: var(--radius-md);
}

/* Manuelles Fallback-/Override-Icon (vault_entries.icon) im Avatar-Badge - Groesse haengt vom
   umgebenden Badge ab (32px Liste vs. 48px Detail-Header), Farbe folgt currentColor = #fff wie
   die Initialbuchstaben-Alternative. */
.vault-avatar-square .vault-avatar-icon {
    width: 17px;
    height: 17px;
}

.vault-avatar-square--lg .vault-avatar-icon {
    width: 24px;
    height: 24px;
}

.vault-mdlist-item-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.vault-mdlist-item-text strong {
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-mdlist-item-text span {
    font-size: 11.5px;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-mdlist-warn {
    flex-shrink: 0;
    display: inline-flex;
    color: var(--status-warning);
}

.vault-mddetail-pane {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    min-height: 0;
}

.vault-mddetail-mobile-back {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--color-border);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-accent);
    background: var(--color-surface);
    flex-shrink: 0;
}

.vault-mddetail-body {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
    padding: 32px 32px 40px;
}

/* UniFi-Style Header (Nutzer-Feedback 2026-08-02, ersetzt die vorherige zentrierte
   Riesen-Icon-Darstellung): links Icon-Badge + Titel + Subzeile, rechts die Aktions-Buttons in
   derselben Zeile statt einer separaten Toolbar-Leiste darueber. */
.vault-mddetail-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding-bottom: 20px;
    margin-bottom: 24px;
    border-bottom: 1px solid var(--color-border);
}

.vault-mddetail-header-left {
    display: flex;
    align-items: center;
    gap: 14px;
    min-width: 0;
}

.vault-mddetail-header-text {
    min-width: 0;
}

.vault-mddetail-header h1 {
    font-size: 20px;
    font-weight: 700;
    margin: 0;
    text-wrap: balance;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-mddetail-subtitle {
    font-size: 12px;
    color: var(--color-text-muted);
    font-family: var(--font-mono);
    margin-top: 2px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-mddetail-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

/* Fallback-/Override-Icon-Grid im Anlege-/Bearbeiten-Modal (siehe vault_entries.icon,
   Migration 247) - jede Kachel zeigt das echte SVG aus icons.twig statt eines Textnamens
   (Nutzer-Feedback: "Grid/Dropdown-Picker" statt reinem <select>, analog customer_types.twig,
   aber visuell statt textbasiert). Erste Kachel ("Kein Icon", x-Icon) ist immer vorhanden. */
.vault-icon-picker {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(38px, 1fr));
    gap: 6px;
    margin-top: 4px;
}

.vault-icon-picker-option {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
}

.vault-icon-picker-option:hover {
    border-color: var(--color-accent);
    color: var(--color-text);
}

.vault-icon-picker-option.is-active {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.vault-icon-picker-option:disabled {
    cursor: not-allowed;
    opacity: .6;
}

/* Flaches UniFi-Container-Design (Nutzer-Feedback 2026-08-02, exaktes Mockup): groesserer Radius,
   spuerbarer Schatten, linksbuendig statt zentriert - passt sich damit dem Header darueber an, der
   ebenfalls links beginnt. */
.vault-field-card {
    max-width: 640px;
    margin: 0 0 20px;
    padding: 0 20px 6px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-card);
    overflow: hidden;
}

/* Kicker-Ueberschrift "ZUGANGSDATEN & DETAILS" oben in der Card (Nutzer-Feedback 2026-08-02). */
.vault-field-card-kicker {
    padding: 18px 0 4px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--color-text-muted);
}

.vault-field-card .vault-field-row {
    padding-left: 0;
    padding-right: 0;
}

/* space-between statt fester Label-Spaltenbreite (Nutzer-Feedback 2026-08-02, UniFi-Mockup) -
   Label haengt links, Wert-Cluster rechtsbuendig; jede Zeile bemisst ihre Label-Breite individuell
   statt einer starren Tabellen-Spalte. */
.vault-field-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
}

.vault-field-row:last-child {
    border-bottom: none;
}

.vault-field-row--notes {
    align-items: flex-start;
}

/* Trennzeile fuer "Benutzerdefinierte Felder" INNERHALB der Haupt-Card (Nutzer-Feedback
   2026-08-02: eine zweite separate Card darunter passte optisch nicht zum Design) - vorher
   komplett ungestylt, wirkte wie ein unformatiertes <div> zwischen zwei Kaesten. */
.vault-field-card-subheading {
    padding: 12px 0 4px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-border);
}

.vault-field-label {
    flex-shrink: 0;
    font-size: 12.5px;
    color: var(--color-text-muted);
    font-weight: 500;
}

.vault-field-value {
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 6px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
    text-align: right;
}

/* Notizen sind Freitext, der die Zeile fuellen soll statt rechts zu "kleben" wie die uebrigen,
   kurzen Werte (siehe .vault-field-row--notes weiter oben). */
.vault-field-value.is-notes {
    flex: 1;
    justify-content: flex-start;
    text-align: left;
    font-weight: 400;
    white-space: pre-wrap;
    line-height: 1.5;
}

.vault-field-value input[data-role="password-display"]:focus {
    outline: none;
    box-shadow: none;
}

.vault-field-value a {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    color: var(--color-accent);
    text-decoration: none;
}

.vault-field-value a span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.vault-field-value a svg {
    flex-shrink: 0;
}

.vault-field-value a:hover {
    text-decoration: underline;
}

.vault-field-value .tag-badge {
    margin: 2px 4px 2px 0;
}

/* Gleiche Breite/Ausrichtung wie .vault-field-card (Nutzer-Feedback 2026-08-02). */
.vault-reuse-warning {
    display: flex;
    gap: 14px;
    max-width: 640px;
    margin: 0 0 20px;
    background: var(--status-warning-soft);
    border: 1px solid var(--status-warning);
    border-radius: var(--radius-lg);
    padding: 16px 18px;
}

.vault-reuse-warning-icon {
    flex-shrink: 0;
    width: 22px;
    height: 22px;
    margin-top: 1px;
    color: var(--status-warning);
}

.vault-reuse-warning strong {
    display: block;
    font-size: 13.5px;
    margin-bottom: 4px;
}

.vault-reuse-warning p {
    font-size: 12.5px;
    color: var(--color-text-muted);
    line-height: 1.5;
    margin: 0 0 10px;
}

.vault-reuse-warning .btn {
    font-size: 12.5px;
    padding: 6px 14px;
}

@media (max-width: 860px) {
    .vault-mdview {
        flex-direction: column;
    }

    .vault-mdlist-pane {
        width: 100%;
        border-right: none;
    }

    .vault-mddetail-pane {
        position: fixed;
        inset: 0;
        z-index: 150;
        transform: translateX(100%);
        transition: transform 0.25s ease;
    }

    .vault-mddetail-pane.is-open {
        transform: translateX(0);
    }

    .vault-mddetail-mobile-back {
        display: flex;
    }
}

/* Scrollbalken der beiden intern scrollenden Spalten bleiben unsichtbar, bis die Maus im
   jeweiligen Bereich ruht (Hover) - vermeidet die permanent sichtbaren, breiten Standard-
   Scrollbalken auf einer sonst nahtlosen Flaeche. scrollbar-width:none blendet sie in Firefox
   komplett aus, bis :hover sie auf "thin" umschaltet; bei WebKit uebernimmt der transparente
   vs. eingefaerbte Thumb dieselbe Aufgabe. */
.vault-mdlist,
.vault-mddetail-body {
    scrollbar-width: none;
}

.vault-mdlist:hover,
.vault-mddetail-body:hover {
    scrollbar-width: thin;
}

.vault-mdlist::-webkit-scrollbar,
.vault-mddetail-body::-webkit-scrollbar {
    width: 6px;
}

.vault-mdlist::-webkit-scrollbar-thumb,
.vault-mddetail-body::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 3px;
}

.vault-mdlist:hover::-webkit-scrollbar-thumb,
.vault-mddetail-body:hover::-webkit-scrollbar-thumb {
    background: var(--color-border);
}

/* WLAN-Spezialkarte in der Detailansicht + QR-Modal (WIFI:S:...;T:WPA;P:...;;-Format) -
   QR-Rendering per lokal vendorisierter qrcode-generator-Lib (public/vendor/qrcode/), das
   Passwort verlaesst den Server nur fuer den Moment ueber den bestehenden /reveal-Endpunkt
   (identischer Audit-Log-Pfad wie Copy/Reveal-Buttons), nie als Klartext im initialen HTML. */
/* Gleiche Breite/Ausrichtung wie .vault-field-card (Nutzer-Feedback 2026-08-02) - beide Cards
   sollen buendig untereinander stehen statt unterschiedlich breit/zentriert zu wirken. */
.vault-wifi-card {
    max-width: 640px;
    margin: 0 0 20px;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 16px 18px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-accent-soft);
    box-shadow: var(--shadow-card);
}

.vault-wifi-card-icon {
    flex-shrink: 0;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-accent);
    display: flex;
    align-items: center;
    justify-content: center;
}

.vault-wifi-card-icon .icon {
    width: 22px;
    height: 22px;
}

.vault-wifi-card-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.vault-wifi-card-text strong {
    font-size: 13.5px;
}

.vault-wifi-card-text span {
    font-size: 12px;
    color: var(--color-text-muted);
}

.vault-wifi-qr-box {
    max-width: 360px;
    text-align: center;
}

.vault-wifi-qr-hint {
    font-size: 12.5px;
    color: var(--color-text-muted);
    margin: 0 0 16px;
    line-height: 1.5;
}

.vault-wifi-frame-row {
    margin: 0 auto 16px;
    display: flex;
    justify-content: center;
}

.vault-wifi-qr-canvas {
    display: flex;
    justify-content: center;
    padding: 16px;
    background: #fff;
    border-radius: var(--radius-md);
    border: 1px solid var(--color-border);
    margin-bottom: 16px;
}

.vault-wifi-qr-canvas svg {
    width: 100%;
    max-width: 260px;
    height: auto;
    display: block;
}

.vault-wifi-qr-actions {
    justify-content: center;
    flex-wrap: wrap;
}

/* ============ CoreDesk Archiv: 3-Spalten Finder-Ansicht (Sidebar/Liste/Vorschau) ============
   Vereinte Lese-Ansicht ueber customer_documents/ticket_photos/contracts (siehe ArchiveRepository).
   Layout-Vorbild ist das bestehende .vault-mdview-Master-Detail, um eine dritte Spalte erweitert -
   Farben/Radien/Schatten kommen bewusst aus denselben Tokens statt neuer Custom Properties. */
.archive-view {
    display: flex;
    min-height: 0;
    height: calc(100vh - 230px);
    min-height: 480px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.archive-sidebar {
    width: 220px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 20px;
    padding: 18px 12px;
    border-right: 1px solid var(--color-border);
    background: var(--color-bg);
    overflow-y: auto;
}

.archive-sidebar-section h4 {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.4px;
    margin: 0 0 8px 8px;
}

.archive-filter-btn,
.archive-tag-filter-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 7px 8px;
    border: none;
    background: transparent;
    border-radius: var(--radius-sm);
    font-size: 13px;
    color: var(--color-text);
    cursor: pointer;
    text-align: left;
}

.archive-filter-btn:hover,
.archive-tag-filter-btn:hover {
    background: rgba(0, 0, 0, 0.04);
}

.archive-filter-btn.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: 600;
}

.archive-tag-filter-btn.is-active {
    background: rgba(0, 0, 0, 0.06);
    font-weight: 600;
}

.archive-tag-filter-btn .tag-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.archive-list-pane {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    border-right: 1px solid var(--color-border);
    min-height: 0;
}

.archive-list-search {
    flex: 0 0 auto;
    margin: 14px 16px 8px;
}

.archive-list-table {
    flex: 1;
    overflow-y: auto;
    display: block;
}

.archive-list-table thead,
.archive-list-table tbody {
    display: block;
}

.archive-list-table tr {
    display: table;
    width: 100%;
    table-layout: fixed;
}

.archive-list-table th[data-sort] {
    cursor: pointer;
    user-select: none;
}

.archive-row {
    cursor: pointer;
}

.archive-row.is-active {
    background: var(--color-accent-soft);
}

.archive-col-name {
    display: flex;
    align-items: center;
    gap: 10px;
}

.archive-col-name .icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    color: var(--color-text-muted);
}

.archive-preview-pane {
    width: 320px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    background: var(--color-bg);
    overflow-y: auto;
}

.archive-preview-mobile-back {
    display: none;
    align-items: center;
    gap: 6px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--color-border);
    font-size: 13px;
    font-weight: 600;
    color: var(--color-accent);
    flex-shrink: 0;
    cursor: pointer;
}

.archive-preview-empty {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 32px;
    color: var(--color-text-muted);
    font-size: 13px;
}

.archive-preview-content {
    flex-direction: column;
    gap: 18px;
    padding: 22px;
}

.archive-preview-media {
    width: 100%;
    height: 160px;
    border-radius: var(--radius-md);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.archive-preview-media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.archive-preview-icon {
    width: 56px;
    height: 56px;
    color: var(--color-text-muted);
}

.archive-preview-icon .icon {
    width: 56px;
    height: 56px;
}

.archive-preview-title h3 {
    font-size: 15px;
    margin: 0 0 2px;
    word-break: break-word;
}

.archive-preview-title span {
    font-size: 12px;
    color: var(--color-text-muted);
}

.archive-preview-tags {
    display: flex;
    flex-wrap: wrap;
}

.archive-meta-grid {
    display: grid;
    grid-template-columns: 90px 1fr;
    row-gap: 10px;
    font-size: 12.5px;
    padding: 14px 0;
    border-top: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
}

.archive-meta-grid .meta-label {
    color: var(--color-text-muted);
}

.archive-preview-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

@media (max-width: 900px) {
    .archive-view {
        flex-direction: column;
        height: auto;
        min-height: calc(100vh - 200px);
    }

    .archive-sidebar {
        width: 100%;
        flex-direction: row;
        flex-wrap: wrap;
        border-right: none;
        border-bottom: 1px solid var(--color-border);
    }

    .archive-list-pane {
        border-right: none;
    }

    .archive-preview-pane {
        position: fixed;
        inset: 0;
        z-index: 150;
        width: 100%;
        transform: translateX(100%);
        transition: transform 0.25s ease;
    }

    .archive-preview-pane.is-open {
        transform: translateX(0);
    }

    .archive-preview-mobile-back {
        display: flex;
    }
}

/* CoreDesk Directory (templates/directory/index.twig) - gleiches Master-Detail-Grundgeruest
   wie .archive-view, aber eigene Sidebar-Klasse (die Directory-Sidebar traegt kein
   .archive-sidebar-Duplikat auf dem <aside> selbst, nur auf den Abschnitten darin). */
.directory-view {
    display: flex;
    min-height: 0;
    height: calc(100vh - 230px);
    min-height: 480px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.directory-sidebar {
    width: 220px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 20px;
    padding: 18px 12px;
    border-right: 1px solid var(--color-border);
    background: var(--color-bg);
    overflow-y: auto;
}

.directory-list-pane .vault-mdlist {
    flex: 1;
    overflow-y: auto;
}

/* Directory-Listenzeile: eigene Sub-Klassen statt der Vault-eigenen .vault-mdlist-item-text
   (Directory braucht 3 Slots - Titel/Untertitel/Tag-Punkte - statt Vaults 2). */
.vault-mdlist-item-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.vault-mdlist-item-title {
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-mdlist-item-sub {
    font-size: 11.5px;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vault-mdlist-item-tags {
    flex-shrink: 0;
    display: flex;
    gap: 3px;
}

/* Favoriten-Stern ("iPhone-Weiche"): outline/muted im Normalzustand, gefuellt/amber sobald
   is_favorite=1 - sowohl in der Listenzeile als auch im Detailpanel-Header (renderList()/
   renderDetail() in directory-view.js). */
.directory-favorite-star {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
    border-radius: var(--radius-sm, 6px);
}

.directory-favorite-star:hover {
    background: var(--color-bg-hover, rgba(0, 0, 0, 0.05));
    color: var(--status-warning, #d97706);
}

.directory-favorite-star.is-active {
    color: var(--status-warning, #d97706);
}

.directory-favorite-star.is-active .icon {
    fill: currentColor;
}

/* Gewerk-Badge (z.B. "Elektriker", "Sanitaer") - neben dem Namen in Listenzeile UND Detailpanel-
   Header, sowie im Ticket-Sheet "Externes Gewerk hinzuziehen" (siehe ticket-new-design.js) und im
   Dispatcher-Widget (dispatch.js) - dieselbe Klasse ueberall, damit das Gewerk visuell konsistent
   als eigenes Merkmal erkennbar ist, unabhaengig von den Farb-Tags. */
.directory-trade-badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--color-accent-soft, rgba(59, 130, 246, 0.12));
    color: var(--color-accent, #2563eb);
    vertical-align: middle;
    margin-left: 6px;
}

/* Directory-Detailpanel (JS-gerendert, siehe directory-view.js::renderDetail()) - .archive-
   preview-content liefert bereits das Grundgeruest, hier nur die 2 fehlenden Bausteine. */
.archive-preview-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.archive-preview-header h3 {
    margin: 0;
    font-size: 16px;
}

.archive-preview-meta-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 0;
    border-bottom: 1px solid var(--color-border);
    font-size: 13px;
}

.archive-preview-meta-row > span:first-child {
    flex: 1;
    min-width: 0;
}

.archive-preview-meta-row form {
    margin-left: auto;
}

.archive-preview-meta-row:last-child {
    border-bottom: none;
}

.archive-preview-meta-row .icon {
    width: 15px;
    height: 15px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

@media (max-width: 900px) {
    .directory-view {
        flex-direction: column;
        height: auto;
        min-height: calc(100vh - 200px);
    }

    .directory-sidebar {
        width: 100%;
        flex-direction: row;
        flex-wrap: wrap;
        border-right: none;
        border-bottom: 1px solid var(--color-border);
    }

    .directory-list-pane {
        border-right: none;
    }
}

.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
}

.icon-btn:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

/* Kurzes Kopier-Feedback direkt am Icon (Nutzer-Wunsch 2026-08-14, Tresor) - ersetzt den lila
   Inline-Text "Kopiert!", der das Layout verschob, durch eine 1s-Farbmarkierung + Toast
   (siehe window.vaultCopyFeedback in vault-widget.js). Gleiches Muster wie
   .cmdbuilder-copy-btn.is-copied andernorts im Projekt. */
.icon-btn.is-copied {
    color: var(--color-success, #16a34a);
    border-color: var(--color-success, #16a34a);
}

.icon-btn .icon {
    width: 15px;
    height: 15px;
}

.icon-btn-row {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* Mail-Icon in "Historie & Berichte" (admin/_billing_tab_runs.twig) - zeigt an, dass dieser
   Leistungsschein bereits mindestens einmal per Mail verschickt wurde (siehe
   BillingRunController::mailModal(), pdf_sent_at). */
.icon-btn.is-sent {
    color: var(--status-active);
    border-color: var(--status-active-soft);
}

.icon-btn.is-sent:hover {
    color: var(--status-active);
    background: var(--status-active-soft);
}

/* Kontext-Karte im "Leistungsnachweis per E-Mail versenden"-Modal (Nutzer-Feedback 2026-08-03:
   Beinahe-Fehlversand, da zwei Laeufe desselben Kunden mit identischem Betrag direkt untereinander
   standen und das falsche Mail-Icon angeklickt wurde) - Kunde/Zeitraum/Rechnungsnr. bewusst als
   auffaellige Karte statt nur klein im Anhang-Dateinamen weiter unten, damit ein Fehlklick auf die
   falsche Tabellenzeile vor dem Versand sofort auffaellt. Zweite Iteration (Nutzer-Feedback: "sieht
   optisch nicht gut aus" - fetter blauer Fliesstext direkt unter dem Titel wirkte wie eine
   Fehlermeldung) - jetzt eine ruhige, getönte Karte mit Icon; der Kundenname steht gross/fett als
   das eigentlich sicherheitsrelevante Detail, Zeitraum/Rechnungsnr. als kleinere Zeile darunter. */
.billing-mail-modal-context {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: -4px 0 18px;
    padding: 12px 14px;
    border-radius: var(--radius-md);
    background: var(--color-accent-soft);
}
.billing-mail-modal-context > .icon-sm {
    color: var(--color-accent);
    flex-shrink: 0;
}
.billing-mail-modal-context-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}
.billing-mail-modal-context-text strong {
    font-size: 14px;
    font-weight: 700;
    color: var(--color-text);
}
.billing-mail-modal-context-text span {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-accent);
}

.billing-mail-attachment-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    font-size: 13px;
    color: var(--color-text-muted);
}

.secret-cell {
    font-family: var(--font-mono);
    font-size: 13px;
    width: 140px;
    max-width: 140px;
    border: none;
    background: transparent;
    color: var(--color-text);
    padding: 0;
}

.secret-cell:focus {
    outline: none;
    background: var(--color-bg);
}

.otp-cell {
    display: flex;
    align-items: center;
    gap: 8px;
}

.otp-code {
    font-family: var(--font-mono);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.05em;
}

.otp-remaining {
    font-size: 11px;
    color: var(--color-text-muted);
    min-width: 22px;
}

.tag-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 9px;
    border-radius: 999px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    font-size: 11px;
    font-weight: 600;
    margin: 2px;
    white-space: nowrap;
}

/* Dezente Farbunterlegung fuer "Speicherplatz frei" in der Asset-Tech-Spec-Karte - kein
   berechneter Prozentwert (Freitextfeld ohne garantiertes Zahlenformat), nur ein sanfter Hinweis. */
.asset-storage-free {
    background: rgba(34, 197, 94, .10);
    color: var(--color-text-muted);
    padding: 1px 6px;
    border-radius: 4px;
}

/* Quick-Glance-Kachelraster fuer OS/CPU/RAM/Speicher in der Asset-Tech-Spec-Karte (siehe
   assets/view.twig) - ersetzt fuer diese vier Kernwerte die reine Label:Wert-Liste durch
   scanbare Kacheln, damit die wichtigsten Eckdaten eines Geraets auf einen Blick erfassbar sind. */
.asset-spec-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 10px;
    margin-bottom: 16px;
}
.asset-spec-tile {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    background: var(--color-surface-muted, #f8fafc);
}
.asset-spec-tile svg {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: var(--color-text-muted);
    margin-top: 1px;
}
.asset-spec-tile-label {
    display: block;
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}
.asset-spec-tile-value {
    display: block;
    font-size: 13px;
    font-weight: 600;
    word-break: break-word;
}

/* Kompaktes Key-Value-Grid fuer die Asset-Uebersicht (siehe UI-Refactoring-Auftrag "Asset Detail
   View") - ersetzt die vorherige endlose vertikale .profile-info-list durch ein 3-spaltiges
   Kachel-Raster (gleiches Label/Wert-Duo wie .tile-info-block, nur im Grid statt gestapelt), damit
   die wenigen Datenpunkte einer Geraeteakte nicht mehr unnoetig viel Hoehe beanspruchen. */
.asset-kv-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 16px 20px;
}

.asset-kv-grid .tile-info-block {
    margin-bottom: 0;
}

.asset-kv-grid .tile-info-block--full {
    grid-column: 1 / -1;
}

/* Duenne Trennlinie zwischen Uebersicht/Tech-Specs/Heartbeat innerhalb EINER gemeinsamen Karte
   statt drei separater Karten mit je eigenem Rand - haelt die Abschnitte klar auseinander, ohne
   den vertikalen Platzverbrauch dreier Kartenraender/-innenabstaende zu wiederholen. */
.asset-detail-section + .asset-detail-section {
    margin-top: 18px;
    padding-top: 18px;
    border-top: 1px solid var(--color-border);
}

/* Farbige Berechtigungs-Badges (Ansprechpartner-CRM: Primaer/Haupt-IT/Freigabe/Ticket-berechtigt) -
   dieselbe Pill-Form wie .tag-badge, nur mit semantischer Akzentfarbe statt neutralem Grau. */
.tag-badge--primary { background: var(--color-accent-soft); color: var(--color-accent); border-color: transparent; }
.tag-badge--it-lead { background: var(--status-active-soft); color: var(--status-active); border-color: transparent; }
.tag-badge--approval { background: var(--status-warning-soft); color: var(--status-warning); border-color: transparent; }
.tag-badge--ticket-permission { background: var(--status-callback-soft); color: var(--status-callback); border-color: transparent; }

/* Smart Inbox (templates/inbox/index.twig): dezenter KI-Vorschlags-Hinweis ueber dem
   Kunde-Dropdown + Reihe der drei Aktions-Buttons (Ticket/Kontakt, Beleg ablegen, Sperren). */
.inbox-suggestion {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: 10px;
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-size: 12px;
    font-weight: 600;
}

.inbox-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 10px;
}

.inbox-block-form {
    margin-left: auto;
}

.inbox-block-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--color-danger-text);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
    text-decoration: underline;
}

.inbox-block-link:hover {
    opacity: 0.8;
}

/* "Löschen" (siehe EmailInboundQueueController::delete()) - bewusst gedeckter als
   .inbox-block-link (kein Danger-Rot), da hier weder eine dauerhafte Absender-Sperre noch ein
   Spam-Verdacht ausgesprochen wird, nur eine einzelne irrelevante Mail verschwindet. */
.inbox-delete-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--color-text-muted);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
    text-decoration: underline;
}

.inbox-delete-link:hover {
    color: var(--color-text);
}

.customer-type-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid;
    font-size: 11px;
    font-weight: 600;
    margin-left: 8px;
    white-space: nowrap;
}

.customer-type-chip .icon-sm {
    width: 12px;
    height: 12px;
}

/* ============================================================================
   CoreDesk Inside (templates/customers/dashboard.twig) - zweispaltiges Modul-Layout: links die
   Tab-Navigation ueber die einzelnen Kunden-Module (CRM/Tickets/Assets/Wiki/Dokumente), rechts
   die konstanten Kunden-Stammdaten-Karten (Firmenprofil/IT-Infrastruktur). */
.inside-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 340px;
    gap: 20px;
    align-items: start;
}

.inside-widget-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 16px;
}

.inside-widget-card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 8px;
    margin-bottom: 4px;
}

.inside-widget-card-title {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

.inside-widget-card-count {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
    background: var(--color-bg);
    padding: 2px 8px;
    border-radius: 999px;
    flex-shrink: 0;
}

.inside-widget-list {
    display: flex;
    flex-direction: column;
}

.inside-widget-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 0;
    border-bottom: 1px solid var(--color-border);
    text-decoration: none;
    color: inherit;
}

.inside-widget-row:last-child {
    border-bottom: none;
}

.inside-widget-row:hover .inside-widget-row-title {
    color: var(--color-accent);
}

.inside-widget-row-icon {
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.inside-widget-row .status-dot {
    flex-shrink: 0;
}

.inside-widget-row-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.inside-widget-row-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.inside-widget-row-sub {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-top: 1px;
}

.inside-widget-footer {
    display: block;
    text-align: center;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-accent);
    padding-top: 10px;
    margin-top: 4px;
    border-top: 1px solid var(--color-border);
    text-decoration: none;
}

.inside-widget-footer:hover {
    text-decoration: underline;
}

.inside-side-panel {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.inside-header-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 18px;
    margin-top: 8px;
    width: 100%;
}

.inside-header-badge-group {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
}

.inside-header-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 4px 10px;
    text-decoration: none;
    white-space: nowrap;
}

a.inside-header-pill:hover {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.social-badge--sm {
    width: 24px;
    height: 24px;
}

.inside-header-badge-edit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 999px;
    background: transparent;
    border: none;
    color: var(--color-text-muted);
    cursor: pointer;
    flex-shrink: 0;
}

.inside-header-badge-edit:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

.inside-mini-widget-header {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 8px;
    margin-bottom: 10px;
}

.inside-mini-widget-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.inside-mini-widget-body strong {
    font-size: 14px;
    color: var(--color-text);
}

.inside-mini-widget-body span {
    font-size: 12px;
    color: var(--color-text-muted);
}

.inside-mini-widget-body.is-highlight strong,
.inside-mini-widget-body.is-highlight span {
    color: var(--color-accent);
}

.inside-heartbeat-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.inside-heartbeat-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.inside-heartbeat-dot {
    width: 7px;
    height: 7px;
    border-radius: 999px;
    background: var(--color-success, #16a34a);
    flex-shrink: 0;
}

.inside-heartbeat-name {
    flex: 1;
    min-width: 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.inside-heartbeat-time {
    font-size: 11px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.inside-sla-widget-body {
    display: flex;
    align-items: center;
    gap: 14px;
}

.inside-sla-ring {
    position: relative;
    width: 64px;
    height: 64px;
    flex-shrink: 0;
}

.inside-sla-ring svg {
    transform: rotate(-90deg);
}

.inside-sla-ring-bg {
    fill: none;
    stroke: var(--color-border);
    stroke-width: 10;
}

.inside-sla-ring-value {
    fill: none;
    stroke: var(--color-accent);
    stroke-width: 10;
    stroke-linecap: round;
    transition: stroke-dasharray 300ms ease;
}

.inside-sla-ring-text {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
}

.inside-sla-ring-percent {
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text);
}

.inside-sla-widget-caption {
    font-size: 12px;
    color: var(--color-text-muted);
    flex: 1;
}

/* Grid-Items haben per Spec ein implizites min-width:auto (= Max-Content-Breite ihres Inhalts),
   nicht 0 - ohne diese Regel weigert sich die Spalte, unter die Breite der nicht umbrechenden
   Tab-Leiste (.inside-tabs, flex-wrap:nowrap im Mobil-Breakpoint) zu schrumpfen. Das ist der
   eigentliche Grund, warum die Tab-Leiste bisher weder umbrach noch intern scrollte, sondern die
   gesamte Seite horizontal aufriss (der Bugreport "kein mobiler Umbruch"). */
.inside-layout > div {
    min-width: 0;
}

@media (max-width: 900px) {
    .inside-layout {
        grid-template-columns: 1fr;
    }
}

.inside-header {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    /* Ueberschreibt .card's overflow-x:auto - dessen overflow-y:visible wird sonst vom Browser
       stillschweigend zu "auto" berechnet (CSS-Spec-Regel: sobald eine Achse nicht "visible" ist,
       wird die andere ebenfalls geclippt), was das absolut positionierte Kebab-Dropdown-Menu
       (.inside-header-kebab-content) am Kartenrand abschneidet statt frei darueber schweben zu
       lassen - der gemeldete "oeffnet sich nur halb"-Bug. Diese Karte enthaelt keine breiten
       Tabellen, braucht also nie das x-Scrollen von .card. */
    overflow: visible;
}

/* Gleiche Kennzeichnung wie .customer-card--internal, hier fuer den Kopfbereich der CoreDesk-
   Inside-Detailseite - identischer Verlauf, damit Karte und Detailseite optisch zusammengehoeren. */
.inside-header--internal {
    background: linear-gradient(135deg, var(--color-accent-soft), transparent 140%);
    border-color: var(--color-accent);
}

.inside-header-title {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
}

.inside-header-title h1 {
    margin: 0;
    font-size: 20px;
    font-weight: 700;
}

.inside-header-meta {
    color: var(--color-text-muted);
    font-size: 12px;
    margin-top: 4px;
}

.inside-header-birthday {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--color-text-muted);
}

.inside-header-birthday svg {
    width: 12px;
    height: 12px;
}

.inside-header-birthday.is-today {
    color: var(--color-accent);
    font-weight: 600;
}

.inside-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    /* .inside-header selbst ist flex-wrap:wrap - bei einem langen Firmennamen/vielen Badges in
       .inside-header-title wraeppt dieser Block auf eine eigene Zeile. Allein auf dieser Zeile
       zieht justify-content:space-between (auf .inside-header) NICHT nach rechts, da es bei nur
       einem Element pro Zeile nichts zu verteilen gibt - der Block klebt sonst faelschlich links.
       margin-left:auto zwingt ihn unabhaengig vom Umbruch an den rechten Rand, was auf breiten
       Bildschirmen (wo er ohnehin schon rechts sitzt) keinen Unterschied macht. Genau das war die
       Ursache des gemeldeten "Kebab-Menu oeffnet sich abgeschnitten"-Bugs auf Mobilgeraeten: das
       Dropdown (position:absolute; right:0) haengt am faelschlich linksbuendigen Kebab-Button und
       ragt dadurch ueber den linken Bildschirmrand hinaus. */
    margin-left: auto;
}

.inside-tabs {
    display: inline-flex;
    flex-wrap: wrap;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    padding: 4px;
    border-radius: var(--radius-md);
    gap: 2px;
    margin-bottom: 16px;
}

.inside-tabs button,
.inside-tabs a {
    padding: 7px 14px;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    background: transparent;
    border: none;
    border-radius: var(--radius-sm);
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
}

.inside-tabs button:hover,
.inside-tabs a:hover {
    color: var(--color-text);
}

.inside-tabs button.is-active,
.inside-tabs a.is-active {
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--shadow-card);
}

/* Kebab-Overflow fuer die sekundaeren Kopfzeilen-Aktionen (Anrufen/E-Mail/Portal-Einladung/
   Bearbeiten/Sperren) - auf Desktop komplett transparent (display:contents laesst die Kinder
   wie unverpackt im Flex-Layout erscheinen), erst unterhalb 767px wird daraus ein echtes
   :focus-within-Dropdown (identisches JS-freies Muster wie .btn-group-dropdown). */
.inside-header-kebab,
.inside-header-kebab-content {
    display: contents;
}

.inside-header-kebab-toggle {
    display: none;
}

/* Mobil (<768px, siehe CoreDesk-Inside-Redesign): Tab-Leiste wird zum horizontal scrollbaren
   Band mit versteckter Scrollbar (Apple-App-Store-Stil) statt unsauber umzubrechen; die durch
   den neuen Quick-Add-FAB redundant gewordenen Kopfzeilen-Buttons (Neues Ticket/Neuer Kontakt)
   verschwinden; die restlichen sekundaeren Aktionen wandern ins Kebab-Menue. */
@media (max-width: 767px) {
    .inside-tabs {
        /* display muss von inline-flex (Basisregel) auf flex + volle Breite umgestellt werden -
           ein inline-flex-Element bemisst seine eigene Breite sonst am Inhalt (max-content), d.h.
           bei flex-wrap:nowrap waechst die Box selbst ueber den Viewport hinaus statt intern zu
           scrollen (overflow-x:auto greift nur, wenn die Box eine begrenzte Breite hat) - das war
           der eigentliche Grund, warum die Leiste bisher weder umbrach noch scrollte, sondern die
           ganze Seite horizontal aufriss. */
        display: flex;
        width: 100%;
        max-width: 100%;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }

    .inside-tabs::-webkit-scrollbar {
        display: none;
    }

    /* Push-Sync-Tab ist eine Desktop-Aktion (laedt eine .sh/.ps1-Datei zum Ausfuehren im
       Terminal) - auf Mobilgeraeten ohne praktischen Nutzen, daher hier ausgeblendet statt in
       der ohnehin schon engen Tab-Leiste Platz zu beanspruchen. */
    .inside-tabs [data-tab="asset-sync"] {
        display: none;
    }

    .inside-header-new-ticket-group,
    .inside-header-new-contact-btn {
        display: none;
    }

    .inside-header-kebab {
        display: inline-flex;
        position: relative;
    }

    .inside-header-kebab-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 36px;
        height: 36px;
        min-width: 44px;
        min-height: 44px;
    }

    .inside-header-kebab-content {
        display: none;
        position: absolute;
        top: calc(100% + 4px);
        right: 0;
        flex-direction: column;
        gap: 4px;
        min-width: 210px;
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: 8px;
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
        padding: 6px;
        z-index: 50;
    }

    /* :focus-within bleibt als Tastatur-Fallback bestehen, ist aber auf Touch-Geraeten
       (v.a. iOS Safari) nicht zuverlaessig, da ein <button> dort per Tap nicht in jedem Fall
       fokussiert wird - .is-open wird stattdessen per JS (inside-quickadd.js) explizit
       umgeschaltet und ist der eigentlich tragende Mechanismus auf Mobilgeraeten. */
    .inside-header-kebab:focus-within .inside-header-kebab-content,
    .inside-header-kebab.is-open .inside-header-kebab-content {
        display: flex;
    }

    .inside-header-kebab-content .btn,
    .inside-header-kebab-content form {
        width: 100%;
    }

    .inside-header-kebab-content .btn {
        justify-content: flex-start;
        min-height: 44px;
    }

    /* CRM-Cards: erzwingt eine Spalte statt des Desktop-auto-fill-Grids (minmax(300px,1fr)
       liess auf mittleren Breiten wie 600-767px weiterhin 2 Spalten zu, was die ganze Seite
       horizontal zum Scrollen brachte - gemeldeter Bug). Ueber die ID statt nur die Klasse
       adressiert, da die Basisregel .inside-crm-cards (gleiche Spezifitaet 0-1-0) im Stylesheet
       WEITER UNTEN steht und bei gleicher Spezifitaet sonst per Quellreihenfolge gewinnen wuerde,
       unabhaengig davon, dass diese Regel hier in der @media-Bedingung steht. */
    #inside-crm-cards-panel.inside-crm-cards {
        grid-template-columns: 1fr;
    }

    /* Auf Mobilgeraeten gibt es nur noch die Cards-Ansicht (Nutzer-Feedback) - die
       Tabellenansicht mit 4 Spalten war die eigentliche Ursache des horizontalen Scrollens.
       InsideApp waehlt currentCrmView beim Laden ohnehin bereits per matchMedia auf 'cards',
       hier wird zusaetzlich der Umschalter selbst entfernt, damit gar nicht erst zurueck auf
       'Tabelle' gewechselt werden kann. Ueber zwei Klassen (0-2-0) statt nur .inside-view-toggle
       (0-1-0) adressiert, da die gleich spezifische Basisregel WEITER UNTEN im Stylesheet steht
       und bei Spezifitaets-Gleichstand sonst per Quellreihenfolge gewinnen wuerde (exakt das
       bereits bekannte Muster von #inside-crm-cards-panel.inside-crm-cards weiter oben). */
    .inside-panel-subheader-left .inside-view-toggle {
        display: none;
    }

    /* Kompakte Kontaktzeile im "Contacts-App"-Stil: Avatar + Name/Rolle bleiben immer sichtbar,
       Anrufen/Mailen sind sofort per Daumen erreichbar (kein Aufklappen noetig), der Chevron
       oeffnet bei Bedarf den Rest (Badges/Details/Notizen/Aktionen). Beide ueber
       .inside-crm-card-header .xyz (0-2-0) statt nur die Klasse (0-1-0) adressiert, da die
       gleich spezifischen Basisregeln (display:none) WEITER UNTEN im Stylesheet stehen und bei
       Spezifitaets-Gleichstand sonst gewinnen wuerden (selbes Muster wie .inside-view-toggle/
       .inside-crm-card-body-outer weiter oben). */
    .inside-crm-card-header .inside-crm-card-quick-actions {
        display: flex;
    }

    .inside-crm-card-header .inside-crm-card-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
        min-width: 44px;
        flex-shrink: 0;
        background: transparent;
        border: none;
        color: var(--color-text-muted);
        cursor: pointer;
    }

    .inside-crm-card-toggle .icon {
        transition: transform .15s ease;
    }

    .inside-crm-card.is-expanded .inside-crm-card-toggle .icon {
        transform: rotate(180deg);
    }

    /* Anrufen/Mailen decken die Kontaktdaten bereits ab - die textuelle Wiederholung in den
       aufklappbaren Details waere hier reine Redundanz. */
    .inside-crm-card-detail-item {
        display: none;
    }

    /* Animiertes Auf-/Zuklappen ueber den CSS-Grid-Trick (grid-template-rows 0fr->1fr auf einem
       Grid-Container mit genau einem Kind, das overflow:hidden traegt) statt eines blunten
       display:none-Umschaltens - fuehlt sich wie ein natives Bottom-Reveal an statt eines harten
       Sprungs. .inside-crm-card-body-outer traegt auf Desktop bewusst display:contents (siehe
       Basisregel) - hier ueber .inside-crm-card .inside-crm-card-body-outer (0-2-0) statt nur
       die Klasse (0-1-0) adressiert, da die gleich spezifische Basisregel WEITER UNTEN im
       Stylesheet steht und bei Spezifitaets-Gleichstand sonst gewinnen wuerde (siehe
       .inside-view-toggle/#inside-crm-cards-panel weiter oben fuer dasselbe Muster). */
    .inside-crm-card .inside-crm-card-body-outer {
        display: grid;
        grid-template-rows: 0fr;
        transition: grid-template-rows .28s ease;
    }

    .inside-crm-card.is-expanded .inside-crm-card-body-outer {
        grid-template-rows: 1fr;
    }

    .inside-crm-card-body-outer > .inside-crm-card-body {
        overflow: hidden;
        min-height: 0;
    }
}

.inside-panel-subheader {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 10px;
    margin-bottom: 12px;
}

.inside-panel-subheader-left {
    display: flex;
    align-items: center;
    gap: 10px;
}

.inside-panel-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

.inside-view-toggle {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    padding: 2px;
    gap: 2px;
}

.inside-view-toggle button {
    padding: 3px 10px;
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    background: transparent;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

.inside-view-toggle button.is-active {
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--shadow-card);
}

.inside-filter-input {
    font-size: 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 6px 10px;
    width: 200px;
    max-width: 100%;
}

.inside-filter-input:focus {
    outline: none;
    border-color: var(--color-accent);
}

.inside-panel-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    padding-top: 10px;
    margin-top: 4px;
    border-top: 1px solid var(--color-border);
    font-size: 11px;
    color: var(--color-text-muted);
}

.inside-crm-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 12px;
}

.inside-crm-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 14px;
    display: flex;
    flex-direction: column;
    background: var(--color-surface);
    transition: box-shadow .15s ease, border-color .15s ease;
}

.inside-crm-card:hover {
    box-shadow: var(--shadow-card);
    border-color: var(--color-accent);
}

/* Kontaktzeile: Avatar + Name/Rolle + Quick-Actions (Anrufen/Mailen, nur mobil sichtbar) + Chevron
   (nur mobil sichtbar, siehe @media max-width:767px). .inside-crm-card-body-outer traegt auf
   Desktop display:contents - dadurch wird .inside-crm-card-body zum direkten Flex-Kind der Karte
   und uebernimmt per flex:1 das bisherige justify-content:space-between-Verhalten (Aktionen per
   margin-top:auto am unteren Kartenrand, auch wenn Nachbarkarten in derselben Grid-Zeile per
   Stretch hoeher gezogen werden). */
.inside-crm-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
}

.inside-crm-card-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    min-width: 36px;
    border-radius: 50%;
    font-size: 13px;
    font-weight: 700;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    flex-shrink: 0;
}

.inside-crm-card-title-block {
    flex: 1 1 auto;
    min-width: 0;
}

.inside-crm-card-name {
    font-weight: 700;
    font-size: 14px;
}

.inside-crm-card-name a {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
}

.inside-crm-card-department {
    color: var(--color-text-muted);
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.inside-crm-card-quick-actions {
    display: none;
    gap: 6px;
    flex-shrink: 0;
}

.inside-crm-quick-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    min-width: 36px;
    border-radius: 50%;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    flex-shrink: 0;
    transition: background .12s ease, color .12s ease, border-color .12s ease;
}

.inside-crm-quick-btn:active,
.inside-crm-quick-btn:hover {
    background: var(--color-accent-soft);
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.inside-crm-card-body-outer {
    display: contents;
}

.inside-crm-card-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

.inside-crm-card-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 12px;
}

.inside-crm-card-badges .tag-badge {
    margin: 0;
}

.inside-crm-card-details {
    display: flex;
    flex-direction: column;
    gap: 6px;
    font-size: 12.5px;
    color: var(--color-text);
    margin-bottom: 12px;
}

.inside-crm-card-detail-item {
    display: flex;
    align-items: center;
    gap: 8px;
}

.inside-crm-card-detail-item .icon-xs {
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.inside-crm-card-social-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-top: 4px;
    padding-top: 8px;
    border-top: 1px dashed var(--color-border);
}

.inside-crm-card-social-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--color-text-muted);
    font-size: 12px;
    text-decoration: none;
}

.inside-crm-card-social-link:hover {
    color: var(--color-accent);
}

.inside-crm-card-timeline {
    background: var(--color-bg);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    font-size: 12px;
    border: 1px solid var(--color-border);
    margin-bottom: 12px;
}

.inside-crm-card-timeline-title {
    font-weight: 700;
    color: var(--color-text-muted);
    text-transform: uppercase;
    font-size: 10px;
    letter-spacing: 0.03em;
    margin-bottom: 8px;
}

.inside-crm-card-timeline-item {
    margin-bottom: 6px;
    color: var(--color-text);
    line-height: 1.4;
}

.inside-crm-card-timeline-item:last-child {
    margin-bottom: 0;
}

.inside-crm-card-timeline-item.is-empty {
    color: var(--color-text-muted);
    font-style: italic;
}

.inside-crm-card-timeline-date {
    color: var(--color-text-muted);
    font-weight: 500;
}

.inside-crm-card-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: auto;
    padding-top: 10px;
    border-top: 1px solid var(--color-border);
}

/* Nur auf Mobilbreiten ein Akkordeon (siehe @media max-width:767px weiter oben) - auf Desktop
   bleibt die Karte wie bisher immer vollstaendig sichtbar, der Toggle-Button bleibt verborgen. */
.inside-crm-card-toggle {
    display: none;
}

.inside-crm-card-action-btn {
    background: none;
    border: none;
    color: var(--color-text-muted);
    font-size: 12.5px;
    font-weight: 500;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 0;
}

.inside-crm-card-action-btn:hover {
    color: var(--color-accent);
}

/* CoreDesk-Inside-CRM-Tabellenansicht: Kontakt-Spalte mit Telefonlink + E-Mail-Zeile inkl.
   Social-Icons, sowie ein rein CSS-basierter Hover-Tooltip fuer die letzte interne Notiz neben
   dem Namen (kein JS noetig, analog zum einfachen title-Attribut, aber mit mehrzeiliger Vorschau). */
.inside-crm-table-contact-box {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.inside-crm-table-email-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--color-text-muted);
}

.inside-crm-table-social-links {
    display: flex;
    gap: 6px;
    align-items: center;
}

.inside-crm-table-social-links a {
    color: var(--color-text-muted);
    display: inline-flex;
    align-items: center;
}

.inside-crm-table-social-links a:hover {
    color: var(--color-accent);
}

.inside-crm-table-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    justify-content: flex-end;
}

.contact-note-tooltip-container {
    position: relative;
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    color: var(--color-accent);
    margin-left: 6px;
    vertical-align: middle;
}

.contact-note-tooltip {
    visibility: hidden;
    opacity: 0;
    width: 260px;
    background: var(--color-text);
    color: var(--color-bg);
    text-align: left;
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    position: absolute;
    z-index: 20;
    bottom: 125%;
    left: 50%;
    transform: translateX(-50%);
    box-shadow: var(--shadow-card);
    font-size: 11.5px;
    font-weight: 400;
    line-height: 1.4;
    transition: opacity .15s ease;
}

.contact-note-tooltip-container:hover .contact-note-tooltip {
    visibility: visible;
    opacity: 1;
}

.contact-note-tooltip-date {
    color: var(--color-bg);
    opacity: 0.7;
    font-weight: 600;
    margin-bottom: 2px;
}

.profile-info-list {
    display: flex;
    flex-direction: column;
}

.profile-info-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    padding: 7px 0;
    border-bottom: 1px solid var(--color-bg);
    font-size: 12px;
}

.profile-info-row:last-child {
    border-bottom: none;
    padding-bottom: 0;
}

.profile-info-row-label {
    color: var(--color-text-muted);
}

.profile-info-row-value {
    font-weight: 600;
    text-align: right;
}

/* Kunden-Galerie im UniFi-Stil: klare Kopfzeile mit Status-Punkt, Trennlinie,
   gleichmaessig verteilte Stat-Icons unten - statt der generischen Asset-Karte. */
.customer-card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 16px;
}

.customer-card {
    display: block;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    padding: 16px;
    text-align: left;
    color: var(--color-text);
    transition: box-shadow .15s ease, border-color .15s ease;
}

.customer-card:hover {
    border-color: var(--color-accent);
    box-shadow: 0 2px 4px rgba(16, 24, 40, 0.06), 0 4px 12px rgba(16, 24, 40, 0.08);
    text-decoration: none;
}

.customer-card.is-inactive {
    opacity: 0.6;
}

/* Kennzeichnet den einen Kunden-Datensatz, der den Systembetreiber selbst darstellt
   (customers.is_internal = 1, siehe Migration 078) - dezenter blauer Verlauf statt eines Textes,
   damit auf einen Blick klar ist "das ist unsere eigene Firma", ohne die Karte zu ueberladen. */
.customer-card--internal {
    background: linear-gradient(135deg, var(--color-accent-soft), transparent 140%);
    border-color: var(--color-accent);
}

.customer-row--internal {
    background: linear-gradient(90deg, var(--color-accent-soft), transparent 140%);
}

.customer-card-header {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 8px;
}

.customer-card-header .status-dot {
    flex-shrink: 0;
}

.customer-card-name {
    font-weight: 600;
    font-size: 14px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
}

.customer-card-locked {
    margin-bottom: 8px;
}

.customer-card-isp {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-height: 18px;
}

.customer-card-divider {
    border-top: 1px solid var(--color-border);
    margin: 12px 0 10px;
}

.customer-card-stats {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 12px;
    color: var(--color-text-muted);
}

.customer-card-stats > span {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.tag-filter-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 16px;
}

.tag-filter-bar a.tag-badge {
    cursor: pointer;
}

.tag-filter-bar a.tag-badge.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #FFFFFF;
}

.tag-picker {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

.tag-picker .tag-badge {
    cursor: pointer;
    margin: 0;
    user-select: none;
}

.tag-picker .tag-badge.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #FFFFFF;
}

.tag-picker-empty {
    font-size: 11px;
    color: var(--color-text-muted);
}

.copy-feedback {
    font-size: 11px;
    color: var(--color-accent);
    font-weight: 600;
    margin-left: 4px;
}

/* Modal / Flyout-Karte (siehe Referenz-Mockup flyout.html) - schwebende, an allen 4 Ecken
   abgerundete Karte statt eines rechts angedockten volle-Hoehe-Panels: der Overlay-Hintergrund
   richtet die Karte per Flexbox oben rechts aus (exakt wie im Mockup, dessen Body nur
   "justify-content:flex-end" setzt und keine explizite Hoehe hat - die Karte haengt dadurch am
   oberen Rand, nicht vertikal zentriert). Die Karte selbst waechst nur so hoch wie ihr Inhalt
   (bis zur max-height-Grenze, danach scrollt der Koerper intern). Bewusst KEIN display:none mehr
   auf .modal-overlay (Animationen lassen sich aus display:none heraus nicht abspielen) -
   stattdessen opacity/visibility/pointer-events, damit .is-open sauber ein-/ausblendet UND die
   Karte gleichzeitig sanft einskalieren kann. Betrifft alle bestehenden Modal-Instanzen ohne
   Template-Aenderungen (Ausnahme: 2 strukturelle Sonderfaelle, siehe .modal-body weiter unten) -
   CoreDesk Finder (.finder-backdrop/.finder-box) nutzt bewusst eigene Klassen und ist davon
   unberuehrt. */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.2);
    backdrop-filter: blur(2px);
    z-index: 100;
    display: flex;
    align-items: flex-start;
    justify-content: flex-end;
    padding: 24px;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 250ms ease, visibility 0s linear 250ms;
}

.modal-overlay.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 250ms ease;
}

.modal-box {
    background: var(--color-surface);
    box-shadow: 0 10px 25px -5px rgba(15, 23, 42, 0.15), 0 8px 10px -6px rgba(15, 23, 42, 0.08);
    border: 1px solid var(--color-border);
    border-radius: 12px;
    position: relative;
    width: 100%;
    max-width: 650px;
    max-height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transform: scale(0.96);
    opacity: 0;
    transition: transform 200ms ease, opacity 200ms ease;
}

.modal-overlay.is-open .modal-box {
    transform: scale(1);
    opacity: 1;
}

/* Kompakte Variante fuer kleine Bestaetigungs-/Grund-Dialoge (z.B. "Nicht berechnen" bei der
   Leistungsfreigabe) - ersetzt vereinzelte Inline-style="max-width:420px"-Flicken durch eine
   wiederverwendbare Klasse. */
.modal-box--sm { max-width: 420px; }

/* Breiterer Modal-Rahmen fuers Kunden-Servicebericht-Modal (Nutzer-Wunsch 2026-08-09) - braucht
   Platz fuer das Live-Vorschau-iFrame, das sonst auf 650px Breite unbrauchbar schmal waere. */
.modal-box--lg { max-width: 760px; }

/* Ein-Wert-Suchfeld-Picker (window.createSingleSearchPicker, public/js/single-search-picker.js) -
   urspruenglich nur auf der Ticket-Detailseite (.tdv2-*-Tokens in ticket-new-design.css). Nutzer-
   Wunsch 2026-08-12: dieselbe Komponente auch im "Ticket schnell anlegen"-Modal (laedt nur app.css,
   nicht ticket-new-design.css) - hier als --color-*-Aequivalent der tdv2-Variante portiert. Die
   dazugehoerige Kandidatenliste (.collab-candidate-list/-row/-add-btn/-empty) nutzt bereits an
   anderer Stelle (Projekt-Team-Picker) --color-*-Tokens und braucht daher keine Portierung. */
.picker-selected { display: flex; align-items: center; justify-content: space-between; gap: 8px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 8px 10px; background: var(--color-surface-2); }
.picker-selected[hidden] { display: none; }
.picker-selected-name { font-size: 13px; font-weight: 600; color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker-change-btn { flex-shrink: 0; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; padding: 0; border: none; background: transparent; color: var(--color-text-subtle); cursor: pointer; border-radius: 6px; }
.picker-change-btn:hover { background: var(--color-surface); color: var(--color-text); }
.picker-change-btn svg { width: 13px; height: 13px; }
.field-picker .collab-candidate-list { margin-top: 6px; }

/* Autocomplete/Combobox-Suchfeld (window.createFloatingAutocomplete, public/js/floating-
   autocomplete.js) - Dropdown legt sich als schwebendes Overlay UEBER das Formular statt es wie
   .field-picker/.collab-candidate-list nach unten zu verdraengen. Nutzer-Wunsch 2026-08-13 fuers
   Kunden-Suchfeld im "Ticket schnell anlegen"-Modal, angelehnt an die bereits bestehende, dort
   aber zweimal inline duplizierte Vault-Kombobox (.vault-search-dropdown in templates/vault/
   index.twig) - hier als generische, wiederverwendbare Klasse extrahiert. */
.autocomplete-field { position: relative; }
.autocomplete-dropdown {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 20;
    max-height: 260px;
    overflow-y: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md, 0 4px 16px rgba(0,0,0,.15));
    padding: 4px;
}
.autocomplete-dropdown-item { padding: 7px 9px; border-radius: var(--radius-sm); cursor: pointer; font-size: 13px; color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.autocomplete-dropdown-item:hover,
.autocomplete-dropdown-item.is-active { background: var(--color-accent-soft); }
.autocomplete-dropdown-empty { padding: 10px 9px; font-size: 12.5px; color: var(--color-text-muted); }

/* 2-Tab-Umschaltung im Kunden-Servicebericht-Modal (Live-Vorschau/Mail & Optionen) - wiederverwendet
   die Pill-Optik von .billing-module-tabs/.billing-module-tab, hier horizontal statt vertikal und
   auf <button> statt <a> (reines JS-Umschalten, keine Navigation) - deshalb Browser-Button-Chrome
   zurueckgesetzt. */
.modal-tabs {
    display: flex;
    gap: 4px;
    margin: 4px 0 16px;
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 8px;
}

.modal-tab {
    border: none;
    background: none;
    cursor: pointer;
    font: inherit;
}

.customer-report-preview-frame {
    width: 100%;
    height: 420px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: #fff;
}

.customer-report-quick-actions {
    display: flex;
    gap: 8px;
    margin: 4px 0 16px;
}

/* Fester Kopf: Titel (h2, direktes Kind fast aller Modale) bleibt beim Scrollen sichtbar,
   da er als eigenes Flex-Element AUSSERHALB des scrollenden Bereichs (.modal-box form /
   .modal-body) steht statt selbst zu scrollen. */
.modal-box h2 {
    flex-shrink: 0;
    margin: 0;
    padding: 20px 52px 16px 24px;
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
    font-size: 16px;
    font-weight: 600;
}

/* Scrollbare Mitte: bei den meisten Modalen ist das <form> selbst der einzige Container
   fuer Felder + Fuss-Buttons - der wird hier zum alleinigen Scroll-Bereich. Fuer die 2
   strukturellen Ausnahmen (kein <form> bzw. Aktionen ausserhalb des <form>, siehe
   templates/assets/_fields.twig und die Merge-Ansicht in templates/tickets/show.twig)
   uebernimmt die neue .modal-body-Klasse exakt dieselbe Rolle. Selbst eine Flex-Spalte
   (statt nur overflow-y:auto), damit .modal-actions per margin-top:auto unten bleibt, auch
   wenn der Inhalt gar nicht scrollt (kurze Formulare wie "Neue Abteilung") - sonst wuerde
   der Fuss direkt unter dem letzten Feld haengen statt am Panel-Ende. */
.modal-box form,
.modal-box .modal-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 24px;
}

/* Fester Fuss: margin-top:auto schiebt .modal-actions ans Ende der Flex-Spalte, wenn der
   Inhalt kuerzer ist als die verfuegbare Hoehe; position:sticky haelt ihn zusaetzlich am
   unteren Rand sichtbar, sobald der Inhalt tatsaechlich scrollt (lange Formulare). Beide
   Mechaniken zusammen ergeben "immer unten, ob mit oder ohne Scrollen" ohne zusaetzlichen
   Wrapper im Template. */
.modal-actions {
    position: sticky;
    bottom: -24px;
    display: flex;
    gap: 10px;
    justify-content: flex-end;
    margin-top: auto;
    margin-right: -24px;
    margin-bottom: -24px;
    margin-left: -24px;
    padding: 16px 24px;
    background: var(--color-bg);
    border-top: 1px solid var(--color-border);
}

/* Lade-Spinner fuer den Stammdaten-Aktualisierung-Einladungs-Button (Nutzer-Fund 2026-08-12: das
   Formular verschickt eine echte E-Mail, ein Doppelklick darf keine zweite Mail versenden) -
   gleiches Icon-durch-Spinner-Tausch-Prinzip wie .hr-save-bar .btn-primary.loading, hier auf einen
   einzelnen Button statt eine ganze Save-Bar skaliert. Rotationsanimation (@keyframes hr-save-spin)
   bewusst wiederverwendet statt dupliziert - ein Keyframe kann von jedem Selektor genutzt werden. */
.portal-invite-submit-spinner {
    display: none;
    width: 13px;
    height: 13px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: #fff;
    animation: hr-save-spin 700ms linear infinite;
}

#portal-invite-submit-btn.loading .portal-invite-submit-spinner {
    display: block;
}

#portal-invite-submit-btn.loading .portal-invite-submit-label {
    display: none;
}

#portal-invite-submit-btn.loading {
    cursor: not-allowed;
    opacity: .85;
}

/* Schliessen-Button oben rechts im Panel (siehe modal-behavior.js) - kreisrunde Variante
   des .icon-btn-Musters ohne Rahmen/Hintergrund, damit er nicht wie ein normaler
   Toolbar-Button wirkt (Referenz: flyout.html .close-btn). */
.modal-close-btn {
    position: absolute;
    top: 16px;
    right: 16px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: none;
    background: transparent;
    border-radius: 50%;
    font-size: 20px;
    line-height: 1;
    color: var(--color-text-muted);
    cursor: pointer;
    z-index: 1;
}

.modal-close-btn:hover {
    background: var(--color-bg);
    color: var(--color-text);
}

/* Textfelder in Modalen: mehr Platz von Anfang an, nur vertikal vergroesserbar, waechst
   automatisch beim Tippen mit (siehe modal-behavior.js). Bewusst auf ".modal-box textarea"
   beschraenkt statt global - .field textarea wird auch auf normalen Formularseiten
   (Kunden/Projekte/Netzwerke/Assets/Ticket-Vollformular, Zeiterfassungs-Inline-Formulare)
   verwendet und soll dort unveraendert bleiben. */
.modal-box textarea {
    min-height: 160px;
    resize: vertical;
    width: 100%;
}

/* Eingabefelder innerhalb von Modalen: gleiche Basis wie .field input global, aber ohne die
   dortige max-width:420px-Deckelung (auf Vollformular-Seiten gedacht, um sehr breite Cards
   nicht mit unnoetig langen Eingabefeldern zu fuellen) - innerhalb einer ohnehin auf 650px
   begrenzten Modal-Karte soll ein Einzelfeld dagegen die volle Kartenbreite nutzen, exakt wie
   im Referenz-Mockup flyout.html (.form-input/.form-select). */
.modal-box .field input,
.modal-box .field select,
.modal-box .field textarea {
    max-width: none;
    padding: 10px 12px;
    border-radius: 6px;
}

@media (max-width: 640px) {
    .modal-overlay {
        padding: 12px;
    }

    .modal-box {
        max-width: 100%;
    }
}

/* Dashboard: Offene-Tickets-Panel + Termin-Wochenkalender */
.dashboard-columns {
    display: grid;
    grid-template-columns: 1fr 1.6fr;
    gap: 20px;
    align-items: start;
}

@media (max-width: 900px) {
    .dashboard-columns {
        grid-template-columns: 1fr;
    }
}

.dashboard-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.dashboard-panel-header h2 {
    margin: 0;
}

.calendar-legend {
    display: flex;
    gap: 14px;
    font-size: 12px;
    color: var(--color-text-muted);
    flex-wrap: wrap;
}

.calendar-nav {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
}

.calendar-week-label {
    margin-left: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.calendar-view-toggle {
    display: flex;
    gap: 4px;
}

.calendar-day-booked {
    display: block;
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.calendar-day-booked.is-full {
    color: var(--status-active);
}

/* ============================================================================
   Horizontaler Techniker-Kalender: Tage als Zeilen, Stunden als horizontale Achse (statt der
   frueheren vertikalen Tagesspalten). Die Stundenspalte hat eine FESTE Pixelbreite (siehe
   --htimeline-hour-width) statt "1fr" - dadurch ist die Zeile immer breiter als der sichtbare
   Container und laesst sich per .htimeline { overflow-x: auto } stufenlos horizontal scrollen
   (00-24 Uhr technisch immer im DOM, siehe public/js/htimeline.js fuer den Auto-Scroll-Fokus
   auf 07:00 beim Laden). Positionierung der Termin-Balken erfolgt weiterhin per JS (left/width
   %), jetzt aber relativ zu dieser festen Trackbreite statt eines wechselnden Zeitfensters.
   ============================================================================ */
.htimeline {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    overflow-x: auto;
    --htimeline-hour-width: 70px;
}

.htimeline-header-row,
.htimeline-row {
    display: grid;
    grid-template-columns: 130px calc(var(--htimeline-hour-width) * 24);
    align-items: stretch;
}

.htimeline-header-row {
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 4px;
    margin-bottom: 4px;
}

.htimeline-row {
    border-bottom: 1px solid var(--color-border);
}

.htimeline-row:last-child {
    border-bottom: none;
}

.htimeline-row.is-today {
    background: var(--color-accent-soft);
}

.htimeline-row-label {
    position: sticky;
    left: 0;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
    z-index: 3;
    padding: 8px 10px;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

/* Bewusst KEIN echtes rgba() (--color-accent-soft) als Hintergrund - waere hier falsch, da
   .htimeline-row-label sticky positioniert ist und beim horizontalen Scrollen daher wirklich
   deckend sein muss, sonst schimmert der darunter liegende, scrollende Zeileninhalt sichtbar
   durch. color-mix() erzeugt stattdessen den gleichen leicht-durchsichtigen Eindruck als
   *deckende* Farbe (10% Akzent auf Oberflaechenfarbe vorgemischt), ohne echte Transparenz. */
.htimeline-row.is-today .htimeline-row-label {
    color: var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 10%, var(--color-surface));
}

/* Name + Abwesenheits-Status uebereinander gestapelt (siehe Dispatch-Screenshot-Vorgabe) - bewusst
   ein zusaetzlicher innerer Wrapper statt .htimeline-row-label selbst auf flex-direction:column
   umzustellen, da diese Klasse auch anderswo (Dashboard-/Pulse-Zeilenlabels ohne zweite Zeile)
   verwendet wird und dort weiterhin einzeilig vertikal zentriert bleiben soll. */
.htimeline-row-label-stack {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.htimeline-row-status {
    font-size: 11px;
    font-weight: 600;
    color: var(--status-absent);
}

.htimeline-track {
    position: relative;
    z-index: 0;
    min-height: 88px; /* Fallback ohne Termine (1 Lane * LANE_HEIGHT(80) + 8px, siehe htimeline.js)
                          - bei Ueberschneidungen setzt htimeline.js hier eine hoehere, zur
                          Lane-Anzahl passende Hoehe inline (siehe layout()), gedeckelt auf
                          MAX_TRACK_HEIGHT(140px). */
    padding-left: 6px;
}

.htimeline-ticks {
    min-height: 18px;
    overflow: hidden;
}

.htimeline-tick {
    position: absolute;
    top: 0;
    font-size: 11px;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.htimeline-now-line {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--color-danger-text);
    z-index: 2;
}

.htimeline-now-line::before {
    content: '';
    position: absolute;
    top: -3px;
    left: -3px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-danger-text);
}

/* top/height (statt frueher top/bottom) werden fuer echte Termine/Zeiterfassungen individuell
   per JS gesetzt (siehe htimeline.js Lane-Stacking) - top:4px/height:36px hier nur als Fallback
   fuer den (haeufigsten) Fall genau einer Lane, damit ohne JS-Ausfuehrung nichts kollabiert. */
.htimeline-appt {
    position: absolute;
    top: 4px;
    height: 36px;
    border-radius: 4px;
    padding: 2px 6px;
    font-size: 11px;
    line-height: 1.3;
    overflow: hidden;
    color: #fff;
    text-decoration: none;
    z-index: 1;
}

.htimeline-appt-title {
    display: block;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
}

.htimeline-appt--customer_appointment { background: var(--entrytype-appointment); }
.htimeline-appt--tentative { background: var(--status-neutral); }
.htimeline-appt--booked_service { background: var(--entrytype-service); }
.htimeline-appt--callback { background: var(--entrytype-callback); }
/* Automatische E-Mail-Antwort-Pauschale (internal_category='email_reply', siehe
   TicketMessageController::send() und DashboardController/DispatchController) - eigener
   schiefergrauer Balken statt des blauen "booked_service"-Tons, damit sie sich auf allen Kalender-/
   Zeitstrahl-Ansichten (Dashboard-Wochenkalender, Dispatch Tages-/Wochenansicht) sofort von echter
   Kundenarbeit unterscheidet. Naturgemaess ohnehin schon "deutlich kleiner" als eine normale
   Leistung, da die Balkenbreite/-hoehe proportional zur (fixen 5-Minuten-)Dauer ist. */
.htimeline-appt--email_reply { background: var(--entrytype-email); }

/* Nutzer-Feedback 2026-08-07: die 5-Minuten-Pauschale bekam bisher dieselbe Lane-Hoehe wie ein
   echter, oft stundenlanger Termin (siehe htimeline.js layoutTrack(), LANE_HEIGHT=80) - dadurch
   wirkte der Balken unnoetig hoch bei gleichzeitig zu schmal fuer den Text ("08:0..."-Abschnitt).
   height:24px per !important ueberschreibt die von htimeline.js gesetzte Inline-Hoehe (reine
   Darstellungsgroesse, die reservierte Lane-Slot-Hoehe/Ueberlappungslogik bleibt unberuehrt, der
   Balken sitzt nur kleiner oben in seinem Slot) - Uhrzeit+Betreff weichen einem reinen Mail-Icon
   (siehe .htimeline-appt-icon-only unten), da bei 24px kein Text mehr sinnvoll lesbar waere.
   Nutzer-Feedback 2026-08-17: width:24px !important ergaenzt aus demselben Grund - ohne das
   erbt der Balken weiterhin die von htimeline.js gesetzte Inline-Breite (positionByTime(),
   MIN_WIDTH_PERCENT=1.5% = 21,6 Minuten). Ein 5-Minuten-Pauschale-Balken landete damit optisch
   auf derselben Breite wie jeder andere kurze Termin bis 21,6 Minuten (z.B. ein 18-Minuten-
   Termin direkt darueber) - obwohl die Pauschale immer exakt gleich lang ist, sollte sie
   erkennbar als kompaktes Icon-Badge wirken statt als (zufaellig gleich breiter) Dauer-Balken. */
.htimeline-appt--email_reply {
    height: 24px !important;
    width: 24px !important;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}

.htimeline-appt-icon-only {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Erfasste Leistung OHNE zugehoerigen Termin (siehe DispatchController::buildDayRows(),
   'source' => 'time_entry') - optisch als "nachtraeglich erfasst" statt "vorab gebucht"
   erkennbar: gestrichelter Rahmen statt Vollflaeche, kein Drag/Bearbeiten-Modal moeglich. */
.htimeline-appt--logged {
    background: transparent;
    border: 1.5px dashed var(--color-accent);
    color: var(--color-text);
}

/* Ueberschreibt die blaue .htimeline-appt--logged-Randfarbe oben fuer die E-Mail-Antwort-
   Pauschale - beide Klassen werden zusammen gerendert (siehe _day_grid.twig). */
.htimeline-appt--logged.htimeline-appt--email_reply {
    border-color: var(--entrytype-email);
}

/* CoreDesk Quick-Punch (siehe Migration 153): bewusst SOLIDE Indigo statt der gestrichelten
   .htimeline-appt--logged-Optik der anderen nachtraeglich-erfassten Balken (email_reply etc.) -
   der Nutzer wollte interne Routine-Zeit auf den ersten Blick, kraeftig farblich abgesetzt von
   echter Kundenarbeit erkennbar, nicht nur dezent umrandet. Vormals Tailwind purple-600 (#9333ea) -
   auf Indigo umgestellt, da Purple jetzt vom "Fixer Kundentermin" belegt ist (siehe
   --entrytype-appointment) und beide sonst nicht mehr unterscheidbar waeren. */
.htimeline-appt--logged.htimeline-appt--quick_punch {
    background: var(--entrytype-push);
    border: none;
    color: #fff;
}

/* CoreDesk LeadHub > Team Performance Timeline: noch nicht stattgefundene Termine (zukuenftiger
   Tag, siehe LeadHubTimelineService/_team_timeline_day_grid.twig) werden gestrichelt/blasser
   dargestellt statt vollflaechig, um sie optisch von bereits geleisteter Arbeit abzusetzen -
   ueberschreibt die jeweilige Typfarbe oben mit demselben Farbton, aber transparentem Hintergrund. */
.htimeline-appt--planned {
    background: transparent !important;
    border: 1.5px dashed currentColor;
    opacity: 0.85;
}
.htimeline-appt--planned.htimeline-appt--customer_appointment { color: var(--entrytype-appointment); }
.htimeline-appt--planned.htimeline-appt--tentative { color: var(--status-neutral); }
.htimeline-appt--planned.htimeline-appt--booked_service { color: var(--entrytype-service); }
.htimeline-appt--planned.htimeline-appt--callback { color: var(--entrytype-callback); }

/* Quick-Punch-Balken/Chips der eigenen Zeile sind klickbar zum Editieren/Loeschen (siehe
   quick-punch.js::openEditModal()) - auf Pulse und CoreDesk Hub immer die eigenen Buchungen, im
   Dispatch-Grid nur die eigene Zeile (Kolleg:innen bleiben reine Anzeige-Balken/Chips). Auch auf
   .dispatch-chip anwendbar (nur der cursor-Stil, kein weiteres Markup noetig). */
.htimeline-appt--editable {
    cursor: pointer;
}

/* Pausen-Management (siehe DispatchController::resolveBreakWindow()): bewusst dezent statt
   ausgemalt wie .htimeline-appt - der Block soll signalisieren "hier ist Zeit reserviert", aber
   sich klar von einem echten Termin abheben. Wird VOR den Terminen im DOM gerendert (siehe
   _day_grid.twig), sodass ein ueberlappend eingeplanter Termin (Soft-Block, kein Hard-Block)
   optisch darueber liegt. Icon-only (kein Text-Label mehr) statt Emoji+Text, da der Block bei
   30-45 Minuten Breite im gezoomten Zeitstrahl (70px/Stunde) oft nur 20-35px breit ist - voller
   Inhalt (Uhrzeit, Quelle) steht stattdessen im Hover-Tooltip (siehe _break_tooltip.twig, gleicher
   Mechanismus wie bei Terminen). */
.htimeline-break {
    position: absolute;
    top: 4px;
    bottom: 4px;
    min-width: 18px;
    border-radius: 4px;
    background: var(--color-bg);
    border: 1px dashed var(--color-border);
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1;
}

/* Verschieben/Entfernen-Buttons blenden sich erst beim Hover ueber den Block ein (statt dauerhaft
   Platz zu beanspruchen) - existieren ohnehin nur fuer die eigene Pause (source == 'default'/
   'moved', siehe Template), eine aus einem Ticket dokumentierte Pause laesst sich nicht per
   Kalender bearbeiten. Zwei Buttons (Verschieben-Stift + Entfernen/Wiederherstellen-X) teilen
   sich den Block nebeneinander statt je einzeln inset:0 zu beanspruchen (siehe Migration 171). */
.htimeline-break-actions {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
    background: var(--color-bg);
    border-radius: 4px;
    opacity: 0;
    transition: opacity .15s;
}

.htimeline-break:hover .htimeline-break-actions {
    opacity: 1;
}

/* Touch-Geraete kennen keinen echten Hover-Zustand - ohne diese Regel blieben die Verschieben-/
   Entfernen-Buttons auf Handy/Tablet dauerhaft unsichtbar und damit unerreichbar (Bug-Report:
   "Pause laesst sich im Livesystem nicht antippen", reproduzierbar nur auf Touch-Geraeten). */
@media (hover: none) {
    .htimeline-break-actions {
        opacity: 1;
    }
}

.htimeline-break-action-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1;
    height: 100%;
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    color: var(--color-text-muted);
}

.htimeline-break-action-btn:hover {
    color: var(--color-accent);
}

/* Ersetzt den Pausen-Block in der Zeilenbeschriftung, solange der Techniker die Pause fuer diesen
   Tag aktiv entfernt hat (siehe TechnicianBreakSkipRepository) - ohne diesen Hinweis gaebe es
   keinen Weg mehr, eine versehentlich entfernte Pause wieder sichtbar zu machen. */
.htimeline-break-restore {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    background: none;
    border: 1px dashed var(--color-border);
    border-radius: 4px;
    padding: 1px 6px;
    cursor: pointer;
    width: fit-content;
}

.htimeline-break-restore:hover {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

/* UniFi-OS-Style Toast-Benachrichtigung - 1:1 nach Vorlage (toast.html) uebernommen, inkl.
   Position unten links (left:96px raeumt bewusst die 64px breite Sidebar plus Abstand frei).
   Ersetzt das vorherige, einfache .dispatch-toast-Textbanner - siehe public/js/toast.js fuer die
   gemeinsame spawnToast()-Funktion, die von dispatch.js und signal-bell.js genutzt wird. */
.unifi-toast-container {
    position: fixed;
    bottom: 24px;
    left: 96px;
    z-index: 9999;
    display: flex;
    flex-direction: column-reverse;
    gap: 12px;
    width: 360px;
    pointer-events: none;
}

/* Unterhalb von 1024px gibt es keine linke Sidebar mehr, dafuer aber die mobile
   Bottom-Navigation (0-64px vom unteren Rand) und den darueberliegenden FAB (76-132px vom
   unteren Rand, siehe .mobile-bottom-nav/.mobile-fab) - der Toast muss ueber beiden schweben
   statt sie zu verdecken. */
@media (max-width: 1023px) {
    .unifi-toast-container {
        bottom: 148px;
        left: 16px;
        right: 16px;
        width: auto;
    }
}

.unifi-toast {
    pointer-events: auto;
    background: rgba(15, 23, 42, 0.95);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 10px;
    padding: 14px 16px;
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.25), 0 10px 10px -5px rgba(0, 0, 0, 0.15);
    display: flex;
    gap: 14px;
    align-items: flex-start;
    position: relative;
    transform: translateX(-420px);
    opacity: 0;
    transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
}

.unifi-toast.show {
    transform: translateX(0);
    opacity: 1;
}

.toast-status-indicator {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: 6px;
    flex-shrink: 0;
}

.unifi-toast.info .toast-status-indicator { background-color: #005FFF; }
.unifi-toast.success .toast-status-indicator { background-color: #00B159; }
.unifi-toast.warning .toast-status-indicator { background-color: #FF9F00; }
.unifi-toast.error .toast-status-indicator { background-color: #E02424; }

/* Optionales Icon statt des schlichten Farbpunkts (opts.icon, siehe toast.js) - z.B. das
   Kopier-Haekchen im Tresor (Nutzer-Wunsch 2026-08-14). */
.toast-status-indicator--icon {
    width: 18px;
    height: 18px;
    border-radius: 0;
    background: none !important;
    margin-top: 1px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.unifi-toast.success .toast-status-indicator--icon { color: #00B159; }
.unifi-toast.info .toast-status-indicator--icon { color: #005FFF; }
.toast-status-indicator--icon svg { width: 18px; height: 18px; }

.toast-content {
    flex-grow: 1;
    padding-right: 20px;
}

.toast-title {
    font-size: 14px;
    font-weight: 600;
    color: #FFFFFF;
    margin-bottom: 3px;
}

.toast-message {
    font-size: 13px;
    color: #94A3B8;
    line-height: 1.45;
}

.toast-close-btn {
    background: none;
    border: none;
    color: #64748B;
    font-size: 18px;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    position: absolute;
    top: 14px;
    right: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 4px;
    transition: all 0.15s;
}

.toast-close-btn:hover {
    color: #FFFFFF;
    background-color: rgba(255, 255, 255, 0.08);
}

/* Fahrzeit-Abschnitt direkt vor dem eigentlichen Termin-/Leistungsbalken - gleiche Farbe, aber
   die bereits vorhandenen "-soft"-Theme-Variablen (auch im Dark Mode abgestimmt) statt eines
   neuen Tons, damit sie sich klar als "derselbe Termin, andere Phase" statt als eigenstaendiges
   Element lesen. z-index:0 haelt sie hinter dem eigentlichen Balken, falls sich beide durch
   Rundung minimal ueberlappen. */
.htimeline-appt-travel {
    z-index: 0;
    pointer-events: none;
}

.htimeline-appt-travel--customer_appointment { background: var(--entrytype-appointment-tint); }
.htimeline-appt-travel--tentative { background: var(--status-neutral-soft); }
.htimeline-appt-travel--booked_service { background: var(--color-accent-soft); }

/* Pulse-Modul (Ueberblicksseite, siehe templates/pulse/index.twig): Doppelspur-Timeline
   (Spur A "Planung" blass, Spur B "Erfassung" kraeftig, nach Abrechnungskategorie eingefaerbt)
   sowie der 8h-Fortschrittsbalken. Nutzt bewusst dieselben .htimeline-*-Klassen wie der
   Dashboard-Wochenkalender statt eigener Layout-Regeln. */
.htimeline-appt--planning { opacity: 0.55; }
.htimeline-appt--billable { background: var(--color-accent); }
.htimeline-appt--included { background: var(--status-active); }
.htimeline-appt--internal { background: var(--status-neutral); }
.htimeline-appt-travel--pulse-travel { background: var(--status-warning); z-index: 0; pointer-events: none; }

.htimeline-appt-icon {
    display: inline-flex;
    vertical-align: -2px;
    margin-right: 3px;
}

/* Reichhaltiger Hover-Tooltip fuer Termin-/Zeiterfassungsbalken (siehe htimeline.js
   wireRichTooltip() + templates/_appt_tooltip.twig) - bewusst ein eigenstaendiges JS-Tooltip
   statt des einfachen CSS-attr()-Tooltips ([data-tooltip], siehe weiter oben), da hier
   mehrzeiliger, strukturierter Inhalt (Ticket/Kunde/Zeit/Notiz-Vorschau/Icon) noetig ist, den
   CSS content:attr() nicht abbilden kann. Das bestehende [data-tooltip]-Verhalten bleibt fuer
   alle anderen Elemente (Sidebar, Legende, Feiertags-/Wochenend-Baender) unveraendert. */
.htimeline-appt-tooltip {
    display: none;
}

.htimeline-rich-tooltip,
.avatar-rich-tooltip {
    position: fixed;
    z-index: 300;
    display: none;
    max-width: 280px;
    background: var(--color-text);
    color: var(--color-bg);
    padding: 10px 12px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    line-height: 1.5;
    box-shadow: var(--shadow-card);
    pointer-events: none;
}

.htimeline-rich-tooltip.is-visible,
.avatar-rich-tooltip.is-visible {
    display: block;
}

/* WAN/ISP-Node-Tooltip-Inhalt (siehe topology-canvas.js buildIspTooltipHtml()) - dt/dd statt
   Fliesstext, da mehrere Vertragsfelder untereinander stehen. Helle statt gedaempfte dt-Farbe (im
   Gegensatz zu .th-drawer-fields dt), da der Tooltip-Hintergrund dunkel ist. */
.th-node-isp-tooltip-fields {
    margin: 0;
}

.th-node-isp-tooltip-fields dt {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: rgba(255, 255, 255, 0.55);
    margin: 6px 0 1px;
}

.th-node-isp-tooltip-fields dt:first-child {
    margin-top: 0;
}

.th-node-isp-tooltip-fields dd {
    font-size: 12.5px;
    font-weight: 600;
    margin: 0;
    /* Lange Werte ohne Leerzeichen (v.a. IPv6-Adressen) brechen sonst nicht um und sprengen die
       max-width der Tooltip-Box (Nutzer-Feedback: IPv6 zu nah am rechten Rand/ueberlaufend). */
    overflow-wrap: anywhere;
}

.tech-avatar-rich-trigger {
    cursor: default;
}

/* JS-Tooltip fuer Icons innerhalb der scrollbaren .sidebar-nav (siehe public/js/sidebar-tooltip.js) -
   gleiche einzeilige Optik wie das generische [data-tooltip]::after, aber via position:fixed
   statt position:absolute, da es sonst vom neuen overflow-y:auto der Liste abgeschnitten wuerde. */
.sidebar-nav-tooltip {
    position: fixed;
    z-index: 300;
    display: none;
    background: var(--color-text);
    color: var(--color-bg);
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 500;
    white-space: nowrap;
    box-shadow: var(--shadow-card);
    pointer-events: none;
}

.sidebar-nav-tooltip.is-visible {
    display: block;
}

.team-lead-badge {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-left: 4px;
    font-size: 10px;
    font-weight: 600;
    color: var(--status-warning);
    vertical-align: middle;
}

/* Live-Uhrzeit-Vorschau waehrend Drag&Drop im Dispatch (siehe dispatch.js:
   showDragTimePreview()) - folgt dem Cursor, damit der Disponent exakt sieht, auf welche
   15-Minuten-Position der Termin/das Ticket faellt. Bewusst in Akzentfarbe statt der neutralen
   Tooltip-Farbe, um sich klar vom passiven Hover-Tooltip zu unterscheiden. */
.dispatch-drag-time-preview {
    position: fixed;
    z-index: 300;
    display: none;
    background: var(--color-accent);
    color: #fff;
    padding: 4px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 600;
    box-shadow: var(--shadow-card);
    pointer-events: none;
}

.dispatch-drag-time-preview.is-visible {
    display: block;
}

.httip-title {
    font-weight: 600;
    margin-bottom: 2px;
}

.httip-notes {
    margin-top: 4px;
    padding-top: 4px;
    border-top: 1px solid rgba(255, 255, 255, 0.25);
    opacity: 0.9;
}

.httip-meta {
    margin-top: 4px;
    opacity: 0.85;
}

/* CoreDesk Dispatch (teamweite Einsatzplanung, siehe templates/dispatch/): Tagesansicht nutzt
   dieselben .htimeline-*-Klassen wie Dashboard/Pulse (Zeilen sind hier Techniker statt Tage),
   ergaenzt um eine Abwesenheits-Ebene (.dispatch-absence) und Drag-Drop-Handles. Neue, dedizierte
   Farbe (--status-absent) statt Wiederverwendung von --status-warning, um keine Verwechslung mit
   dem bereits aehnlich eingefaerbten "Warten auf Kunde"-Badge zu erzeugen. */
.dispatch-absence {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 8px;
    background-color: var(--status-absent-soft);
    background-image: repeating-linear-gradient(135deg, var(--status-absent-soft), var(--status-absent-soft) 6px, transparent 6px, transparent 12px);
}

/* Badge auf dem schraffierten Abwesenheits-Balken (siehe DispatchController::buildDayRows()) -
   weisser/heller Chip statt reiner Farbtext, damit die Beschriftung auch bei langen schraffierten
   Balken (mehrtaegige Abwesenheit) klar lesbar bleibt. Hover zeigt per _absence_tooltip.twig den
   echten Zeitraum (von-bis) - siehe htimeline.js: wireRichTooltip(). */
.dispatch-absence-badge {
    background: var(--color-surface);
    border: 1px solid var(--status-absent);
    color: var(--status-absent);
    border-radius: 999px;
    padding: 2px 10px;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
    cursor: default;
}

/* Individuelle HR-Arbeitszeit-Sperrzeit je Techniker (siehe UserWorkScheduleRepository,
   DispatchController::buildDayRows()) - bewusst dieselbe rot-schraffierte Optik wie
   .dispatch-absence (beides signalisiert "hier nicht einplanbar"), aber eigene Klasse, da
   semantisch etwas anderes ist (nicht im Dienst laut Arbeitsvertrag statt echter Abwesenheit)
   und nie gleichzeitig mit .dispatch-absence fuer denselben Techniker/Tag gerendert wird. Rein
   informativ wie die Off-Hours-Baender (siehe TechnicianAvailabilityChecker) - blockiert keine
   Klicks/Drag-and-Drop, ein Kollege soll auch ausserhalb des nominalen Schichtplans einplanen
   koennen. */
.dispatch-outside-hours {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 0;
    background-color: var(--status-absent-soft);
    background-image: repeating-linear-gradient(135deg, var(--status-absent-soft), var(--status-absent-soft) 6px, transparent 6px, transparent 12px);
    pointer-events: none;
}

/* Rein visuelle Feiertags-/Wochenend-/Feierabend-Schattierung (siehe SystemHolidayRepository,
   DispatchController::dayMeta()/DashboardController::dayMeta()) - bewusst KEINE Schraffur (bleibt
   der Abwesenheits-Darstellung vorbehalten) und explizit z-index:-1 statt nur DOM-Reihenfolge,
   damit die Baender garantiert unter Terminen/Abwesenheiten liegen und Drag-and-Drop/Klicks
   niemals blockieren - Termine muessen jederzeit uneingeschraenkt in diese Zonen verschiebbar
   bleiben. */
.htimeline-weekend,
.htimeline-holiday,
.htimeline-offhours {
    position: absolute;
    top: 0;
    bottom: 0;
    pointer-events: none;
    z-index: -1;
}

.htimeline-weekend { background: var(--calendar-weekend-bg); }
.htimeline-holiday { background: var(--calendar-holiday-bg); }
.htimeline-offhours { background: var(--calendar-offhours-bg); }

.htimeline-appt[draggable="true"] {
    cursor: grab;
}

.htimeline-appt[draggable="true"]:active {
    cursor: grabbing;
}

.htimeline-track.is-drag-over {
    background: var(--color-accent-soft);
}

.htimeline-track.is-drag-rejected {
    background: var(--status-absent-soft);
}

/* Ticket-Pool ("Unzugeordnete Tickets") links neben dem Dispatch-Grid, siehe
   templates/dispatch/_ticket_pool.twig - zweispaltiges Layout wie im Referenz-Mockup,
   Pool-Spalte schmal und fest, Grid-Spalte nimmt den Rest ein. */
.dispatch-layout {
    display: flex;
    gap: 16px;
    align-items: flex-start;
}

.dispatch-pool {
    width: 280px;
    flex-shrink: 0;
    max-height: calc(100vh - 140px);
    overflow-y: auto;
    position: sticky;
    top: 16px;
}

.dispatch-grid-card {
    flex: 1;
    min-width: 0;
}

.dispatch-pool-hint {
    color: var(--color-text-muted);
    font-size: 12px;
    margin: -8px 0 12px;
}

.dispatch-pool-card {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin-bottom: 10px;
    cursor: grab;
}

.dispatch-pool-card:active {
    cursor: grabbing;
}

.dispatch-pool-card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 4px;
}

.dispatch-pool-card-id {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    color: var(--color-text-muted);
    font-weight: 600;
}

.dispatch-pool-card-subject {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
    margin-bottom: 4px;
}

.dispatch-pool-card-meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 11px;
    color: var(--color-text-muted);
}

.dispatch-pool-card-category {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    padding: 1px 6px;
}

@media (max-width: 900px) {
    .dispatch-layout {
        flex-direction: column;
    }

    .dispatch-pool {
        width: 100%;
        max-height: 300px;
        position: static;
    }
}

/* Wochenansicht: bewusst kein Stunden-Raster (siehe Anforderung "detaillierte Tagesansicht
   (Stunden-Raster)" vs. Wochenuebersicht) - stattdessen kompakte, gestapelte Chips pro
   Tages-Zelle. Gleiche 130px-Label-Spalte wie .htimeline-row-label, damit Tages- und
   Wochenansicht optisch als dasselbe Grid wirken. */
.dispatch-week-row {
    display: grid;
    grid-template-columns: 130px repeat(7, 1fr);
    border-bottom: 1px solid var(--color-border);
    min-width: 900px;
}

.dispatch-week-row:last-child {
    border-bottom: none;
}

.dispatch-week-header {
    display: grid;
    grid-template-columns: 130px repeat(7, 1fr);
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 4px;
    margin-bottom: 4px;
    min-width: 900px;
}

.dispatch-week-header-cell {
    padding: 4px 6px;
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    text-align: center;
}

.dispatch-week-cell {
    position: relative;
    min-height: 52px;
    padding: 4px;
    border-left: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.dispatch-week-cell--weekend {
    background: var(--calendar-weekend-bg);
}

.dispatch-week-cell--holiday {
    background: var(--calendar-holiday-bg);
}

.dispatch-week-cell--today {
    background: var(--color-accent-soft);
}

.dispatch-week-cell--absent {
    background-color: var(--status-absent-soft);
    background-image: repeating-linear-gradient(135deg, var(--status-absent-soft), var(--status-absent-soft) 6px, transparent 6px, transparent 12px);
}

.dispatch-chip {
    border-radius: 4px;
    padding: 2px 6px;
    font-size: 11px;
    line-height: 1.3;
    color: #fff;
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: grab;
}

.dispatch-chip--customer_appointment { background: var(--entrytype-appointment); }
.dispatch-chip--tentative { background: var(--status-neutral); }
.dispatch-chip--booked_service { background: var(--entrytype-service); }
.dispatch-chip--callback { background: var(--entrytype-callback); }
/* Siehe .htimeline-appt--email_reply - dasselbe Konzept fuer die Wochenansicht-Chips. */
.dispatch-chip--email_reply { background: var(--entrytype-email); }

/* Siehe .htimeline-appt--logged - dasselbe Konzept fuer die Wochenansicht-Chips. */
.dispatch-chip--logged {
    background: transparent;
    border: 1.5px dashed var(--color-accent);
    color: var(--color-text);
    cursor: pointer;
}

.dispatch-chip--logged.dispatch-chip--email_reply {
    border-color: var(--entrytype-email);
}

/* Siehe .htimeline-appt--logged.htimeline-appt--quick_punch - dasselbe Konzept fuer die
   Wochenansicht-Chips (solide Indigo statt gestrichelt). */
.dispatch-chip--logged.dispatch-chip--quick_punch {
    background: var(--entrytype-push);
    border: none;
    color: #fff;
    cursor: default;
}

.pulse-progress-bar {
    display: flex;
    height: 22px;
    border-radius: 999px;
    overflow: hidden;
    background: var(--color-bg);
    margin-top: 10px;
}

.pulse-progress-segment { height: 100%; }
.pulse-progress-segment--billable { background: var(--color-accent); }
.pulse-progress-segment--included { background: var(--status-active); }
.pulse-progress-segment--travel { background: var(--status-warning); }
.pulse-progress-segment--internal { background: var(--status-neutral); }
.pulse-progress-segment--gap { background: var(--color-danger-text); }

.pulse-progress-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin-top: 10px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.pulse-legend-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    margin-right: 5px;
    vertical-align: middle;
}

.pulse-legend-dot--billable { background: var(--color-accent); }
.pulse-legend-dot--included { background: var(--status-active); }
.pulse-legend-dot--travel { background: var(--status-warning); }
.pulse-legend-dot--internal { background: var(--status-neutral); }
.pulse-legend-dot--gap { background: var(--color-danger-text); }

.pulse-overtime-line {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border);
    font-size: 13px;
}

/* Abwesenheits-Kachel ersetzt den Fortschrittsbalken komplett fuer ganztaegige Urlaubs-/
   Krankheitstage (siehe PulseController::index()) - nutzt dieselben --status-absent-Variablen
   wie .dispatch-absence im Dispatch-Modul, aber als grosse, ruhige Statuskachel statt Balken. */
.pulse-absence-tile {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 10px 4px;
}

/* CoreDesk Directory: Dispatcher-Widget "Unterstuetzung anfordern" (templates/dispatch/
   _external_tradesperson_widget.twig) - schwebender Trigger unten rechts im Dispatch-Grid,
   rein CSS-getriebenes Popover (is-open-Klasse), gleiche Tokens wie der Rest der App statt
   eigenem Farbschema. */
.dispatch-tradesperson-widget {
    position: fixed;
    right: 24px;
    bottom: 24px;
    z-index: 60;
}

.dispatch-tradesperson-trigger {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: none;
    background: var(--color-accent);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-card);
    cursor: pointer;
}

.dispatch-tradesperson-trigger:hover {
    background: var(--color-accent-hover);
}

.dispatch-tradesperson-trigger .icon {
    width: 22px;
    height: 22px;
}

.dispatch-tradesperson-panel {
    display: none;
    position: absolute;
    right: 0;
    bottom: 58px;
    width: 320px;
    max-height: 70vh;
    overflow-y: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    padding: 14px;
}

.dispatch-tradesperson-panel.is-open {
    display: block;
}

.dispatch-tradesperson-panel-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
}

.dispatch-tradesperson-panel-head h3 {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
}

.dispatch-tradesperson-close {
    border: none;
    background: none;
    cursor: pointer;
    color: var(--color-text-muted);
    display: flex;
    padding: 2px;
}

.dispatch-tradesperson-close .icon {
    width: 16px;
    height: 16px;
}

.dispatch-tradesperson-panel input#dispatch-tradesperson-search {
    width: 100%;
    margin-bottom: 10px;
}

.dispatch-tradesperson-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 10px;
}

.dispatch-tradesperson-chip {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    border-radius: 999px;
    padding: 3px 10px;
    font-size: 12px;
    cursor: pointer;
}

.dispatch-tradesperson-chip.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}

.dispatch-tradesperson-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 4px;
    border-bottom: 1px solid var(--color-border);
}

.dispatch-tradesperson-row:last-child {
    border-bottom: none;
}

.dispatch-tradesperson-row-info {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.dispatch-tradesperson-row-name {
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dispatch-tradesperson-row-company {
    font-size: 12px;
    color: var(--color-text-muted);
}

.dispatch-tradesperson-row-actions {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
}

.dispatch-tradesperson-call,
.dispatch-tradesperson-copy {
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-accent);
    cursor: pointer;
}

.dispatch-tradesperson-call:hover,
.dispatch-tradesperson-copy:hover {
    background: var(--color-accent-soft);
}

.dispatch-tradesperson-empty {
    font-size: 13px;
    color: var(--color-text-muted);
    padding: 10px 4px;
    margin: 0;
}

@media (max-width: 640px) {
    .dispatch-tradesperson-panel {
        position: fixed;
        left: 12px;
        right: 12px;
        bottom: 84px;
        width: auto;
    }
}

.pulse-absence-tile-icon {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    color: var(--status-absent);
}

.pulse-absence-tile-label {
    font-size: 18px;
    font-weight: 700;
    color: var(--status-absent);
}

.pulse-absence-tile-hint {
    font-size: 13px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

/* Wochenende/Feiertag sind keine Abwesenheiten (kein Urlaubskonto/Attest betroffen), daher eigene,
   ruhigere Akzentfarben statt des Abwesenheits-Rot (siehe PulseController::index()). */
.pulse-absence-tile--weekend .pulse-absence-tile-icon,
.pulse-absence-tile--weekend .pulse-absence-tile-label {
    color: var(--status-neutral);
}

.pulse-absence-tile--holiday .pulse-absence-tile-icon,
.pulse-absence-tile--holiday .pulse-absence-tile-label {
    color: var(--status-hold);
}

.pulse-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.pulse-kpi-label {
    font-size: 12px;
    color: var(--color-text-muted);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.pulse-kpi-value {
    font-size: 26px;
    font-weight: 700;
    line-height: 1.2;
    margin-top: 4px;
}

.pulse-kpi-trend {
    font-size: 13px;
    margin-top: 4px;
}

.pulse-kpi-hint {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.pulse-kpi-subgrid {
    display: flex;
    flex-direction: column;
    gap: 14px;
    margin-top: 8px;
}

.pulse-widgets-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.pulse-widget-card--revenue {
    background: linear-gradient(135deg, var(--color-accent-soft), var(--color-bg));
}

.pulse-revenue-value {
    font-size: 30px;
    font-weight: 700;
    line-height: 1.2;
    margin-top: 4px;
    color: var(--color-accent);
    font-variant-numeric: tabular-nums;
}

.pulse-tickets-row {
    display: flex;
    gap: 28px;
    margin-top: 4px;
}

.pulse-sla-row {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 6px;
}

.pulse-donut {
    --donut-size: 64px;
    width: var(--donut-size);
    height: var(--donut-size);
    flex-shrink: 0;
    border-radius: 50%;
    background: conic-gradient(var(--donut-color) calc(var(--donut-percent) * 1%), var(--color-border) 0);
    display: flex;
    align-items: center;
    justify-content: center;
}

.pulse-donut::before {
    content: '';
    position: absolute;
}

.pulse-donut-value {
    width: calc(var(--donut-size) - 16px);
    height: calc(var(--donut-size) - 16px);
    border-radius: 50%;
    background: var(--color-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.pulse-topc-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: 6px;
}

.pulse-topc-row {
    display: grid;
    grid-template-columns: 96px 1fr auto;
    align-items: center;
    gap: 10px;
}

.pulse-topc-name {
    font-size: 12px;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pulse-topc-bar {
    height: 8px;
    border-radius: 4px;
    background: var(--color-border);
    overflow: hidden;
}

.pulse-topc-bar-fill {
    height: 100%;
    background: var(--color-accent);
    border-radius: 4px;
}

.pulse-topc-value {
    font-size: 12px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

@media (max-width: 640px) {
    .pulse-topc-row {
        grid-template-columns: 72px 1fr auto;
    }

    .htimeline {
        --htimeline-hour-width: 48px;
    }

    .htimeline-header-row,
    .htimeline-row {
        grid-template-columns: 90px calc(var(--htimeline-hour-width) * 24);
    }
}

/* Ticket-Detail: dichtes Info-Grid (View+Edit in einem, TANSS-inspiriert) */
.ticket-subject-input {
    width: 100%;
    border: 1px solid transparent;
    background: transparent;
    font-size: 20px;
    font-weight: 700;
    font-family: inherit;
    padding: 4px 6px;
    margin: 0 0 16px -6px;
    color: var(--color-text);
    border-radius: var(--radius-sm);
}

.ticket-subject-input:hover,
.ticket-subject-input:focus {
    background: var(--color-accent-soft);
    border-color: var(--color-border);
    outline: none;
}

.info-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px 28px;
    margin-bottom: 18px;
}

@media (max-width: 640px) {
    .info-grid {
        grid-template-columns: 1fr;
    }
}

.info-grid-label {
    display: block;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    margin-bottom: 3px;
}

/* Hebt ein einzelnes info-grid-Feld farblich von den Nachbarfeldern ab (z.B. Fälligkeit) - fuer
   Felder, die trotz gleicher Optik wichtiger als der Rest sind und sonst untergehen. */
.info-grid-field--highlight {
    background: var(--status-warning-soft);
    border: 1px solid var(--status-warning-soft);
    border-left: 3px solid var(--status-warning);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    margin: -10px -12px;
}

.info-grid-field--highlight .info-grid-label {
    color: #A66A1E;
}

.info-grid select,
.info-grid input[type="date"],
.info-grid input[type="datetime-local"],
.info-grid input[type="text"] {
    width: 100%;
    max-width: none;
    border: 1px solid var(--color-border);
    background: var(--color-bg);
    padding: 6px 8px;
    font-size: 13px;
    font-weight: 400;
    font-family: inherit;
    color: var(--color-text);
    border-radius: var(--radius-sm);
}

.ticket-customer-link {
    margin-bottom: 6px;
}

.ticket-customer-link a {
    font-size: 15px;
    font-weight: 700;
}

.info-grid select:hover,
.info-grid input:hover {
    border-color: var(--color-text-muted);
}

.info-grid select:focus,
.info-grid input:focus {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    outline: none;
}

.info-static {
    font-size: 13px;
    font-weight: 600;
}

/* Betroffene Geraete & Assets (Ticket-Sidebar Card 3) - dezenter grauer Hintergrund pro
   Eintrag statt reiner Textzeile, Geraetename in Monospace (gleiche Schriftfamilie wie SLA-
   Uhrzeiten), Status als bereits app-weit etablierter .badge--{status} rechts daneben. */
.ticket-asset-row {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--color-bg);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    margin-bottom: 6px;
}

.ticket-asset-name {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    color: var(--color-text);
}

.ticket-asset-row .badge {
    margin-left: auto;
}

/* Kosten-Vorrechnung (Card 4) - feine Trennlinie zwischen den einzelnen Zeit-/Kostenzeilen und
   der Gesamtsumme, damit "erfasste Zeit" optisch klar von "geschaetzte Kosten" abgesetzt ist. */
.cost-summary-rows {
    border-top: 1px solid var(--color-border);
    padding-top: 8px;
    margin-top: 8px;
}

.cost-summary-line {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.cost-summary-label {
    font-size: 13px;
    color: var(--color-text-muted);
}

.cost-summary-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text);
}

.cost-summary-total-line {
    border-top: 1px solid var(--color-border);
    padding-top: 8px;
    margin-top: 8px;
}

.cost-summary-total-line .cost-summary-value {
    font-size: 14px;
}

/* Ticket-Detail: 2-Spalten-Layout (Hauptbereich + Sidebar, Freshdesk-inspiriert) */
.ticket-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 340px;
    gap: 20px;
    align-items: start;
}

@media (max-width: 900px) {
    .ticket-layout {
        grid-template-columns: 1fr;
    }
}

/* Bleibt beim Scrollen durch einen langen E-Mail-Verlauf oben sichtbar - undurchsichtiger
   Hintergrund (Theme-Variable statt festem #fff, damit Dark Mode nicht bricht) + Schatten als
   Scroll-Trennlinie. z-index noetig, da spaeter im DOM stehende Timeline-Karten sonst beim
   Vorbeiscrollen ueber der Leiste liegen wuerden (unter dem Modal-Overlay z-index:100 gehalten).
   Negative Margins + kompensierendes Padding heben das horizontale Padding von .content
   (24px 28px) auf, damit die Leiste bis an den Rand des Inhaltsbereichs reicht statt links/
   rechts einen Streifen scrollenden Inhalts durchblitzen zu lassen. */
.ticket-toolbar {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
    margin: 0 -28px 16px;
    position: sticky;
    top: 0;
    z-index: 40;
    background: var(--color-surface);
    padding: 10px 28px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}

/* Typ-Umschalter oben in der vereinheitlichten "Neuer Eintrag"-Maske (Support-Eintrag/Termin/
   Termin-Vormerkung, siehe switchEntryType() in tickets/show.twig) - drei gleichrangige Tabs,
   der aktive per btn-primary hervorgehoben. */
.entry-type-tabs {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
}

.project-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 16px;
    font-size: 13px;
    flex-wrap: wrap;
}

.project-banner .inline-form {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    width: 100%;
}

.ticket-running-status {
    font-size: 13px;
    color: var(--color-text-muted);
    margin-left: auto;
}

/* Live SLA-Countdown im Sticky-Header (siehe show.twig <script>) - Ampel-Logik per Textfarbe:
   gruen (>50% Restzeit) -> orange (<25%) -> rot (ueberschritten). Steht direkt neben der
   Lauf-Zeit-Anzeige, die bereits per margin-left:auto an den rechten Rand geschoben wird. */
.sla-countdown {
    display: inline-flex;
    align-items: center;
    font-size: 12px;
    font-weight: 700;
    margin-left: 14px;
    padding: 4px 10px;
    border-radius: 999px;
    white-space: nowrap;
}

.sla-countdown:empty { display: none; }

.sla-countdown--ok { background: var(--status-active-soft); color: var(--status-active); }
.sla-countdown--warning { background: var(--status-warning-soft); color: var(--status-warning); }
.sla-countdown--breached { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }
.sla-countdown--paused { background: var(--color-bg); color: var(--color-text-muted); font-weight: 400; font-style: italic; }

.timeline-entry {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-left: 3px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    margin-bottom: 10px;
}

.timeline-entry--time_entry { border-left-color: var(--color-accent); }
.timeline-entry--appointment-customer_appointment { border-left-color: var(--status-active); }
.timeline-entry--appointment-tentative { border-left-color: var(--status-neutral); }
.timeline-entry--appointment-booked_service { border-left-color: var(--color-accent); }
.timeline-entry--appointment-callback { border-left-color: var(--status-callback); }
.timeline-entry--comment { border-left-color: #f0dca0; background: #fdf6e3; }

/* Angepinnte Kommentare (siehe tickets/_comment_card.twig) - eigener Akzent statt des normalen
   Kommentar-Gelbs, damit sie sich im oberhalb der Timeline gerenderten "angepinnt"-Bereich klar
   von normalen Kommentaren abheben. */
.timeline-entry--comment.is-pinned {
    border-left-color: var(--color-accent);
    box-shadow: 0 0 0 1px var(--color-accent-soft);
}

/* Statuswechsel-Log ("Ticket geschlossen am..."/"wieder geoeffnet am...") - bewusst keine volle
   .timeline-entry-Card wie Nachrichten/Leistungen, sondern eine schlanke Trennzeile, da es reine
   Protokoll-Information ohne eigenen Inhalt ist. */
.timeline-status-event {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 12px;
    color: var(--color-text-muted);
    padding: 6px 4px;
    margin-bottom: 10px;
}

.timeline-status-event::before,
.timeline-status-event::after {
    content: "";
    flex: 1;
    border-top: 1px dashed var(--color-border);
}

/* ============================================================================
   Passwort-Generator im Tresor-Formular (templates/vault/index.twig) - klappbares Panel
   mit Laengen-Schieberegler und Kategorie-Toggles, aehnlich gaengiger Passwort-Manager.
   ============================================================================ */
.password-generator-panel {
    margin-top: 8px;
    padding: 12px 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
}

.password-generator-panel input[type="range"] {
    width: 100%;
    accent-color: var(--color-accent);
}

.pwgen-toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    font-size: 13px;
    padding: 6px 0;
    border-bottom: 1px solid var(--color-border);
}

.pwgen-toggle-row:last-of-type {
    border-bottom: none;
}

.pwgen-toggle-row code {
    margin-left: 6px;
    font-size: 11px;
    color: var(--color-text-muted);
}

.pwgen-toggle-row input[type="checkbox"] {
    width: 36px;
    height: 20px;
    accent-color: var(--color-accent);
    flex-shrink: 0;
}

/* Kopfzeile der Leistungskarten - volle Kartenbreite per negativem Margin (hebt das
   Karten-Padding 12px/14px auf), da der Header einen eigenen, vom Body abgesetzten
   Hintergrund braucht (Tailwind-Vorlage: bg-slate-50/60 border-b border-slate-200/60
   px-5 py-3). color-mix() statt fixem Hex, damit die Slate-Toene ueber --color-bg/
   --color-border auch im Dark Theme passend abgetoent bleiben statt hartkodiert hell zu sein. */
.timeline-entry-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    font-size: 12px;
    color: var(--color-text-muted);
    background: color-mix(in srgb, var(--color-bg) 60%, transparent);
    border-bottom: 1px solid color-mix(in srgb, var(--color-border) 60%, transparent);
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    margin: -12px -14px 12px;
    padding: 12px 20px;
}

/* Leichter Farbton im Kartenkopf je Eintragstyp (Referenz: td-2.html-Mockup) - Leistungen
   bläulich, E-Mails gruenlich, ueber das ohnehin schon vorhandene data-kind-Attribut (siehe
   filterTimeline() weiter oben) gescopt, keine neuen Klassen/Template-Aenderungen noetig. */
[data-kind="time_entry"] .timeline-entry-header {
    background: var(--color-accent-soft);
}

[data-kind="email_message"] .timeline-entry-header {
    background: var(--status-active-soft);
}

/* Dieselbe Faerbe-Konvention fuer die neue Ansprechpartner-Detailseite (templates/contacts/
   show.twig): Notizen teal (bisher nur als tag-badge--ticket-permission genutzter Ton, hier
   erstmals als Timeline-Kopf-Faerbung), E-Mails im selben Gruenton wie die Ticket-E-Mails. */
[data-kind="note"] .timeline-entry-header {
    background: var(--status-callback-soft);
}

[data-kind="email"] .timeline-entry-header {
    background: var(--status-active-soft);
}

/* Kompakte Sonderdarstellung fuer die automatische E-Mail-Antwort-Pauschale (siehe
   TicketMessageController::send()) - bewusst deutlich kleiner als eine normale Leistungs-Karte,
   da es eine automatisch erzeugte Buchhaltungs-Notiz ist, kein manuell erfasster Arbeitsnachweis.
   Leichter Orange-Ton (--status-warning-soft, sonst fuer Health-Radar/Warn-Zustaende genutzt)
   statt des blauen Leistungs-Tons, damit sie sich auf den ersten Blick von echten manuellen
   Leistungen unterscheidet. Ueberschreibt bewusst [data-kind="time_entry"] .timeline-entry-header
   oben (gleiche Spezifitaet, steht aber spaeter im Stylesheet). */
.timeline-entry--email-reply {
    padding: 6px 10px;
    margin-bottom: 6px;
}

.timeline-entry--email-reply .timeline-entry-header {
    background: var(--status-warning-soft);
    margin: -6px -10px 0;
    padding: 5px 10px;
    font-size: 11px;
    border-radius: var(--radius-sm);
    border-bottom: none;
}

.timeline-entry--email-reply .timeline-entry-title,
.timeline-entry--email-reply .timeline-entry-type {
    font-size: 11px;
}

.timeline-entry--email-reply .timeline-entry-actions {
    margin: 0 -10px -6px;
    padding: 4px 10px;
    background: transparent;
    border-top: none;
}

.timeline-entry--email-reply .timeline-entry-actions .btn-action {
    padding: 2px 8px;
    font-size: 11px;
}

.timeline-entry-title {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
    color: var(--color-text);
    font-size: 12px;
}

.timeline-entry-body {
    font-size: 13px;
    color: var(--color-text);
    white-space: pre-wrap;
    line-height: 1.625;
}

.timeline-entry-type {
    font-weight: 400;
    color: var(--color-text-muted);
}

.timeline-entry-meta {
    display: flex;
    align-items: center;
    gap: 8px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

.timeline-entry-duration {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 11px;
    font-weight: 700;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    padding: 2px 8px;
    border-radius: 999px;
    white-space: nowrap;
}

/* Fusszeile der Leistungskarten - ebenfalls volle Kartenbreite (Tailwind-Vorlage:
   bg-slate-50/40 border-t border-slate-100 px-5 py-2.5 flex gap-2). */
.timeline-entry-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    background: color-mix(in srgb, var(--color-bg) 40%, transparent);
    border-top: 1px solid color-mix(in srgb, var(--color-border) 50%, transparent);
    border-radius: 0 0 var(--radius-sm) var(--radius-sm);
    margin: 12px -14px -12px;
    padding: 10px 20px;
}

.timeline-entry-internal-note {
    color: var(--color-text-muted);
    font-style: italic;
    display: flex;
    align-items: flex-start;
    gap: 4px;
    margin-top: 4px;
}

/* Mail-Kommunikationsmodul (Phase 1): kundensichtbare Notiz = blau, interne Notiz = gelb -
   visuell sofort erkennbar, ob ein Eintrag im Ticket-Verlauf der Kunde sehen darf oder nicht. */
.timeline-message {
    font-size: 13px;
    white-space: pre-wrap;
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    margin-top: 6px;
}

.timeline-message-label {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    margin-bottom: 4px;
}

.timeline-message--internal {
    background: #fdf6e3;
    border: 1px solid #f0dca0;
    color: #6b5410;
    font-style: normal;
}

/* Kompaktes Support-Eintrag-Formular (TANSS-Vorbild): flache, tabellarische Zeilen statt
   einzelner .field-Bloecke - haelt das Erfassen einer Leistung auf einen Blick uebersichtlich. */
.support-loc-row, .entry-row {
    display: flex;
    align-items: center;
    gap: 12px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 10px 14px;
    margin-bottom: 8px;
    flex-wrap: wrap;
}

.entry-row-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-width: 70px;
}

.entry-row-controls {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.support-loc-options {
    display: flex;
    gap: 8px;
}

/* Nutzer-Fund 2026-08-13 (Abrechnung > Freigabe > Quick-Edit-Drawer): Icon lag oben-links in der
   Ecke statt zentriert, weil das Drawer-Markup (anders als tickets/_shared_entry_fields.twig) das
   <label class="support-loc-option"> in einen <div class="field aqe-field">-Wrapper einbettet -
   dort ueberschreiben GLEICH ZWEI spezifischere Regeln (".field label" UND ".aqe-field label",
   je 0-1-1) unser urspruengliches "display:inline-flex" (0-1-0). Ein simpler "label."-Praefix
   (ebenfalls 0-1-1) reichte nicht, da ".aqe-field label" spaeter im Stylesheet steht und bei
   Gleichstand gewinnt - echter Fix braucht eine hoehere, ordnungsunabhaengige Spezifitaet ueber
   den stabilen Eltern-Wrapper (0-2-1, in BEIDEN Verwendern .support-loc-options vorhanden).
   Zusaetzlich 34x30px auf ein sauberes 32x32px-Quadrat vereinheitlicht (Nutzer-Feedback: "sehen
   nicht sauber aus"). */
.support-loc-options label.support-loc-option {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    cursor: pointer;
}

.support-loc-option input {
    display: none;
}

.support-loc-option:has(input:checked) {
    background: var(--color-accent-soft);
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.date-nav {
    width: 24px;
    height: 24px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    cursor: pointer;
    font-size: 14px;
    line-height: 1;
    color: var(--color-text-muted);
}

.date-weekday {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    min-width: 24px;
}

/* "Zusaetzlicher Techniker" (Support-Eintrag/Termin/Terminvormerkung, siehe
   _shared_entry_fields.twig) - gestrichelter Rahmen hebt die Sektion dezent vom Rest des
   Formulars ab, ohne selbst laut zu wirken (gleiches Prinzip wie .htimeline-break in Phase 1). */
.tech-box {
    background: var(--color-bg);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
    padding: 14px;
}

.tech-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border);
}

.tech-row:first-child {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

.second-tech-select {
    min-width: 200px;
}

.tech-row-remove-btn {
    color: var(--color-danger-text);
    border-color: var(--color-danger-border);
    padding: 4px 10px;
    font-size: 12px;
}

.entry-time-sep {
    font-size: 12px;
    color: var(--color-text-muted);
}

/* "von"/"bis" sind klickbare Buttons (jetzige Uhrzeit einsetzen), sollen aber wie der
   bisherige reine Text-Span aussehen statt wie ein Standard-Button. */
.entry-time-now-btn {
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    text-decoration: underline dotted;
    cursor: pointer;
}

.entry-time-now-btn:hover {
    color: var(--color-accent);
}

.time-box {
    width: 48px;
    max-width: 48px;
    text-align: center;
    padding: 6px 4px;
    /* Ohne das hier fressen die nativen Auf/Ab-Pfeile eines type="number"-Feldes einen Teil der
       48px Breite weg - bei zweistelligen Werten (z.B. "00", "59") wurde dadurch die letzte
       Ziffer abgeschnitten. */
    -moz-appearance: textfield;
    appearance: textfield;
}

.time-box::-webkit-inner-spin-button,
.time-box::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.entry-rounding-preview {
    font-size: 12px;
    color: var(--color-text-muted);
}

/* Die Leistungs-Notiz erscheint auf der Kundenrechnung - braucht daher mehr Platz als die
   sonst auf 420px begrenzten Textfelder, damit man saubere, vollstaendige Texte schreiben kann. */
.time-note-textarea {
    max-width: none !important;
    width: 100%;
    min-height: 140px;
}

.sidebar-section {
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 14px;
    margin-bottom: 14px;
}

.sidebar-section:last-child {
    border-bottom: none;
    margin-bottom: 0;
    padding-bottom: 0;
}

.sidebar-section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    cursor: pointer;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin-bottom: 12px;
    user-select: none;
}

/* Statische Variante von .sidebar-section-header (kein Klick/Chevron) - fuer die immer
   sichtbare "Kunde & Core-Daten"-Karte auf der Ticket-Detailseite, die anders als
   Checkliste/Geraete/Kosten-Vorrechnung nicht einklappbar ist. */
.sidebar-section-title {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin-bottom: 12px;
}

/* Hauchduenne Trennlinie direkt unter dem Card-Titel (z.B. Kosten-Vorrechnung), um Titel
   optisch vom Inhalt abzusetzen - negative margin zieht sie in die Luecke, die der Header
   ohnehin per margin-bottom laesst, statt zusaetzlichen Weissraum aufzubauen. */
.sidebar-section-divider {
    border-bottom: 1px solid var(--color-border);
    margin: -6px 0 12px;
}

.sidebar-section-header .chevron {
    transition: transform 0.15s ease;
}

.sidebar-section.is-collapsed .sidebar-section-header .chevron {
    transform: rotate(-90deg);
}

.sidebar-section.is-collapsed .sidebar-section-body {
    display: none;
}

/* ============================================================================
   Ticket-Historie (Sidebar-Meilensteinansicht, siehe TicketController::buildHistoryTimeline())
   - vertikale Linie mit einem Punkt je Eintrag, neuester Eintrag oben. Bewusst kein
   .timeline-entry-Card wie bei Nachrichten/Leistungen - reines Protokoll ohne eigenen Inhalt,
   aehnlich .timeline-status-event, aber vertikal statt als Trennzeile.
   ============================================================================ */
.history-timeline {
    position: relative;
    padding-left: 16px;
}

.history-timeline::before {
    content: "";
    position: absolute;
    left: 4px;
    top: 4px;
    bottom: 4px;
    border-left: 2px solid var(--color-border);
}

.history-entry {
    position: relative;
    padding-bottom: 14px;
}

.history-entry:last-child {
    padding-bottom: 0;
}

.history-entry::before {
    content: "";
    position: absolute;
    left: -16px;
    top: 3px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-accent);
    border: 2px solid var(--color-bg-card, #fff);
    box-shadow: 0 0 0 1px var(--color-border);
}

.history-entry-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 8px;
    font-size: 11px;
    color: var(--color-text-muted);
    margin-bottom: 4px;
}

.history-entry-time {
    white-space: nowrap;
}

.history-entry-user {
    font-weight: 600;
    text-align: right;
}

.history-entry-line {
    font-size: 12px;
    line-height: 1.5;
}

.history-entry-line--reason {
    color: var(--color-text-muted);
    font-style: italic;
}

.history-entry-line .badge {
    font-size: 10px;
    padding: 1px 6px;
}

/* SLA-Wirkungs-Badge an Status-Historie-Zeilen im klassischen Design (Nutzer-Wunsch 2026-08-12,
   siehe SlaEngine::impactOfStatusChange()) - gleiche Klassen/Werte wie das tdv2-Pendant in
   ticket-new-design.css, hier nur mit den app.css-eigenen Farb-Tokens statt --tdv2-*. */
.sla-impact-badge { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px; border-radius: 999px; font-size: 10px; font-weight: 700; margin-left: 6px; white-space: nowrap; }
.sla-impact-badge--active,
.sla-impact-badge--resumed { background: var(--status-active-soft); color: var(--status-active); }
.sla-impact-badge--paused { background: var(--color-bg); color: var(--color-text-muted); }
.sla-impact-badge--stopped { background: var(--color-bg); color: var(--color-text-muted); }

.asset-heartbeat-block {
    margin-top: 4px;
    padding: 8px 10px;
    background: var(--color-bg-subtle, #f8fafc);
    border: 1px solid var(--color-border);
    border-radius: 6px;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.asset-heartbeat-item {
    font-size: 12px;
    color: var(--color-text-muted);
}

.asset-heartbeat-item strong {
    color: var(--color-text);
    font-weight: 600;
}

.badge--warning {
    background: var(--status-warning-soft);
    color: #A66A1E;
}

.history-show-more {
    display: block;
    margin: 10px 0 0 16px;
    padding: 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-accent);
    background: none;
    border: none;
    cursor: pointer;
}

.history-show-more:hover {
    text-decoration: underline;
}

/* ============================================================================
   Topbar auf schmalen Breiten (Telefon/Tablet-Hochformat): seit dem Umbau von .topbar auf
   Flexbox (statt "1fr auto 1fr"-Grid, siehe .topbar-center weiter oben - der CoreDesk-Finder
   folgt jetzt direkt nach dem linken Kopfbereich statt fix mittig zu sitzen) schrumpfen
   .topbar-left/.topbar-center von selbst; hier nur noch die Detailanpassungen: Suche wird zum
   reinen Icon-Button, Ueberschrift bekommt Ellipsis statt Umbruch, Kontext-Nutzer-Name/Rolle
   rechts wird ausgeblendet (Avatar allein reicht auf dem schmalen Viewport als Identitaets-
   Anker, Details bleiben im Dropdown erreichbar). */

/* Ticket-Detailseite (Ticket-Ansicht 2.0, .tdv2-header): braucht mehr Platz als die schlichte
   .topbar h1-Ueberschrift anderer Seiten (Titel + Status-Pill + Prio-Pill statt nur Text) - ohne
   diese Regel ueberlappte .topbar-center (die 380px breite Finder-Suche, siehe oben) den
   Ticket-Header schon deutlich vor der allgemeinen 900px-Kollaps-Breite unten (Bug-Report:
   "Als Termin vormerken"-Banner/Kopfzeile bei ca. 1000px sichtbar angeschnitten/ueberlappt).
   :has() statt eines generischen Breakpoints, damit andere Seiten mit der kurzen h1-Ueberschrift
   ihre bestehende 900px-Schwelle unveraendert behalten. Schwelle bewusst hoch (1500px, nicht nur
   knapp ueber dem im Bug-Report gemeldeten ~1000px): Status-/Prio-Pill und Overflow-Menu-Icon
   sind NICHT schrumpfbar, deren kombinierte Mindestbreite (samt Avatar rechts) braucht bei
   schmaleren Viewports frueher Platz, als der urspruenglich getestete Fall nahelegt. (Der Titel
   selbst sitzt seit Nutzer-Wunsch 2026-08-17 nicht mehr in dieser Zeile, siehe .tdv2-ticket-title -
   diese Regel betrifft daher nur noch die Ticket-Nr + Pills + Icons.) */
@media (max-width: 1500px) {
    .topbar:has(.tdv2-header) .topbar-center {
        margin-left: auto;
        flex: 0 0 auto;
        max-width: none;
    }

    /* Gleiches Icon-Only-Rezept wie die allgemeine 900px-Regel unten (.finder-trigger span/kbd
       ausblenden, Kreis-Button) - hier nur frueher ausgeloest, da .topbar-center oben bereits
       auf "so schmal wie der Inhalt" umgestellt ist und der Trigger sonst weiterhin seine volle
       380px-Basisbreite beansprucht. */
    .topbar:has(.tdv2-header) .finder-trigger {
        width: 32px;
        height: 32px;
        padding: 0;
        justify-content: center;
        border-radius: 50%;
        flex-shrink: 0;
    }

    .topbar:has(.tdv2-header) .finder-trigger span,
    .topbar:has(.tdv2-header) .finder-trigger kbd {
        display: none;
    }

    .tdv2-save-indicator {
        display: none;
    }
}

@media (max-width: 900px) {
    .topbar {
        padding: 8px 12px;
        gap: 6px;
    }

    .topbar-left {
        min-width: 0;
    }

    /* Auf der Ticket-Detailseite sitzen Status-/Prio-Punkt (tdv2-header) und der Finder-Suche-
       Icon-Button (topbar-center) sonst nur 6px auseinander - bei zwei kleinen, aber
       unterschiedlich grossen Kreisen (22px Punkt vs. 32px Icon-Button) wirkt das gedraengt/
       asymmetrisch statt wie zwei getrennte Gruppen. Zusaetzlicher Abstand schafft eine klare
       optische Trennung.
       flex-basis/max-width der Desktop-Regel (0 1 380px, siehe oben) blieben bisher bestehen,
       obwohl der Finder hier nur noch ein 32px-Icon-Button ist - der Suchcontainer reservierte
       dadurch bis zu ~170px, die dem Logo/Titel in .topbar-left fehlten (gemeldeter Bug: Hub-
       Wordmark "CoreDeskHub" wurde auf iPhone-Breiten zu "Cor..." abgeschnitten). flex:0 0 auto
       laesst den Container nur noch so breit wie sein tatsaechlicher Inhalt sein. */
    .topbar-center {
        margin-left: auto;
        flex: 0 0 auto;
        max-width: none;
    }

    .topbar h1 {
        min-width: 0;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        font-size: 14px;
    }

    .tdv2-header {
        gap: 6px;
    }

    .tdv2-save-indicator {
        display: none;
    }

    /* Status-/Prio-Pill: auf Mobilbreiten nur noch der farbige Punkt statt ausgeschriebenem Text
       ("In Bearbeitung", "Medium" ...) - der ausgeschriebene Text sprengte zusammen mit
       Titel-Input und Overflow-Button den verfuegbaren Platz in der Kopfzeile und erzwang
       horizontales Verschieben der ganzen Topbar. Text bleibt als title-Attribut (Tooltip/
       Screenreader) erhalten, siehe show_new.twig. */
    .tdv2-pill-select .tdv2-pill {
        width: 22px;
        height: 22px;
        padding: 0;
        font-size: 0;
        gap: 0;
        border-radius: 50%;
        justify-content: center;
        flex-shrink: 0;
    }

    .tdv2-pill-select .tdv2-pill::before {
        width: 10px;
        height: 10px;
    }

    .finder-trigger {
        width: 32px;
        height: 32px;
        padding: 0;
        justify-content: center;
        border-radius: 50%;
        flex-shrink: 0;
    }

    .finder-trigger span,
    .finder-trigger kbd {
        display: none;
    }

    /* Ticket-Detailseite: eigenes 2-zeiliges Kopf-Layout auf Mobilbreiten. Status-/Prio-Punkt und
       der Finder-Icon-Button lagen sonst nur wenige Pixel auseinander und konnten je nach
       Titellaenge sogar uebereinander liegen (Nutzer-Feedback per Foto: Lupe lag unter dem roten
       Prio-Punkt). Loesung: .topbar-left/-center/-right + .tdv2-header werden "aufgeloest"
       (display:contents - nur ihre Kinder werden echte Flex-Items von .topbar) und per order in
       zwei feste Zeilen sortiert: Zeile 1 = Status-/Prio-Punkt + alle Aktions-Icons, Zeile 2 =
       Ticket-Nr (seit Nutzer-Wunsch 2026-08-17 der einzige Inhalt dort - der Titel wanderte als
       eigene Ueberschrift ueber die Beschreibung, siehe .tdv2-ticket-title). Scoped auf
       body.page-ticket-detail-v2, damit alle anderen Seiten (die topbar-left/-center/-right als
       normale Boxen brauchen) unveraendert bleiben. */
    body.page-ticket-detail-v2 .topbar {
        flex-wrap: wrap;
    }

    body.page-ticket-detail-v2 .topbar-left,
    body.page-ticket-detail-v2 .topbar-center,
    body.page-ticket-detail-v2 .topbar-right,
    body.page-ticket-detail-v2 .tdv2-header {
        display: contents;
    }

    /* Erzwingt den Zeilenumbruch zwischen Zeile 1 (Icons) und Zeile 2 (Ticket-Nr). */
    body.page-ticket-detail-v2 .topbar::before {
        content: '';
        order: 5;
        flex-basis: 100%;
    }

    body.page-ticket-detail-v2 .tdv2-ticket-num {
        order: 10;
    }

    .topbar-user-info {
        display: none;
    }

    .topbar-user-menu {
        padding-left: 6px;
        padding-bottom: 8px;
        margin-left: 0;
        margin-bottom: -8px;
    }

    .topbar-user-menu .tech-avatar {
        width: 24px;
        height: 24px;
        font-size: 10px;
    }

    .signal-bell {
        width: 32px;
        height: 32px;
    }
}

/* ============================================================================
   Mobile-Anpassungen - die App ist primaer fuer Desktop/Tablet gebaut, soll auf
   Telefon-Breiten aber zumindest bedienbar bleiben statt abgeschnitten/zerquetscht
   zu werden: schmalere Sidebar, horizontal scrollbare Tabellen/Wochenkalender statt
   ueberlaufendem Layout, einspaltige Detail-Grids.
   ============================================================================ */
@media (max-width: 640px) {
    /* iOS Safari zoomt beim Fokussieren automatisch in jedes Eingabefeld mit font-size < 16px
       hinein und zoomt beim Verlassen nicht zuverlaessig wieder zurueck - genau das vom Nutzer
       gemeldete Phaenomen. Root-Cause-Fix statt maximum-scale=1 im Viewport-Meta-Tag (das wuerde
       Pinch-Zoom app-weit abschalten, ein Accessibility-Rueckschritt): hier wird die Schrift alle
       Formularfelder auf mobilen Breiten auf genau die Schwelle angehoben, ab der Safari nicht mehr
       zoomt. !important, da mehrere Stylesheets (app.css/ticket-new-design.css/photo-capture.css)
       kleinere Werte fuer einzelne Feldklassen definieren und die Ladereihenfolge sonst je nach
       Seite variieren wuerde. */
    input, textarea, select {
        font-size: 16px !important;
    }

    /* Aeusseres Seiten-Padding (siehe .content weiter oben, 24px/28px) ist fuer Desktop/Tablet
       gedacht - auf Telefon-Breiten frisst das unnoetig Platz von den Karten weg (gemeldeter
       Bug: Kacheln wirken gestaucht). Reduziert auf ein Minimum, gilt fuer jede Seite gleich. */
    .content {
        padding: 12px;
    }

    /* .sidebar/.nav-item-Schrumpf-Regeln entfernt: die Sidebar wird jetzt komplett
       ausgeblendet unter 1024px (siehe Breakpoint-Vertrag oben + neue Mobile-Chrome-Sektion
       mit .mobile-bottom-nav/.mobile-fab), statt nur schmaler gemacht zu werden. */
    .detail-grid {
        grid-template-columns: 1fr !important;
        gap: 4px;
    }

    .detail-row {
        display: flex;
        flex-direction: column;
        margin-bottom: 8px;
    }

    /* Kompaktes Support-Eintrag-Formular: Zeilen brechen auf Telefonbreite um, statt
       die Zeit-Boxen unleserlich klein zu quetschen. */
    .entry-row, .support-loc-row {
        flex-direction: column;
        align-items: flex-start;
    }

    .entry-row-controls {
        flex-wrap: wrap;
    }

    .ticket-toolbar {
        flex-direction: column;
        align-items: stretch;
        /* Als senkrecht gestapelte Buttonliste wuerde eine dauerhaft fixierte Leiste zu viel vom
           kleinen Viewport dauerhaft blockieren. */
        position: static;
    }

    .ticket-running-status {
        margin-left: 0;
    }
}

@media (max-width: 768px) {
    /* Tabellen in Karten bekommen horizontales Scrollen statt das ganze Layout zu
       sprengen - die Tabellenstruktur selbst bleibt unangetastet (kein display:block),
       nur die umschliessende Karte scrollt. */
    .card:has(> table.data-table) {
        overflow-x: auto;
    }
}

/* Mobile Ticket-Karten (ersetzt die Tabelle auf Phone-Breite, siehe Breakpoint-Vertrag
   oben) - per Default ausgeblendet, teilt sich Suche/Filter/Sortierung mit der
   Tabellen-Ansicht ueber identische data-*-Attribute + einen zweiten initListControls()-
   Aufruf (siehe tickets/index.twig). */
.ticket-mobile-list {
    display: none;
}

@media (max-width: 599px) {
    .card:has(> table.data-table) {
        display: none;
    }

    /* Kunden-Subnav "Tickets"-Tab (asset-list-shell-Design, siehe tickets/index.twig) weicht auf
       Phone-Breite ebenfalls der Mobile-Kartenliste - eigene Klasse statt .asset-list-shell direkt,
       damit die echte Asset-Liste (die stattdessen bei 900px auf gestapeltes Layout wechselt)
       unberuehrt bleibt. */
    .ticket-list-shell {
        display: none;
    }

    .ticket-mobile-list {
        display: flex;
        flex-direction: column;
        gap: 10px;
    }

    .ticket-mobile-card {
        display: block;
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-left: 3px solid var(--color-border);
        border-radius: var(--radius-md);
        padding: 12px;
        color: var(--color-text);
        text-decoration: none;
    }

    .ticket-mobile-card[data-priority="urgent"] { border-left-color: var(--color-critical); }
    .ticket-mobile-card[data-priority="high"] { border-left-color: var(--color-danger-text); }
    .ticket-mobile-card[data-priority="normal"] { border-left-color: var(--color-accent); }
    .ticket-mobile-card[data-priority="low"] { border-left-color: var(--status-neutral); }

    .ticket-mobile-card.is-overdue { border-left-color: var(--color-critical); }

    .tmc-head {
        display: flex;
        align-items: center;
        gap: 6px;
        margin-bottom: 6px;
    }

    .tmc-id {
        font-size: 11px;
        font-weight: 700;
        color: var(--color-text-muted);
    }

    .tmc-subject {
        font-size: 14px;
        font-weight: 700;
        color: var(--color-text);
        margin-bottom: 2px;
    }

    .tmc-customer {
        font-size: 12.5px;
        color: var(--color-text-muted);
        margin-bottom: 8px;
    }

    .tmc-meta {
        display: flex;
        align-items: center;
        flex-wrap: wrap;
        gap: 8px;
        margin-bottom: 8px;
    }

    .tmc-queue {
        font-size: 12px;
        color: var(--color-text-muted);
    }

    .tmc-foot {
        display: flex;
        align-items: center;
        gap: 8px;
        font-size: 12px;
        color: var(--color-text-muted);
        padding-top: 8px;
        border-top: 1px solid var(--color-border);
    }
}

.tmc-time {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 12px;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

/* Schnellfilter-Pills "Alle/Meine/Unzugewiesen" (Nutzer-Feedback 2026-08-06) - reine Kurzform fuer
   die bereits bestehende Techniker-Facette in der Sidebar (siehe applyQuickFilter() in
   tickets/index.twig): manipulieren dieselben Checkboxen, statt einen eigenen zweiten
   Filter-Zustand einzufuehren. */
.ticket-quick-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

.ticket-quick-filter-pill {
    padding: 4px 10px;
    border-radius: 999px;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--color-text-muted);
    cursor: pointer;
}

.ticket-quick-filter-pill:hover { background: var(--color-bg); }
.ticket-quick-filter-pill.is-active { background: var(--color-accent); border-color: var(--color-accent); color: #FFFFFF; }

/* "Gruppieren nach"-Auswahl in der Toolbar (Nutzer-Feedback 2026-08-06). */
.ticket-group-by-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.ticket-group-by-row select {
    font-size: 12.5px;
    padding: 5px 8px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
}

/* SharePoint-artige Gruppen-Kopfzeilen (Nutzer-Feedback 2026-08-06) - komplett clientseitig aus
   den bestehenden data-*-Attributen der bereits gerenderten Zeilen/Karten gebaut (siehe
   applyGrouping() in tickets/index.twig), keine Server-Rundreise. Desktop-Variante ist eine echte
   <tr><td colspan> (bricht den Sticky-Spalten-Mechanismus bewusst nicht mit, da eine einzelne
   volle Zelle keine Spalten-Ausrichtung braucht), Mobile-Variante ein simples <div> vor dem
   jeweiligen Karten-Block. */
tr.ticket-group-header-row td {
    position: static;
    background: var(--color-bg);
    padding: 9px 16px;
    border-bottom: 1px solid var(--color-border);
    cursor: pointer;
    user-select: none;
    white-space: normal;
}

.ticket-group-header-content {
    display: flex;
    align-items: center;
    gap: 8px;
}

.ticket-group-header-toggle {
    display: inline-flex;
    color: var(--color-text-muted);
    transition: transform .12s ease;
    flex: 0 0 auto;
}

tr.ticket-group-header-row.is-collapsed .ticket-group-header-toggle,
.ticket-mobile-group-header.is-collapsed .ticket-group-header-toggle {
    transform: rotate(-90deg);
}

.ticket-group-header-label {
    font-weight: 700;
    font-size: 13px;
    color: var(--color-text);
}

.ticket-group-header-count {
    font-size: 12px;
    color: var(--color-text-muted);
}

.ticket-group-header-time {
    margin-left: auto;
    font-size: 12px;
    font-weight: 700;
    color: var(--color-accent);
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

.ticket-mobile-group-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 4px 4px;
    cursor: pointer;
    user-select: none;
}

/* ============================================================================
   MOBILE CHROME (Bottom-Nav + FAB + Leistung-Sheet) - folgt dem Breakpoint-Vertrag
   oben. Ersetzt die Sidebar-Navigation unterhalb von 1024px komplett durch eine
   fixierte Bottom-Nav mit den 5 wichtigsten Zielen (Hub/Tickets/Leistung/Kunden/
   Optionen) + einen Floating Action Button fuer "Leistung schreiben". Weitere
   Sidebar-Module (Vault, Dispatch, Pulse, LeadHub, Freigaben, Inbox, Academy, HR)
   sind auf Phone/Tablet in dieser Iteration bewusst nicht ueber die Bottom-Nav
   erreichbar - siehe Umsetzungsplan.
   ============================================================================ */
.mobile-bottom-nav, .mobile-fab, .inside-fab { display: none; }

@media (max-width: 1023px) {
    .sidebar { display: none; }
    .main { padding-bottom: 64px; }

    .mobile-bottom-nav {
        display: flex;
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
        align-items: center; justify-content: space-between;
        background: var(--color-surface);
        border-top: 1px solid var(--color-border);
        padding: 8px 8px calc(8px + env(safe-area-inset-bottom));
    }

    .mobile-nav-item {
        flex: 1; min-height: 44px;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
        background: none; border: none; font-family: var(--font-sans);
        color: var(--color-text-muted);
    }

    .mobile-nav-item span { font-size: 10px; font-weight: 700; }
    .mobile-nav-item .icon, .mobile-nav-item svg { width: 20px; height: 20px; }
    .mobile-nav-item.is-active { color: var(--color-accent); }

    .mobile-fab {
        display: flex;
        position: fixed; right: 16px; bottom: 76px; z-index: 51;
        width: 56px; height: 56px; align-items: center; justify-content: center;
        border-radius: 50%; border: none;
        background: var(--color-accent); color: #fff;
        box-shadow: var(--shadow-card);
    }

    .mobile-fab .icon, .mobile-fab svg { width: 24px; height: 24px; }

    /* CoreDesk-Inside-eigener Quick-Add-FAB - gleiche Position/Optik wie .mobile-fab, aber
       eigene Klasse, damit ihn die weiter unten stehende body.page-customer-inside .mobile-fab
       {display:none}-Regel nicht selbst mit ausblendet. */
    .inside-fab {
        display: flex;
        position: fixed; right: 16px; bottom: 76px; z-index: 51;
        width: 56px; height: 56px; align-items: center; justify-content: center;
        border-radius: 50%; border: none;
        background: var(--color-accent); color: #fff;
        box-shadow: var(--shadow-card);
    }

    .inside-fab .icon, .inside-fab svg { width: 24px; height: 24px; }

    /* Auf der Ticket-Detailseite kollidiert die globale Leistung-FAB fast exakt mit dem
       Foto-Dokumentation-FAB (siehe photo-capture.css) - Leistung erfassen ist dort ueber die
       "Neuer Eintrag"-Vitalsleiste ohnehin bereits erreichbar, die globale Kurzwahl waere hier
       nur redundant. */
    body.page-ticket-detail-v2 .mobile-fab {
        display: none;
    }

    /* CoreDesk Inside (Kunden-Dashboard): eigener Quick-Add-FAB (Ticket/Kontakt/Asset, siehe
       #inside-quickadd-fab) ersetzt die globale Leistung-Kurzwahl auf dieser Seite. */
    body.page-customer-inside .mobile-fab {
        display: none;
    }
}

/* ============================================================================
   CoreDesk Hub mobil (siehe home.twig) - Ticket-Summary-Chips, Zeitstrahl
   (Tages-Pillen + vertikale Agenda-Karten statt horizontalem Desktop-Gantt) und
   kompaktes Attention-Banner. Alle drei Bloecke rendern serverseitig immer, sind
   aber per CSS nur unterhalb von 1024px sichtbar (Breakpoint-Vertrag oben) -
   die Zeitstrahl-Daten (weekDays) und das Attention-Banner (attentionTickets)
   sind identisch mit den Desktop-Widgets, nur anders angeordnet.
   ============================================================================ */
.mobile-ticket-chips, .mobile-timeline, .mobile-attention-banner, .mobile-tickets-card { display: none; }

@media (max-width: 1023px) {
    .mobile-tickets-card {
        display: block;
        margin-bottom: 16px;
    }

    .mobile-tickets-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        font-size: 10px;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.03em;
        color: var(--color-text-muted);
        margin-bottom: 10px;
    }

    .mobile-tickets-all-link {
        display: inline-flex;
        align-items: center;
        gap: 2px;
        color: var(--color-accent);
        text-transform: none;
        font-weight: 700;
    }

    .mobile-tickets-all-link .icon, .mobile-tickets-all-link svg { width: 14px; height: 14px; }

    .mobile-ticket-chips {
        display: flex;
        gap: 10px;
        margin-bottom: 12px;
    }

    .mobile-ticket-chip {
        flex: 1;
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-md);
        padding: 10px 12px;
        display: flex;
        flex-direction: column;
    }

    .mobile-ticket-chip .mtc-num { font-size: 20px; font-weight: 800; }
    .mobile-ticket-chip .mtc-label { font-size: 11px; color: var(--color-text-muted); font-weight: 700; }

    .mobile-tickets-list {
        display: flex;
        flex-direction: column;
        gap: 8px;
    }

    .mobile-tickets-list-row {
        display: flex;
        align-items: center;
        gap: 10px;
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-left: 3px solid var(--color-critical);
        border-radius: var(--radius-md);
        padding: 10px 12px;
    }

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

    .mtl-badge {
        align-self: flex-start;
        font-size: 9.5px;
        font-weight: 800;
        text-transform: uppercase;
        letter-spacing: 0.02em;
        padding: 2px 6px;
        border-radius: 4px;
        margin-bottom: 2px;
    }

    .mtl-badge--overdue { background: var(--color-danger-bg); color: var(--color-danger-text); }
    .mtl-badge--due-today { background: var(--status-warning-soft); color: var(--status-warning); }

    .mtl-subject {
        font-size: 12.5px;
        font-weight: 700;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .mtl-customer {
        font-size: 11px;
        color: var(--color-text-muted);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .mobile-tickets-list-row .icon, .mobile-tickets-list-row svg { flex-shrink: 0; width: 16px; height: 16px; color: var(--color-text-muted); }

    /* Desktop-Gantt (.dashboard-columns/.htimeline) weicht dem mobilen Zeitstrahl. */
    .dashboard-columns { display: none; }

    /* overflow-x:hidden ueberschreibt bewusst das geerbte .card{overflow-x:auto} (siehe
       Basis-Regel oben) - die Karte selbst darf nie horizontal scrollen, nur die beiden
       Kind-Container .mobile-day-picker/.mobile-day-carousel duerfen das (Tageswechsel). */
    .mobile-timeline { display: block; margin-bottom: 24px; overflow-x: hidden; }

    .mobile-day-picker {
        display: flex;
        gap: 8px;
        overflow-x: auto;
        padding: 4px 2px 8px;
    }

    .mobile-day-pill {
        flex: 0 0 52px;
        background: var(--color-surface);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-md);
        padding: 8px 0;
        text-align: center;
        font-family: var(--font-sans);
    }

    .mobile-day-pill .mdp-dow { display: block; font-size: 10px; font-weight: 700; color: var(--color-text-muted); }
    .mobile-day-pill .mdp-num { display: block; font-size: 15px; font-weight: 800; margin: 2px 0; }
    .mobile-day-pill .mdp-dot { display: inline-block; width: 5px; height: 5px; border-radius: 50%; background: var(--color-border); }

    .mobile-day-pill.is-active {
        background: var(--color-accent);
        border-color: var(--color-accent);
    }

    .mobile-day-pill.is-active .mdp-dow, .mobile-day-pill.is-active .mdp-num { color: #fff; }
    .mobile-day-pill.is-active .mdp-dot { background: #fff; }
    .mobile-day-pill.is-weekend .mdp-num { color: var(--color-text-muted); }

    .mobile-day-carousel {
        display: flex;
        gap: 12px;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        padding: 2px;
    }

    .mobile-day-agenda-card {
        flex: 0 0 100%;
        scroll-snap-align: start;
        background: var(--color-bg);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-md);
        padding: 12px;
        overflow: hidden;
    }

    .mobile-agenda-head {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 10px;
        font-size: 12px;
        font-weight: 700;
    }

    .mobile-agenda-head .mdac-count {
        font-size: 10.5px;
        font-weight: 700;
        color: var(--color-text-muted);
        background: var(--color-surface);
        border-radius: 999px;
        padding: 2px 8px;
    }

    .mobile-agenda-empty {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 6px;
        padding: 24px 0;
        color: var(--color-text-muted);
        font-size: 12px;
    }

    /* Echtes 07-19-Uhr Stundenraster statt einer flachen Liste (siehe DashboardController::
       buildMobileWeekDays()/mobileAgendaBar() fuer die topPx/heightPx-Berechnung) - feste Hoehe
       mit ausschliesslich vertikalem Scroll, damit die Karte selbst (anders als die Tages-Pillen-
       Leiste/das Karten-Karussell) niemals horizontal scrollt. */
    .mobile-agenda-body {
        position: relative;
        height: 300px;
        overflow-y: auto;
        overflow-x: hidden;
        padding-top: 4px;
    }

    .mobile-agenda-hours { position: relative; }

    .mobile-agenda-hour-row {
        height: 34px;
        display: flex;
        align-items: flex-start;
        border-top: 1px solid var(--color-border);
    }

    .mobile-agenda-hour-row:first-child { border-top: none; }

    .mobile-agenda-hour-row .mah-label {
        width: 34px;
        flex: 0 0 auto;
        font-size: 10px;
        font-weight: 700;
        color: var(--color-text-muted);
        transform: translateY(-6px);
    }

    .mobile-agenda-events {
        position: absolute;
        left: 38px;
        right: 2px;
        top: 0;
        bottom: 0;
    }

    /* left/width werden inline pro Termin gesetzt (siehe home.twig) - abhaengig von
       lane/laneCount aus DashboardController::assignMobileAgendaLanes(), damit zeitlich
       ueberlappende Termine nebeneinander statt uebereinander liegend gerendert werden. */
    .mobile-agenda-event {
        position: absolute;
        display: flex;
        flex-direction: column;
        justify-content: center;
        gap: 1px;
        padding: 3px 8px;
        border-radius: var(--radius-sm);
        border-left: 3px solid transparent;
        background: var(--color-surface);
        color: var(--color-text);
        overflow: hidden;
    }

    .mobile-agenda-event .mae-time {
        font-size: 9.5px;
        font-weight: 800;
        text-transform: uppercase;
        opacity: 0.75;
        white-space: nowrap;
    }

    .mobile-agenda-event .mae-title {
        font-size: 11.5px;
        font-weight: 700;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* Wiederverwendung der Desktop-Gantt-Typfarben (.htimeline-appt--{type}) als linker
       Akzent-Rahmen statt vollflaechigem Hintergrund - besser lesbar in der schmalen Karte. */
    .mobile-agenda-event.htimeline-appt--customer_appointment { border-left-color: var(--entrytype-appointment); }
    .mobile-agenda-event.htimeline-appt--tentative { border-left-color: var(--status-neutral); }
    .mobile-agenda-event.htimeline-appt--booked_service { border-left-color: var(--entrytype-service); }
    .mobile-agenda-event.htimeline-appt--callback { border-left-color: var(--entrytype-callback); }
    .mobile-agenda-event.htimeline-appt--email_reply { border-left-color: var(--entrytype-email); }
    /* CoreDesk Quick-Punch (siehe Migration 153) - gleiche Indigo-Akzentfarbe wie Desktop/Dispatch/
       Pulse, hier als Akzent-Rahmen statt Vollflaeche (Konvention dieser Karte, siehe oben). */
    .mobile-agenda-event.htimeline-appt--quick_punch { border-left-color: var(--entrytype-push); }

    .mobile-legend-row { padding: 8px 2px 0; }

    /* Kompakte Attention-Zusammenfassung statt der vollen Desktop-Liste - Team-Praesenz bleibt
       sichtbar, bekommt aber die volle Zeilenbreite, da die Attention-Karte hier entfaellt. */
    .mobile-attention-banner {
        display: flex;
        align-items: center;
        gap: 10px;
        background: var(--color-danger-bg);
        border: 1px solid var(--color-danger-border);
        border-radius: var(--radius-md);
        padding: 11px 12px;
        margin-bottom: 12px;
        color: var(--color-danger-text);
    }

    .mobile-attention-banner .mab-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
    .mobile-attention-banner .mab-title { font-size: 11.5px; font-weight: 800; }
    .mobile-attention-banner .mab-sub {
        font-size: 10.5px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .attention-card { display: none; }
    .hub-attention-row { grid-template-columns: 1fr; }

    /* Die lange Sektions-Ueberschrift (Titel+Erklaertext) ueber der Attention-/Team-Praesenz-Zeile
       weicht auf Mobil der kompakten Karten-Kopfzeile (.team-presence-header-mobile unten) - auf
       Desktop bleibt sie unveraendert bestehen. */
    .hub-section-header--presence { display: none; }

    .team-presence-header-desktop { display: none; }
    .team-presence-header-mobile, .team-presence-all-link { display: inline-flex; }

    .team-presence-avatars {
        flex-wrap: nowrap;
        overflow-x: auto;
        max-height: none;
    }
}

/* ============================================================================
   Mobiles Bottom-Sheet "Leistung schreiben" (siehe templates/partials/_leistung_sheet.twig,
   public/js/bottom-sheet.js, public/js/leistung-sheet.js) - global in layout.twig eingebunden,
   ueberall per FAB/Bottom-Nav erreichbar (siehe Mobile-Chrome-Sektion oben). Bewusst NICHT das
   bestehende .modal-overlay/.modal-box-Muster wiederverwendet (kein Focus-Trap/ARIA dort) -
   eigenstaendiges Sheet-Muster mit denselben Design-Tokens.
   ============================================================================ */
.mobile-sheet-backdrop {
    position: fixed; inset: 0; z-index: 90;
    background: rgba(10, 12, 16, 0.45);
    opacity: 0; pointer-events: none;
    transition: opacity 0.2s;
}

.mobile-sheet-backdrop.is-open { opacity: 1; pointer-events: auto; }

.mobile-sheet {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 91;
    background: var(--color-surface);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -12px 30px rgba(16, 24, 40, 0.18);
    transform: translateY(102%);
    transition: transform 0.3s cubic-bezier(.16, 1, .3, 1);
    max-height: 88vh;
    display: flex; flex-direction: column;
    max-width: 480px; margin: 0 auto;
}

.mobile-sheet.is-open { transform: translateY(0); }

.mobile-sheet-handle-wrap { display: flex; justify-content: center; padding: 10px 0 4px; flex: 0 0 auto; }
.mobile-sheet-handle { width: 38px; height: 5px; border-radius: 99px; background: var(--color-border); }

.mobile-sheet-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 2px 18px 12px; flex: 0 0 auto;
}

.mobile-sheet-head h3 { font-size: 16px; font-weight: 800; margin: 0; }

.mobile-sheet-close {
    width: 32px; height: 32px; min-width: 32px; min-height: 32px;
    border-radius: 50%; background: var(--color-bg); border: none;
    color: var(--color-text-muted);
    display: flex; align-items: center; justify-content: center;
}

.mobile-sheet-body { padding: 0 18px 22px; overflow-y: auto; }

.mobile-mode-tabs {
    display: flex; background: var(--color-bg); border-radius: var(--radius-md);
    padding: 3px; margin-bottom: 16px;
}

.mobile-mode-tab {
    flex: 1; min-height: 40px; text-align: center; border-radius: 8px; border: none; background: none;
    font-size: 12.5px; font-weight: 700; color: var(--color-text-muted);
    display: flex; align-items: center; justify-content: center; gap: 6px;
}

.mobile-mode-tab.is-active { background: var(--color-surface); color: var(--color-accent); box-shadow: var(--shadow-card); }

.mobile-field { margin-bottom: 14px; }
.mobile-field label {
    display: block; font-size: 11px; font-weight: 800; color: var(--color-text-muted);
    text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 6px;
}

.mobile-picker-field {
    width: 100%; min-height: 44px; text-align: left;
    background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
    padding: 11px 12px; font-size: 13.5px; font-weight: 600; color: var(--color-text);
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
}

.mobile-picker-field:disabled { opacity: 0.6; }
.mobile-picker-field .pf-val { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* CoreDesk-Inside Quick-Add-Chooser-Sheet (siehe #inside-quickadd-sheet): 3 grosse, daumenfreundliche
   Aktionszeilen statt kleiner Buttons. */
.quickadd-action-row {
    display: flex; align-items: center; gap: 12px; width: 100%;
    min-height: 56px; padding: 10px 12px; margin-bottom: 8px;
    background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
    font-size: 14px; font-weight: 600; color: var(--color-text); text-decoration: none;
    text-align: left;
}
.quickadd-action-row .icon, .quickadd-action-row svg { width: 22px; height: 22px; flex-shrink: 0; color: var(--color-accent); }
.quickadd-action-row-label { flex: 1; }
.quickadd-action-row-chevron { color: var(--color-text-muted); }

/* Seriennummer-Feld (Asset-Quick-Add) und Assets-Suchfeld teilen sich dasselbe Scan-Icon-neben-
   Input-Layout (siehe public/js/inside-quickadd.js scanBarcode()). */
.mobile-scan-input-wrap { display: flex; align-items: center; gap: 8px; }
.mobile-scan-input-wrap input { flex: 1; min-height: 44px; }
.mobile-scan-input-wrap .scan-trigger-btn {
    width: 44px; height: 44px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
    background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
    color: var(--color-accent);
}
.mobile-scan-input-wrap .scan-trigger-btn .icon, .mobile-scan-input-wrap .scan-trigger-btn svg { width: 20px; height: 20px; }

/* Vollbild-Kamera-Vorschau fuer den Barcode-/QR-Scan (BarcodeDetector-Web-API, siehe
   inside-quickadd.js::scanBarcode()) - erscheint ueber dem jeweils offenen Bottom-Sheet. */
.scan-overlay {
    display: none;
    position: fixed; inset: 0; z-index: 200;
    background: #000;
    align-items: center; justify-content: center;
}
.scan-overlay.is-open { display: flex; }
.scan-overlay-video { width: 100%; height: 100%; object-fit: cover; }
.scan-overlay-frame {
    position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 70vw; max-width: 320px; aspect-ratio: 1; border: 3px solid #fff; border-radius: var(--radius-md);
    box-shadow: 0 0 0 2000px rgba(0, 0, 0, 0.45);
}
.scan-overlay-cancel {
    position: absolute; top: calc(16px + env(safe-area-inset-top)); right: 16px; z-index: 201;
    width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.15); border: none; border-radius: 50%; color: #fff;
}

.mobile-duration-chips { display: flex; flex-wrap: wrap; gap: 8px; }

.mobile-chip {
    min-height: 44px;
    border: 1px solid var(--color-border); background: var(--color-surface); border-radius: var(--radius-sm);
    padding: 9px 4px; font-size: 12.5px; font-weight: 700; color: var(--color-text-muted);
    flex: 1 1 auto; min-width: 52px; text-align: center;
}

.mobile-chip.is-active { background: var(--color-accent); border-color: var(--color-accent); color: #fff; }

.mobile-chip-custom {
    flex: 1 1 100%; display: flex; align-items: center; gap: 8px; background: var(--color-bg);
}

.mobile-chip-custom input {
    flex: 1; border: none; background: transparent; outline: none;
    font-size: 13px; font-weight: 700; color: var(--color-text); width: 100%;
}

.mobile-toggle-row {
    display: flex; align-items: center; justify-content: space-between;
    background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
    padding: 11px 13px;
}

.mobile-toggle-row .mtr-label { font-size: 13px; font-weight: 700; }
.mobile-toggle-row .mtr-sub { font-size: 11px; color: var(--color-text-muted); }

.mobile-switch {
    position: relative; width: 44px; height: 26px; min-width: 44px; border-radius: 999px;
    background: var(--color-border); border: none; flex: 0 0 auto;
}

.mobile-switch::after {
    content: ""; position: absolute; top: 2px; left: 2px; width: 22px; height: 22px;
    border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
    transition: transform 0.15s;
}

.mobile-switch.is-on { background: var(--color-accent); }
.mobile-switch.is-on::after { transform: translateX(18px); }

.mobile-textarea {
    width: 100%; background: var(--color-bg); border: 1px solid var(--color-border);
    border-radius: var(--radius-md); padding: 11px 12px; font-size: 13.5px; color: var(--color-text);
    resize: none; outline: none; font-family: var(--font-sans);
}

.mobile-textarea:focus { border-color: var(--color-accent); }

.mobile-sheet-actions { display: flex; gap: 10px; margin-top: 6px; }

.mobile-timer-wrap { text-align: center; padding: 10px 0 20px; }
.mobile-timer-display {
    font-size: 40px; font-weight: 800; letter-spacing: 0.02em;
    font-variant-numeric: tabular-nums; margin-bottom: 4px;
}

.mobile-timer-caption { font-size: 12px; color: var(--color-text-muted); margin-bottom: 22px; }

.mobile-timer-btn {
    width: 72px; height: 72px; min-width: 72px; border-radius: 50%; border: none;
    display: flex; align-items: center; justify-content: center; margin: 0 auto;
}

.mobile-timer-btn.is-idle { background: var(--color-accent); color: #fff; }
.mobile-timer-btn.is-idle:disabled { opacity: 0.5; }
.mobile-timer-btn.is-running { background: var(--color-danger-text); color: #fff; }
.mobile-timer-btn .icon, .mobile-timer-btn svg { width: 26px; height: 26px; }
.mobile-timer-btn .mtb-icon-stop { display: none; }
.mobile-timer-btn.is-running .mtb-icon-play { display: none; }
.mobile-timer-btn.is-running .mtb-icon-stop { display: flex; }
.mobile-timer-hint { font-size: 11.5px; color: var(--color-text-muted); margin-top: 18px; line-height: 1.5; }

.mobile-picker-search {
    display: flex; align-items: center; gap: 8px; min-height: 44px;
    background: var(--color-bg); border: 1px solid var(--color-border); border-radius: var(--radius-md);
    padding: 10px 12px; margin-bottom: 10px;
}

.mobile-picker-search input { flex: 1; border: none; background: transparent; outline: none; font-size: 13.5px; color: var(--color-text); }

.mobile-picker-list { display: flex; flex-direction: column; gap: 4px; }

.mobile-picker-row {
    min-height: 44px; display: flex; align-items: center; justify-content: space-between;
    padding: 11px 10px; border-radius: var(--radius-sm); background: none; border: none;
    text-align: left; width: 100%; color: var(--color-text); font-family: var(--font-sans);
}

.mobile-picker-row:active { background: var(--color-bg); }
.mobile-picker-row .mpr-title { font-size: 13px; font-weight: 700; }
.mobile-picker-row .mpr-sub { font-size: 11px; color: var(--color-text-muted); margin-top: 1px; }
.mobile-picker-empty { padding: 24px 10px; text-align: center; color: var(--color-text-muted); font-size: 12.5px; }

/* ============================================================================
   Projekte: Status-Leiste, Pauschal-Budget-Fortschrittsbalken, Phasen-Kanban-Board.
   ============================================================================ */
.project-status-bar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    margin-bottom: 16px;
}

.project-status-card {
    margin-bottom: 0;
}

.project-status-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin-bottom: 8px;
}

.project-status-value {
    font-size: 16px;
    font-weight: 600;
    margin-bottom: 8px;
}

.progress-bar {
    height: 8px;
    border-radius: 999px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    overflow: hidden;
}

.progress-bar-fill {
    height: 100%;
    background: var(--status-active);
    border-radius: 999px;
    transition: width .2s ease;
}

.progress-bar-fill.is-warning { background: var(--status-warning); }
.progress-bar-fill.is-over { background: var(--color-danger-text); }

/* Projekt-Cockpit (siehe templates/projects/show.twig): ersetzt die vormalige einzelne graue
   Abrechnungs-Warnbox + die 10 flachen Buttons in einer Reihe durch ein Executive-Dashboard-Banner
   (Status-Badges, Budget/Frist/Ticket-Metriken, Billing-Widget) und eine in 3 Zonen gruppierte
   Action-Bar. Nutzt bewusst dieselben .btn-group-dropdown/.dropdown-menu-Klassen wie das bestehende
   "Neues Ticket"-Split-Button-Dropdown (rein CSS-:focus-within, kein zusaetzliches JS). */
.pcockpit {
    background: linear-gradient(160deg, var(--color-surface) 0%, var(--color-accent-tint) 160%);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 16px 20px;
    margin-bottom: 16px;
    box-shadow: var(--shadow-card);
}

/* Titel-/Subtitel-/Workspace-CTA-Zeile im Cockpit-Header (siehe templates/projects/show.twig,
   Uebernahme des "pm1.html"-Agentur-Mockups fuer Layout/Dichte - eigene Icon-Sprache, keine
   FontAwesome-Icons aus dem Mockup uebernommen). */
.pcockpit-header-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    padding-bottom: 14px;
    margin-bottom: 14px;
    border-bottom: 1px solid var(--color-border);
}

.pcockpit-title-group h1 {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 19px;
    font-weight: 700;
    color: var(--color-text);
    margin: 0;
    line-height: 1.3;
}

.pcockpit-subtitle {
    font-size: 12.5px;
    color: var(--color-text-muted);
    margin: 4px 0 0;
}

.pcockpit-subtitle strong {
    color: var(--color-text);
    font-weight: 600;
}

.pcockpit-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.pcockpit-badges {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.pcockpit-milestone-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 999px;
    background: var(--color-accent-tint);
    color: var(--color-accent);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.pcockpit-billing-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: 999px;
    font-size: 12.5px;
    font-weight: 600;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    color: var(--color-text-muted);
    cursor: help;
    white-space: nowrap;
}

.pcockpit-bill-btn {
    box-shadow: 0 2px 10px rgba(5, 89, 201, 0.28);
}

/* KPI-Karten im Cockpit-Header: 3 randlose Icon-Chip-Karten (Budget/Deadline/Ticket-Fortschritt)
   statt der vormals unbebilderten .pcockpit-metric-Textzeilen - Icon-Chip-Farbe (is-success/
   -warning/-danger/-accent) signalisiert den Zustand auf einen Blick, analog zu den bestehenden
   Badge-Statusfarben. */
.pcockpit-kpi-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
}

@media (max-width: 900px) {
    .pcockpit-kpi-row {
        grid-template-columns: 1fr;
    }
}

.pcockpit-kpi-card {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
}

.pcockpit-kpi-body {
    min-width: 0;
}

.pcockpit-kpi-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.pcockpit-kpi-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.pcockpit-kpi-team-manage {
    width: 18px;
    height: 18px;
    padding: 0;
}

.pcockpit-kpi-percent {
    font-size: 15px;
    font-weight: 700;
    color: var(--color-text);
    font-variant-numeric: tabular-nums;
}

.pcockpit-kpi-value {
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text);
    margin-top: 6px;
    font-variant-numeric: tabular-nums;
}

.pcockpit-kpi-value small {
    font-size: 11.5px;
    font-weight: 500;
    color: var(--color-text-muted);
}

.pcockpit-kpi-sub {
    font-size: 11px;
    margin: 4px 0 0;
    color: var(--color-text-muted);
}

.pcockpit-kpi-pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-top: 8px;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

.pcockpit-kpi-pill.is-success { background: var(--status-active-soft); color: var(--status-active); }
.pcockpit-kpi-pill.is-warning { background: var(--status-warning-soft); color: var(--status-warning); }
.pcockpit-kpi-pill.is-danger { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }

.pcockpit-kpi-progress-wrap { margin-top: 8px; }
.pcockpit-kpi-progress-wrap .progress-bar { margin: 0; }

.pcockpit-kpi-footnote {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 8px;
    font-size: 11.5px;
    color: var(--color-text-muted);
}

.pcockpit-kpi-footnote .pcockpit-kpi-pill { margin-top: 0; }

.pcockpit-kpi-team-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 10px;
}

.pcockpit-kpi-avatar-stack {
    display: flex;
    align-items: center;
}

.pcockpit-kpi-avatar-stack .tech-avatar {
    margin-left: -8px;
    box-shadow: 0 0 0 2px var(--color-bg);
}

.pcockpit-kpi-avatar-stack .tech-avatar:first-child { margin-left: 0; }

.pcockpit-kpi-icon {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
}

.pcockpit-kpi-icon.is-success { background: var(--status-active-soft); color: var(--status-active); }
.pcockpit-kpi-icon.is-warning { background: var(--status-warning-soft); color: var(--status-warning); }
.pcockpit-kpi-icon.is-danger { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }
.pcockpit-kpi-icon.is-accent { background: var(--color-accent-tint); color: var(--color-accent); }

.pcockpit-top-actions {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* Cockpit-Karten-Grid (siehe projects/show.twig): Team, Meilensteine, PM-Kurznotizen, Gastzugang
   als 3-spaltiges Grid mit nativem CSS-Auto-Flow (die 4. Karte "Gastzugang" wandert automatisch
   unter die 1. "Team", da nur 3 Spalten zur Verfuegung stehen) statt der vormaligen festen
   2-Spalten-Aufteilung Hauptinhalt/Sidebar. */
.pcockpit-cards-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
    margin-bottom: 16px;
    align-items: start;
}

@media (max-width: 900px) {
    .pcockpit-cards-grid {
        grid-template-columns: 1fr;
    }
}

/* Schlanke Ein-Zeiler-Alternative zu ".empty-state" + separatem Kopfzeilen-Button, wenn eine Liste
   (z.B. Meilensteine) leer ist - Text und "+"-CTA teilen sich eine Zeile statt zwei getrennte
   Elemente vertikal zu stapeln. */
.pcockpit-empty-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 10px 12px;
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text-muted);
    font-size: 12px;
    cursor: pointer;
    text-align: left;
}

.pcockpit-empty-row:hover {
    border-color: var(--color-accent);
    color: var(--color-text);
}

.pcockpit-empty-row-cta {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
    font-weight: 600;
    color: var(--color-accent);
}

/* Gruppierte Action-Bar in 3 Zonen (Primaer / Ansichten & Workspace / Einstellungen) statt der
   vormaligen endlosen horizontalen Button-Kette. */
.pcockpit-actionbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}

.pcockpit-actions-group {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.pcockpit-kebab-toggle {
    font-size: 16px;
    line-height: 1;
    padding-left: 12px;
    padding-right: 12px;
}

.dropdown-menu a.dropdown-menu-item-danger {
    color: var(--color-danger-text);
}

@media (max-width: 767px) {
    .pcockpit-actionbar {
        justify-content: flex-start;
    }

    .pcockpit-actions-group {
        width: 100%;
    }
}

/* Horizontales Scrollen ist bewusst nativ (kein JS-Slider) - auf grossen Monitoren zeigt der
   Browser einfach mehr 300px-Spalten nebeneinander ohne zu scrollen, auf schmaleren Bildschirmen
   (siehe Media Query unten) wird daraus dank scroll-snap ein wischbares Karten-Deck. */
.kanban-board {
    display: flex;
    gap: 14px;
    overflow-x: auto;
    padding-bottom: 8px;
    align-items: flex-start;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
}

.kanban-column {
    flex: 0 0 300px;
    scroll-snap-align: start;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    max-height: 75vh;
}

.kanban-column-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 12px 10px 8px 14px;
}

.kanban-column-title-row {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.kanban-column-title {
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Rueckwaertskompatibel fuer alte Selektoren/Markup-Reste, die den frueheren einzeiligen
   Spalten-Kopf (Titel+Anzahl in einem Flex-Container) erwarteten. */
.kanban-column-header-top {
    display: flex;
    align-items: center;
    gap: 8px;
}

.kanban-count {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    padding: 1px 7px;
    color: var(--color-text-muted);
}

.kanban-column-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 2px;
    flex-shrink: 0;
}

.kanban-column-actions .icon-btn {
    width: 24px;
    height: 24px;
    color: var(--color-text-muted);
}

.kanban-column-actions .icon-btn:hover {
    color: var(--color-text);
}

/* Phasen-Header-Metriken (Anforderung "Erweiterung der Phasen-Struktur") - kleine Icon+Wert-Paare
   unter dem Phasennamen, dezenter als der Titel, kein Pill-Hintergrund fuer den Datumsbereich. */
.phase-metrics {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    font-size: 11px;
    font-weight: 400;
    color: var(--color-text-muted);
    padding: 0 14px 10px;
    border-bottom: 1px solid var(--color-border);
}

.phase-metric {
    white-space: nowrap;
}

.phase-date-range {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    white-space: nowrap;
}

.phase-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* Leistungsnachweis-Vorschau-Partial (templates/projects/_phase_billing_preview.twig), injiziert
   per fetch()+innerHTML in #phase-billing-preview-modal-overlay - siehe ProjectController::
   phaseBillingPreview(). Nutzt ausschliesslich bestehende .card/.badge/.data-table-Optik. */
.phase-billing-preview-head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.phase-billing-preview-head h3 {
    font-size: 15px;
    margin: 0;
}

.phase-billing-preview-flat {
    font-size: 13px;
}

.kanban-column-body {
    padding: 10px;
    overflow-y: auto;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Dezente Scrollbar, die erst beim Hovern der Spalte erscheint (Nutzer-Feedback 2026-08-02:
   "starr einzeln scrollende" Spalten wirkten unruhig) - Firefox ueber scrollbar-color,
   Chrome/Safari/Edge ueber die -webkit-scrollbar-Pseudoelemente. Der Scroll-Mechanismus selbst
   (eigenes overflow-y je Spalte) bleibt bewusst bestehen, nur die visuelle Praesenz wird reduziert. */
.kanban-column-body {
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
}

.kanban-column-body:hover {
    scrollbar-color: var(--color-border) transparent;
}

.kanban-column-body::-webkit-scrollbar {
    width: 6px;
}

.kanban-column-body::-webkit-scrollbar-track {
    background: transparent;
}

.kanban-column-body::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 999px;
}

.kanban-column-body:hover::-webkit-scrollbar-thumb {
    background: var(--color-border);
}

/* Monday.com-Stil: weisse, staerker gerundete Karte mit dezentem Hover-Lift statt der vormals
   flachen .radius-sm-Karte - Layout/Dichte vom Referenz-Mockup, Farben ausschliesslich ueber
   bestehende --color-* und --shadow-card-Tokens. */
/* Padding erhoeht + staendiger dezenter Zusatz-Schatten (Nutzer-Feedback 2026-08-02: Karten hoben
   sich zu schwach vom Spalten-Hintergrund ab) - der Zusatz-Schatten (fixe rgba, nicht themebar)
   ergaenzt --shadow-card, das im unifi-Theme bewusst "none" ist (siehe app.css ~Zeile 160), damit
   die Karten dort trotzdem sichtbar "abheben" statt sich nur ueber die Border abzugrenzen. Beim
   Hover verstaerkt sich derselbe Zusatz-Schatten weiter (bestehendes Verhalten). */
.kanban-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 14px 16px;
    box-shadow: var(--shadow-card);
    transition: box-shadow .15s ease, transform .15s ease, border-color .15s ease;
}

/* Nutzer-Feedback 2026-08-02: die vorherige Drei-Schichten-Stapelung (--shadow-card + eigene rgba
   sowohl im Ruhe- als auch im Hover-Zustand) wirkte kantig/kastig statt weich. Der Hover-Zustand
   ERSETZT --shadow-card jetzt durch EINEN sauberen Schatten mit negativem Spread (-6px) - dieser
   "eingezogene" Schattenrand ist der Standard-Trick fuer einen weichen, natuerlich wirkenden
   Lift-Effekt statt eines harten rechteckigen Rahmens; der Ruhezustand bleibt unveraendert bei
   --shadow-card (im unifi-Theme bewusst "none", siehe dortiger Kommentar). */
.kanban-card[draggable="true"]:hover {
    border-color: var(--color-accent);
    box-shadow: 0 10px 24px -6px rgba(15, 23, 42, .16);
    transform: translateY(-2px);
}

.kanban-card.is-dragging {
    opacity: .4;
}

.kanban-card-top {
    display: flex;
    align-items: flex-start;
    gap: 7px;
    margin-bottom: 6px;
}

/* Status-Icon-Praefix vor dem Titel (Monday.com-Referenz) - Ampelfarbe ueber dieselben
   --status-*-Tokens wie die Badges darunter, damit Icon- und Badge-Farbe je Status uebereinstimmen. */
.kanban-card-status-icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    margin-top: 1px;
}

.kanban-card-status-icon.is-success { color: var(--status-active); }
.kanban-card-status-icon.is-warning { color: var(--status-warning); }
.kanban-card-status-icon.is-hold { color: var(--status-hold); }
.kanban-card-status-icon.is-callback { color: var(--status-callback); }
.kanban-card-status-icon.is-accent { color: var(--color-accent); }
.kanban-card-status-icon.is-neutral { color: var(--status-neutral); }

/* Nutzer-Feedback 2026-08-02: Titel sollte klarer/praegnanter wirken als die Badges darunter -
   groesser + kraeftiger statt 13px/600. */
.kanban-card-title {
    display: block;
    font-size: 13.5px;
    font-weight: 700;
    line-height: 1.35;
    color: var(--color-text);
    min-width: 0;
}

.kanban-card-meta {
    display: flex;
    gap: 6px;
    flex-wrap: wrap;
    margin-bottom: 6px;
}

/* Badges innerhalb der Kanban-Karte optisch zurueckgenommen (Nutzer-Feedback 2026-08-02: Badges
   lenkten zu stark vom Titel ab) - nur hier ueberschrieben, die globale .badge-Klasse (Ticket-
   Detailseite, Ticket-Hub-Tabelle etc.) bleibt unveraendert. */
.kanban-card-meta .badge {
    font-size: 10.5px;
    font-weight: 500;
    padding: 2px 8px;
}

/* Aufgelaufener Aufwand einer Ticket-Karte ("⏱ 2,5 Std. • 212,50 €") - bewusst eine gedaempfte
   Textzeile statt eines Badges, passt besser zum Mockup als eine weitere Pill neben Status/Prio. */
.kanban-card-effort {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-bottom: 6px;
}

/* Unterste Zeile der Karte: Techniker-Avatar links, Faelligkeits-Hinweis rechts (ersetzt die
   frueheren separaten Zuweisungs-/Faelligkeits-Zeilen sowie den Loeschen-Button - siehe
   _kanban_card.twig). space-between schiebt den Faelligkeits-Hinweis auch dann nach rechts, wenn
   kein Techniker zugewiesen ist (dann bleibt der linke Platzhalter leer). */
.kanban-card-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--color-border);
}

/* "+ Ticket hinzufuegen" am Spaltenende (Monday.com-Referenz) - schlanker Text-Link statt Button,
   siehe workspace.twig. */
.kanban-add-task {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 2px 10px 10px;
    padding: 7px 4px;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    border-radius: var(--radius-sm);
}

.kanban-add-task:hover {
    color: var(--color-accent);
    background: var(--color-accent-tint);
}

/* Schwebender Ziehschatten fuer Mobile-Long-Press-Drag (siehe public/js/kanban-board.js) - analog
   .ptbl-touch-ghost im Tabellen-View. */
.kanban-touch-ghost {
    position: fixed;
    z-index: 2000;
    top: 0;
    left: 0;
    pointer-events: none;
    opacity: .92;
    box-shadow: var(--shadow-card), 0 8px 24px rgba(0, 0, 0, .18);
    transform: rotate(-1deg);
}

.due-status {
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.due-status--soon {
    color: var(--status-warning);
}

.ticket-checklist {
    max-height: 320px;
    overflow-y: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    margin-bottom: 14px;
}

.ticket-checklist-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    font-size: 13px;
    font-weight: 400;
    border-bottom: 1px solid var(--color-border);
    cursor: pointer;
}

.ticket-checklist-item:last-child {
    border-bottom: none;
}

.ticket-checklist-item input {
    width: auto;
}

/* Phasen-Kanban (Monday.com-Stil, siehe projects/workspace.twig): natives Drag-and-Drop auf
   Desktop, ein eigener Long-Press-Touch-Handler auf Mobilgeraeten (beide siehe
   public/js/kanban-board.js) verschieben Tickets zwischen Phasen-Spalten. */
.kanban-card[draggable="true"] {
    cursor: grab;
}

.kanban-card[draggable="true"]:active {
    cursor: grabbing;
}

.kanban-column-body.is-drag-over {
    background: var(--color-accent-soft);
}

.kanban-card-category-tag {
    font-size: 10px;
}

.project-team-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 16px;
}

.project-team-label {
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    text-transform: uppercase;
    margin-bottom: 6px;
}

/* Projekt-Meilensteine (siehe projects/show.twig): kompakte Liste statt erzwungener horizontaler
   Zeitleiste, damit sie auch mit vielen und/oder terminlosen Meilensteinen robust bleibt. */
.milestone-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.milestone-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
}

.milestone-status-icon {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin-top: 1px;
    background: var(--status-neutral-soft);
    color: var(--status-neutral);
}

.milestone-status-icon .icon-sm { width: 13px; height: 13px; }

.milestone-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: currentColor;
}

.milestone-status-icon.is-on_track { background: var(--status-active-soft); color: var(--status-active); }
.milestone-status-icon.is-at_risk { background: var(--status-warning-soft); color: var(--status-warning); }
.milestone-status-icon.is-done { background: var(--color-accent-soft); color: var(--color-accent); }

.milestone-row-body {
    flex-grow: 1;
    min-width: 0;
}

.milestone-row-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.milestone-row-title {
    font-weight: 600;
    font-size: 13px;
}

.milestone-row-desc {
    font-size: 12px;
    color: var(--color-text-muted);
    margin: 4px 0 0;
}

.milestone-date-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 11px;
    color: var(--color-text-muted);
}

.milestone-date-chip.is-overdue {
    color: var(--color-danger-text);
    font-weight: 600;
}

.milestone-row-meta {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 6px;
    flex-wrap: wrap;
}

.milestone-progress-mini {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    color: var(--color-text-muted);
}

.milestone-progress-mini .progress-bar {
    width: 70px;
    height: 4px;
    margin: 0;
}

.milestone-phase-chip {
    font-size: 10.5px;
    font-weight: 600;
    padding: 2px 7px;
    border-radius: 999px;
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.milestone-row-actions {
    flex-shrink: 0;
}

/* PM-Kurznotizen (siehe projects/show.twig): schnelles, zeilenbasiertes Meeting-Log - bewusst
   kein Rich-Text, ein Eingabefeld + Enter speichert sofort per AJAX (kein Seiten-Reload). */
.pm-notes-form-row {
    display: flex;
    gap: 8px;
    margin-bottom: 12px;
}

.pm-notes-form-row input[type=text] {
    flex-grow: 1;
}

.pm-notes-form-row select {
    max-width: 220px;
    flex-shrink: 0;
}

.pm-notes-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-height: 320px;
    overflow-y: auto;
}

.pm-note-item {
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
}

.pm-note-text {
    margin: 0 0 4px;
    font-size: 12.5px;
    white-space: pre-wrap;
}

.pm-note-meta {
    font-size: 11px;
    color: var(--color-text-muted);
}

.pm-note-tag {
    font-size: 9.5px;
    font-weight: 700;
    color: var(--color-accent);
    background: var(--color-accent-soft);
    padding: 1px 6px;
    border-radius: 999px;
    margin-left: 6px;
}

/* Tablet/Mobil: Spalten bleiben nebeneinander (kein vertikaler Stapel mehr) und werden per
   scroll-snap zu einem wischbaren Karten-Deck - fast bildschirmbreit, mit einem kleinen "Ausblick"
   auf die naechste Spalte als Wisch-Hinweis. Drag-and-Drop bleibt aktiv (Long-Press-Touch-Handler,
   siehe public/js/kanban-board.js), daher hier keine Deaktivierung mehr. */
@media (max-width: 1024px) {
    .kanban-board {
        scroll-snap-type: x mandatory;
    }

    .kanban-column {
        flex: 0 0 82vw;
        max-width: 380px;
        max-height: none;
    }
}

@media (max-width: 480px) {
    .kanban-column {
        flex-basis: 88vw;
    }
}

/* ============================================================================
   Ueberfaellige Tickets (Faelligkeit erreicht/ueberschritten, noch nicht
   geschlossen) muessen ueberall, wo Tickets auftauchen, sofort ins Auge fallen -
   roter Rand/Tint statt nur eine Farbe in einer ohnehin schon farbigen Badge-Zeile.
   ============================================================================ */
.is-overdue {
    position: relative;
    border-color: var(--color-danger-border) !important;
    background: var(--color-danger-bg) !important;
}

tr.is-overdue {
    box-shadow: inset 3px 0 0 0 var(--color-danger-text);
}

/* Nutzer-Feedback 2026-08-07: doch wieder ein leichter roter Zeilen-Tint in der Ticket-Tabelle
   (index.twig + projects/_table_view.twig, beide ueber .asset-list-table) - --color-danger-bg ist
   bereits der hellste Rot-Ton im Palette-System (siehe :root oben), daher kein zusaetzlicher
   Abschwaechungs-Faktor noetig. Ergaenzt den bereits vorhandenen linken Akzentstreifen (siehe
   tr.is-overdue-Regel oben). */
.asset-list-table tr.is-overdue {
    background: var(--color-danger-bg) !important;
}

/* Zell-Hintergruende (z.B. .tk-sticky-col, siehe oben) malen im Tabellen-Boxmodell immer ueber den
   Zeilen-Hintergrund - ohne diese Regel blieben die fixierten Spalten einer ueberfaelligen
   Ticket-Zeile weiss/surface-farben, waehrend der Rest der Zeile bereits rot getönt ist. */
table.asset-list-table--ticket-sticky-cols tbody tr.is-overdue td.tk-sticky-col {
    background: var(--color-danger-bg);
}

.is-overdue.asset-card,
.is-overdue.kanban-card {
    border-left: 3px solid var(--color-danger-text);
}

/* ============================================================================
   Tickets mit neuer, noch nicht angesehener Kundenaktivitaet (eingehende Mail oder
   neu per Mail angelegtes Ticket) - hellgruener Zeilen-Tint + blinkender Chip mit
   Mail-Icon, damit man es auch im Augenwinkel in einer langen Liste sofort bemerkt.
   Verschwindet automatisch, sobald das Ticket geoeffnet wird.
   ============================================================================ */
tr.has-unread-activity,
a.has-unread-activity,
div.has-unread-activity.kanban-card {
    background: var(--status-active-soft) !important;
}

/* Gleicher Grund wie bei .is-overdue oben (Zell-Hintergrund von .tk-sticky-col malt sonst weiss
   ueber den Zeilen-Tint): ohne diese Regel blieb ausgerechnet die Betreff-Spalte einer Zeile mit
   neuer Kundenaktivitaet weiss, waehrend der Rest der Zeile schon gruen getoent war (Nutzer-Feedback
   2026-08-08). */
table.asset-list-table--ticket-sticky-cols tbody tr.has-unread-activity td.tk-sticky-col {
    background: var(--status-active-soft);
}

.unread-activity-chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--status-active);
    color: #fff;
    margin-right: 6px;
    vertical-align: middle;
    animation: unread-activity-blink 1.4s ease-in-out infinite;
}

.unread-activity-chip svg {
    width: 12px;
    height: 12px;
    stroke: #fff;
}

/* ============================================================================
   Neu erstellte Tickets (egal ob per E-Mail, manuell oder wiederkehrend angelegt) -
   flieder Zeilen-Tint + blinkender Chip mit Text "Neu", damit sie in jeder Liste sofort
   auffallen und nicht mit "neue Kundenaktivitaet" (gruen) verwechselt werden. Verschwindet
   automatisch, sobald das Ticket einmal geoeffnet wurde (siehe TicketRepository::markSeen()).
   ============================================================================ */
tr.is-new-ticket,
a.is-new-ticket,
div.is-new-ticket.kanban-card {
    background: var(--status-new-soft) !important;
}

/* Gleicher Grund wie bei .is-overdue/.has-unread-activity oben - ohne diese Regel blieb die
   Betreff-Spalte eines neuen Tickets weiss, waehrend der Rest der Zeile bereits flieder getoent
   war (Nutzer-Feedback 2026-08-08). Bewusst NACH der .has-unread-activity-Sticky-Regel platziert,
   damit bei einem gleichzeitig neuen UND ungelesenen Ticket dieselbe Prioritaet gilt wie beim
   Zeilen-Hintergrund selbst (is-new-ticket gewinnt). */
table.asset-list-table--ticket-sticky-cols tbody tr.is-new-ticket td.tk-sticky-col {
    background: var(--status-new-soft);
}

/* Dashboard-Card fuer brandneue, noch nie geoeffnete Tickets - akzentuierter linker Rahmen in
   derselben Farbe wie der "Neu"-Chip, damit die Card selbst (nicht nur die Zeilen) sofort als
   Handlungsaufforderung auffaellt. Bewusst ausserhalb der .dashboard-masonry platziert (siehe
   home.twig), damit sie unabhaengig von der Spaltenbalance immer oberhalb der Projekte-Card steht. */
.new-tickets-panel {
    border-left: 3px solid var(--status-new);
}

.new-ticket-chip {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--status-new);
    color: var(--status-new-text);
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    margin-right: 6px;
    vertical-align: middle;
    animation: unread-activity-blink 1.4s ease-in-out infinite;
}

@keyframes unread-activity-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

/* Geschlossene Untertickets innerhalb einer Projekthuelle (Tabellen- und Kanban-Ansicht) - anders
   als ueberall sonst im System werden geschlossene Tickets hier NICHT ausgeblendet (siehe
   TicketRepository::forProject()), sondern bleiben sichtbar und nur ausgegraut, damit sich am
   Projektende pruefen laesst, ob wirklich alle Teilaufgaben erledigt sind. Gleicher Grauton wie
   bei gesperrten Kunden/inaktiven Geraeten (.customer-card.is-inactive/.asset-card.is-inactive). */
tr.is-closed,
div.is-closed.kanban-card {
    opacity: 0.6;
}

.overdue-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--color-danger-text);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    animation: overdue-pulse 1.6s ease-in-out infinite;
}

/* Relative Faelligkeits-Angabe ("in 2 Std." / "seit 4 Tagen ueberfaellig") neben dem festen Datum
   in der Faelligkeits-Spalte (Nutzer-Feedback 2026-08-07, siehe GermanDate::dueRelative()) - bewusst
   ein eigener, dezenter Chip statt des bereits vorhandenen pulsierenden .overdue-badge (das bleibt
   unveraendert als "Ueberfaellig"-Marker bestehen), da hier variabler Text statt eines festen
   Labels dargestellt wird. */
.due-relative-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 600;
    white-space: nowrap;
    background: color-mix(in srgb, var(--color-text) 8%, transparent);
    color: var(--color-text-muted);
}

.due-relative-badge--overdue {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

/* Icon-only Tabellen-Header (bisher nur fuer die Zeit-Spalte, Nutzer-Feedback 2026-08-07) - Tooltip
   uebernimmt das title-Attribut am <th> selbst, kein zusaetzliches Markup noetig. */
.th-icon-only {
    display: inline-flex;
    vertical-align: -3px;
}

@keyframes overdue-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.6; }
}

/* ============================================================================
   CoreDesk Hub: 2-spaltiges CSS-Grid (kein Masonry mit column-count mehr - das
   liess "Ready to Pick" je nach Hoehe von "Meine Tickets" mal in Spalte 1, mal
   in Spalte 2 landen). "Ready to Pick" ist die Haupt-Queue des Technikers und
   muss deshalb fest oben rechts verankert bleiben, unabhaengig davon, wie lang
   "Meine Tickets" gerade ist. Linke Spalte (Meine Tickets, Projekte, kuenftige
   Widgets) ist ein normaler Flex-Stack ohne Hoehenzwang, damit kuerzere Karten
   nahtlos nachruecken statt Leerraum zu erzwingen; items-start verhindert, dass
   das Grid die kuerzere Spalte auf die Hoehe der laengeren streckt.
   ============================================================================ */
.hub-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    align-items: start;
    margin-bottom: 24px;
}

.hub-grid-col {
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-width: 0;
}

.ticket-dashboard-column {
    min-width: 0;
}

@media (max-width: 900px) {
    .hub-grid {
        grid-template-columns: 1fr;
    }
}

/* Alarm-Zone (siehe home.twig, #hub-alarm-zone-grid): wenn keine neuen Tickets vorliegen, zeigt
   die Zone nur die Attention-Karte in voller Breite statt eines leeren 50/50-Splits. Wird sowohl
   beim initialen Server-Render als auch beim 15s-Poll (refreshNewTicketsPanel() in home.twig)
   umgeschaltet. Doppelt-Klassen-Selektor (.hub-grid.hub-grid--single) noetig, da es weiter unten
   in dieser Datei (CoreDesk Ticket-Hub-Board) eine spaetere .hub-grid-Regel mit gleicher
   Spezifitaet gibt, die sonst per Quellreihenfolge gewinnen wuerde. */
.hub-grid.hub-grid--single {
    grid-template-columns: 1fr;
}

.hub-grid--single #new-tickets-panel {
    display: none;
}

/* "Ready to Pick"-Meta-Zeile (Abteilung + relatives Eingangsdatum) unter dem Ticket-Titel, siehe
   templates/_ready_to_pick.twig. */
.ready-to-pick-meta {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

/* Kurzes gruenes Aufleuchten des "Mir zuweisen"-Buttons als Bestaetigung, bevor die Zeile beim
   Panel-Refresh verschwindet (siehe home.twig-Script) - bewusst am Button selbst statt an der
   ganzen Zeile, da die Zeile kurz danach ohnehin per innerHTML-Tausch ersetzt wird. */
.ready-to-pick-assign.is-claimed {
    background: var(--status-active);
    border-color: var(--status-active);
    animation: claim-flash 0.4s ease-out;
}

@keyframes claim-flash {
    0% { transform: scale(1); }
    40% { transform: scale(1.08); }
    100% { transform: scale(1); }
}

/* ============================================================================
   "Focus View" (Arbeits-Modus): server-seitig nach Focus Score sortierte Ticketliste statt
   Datum/Prioritaet - der Score-Badge erklaert dem Techniker, warum ein Ticket oben steht, der
   Hinweis-Banner gibt die strikte Abarbeitungsreihenfolge vor.
   ============================================================================ */
.focus-score-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--color-accent);
    color: #fff;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.02em;
    margin-right: 6px;
    vertical-align: middle;
}

/* Info-Popover neben dem "Fokus View"-Button, erklaert die Punkte-Zusammensetzung des Focus
   Score (siehe TicketRepository::FOCUS_SCORE_FRAGMENT) - CSS-only per :hover/:focus-within statt
   JS, da der Inhalt statisch ist und nur beim Hover/Fokus eingeblendet werden muss. */
.legend-info-wrap {
    position: relative;
    display: inline-flex;
    margin-left: 4px;
}

.legend-info-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
}

.legend-info-btn:hover,
.legend-info-btn:focus-visible {
    color: var(--color-accent);
    border-color: var(--color-accent);
}

.legend-info-popover {
    display: none;
    position: absolute;
    top: 100%;
    /* Der Info-Button sitzt am rechten Rand der Kartenkopfzeile - rechtsbuendig statt linksbuendig
       oeffnen, damit das 300px breite Popover nach links (in die Karte hinein) statt nach rechts
       (aus der Karte heraus, dort vom overflow-x:auto der .card abgeschnitten) aufklappt. */
    right: 0;
    left: auto;
    margin-top: 8px;
    width: 300px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-card);
    padding: 12px 14px;
    font-size: 12px;
    line-height: 1.5;
    color: var(--color-text);
    z-index: 200;
}

.legend-info-popover ul {
    margin: 6px 0 0;
    padding-left: 18px;
}

.legend-info-popover li {
    margin-bottom: 4px;
}

.legend-info-wrap:hover .legend-info-popover,
.legend-info-wrap:focus-within .legend-info-popover {
    display: block;
}

.focus-view-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    margin-bottom: 12px;
    border-radius: 8px;
    background: var(--status-active-soft);
    color: var(--color-text);
    font-size: 13px;
    font-weight: 600;
}

.dept-group {
    margin-bottom: 18px;
}

.dept-group-header {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    padding: 8px 0;
    border-bottom: 1px solid var(--color-border);
    margin-bottom: 6px;
}

.dept-group-count {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 1px 7px;
    font-size: 11px;
}

/* Kompakte UniFi-Style Tabelle je Abteilung im Dashboard - schmalere Zellen als das normale
   .data-table, da nur Punkt-Icons + kurze Werte statt Badges/Text-Spalten. */
.dept-table th, .dept-table td {
    padding: 6px 10px;
}

.dept-table .col-dot {
    width: 1%;
    white-space: nowrap;
}

.dept-table .col-date {
    width: 1%;
    white-space: nowrap;
    color: var(--color-text-muted);
    font-size: 12px;
}

.dept-table tr.is-overdue .col-date {
    color: var(--color-danger-text);
    font-weight: 700;
}

/* Techniker-Avatar (Kuerzel statt Namensinitialen, siehe templates/partials/avatar.twig) - der
   Avatar selbst bleibt seit dem MA-Avatar-Redesign (Nutzer-Vorgabe, Admin > Design > MA-Avatar-Stil)
   IMMER neutral eingefaerbt; die Praesenzfarbe (TechnicianStatusResolver::currentStatus()) sitzt nur
   noch im Status-Indikator (Punkt/Ring/Badge je nach globaler Stilwahl, siehe .avatar-style-*
   weiter unten). Bewusst bestehende Statusfarben-Tokens wiederverwendet statt neuer Tons, damit
   sich die Bedeutung (gruen=frei, gelb=im Einsatz, rot=abwesend, grau=ausserhalb der Arbeitszeit)
   konsistent mit dem Rest der App liest - jetzt ueber --presence-color/--presence-soft an die
   Indikator-Regeln weitergereicht statt direkt als Hintergrund. */
.tech-avatar {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: -0.2px;
    background: var(--color-bg);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    cursor: default;
}

/* 3-stellige Kuerzel (z.B. "LHA") wirken im 24px-Kreis eng am Rand - dezent kleinerer Font +
   engerer Zeichenabstand nur fuer diesen Fall, 2-stellige Kuerzel behalten die vom Nutzer
   vorgegebenen 10px. */
.tech-avatar--tight {
    font-size: 9px;
    letter-spacing: -0.4px;
}

/* Einfaches (nicht-Rich-)Tooltip als echtes Kind-Element statt [data-tooltip]::after (siehe
   public/css/app.css weiter oben, Zeile ~477): die Avatar-Stilvarianten dot/glow/squircle
   belegen ::after bereits fuer den Praesenz-Punkt, ein Element kann aber nur ein ::after haben -
   dadurch blockierte die staerker spezifische Stil-Regel das generische Tooltip vollstaendig
   (Nutzer-Feedback: "beim Mouse-over kommt nicht der Tooltip"). Gleiche Optik wie das generische
   [data-tooltip]-Tooltip, aber ueber :hover/:focus-visible auf einem echten Kind-Span statt
   content:attr(). */
.tech-avatar-simple-tooltip {
    display: none;
    position: absolute;
    left: 100%;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 12px;
    background: var(--color-text);
    color: var(--color-bg);
    padding: 5px 10px;
    border-radius: var(--radius-sm);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: normal;
    white-space: nowrap;
    box-shadow: var(--shadow-card);
    z-index: 200;
    pointer-events: none;
}

.tech-avatar-simple-trigger:hover .tech-avatar-simple-tooltip,
.tech-avatar-simple-trigger:focus-visible .tech-avatar-simple-tooltip {
    display: block;
}

/* Nutzer-Fund 2026-08-13: in der horizontal scrollbaren Ticket-Tabelle (.ptbl-table-scroll,
   overflow-x:auto) schnitt dieses :hover-Tooltip ab bzw. verzerrte den sichtbaren Scrollbereich
   der Spalte "Zugewiesen an" - hier bewusst deaktiviert, avatar-tooltip.js zeigt an dieser Stelle
   stattdessen dieselbe body-level position:fixed-Tooltip-Infrastruktur wie der reichhaltige
   Avatar-Tooltip (siehe TRIGGER_PAIRS dort). Alle anderen .tech-avatar-simple-trigger-Stellen
   (z.B. Team-Avatar-Reihe im Projekt-Cockpit-Header) bleiben unveraendert. */
.ptbl-table-scroll .tech-avatar-simple-tooltip {
    display: none !important;
}

.tech-avatar--lg {
    width: 30px;
    height: 30px;
    font-size: 12px;
}

.tech-avatar--xl {
    width: 56px;
    height: 56px;
    font-size: 18px;
    margin: 0 auto;
}

/* Avatar-Bild statt Initialen-Kuerzel (Profil > Persoenliche Daten Upload) - selbe Masse wie
   .tech-avatar, aber object-fit:cover statt Hintergrundfarbe+Text, da hier ein echtes Foto sitzt. */
.tech-avatar-img {
    display: inline-block;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.tech-avatar-img--xl {
    width: 56px;
    height: 56px;
    margin: 0 auto;
}

/* Initialen-Kreis fuer die neue Ansprechpartner-Detailseite (templates/contacts/show.twig) -
   dieselben Masse wie .tech-avatar--xl, aber bewusst NICHT diese Klasse selbst wiederverwendet:
   .tech-avatar traegt Praesenz-Status-Farben fuer Techniker (verfuegbar/abwesend/...), die fuer
   einen externen Ansprechpartner keine Bedeutung haben - hier immer neutraler Akzent-Ton. */
.contact-detail-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    font-size: 18px;
    font-weight: 700;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    flex-shrink: 0;
}

.tech-avatar--available { --presence-color: var(--status-active); --presence-soft: var(--status-active-soft); }
.tech-avatar--onsite, .tech-avatar--busy { --presence-color: var(--status-warning); --presence-soft: var(--status-warning-soft); }
.tech-avatar--vacation, .tech-avatar--sick, .tech-avatar--training, .tech-avatar--other, .tech-avatar--home_office { --presence-color: var(--status-absent); --presence-soft: var(--status-absent-soft); }
.tech-avatar--outside_hours { --presence-color: var(--status-neutral); --presence-soft: var(--status-neutral-soft); }
.tech-avatar--break { --presence-color: var(--status-neutral); --presence-soft: var(--status-neutral-soft); }
.tech-avatar--offline { --presence-color: var(--status-neutral); --presence-soft: var(--status-neutral-soft); }

/* MA-Avatar-Stilvarianten (Admin > Design > MA-Avatar-Stil, SettingsRepository::avatarStyle()) -
   genau eine dieser vier Klassen sitzt zusaetzlich zur Status-/Groessen-Klasse auf .tech-avatar
   (siehe partials/avatar.twig). Alle vier nutzen ausschliesslich --presence-color/--presence-soft
   (oben gesetzt) statt eigener Hex-Werte, damit Light/Dark-Theme automatisch passen. */

/* dot: dezenter neutraler Avatar + Status-Punkt unten rechts (Slack/Linear-Standard) */
.tech-avatar.avatar-style-dot::after {
    content: '';
    position: absolute;
    bottom: -1px;
    right: -1px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--presence-color);
    border: 2px solid var(--color-surface);
}

/* glow: sanft getoenter Hintergrund/Rand in der Praesenzfarbe + Punkt oben rechts - Nutzer-Feedback
   (zwei Runden): 1px/volle Praesenzfarbe wirkte zu blass, die daraufhin probierten 2px/volle Farbe
   dann zu dick und zu dunkel. Geloest ueber color-mix() statt eines dritten Tokens: 1px Randbreite,
   aber die Farbe selbst um 45% Richtung Oberflaeche aufgehellt - duenn UND hell zugleich. */
.tech-avatar.avatar-style-glow {
    background: var(--presence-soft);
    border-width: 1px;
    border-color: color-mix(in srgb, var(--presence-color) 55%, var(--color-surface));
    color: var(--presence-color);
}

/* Fester Punkt UND Puls-Ring sind bewusst identisch dimensioniert (11x11 inkl. Rand, background-
   clip:padding-box haelt die sichtbare Farbflaeche bei 7x7) und auf denselben top/right-Versatz
   gesetzt - Nutzer-Feedback "Puls ist nicht symmetrisch": vorher hatte nur der feste Punkt einen
   2px-Rand, der Puls-Ring nicht, wodurch beide Boxen unterschiedlich gross waren und trotz
   gleicher top/right-Koordinate nicht konzentrisch lagen. */
.tech-avatar.avatar-style-glow::after {
    content: '';
    position: absolute;
    top: -2px;
    right: -2px;
    width: 11px;
    height: 11px;
    box-sizing: border-box;
    border-radius: 50%;
    background: var(--presence-color);
    background-clip: padding-box;
    border: 2px solid var(--color-surface);
    z-index: 1;
}

/* Pulsierender Ring hinter dem festen Punkt (Nutzer-Feedback: "es fehlt Pulsing/Soft Glow" -
   Vorbild-Entwurf nutzte Tailwinds animate-ping) - eigenes ::before, da ::after bereits den festen
   Punkt traegt. prefers-reduced-motion respektiert, da eine Dauerschleifen-Animation sonst fuer
   Nutzer mit Bewegungsempfindlichkeit stoeren wuerde. */
.tech-avatar.avatar-style-glow::before {
    content: '';
    position: absolute;
    top: -2px;
    right: -2px;
    width: 11px;
    height: 11px;
    box-sizing: border-box;
    border-radius: 50%;
    background: var(--presence-color);
    background-clip: padding-box;
    border: 2px solid transparent;
    opacity: 0.85;
    animation: tech-avatar-pulse 1.8s cubic-bezier(0, 0, 0.2, 1) infinite;
}

/* Nutzer-Feedback: bei perfekt konzentrischer Ausrichtung (siehe oben) verschwand der wachsende
   Ring fast vollstaendig hinter dem gleich grossen, deckenden ::after-Punkt (identisches Verhalten
   wie Tailwinds animate-ping-Referenzbeispiel: der Ring ist nur SICHTBAR, sobald er ueber den
   statischen Punkt hinauswaechst) - in Light-Mode mit hellem Seitenhintergrund faellt dieser duenne
   Ring-Rand deutlich schwaecher auf als im dunklen Vorbild-Entwurf. Hoehere Start-Deckkraft (0.85
   statt 0.65) und ein groesserer Skalierungs-Endwert schaffen einen laenger sichtbaren, kraeftigeren
   Ring, ohne die Konzentrizitaet (top/right/width/height) anzutasten. */
@keyframes tech-avatar-pulse {
    0% { transform: scale(1); opacity: 0.85; }
    60% { opacity: 0.35; }
    100% { transform: scale(2.6); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .tech-avatar.avatar-style-glow::before {
        animation: none;
        display: none;
    }
}

/* squircle: abgerundetes Quadrat statt Kreis + Badge oben rechts */
.tech-avatar.avatar-style-squircle {
    border-radius: 8px;
}

.tech-avatar.avatar-style-squircle::after {
    content: '';
    position: absolute;
    top: -3px;
    right: -3px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--presence-color);
    border: 2px solid var(--color-surface);
}

/* border: farbiger Ring mit kleinem Abstand um den Avatar, kein separater Punkt - der Ring selbst
   traegt den Status. Doppelter box-shadow statt outline, um den Versatz (Ring-Offset) sauber
   abzubilden: der innere Schatten in Oberflaechenfarbe erzeugt die Luecke, der aeussere den Ring.
   Nutzer-Feedback ("finde ich langweilig ... evtl. Background ein leichter Farbton"): der Stil
   hiess zwar "Border GLOW", zeigte aber nie ein tatsaechliches Glimmen - nur einen harten,
   flachen Ring. Jetzt zusaetzlich ein radialer Hintergrund-Farbton (sehr leicht, 12% Praesenzfarbe,
   von der Avatar-Mitte ausgehend) fuer Tiefe, plus ein dritter, weicher box-shadow-Layer als
   echter nach aussen blutender Leuchtschein (blur statt hartem Rand) - der Ring bleibt als
   Signature-Element bestehen, wirkt aber nicht mehr flach. */
.tech-avatar.avatar-style-border {
    background:
        radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--presence-color) 14%, transparent) 0%, transparent 75%),
        var(--color-bg);
    border-color: transparent;
    box-shadow:
        0 0 0 2px var(--color-surface),
        0 0 0 4px var(--presence-color),
        0 0 9px 1px color-mix(in srgb, var(--presence-color) 50%, transparent);
}

/* crescent: eigenstaendiges Signature-Element statt Punkt/Ring - eine volle "Mond"-Scheibe in
   Praesenzfarbe (::before), von einer gleich grossen, oberflaechenfarbenen Scheibe (::after) so
   ueberlagert, dass nur eine Sichel am unteren rechten Rand des Avatars sichtbar bleibt. */
.tech-avatar.avatar-style-crescent::before {
    content: '';
    position: absolute;
    bottom: -2px;
    right: -2px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--presence-color);
    box-shadow: 0 0 0 2px var(--color-surface);
    z-index: 1;
}

.tech-avatar.avatar-style-crescent::after {
    content: '';
    position: absolute;
    bottom: -4px;
    right: 1px;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--color-surface);
    z-index: 2;
}

.dot-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px solid var(--color-border);
    font-size: 11px;
    color: var(--color-text-muted);
}

.dot-legend-item {
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

/* Visuelles Urlaubskonto (Ring-Diagramm + Legende) im "Abwesenheit einreichen"-Modal, ersetzt die
   frueheren vier Text-Kacheln (siehe hr-frontent2.html Referenz-Mockup). Der Ring nutzt r=40 ->
   Umfang 2*PI*40 = 251.2 - stroke-dasharray wird in vacation-request.js proportional zum
   Resturlaub-Anteil gesetzt. */
.vacation-account-dashboard {
    display: flex;
    align-items: center;
    gap: 24px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 16px 20px;
    margin-bottom: 16px;
}

.vacation-progress-ring {
    position: relative;
    width: 100px;
    height: 100px;
    flex-shrink: 0;
}

.vacation-progress-ring svg {
    transform: rotate(-90deg);
}

.vacation-progress-ring-bg {
    fill: none;
    stroke: var(--color-border);
    stroke-width: 10;
}

.vacation-progress-ring-value {
    fill: none;
    stroke: var(--color-accent);
    stroke-width: 10;
    stroke-linecap: round;
    transition: stroke-dasharray 300ms ease;
}

.vacation-progress-ring-text {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
}

.vacation-progress-ring-days {
    display: block;
    font-size: 20px;
    font-weight: 700;
    color: var(--color-accent);
    line-height: 1;
}

.vacation-progress-ring-label {
    display: block;
    font-size: 9px;
    font-weight: 600;
    color: var(--color-text-muted);
    text-transform: uppercase;
    margin-top: 2px;
}

.vacation-account-legend {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px 20px;
    flex: 1;
}

.vacation-account-legend-value {
    display: flex;
    align-items: baseline;
    gap: 6px;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text);
}

.vacation-account-legend-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.vacation-account-legend-label {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

/* Dynamische Flyout-Breite je nach beantragtem Zeitraum (siehe vacation-request.js,
   updateModalWidth()) - kompakt bei <=1 Woche, breit bei 3+ Wochen, damit der horizontale
   Team-Kalender genug Platz fuer mehrere Wochenspalten hat. Nur oberhalb der mobilen Breakpoint-
   Grenze aktiv - darunter greift weiterhin die generische ".modal-box { max-width: 100%; }"-Regel
   (siehe @media (max-width: 640px) weiter unten), sonst wuerde die hoehere ID-Spezifitaet dieser
   Regeln die mobile Vollbreite auf kleinen Bildschirmen ueberschreiben. */
@media (min-width: 641px) {
    #vacation-request-modal-box.modal-box--absence-compact { max-width: 620px; }
    #vacation-request-modal-box.modal-box--absence-medium { max-width: 760px; }
    #vacation-request-modal-box.modal-box--absence-wide { max-width: 950px; }
}

/* Horizontaler Team-Kalender (Referenz-Mockup hr-frontent2.html) */
.calendar-box {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: var(--color-surface);
    margin-bottom: 16px;
}

.cal-section-header {
    padding: 10px 14px;
    background: var(--color-bg);
    border-bottom: 1px solid var(--color-border);
}

.cal-section-title {
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    text-transform: uppercase;
    margin: 0;
}

.cal-scroll-wrapper {
    overflow-x: auto;
    width: 100%;
}

.cal-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
    min-width: 480px;
}

.cal-table th, .cal-table td {
    border-bottom: 1px solid var(--color-border);
    padding: 8px;
    text-align: center;
    white-space: nowrap;
}

.cal-table th {
    background: var(--color-bg);
    color: var(--color-text-muted);
    font-weight: 600;
}

/* Erste Spalte (Teamkollege) bleibt beim horizontalen Scrollen fixiert - lange Namen duerfen
   niemals umbrechen oder die Zeilenhoehe verzerren, siehe Nutzer-Vorgabe. */
.cal-table td.cal-name-cell, .cal-table th.cal-name-cell {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--color-surface);
    text-align: left;
    font-weight: 500;
    width: 140px;
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
    border-right: 1px solid var(--color-border);
}

.cal-table th.cal-name-cell { background: var(--color-bg); }

.cal-week-group-header {
    background: var(--color-border);
    color: var(--color-text);
    font-size: 10px;
    text-transform: uppercase;
    font-weight: 700;
}

.cal-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 4px;
    border-radius: 6px;
    font-weight: 600;
    font-size: 10px;
}

.cal-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 14px;
    padding: 10px 14px;
    background: var(--color-bg);
    border-top: 1px solid var(--color-border);
    font-size: 11px;
    color: var(--color-text-muted);
}

.cal-legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
}

.cal-legend-dot {
    width: 14px;
    height: 14px;
    border-radius: 4px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.radio-inline-group {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    font-size: 13px;
    padding-top: 4px;
}

.radio-inline-group label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    font-weight: 400;
}

.hub-vacation-requests-card {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 16px;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-weight: 600;
    font-size: 13px;
    text-decoration: none;
}

.hub-vacation-requests-card svg:last-child {
    margin-left: auto;
}

/* Urlaubsantrags-Workflow (Komponente C "GameChanger"-Genehmigungswidget, siehe
   templates/hr/vacation_requests.twig) - minimalistisches UniFi-White: weisse Karte, duenne
   slate-200-artige Linie (--color-border), Status per Banner-Farbe statt Icon-Overkill. */
.vacation-request-card {
    margin-bottom: 16px;
}

.vacation-request-card-header {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 10px;
}

.vacation-request-card-name {
    font-weight: 700;
}

.vacation-request-card-meta {
    font-size: 12px;
    color: var(--color-text-muted);
}

.vacation-request-note {
    font-size: 13px;
    color: var(--color-text-muted);
    background: var(--color-bg);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    margin-bottom: 10px;
}

.vacation-request-banner {
    padding: 10px 14px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    margin-bottom: 12px;
    background: var(--status-active-soft);
    color: var(--status-active);
}

.vacation-request-banner--ok {
    background: var(--status-active-soft);
    color: var(--status-active);
}

.vacation-request-banner--conflict {
    background: var(--status-warning-soft);
    color: #A66A1E;
}

.vacation-mini-timeline {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 14px;
}

.vacation-mini-timeline-row {
    display: flex;
    align-items: center;
    gap: 10px;
}

.vacation-mini-timeline-label {
    flex: 0 0 120px;
    font-size: 12px;
    color: var(--color-text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.vacation-mini-timeline-cells {
    display: grid;
    gap: 2px;
    flex: 1;
}

.vacation-mini-timeline-cell {
    height: 16px;
    border-radius: 3px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
}

.vacation-mini-timeline-cell--absent {
    background: var(--status-warning);
    border-color: var(--status-warning);
}

.vacation-request-actions {
    display: flex;
    gap: 10px;
}

.vacation-request-actions form {
    margin: 0;
}

/* ============================================================================
   E-Mail-Vorlagen (templates/admin/email_templates.twig) - zweispaltiges Layout:
   links die einzelnen Vorlagen-Karten (nutzt die volle verfuegbare Breite), rechts eine
   sticky Platzhalter-/Quicklink-Sidebar zum Anspringen der einzelnen Vorlagen auf der langen Seite.
   BEWUSST kein align-items:start (mehr): position:sticky "haftet" nur innerhalb der Hoehe seines
   eigenen Containers - mit align-items:start ist die rechte Grid-Spalte nur so hoch wie ihr eigener
   (kurzer) Inhalt, wodurch die Sidebar schon nach wenigen Pixeln Scroll wieder losgeloest wurde
   (Bug-Report: Platzhalter-Liste verschwand beim Scrollen der langen linken Spalte). Der Default
   (stretch) laesst die rechte Spalte auf die volle Grid-Zeilenhoehe (= Hoehe der langen linken
   Spalte) strecken - dadurch hat die Sidebar genug "Spielraum" im eigenen Container, um ueber die
   komplette Scrollstrecke sticky zu bleiben. */
.email-template-layout {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 20px;
}

@media (max-width: 900px) {
    .email-template-layout {
        grid-template-columns: 1fr;
    }
}

/* max-height + eigener overflow-y noetig, nicht nur position:sticky: die Sidebar traegt seit der
   Platzhalter-Liste (Nutzer-Wunsch: Platzhalter beim Scrollen "immer griffbereit") mehr Inhalt als
   auf viele Viewports passt (Platzhalter-Chips + potenziell lange Vorlagen-Quicklink-Liste). Ohne
   eigenes Scroll-Verhalten wuerde die Platzhalter-Liste beim Herunterscrollen der langen
   Hauptspalte trotz sticky irgendwann aus dem sichtbaren Bereich der (dann zu hohen) Sidebar
   herauswandern - genau das Gegenteil von "griffbereit". */
.email-template-sidebar {
    position: sticky;
    top: 20px;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
}

.email-template-quicklinks {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.email-template-quicklinks a {
    display: block;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    color: var(--color-text);
    font-size: 13px;
}

.email-template-quicklinks a:hover {
    background: var(--color-bg);
    color: var(--color-accent);
}

/* "Verfuegbare Platzhalter" in der sticky Sidebar (Nutzer-Wunsch: immer griffbereit beim Scrollen,
   statt einer statischen Karte oben in der langen Vorlagen-Liste) - jeder Token ist ein Chip-Button
   mit data-copy-value (siehe public/js/copy-to-clipboard.js, global in layout.twig initialisiert -
   keine eigene JS-Logik hier noetig). */
.email-template-placeholder-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.email-template-placeholder-group-title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--color-text-muted);
    margin-bottom: 5px;
}

.email-template-placeholder-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    margin: 0 4px 4px 0;
    padding: 4px 8px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 11.5px;
    cursor: pointer;
    transition: background .15s, border-color .15s;
}

.email-template-placeholder-chip:hover {
    background: var(--color-accent-soft);
    border-color: var(--color-accent);
    color: var(--color-accent);
}

.email-template-placeholder-chip code {
    background: none;
    padding: 0;
    font-size: 11.5px;
    color: inherit;
}

.email-template-placeholder-chip svg {
    width: 11px;
    height: 11px;
    flex: 0 0 auto;
    opacity: .6;
}

/* CodeMirror-Editor an das Design-System angleichen (Rahmen/Radius wie andere Inputs). */
.email-template-code + .CodeMirror {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    height: 320px;
    font-family: "SF Mono", "Monaco", "Consolas", monospace;
    font-size: 12px;
}

.email-template-code + .CodeMirror.CodeMirror-focused {
    border-color: var(--color-accent);
}

/* Vorschau-Modal (Nutzer-Wunsch: "Vorschau mit dem Mail-Rahmen und Muster") - simuliert die
   Breite eines E-Mail-Client-Lesefensters statt die volle Modal-Breite auszunutzen, damit die
   Vorschau realistisch wirkt. Das iframe rendert den vom Server gelieferten, bereits mit dem
   Mail-Rahmen umschlossenen HTML-Inhalt isoliert (sandbox, kein Zugriff auf die Elternseite). */
.email-preview-modal-box {
    max-width: 720px;
}

.email-preview-subject {
    display: flex;
    gap: 8px;
    padding: 10px 14px;
    margin-bottom: 12px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    font-size: 13.5px;
}

.email-preview-subject-label {
    color: var(--color-text-muted);
    flex: 0 0 auto;
}

.email-preview-toolbar {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 10px;
}

.email-preview-device-toggle {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

.email-preview-device-toggle button {
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    font-size: 12.5px;
    font-weight: 600;
    padding: 5px 12px;
    border-radius: calc(var(--radius-sm) - 2px);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.email-preview-device-toggle button.is-active {
    background: var(--color-surface);
    color: var(--color-text);
    box-shadow: var(--shadow-card);
}

/* Kein fixes max-height/overflow-y mehr hier (Nutzer-Fund 2026-08-09: erzeugte einen
   eigenen, unnoetigen inneren Scrollbalken selbst bei kurzem Inhalt) - die Hoehe passt
   sich stattdessen dynamisch am tatsaechlich gerenderten Inhalt an (siehe
   resizeEmailPreviewIframe() in email_templates.twig). Bei sehr langen Mails uebernimmt
   ganz normal die aeussere .modal-box .modal-body-Scrollleiste, wie bei jedem anderen
   Modal auch - kein doppelter, verschachtelter Scrollbereich mehr.*/
.email-preview-frame-wrap {
    display: flex;
    justify-content: center;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 20px;
}

/* Mail-Client-Fenster-Mockup (Nutzer-Feedback 2026-08-09: das vorherige dunkle Smartphone-Gehaeuse
   "sah nicht wie ein Handy aus" - stattdessen ein Fenster-Chrome wie bei einem echten Mail-Client/
   Compose-Fenster, siehe Referenz-Screenshot: Titelleiste mit 3 Ampel-Punkten + Betreff mittig,
   darunter der Inhalt. Dieselbe Fenster-Optik fuer Desktop UND Mobil - nur die Breite aendert
   sich (is-mobile-view), kein separates Geraete-Motiv noetig. */
.email-preview-window {
    width: 100%;
    max-width: 640px;
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid var(--color-border);
    box-shadow: 0 20px 40px -16px rgba(0, 0, 0, 0.25), 0 6px 16px -8px rgba(0, 0, 0, 0.12);
    background: #ffffff;
    transition: max-width 200ms ease;
}

.email-preview-frame-wrap.is-mobile-view .email-preview-window {
    max-width: 360px;
}

.email-preview-window-titlebar {
    position: relative;
    display: flex;
    align-items: center;
    padding: 10px 14px;
    background: #f0f0f2;
    border-bottom: 1px solid #e2e2e5;
}

.email-preview-window-dots {
    display: flex;
    gap: 6px;
    flex: 0 0 auto;
}

.email-preview-window-dot {
    width: 11px;
    height: 11px;
    border-radius: 50%;
    display: block;
}

.email-preview-window-dot--red { background: #ff5f57; }
.email-preview-window-dot--yellow { background: #febc2e; }
.email-preview-window-dot--green { background: #28c840; }

.email-preview-window-title {
    position: absolute;
    left: 60px;
    right: 60px;
    text-align: center;
    font-size: 12.5px;
    font-weight: 600;
    color: #4b4b4d;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
}

.email-preview-iframe {
    display: block;
    width: 100%;
    border: none;
    background: #ffffff;
}

/* ============================================================================
   Social-Media-Links (Firmenprofil-Karte + Ansprechpartner-Formular) - Icon wird im Frontend
   automatisch anhand der URL-Domain zugeordnet (siehe App\Support\SocialLinkIcon). Marken-Icons
   bekommen ihre jeweilige Markenfarbe als dezent getoente Kreis-Badge (gleiches Muster wie
   .customer-type-chip), das generische Fallback-Icon bleibt neutral grau. */
.social-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.social-badge {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 999px;
    background: var(--color-bg);
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.social-badge .icon-sm {
    width: 16px;
    height: 16px;
}

.social-badge:hover {
    opacity: 0.8;
}

.social-badge--linkedin {
    background: rgba(10, 102, 194, 0.12);
    color: #0a66c2;
}

.social-badge--xing {
    background: rgba(6, 102, 58, 0.12);
    color: #06663a;
}

.social-badge--youtube {
    background: rgba(255, 0, 0, 0.12);
    color: #ff0000;
}

.social-badge--github {
    background: rgba(36, 41, 47, 0.12);
    color: #24292f;
}

[data-theme="dark"] .social-badge--github {
    background: rgba(255, 255, 255, 0.14);
    color: #E7EAEE;
}

.social-badge--instagram {
    background: rgba(225, 48, 108, 0.12);
    color: #E1306C;
}

.social-badge--facebook {
    background: rgba(24, 119, 242, 0.12);
    color: #1877F2;
}

.social-links-editor {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 10px;
}

.social-links-editor-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.social-links-editor-row input {
    flex: 1;
}

.social-links-editor-remove {
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-muted);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}

.social-links-editor-remove:hover {
    color: var(--color-danger-text);
    border-color: var(--color-danger-border);
}

/* Ansprechpartner-Formular: kompakte Checkbox-Gruppen fuer Berechtigungen und
   Kommunikationspraeferenzen - dichter als der normale .field-Abstand, da hier nur
   ein Kontrollkaestchen + kurzes Label pro Zeile steht. */
.compact-checkbox-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 18px;
}

.compact-checkbox-group label {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 400;
    margin-bottom: 0;
}

.compact-checkbox-group input[type="checkbox"] {
    width: auto;
}

.field-section-title {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin: 0 0 10px;
    padding-top: 4px;
    border-top: 1px solid var(--color-border);
}

/* Interne Notizen-Timeline (Ansprechpartner-Formular, siehe ContactNoteRepository) - eigene
   getoente Box mit Eingabezeile oben, neueste Notiz zuerst darunter. */
.notes-container {
    margin-top: 24px;
    background: var(--color-bg);
    border-radius: var(--radius-md);
    padding: 16px;
    border: 1px solid var(--color-border);
}

.notes-add-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.notes-add-row input {
    flex: 1;
    min-width: 140px;
}

.notes-list {
    margin-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.notes-list:empty {
    margin-top: 0;
}

.note-item {
    background: var(--color-surface);
    padding: 12px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    font-size: 13px;
    gap: 10px;
}

.note-meta {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-bottom: 4px;
}

.note-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

.note-actions button {
    background: none;
    border: none;
    color: var(--color-text-muted);
    cursor: pointer;
    padding: 2px;
    display: flex;
}

.note-actions button:hover {
    color: var(--color-text);
}

.note-actions .note-delete-btn:hover {
    color: var(--color-danger-text);
}

/* ============================================================================
   HR-DASHBOARD (templates/hr/index.twig) - Referenz-Mockup hr-dashboard.html.
   Eigenes "hr-"-Praefix, da Klassennamen wie "list-item"/"card-title" sonst mit
   bereits belegten generischen Namen kollidieren wuerden.
   ============================================================================ */
.hr-kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

.hr-kpi-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 20px 20px 18px;
    box-shadow: var(--shadow-card);
    transition: transform 0.15s ease;
}

.hr-kpi-card:hover {
    transform: translateY(-2px);
}

.hr-kpi-top {
    margin-bottom: 16px;
}

.hr-kpi-label {
    font-size: 12.5px;
    color: var(--color-text-muted);
    font-weight: 600;
}

.hr-kpi-value {
    font-size: 28px;
    font-weight: 800;
    color: var(--color-text);
    letter-spacing: -0.01em;
    margin-bottom: 4px;
}

.hr-kpi-icon {
    width: 44px;
    height: 44px;
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.hr-week-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 12px;
}

.hr-day-column {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 12px;
    min-height: 120px;
}

.hr-day-column--today {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
}

.hr-day-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 6px;
    margin-bottom: 8px;
}

.hr-day-name {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
    text-transform: uppercase;
}

.hr-day-column--today .hr-day-name,
.hr-day-column--today .hr-day-date {
    color: var(--color-accent);
}

.hr-day-date {
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text);
}

.hr-absence-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.hr-day-entry {
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--color-surface);
    border-radius: 999px;
    padding: 5px 10px 5px 5px;
}

.hr-day-column--today .hr-day-entry {
    background: rgba(255, 255, 255, 0.65);
}

[data-theme="dark"] .hr-day-column--today .hr-day-entry {
    background: rgba(0, 0, 0, 0.2);
}

.hr-day-avatar {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--color-border);
    color: var(--color-text-muted);
    font-size: 9px;
    font-weight: 800;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.hr-day-entry-name {
    font-size: 11.5px;
    font-weight: 700;
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.hr-day-tag {
    font-size: 9.5px;
    font-weight: 800;
    padding: 2px 6px;
    border-radius: 999px;
    flex: 0 0 auto;
}

.hr-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-top: 16px;
    padding: 14px 18px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 999px;
}

.hr-legend-item {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 12px;
    color: var(--color-text-muted);
    font-weight: 600;
}

.hr-legend-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.hr-management-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
    margin-bottom: 20px;
}

@media (max-width: 1100px) {
    .hr-management-grid {
        grid-template-columns: 1fr;
    }

    .hr-week-grid {
        grid-template-columns: repeat(5, minmax(110px, 1fr));
        overflow-x: auto;
    }
}

.hr-mgt-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 18px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.hr-list-container {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.hr-list-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    padding: 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
}

.hr-item-title {
    font-size: 13px;
    font-weight: 600;
    display: block;
}

.hr-item-sub {
    font-size: 11px;
    color: var(--color-text-muted);
    display: block;
}

.hr-score-indicator {
    font-size: 11px;
    font-weight: 700;
    padding: 2px 6px;
    border-radius: 4px;
    white-space: nowrap;
}

.hr-widget-title {
    font-size: 14.5px;
    font-weight: 800;
    margin: 0;
    display: flex;
    align-items: center;
    gap: 8px;
}

.hr-widget-icon {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    display: flex;
    align-items: center;
    justify-content: center;
}

.hr-soon-pill {
    font-size: 10px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-accent);
    background: var(--color-accent-soft);
    padding: 2px 8px;
    border-radius: 999px;
}

.hr-widget-footnote {
    font-size: 11.5px;
    color: var(--color-text-muted);
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed var(--color-border);
}

.hr-mood-track {
    height: 12px;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--color-danger-bg-strong), var(--status-warning-soft), var(--status-active-soft));
    margin: 14px 0 6px;
    position: relative;
}

.hr-mood-track::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: var(--color-surface);
    border: 3px solid var(--color-border);
}

.hr-empty-banner {
    background: var(--color-surface);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-md);
    padding: 34px 24px;
    text-align: center;
}

.hr-empty-banner .hr-widget-icon {
    margin: 0 auto 12px;
    background: var(--color-bg);
    color: var(--color-text-muted);
}

.hr-empty-banner p {
    font-size: 13px;
    color: var(--color-text-muted);
    margin: 0;
}

.people-toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}

.people-search {
    flex: 1;
    max-width: 300px;
    display: flex;
    align-items: center;
    gap: 8px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 8px 14px;
    color: var(--color-text-muted);
}

.people-search input {
    border: none;
    background: transparent;
    outline: none;
    font-size: 12.5px;
    color: var(--color-text);
    width: 100%;
}

.people-count {
    margin-left: auto;
    font-size: 12px;
    color: var(--color-text-muted);
    font-weight: 600;
}

.people-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
}

@media (max-width: 1100px) {
    .people-grid {
        grid-template-columns: 1fr;
    }
}

.person-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.person-top {
    display: flex;
    align-items: center;
    gap: 12px;
}

.person-avatar {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: var(--color-accent);
    color: #fff;
    font-weight: 700;
    font-size: 14.5px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.person-avatar.muted {
    background: var(--color-border);
    color: var(--color-text-muted);
}

.person-name {
    font-size: 14px;
    font-weight: 800;
}

.person-num {
    font-size: 11.5px;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.person-meta {
    display: flex;
    flex-direction: column;
    gap: 7px;
    font-size: 12px;
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-border);
    padding-top: 12px;
}

.person-meta div {
    display: flex;
    justify-content: space-between;
}

.person-meta strong {
    color: var(--color-text);
    font-weight: 700;
}

.person-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 2px;
}

.status-dot--inactive {
    background: var(--status-neutral);
}

/* ============ PERSONALAKTE-REDESIGN (2026-07-10) ============
   Sprung-Index, Dirty-Tracking, schwebende Sammel-Speicherleiste, Reveal-Felder - Layout/
   Verhalten aus dem Mockup uebernommen, aber durchgehend mit den bestehenden CoreDesk-Design-
   Tokens statt eigener Farbwerte (siehe UI-Design-Styleguide fuer die dokumentierte Referenz). */
.hr-jumplist {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 14px;
}

.hr-jumplist-title {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-muted);
    margin-right: 4px;
}

.hr-jumplist-link {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--color-text-muted);
    padding: 4px 2px;
    border-bottom: 2px solid transparent;
}

.hr-jumplist-link:hover {
    color: var(--color-text);
}

.hr-jumplist-link.is-active {
    color: var(--color-accent);
    border-bottom-color: var(--color-accent);
}

.hr-dirty-dot {
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    margin-left: 6px;
    background: var(--color-accent);
    opacity: 0;
    transform: scale(0.4);
    transition: opacity 150ms ease, transform 150ms ease, background-color 150ms ease;
}

.hr-dirty-dot.is-dirty {
    opacity: 1;
    transform: scale(1);
}

.hr-dirty-dot.is-saved {
    background: var(--status-active);
    opacity: 1;
    transform: scale(1);
}

.hr-has-reveal {
    position: relative;
}

.hr-has-reveal input {
    padding-right: 36px;
}

.hr-has-reveal .icon-btn {
    position: absolute;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
}

.hr-field-suffix {
    position: relative;
}

.hr-field-suffix span {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 12.5px;
    color: var(--color-text-muted);
    pointer-events: none;
}

.hr-field-suffix input {
    padding-right: 84px;
}

.field-help {
    font-size: 11.5px;
    color: var(--color-text-muted);
    margin: 4px 0 0;
}

.field-help.error {
    display: none;
    color: var(--color-danger-text);
}

.field.show-error .field-help.error {
    display: block;
}

.lock-tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 10.5px;
    color: var(--color-text-muted);
    margin-top: 4px;
}

.hr-save-bar-outer {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    justify-content: center;
    padding-bottom: 26px;
    pointer-events: none;
    z-index: 30;
}

.hr-save-bar {
    pointer-events: auto;
    display: flex;
    align-items: center;
    gap: 18px;
    background: var(--color-text);
    color: #fff;
    border-radius: 999px;
    padding: 8px 8px 8px 22px;
    box-shadow: var(--shadow-card);
    transform: translateY(24px);
    opacity: 0;
    transition: transform 220ms ease, opacity 220ms ease;
}

.hr-save-bar.visible {
    transform: translateY(0);
    opacity: 1;
}

.hr-save-bar-info {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}

.hr-save-bar-count {
    background: var(--color-accent);
    color: #fff;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11.5px;
    font-weight: 800;
}

.hr-save-bar-actions {
    display: flex;
    gap: 6px;
}

.hr-save-bar .btn {
    border-radius: 999px;
}

.hr-save-bar .btn-ghost {
    background: transparent;
    color: rgba(255, 255, 255, 0.75);
    border: none;
}

.hr-save-bar .btn-ghost:hover {
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
}

.hr-save-bar .btn-primary {
    display: flex;
    align-items: center;
    gap: 7px;
}

.hr-save-bar-spinner {
    display: none;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.35);
    border-top-color: #fff;
    animation: hr-save-spin 700ms linear infinite;
}

.hr-save-bar .btn-primary.loading .hr-save-bar-spinner {
    display: block;
}

.hr-save-bar .btn-primary.loading .hr-save-bar-label {
    display: none;
}

@keyframes hr-save-spin {
    to { transform: rotate(360deg); }
}

.autosave-toggle {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.hr-save-status {
    font-size: 11.5px;
    color: var(--color-text-muted);
}

/* Ticket-Bearbeiten-Formular (templates/tickets/show.twig, public/js/ticket-autosave.js) -
   gleiche Optik wie .hr-save-status oben, aber ohne HR-Praefix, da modulweit wiederverwendet. */
.save-status-text {
    font-size: 11.5px;
    color: var(--color-text-muted);
}

/* ============ HR-MODUL-SIDEBAR (2026-07-10) ============
   Zweite, HR-spezifische Kontext-Sidebar neben der globalen Icon-Sidebar - Layout/Verhalten aus dem
   Mockup uebernommen, aber ueber ein negatives Margin auf die bestehende .content-Polsterung
   aufgesetzt statt eines eigenen App-Shells (siehe UI-Design-Styleguide). */
.hr-shell {
    display: flex;
    align-items: flex-start;
    gap: 24px;
    margin: -24px -28px;
}

.hr-module-sidebar {
    flex: 0 0 300px;
    width: 300px;
    padding: 24px 0 24px 28px;
    position: sticky;
    top: 0;
    align-self: flex-start;
}

/* Ohne diesen Breakpoint erzwingt die feste 260px-Spalte auf schmalen Displays horizontales
   Ueberlaufen des gesamten App-Shells - dabei wird die globale Icon-Sidebar (mit Logout) nach
   links aus dem Viewport geschoben, siehe Bugreport "Logout auf People-Seite verschwunden". */
@media (max-width: 900px) {
    .hr-shell {
        flex-direction: column;
        margin: -24px -28px 0;
    }

    .hr-module-sidebar {
        flex: none;
        width: 100%;
        padding: 24px 28px;
        position: static;
    }
}

.hr-employee-card {
    text-align: center;
}

.hr-employee-name {
    font-weight: 700;
    font-size: 15px;
    color: var(--color-text);
    margin-top: 10px;
}

.hr-employee-role {
    font-size: 12.5px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.hr-employee-card .badge {
    margin-top: 10px;
}

.hr-employee-meta-line {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    font-size: 12.5px;
    color: var(--color-text-muted);
    margin-top: 3px;
}

.hr-org-block {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--color-border);
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.hr-org-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.hr-org-label {
    font-size: 11.5px;
    color: var(--color-text-muted);
}

.hr-org-person-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.hr-org-person {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--color-text);
    text-decoration: none;
}

.hr-org-person:hover {
    color: var(--color-accent);
}

.hr-employee-meta {
    margin-top: 16px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    text-align: left;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 14px 16px;
}

.hr-employee-meta div {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    font-size: 12.5px;
}

.hr-employee-meta span {
    flex-shrink: 0;
}

.hr-employee-meta strong {
    text-align: right;
}

.hr-employee-meta span {
    color: var(--color-text-muted);
}

.hr-employee-meta strong {
    color: var(--color-text);
    font-weight: 600;
}

.hr-employee-meta .badge {
    margin-top: 0;
}

.hr-module-nav {
    margin-top: 22px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.hr-module-nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    border-radius: var(--radius-sm);
    font-size: 13.5px;
    font-weight: 600;
    color: var(--color-text-muted);
    cursor: pointer;
}

.hr-module-nav-item:hover {
    background: var(--color-bg);
}

.hr-module-nav-item.is-active {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.hr-module-sidebar .hr-jumplist {
    margin-top: 20px;
    margin-bottom: 0;
    padding: 16px 0 0;
    border: none;
    border-top: 1px solid var(--color-border);
    border-radius: 0;
    box-shadow: none;
    background: none;
    display: block;
}

.hr-module-sidebar .hr-jumplist-title {
    padding: 0 10px;
    margin-bottom: 8px;
}

.hr-module-sidebar .hr-jumplist-link {
    display: block;
    padding: 6px 10px 6px 22px;
    border-radius: var(--radius-sm);
    border-bottom: none;
    position: relative;
}

.hr-module-sidebar .hr-jumplist-link::before {
    content: "";
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: var(--color-border);
}

.hr-module-sidebar .hr-jumplist-link:hover {
    background: var(--color-bg);
}

.hr-module-sidebar .hr-jumplist-link.is-active {
    color: var(--color-accent);
    font-weight: 700;
    background: var(--color-accent-soft);
}

.hr-module-sidebar .hr-jumplist-link.is-active::before {
    background: var(--color-accent);
}

.hr-main {
    flex: 1;
    min-width: 0;
    padding: 24px 28px 24px 0;
}

/* Personalakte-Formulare sollen die volle Breite ihrer (bereits deutlich breiteren) Karten
   ausfuellen statt an der globalen 420px-Eingabefeld-Obergrenze (.field input, siehe app.css)
   zu kleben - betrifft nur die HR-Hauptspalte, nicht das restliche, auf 420px ausgelegte Formular-UI. */
.hr-main .field input[type="text"],
.hr-main .field input[type="email"],
.hr-main .field input[type="password"],
.hr-main .field input[type="date"],
.hr-main .field input[type="time"],
.hr-main .field input[type="datetime-local"],
.hr-main .field input[type="number"],
.hr-main .field input[type="tel"],
.hr-main .field input[type="url"],
.hr-main .field select,
.hr-main .field textarea {
    max-width: none;
}

/* ===================================================================================
   Health Radar - "Warum"-Detailseite (hrd = Health Radar Detail): bewusst viel White
   Space und ruhige Kartenoptik statt dichter Tabellen - Dringlichkeit soll allein ueber
   die drei Statusfarben (danger/warning/clear, bestehende --status-* / --color-danger-*
   Tokens) transportiert werden, nicht ueber visuelle Ueberladung.
   =================================================================================== */
.hrd-wrap {
    max-width: 960px;
    margin: 0 auto;
    padding: 8px 0 48px;
    display: flex;
    flex-direction: column;
    gap: 28px;
}

.hrd-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    padding: 24px 28px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    border-left: 4px solid var(--status-neutral);
}

.hrd-header--critical {
    border-left-color: var(--color-danger-text);
}

.hrd-header--warning {
    border-left-color: var(--status-warning);
}

.hrd-header--clear {
    border-left-color: var(--status-active);
}

.hrd-header-main {
    display: flex;
    align-items: center;
    gap: 16px;
}

.hrd-header-name {
    font-size: 18px;
    font-weight: 700;
    color: var(--color-text);
}

.hrd-header-sub {
    font-size: 13px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.hrd-header-status {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 8px;
}

.hrd-risk-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 700;
    white-space: nowrap;
}

.hrd-risk-pill .icon-sm {
    width: 15px;
    height: 15px;
}

.hrd-risk-pill--critical {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
    border: 1px solid var(--color-danger-border);
}

.hrd-risk-pill--warning {
    background: var(--status-warning-soft);
    color: var(--status-warning);
    border: 1px solid var(--status-warning-soft);
}

.hrd-risk-pill--clear {
    background: var(--status-active-soft);
    color: var(--status-active);
    border: 1px solid var(--status-active-soft);
}

.hrd-pattern-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: flex-end;
}

.hrd-pattern-tag {
    font-size: 11px;
    font-weight: 600;
    padding: 3px 9px;
    border-radius: 999px;
    background: var(--color-bg);
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
}

.hrd-recommendation {
    padding: 18px 22px;
    background: var(--color-danger-bg);
    border: 1px solid var(--color-danger-border);
    border-radius: var(--radius-md);
}

.hrd-recommendation p {
    margin: 0;
    color: var(--color-text);
    line-height: 1.6;
    font-size: 14px;
}

.hrd-recommendation--clear {
    background: var(--status-active-soft);
    border-color: var(--status-active-soft);
}

.hrd-action-taken {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 16px 20px;
    background: var(--status-active-soft);
    border: 1px solid var(--status-active-soft);
    border-radius: var(--radius-md);
    color: var(--status-active);
}

.hrd-action-taken .icon-sm {
    flex-shrink: 0;
    margin-top: 2px;
}

.hrd-action-taken strong {
    display: block;
    font-size: 14px;
    color: var(--color-text);
}

.hrd-action-taken p {
    margin: 4px 0 0;
    font-size: 13px;
    color: var(--color-text-muted);
}

.hrd-section {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.hrd-section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text);
    margin: 0;
}

.hrd-section-hint {
    margin: 0 0 16px;
    font-size: 13px;
    color: var(--color-text-muted);
}

.hrd-metric-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
    gap: 14px;
}

.hrd-metric-card {
    padding: 16px 18px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
}

.hrd-metric-card--warning {
    border-color: var(--status-warning);
    background: var(--status-warning-soft);
}

.hrd-metric-card--critical {
    border-color: var(--color-danger-border);
    background: var(--color-danger-bg);
}

.hrd-metric-label {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 8px;
}

.hrd-metric-value {
    font-size: 22px;
    font-weight: 700;
    color: var(--color-text);
    margin-top: 4px;
    font-variant-numeric: tabular-nums;
}

.hrd-metric-sub {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 4px;
    line-height: 1.4;
}

.hrd-context-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 14px;
}

.hrd-context-card {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 16px 18px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

.hrd-context-card--warning {
    border-color: var(--status-warning);
    background: var(--status-warning-soft);
}

.hrd-context-value {
    font-size: 20px;
    font-weight: 700;
    color: var(--color-text);
    font-variant-numeric: tabular-nums;
}

.hrd-context-label {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.hrd-context-compare {
    font-size: 11px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.hrd-action-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.hrd-action-item {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 14px 18px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.hrd-action-item:hover {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
}

.hrd-action-item > div {
    flex: 1;
}

.hrd-action-item .icon-sm:last-child {
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.hrd-action-title {
    font-size: 14px;
    font-weight: 600;
    color: var(--color-text);
}

.hrd-action-sub {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.hrd-ack-form {
    margin-top: 16px;
    padding: 18px 20px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.hrd-ack-form label {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
}

.hrd-ack-form textarea {
    resize: vertical;
    max-width: none;
}

.hrd-ack-form button {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

@media (max-width: 720px) {
    .hrd-header {
        flex-direction: column;
        align-items: flex-start;
    }

    .hrd-header-status {
        align-items: flex-start;
        width: 100%;
    }

    .hrd-pattern-tags {
        justify-content: flex-start;
    }
}

/* ===================================================================================
   CoreDesk LeadHub (BI-Dashboard fuer Team-Lead/Controller): Attention Radar oben (gleiche
   Dringlichkeit-zuerst-Konvention wie die Health-Radar-Detailseite), 2x2-Quadranten-Grid
   darunter. Segment-Balken/Ring-Technik uebernommen von .vault-segmented-progress bzw. dem
   Urlaubs-Modal-Progress-Ring, nur generalisiert auf neue Klassennamen.
   =================================================================================== */
.leadhub-wrap {
    display: flex;
    flex-direction: column;
    gap: 28px;
    padding: 8px 0 48px;
}

.leadhub-section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text);
    margin: 0 0 4px;
}

.leadhub-section-hint {
    margin: 0 0 16px;
    font-size: 13px;
    color: var(--color-text-muted);
}

.leadhub-subtitle {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    margin: 16px 0 6px;
}

.leadhub-empty-text {
    font-size: 13px;
    color: var(--status-active);
    margin: 0;
}

/* Attention Radar */
.leadhub-radar-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px;
}

.leadhub-radar-card {
    padding: 16px 18px;
    background: var(--color-danger-bg);
    border: 1px solid var(--color-danger-border);
    border-radius: var(--radius-md);
}

.leadhub-radar-card--empty {
    background: var(--status-active-soft);
    border-color: var(--status-active-soft);
}

.leadhub-radar-card h3 {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text);
    margin: 0 0 10px;
}

/* Info-Icon neben dem Radar-Kartentitel: erklaert Team-Leads, was der Detektor bedeutet und
   wieso er relevant ist. Nutzt bewusst das bestehende [data-tooltip]-System (siehe weiter oben),
   aber mit eigenem Modifier statt nowrap - die Erklaertexte sind mehrere Saetze lang und muessen
   umbrechen, was der globale attr()-Tooltip (fuer kurze Sidebar-/Legenden-Labels) nicht kann. */
.leadhub-info-icon {
    display: inline-flex;
    margin-left: auto;
    color: var(--color-text-muted);
    cursor: help;
}

.leadhub-info-icon:hover,
.leadhub-info-icon:focus-visible {
    color: var(--color-accent);
    outline: none;
}

.leadhub-info-icon[data-tooltip]:hover::after,
.leadhub-info-icon[data-tooltip]:focus-visible::after {
    left: auto;
    right: 0;
    top: 100%;
    transform: none;
    margin-left: 0;
    margin-top: 8px;
    width: 260px;
    white-space: normal;
    line-height: 1.5;
    font-weight: 500;
    text-align: left;
}

.leadhub-radar-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.leadhub-radar-list li {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 13px;
}

.leadhub-radar-list a,
.leadhub-radar-title-text {
    color: var(--color-text);
    font-weight: 600;
    text-decoration: none;
}

.leadhub-radar-list a:hover {
    color: var(--color-accent);
}

.leadhub-radar-meta {
    font-size: 12px;
    color: var(--color-text-muted);
}

/* 2x2 Quadranten-Grid */
.leadhub-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

@media (max-width: 900px) {
    .leadhub-grid {
        grid-template-columns: 1fr;
    }
}

.leadhub-quadrant-card {
    padding: 20px 22px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
}

.leadhub-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 12px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.leadhub-legend span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.leadhub-legend-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
}

.leadhub-plain-list {
    list-style: none;
    margin: 0 0 4px;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    font-size: 13px;
}

.leadhub-plain-list a {
    color: var(--color-text);
    text-decoration: none;
}

.leadhub-plain-list a:hover {
    color: var(--color-accent);
}

.leadhub-type-badge {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
}

/* CoreDesk Quick-Punch Overhead-Ratio (siehe Migration 153 / LeadHubRepository::overheadRatios()) -
   grau/gelb/rot je nach konfigurierbarer Schwelle (Admin > Quick-Punch-Kacheln). */
.leadhub-overhead-badge {
    display: inline-block;
    padding: 2px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 700;
}

.leadhub-overhead-badge--ok { background: var(--color-bg); color: var(--color-text-muted); }
.leadhub-overhead-badge--warn { background: #FEF3C7; color: #92400E; }
.leadhub-overhead-badge--alert { background: #FEE2E2; color: #B91C1C; }

/* Q1 - Auslastungs-Segmentbalken (Technik wie .vault-segmented-progress) */
.leadhub-util-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.leadhub-util-row {
    display: grid;
    grid-template-columns: 120px 1fr 100px;
    align-items: center;
    gap: 10px;
}

.leadhub-util-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.leadhub-util-bar {
    height: 10px;
    background: var(--color-border);
    border-radius: 5px;
    overflow: hidden;
    display: flex;
}

.leadhub-util-seg {
    height: 100%;
}

.leadhub-util-seg--billable { background: var(--status-active); }
.leadhub-util-seg--included { background: var(--color-accent); }
.leadhub-util-seg--travel { background: var(--status-warning); }
.leadhub-util-seg--internal { background: var(--status-neutral); }
.leadhub-util-seg--unbooked { background: var(--color-danger-text); }

.leadhub-util-total {
    font-size: 12px;
    color: var(--color-text-muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Q2 - SLA-Ring (Technik wie das Urlaubs-Modal, layout.twig) + Trend-Balkenpaare */
.leadhub-sla-row {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 16px;
}

.leadhub-sla-ring-wrap {
    position: relative;
    width: 84px;
    height: 84px;
    flex-shrink: 0;
}

.leadhub-sla-ring {
    width: 100%;
    height: 100%;
    transform: rotate(-90deg);
}

.leadhub-sla-ring-bg {
    fill: none;
    stroke: var(--color-border);
    stroke-width: 10;
}

.leadhub-sla-ring-value {
    fill: none;
    stroke: var(--color-accent);
    stroke-width: 10;
    stroke-linecap: round;
    transition: stroke-dasharray 0.3s ease;
}

.leadhub-sla-ring-text {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    font-weight: 700;
    color: var(--color-text);
}

.leadhub-trend-bars {
    display: flex;
    align-items: flex-end;
    gap: 4px;
    height: 80px;
}

.leadhub-trend-bar-pair {
    flex: 1;
    display: flex;
    align-items: flex-end;
    gap: 2px;
    height: 100%;
}

.leadhub-trend-bar {
    flex: 1;
    min-height: 2px;
    border-radius: 2px 2px 0 0;
}

.leadhub-trend-bar--incoming { background: var(--color-accent); }
.leadhub-trend-bar--resolved { background: var(--status-active); }

/* Q4 - Interne-Leistungen-Balken */
.leadhub-leakage-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.leadhub-leakage-row {
    display: grid;
    grid-template-columns: 140px 1fr 70px;
    align-items: center;
    gap: 10px;
}

.leadhub-leakage-label {
    font-size: 13px;
    color: var(--color-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.leadhub-leakage-bar {
    height: 8px;
    background: var(--color-border);
    border-radius: 4px;
    overflow: hidden;
}

.leadhub-leakage-bar div {
    height: 100%;
    background: var(--status-warning);
}

.leadhub-leakage-value {
    font-size: 12px;
    color: var(--color-text-muted);
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* CoreDesk LeadHub > Team Performance Timeline - Wochenansicht: pro Mitarbeiter 7 kompakte
   Tages-Balken statt einer vollen Stunden-Matrix (siehe _team_timeline_week_bars.twig). Jede
   Zelle ist ein Link in die Tagesansicht desselben Datums. */
.leadhub-week-bars {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.leadhub-week-bars-row {
    display: flex;
    align-items: center;
    gap: 14px;
}

.leadhub-week-bars-name {
    width: 160px;
    flex-shrink: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.leadhub-week-bars-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 8px;
    flex: 1;
}

.leadhub-week-bar-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 8px 6px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: inherit;
    text-decoration: none;
}

.leadhub-week-bar-cell:hover {
    border-color: var(--color-accent);
    text-decoration: none;
}

.leadhub-week-bar-cell.is-today {
    border-color: var(--color-accent);
    background: var(--color-bg);
}

.leadhub-week-bar-label {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

.leadhub-week-bar-track {
    width: 100%;
    height: 6px;
    background: var(--color-border);
    border-radius: 3px;
    overflow: hidden;
}

.leadhub-week-bar-fill {
    display: block;
    height: 100%;
}

.leadhub-week-bar-fill.badge--good { background: var(--status-active); }
.leadhub-week-bar-fill.badge--warn { background: var(--status-warning); }
.leadhub-week-bar-fill.badge--critical { background: var(--color-critical); }

.leadhub-week-bar-percent {
    font-size: 11px;
    font-weight: 600;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.leadhub-week-bar-chip {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-text-muted);
    background: var(--status-neutral-soft);
    border-radius: var(--radius-sm);
    padding: 2px 8px;
}

.leadhub-week-bar-chip--holiday {
    background: var(--calendar-holiday-bg);
    color: var(--color-accent);
}

.leadhub-week-bar-chip--off {
    background: transparent;
    color: var(--color-text-muted);
}

/* CoreDesk Zeiterfassungs-Freigabe (templates/time_entry_approvals/index.twig) - eine Karte pro
   Techniker+Tag-Gruppe mit einer eingebetteten Detail-Tabelle, damit der Controller tatsaechlich
   sieht, was er da freigibt, statt nur eine Summenzeile abzunicken. */
.approval-group {
    border-bottom: 1px solid var(--color-border);
    padding: 16px 20px;
}

.approval-group:last-child {
    border-bottom: none;
}

.approval-group-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 10px;
}

.approval-group-title {
    display: flex;
    align-items: center;
    gap: 10px;
}

.approval-group-user {
    font-weight: 600;
}

.approval-group-date {
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

.approval-group-summary {
    display: inline-block;
    color: var(--color-text-muted);
    font-size: 0.8rem;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 3px 12px;
    font-variant-numeric: tabular-nums;
}

/* Zeit-Kategorie-Aufschluesselung (Gesamt/Intern/Nicht berechnet/Abrechenbar) unter der Gruppen-
   bzw. Freigegeben-Kopfzeile - siehe TimeEntryApprovalController::timeBreakdown(). Nur sichtbar,
   wenn tatsaechlich etwas Interessantes drinsteckt (interne oder nicht berechnete Anteile), sonst
   deckt sich die Info 1:1 mit der ohnehin sichtbaren .approval-group-summary. */
.approval-time-breakdown {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 6px;
    /* Der "FREIGEGEBENE LEISTUNGEN"-Titel (.approval-seamless-section-title) setzt
       text-transform:uppercase - ohne diese explizite Ruecknahme wuerden die Chips dort in
       Grossbuchstaben erben, waehrend dieselben Chips unter der Pending-Gruppe (kein Uppercase-
       Elternelement) normal geschrieben blieben. Beide Stellen sollen identisch aussehen. */
    text-transform: none;
}

.approval-time-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.76rem;
    font-variant-numeric: tabular-nums;
    padding: 2px 10px;
    border-radius: 999px;
    background: var(--color-bg);
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
}

.approval-time-chip--internal {
    background: var(--status-neutral-soft);
    color: var(--status-neutral);
    border-color: transparent;
}

.approval-time-chip--nb {
    background: var(--status-warning-soft);
    color: var(--status-warning);
    border-color: transparent;
}

.approval-time-chip--billable {
    background: var(--status-active-soft);
    color: var(--status-active);
    border-color: transparent;
}

/* Abrechnungs-Zusammenfassung am Tabellenfuss der Ticket-Detailseite (Nutzer-Wunsch, Vorbild
   TANSS "Statistiken"-Tabelle) - flache Grid-Leiste statt Karte, sitzt als eigenstaendiges
   Flex-Kind unterhalb von .asset-list-table-scroll (siehe body.page-seamless-split
   #approval-pending-groups) und bleibt dadurch immer sichtbar, waehrend nur die Tabelle darueber
   scrollt. flex-shrink:0 verhindert, dass die Flex-Spalte sie beim knappen Platz zusammenstaucht. */
.billing-summary-footer {
    display: flex;
    flex-wrap: wrap;
    border-top: 1px solid var(--color-border);
    background: var(--color-bg);
    flex-shrink: 0;
}

.billing-summary-item {
    flex: 1 1 160px;
    padding: 14px 20px;
    border-right: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.billing-summary-item:last-child {
    border-right: none;
}

.billing-summary-label {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
}

.billing-summary-value {
    font-size: 14.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.billing-summary-item--nb .billing-summary-value {
    color: var(--status-warning);
}

.billing-summary-item--billable .billing-summary-value {
    color: var(--status-active);
}

.billing-summary-item--grand {
    background: var(--color-surface);
}

.billing-summary-item--grand .billing-summary-value {
    font-size: 16.5px;
}

.approval-entry-table {
    font-size: 0.9rem;
}

.approval-note-editable {
    min-width: 140px;
    max-width: 320px;
    padding: 3px 6px;
    border-radius: var(--radius-sm);
    border-bottom: 1px dashed var(--color-border);
    cursor: text;
    white-space: pre-wrap;
    transition: background-color 0.15s ease, border-color 0.15s ease;
    /* Begrenzt den Leistungstext auf 2 Zeilen (siehe .approval-note-more Umschalter) - eine lange
       Notiz soll die Zeilenhoehe der Tabelle nicht sprengen. is-expanded (Klick auf "mehr" oder
       Fokus zum Bearbeiten) hebt die Begrenzung dauerhaft fuer diese Zeile auf. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.approval-note-editable.is-expanded {
    display: block;
    -webkit-line-clamp: unset;
    overflow: visible;
}

.approval-note-editable:empty::before {
    content: attr(title);
    color: var(--color-text-muted);
    font-style: italic;
}

.approval-note-editable:hover,
.approval-note-editable:focus {
    background-color: var(--color-surface-alt, rgba(0, 0, 0, 0.03));
    border-color: var(--color-primary);
    outline: none;
}

.approval-note-editable.is-saved {
    background-color: rgba(34, 197, 94, 0.14);
    border-color: rgba(34, 197, 94, 0.5);
}

.approval-note-editable.is-error {
    background-color: rgba(239, 68, 68, 0.14);
    border-color: rgba(239, 68, 68, 0.5);
}

/* Sichtbares "Nicht berechnet"-Grund-Badge unter der Dauer/Betrag-Zelle (Nutzer-Feedback: der
   Grund war bisher nur im title-Attribut des winzigen Revert-Buttons versteckt). */
.approval-nb-reason {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-top: 3px;
    padding: 1px 6px;
    border-radius: var(--radius-sm);
    background: var(--status-warning-soft);
    color: var(--status-warning);
    font-size: 10.5px;
    font-weight: 600;
    white-space: nowrap;
}

.approval-nb-reason .icon-xs { width: 11px; height: 11px; flex-shrink: 0; }

/* Kurzer, generischer Bestaetigungs-Puls fuer die vier stummen Toggle-Aktionen (nicht berechnen/
   berechnen/intern/oeffentlich) - "Freigeben" gibt durch das Verschwinden der Zeile bereits ein
   sichtbares Signal, diese vier bisher nicht (Nutzer-Feedback). Gleiches Fade-Idiom wie
   .tdv2-tl-entry--highlight (Sprungziel-Aufleuchten im Ticket-Verlauf). */
.approval-row-flash { animation: approval-row-flash-pulse 0.9s ease-out; }
@keyframes approval-row-flash-pulse {
    0% { background: var(--color-accent-soft); }
    100% { background: transparent; }
}

/* Randloses Vollflaechen-Layout fuer die Freigaben-Seite (siehe body.page-seamless-split weiter
   oben): ersetzt die schwebenden .card-Bloecke mit Rand/Schatten/Aussenabstand durch eine einzige
   buendige weisse Flaeche, die von der Topbar bis zum unteren Viewport-Rand reicht und nur intern
   scrollt - Intro/Filterleiste/Gruppen werden durch schlichte 1px-Trennlinien statt Karten-Gaps
   voneinander abgesetzt. */
.approval-seamless {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    background: var(--color-surface);
}

/* Fehlendes Bindeglied zwischen body.page-seamless-split .content (weiter oben) und
   .approval-seamless (direkt darueber): #billing-tab-content und #approval-pending-groups muessen
   selbst zu Flex-Spalten werden, damit .approval-seamless' eigenes flex:1 sowie das flex:1 von
   .asset-list-table-scroll (siehe Asset-Listen-Regeln) tatsaechlich greifen - sonst waechst jeder
   Container nur auf Inhaltshoehe und die Tabelle bekommt nie eine begrenzte, scrollbare Flaeche.
   Bewusst unter body.page-seamless-split gescoped (siehe billing_module.twig body_class-Block) -
   #billing-tab-content wird von allen Billing-Tabs genutzt, ohne die aktive Klasse bleiben Simulate/
   Freigabe/Projekt-Controlling/Historie unveraendert normal scrollend. */
body.page-seamless-split #billing-tab-content {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* .approval-seamless traegt bereits flex:1/min-height:0/overflow-y:auto (siehe oben), aber ohne
   eigenes display:flex werden dessen Kinder (Header/Shortcuts/Filter/#approval-pending-groups)
   nicht zu Flex-Items - #approval-pending-groups' flex:1 (weiter unten) liefe dadurch ins Leere.
   Bewusst NUR hier unter page-seamless-split gesetzt statt direkt auf .approval-seamless, da diese
   Klasse auch vom ungeaenderten Projekt-/Kunden-Modus (_billing_tab_approvals.twig) genutzt wird. */
body.page-seamless-split .approval-seamless {
    display: flex;
    flex-direction: column;
}

/* Seit der Kombi-Shell-Migration (2026-08-02) uebernehmen die geteilten .asset-list-shell/
   .asset-list-sidebar-Basisregeln (height:100% bzw. overflow-y:auto, siehe weiter oben) die Rolle,
   die vorher .billing-module-shell/.billing-module-sidebar hier extra fuer page-seamless-split
   herstellen mussten - keine eigene Regel mehr noetig. Nur der Innenabstand der Tabellen-Pane bleibt
   billing-spezifisch (die uebrigen Kombi-Shell-Seiten sind randlose Volltabellen ohne Innenabstand-
   Bedarf, siehe .asset-list-table-pane weiter oben - hier gescoped auf #billing-module-shell statt
   die geteilte Basisregel zu aendern, sonst wuerden customers/assets/etc. ihre polsterlose Flaeche
   verlieren). Nutzer-Feedback 2026-08-01 ("Action-Bar & Button Spacing") gilt unveraendert: ganz ohne
   Innenabstand klebte der Tabellenkopf/die ZIP-Aktionsleiste direkt an der Sidebar-Kante,
   padding-bottom bewusst 0 - die Tabelle selbst (bzw. .approval-seamless) bringt ihr eigenes
   flex:1/overflow-y:auto mit und soll bis zum unteren Rand reichen, nur oben/links/rechts braucht es
   Luft zur Sidebar/Topbar. */
body.page-seamless-split #billing-module-shell .asset-list-table-pane {
    padding: 16px 16px 0;
}

body.page-seamless-split #approval-pending-groups {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.approval-seamless-intro {
    padding: 20px 24px;
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text-muted);
    font-size: 13.5px;
}

.approval-seamless-intro p {
    margin: 0;
}

/* Kopfzeile der projekt-gescopten Leistungsfreigabe (Refactoring "Kein Drawer") - ersetzt an dieser
   Stelle den generischen Intro-Text, da hier immer genau ein Projekt im Kontext steht. */
.approval-project-header {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 16px 24px;
    border-bottom: 1px solid var(--color-border);
}

.approval-project-header-title {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}

.approval-project-header-title strong {
    font-size: 15px;
}

.approval-project-header-title .text-muted {
    font-size: 12.5px;
}

/* Vorschau/Abrechnen entkoppelt (Nutzer-Wunsch): zwei Buttons statt einem, Vorschau rein lesend
   (BillingPreviewFlyout), Abrechnen weiterhin das bestehende Formular auf ProjectController::bill(). */
.approval-project-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
}

/* Tastatur-Shortcuts-Leiste unter dem Freigabe-Intro-Text (Power-UX Phase 1) - klappbar, Zustand
   wird in localStorage gemerkt (siehe time-approvals.js toggleShortcutsBar()), damit erfahrene
   Nutzer sie nach dem Erlernen ausblenden koennen, ohne die Funktion selbst zu verlieren. */
.approval-shortcuts-bar {
    border-bottom: 1px solid var(--color-border);
    background: var(--color-bg);
}

.approval-shortcuts-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 24px;
    background: none;
    border: none;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--color-text-muted);
    cursor: pointer;
    text-align: left;
}

.approval-shortcuts-toggle:hover {
    color: var(--color-accent);
}

.approval-shortcuts-toggle .icon-sm {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
}

.approval-shortcuts-chevron {
    margin-left: auto;
    transition: transform 0.15s ease;
}

.approval-shortcuts-bar.is-collapsed .approval-shortcuts-chevron {
    transform: rotate(-90deg);
}

.approval-shortcuts-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 7px 20px;
    padding: 0 24px 14px;
}

.approval-shortcuts-bar.is-collapsed .approval-shortcuts-list {
    display: none;
}

.approval-shortcut {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.approval-shortcut kbd {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 7px;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 600;
    line-height: 1;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: 0 1px 0 var(--color-border), inset 0 1px 0 rgba(255, 255, 255, 0.5);
}

.approval-shortcut kbd + kbd {
    margin-left: -3px;
}

.approval-shortcut em {
    font-style: normal;
    font-size: 12px;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.approval-seamless-section-title {
    padding: 14px 24px;
    border-bottom: 1px solid var(--color-border);
    font-size: 13px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
    background: var(--color-bg);
}

/* Typ-Tabs (Alle/Nur Support-Wartung/Nur Projekte) ueber der Freigaben-Filterleiste - nutzt das
   bestehende .view-toggle-Segmented-Control-Muster (siehe projects/show.twig Kanban/Tabelle-
   Umschalter), nur mit eigenem Aussenabstand statt Inline-Platzierung neben anderen Controls. */
.approval-type-toggle {
    margin: 16px 24px 0;
}

/* Projekt-Chip unter dem Ticket-Link in der Freigaben-Tabelle (Pending + Kuerzlich freigegeben) -
   erscheint nur, wenn die Leistung auf einem Projekt-Ticket gebucht wurde (siehe
   TimeEntryRepository::entriesForCustomer()/entriesApprovedForCustomer()). Nutzt bewusst die
   bestehende badge--{{ billing_mode }}-Klasse (siehe _phase_billing_preview.twig) statt neuer
   Emoji-Badges, damit die Abrechnungsart-Kennzeichnung app-weit einheitlich bleibt. */
.approval-project-chip {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 4px;
    font-size: 11px;
    color: var(--color-text-muted);
}

.approval-project-chip .badge {
    font-size: 10px;
    padding: 1px 6px;
}

/* Filterleiste ueber der Freigaben-Liste: Vertragsart + Datumsbereich + Schnellauswahl (letzte
   3/7 Tage), damit ein Controller sich vor der Pruefung auf einen Kunden-Vertragstyp/Zeitraum
   eingrenzen kann, statt immer die komplette offene Liste durchsuchen zu muessen. Buendig statt
   Karte - Trennung zur nachfolgenden Liste per border-bottom statt Aussenabstand. */
.approval-filter-bar {
    display: flex;
    align-items: flex-end;
    gap: 16px;
    flex-wrap: wrap;
    padding: 16px 24px;
    border-bottom: 1px solid var(--color-border);
}

.approval-filter-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.approval-filter-field label {
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.approval-filter-presets {
    display: flex;
    gap: 8px;
}

.approval-filter-actions {
    display: flex;
    gap: 8px;
    margin-left: auto;
}

.approval-actions-col {
    width: 1%;
    white-space: nowrap;
}

.approval-entry-actions {
    display: flex;
    gap: 6px;
    align-items: center;
}

.approval-entry-actions form {
    display: inline-flex;
}

.approval-note-wrap {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
}

.approval-note-more {
    display: none;
    background: none;
    border: none;
    padding: 0;
    font-size: 11px;
    color: var(--color-accent);
    cursor: pointer;
}

.approval-note-more.is-visible {
    display: inline;
}

.approval-select-col {
    width: 1%;
    white-space: nowrap;
}

/* Dynamischer Sammel-Freigabe-Button (siehe Kunden-Gruppen-Kopfzeile): Label + Dauer wechseln
   live zwischen "Alle X freigeben" (0 angehakt) und "X ausgewaehlte freigeben" (>=1 angehakt) -
   siehe time-approvals.js updateBulkButton(). Nur noch deaktiviert, wenn die Gruppe gar keine
   freigebbare Zeile mehr hat. */
.approval-bulk-form {
    display: inline-flex;
}

.approval-bulk-approve-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.85rem;
}

.approval-bulk-approve-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.approval-bulk-count {
    font-variant-numeric: tabular-nums;
}

/* TANSS-Style Zeilen-Hervorhebung fuer bereits freigegebene Leistungen (siehe
   _billing_tab_ready_to_bill_detail.twig, eine chronologische Tabelle statt zweier getrennter
   Bloecke) - dezenter Gruenton auf Hover-Basis statt fixer Hex-Werte, damit Light/Dark-Theme
   automatisch passen. Bewusst VOR .approval-row-focused platziert, damit ein per Tastatur
   fokussierter Eintrag weiterhin den Akzent-Hintergrund gewinnt statt vom Gruenton ueberdeckt
   zu werden (Cascade-Reihenfolge bei gleicher Spezifitaet). */
.is-approved-row {
    background: var(--status-active-soft);
}

.is-approved-row:hover {
    background: var(--status-active-soft);
    filter: brightness(0.97);
}

/* Freigegeben, aber nicht abgerechnet (Kulanz/Gewaehrleistung/...) - bewusst NICHT dasselbe Gruen
   wie eine normal abgerechnete Zeile, sonst sieht eine Kulanz-Leistung faelschlich "erfolgreich"
   aus (Nutzer-Feedback 2026-08-05). Freigegeben & intern bleibt neutral, da hier grundsaetzlich nie
   ein Betrag anfaellt - weder "Erfolg" noch "Kulanz" passt als Farbaussage. */
.is-approved-row.is-approved-row--not-billable {
    background: var(--status-warning-soft);
}

.is-approved-row.is-approved-row--not-billable:hover {
    background: var(--status-warning-soft);
    filter: brightness(0.97);
}

.is-approved-row.is-approved-row--internal {
    background: var(--status-neutral-soft);
}

.is-approved-row.is-approved-row--internal:hover {
    background: var(--status-neutral-soft);
    filter: brightness(0.97);
}

/* Tastatur-first-Navigation ueber die Freigabe-Liste (siehe time-approvals.js j/k/Pfeile) -
   Fokus-Indikator auf der aktuell per Tastatur ausgewaehlten Zeile. */
.approval-row-focused {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
    background: var(--color-accent-soft);
}

/* Gesamtsummen-Kopfzeile ("Offene Freigaben: 15 Eintraege | 12 Std. 30 Min.") neben dem
   Sektions-Titel - bewusst normalgewichtig/kleingeschrieben, damit sie sich vom fett-groszen
   Titel selbst abhebt. */
.approval-totals {
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    font-variant-numeric: tabular-nums;
}

/* Reine Icon-Aktionsbuttons in der Freigaben-Liste (Nicht berechnen/Intern/Freigeben/Ticket offen) -
   Unterscheidung ueber Icon-Form + Farbvariante (--primary/--danger/--locked), Beschriftung nur
   als title-Tooltip beim Hover statt sichtbarem Text. */
.approval-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: var(--radius-sm);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.approval-action-btn:hover {
    background: var(--color-bg);
}

.approval-action-btn--primary {
    border-color: var(--color-accent);
    background: var(--color-accent);
    color: #fff;
}

.approval-action-btn--primary:hover {
    filter: brightness(1.1);
    background: var(--color-accent);
    color: #fff;
}

.approval-action-btn--danger {
    border-color: var(--color-danger-border);
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
}

.approval-action-btn--danger:hover {
    background: var(--color-danger-bg-strong);
}

.approval-action-btn--locked {
    color: var(--color-text-muted);
    background: var(--color-bg);
    cursor: not-allowed;
    opacity: 0.75;
}

/* AJAX-Umbau der Freigaben-Aktionen (siehe time-approvals.js) - Buttons/Checkbox/Badge werden
   immer beide Zustaende gerendert und per Klasse ein-/ausgeblendet statt per Twig {% if %}, damit
   ein Toggle-Klick nur die Klasse umschaltet statt die Seite neu zu laden. */
.approval-select-checkbox.is-hidden,
.approval-action-btn.is-hidden,
.approval-internal-badge.is-hidden,
.approval-nb-reason.is-hidden,
.approval-totals.is-hidden {
    display: none;
}

/* Anfahrts-Badge in der DAUER-Zelle (siehe UI-Spec "Anfahrts-Badges & Betragsberechnung") -
   kompakt, dezent, nur gerendert wenn der Eintrag tatsaechlich Anfahrtszeit erfasst hat. */
.approval-travel-badges {
    display: flex;
    gap: 4px;
    margin-top: 3px;
}

.approval-travel-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 10.5px;
    font-weight: 700;
    color: var(--color-text-muted);
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    padding: 1px 6px;
    border-radius: 6px;
    white-space: nowrap;
}

.approval-travel-badge svg {
    width: 11px;
    height: 11px;
}

/* Support-Ort (Buero/Remote, Vor Ort, Telefon) neben der Dauer - bewusst NUR ein Icon ohne Text
   (Nutzer-Feedback 2026-08-04), Tooltip traegt die Beschriftung. */
.approval-support-location-icon {
    display: inline-flex;
    vertical-align: -2px;
    margin-left: 5px;
    color: var(--color-text-muted);
}

.approval-support-location-icon svg {
    width: 12px;
    height: 12px;
}

/* Support-Ort als eigene Spalten-Badge (icon+label, statt nur Icon) - fuer die Freigabe-Tabellen mit
   dediziertem "Ort"-Header (project_detail/ready_to_bill_detail; die kunden-gruppierte
   _billing_tab_approvals.twig behaelt weiterhin nur das kompakte Icon in der Dauer-Zelle, da sie
   keine eigene Ort-Spalte hat). Vor-Ort bewusst farblich hervorgehoben (Accent-Blau), da es der
   einzige Support-Ort mit direkter Kostenfolge (Anfahrtspauschale) ist. */
.approval-location-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 600;
    padding: 2px 7px;
    border-radius: 5px;
    white-space: nowrap;
    background: var(--status-neutral-soft);
    color: var(--status-neutral);
}

.approval-location-badge svg { width: 11px; height: 11px; flex-shrink: 0; }

.approval-location-badge--onsite {
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.approval-location-travel {
    opacity: .8;
    font-weight: 500;
}

/* UniFi-Style-Redesign der Leistungsfreigabe-Tabelle: randlos, sehr dezente Trennlinien/Hover statt
   Zebra-Streifen, Monospace fuer Zeitraum/Dauer/Betrag. Scoped auf .approval-entry-table (nicht auf
   das global genutzte .data-table, siehe table.data-table oben) - betrifft damit automatisch auch
   die ausgelagerte Freigabe-Historie (admin/_billing_tab_approval_history.twig), die dieselbe Klasse
   traegt. */
table.approval-entry-table th,
table.approval-entry-table td {
    border-bottom: 1px solid var(--color-border);
}

table.approval-entry-table tbody tr:nth-child(even) {
    background: transparent;
}

table.approval-entry-table tbody tr:hover {
    background: var(--color-accent-tint);
}

table.approval-entry-table .status-dot--active {
    margin-right: 6px;
}

.approval-time-cell {
    font-size: 11.5px;
    color: var(--color-text-muted);
    white-space: nowrap;
}

/* Zusammengefasste Datum/Zeit-Spalte der Ticket-Detailtabelle (Nutzer-Wunsch: Datum + Zeitraum in
   einer Spalte statt separat) - Datum normalgewichtig, Uhrzeit-Zeile darunter kleiner/gedaempft. */
.approval-date-time-cell {
    white-space: nowrap;
}

.approval-date-value {
    font-size: 13px;
}

.approval-time-value {
    font-size: 11px;
    margin-top: 1px;
}

/* Statischer Freigegeben-Indikator (ersetzt die entfernte "Freigegeben von"-Textspalte, siehe
   time_approvals.approved_tooltip) - dezent gruen, Details nur als Hover-Tooltip statt permanent
   sichtbarer Spaltenbreite. */
.approval-action-btn--approved {
    color: var(--status-active);
    cursor: default;
}

.approval-time-dash {
    color: var(--color-text-muted);
}

.approval-duration-cell {
    text-align: right;
    white-space: nowrap;
}

.approval-duration-value {
    display: block;
    font-weight: 700;
}

.approval-amount-value {
    display: block;
    font-size: 11px;
    color: var(--color-text-muted);
    margin-top: 1px;
}

/* ============================================================================
   Leistungsfreigabe: Fokussierter Leistungs-Editor (Slide-Over-Drawer, siehe
   templates/admin/billing_module.twig + public/js/time-approvals.js). Eigene aqe-*
   Klassen statt .modal-overlay/.modal-box oder .te-flyout - rechtsseitige
   Slide-Over-Optik wie UniFi, aber unabhaengig von beiden bestehenden Mustern.
   ============================================================================ */
.aqe-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(16, 24, 40, .1);
    backdrop-filter: blur(1px);
    -webkit-backdrop-filter: blur(1px);
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s ease;
    z-index: 900;
}

.aqe-backdrop.is-open {
    opacity: 1;
    pointer-events: auto;
}

.aqe-drawer {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    width: 460px;
    max-width: 94vw;
    background: var(--color-surface);
    box-shadow: -16px 0 40px rgba(16, 24, 40, .16);
    z-index: 901;
    transform: translateX(100%);
    transition: transform .3s cubic-bezier(.2, .8, .2, 1);
    display: flex;
    flex-direction: column;
    color: var(--color-text);
}

.aqe-drawer.is-open {
    transform: translateX(0);
}

.aqe-head {
    padding: 20px 20px 16px;
    border-bottom: 1px solid var(--color-border);
}

.aqe-head-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}

.aqe-ref {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.aqe-title {
    margin: 0 0 2px;
    font-size: 17px;
    font-weight: 700;
    line-height: 1.3;
}

.aqe-subtitle {
    margin: 0;
    font-size: 12.5px;
    color: var(--color-text-muted);
}

.aqe-head-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
}

.aqe-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    background: var(--color-bg);
    color: var(--color-text-muted);
    cursor: pointer;
    text-decoration: none;
}

.aqe-icon-btn:hover {
    color: var(--color-text);
    border-color: var(--color-text-muted);
}

.aqe-body {
    padding: 20px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.aqe-two-col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.aqe-divider {
    height: 1px;
    background: var(--color-border);
}

.aqe-properties {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.aqe-properties-title {
    display: block;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-muted);
    margin-bottom: 4px;
}

.aqe-contract-hint {
    margin: 0 0 4px;
    padding: 8px 10px;
    background: var(--color-accent-soft, rgba(59, 130, 246, .08));
    border-radius: 6px;
    font-size: 12px;
    line-height: 1.4;
    color: var(--color-text-muted);
}

.aqe-property-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 13px;
    color: var(--color-text);
}

.aqe-property-row > span:first-child {
    color: var(--color-text-muted);
}

.aqe-property-row--strong {
    font-weight: 700;
}

.aqe-footer {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    padding: 16px 20px;
    border-top: 1px solid var(--color-border);
}

.aqe-field label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    margin-bottom: 6px;
}

.aqe-field.is-hidden {
    display: none;
}

.aqe-duration-row {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.aqe-duration-row input[type="number"] {
    width: 64px;
}

.aqe-duration-row span {
    font-size: 12px;
    color: var(--color-text-muted);
}

.aqe-toggle-row {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}

.aqe-toggle-row .btn.is-active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: #fff;
}

.aqe-reason-row {
    display: flex;
    gap: 8px;
    margin-top: 10px;
    flex-wrap: wrap;
}

.aqe-reason-row select {
    flex: 1 1 160px;
}

@media (max-width: 767px) {
    .aqe-drawer {
        width: 100vw;
        max-width: 100vw;
    }

    .aqe-two-col {
        grid-template-columns: 1fr;
    }
}

/* ============================================================================
   Profil/Einstellungen-Trennung: Avatar-Upload-Dropzone + Benachrichtigungs-Matrix
   ============================================================================ */
.avatar-dropzone {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 16px;
    border: 2px dashed var(--color-border);
    border-radius: 12px;
    cursor: pointer;
    transition: border-color 150ms ease, background 150ms ease;
}

.avatar-dropzone:hover,
.avatar-dropzone:focus-visible {
    border-color: var(--color-accent);
    background: var(--color-accent-tint);
    outline: none;
}

.avatar-dropzone.is-dragover {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
}

.avatar-dropzone-preview {
    flex-shrink: 0;
    width: 56px;
    height: 56px;
}

.avatar-dropzone-img {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    object-fit: cover;
}

.avatar-dropzone-copy {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 13px;
}

.notification-matrix td:last-child {
    width: 80px;
    text-align: center;
}

.notification-matrix-row--locked td {
    color: var(--color-text-muted);
}

/* Serverseitig ohnehin erzwungen (siehe App\Support\NotificationPreferences::SECURITY_WARNING) -
   disabled-Optik hier nur zur Bestaetigung, dass der Schalter bewusst nicht abwaehlbar ist. */
.toggle-switch input:disabled ~ .toggle-switch-track {
    opacity: 0.6;
    cursor: not-allowed;
}

.toggle-switch:has(input:disabled) {
    cursor: not-allowed;
}

/* "Neues Ticket"-Split-Button-Dropdown (Ticket-Vorlagen-Auswahl, siehe
   partials/_new_ticket_dropdown.twig) - oeffnet/schliesst rein per :focus-within, kein JS noetig.
   Nutzer-Fund 2026-08-17: wirkte "zersplittert" - Haupt-Button und Pfeil-Block hatten je eigene
   Rundung auf ALLEN vier Ecken (nur ein 1px-Nahtrand dazwischen ueberlappt, kein echter Merge),
   dazu einen fix auf weiss-transparent gesetzten Trennstrich-Farbton, der gegen einen neutralen
   .btn (helles/weisses Nicht-Primary) praktisch unsichtbar bzw. wie ein Farbbruch wirkte - und ein
   nicht gestylter nativer Fokusring auf dem <button>, der als eigenstaendiger "abgeschnittener"
   Farbblock am rechten Rand auffiel. Jetzt eine echte nahtlose Pille (aussen abgerundet, Naht
   innen komplett gerade) mit currentColor-basiertem Trennstrich, der sich automatisch an Primary-
   (weisser Text) UND Neutral-Varianten (dunkler Text) des Haupt-Buttons anpasst, plus ein
   dezenter, markenfarbiger Fokus-Ring statt des Browser-Standards. */
.btn-group-dropdown {
    position: relative;
    display: inline-flex;
}

.btn-group-dropdown > a.btn,
.btn-group-dropdown > .btn-dropdown-toggle {
    border-radius: var(--radius-sm);
}

.btn-group-dropdown > a.btn {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.btn-group-dropdown .btn-dropdown-toggle {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    border-left-color: color-mix(in srgb, currentColor 25%, transparent);
    padding-left: 8px;
    padding-right: 10px;
    margin-left: -1px;
}

.btn-group-dropdown .btn-dropdown-toggle:focus-visible {
    z-index: 1;
    outline: none;
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 30%, transparent);
}

.btn-group-dropdown .dropdown-menu {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 260px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    /* Bewusst ein eigener, kraeftigerer Schatten statt --shadow-card (das ist fuer flache
       Seiten-Karten gedacht, hier zu subtil fuer ein schwebendes Popover) - gleiche Groessenordnung
       wie .modal-box, das schon die etablierte "schwebende Flaeche"-Optik der App traegt. */
    box-shadow: 0 10px 25px -5px rgba(15, 23, 42, 0.15), 0 8px 10px -6px rgba(15, 23, 42, 0.08);
    padding: 6px;
    z-index: 50;
}

.btn-group-dropdown:focus-within .dropdown-menu {
    display: block;
}

.dropdown-menu a {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: var(--radius-sm);
    font-size: 13px;
    color: var(--color-text);
    text-decoration: none;
    white-space: nowrap;
    transition: background-color .12s ease;
}

.dropdown-menu a:hover {
    background: var(--color-surface-2, var(--color-bg));
}

.dropdown-menu a svg { flex-shrink: 0; color: var(--color-text-muted); }

/* Hervorgehobene Standard-Aktion ("Neues Ticket (leer)") - fetter, markenfarbenes Icon statt
   eines neutralen, damit sie sich von den Vorlagen-Eintraegen darunter abhebt, ohne gleich einen
   vollen Farbhintergrund zu brauchen. */
.dropdown-menu a.dropdown-menu-item-primary {
    font-weight: 700;
    border-bottom: 1px solid var(--color-border);
    border-radius: 0;
    margin-bottom: 4px;
    padding-bottom: 9px;
}
.dropdown-menu a.dropdown-menu-item-primary svg { color: var(--color-accent); }

.dropdown-menu-label {
    padding: 8px 10px 4px;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-subtle, var(--color-text-muted));
}

/* Schwebendes Timer-Widget (siehe public/js/floating-timer.js) - fixiert unten rechts, ueber allen
   Seiteninhalten, damit die laufende Stempeluhr auch ausserhalb des Ticket-Details sichtbar bleibt. */
.floating-timer-widget {
    position: fixed;
    right: 24px;
    bottom: 24px;
    z-index: 90;
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    width: 168px;
    padding: 16px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card), 0 8px 24px rgba(16, 24, 40, 0.12);
}

.floating-timer-header {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    color: var(--color-text-muted);
    font-size: 12px;
    font-weight: 600;
}

.floating-timer-icon {
    display: inline-flex;
    color: var(--color-accent);
    flex-shrink: 0;
}

.floating-timer-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: inherit;
    text-decoration: none;
}

.floating-timer-label:hover {
    text-decoration: underline;
}

.floating-timer-ring {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 112px;
    height: 112px;
    border-radius: 50%;
    border: 4px solid var(--color-border);
}

.floating-timer-widget.is-paused .floating-timer-ring {
    border-color: var(--status-warning);
}

.floating-timer-clock {
    font-variant-numeric: tabular-nums;
    font-size: 15px;
    font-weight: 600;
    color: var(--color-text);
}

.floating-timer-actions {
    display: flex;
    gap: 10px;
}

.floating-timer-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

.floating-timer-btn:hover {
    background: var(--color-accent-glow);
}

.floating-timer-btn--stop {
    background: var(--color-danger-bg-strong);
    color: var(--color-danger-text);
}

/* Mobil: die volle 168px-Karte mit grossem Ring wuerde sowohl die globale Leistung-FAB als auch
   den neuen Foto-FAB (beide unten rechts, siehe .mobile-fab/.photo-capture-fab-wrap) verdecken.
   Nutzer wollte das Widget ausdruecklich behalten ("sehr nuetzlich"), nur eine andere mobile
   Darstellung - daher hier keine Entfernung, sondern eine schlanke horizontale Pille unten links
   (Gegenseite zu den FABs), der Ring wird zu einer einfachen Pille ohne Kreisrahmen, damit die
   Uhrzeit bei kleinerer Schrift nicht mehr abgeschnitten wird. */
@media (max-width: 1023px) {
    .floating-timer-widget {
        left: 16px;
        right: auto;
        bottom: 76px;
        flex-direction: row;
        align-items: center;
        gap: 8px;
        width: auto;
        max-width: calc(100vw - 32px);
        padding: 8px 10px;
    }

    .floating-timer-header {
        width: auto;
    }

    .floating-timer-label {
        max-width: 64px;
    }

    .floating-timer-ring {
        width: auto;
        height: auto;
        border: none;
        border-radius: 999px;
        padding: 6px 10px;
        background: var(--color-accent-soft);
    }

    .floating-timer-widget.is-paused .floating-timer-ring {
        background: var(--status-warning-soft);
    }

    .floating-timer-widget.is-paused .floating-timer-clock {
        color: var(--status-warning);
    }

    .floating-timer-clock {
        font-size: 13px;
    }

    .floating-timer-actions {
        gap: 6px;
    }

    .floating-timer-btn {
        width: 34px;
        height: 34px;
    }

    .floating-timer-btn svg,
    .floating-timer-btn .icon {
        width: 16px;
        height: 16px;
    }
}

/* ==========================================================================
   HR-Akte, Reiter "Abwesenheit": KPI-Karten + Vorjahresvergleich + Heatmap
   ========================================================================== */

.hr-absence-kpi-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 12px;
    margin: 14px 0;
}

@media (max-width: 760px) {
    .hr-absence-kpi-row { grid-template-columns: repeat(2, 1fr); }
}

.hr-absence-kpi-card {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 13px 15px;
}

.hr-absence-kpi-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

.hr-absence-kpi-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}

.hr-absence-kpi-value {
    font-size: 22px;
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    margin-top: 4px;
}

.hr-absence-kpi-compare {
    font-size: 11.5px;
    color: var(--color-text-muted);
    margin-top: 4px;
}

.hr-absence-section-title {
    font-size: 13.5px;
    font-weight: 700;
    margin: 22px 0 10px;
}

.hr-yoy-legend {
    display: flex;
    gap: 16px;
    font-size: 12px;
    color: var(--color-text-muted);
    margin-bottom: 8px;
}

.hr-yoy-legend span,
.hr-absence-heatmap-legend span {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.hr-yoy-legend i,
.hr-absence-heatmap-legend i {
    width: 10px;
    height: 10px;
    border-radius: 3px;
    display: inline-block;
}

.hr-yoy-chart {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: 6px;
    align-items: end;
    height: 100px;
    overflow-x: auto;
}

.hr-yoy-month {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    height: 100%;
    justify-content: flex-end;
    min-width: 22px;
}

.hr-yoy-bars {
    display: flex;
    gap: 2px;
    align-items: flex-end;
    height: 78px;
}

.hr-yoy-bars i {
    width: 7px;
    border-radius: 2px 2px 0 0;
    display: block;
}

.hr-yoy-month label {
    font-size: 10px;
    color: var(--color-text-muted);
}

.hr-absence-heatmap-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-bg);
    padding: 16px;
    margin-top: 8px;
}

.hr-absence-heatmap-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 10px;
}

.hr-absence-heatmap-head .hr-absence-section-title {
    margin: 0;
}

.hr-absence-heatmap-note {
    font-size: 11.5px;
    color: var(--color-text-muted);
}

.hr-absence-heatmap-scroll {
    overflow-x: auto;
    padding-bottom: 4px;
}

#hr-absence-heatmap-months {
    display: flex;
    font-size: 10.5px;
    color: var(--color-text-muted);
    margin-bottom: 5px;
    min-width: 700px;
    justify-content: space-between;
    padding-left: 2px;
}

#hr-absence-heatmap-grid {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(7, 12px);
    grid-auto-columns: 12px;
    gap: 3px;
    min-width: 700px;
}

.hr-absence-heat-cell {
    width: 12px;
    height: 12px;
    border-radius: 3px;
    background: var(--color-surface);
    box-shadow: inset 0 0 0 1px var(--color-border);
}

.hr-absence-heatmap-legend {
    display: flex;
    gap: 16px;
    margin-top: 12px;
    font-size: 11.5px;
    color: var(--color-text-muted);
    flex-wrap: wrap;
}

/* CoreDesk FlowBuilder (Phase 2) - Flow-Kategorie- und Status-Badges auf der Listenansicht. */
.badge--draft { background: var(--status-neutral-soft); color: var(--status-neutral); }
.badge--draft .badge-dot { background: var(--status-neutral); }

.badge--category_escalation { background: var(--color-critical-soft); color: var(--color-critical); }
.badge--category_automation { background: var(--color-accent-soft); color: var(--color-accent); }
.badge--category_vip { background: var(--status-hold-soft); color: var(--status-hold); }

/* Editor: Palette + Node-Stack + Inspector als 3-Spalten-Grid, faellt auf schmalen Screens
   untereinander (kein eigenes Breakpoint-System noetig, Grid uebernimmt das automatisch). */
.flowbuilder-editor-grid {
    display: grid;
    grid-template-columns: 200px 1fr 320px;
    gap: 16px;
    align-items: start;
}

@media (max-width: 900px) {
    .flowbuilder-editor-grid {
        grid-template-columns: 1fr;
    }
}

.flowbuilder-palette {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.flowbuilder-palette h3,
.flowbuilder-node-stack-card h3,
.flowbuilder-inspector-card h3 {
    margin-top: 0;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

.flowbuilder-palette-btn {
    justify-content: flex-start;
    gap: 8px;
}

.flowbuilder-node-stack {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-height: 80px;
}

.flowbuilder-sim-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 12px;
    margin: 16px 0;
}

.flowbuilder-sim-stat {
    background: var(--color-surface-alt, var(--color-bg));
    border: 1px solid var(--color-border);
    border-radius: 10px;
    padding: 12px 14px;
    text-align: center;
}

.flowbuilder-sim-stat-value {
    font-size: 24px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.flowbuilder-sim-stat-label {
    font-size: 12px;
    color: var(--color-text-muted);
    margin-top: 4px;
}

.flowbuilder-sim-samples-wrap h4 {
    margin: 16px 0 8px;
    font-size: 13px;
    color: var(--color-text-muted);
}

.flowbuilder-validation-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 700;
    padding: 6px 12px;
    border-radius: 999px;
    border: 1px solid transparent;
    cursor: pointer;
    position: relative;
    margin-left: auto;
}

.flowbuilder-validation-chip.is-ok {
    background: var(--status-active-soft);
    color: var(--status-active);
    border-color: var(--status-active-soft);
}

.flowbuilder-validation-chip.is-warning {
    background: var(--status-warning-soft);
    color: #A66A1E;
    border-color: var(--status-warning-soft);
}

.flowbuilder-validation-chip.is-error {
    background: var(--color-danger-bg);
    color: var(--color-danger-text);
    border-color: var(--color-danger-border);
}

.flowbuilder-validation-pop {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: 300px;
    background: var(--color-surface, #fff);
    border: 1px solid var(--color-border);
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    padding: 12px 14px;
    font-size: 12px;
    font-weight: 400;
    color: var(--color-text-muted);
    z-index: 40;
    display: none;
    text-align: left;
}

.flowbuilder-validation-pop.show {
    display: block;
}

.flowbuilder-validation-pop .vp-title {
    font-weight: 800;
    color: var(--color-text);
    margin-bottom: 8px;
}

.flowbuilder-validation-pop .vp-item {
    display: flex;
    gap: 7px;
    padding: 4px 0;
    align-items: flex-start;
}

.flowbuilder-validation-pop .vp-item--error {
    color: var(--color-danger-text);
}

.flowbuilder-validation-pop .vp-item--warning {
    color: #A66A1E;
}

.flowbuilder-validation-pop .vp-item--ok {
    color: var(--status-active);
}

.flowbuilder-history-list {
    max-height: 60vh;
    overflow-y: auto;
}

.history-item {
    display: flex;
    gap: 12px;
    padding: 12px 0;
    border-bottom: 1px solid var(--color-border);
}

.history-item:last-child {
    border-bottom: none;
}

.history-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--color-border);
    margin-top: 5px;
    flex: 0 0 auto;
}

.history-item.current .history-dot {
    background: var(--color-accent);
}

.history-time {
    font-size: 11px;
    color: var(--color-text-muted);
    font-weight: 700;
}

.history-who {
    font-size: 12.5px;
    font-weight: 700;
    margin-top: 1px;
}

.history-diff {
    font-size: 11.5px;
    color: var(--color-text-muted);
    margin: 3px 0 0;
    padding-left: 16px;
}

.history-restore {
    font-size: 11px;
    font-weight: 700;
    color: var(--color-accent);
    margin-top: 5px;
    display: inline-block;
    cursor: pointer;
}

/* Admin > Roadmap: reine Anzeige-Checkliste, keine Checkbox-Interaktion (siehe RoadmapController). */
.roadmap-list {
    display: flex;
    flex-direction: column;
}

.roadmap-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 0;
    border-bottom: 1px solid var(--color-border);
}

.roadmap-item:last-child {
    border-bottom: none;
}

.roadmap-item-dot {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    margin-top: 1px;
}

.roadmap-item-dot.is-done {
    background: var(--status-active-soft);
    color: var(--status-active);
}

.roadmap-item-dot.is-planned {
    border: 2px dashed var(--color-border);
    color: var(--color-text-muted);
}

.roadmap-item-body {
    flex: 1;
}

.roadmap-item-title {
    font-weight: 700;
    font-size: 13.5px;
}

.roadmap-item-desc {
    font-size: 12.5px;
    color: var(--color-text-muted);
    margin-top: 2px;
    max-width: 70ch;
}

.roadmap-item-target {
    font-size: 11px;
    font-weight: 700;
    padding: 3px 10px;
    border-radius: 6px;
    white-space: nowrap;
    flex: 0 0 auto;
}

.roadmap-item-target.is-done {
    background: var(--status-active-soft);
    color: var(--status-active);
}

.roadmap-item-target.is-planned {
    background: var(--status-hold-soft);
    color: var(--status-hold);
}

.roadmap-item-dot.is-blocked {
    background: var(--status-warning-soft);
    color: var(--status-warning);
}

.roadmap-item-target.is-blocked {
    background: var(--status-warning-soft);
    color: var(--status-warning);
}

.history-restore:hover {
    text-decoration: underline;
}

/* ===================================================================================
   FlowBuilder Editor Redesign (Phase 4 Nacharbeit) - visuelle Angleichung an den
   verbindlichen Entwurf (~/Downloads/coredesk-flowbuilder-entwurf.html): sequenzieller
   Node-Stack mit Verbindungslinien, UND/ODER-Badges zwischen Bedingungen und ein
   zweispaltiger Verzweigungs-Track. Ausschliesslich bestehende app.css-Tokens, keine
   eigene Farbpalette. Die Knotenkarten-Markup wird komplett von flowbuilder-editor.js
   erzeugt (siehe renderNodeStack()/nodeHtml()), diese Klassen sind der Gegenpart dazu.
   =================================================================================== */

.flowbuilder-canvas {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
    min-height: 120px;
    padding: 20px 12px 28px;
    border-radius: var(--radius-lg);
    background-image: radial-gradient(circle, var(--color-border) 1px, transparent 1px);
    background-size: 18px 18px;
}

.flowbuilder-connector {
    width: 2px;
    height: 22px;
    background: var(--color-border);
    flex: 0 0 auto;
}

.flowbuilder-link-badge {
    align-self: center;
    font-size: 10px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: #fff;
    padding: 3px 11px;
    border-radius: 999px;
    margin: 2px 0;
    flex: 0 0 auto;
}

.flowbuilder-link-badge.is-and {
    background: var(--status-hold);
}

.flowbuilder-link-badge.is-or {
    background: var(--color-accent);
}

.flowbuilder-negate-badge {
    font-size: 9.5px;
    font-weight: 800;
    color: #fff;
    background: var(--color-critical);
    padding: 1px 7px;
    border-radius: 6px;
    margin-left: 6px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    flex: 0 0 auto;
}

.flowbuilder-node {
    width: 100%;
    max-width: 420px;
    background: var(--color-surface);
    border: 1.5px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 12px 14px;
    box-shadow: var(--shadow-card);
    cursor: pointer;
    position: relative;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    flex: 0 0 auto;
}

.flowbuilder-node:hover {
    border-color: var(--color-accent);
}

.flowbuilder-node.is-selected {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}

.flowbuilder-node-head {
    display: flex;
    align-items: center;
    gap: 10px;
}

.flowbuilder-node-icon {
    width: 30px;
    height: 30px;
    border-radius: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.flowbuilder-node-icon svg {
    width: 15px;
    height: 15px;
}

.flowbuilder-node-copy {
    min-width: 0;
}

.flowbuilder-node-kicker {
    font-size: 9.5px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
}

.flowbuilder-node-title {
    font-size: 13px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--color-text);
    overflow-wrap: anywhere;
}

.flowbuilder-node[data-node-type="condition"] .flowbuilder-node-icon { background: var(--status-hold-soft); color: var(--status-hold); }
.flowbuilder-node[data-node-type="branch"] .flowbuilder-node-icon { background: var(--color-accent-soft); color: var(--color-accent); }
.flowbuilder-node[data-node-type="wait"] .flowbuilder-node-icon { background: var(--status-callback-soft); color: var(--status-callback); }
.flowbuilder-node[data-node-type="action"] .flowbuilder-node-icon { background: var(--status-active-soft); color: var(--status-active); }

.flowbuilder-branch-track {
    display: flex;
    gap: 20px;
    width: 100%;
    max-width: 460px;
    justify-content: center;
    margin-top: 2px;
    align-items: flex-start;
}

.flowbuilder-branch-col {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.flowbuilder-branch-label {
    font-size: 10.5px;
    font-weight: 800;
    padding: 4px 12px;
    border-radius: 999px;
    margin-bottom: 10px;
    text-align: center;
}

.flowbuilder-branch-label.side-a { background: var(--status-active-soft); color: var(--status-active); }
.flowbuilder-branch-label.side-b { background: var(--color-danger-bg); color: var(--color-danger-text); }

.flowbuilder-branch-col .flowbuilder-node { max-width: 100%; }

.flowbuilder-end-cap {
    font-size: 11px;
    color: var(--color-text-muted);
    font-weight: 700;
    margin-top: 8px;
    text-align: center;
}

.flowbuilder-branch-empty-hint {
    font-size: 11.5px;
    color: var(--color-text-muted);
    text-align: center;
    padding: 12px;
    border: 1.5px dashed var(--color-border);
    border-radius: var(--radius-md);
    width: 100%;
}

/* Palette: Icon-Box + zweizeiliges Label statt schlichtem Icon-Button. */
.flowbuilder-palette-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 11px;
    border-radius: var(--radius-md);
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    margin-bottom: 8px;
    cursor: pointer;
    width: 100%;
    text-align: left;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.flowbuilder-palette-item:hover {
    border-color: var(--color-accent);
    box-shadow: var(--shadow-card);
}

.flowbuilder-palette-icon {
    width: 28px;
    height: 28px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.flowbuilder-palette-icon svg { width: 14px; height: 14px; }
.flowbuilder-palette-icon[data-type="condition"] { background: var(--status-hold-soft); color: var(--status-hold); }
.flowbuilder-palette-icon[data-type="branch"] { background: var(--color-accent-soft); color: var(--color-accent); }
.flowbuilder-palette-icon[data-type="wait"] { background: var(--status-callback-soft); color: var(--status-callback); }
.flowbuilder-palette-icon[data-type="action"] { background: var(--status-active-soft); color: var(--status-active); }

.flowbuilder-palette-label {
    font-size: 12.5px;
    font-weight: 700;
    display: flex;
    flex-direction: column;
    color: var(--color-text);
}

.flowbuilder-palette-sub {
    font-size: 10.5px;
    color: var(--color-text-muted);
    font-weight: 500;
}

/* Inspector: Icon+Titel-Kopf oberhalb der bestehenden .field-Formularelemente. */
.flowbuilder-insp-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--color-border);
}

.flowbuilder-insp-head .flowbuilder-node-icon {
    width: 34px;
    height: 34px;
}

.flowbuilder-insp-head .flowbuilder-node-icon[data-node-type="condition"] { background: var(--status-hold-soft); color: var(--status-hold); }
.flowbuilder-insp-head .flowbuilder-node-icon[data-node-type="branch"] { background: var(--color-accent-soft); color: var(--color-accent); }
.flowbuilder-insp-head .flowbuilder-node-icon[data-node-type="wait"] { background: var(--status-callback-soft); color: var(--status-callback); }
.flowbuilder-insp-head .flowbuilder-node-icon[data-node-type="action"] { background: var(--status-active-soft); color: var(--status-active); }

.flowbuilder-insp-head .flowbuilder-insp-title {
    margin: 0;
    font-size: 14px;
    font-weight: 800;
    color: var(--color-text);
}

.flowbuilder-insp-type {
    font-size: 11px;
    color: var(--color-text-muted);
}


/* ============================================================================================
   CoreDesk Ticket-Hub-Board (siehe coredesk-ticket-hub-board-entwurf.html) - neuer Standard
   unter /tickets, zwei gleich breite Panels ("Meine Tickets" / "Ready to Pick"). Bewusst NICHT
   Teil von Ticket-Ansicht 2.0 (kein --tdv2-*), nutzt dieselben globalen Tokens wie der Rest des
   Hub-Dashboards (--status-* / --color-critical / --radius-* / --shadow-card).
   ============================================================================================ */
.hub-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.hub-all-link { margin-left: auto; font-size: 12.5px; font-weight: 700; color: var(--color-text-muted); display: flex; align-items: center; gap: 5px; white-space: nowrap; }
.hub-all-link:hover { color: var(--color-accent); }
.hub-all-link svg { width: 13px; height: 13px; }

.hub-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
@media (max-width: 1024px) {
    .hub-grid { grid-template-columns: 1fr; }
    .hub-panel[data-panel="pick"] { display: none; }
    body.hub-tab-pick .hub-panel[data-panel="mine"] { display: none; }
    body.hub-tab-pick .hub-panel[data-panel="pick"] { display: block; }
}

.hub-panel {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* "Randloses" Panel fuer die UniFi Filter-Listenansicht (nur aktiv im Liste-Modus, siehe
   ticket-hub-board.js syncSeamlessPanel()) - analog zu .ticket-toolbar oben: negatives Margin +
   kompensierendes Padding heben nur das horizontale .content-Padding (24px 28px) auf, damit
   Filter-Sidebar/Tabelle randlos bis an Sidebar/Topbar heranreichen, wie im UniFi-Vorbild.
   Bewusst nur horizontal (nicht wie body.page-seamless-split auch vertikal/100vh) - Galerie/
   Focus View behalten ihr normales Seiten-Scrollen mit Nachlade-Pagination (siehe
   .hub-load-sentinel weiter unten), ein zweiter Scrollbalken durch eine erzwungene
   Panel-Fixhoehe waere hier unerwuenscht. */
.hub-panel--seamless {
    margin: 0 -28px;
    border-left: none;
    border-right: none;
    border-radius: 0;
    box-shadow: none;
}
.hub-panel--seamless .hub-panel-head,
.hub-panel--seamless .hub-legend-box {
    padding-left: 28px;
    padding-right: 28px;
}
.hub-panel--seamless .entra-filter-bar {
    padding-left: 28px;
    padding-right: 28px;
}
.hub-panel--seamless .unifi-filter-table-wrap {
    padding-right: 28px;
    padding-left: 28px;
}
@media (max-width: 767px) {
    .hub-panel--seamless { margin: 0 -12px; }
    .hub-panel--seamless .hub-panel-head,
    .hub-panel--seamless .hub-legend-box { padding-left: 12px; padding-right: 12px; }
    .hub-panel--seamless .entra-filter-bar { padding-left: 12px; padding-right: 12px; }
    .hub-panel--seamless .unifi-filter-table-wrap { padding-left: 12px; padding-right: 12px; }
}
.hub-panel-head { padding: 16px 18px; border-bottom: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.hub-panel-head-title { display: flex; align-items: center; gap: 9px; }
.hub-panel-head-title svg { width: 17px; height: 17px; color: var(--color-text-subtle); }
.hub-panel-head-title h2 { font-size: 14.5px; font-weight: 800; margin: 0; }
.hub-count-badge { background: var(--color-surface-2); color: var(--color-text-muted); font-size: 11.5px; font-weight: 800; padding: 2px 9px; border-radius: 999px; }
.hub-count-badge--brand { background: var(--color-accent-soft); color: var(--color-accent); }
.hub-ph-right { display: flex; align-items: center; gap: 8px; }
.hub-ph-hint { font-size: 11px; color: var(--color-text-subtle); display: flex; align-items: center; gap: 5px; }
.hub-ph-hint svg { width: 12px; height: 12px; }
.hub-toggle-btn { display: flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 700; color: var(--color-text-muted); border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 6px 11px; background: var(--color-surface); min-height: 32px; }
.hub-toggle-btn svg { width: 13px; height: 13px; }
.hub-toggle-btn.active { color: var(--color-accent); border-color: var(--color-accent); background: var(--color-accent-soft); }

/* Liste/Galerie-Segmentgruppe (Ticket Hub) - zwei .hub-toggle-btn ohne Zwischenraum unter
   gemeinsamem abgerundetem Rahmen, wie ein UniFi-Segmented-Control. */
.hub-view-toggle { display: flex; border: 1px solid var(--color-border); border-radius: var(--radius-sm); overflow: hidden; }
.hub-view-toggle .hub-toggle-btn { border: none; border-radius: 0; }
.hub-view-toggle .hub-toggle-btn:not(:last-child) { border-right: 1px solid var(--color-border); }

/* ---- UniFi Filter-Listenansicht (siehe UI-Design-Styleguide Abschnitt 18) - Entra-Style
   Filter-Bar ueber einer sortierbaren Tabelle, Standard-Ansicht des Ticket Hub. Die vormalige
   linke Checkbox-Sidebar (Status/Prioritaet) wurde durch die horizontale Filter-Bar ersetzt -
   Status/Prioritaet leben jetzt im "Mehr Filter"-Popover, siehe .entra-more-filters. ---- */
.unifi-filter-list { display: flex; flex-direction: column; gap: 0; }
.unifi-filter-group-title { font-size: 10.5px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-text-subtle); margin-bottom: 6px; }
.unifi-filter-option { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--color-text-muted); padding: 3px 0; cursor: pointer; }
.unifi-filter-option span:first-of-type { flex: 1 1 auto; }
.unifi-filter-count { font-size: 11px; font-weight: 700; color: var(--color-text-subtle); }
.unifi-filter-table-wrap { overflow-x: auto; }
table.unifi-filter-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 12.5px; }
.unifi-filter-table th { text-align: left; font-size: 10.5px; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-subtle); padding: 8px 10px; border-bottom: 1px solid var(--color-border); cursor: pointer; user-select: none; white-space: nowrap; }
.unifi-filter-table th.is-sorted { color: var(--color-accent); }
.unifi-filter-table td { padding: 9px 10px; border-bottom: 1px solid var(--color-border); vertical-align: middle; white-space: nowrap; }
.unifi-filter-row { cursor: pointer; }
.unifi-filter-row:hover { background: var(--color-surface-2); }

/* Sticky erste Spalten (Status-Punkt/Checkbox + Titel) analog Ubiquiti UniFi Geraeteliste - beim
   horizontalen Scrollen (siehe .unifi-filter-table-wrap oben) bleiben sie links verankert,
   waehrend die uebrigen Spalten darunter durchscrollen. Nutzer-Klarstellung 2026-08-01: ".unifi-
   filter-table darf nicht starr von einer 34px-Checkbox-Spalte abhaengen" - deshalb bewusst als
   Opt-in-Modifier (.unifi-filter-table--sticky-cols) statt direkt an die Basis-Klasse gekoppelt.
   Tabellen mit echter schmaler Fuehrungsspalte (Checkbox/Status-Punkt, z.B. Ticket Hub, Historie &
   Berichte, Leistungsfreigabe) setzen zusaetzlich diesen Modifier; Tabellen ohne eine solche
   Spalte (z.B. Bereit zur Abrechnung, Projekt-Controlling, Freigabe-Historie: Betreff/Datum direkt
   in Spalte 1) bleiben bei der reinen Basis-Optik (Kopfzeile/Rahmen/Hover) ohne die feste 34px-
   Breite, die dort sonst den Zellinhalt abschneiden wuerde. Die Snippet-Zeile (colspan=7, siehe
   renderUnifiRow() in ticket-hub-board.js) ist bewusst ausgenommen - eine so breite Zelle sticky
   zu setzen wuerde sie beim Scrollen komplett im Viewport fixieren statt nur den linken Rand.
   border-collapse:separate (statt collapse, siehe table.unifi-filter-table oben) ist fuer
   zuverlaessiges Sticky-Rendering noetig. */
.unifi-filter-table--sticky-cols th:nth-child(1),
.unifi-filter-table--sticky-cols tbody tr:not(.unifi-filter-row--snippet) td:nth-child(1) {
    position: sticky;
    left: 0;
    z-index: 2;
    width: 34px;
    min-width: 34px;
    max-width: 34px;
    background: var(--color-surface);
}
.unifi-filter-table--sticky-cols th:nth-child(2),
.unifi-filter-table--sticky-cols tbody tr:not(.unifi-filter-row--snippet) td:nth-child(2) {
    position: sticky;
    left: 34px;
    z-index: 2;
    min-width: 220px;
    background: var(--color-surface);
    box-shadow: 2px 0 4px -2px rgba(0, 0, 0, 0.15);
}
.unifi-filter-table--sticky-cols thead th:nth-child(1),
.unifi-filter-table--sticky-cols thead th:nth-child(2) { z-index: 3; }
.unifi-filter-table--sticky-cols .unifi-filter-row:hover td:nth-child(1),
.unifi-filter-table--sticky-cols .unifi-filter-row:hover td:nth-child(2) { background: var(--color-surface-2); }
.unifi-cell-muted { color: var(--color-text-subtle); font-style: italic; }

/* ---- Entra-Style Filter Bar (siehe ticket-hub-board.js wireEntraFilterBar()) - horizontale
   Zeitfilter-Pillrow + Suchbar/Dropdown-Zeile + Aktive-Filter-Chips, ersetzt die vormalige linke
   Checkbox-Sidebar als primaeres Filter-UI im Ticket Hub. ---- */
.entra-filter-bar { display: flex; flex-direction: column; gap: 10px; padding: 14px 4px 16px; border-bottom: 1px solid var(--color-border); margin-bottom: 4px; }
.entra-range-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; position: relative; }
.entra-pill { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 700; color: var(--color-text-muted); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 999px; padding: 5px 13px; cursor: pointer; white-space: nowrap; }
.entra-pill:hover { border-color: var(--color-accent); color: var(--color-text); }
.entra-pill.active { color: var(--color-accent); border-color: var(--color-accent); background: var(--color-accent-soft); }
.entra-pill--custom { position: relative; }
.entra-custom-range-popover { position: absolute; top: calc(100% + 6px); left: 0; z-index: 40; display: flex; flex-direction: column; gap: 8px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-sm); box-shadow: var(--shadow-md, 0 8px 24px rgba(0,0,0,0.12)); padding: 12px; min-width: 220px; }
.entra-custom-range-popover label { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; font-weight: 700; color: var(--color-text-subtle); }
.entra-custom-range-popover input[type="date"] { font-size: 12.5px; padding: 6px 8px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text); }

.entra-bar-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.entra-search-wrap { flex: 1 1 220px; min-width: 180px; }
/* Blauer Rahmen + Hoehe an .hub-toggle-btn angeglichen (Nutzer-Feedback 2026-08-02) - Suchfeld soll
   optisch als aktiver Aktionsbereich hervorstechen, nicht als neutrales Formularfeld wirken. */
.entra-search-input { width: 100%; min-height: 32px; padding: 7px 11px; font-size: 12.5px; border: 1px solid var(--color-accent); border-radius: var(--radius-sm); background: var(--color-surface); color: var(--color-text); }
.entra-search-input::placeholder { color: var(--color-text-subtle); }

/* Toolbar-Suchfeld im Ticket Hub (siehe templates/tickets/hub.twig) mittig im rechten Toolbar-
   Bereich statt am Rand geklebt, "Focus View" bleibt als letztes Flex-Kind rechtsbuendig - der Slot
   nimmt per flex:1 den gesamten freien Platz zwischen Sidebar-Toggle und Focus-View-Button ein und
   zentriert das (auf max-width begrenzte) Suchfeld darin. */
.hub-toolbar-search-slot { flex: 1; display: flex; justify-content: center; }
.hub-toolbar-search-slot .entra-search-wrap { flex: 0 1 480px; min-width: 0; }

.entra-dropdown { position: relative; }
.entra-dd-toggle { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: var(--color-text-muted); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 7px 11px; cursor: pointer; white-space: nowrap; }
.entra-dd-toggle:hover { border-color: var(--color-accent); color: var(--color-text); }
.entra-dd-panel { position: absolute; top: calc(100% + 6px); left: 0; z-index: 40; display: flex; flex-direction: column; gap: 2px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-sm); box-shadow: var(--shadow-md, 0 8px 24px rgba(0,0,0,0.12)); padding: 10px; min-width: 220px; max-height: 320px; overflow-y: auto; }
.entra-dd-panel[hidden], .entra-custom-range-popover[hidden] { display: none; }
/* Rechtsbuendig zum eigenen Button statt zum linken Rand des Dropdowns - das "+ Mehr Filter"
   Dropdown sitzt am rechten Ende der Filterleiste, ein linksbuendiges Panel (left:0, Default)
   wuerde also ueber den Viewport-Rand hinausragen. right:0 haelt das Panel innerhalb der Zeile,
   die selbst ohnehin nie bis an den Viewport-Rand reicht (siehe Content-Padding). */
.entra-more-filters .entra-dd-panel { min-width: 240px; left: auto; right: 0; }
.entra-more-section { padding: 4px 0 10px; }
.entra-more-section:not(:last-child) { border-bottom: 1px solid var(--color-border); margin-bottom: 6px; }
.entra-more-title { font-size: 10.5px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; color: var(--color-text-subtle); margin-bottom: 6px; }
.entra-check-option { padding: 4px 2px; }

.entra-clear-all { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 700; color: var(--color-danger, #dc2626); background: none; border: none; padding: 7px 6px; cursor: pointer; margin-left: auto; }
.entra-clear-all:hover { text-decoration: underline; }
.entra-clear-all[hidden] { display: none; }

.entra-chips-row { display: flex; flex-wrap: wrap; gap: 6px; }
.entra-chips-row:empty { display: none; }
.entra-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 700; color: var(--color-accent); background: var(--color-accent-soft); border-radius: 999px; padding: 4px 6px 4px 10px; }
.entra-chip-x { display: inline-flex; align-items: center; justify-content: center; width: 15px; height: 15px; border-radius: 50%; border: none; background: none; color: inherit; font-size: 10px; line-height: 1; cursor: pointer; opacity: 0.75; }
.entra-chip-x svg { width: 10px; height: 10px; }
.entra-chip-x:hover { opacity: 1; background: rgba(0,0,0,0.08); }

/* Eingerueckte Leistungs-Treffer-Zeile unter einem per Tiefensuche gefundenen Ticket (siehe
   renderUnifiRow() in ticket-hub-board.js) - optisch bewusst zurueckhaltend (kleinere Schrift,
   kein Hover-Highlight wie die Hauptzeile), da sie nur einen Kontext-Ausschnitt zeigt, kein
   eigenstaendig anklickbares Element im selben Sinn wie das Ticket selbst. */
.unifi-filter-row--snippet td { padding-top: 0; padding-bottom: 8px; border-top: none; }
.unifi-search-snippet { font-size: 12px; color: var(--color-text-subtle); padding-left: 22px; display: flex; align-items: flex-start; gap: 6px; }
.unifi-search-snippet-icon { flex-shrink: 0; margin-top: 1px; width: 14px; height: 14px; color: var(--color-text-subtle); }
.unifi-search-snippet strong { color: var(--color-text); font-weight: 700; }
.unifi-search-snippet-more { color: var(--color-accent); font-weight: 700; }
.unifi-search-snippet-more:hover { text-decoration: underline; }

@media (max-width: 900px) {
    .entra-dd-panel { left: auto; right: 0; }
}
@media (max-width: 767px) {
    .entra-filter-bar { padding-top: 10px; }
    .entra-range-row { overflow-x: auto; flex-wrap: nowrap; padding-bottom: 2px; }
    .entra-bar-row { flex-direction: column; align-items: stretch; }
    .entra-search-wrap { flex: 1 1 auto; }
    .entra-dropdown, .entra-clear-all { width: 100%; }
    .entra-dd-toggle { width: 100%; justify-content: space-between; }
    .entra-dd-panel { left: 0; right: 0; width: 100%; }
    .entra-clear-all { margin-left: 0; text-align: center; justify-content: center; }
}

/* Meine Tickets/Meine Projekte-Tab-Umschalter im Hub (Nutzer-Wunsch 2026-08-10) - wiederverwendet
   die .modal-tabs/.modal-tab+.billing-module-tab-Kombo 1:1 (siehe _report_mail_modal.twig), reines
   JS-Klassen-/hidden-Umschalten ueber [data-hub-tab]/[data-hub-tab-panel], siehe home.twig-Script
   weiter unten. .hub-panel-tabs setzt nur den Modal-Rand zurueck, da hier keine Modal-Umgebung ist. */
.hub-panel-tabs { margin: 0; border-bottom: none; padding-bottom: 0; }
.hub-tab-panel[hidden] { display: none; }

.hub-mine-toolbar-row {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    margin-bottom: 8px;
}

/* "Meine Tickets"-Verlaufssuche (siehe home.twig #hubMineSearchInput, ticket-hub-board.js
   initMineSearch()) - durchsucht serverseitig auch Kommentare/E-Mails/Leistungsnotizen, nicht
   nur den Betreff, siehe TicketRepository::searchMine(). */
.hub-mine-search-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    margin-bottom: 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text-muted);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.hub-mine-search-row:focus-within {
    border-color: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-accent-soft);
}
.hub-mine-search-row svg { width: 14px; height: 14px; flex: 0 0 auto; }
.hub-mine-search-row input {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    background: transparent;
    font-size: 13px;
    color: var(--color-text);
    outline: none;
}
.hub-mine-search-row input::placeholder { color: var(--color-text-subtle); }
.hub-mine-queue-filter {
    flex: 0 0 auto;
    max-width: 140px;
    border: none;
    border-left: 1px solid var(--color-border);
    background: transparent;
    padding: 0 0 0 8px;
    font-size: 12px;
    color: var(--color-text-muted);
    outline: none;
    cursor: pointer;
}

/* ---- Meine Tickets: Queue-Akkordeons ---- */
.hub-panel-body { padding: 12px 0; }
.hub-dept-group { border-radius: var(--radius-md); padding: 2px; margin-bottom: 8px; }
.hub-dept-head { display: flex; align-items: center; justify-content: space-between; padding: 9px 8px; cursor: pointer; border-radius: var(--radius-sm); min-height: 40px; }
.hub-dept-head:hover { background: var(--color-surface-2); }
.hub-dept-head-left { display: flex; align-items: center; gap: 7px; }
.hub-dept-head-left svg { width: 14px; height: 14px; color: var(--color-text-subtle); }
.hub-dept-name { font-size: 11.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-subtle); }
.hub-dept-count { font-size: 10.5px; color: var(--color-text-subtle); font-weight: 600; }
.hub-dept-chevron { width: 22px; height: 22px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; color: var(--color-text-subtle); transition: transform .2s; flex: 0 0 auto; }
.hub-dept-chevron svg { width: 14px; height: 14px; }
.hub-dept-group.hub-collapsed .hub-dept-chevron { transform: rotate(-90deg); }
.hub-dept-body { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; padding: 6px 4px 8px; }
.hub-dept-group.hub-collapsed .hub-dept-body { display: none; }

.hub-mt-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 11px 12px 11px 14px; position: relative; cursor: pointer; transition: border-color .15s, box-shadow .15s; }
.hub-mt-card:hover { border-color: var(--color-text-subtle); box-shadow: var(--shadow-card); }
.hub-mt-card::before { content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px; border-radius: 0 3px 3px 0; background: var(--color-border); }
.hub-mt-card.hub-sla-danger::before { background: var(--color-critical); }
.hub-mt-card.hub-sla-warning::before { background: var(--status-warning); }
.hub-mt-card.hub-sla-ok::before { background: var(--status-active); }
.hub-mt-card.hub-sla-paused::before { background: var(--status-hold); }
/* SLA-ueberschritten: zusaetzlich zum Akzentstrich der ganze Kartenrahmen rot, damit ueberfaellige
   Tickets auf einen Blick auffallen (Nutzer-Feedback) - auch im Hover-Zustand erhalten bleiben. */
.hub-mt-card.hub-sla-danger { border-color: var(--color-critical); }
.hub-mt-card.hub-sla-danger:hover { border-color: var(--color-critical); box-shadow: 0 0 0 1px var(--color-critical); }
.hub-mt-card-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.hub-mt-title-row { display: flex; align-items: flex-start; gap: 7px; min-width: 0; }
.hub-status-dot-sm { width: 7px; height: 7px; border-radius: 50%; flex: 0 0 auto; margin-top: 5px; }
.hub-mt-title { font-size: 13px; font-weight: 700; line-height: 1.35; color: var(--color-text); }
.hub-mt-card:hover .hub-mt-title { color: var(--color-accent); }
.hub-mt-side { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; flex: 0 0 auto; }
.hub-mt-prio { font-size: 9.5px; font-weight: 800; padding: 2px 7px; border-radius: 5px; text-transform: uppercase; letter-spacing: .03em; white-space: nowrap; }
.hub-mt-prio.priority_urgent { background: var(--color-critical-soft); color: var(--color-critical); }
.hub-mt-prio.priority_high { background: var(--color-danger-bg-strong); color: var(--color-danger-text); }
.hub-mt-prio.priority_normal { background: var(--color-accent-soft); color: var(--color-accent); }
.hub-mt-prio.priority_low { background: var(--status-neutral-soft); color: var(--status-neutral); }
.hub-mt-date { font-size: 10px; font-weight: 700; color: var(--color-text-subtle); }
.hub-mt-created { font-size: 9px; font-weight: 500; color: var(--color-text-subtle); opacity: .8; }
.hub-mt-meta { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--color-text-subtle); margin-top: 5px; margin-left: 14px; flex-wrap: wrap; }
.hub-mt-meta .hub-overdue { color: var(--color-critical); font-weight: 700; display: flex; align-items: center; gap: 3px; }
.hub-mt-meta .hub-overdue svg { width: 10px; height: 10px; }

/* ==================================================================================
   TICKET-CARD-DESIGNS 2-5 (admin-konfigurierbar, siehe Admin > Einstellungen >
   Ticket-Card-Design und coredesk-ticket-card-konzepte-entwurf.html) - Alternativen zum
   Standarddesign (.hub-mt-card oben, farbiger Rand-Strich). Gilt NUR fuer die "Meine
   Tickets"-Karten dieses Panels, nicht fuer Projekt-Kanban oder Ready-to-Pick-Zeilen.
   Gemeinsame Sub-Elemente (Titel/Kunde/Badge-Zeile/Frist-Zeile) sind ueber alle 4 Designs
   identisch benannt, nur die Kartenhuelle unterscheidet sich je Konzept. ================== */

.hub-card-kunde { font-size: 11.5px; color: var(--color-text-subtle); margin-top: 4px; }
.hub-card-badge-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 9px; }
.hub-card-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 700; background: var(--color-surface-2); border: 1px solid var(--color-border); color: var(--color-text-subtle); padding: 2px 8px; border-radius: var(--radius-sm); }
.hub-card-chip svg { width: 10px; height: 10px; }
.hub-card-chip--reply { background: var(--status-active-soft); border-color: transparent; color: var(--status-active); }
.hub-card-chip--collab { background: var(--status-hold-soft); border-color: transparent; color: var(--status-hold); padding: 2px 5px; }
.hub-card-frist-line { display: flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 700; margin-top: 8px; }
.hub-card-frist-line svg { width: 12px; height: 12px; flex: 0 0 auto; }
.hub-card-frist-line--danger { color: var(--color-critical); }
.hub-card-frist-line--warning { color: var(--status-warning); }
.hub-card-frist-line--ok, .hub-card-frist-line--muted { color: var(--color-text-subtle); font-weight: 600; }
.hub-card-frist-line--paused { color: var(--status-hold); }

/* ---- Konzept 1: SLA-Ring ---- */
.hub-card-ring { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 15px 60px 15px 15px; position: relative; cursor: pointer; transition: box-shadow .15s, border-color .15s; }
.hub-card-ring:hover { border-color: var(--color-text-subtle); box-shadow: var(--shadow-card); }
.hub-card-ring-svg { position: absolute; top: 14px; right: 14px; width: 42px; height: 42px; }
.hub-card-ring-track { stroke: var(--status-neutral-soft); }
.hub-card-ring-progress--danger { stroke: var(--color-critical); }
.hub-card-ring-progress--warning { stroke: var(--status-warning); }
.hub-card-ring-progress--ok { stroke: var(--status-active); }
.hub-card-ring-progress--paused { stroke: var(--color-border); }

/* ---- Konzept 2: Ambient Glow ---- */
.hub-card-glow { position: relative; border-radius: var(--radius-md); padding: 15px; overflow: hidden; border: 1px solid var(--color-border); background: var(--color-surface); cursor: pointer; transition: box-shadow .15s, border-color .15s; }
.hub-card-glow:hover { border-color: var(--color-text-subtle); box-shadow: var(--shadow-card); }
.hub-card-glow::before { content: ""; position: absolute; inset: 0; opacity: .4; pointer-events: none; }
.hub-card-glow--danger::before { background: radial-gradient(130% 110% at 0% 0%, var(--color-critical) 0%, transparent 62%); }
.hub-card-glow--warning::before { background: radial-gradient(130% 110% at 0% 0%, var(--status-warning) 0%, transparent 62%); }
.hub-card-glow--ok::before { background: radial-gradient(130% 110% at 0% 0%, var(--status-active) 0%, transparent 62%); }
.hub-card-glow--paused::before { background: radial-gradient(130% 110% at 0% 0%, var(--status-hold) 0%, transparent 62%); }
.hub-card-glow-icon-badge { position: relative; z-index: 1; width: 26px; height: 26px; border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; margin-bottom: 9px; color: #fff; }
.hub-card-glow-icon-badge svg { width: 14px; height: 14px; }
.hub-card-glow-icon-badge--danger { background: var(--color-critical); }
.hub-card-glow-icon-badge--warning { background: var(--status-warning); }
.hub-card-glow-icon-badge--ok { background: var(--status-active); }
.hub-card-glow-icon-badge--paused { background: var(--status-hold); }
.hub-card-glow > *:not(.hub-card-glow-icon-badge) { position: relative; z-index: 1; }

/* ---- Konzept 3: Karteireiter ---- */
.hub-card-tab-wrap { position: relative; margin-top: 15px; }
.hub-card-tab { position: absolute; top: -15px; left: -1px; height: 16px; padding: 0 10px; border-radius: 7px 7px 0 0; font-size: 9px; font-weight: 800; color: #fff; display: flex; align-items: center; letter-spacing: .04em; text-transform: uppercase; white-space: nowrap; }
.hub-card-tab--danger { background: var(--color-critical); }
.hub-card-tab--warning { background: var(--status-warning); }
.hub-card-tab--ok { background: var(--status-active); }
.hub-card-tab--paused { background: var(--status-hold); }
.hub-card-tab-body { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 0 var(--radius-md) var(--radius-md) var(--radius-md); padding: 16px 15px 15px; cursor: pointer; transition: box-shadow .15s, border-color .15s; }
.hub-card-tab-body:hover { border-color: var(--color-text-subtle); box-shadow: var(--shadow-card); }

/* ---- Konzept 4: Zeit-zuerst ---- */
.hub-card-time { display: flex; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; cursor: pointer; transition: box-shadow .15s, border-color .15s; }
.hub-card-time:hover { border-color: var(--color-text-subtle); box-shadow: var(--shadow-card); }
.hub-card-time-block { flex: 0 0 78px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; text-align: center; padding: 10px 5px; gap: 2px; }
.hub-card-time-num { font-size: 24px; font-weight: 800; line-height: 1; }
.hub-card-time-unit { font-size: 8px; font-weight: 800; text-transform: uppercase; letter-spacing: .03em; opacity: .9; line-height: 1.3; }
.hub-card-time-block svg { width: 20px; height: 20px; }
.hub-card-time-block--danger { background: var(--color-critical); }
.hub-card-time-block--warning { background: var(--status-warning); }
.hub-card-time-block--ok { background: var(--status-active); }
.hub-card-time-block--paused { background: var(--status-hold); }
.hub-card-time-body { flex: 1; padding: 13px 14px; min-width: 0; }

/* ---- Flache "Meine Tickets"-Liste (siehe coredesk-meine-tickets-flat-entwurf.html) - ersetzt
   ausschliesslich auf CoreDesk Hub die Queue-Akkordeon-Gruppierung (.hub-dept-group) durch einen
   einzelnen, nach Dringlichkeit sortierten Kartenstrom (ticket-hub-board.js::renderMineFlat()).
   Farbiger Rand-Strich + SLA-Balken teilen sich dieselben Status-Tokens wie alle anderen
   Kartendesigns oben (--color-critical/--status-warning/--status-active/--status-hold). ---- */
.hub-flat-list { display: flex; flex-direction: column; gap: 8px; padding: 8px 0; }
.hub-flat-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 14px 12px 11px 14px; position: relative; cursor: pointer; transition: border-color .15s, box-shadow .15s; }
.hub-flat-card:hover { border-color: #b8c9df; box-shadow: 0 4px 10px rgba(0, 0, 0, 0.04); }
.hub-flat-card-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.hub-flat-card-title { font-size: 13px; font-weight: 700; color: var(--color-text); }
.hub-flat-card:hover .hub-flat-card-title { color: var(--color-accent); }
.hub-flat-card-customer { font-size: 11.5px; color: var(--color-text-subtle); margin-top: 2px; }
/* min-height reserviert exakt den Platz, den Icon+Text sonst einnehmen wuerden - wichtig fuer
   sla_disabled-Tickets, deren Status-Zeile/Balken zwar leer bleiben (siehe renderMineCardFlat()),
   aber trotzdem dieselbe Kartenhoehe wie jede andere Karte in der Liste ergeben muessen. */
.hub-flat-card-sla-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 8px; font-size: 11px; font-weight: 700; min-height: 13px; }
.hub-flat-card-sla-label { display: flex; align-items: center; gap: 5px; }
.hub-flat-card-sla-label svg { width: 13px; height: 13px; }
.hub-flat-card--danger .hub-flat-card-sla-label { color: var(--color-critical); }
.hub-flat-card--warning .hub-flat-card-sla-label { color: var(--status-warning); }
.hub-flat-card--ok .hub-flat-card-sla-label { color: var(--status-active); }
.hub-flat-card--paused .hub-flat-card-sla-label { color: var(--status-hold); }
.hub-flat-card-sla-detail { color: var(--color-text-subtle); font-weight: 600; }
.hub-flat-card--danger .hub-flat-card-sla-detail { color: var(--color-critical); }
.hub-flat-card--warning .hub-flat-card-sla-detail { color: var(--status-warning); }
.hub-flat-card--ok .hub-flat-card-sla-detail { color: var(--status-active); }
.hub-flat-card--paused .hub-flat-card-sla-detail { color: var(--status-hold); }
.hub-flat-card-bar { margin-top: 6px; height: 4px; border-radius: 999px; background: var(--color-surface-2); overflow: hidden; }
/* Reiner Statusfarb-Balken wie im Mockup - immer volle Breite, keine "verstrichene Zeit"-
   Fortschrittsanzeige (die gab es im Entwurf nie, siehe .sla-progress-bar dort: width:100% fix,
   nur die Hintergrundfarbe wechselt je Status). Verhindert auch den Bug, dass ein Ticket ganz ohne
   Faelligkeit (due_date) einen 0%-breiten, unsichtbaren Balken bekam. */
.hub-flat-card-bar-fill { width: 100%; height: 100%; border-radius: 999px; }
.hub-flat-card-bar-fill--danger { background: var(--color-critical); }
.hub-flat-card-bar-fill--warning { background: var(--status-warning); }
.hub-flat-card-bar-fill--ok { background: var(--status-active); }
.hub-flat-card-bar-fill--paused { background: repeating-linear-gradient(135deg, var(--status-hold-soft), var(--status-hold-soft) 4px, transparent 4px, transparent 8px); }
/* sla_disabled=1: Balken komplett unsichtbar (kein Farbfeld), .hub-flat-card-bar (Elternelement,
   4px hoch) bleibt aber im Markup - reserviert also weiterhin ihre Zeile fuer gleiche Kartenhoehe. */
.hub-flat-card-bar-fill--off { background: transparent; }
.hub-flat-card-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.hub-card-chip--dept { background: var(--color-surface-2); color: var(--color-text-subtle); }

/* ---- Flache Karte Zeile 5 (Footer): links bestehende Chips, rechts neue Feature-Gruppe
   (Punkte-Badge + Beobachter-/Termin-Rundindikatoren, siehe coredesk-meine-tickets-sort-entwurf).
   .hub-flat-card-chips traegt hier keinen eigenen margin-top mehr, das uebernimmt der Footer. ---- */
.hub-flat-card-footer { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 9px; }
.hub-flat-card-feature-group { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.hub-flat-card-icon-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; background: var(--color-surface-2); border: 1px solid var(--color-border); color: var(--color-text-subtle); flex: 0 0 auto; }
.hub-flat-card-icon-btn svg { width: 13px; height: 13px; }
.hub-flat-card-icon-btn:hover { color: var(--color-accent); border-color: var(--color-accent); }
/* Termin-Indikator faerbt sein Icon dauerhaft gruen (wie die "Fixer Kundentermin"-Legende im
   Zeitstrahl, --status-active) - ueberschreibt bewusst auch den generischen Hover-Zustand oben,
   damit die Farbe nicht bei Hover kippt. */
.hub-appointment-tooltip-trigger, .hub-appointment-tooltip-trigger:hover { color: var(--status-active); border-color: var(--color-border); }
/* Punkte-Chip: exakt gleiche Hoehe wie die runden Rundindikatoren daneben (24px), aber rechteckig
   mit kleinem Radius statt Pille, Ziffern in gedecktem Dunkelgrau statt Score-Ton-Farbe (siehe
   coredesk-meine-tickets-sort-entwurf .ticket-points-badge) - Compound-Selektor noetig, da die
   Basis-.hub-score-pill-Regel (border-radius:999px, min-width:44px) spaeter im Stylesheet steht
   und bei gleicher Spezifitaet sonst gewinnen wuerde. */
.hub-score-pill.hub-score-pill--compact { min-width: 0; height: 24px; min-height: 24px; padding: 0 8px; gap: 4px; font-size: 10px; border-radius: var(--radius-sm); background: var(--color-surface-2); color: var(--color-text-muted); border-color: var(--color-border); }
.hub-score-pill.hub-score-pill--compact svg { width: 11px; height: 11px; }
.hub-flat-card:hover .hub-score-pill.hub-score-pill--compact { background: var(--color-accent-soft); color: var(--color-accent); border-color: var(--color-accent-glow); }

/* ---- Meine Tickets: Status-/Prio-Legende (Panel-Footer, siehe Entwurf .legend-box) ---- */
.hub-legend-box { padding: 12px 16px; border-top: 1px solid var(--color-border); display: flex; flex-direction: column; gap: 8px; flex: 0 0 auto; }
.hub-legend-row { display: flex; flex-wrap: wrap; gap: 8px 16px; }
.hub-legend-item { display: flex; align-items: center; gap: 5px; font-size: 10.5px; color: var(--color-text-subtle); font-weight: 600; }
.hub-legend-item .status-dot { margin-right: 0; }

/* ---- Ready to Pick: Zeilen ---- */
.hub-rp-list { display: flex; flex-direction: column; gap: 8px; padding: 6px 8px; }

/* Lade-Trigger fuer das Nachladen-beim-Scrollen (IntersectionObserver, siehe ticket-hub-board.js
   attachPagination()/revealMore()) - ersetzt die frühere innere Scrollbox (max-height+overflow-y
   auf .hub-panel-body/.hub-rp-list) auf Nutzer-Wunsch: kein zweiter Scrollbalken mehr, die Karten
   wachsen mit der normalen Seiten-Scrollrichtung und laden automatisch nach. */
.hub-load-sentinel { grid-column: 1 / -1; text-align: center; padding: 14px 0 6px; font-size: 11px; font-weight: 600; color: var(--color-text-subtle); }
.hub-rp-row { display: flex; align-items: center; gap: 14px; padding: 12px 10px; border-radius: var(--radius-md); border: 1px solid var(--color-border); background: var(--color-surface); transition: background .15s, border-color .15s; cursor: grab; }
.hub-rp-row:hover { background: var(--color-surface-2); border-color: var(--color-text-subtle); }
.hub-rp-row:active { cursor: grabbing; }
.hub-rp-row.hub-dragging { opacity: .4; }
/* SLA-ueberschritten: bewusst KEIN roter Rahmen mehr (Nutzer-Feedback: wirkte zu aufdringlich) -
   die Zeile bleibt neutral grau wie jede andere, die "Frist ueberschritten"-Information steckt
   stattdessen ausschliesslich im .hub-rp-thermo-chip weiter unten. */

.hub-score-wrap { position: relative; flex: 0 0 auto; }
.hub-score-pill { border: 1px solid transparent; font-weight: 800; font-size: 12px; padding: 6px 12px; border-radius: 999px; display: flex; align-items: center; justify-content: center; min-width: 44px; min-height: 32px; }
.hub-score-pill.hub-danger { background: var(--color-danger-bg-strong); color: var(--color-danger-text); border-color: var(--color-danger-border); }
.hub-score-pill.hub-warn { background: var(--status-warning-soft); color: var(--status-warning); }
.hub-score-pill.hub-low { background: var(--status-neutral-soft); color: var(--status-neutral); }
.hub-score-popover {
    position: absolute; top: calc(100% + 8px); left: 0; width: 240px;
    background: #101828; color: #fff; padding: 12px 14px; border-radius: var(--radius-md);
    box-shadow: var(--shadow-card); font-size: 11.5px; z-index: 30;
    opacity: 0; pointer-events: none; transform: translateY(-6px); transition: opacity .15s, transform .15s;
}
.hub-score-popover.hub-show { opacity: 1; pointer-events: auto; transform: translateY(0); }
.hub-score-popover.hub-align-right { left: auto; right: 0; }
.hub-score-popover .hub-sp-title { font-weight: 800; border-bottom: 1px solid rgba(255,255,255,.12); padding-bottom: 6px; margin-bottom: 6px; color: #e4e7ec; }
.hub-score-popover .hub-sp-row { display: flex; justify-content: space-between; gap: 10px; padding: 2px 0; color: #c9cdd6; }
.hub-score-popover .hub-sp-row b { color: #fff; white-space: nowrap; }
.hub-score-popover .hub-sp-total { display: flex; justify-content: space-between; border-top: 1px solid rgba(255,255,255,.12); margin-top: 6px; padding-top: 6px; font-weight: 800; }

.hub-rp-main { flex: 1; min-width: 0; }
.hub-rp-title { font-size: 13px; font-weight: 700; line-height: 1.35; }
.hub-rp-row:hover .hub-rp-title { color: var(--color-accent); }
.hub-rp-thermo-row { display: flex; align-items: center; gap: 6px; margin-top: 7px; }
.hub-rp-created { font-size: 10px; font-weight: 500; color: var(--color-text-subtle); opacity: .8; }
.hub-rp-dot-sep { font-size: 10px; color: var(--color-text-subtle); opacity: .6; }
/* Gefuellter Chip statt reiner Textfarbe (Nutzer-Feedback) - identische Farbgebung wie
   .overdue-badge andernorts in der App, damit "Frist ueberschritten" ueberall gleich aussieht. */
.hub-rp-thermo-chip { display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px; background: var(--color-danger-text); color: #fff; font-size: 10px; font-weight: 700; white-space: nowrap; }

.hub-rp-customer { flex: 0 0 160px; min-width: 0; }
.hub-rp-customer .hub-rc-name { font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hub-rp-customer .hub-rc-sub { font-size: 10.5px; color: var(--color-text-subtle); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* SLA-Ampel-Punkt (siehe coredesk-ticket-hub-board-entwurf.html: rp-sla-dot) - fuer die flache,
   score-sortierte Focus-View-Liste in "Meine Tickets" (kein Thermometer, da hier kein Score-
   Ranking-Vergleich mit Ready-to-Pick noetig ist, sondern nur der SLA-Zustand selbst zaehlt). */
.rp-sla-dot { position: relative; width: 10px; height: 10px; flex: 0 0 auto; }
.rp-sla-dot .core { width: 10px; height: 10px; border-radius: 50%; position: relative; z-index: 1; }
.rp-sla-dot .ping { position: absolute; inset: 0; border-radius: 50%; opacity: .6; animation: hub-sla-ping 1.6s cubic-bezier(0,0,.2,1) infinite; }
.rp-sla-dot.danger .core { background: var(--color-critical); } .rp-sla-dot.danger .ping { background: var(--color-critical); }
.rp-sla-dot.warning .core { background: var(--status-warning); } .rp-sla-dot.warning .ping { display: none; }
.rp-sla-dot.ok .core { background: var(--status-active); } .rp-sla-dot.ok .ping { display: none; }
.rp-sla-dot.paused .core { background: var(--status-hold); } .rp-sla-dot.paused .ping { display: none; }
@keyframes hub-sla-ping { 75%, 100% { transform: scale(2.2); opacity: 0; } }

/* "Neu"-Punkt fuer Ready-to-Pick-Zeilen (tickets.is_new): pulsiert dauerhaft gruen, bis irgendein
   Mitarbeiter das Ticket einmal geoeffnet hat (TicketController::show() -> markSeen(), global statt
   pro Nutzer). Getrennt vom SLA-Ampel-Punkt (rp-sla-dot), da beide Zustaende unabhaengig
   gleichzeitig zutreffen koennen (neu UND ueberfaellig). */
.rp-new-dot { position: relative; width: 10px; height: 10px; flex: 0 0 auto; }
.rp-new-dot .core { width: 10px; height: 10px; border-radius: 50%; position: relative; z-index: 1; background: var(--status-active); }
.rp-new-dot .ping { position: absolute; inset: 0; border-radius: 50%; opacity: .6; background: var(--status-active); animation: hub-sla-ping 1.6s cubic-bezier(0,0,.2,1) infinite; }

.hub-mine-focus-row { cursor: pointer; }

.hub-pick-btn { flex: 0 0 auto; background: var(--color-accent); border: 1px solid var(--color-accent); color: #fff; font-weight: 800; font-size: 12px; padding: 8px 14px; border-radius: var(--radius-sm); display: flex; align-items: center; gap: 6px; min-height: 44px; }
.hub-pick-btn:hover { filter: brightness(1.08); }

.hub-panel.hub-drop-hover .hub-panel-body { outline: 2px dashed var(--color-accent); outline-offset: -6px; background: var(--color-accent-soft); }

@media (max-width: 1024px) {
    .hub-panel { border-radius: var(--radius-md); }
    .hub-rp-row { flex-wrap: wrap; }
    .hub-rp-customer { flex: 1 1 100%; order: 5; margin-top: 4px; }
    .hub-pick-btn { margin-left: auto; }
    .hub-score-pill { min-height: 44px; }
}

.hub-tab-switcher { display: none; }
@media (max-width: 1024px) {
    .hub-tab-switcher { display: flex; gap: 4px; background: var(--color-surface-2); border-radius: var(--radius-md); padding: 4px; margin-bottom: 14px; }
    .hub-tab-switcher button { flex: 1; border: none; background: transparent; padding: 10px 6px; border-radius: var(--radius-sm); font-size: 12.5px; font-weight: 800; color: var(--color-text-muted); display: flex; align-items: center; justify-content: center; gap: 6px; min-height: 44px; }
    .hub-tab-switcher button.active { background: var(--color-surface); color: var(--color-accent); box-shadow: var(--shadow-card); }
    .hub-tab-switcher .hub-ts-count { background: var(--status-neutral-soft); color: var(--color-text-subtle); font-size: 10px; padding: 1px 6px; border-radius: 99px; }
    .hub-tab-switcher button.active .hub-ts-count { background: var(--color-accent-soft); color: var(--color-accent); }
    .filter-bar { min-height: 0; }
    .filter-chip { min-height: 36px; }
}

/* ---- Ticket-Hub-Board: Toast (Pick/Undo-Rueckmeldung) ---- */
.hub-toast-wrap { position: fixed; bottom: 26px; left: 50%; transform: translateX(-50%); z-index: 900; display: flex; flex-direction: column; gap: 8px; align-items: center; }
.hub-toast { display: flex; align-items: center; gap: 12px; background: #101828; color: #fff; padding: 12px 16px 12px 18px; border-radius: var(--radius-md); font-size: 13px; font-weight: 600; box-shadow: var(--shadow-card); opacity: 0; transform: translateY(14px); transition: opacity .25s, transform .25s; }
.hub-toast.hub-show { opacity: 1; transform: translateY(0); }
.hub-toast--error { background: var(--color-critical); }
.hub-toast-undo { background: rgba(255,255,255,.12); border: none; color: #fff; font-weight: 800; font-size: 11.5px; padding: 5px 10px; border-radius: var(--radius-sm); min-height: 30px; }
.hub-toast-undo:hover { background: rgba(255,255,255,.2); }

/* Admin > Design: schematische Vorschau-Kacheln fuer Standard-Ansicht (Klassisch/Neu) - reine
   Platzhalter-Balken statt echter Screenshots, da keine gerenderten Vorschaubilder existieren. */
.design-preview-schematic { background: var(--color-surface-muted); border: 2px solid var(--color-border); border-radius: var(--radius-md); padding: 14px; transition: border-color .15s; }
.design-preview-schematic--active { border-color: var(--color-primary); }
.design-preview-schematic-bar { height: 10px; width: 40%; background: var(--color-text-subtle); border-radius: var(--radius-sm); margin-bottom: 10px; opacity: .5; }
.design-preview-schematic-bar--ring { width: 24px; height: 24px; border-radius: 50%; border: 3px solid var(--color-primary); background: none; opacity: .8; }
.design-preview-schematic-row { display: flex; gap: 6px; margin-bottom: 6px; }
.design-preview-schematic-block { height: 8px; background: var(--color-border); border-radius: var(--radius-sm); }
.design-preview-schematic-label { font-size: 12px; color: var(--color-text-muted); font-weight: 600; margin: 10px 0 0; text-align: center; }

/* ---- CoreDesk Project Workspace (templates/layout-workspace.twig, templates/projects/workspace.twig) ----
   Vollflaechige Praesentationsansicht fuers Projektgeschaeft, zusaetzlich zur bestehenden
   projects/show.twig. Baut ausschliesslich auf bestehenden Tokens auf, dadurch automatisch
   Light+Dark-faehig ohne eigene [data-theme="dark"]-Overrides (Muster wie jeder andere
   Namensraum hier). */
.pw-body { margin: 0; background: var(--color-bg); }
.pw-shell { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }

.pw-topbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-height: 64px; padding: 10px 24px; background: var(--color-surface); border-bottom: 1px solid var(--color-border); backdrop-filter: blur(12px); flex-wrap: wrap; }
.pw-topbar-left { display: flex; align-items: center; gap: 14px; }
.pw-back-link { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: var(--radius-sm); color: var(--color-text-muted); }
.pw-back-link:hover { background: var(--color-bg); color: var(--color-text); }
.pw-brand { display: flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--color-text-muted); white-space: nowrap; }
.pw-brand-accent { color: var(--color-accent); }
.pw-topbar-divider { width: 1px; height: 28px; background: var(--color-border); }
.pw-title-row { display: flex; align-items: center; gap: 8px; }
.pw-title-row h1 { font-size: 15px; font-weight: 700; margin: 0; }
.pw-customer-name { font-size: 11.5px; color: var(--color-text-muted); margin: 2px 0 0; }
.pw-customer-name strong { color: var(--color-text); }

.pw-kpis { display: flex; align-items: center; gap: 16px; background: var(--color-bg); border: 1px solid var(--color-border); padding: 6px 14px; border-radius: var(--radius-lg); }
.pw-kpi-card { display: flex; align-items: center; gap: 8px; }
.pw-kpi-card > .icon, .pw-kpi-card > .icon-sm { color: var(--color-accent); flex-shrink: 0; }
.pw-kpi-row { display: flex; align-items: center; gap: 8px; font-size: 11.5px; white-space: nowrap; }
.pw-kpi-label { color: var(--color-text-muted); }
.pw-kpi-value { font-weight: 700; color: var(--color-text); }
.pw-kpi-bar { width: 100px; height: 5px; margin-top: 3px; }

.pw-topbar-actions { display: flex; align-items: center; gap: 8px; }
.pw-topbar-actions .btn.is-active { background: var(--status-warning-soft); border-color: var(--status-warning); color: var(--status-warning); }

.pw-guest-banner { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 24px; background: var(--status-warning-soft); border-bottom: 1px solid var(--color-border); font-size: 12px; color: var(--status-warning); }
.pw-guest-banner button { background: none; border: none; color: inherit; text-decoration: underline; cursor: pointer; font-size: 12px; }
.pw-guest-banner.is-hidden { display: none; }

.pw-tabs { display: flex; gap: 22px; padding: 0 24px; background: var(--color-surface); border-bottom: 1px solid var(--color-border); overflow-x: auto; }
.pw-tab-link { display: flex; align-items: center; gap: 6px; padding: 11px 0; background: none; border: none; border-bottom: 2px solid transparent; color: var(--color-text-muted); font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.pw-tab-link:hover { color: var(--color-text); }
.pw-tab-link.is-active { color: var(--color-accent); border-bottom-color: var(--color-accent); }

.pw-main { flex: 1; overflow-y: auto; padding: 20px 24px; background: var(--color-bg); }
.pw-view { display: none; }
.pw-view.is-active { display: block; }
.pw-view .kanban-board { height: 100%; }

.pw-gantt-row { display: grid; grid-template-columns: 220px 1fr; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--color-border); }
.pw-gantt-row:last-child { border-bottom: none; }
.pw-gantt-label { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; }
.pw-gantt-track { position: relative; height: 30px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-sm); }
.pw-gantt-bar { position: absolute; top: 4px; bottom: 4px; background: var(--color-accent-soft); border: 1px solid var(--color-accent); border-radius: var(--radius-sm); }
.pw-gantt-today-marker { position: absolute; top: -4px; bottom: -4px; width: 2px; background: var(--color-critical); z-index: 1; }
.pw-gantt-no-dates { display: block; padding: 0 10px; line-height: 30px; font-size: 11.5px; color: var(--color-text-muted); }

.pw-billing-summary { margin-bottom: 16px; max-width: 520px; }
.pw-billing-phase { margin-bottom: 12px; }

.pw-wiki-teaser { max-width: 420px; text-align: center; padding: 32px 24px; }
.pw-wiki-teaser h2 { font-size: 14px; margin: 10px 0 4px; }
.pw-wiki-teaser p { font-size: 12.5px; color: var(--color-text-muted); margin-bottom: 16px; }

.pw-cost-sensitive.is-hidden { display: none; }
.pw-guest-only { display: none; }
.pw-guest-only.is-visible { display: inline; }

@media (max-width: 1180px) {
    .pw-kpis { display: none; }
}
@media (max-width: 767px) {
    .pw-topbar { padding: 10px 16px; }
    .pw-topbar-left { flex-wrap: wrap; }
    .pw-title-row { flex-wrap: wrap; }
    .pw-tabs { padding: 0 16px; }
    .pw-main { padding: 16px; }
    .pw-gantt-row { grid-template-columns: 1fr; }
}

/* Kunden-Subnav: ersetzt auf allen /customers/{id}/...-Seiten die vertikale Icon-Rail durch eine
   horizontale Tab-Leiste (UniFi-Vorbild), Optik an .pw-topbar/.pw-tabs (oben) angelehnt - echte
   Seitenwechsel per <a href> statt JS-Panel-Switch, daher eigener Namensraum statt Wiederverwendung. */
/* Kunden-Kopfzeile in der Topbar (ersetzt dort die generische Seiten-Ueberschrift, siehe
   layout.twig topbar-left) - Kunden-Switcher (Status-Punkt, Name, Kundennummer, Chevron) plus
   Breadcrumb zum aktuell aktiven Subnav-Tab. */
.customer-topbar-heading { display: flex; align-items: center; gap: 10px; min-width: 0; }
.customer-subnav-back { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--radius-sm); color: var(--color-text-muted); flex-shrink: 0; }
.customer-subnav-back:hover { background: var(--color-bg); color: var(--color-text); }
.customer-subnav-switch { position: relative; flex-shrink: 0; }
.customer-subnav-switch-trigger { display: flex; align-items: center; gap: 8px; background: none; border: none; padding: 4px 6px; margin: -4px -6px; border-radius: var(--radius-sm); color: var(--color-text-muted); cursor: pointer; font-family: inherit; }
.customer-subnav-switch-trigger:hover { background: var(--color-bg); }
.customer-subnav-switch-text { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; text-align: left; }
/* Ellipsis statt Kollision mit der Suche, wenn ein schmaleres Desktop-Fenster den Kundennamen
   gegen .topbar-center draengt - fester max-width statt Flex-Vererbung, da .customer-subnav-switch
   bewusst nicht schrumpft (Status-Punkt/Chevron sollen immer sichtbar bleiben). */
.customer-subnav-switch-text strong { display: block; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 700; color: var(--color-text); }
.customer-subnav-switch-text small { font-size: 11px; font-weight: 500; color: var(--color-text-muted); font-family: var(--font-mono, monospace); }
.customer-subnav-divider { color: var(--color-border); font-size: 14px; }
.customer-subnav-crumb { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 700; color: var(--color-text); white-space: nowrap; }
/* Zahnrad-Icon fuer die ausgelagerten Kunden-Einstellungen (Asset-Sync/Stammdaten/Danger-Zone) -
   bewusst ausserhalb der horizontalen Subnav-Tabs, da reine Einrichtungskonfiguration statt
   taeglicher Workflow (siehe Kunden-Detailansicht-Aufraeumung). */
.customer-subnav-settings-link { display: flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--radius-sm); color: var(--color-text-muted); flex-shrink: 0; margin-left: auto; }
.customer-subnav-settings-link:hover, .customer-subnav-settings-link.is-active { background: var(--color-bg); color: var(--color-text); }

/* Kunden-Switcher-Dropdown (siehe layout.twig, RecentCustomerVisits) - selbe CSS-only
   Hover-Dropdown + .is-open-Klick-Umschaltung wie .topbar-user-menu (mobile-chrome.js). */
.customer-subnav-switch-dropdown { display: none; flex-direction: column; position: absolute; top: 100%; left: 0; min-width: 220px; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md); box-shadow: var(--shadow-card); z-index: 100; padding: 6px; margin-top: 4px; }
.customer-subnav-switch:hover .customer-subnav-switch-dropdown,
.customer-subnav-switch.is-open .customer-subnav-switch-dropdown { display: flex; }
.customer-subnav-switch-item { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: var(--radius-sm); font-size: 12px; font-weight: 600; color: var(--color-text); }
.customer-subnav-switch-item:hover { background: var(--color-bg); text-decoration: none; }
.customer-subnav-switch-item.is-current { background: var(--color-bg); }
.customer-subnav-switch-item-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.customer-subnav-switch-item small { font-size: 10px; font-weight: 500; color: var(--color-text-muted); font-family: var(--font-mono, monospace); flex-shrink: 0; }
.customer-subnav-switch-back { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: var(--radius-sm); font-size: 12px; font-weight: 600; color: var(--color-text); }
.customer-subnav-switch-back:hover { background: var(--color-bg); text-decoration: none; }

/* Vertikale Kombi-Shell Sub-Sidebar auf Kundenunterseiten (Nutzer-Feedback 2026-08-03, ersetzt die
   bisherige horizontale .customer-subnav-tabs-Leiste - siehe Kommentar in layout.twig fuer die
   Gruppierungs-Logik). Nutzt bewusst dieselben .asset-list-shell/.asset-list-sidebar/
   .asset-list-sidebar-head/.asset-list-sidebar-collapse-Klassen wie /admin/billing,
   /customers?view=list, /assets?view=list - hier folgen nur die ECHTEN Abweichungen. Optik dritte
   Iteration ("Hybrid": UniFi-Typografie/Abstaende/Icons aus Iteration 2 + M365-Akkordeon-Mechanik
   aus Iteration 1 zurueck, da eine komplett flache Liste bei 17 Items vertikales Scrollen erzwang).
   .customer-shell ergaenzt .asset-list-shell um sticky statt einer festen Hoehen-Berechnung, damit
   sowohl normale (block, seitenweit scrollend) als auch randlose page-seamless-split-
   Kundenunterseiten (z.B. Vault) ohne Sonderfall funktionieren. */
body.page-seamless-split .customer-shell {
    flex: 1;
    min-height: 0;
}

.customer-sidebar {
    position: sticky;
    top: 0;
    max-height: 100vh;
    padding: 20px 16px;
    gap: 4px;
}

.customer-sidebar-group { border: none; margin: 0; padding: 0; }
/* Sektionen trennen sich durch eine duenne Linie VOR der Gruppe, nicht durch einen Rahmen unter dem
   Titel - die erste Gruppe bekommt keine (kein Strich direkt unter dem Kopfbereich). */
.customer-sidebar-group + .customer-sidebar-group {
    margin-top: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--color-border);
}

/* Sektions-Header ist jetzt ein <button> (Akkordeon-Trigger) statt reinem Text - Button-Reset, dann
   dieselbe 11px/700/uppercase/muted-Optik wie zuvor. .asset-list-filter-section h4 (Basis-Regel,
   siehe weiter oben) greift hier weiterhin nicht, da der Titel kein <h4> ist. */
.customer-sidebar-group-title {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    margin: 0;
    padding: 8px;
    border: none;
    background: none;
    font: inherit;
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--color-text-muted);
    cursor: pointer;
    border-radius: 6px;
}
.customer-sidebar-group-title:hover { color: var(--color-text); background: var(--color-surface); }
.customer-sidebar-group-chevron { margin-left: auto; flex-shrink: 0; transition: transform .18s ease; }
.customer-sidebar-group.is-open .customer-sidebar-group-chevron { transform: rotate(180deg); }

/* Fluessige Auf-/Zuklapp-Animation ganz ohne JS-Bibliothek (Grid-Rows-Trick statt max-height/JS-
   Hoehenmessung - robust bei variabler Item-Anzahl je Gruppe). */
.customer-sidebar-group-collapse {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .18s ease;
}
.customer-sidebar-group.is-open .customer-sidebar-group-collapse { grid-template-rows: 1fr; }
.customer-sidebar-group-collapse > .customer-sidebar-group-items { min-height: 0; overflow: hidden; }

.customer-sidebar-group-items { display: flex; flex-direction: column; gap: 2px; padding-top: 4px; }

.customer-sidebar-link {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 10px 8px 10px 12px;
    border-radius: 8px;
    color: var(--color-text);
    font-size: 13.5px;
    font-weight: 500;
    white-space: nowrap;
}
.customer-sidebar-link .icon-sm { width: 20px; height: 20px; color: var(--color-text-muted); transition: color .12s ease; }
.customer-sidebar-link:hover { color: var(--color-text); background: var(--color-surface); text-decoration: none; }
.customer-sidebar-link:hover .icon-sm { color: var(--color-text); }
/* UniFi-Referenz: keine breite farbige Box - aktiver Eintrag ist ein dezenter grauer Hintergrund +
   kraeftigerer Text + blau eingefaerbtes Icon, statt vollflaechigem Akzent-Ton. */
.customer-sidebar-link.is-active { color: var(--color-text); font-weight: 600; background: var(--color-surface); }
.customer-sidebar-link.is-active .icon-sm { color: var(--color-accent); }
.customer-sidebar-link-count {
    margin-left: auto;
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 999px;
    background: var(--color-bg);
    color: var(--color-text-muted);
    font-size: 10px; font-weight: 700;
}
.customer-sidebar-link.is-active .customer-sidebar-link-count { background: var(--color-accent-soft); color: var(--color-accent); }

/* Icon-only-Einklappen: der Toggle-Button/Pfeil selbst ist die geteilte .asset-list-sidebar-collapse-
   Optik, hier wird nur das ERGEBNIS des Einklappens ueberschrieben - Gruppentitel-Text/Chevron und
   Link-Beschriftung/Zaehler ausgeblendet, nur Icons bleiben sichtbar (title-Attribut auf
   .customer-sidebar-link liefert den Namen als Tooltip nach). Der Akkordeon-Zustand jeder Gruppe wird
   dabei ignoriert - im Icon-only-Modus sind IMMER alle Icons sichtbar (grid-template-rows wird auf
   1fr erzwungen), unabhaengig davon ob die Gruppe gerade auf- oder zugeklappt gespeichert ist. */
.customer-shell.is-sidebar-collapsed .customer-sidebar {
    width: 56px;
    padding-left: 8px;
    padding-right: 8px;
    overflow: visible;
    border-right: 1px solid var(--color-border);
}
.customer-shell.is-sidebar-collapsed .asset-list-sidebar-title,
.customer-shell.is-sidebar-collapsed .customer-sidebar-group-title span,
.customer-shell.is-sidebar-collapsed .customer-sidebar-group-chevron,
.customer-shell.is-sidebar-collapsed .customer-sidebar-link-label,
.customer-shell.is-sidebar-collapsed .customer-sidebar-link-count {
    display: none;
}
.customer-shell.is-sidebar-collapsed .asset-list-sidebar-head { justify-content: center; }
.customer-shell.is-sidebar-collapsed .customer-sidebar-group-title { justify-content: center; cursor: default; }
.customer-shell.is-sidebar-collapsed .customer-sidebar-group-title:hover { background: none; }
.customer-shell.is-sidebar-collapsed .customer-sidebar-group-collapse { grid-template-rows: 1fr !important; }
.customer-shell.is-sidebar-collapsed .customer-sidebar-group-items { align-items: center; padding-top: 0; }
.customer-shell.is-sidebar-collapsed .customer-sidebar-link { padding: 10px; justify-content: center; }

@media (max-width: 767px) {
    .customer-topbar-heading { gap: 6px; }
    .customer-subnav-divider,
    .customer-subnav-crumb { display: none; }
    .customer-sidebar { position: static; width: 100%; max-height: none; border-right: none; border-bottom: 1px solid var(--color-border); }
}

/* Fly-out "Leistungsschein Vorschau" - gemeinsam genutzt von tickets/show_new.twig und
   projects/show.twig (siehe templates/partials/_billing_preview_flyout.twig), auf den globalen
   Design-Tokens statt der ticket-spezifischen --tdv2-* Variablen aufgebaut, damit dieselbe
   Komponente auf beiden Seiten ohne zusaetzlichen Stylesheet-Import funktioniert. */
.billing-flyout-bg { position: fixed; inset: 0; background: rgba(16,24,40,.45); opacity: 0; pointer-events: none; transition: opacity .25s; z-index: 900; }
.billing-flyout-bg.show { opacity: 1; pointer-events: auto; }
.billing-flyout { position: fixed; top: 0; right: 0; bottom: 0; width: 46vw; min-width: 480px; max-width: 94vw; background: var(--color-surface); box-shadow: 0 16px 40px rgba(16,24,40,.16); z-index: 901; transform: translateX(100%); transition: transform .28s cubic-bezier(.2,.8,.2,1); display: flex; flex-direction: column; color: var(--color-text); }
.billing-flyout.show { transform: translateX(0); }
.billing-flyout-head { padding: 18px 22px; border-bottom: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; flex: 0 0 auto; }
.billing-flyout-head h3 { margin: 0; font-size: 15px; font-weight: 800; }
.billing-flyout-close { width: 32px; height: 32px; border-radius: 9px; border: none; background: var(--color-bg); color: var(--color-text-muted); display: flex; align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer; }
.billing-flyout iframe { flex: 1; border: none; width: 100%; background: #fff; }

@media (max-width: 900px) {
    .billing-flyout { width: 100vw; min-width: 0; }
}

/* Fly-out "Leistung" fuer einen einzelnen Leistungs-Suchtreffer im CoreDesk Ticket Hub (siehe
   renderUnifiRow() in ticket-hub-board.js + public/js/time-entry-flyout.js) - eigener, schmalerer
   Klassen-Namensraum statt Wiederverwendung von .billing-flyout, da hier reiner Text statt eines
   iframes angezeigt wird. Gleiches rechts-andockendes Grundmuster wie .billing-flyout. */
.te-flyout-bg { position: fixed; inset: 0; background: rgba(16,24,40,.45); opacity: 0; pointer-events: none; transition: opacity .25s; z-index: 900; }
.te-flyout-bg.show { opacity: 1; pointer-events: auto; }
.te-flyout { position: fixed; top: 0; right: 0; bottom: 0; width: 36vw; min-width: 380px; max-width: 94vw; background: var(--color-surface); box-shadow: 0 16px 40px rgba(16,24,40,.16); z-index: 901; transform: translateX(100%); transition: transform .28s cubic-bezier(.2,.8,.2,1); display: flex; flex-direction: column; color: var(--color-text); }
.te-flyout.show { transform: translateX(0); }
.te-flyout-head { padding: 18px 22px; border-bottom: 1px solid var(--color-border); display: flex; align-items: center; justify-content: space-between; flex: 0 0 auto; }
.te-flyout-head h3 { margin: 0; font-size: 15px; font-weight: 800; }
.te-flyout-close { width: 32px; height: 32px; border-radius: 9px; border: none; background: var(--color-bg); color: var(--color-text-muted); display: flex; align-items: center; justify-content: center; flex-shrink: 0; cursor: pointer; }
.te-flyout-body { flex: 1; overflow-y: auto; padding: 20px 22px; }
.te-flyout-subject { font-size: 14px; font-weight: 800; color: var(--color-text); }
.te-flyout-customer { font-size: 12.5px; color: var(--color-text-subtle); margin-top: 2px; }
.te-flyout-meta-row { display: flex; gap: 14px; margin-top: 12px; padding-bottom: 14px; border-bottom: 1px solid var(--color-border); font-size: 12.5px; color: var(--color-text-muted); }
.te-flyout-meta-item { display: flex; align-items: center; gap: 6px; }
.te-flyout-meta-label { font-weight: 700; color: var(--color-text-subtle); }
.te-flyout-text { margin-top: 16px; font-size: 13.5px; line-height: 1.6; color: var(--color-text); white-space: pre-wrap; word-break: break-word; }
.te-flyout-footer { padding: 16px 22px; border-top: 1px solid var(--color-border); flex: 0 0 auto; }

@media (max-width: 900px) {
    .te-flyout { width: 100vw; min-width: 0; }
}

/* Sprungziel aus dem Leistungs-Flyout ("Zum Ticket", siehe oben) - kurzes farbiges Aufleuchten des
   Ziel-Eintrags im Ticket-Verlauf (.tdv2-tl-entry, siehe show_new.twig), damit der Techniker sofort
   sieht, welcher von ggf. mehreren Eintraegen gemeint ist, ohne die Zeile dauerhaft zu markieren. */
.tdv2-tl-entry--highlight { animation: tdv2-entry-highlight-flash 2.5s ease-out; }
@keyframes tdv2-entry-highlight-flash {
    0% { background: var(--color-accent-soft); }
    100% { background: transparent; }
}

/* ============================================================================
   Asset-Formular: Progressive Disclosure + Icon-Grid-Typ-Auswahl + Live-Vorschau
   (siehe templates/assets/_fields.twig, templates/assets/form.twig, Chat-Konzept
   "Neues Geraet inventarisieren" - kontextsensitives Formular, keine irrelevanten
   Felder im DOM). Wiederverwendet fuer Desktop-Anlegen/-Bearbeiten UND (Typ-Chips/
   Netzwerk-Akkordeon-Variante) das mobile Quick-Add-Sheet in customers/dashboard.twig.
   ============================================================================ */
.visually-hidden {
    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;
}

.asset-type-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
    gap: 8px;
    margin-bottom: 4px;
}
.asset-type-btn {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
    padding: 10px 6px; border: 1px solid var(--color-border); border-radius: var(--radius-md);
    background: var(--color-bg); color: var(--color-text-muted); font-size: 11px; font-weight: 600;
    text-align: center; cursor: pointer; transition: border-color .15s, color .15s, background .15s;
}
.asset-type-btn svg, .asset-type-btn .icon { width: 20px; height: 20px; }
.asset-type-btn:hover { border-color: var(--color-accent); color: var(--color-text); }
.asset-type-btn.is-active { border-color: var(--color-accent); background: var(--color-accent-soft); color: var(--color-accent); }

/* Fluessiges Ein-/Ausblenden der kontextsensitiven Sektionen (Netzwerk, Technische
   Spezifikationen) per CSS-Grid-Rows-Trick statt Hoehen-Berechnung in JS: der aeussere
   Wrapper animiert grid-template-rows 1fr<->0fr, der innere overflow:hidden-Container
   liefert dafuer die noetige Clipping-Flaeche. */
.progressive-section {
    display: grid;
    grid-template-rows: 1fr;
    transition: grid-template-rows .25s ease, opacity .2s ease;
    opacity: 1;
}
.progressive-section > .progressive-section-inner { overflow: hidden; min-height: 0; }
.progressive-section.is-collapsed { grid-template-rows: 0fr; opacity: 0; }

/* Einklappbare "Weitere Angaben"-Sektion (Status/Einkauf/Zugang/Notizen) - <details>/<summary>
   statt eigenem JS-Toggle, analog zum bereits etablierten Netzwerk-Akkordeon-Muster mobil. */
.field-accordion {
    border: 1px solid var(--color-border); border-radius: var(--radius-md);
    padding: 14px 16px; margin: 20px 0 4px; background: var(--color-bg);
}
.field-accordion summary {
    display: flex; align-items: center; justify-content: space-between;
    cursor: pointer; list-style: none; font-size: 13px; font-weight: 700; color: var(--color-text);
}
.field-accordion summary::-webkit-details-marker { display: none; }
.field-accordion summary .chevron { color: var(--color-text-muted); transition: transform .2s; flex-shrink: 0; width: 16px; height: 16px; }
.field-accordion[open] summary .chevron { transform: rotate(180deg); }
.field-accordion-body { padding-top: 16px; margin-top: 12px; border-top: 1px solid var(--color-border); }

/* Desktop-Split-View "Neues Geraet" (templates/assets/form.twig): links das Formular (~60%),
   rechts eine sticky Live-Vorschau-Karte (~40%), die zeigt, wie das Asset spaeter in der
   CoreDesk-Uebersicht (.asset-card) aussehen wird. */
.asset-form-split { display: grid; grid-template-columns: 1.5fr 1fr; gap: 24px; align-items: start; }

.asset-preview-card {
    position: sticky; top: 24px;
    background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-md);
    box-shadow: var(--shadow-card); padding: 20px;
}
.asset-preview-card .apc-eyebrow { font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-muted); margin-bottom: 4px; }
.asset-preview-card .apc-hint { font-size: 12px; color: var(--color-text-muted); margin-bottom: 16px; }
.asset-preview-card .apc-header { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
.asset-preview-card .apc-icon { width: 38px; height: 38px; border-radius: 10px; background: var(--color-accent-soft); color: var(--color-accent); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.asset-preview-card .apc-icon svg { width: 20px; height: 20px; }
.asset-preview-card .apc-header-text { min-width: 0; }
.asset-preview-card .apc-name { font-size: 15px; font-weight: 800; color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.asset-preview-card .apc-type { font-size: 12px; color: var(--color-text-muted); }
.asset-preview-card .apc-rows { display: flex; flex-direction: column; }
.asset-preview-card .apc-row { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; padding: 8px 0; border-top: 1px solid var(--color-border); }
.asset-preview-card .apc-row:first-child { border-top: none; }
.asset-preview-card .apc-row-label { color: var(--color-text-muted); flex-shrink: 0; }
.asset-preview-card .apc-row-value { font-weight: 600; color: var(--color-text); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.asset-preview-card .apc-row-value.is-placeholder { color: var(--color-text-muted); font-weight: 400; font-style: italic; }

@media (max-width: 1000px) {
    .asset-form-split { grid-template-columns: 1fr; }
    .asset-preview-card { position: static; order: -1; }
}

/* Mobiles Quick-Add-Sheet (templates/customers/dashboard.twig): horizontal scrollbare
   Typ-Chip-Leiste statt Dropdown (teilt sich .mobile-chip mit dem Leistung-Dauer-Sheet), 2-Spalten-
   Grid fuer Hersteller/Modell, sowie Kopfzeilen-Button fuers Typenschild-Scannen. */
.mobile-sheet-head-actions { display: flex; align-items: center; gap: 8px; }
.mobile-sheet-ocr-btn {
    width: 40px; height: 40px; border-radius: 50%; border: none;
    background: var(--color-accent-soft); color: var(--color-accent);
    display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.mobile-chip-row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; margin: 0 -18px; padding-left: 18px; padding-right: 18px; }
.mobile-chip-row::-webkit-scrollbar { display: none; }
.mobile-chip-row .mobile-chip { flex: 0 0 auto; min-width: 0; padding: 9px 14px; }
.mobile-field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.mobile-field-grid > .mobile-field { min-width: 0; }
.mobile-field-grid > .mobile-field input { width: 100%; box-sizing: border-box; }

/* Vollbild-Kamera-Vorschau fuers Typenschild-Scannen (Tesseract.js OCR, siehe
   public/js/asset-ocr-scan.js) - eigenstaendig neben dem bereits bestehenden Barcode-
   .scan-overlay, da hier ein Shutter-Button fuer eine EINZELNE Aufnahme statt kontinuierlichem
   Frame-Sampling noetig ist. */
.ocr-overlay { display: none; position: fixed; inset: 0; z-index: 200; background: #000; align-items: center; justify-content: center; }
.ocr-overlay.is-open { display: flex; }
.ocr-overlay-video { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
.ocr-overlay-frame {
    position: absolute; top: 40%; left: 50%; transform: translate(-50%, -50%);
    width: 80vw; max-width: 360px; height: 140px; border: 3px solid #fff; border-radius: var(--radius-md);
    box-shadow: 0 0 0 2000px rgba(0, 0, 0, .5);
}
.ocr-overlay-hint {
    position: absolute; top: calc(40% + 92px); left: 50%; transform: translateX(-50%);
    color: #fff; font-size: 12.5px; font-weight: 600; text-shadow: 0 1px 3px rgba(0,0,0,.6);
    text-align: center; padding: 0 24px; width: 100%;
}
.ocr-overlay-cancel {
    position: absolute; top: calc(16px + env(safe-area-inset-top)); right: 16px; z-index: 201;
    width: 44px; height: 44px; display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, .15); border: none; border-radius: 50%; color: #fff;
}
.ocr-overlay-shutter {
    position: absolute; bottom: calc(36px + env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%);
    width: 68px; height: 68px; border-radius: 50%; background: #fff; border: 4px solid rgba(255, 255, 255, .4); z-index: 201;
}
.ocr-overlay-processing {
    position: absolute; inset: 0; background: rgba(0, 0, 0, .72); display: none;
    flex-direction: column; align-items: center; justify-content: center; gap: 14px; color: #fff; z-index: 202;
}
.ocr-overlay-processing.is-open { display: flex; }
.ocr-overlay-spinner { width: 36px; height: 36px; border-radius: 50%; border: 3px solid rgba(255, 255, 255, .25); border-top-color: #fff; animation: ocrSpin .8s linear infinite; }
@keyframes ocrSpin { to { transform: rotate(360deg); } }
.ocr-overlay-processing span { font-size: 13px; font-weight: 600; }

/* Sonar Command Builder Panel (public/js/sonar-command-builder.js) - interaktives Kontroll-Panel
   ueber dem Terminal-Code-Block, mit dem sich Techniker den passenden Scan-Befehl per UI
   zusammenklicken statt Parameter manuell zu tippen. Wird sowohl auf der globalen Ghost-Agent-
   Seite (voller Funktionsumfang) als auch auf der kundengebundenen Sonar-Seite (reduzierter
   Funktionsumfang, kein Alias/Export-Format) eingebunden. */
.cmdbuilder-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.cmdbuilder-preset {
    display: inline-flex; align-items: center; gap: 6px; padding: 7px 14px; border-radius: 999px;
    border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text);
    font-size: 12.5px; font-weight: 600; cursor: pointer; transition: border-color .15s, background .15s;
}
.cmdbuilder-preset:hover { border-color: var(--color-accent); background: var(--color-surface-alt, #f1f5f9); }

.cmdbuilder-panel { border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: 16px; background: var(--color-surface-alt, var(--color-surface)); }
.cmdbuilder-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 18px; margin-bottom: 14px; }
.cmdbuilder-row:last-child { margin-bottom: 0; }
.cmdbuilder-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.cmdbuilder-field label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--color-text-muted); }
.cmdbuilder-field input[type="text"] { min-width: 220px; }
.cmdbuilder-field-grow { flex: 1; }

.cmdbuilder-os-switch { display: flex; gap: 8px; }
.cmdbuilder-os-option {
    display: flex; align-items: center; gap: 8px; padding: 9px 16px; border-radius: var(--radius-sm);
    border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text);
    font-size: 13px; font-weight: 600; cursor: pointer; transition: border-color .15s, background .15s, color .15s;
}
.cmdbuilder-os-option .icon-emoji { font-size: 16px; line-height: 1; }
.cmdbuilder-os-option:hover { border-color: var(--color-accent); }
.cmdbuilder-os-option.is-active { background: var(--color-accent); border-color: var(--color-accent); color: #fff; }

.cmdbuilder-ports-toggle { display: flex; align-items: center; gap: 10px; }
.cmdbuilder-ports-toggle span { font-size: 12.5px; color: var(--color-text); }

.cmdbuilder-web-hint { font-size: 12px; color: var(--color-text-muted); padding: 10px 0; }
.cmdbuilder-web-actions { display: flex; align-items: center; gap: 10px; }

.cmdbuilder-output-wrap { margin-top: 16px; }
.cmdbuilder-output-wrap .code-terminal { margin: 0; }
.cmdbuilder-output-wrap .code-terminal code { white-space: pre-wrap; }
.cmdbuilder-copy-btn {
    display: inline-flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: var(--radius-sm);
    border: none; background: var(--color-accent); color: #fff; font-size: 12.5px; font-weight: 700;
    cursor: pointer; white-space: nowrap; transition: background .15s;
}
.cmdbuilder-copy-btn:hover { background: var(--color-accent-hover, var(--color-accent)); }
.cmdbuilder-copy-btn:disabled { opacity: .45; cursor: not-allowed; }
.cmdbuilder-copy-btn.is-copied { background: var(--color-success, #16a34a); }

@media (max-width: 767px) {
    .cmdbuilder-row { flex-direction: column; align-items: stretch; gap: 12px; }
    .cmdbuilder-field input[type="text"] { min-width: 0; width: 100%; }
}

/* Abrechnungs-Modul: vertikale Sub-Sidebar (templates/admin/billing_module.twig), seit 2026-08-02
   im "Kombi-Shell"-Muster (siehe /admin/ui-design Abschnitt 20/21) - Rahmen/Collapse-Icon/-Mechanik
   sind die geteilten .asset-list-shell/.asset-list-sidebar/.asset-list-sidebar-collapse-Regeln
   weiter oben (dieselben wie /customers?view=list, /assets?view=list), hier bleibt nur noch die
   Tab-Optik (.billing-module-tabs/.billing-module-tab) als eigener, benannter Baustein - "Tab-Optik
   1:1 aus der Abrechnungs-Sidebar" laut Glossar, auch von den anderen Kombi-Shell-Seiten fuer ihre
   Listen-/Galerie-Umschalter wiederverwendet. Gleiches Aktiv-Zustand-Rezept wie .hr-module-sidebar
   .hr-jumplist-link.is-active (siehe HR-Modul weiter oben: color:var(--color-accent) +
   background:var(--color-accent-soft)), hier eigene Klassen statt geteilter Selektoren, da
   .billing-module-tab von billing-module-tabs.js weiterhin per Klassenname angesprochen wird. */
.billing-module-tabs {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* Dezente Section-Header innerhalb der Abrechnungs-Sidebar (Nutzer-Feedback 2026-08-02: die
   Tab-Reihenfolge war "ungeordnet" - jetzt chronologisch nach dem tatsaechlichen
   Abrechnungs-Workflow in drei Gruppen sortiert, siehe billing_module.twig). Identisches
   capitalize+Trennlinie-Rezept wie .asset-list-filter-section h4 (Kombi-Shell-Basisregel) - anfangs
   bewusst ohne border-bottom (Gruppen nur durch Abstand getrennt), auf Nutzer-Feedback 2026-08-03
   aber wieder angeglichen: alle Kombi-Shell-Sidebars (Tickets/Kunden/Assets/... UND das
   Abrechnungs-Modul) sollen optisch identisch getrennte Gruppen zeigen. Erster Trenner ohne
   Top-Abstand (direkt unter der Sidebar-Kopfzeile), alle weiteren mit Abstand zur vorherigen
   Gruppe. */
.billing-module-section-title {
    margin: 14px 0 4px;
    padding: 0 10px 6px;
    font-size: 11px;
    font-weight: 700;
    text-transform: capitalize;
    letter-spacing: .04em;
    color: var(--color-text-muted);
    border-bottom: 1px solid var(--color-border);
}

.billing-module-section-title:first-child {
    margin-top: 0;
}

/* Workflow-Stepper auf dem "Simulation & Monatslauf"-Tab (Nutzer-Feedback 2026-08-02, ersetzt den
   vormaligen Erklaertext-Block) - flache 3-Schritte-Leiste im UniFi-Stil, rein informativ (siehe
   Kommentar in _billing_tab_simulate.twig zur bewusst fehlenden "aktueller Schritt"-Hervorhebung).
   flex-wrap erlaubt ein sauberes Umbrechen auf schmaler Sidebar-Kombi-Shell-Breite, ohne dass
   Schritte abgeschnitten wirken. */
.billing-stepper {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    max-width: 720px;
    padding: 14px 18px;
    margin-bottom: 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.billing-stepper-step {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1 1 160px;
    min-width: 160px;
}

.billing-stepper-num {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex-shrink: 0;
    border-radius: 50%;
    background: var(--color-accent-soft);
    color: var(--color-accent);
    font-size: 12.5px;
    font-weight: 700;
}

.billing-stepper-text {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.billing-stepper-text strong {
    font-size: 13px;
}

.billing-stepper-text span {
    font-size: 12px;
    color: var(--color-text-muted);
}

.billing-stepper-arrow {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    color: var(--color-text-muted);
}

/* Kompakte Hero-Action-Karte (Nutzer-Feedback 2026-08-02) - buendelt Status-Badge, Kurzkontext und
   die beiden Haupt-Buttons (Simulieren/Scharf abrechnen), ersetzt die vormalige einfache .card mit
   Fliesstext davor. */
.billing-hero-card {
    max-width: 720px;
    padding: 20px;
    margin-bottom: 16px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

.billing-hero-status {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    font-weight: 700;
    color: var(--color-text);
}

.billing-hero-summary {
    margin: 6px 0 14px;
    color: var(--color-text-muted);
    font-size: 13.5px;
}

.billing-hero-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.billing-hero-hint {
    margin-top: 10px;
    color: var(--color-text-muted);
    font-size: 0.9rem;
}

/* Dezent abgerundetes Storno-Accordion (Nutzer-Feedback 2026-08-02) - eigener Chevron statt der
   nativen Browser-Dreiecksmarkierung (per ::-webkit-details-marker/list-style ausgeblendet), rotiert
   ueber das native [open]-Attribut ohne zusaetzliches JS. */
.billing-help-accordion summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    font-weight: 600;
    list-style: none;
}

.billing-help-accordion summary::-webkit-details-marker {
    display: none;
}

.billing-help-accordion summary::after {
    content: '';
    width: 8px;
    height: 8px;
    flex-shrink: 0;
    border-right: 2px solid var(--color-text-muted);
    border-bottom: 2px solid var(--color-text-muted);
    transform: rotate(45deg);
    transition: transform .15s ease;
}

.billing-help-accordion[open] summary::after {
    transform: rotate(-135deg);
}

/* Period-Filter-Leiste + KPI-Zeile in "Abrechnungs-Berichte" (Nutzer-Feedback 2026-08-03, siehe
   BillingRunController::runs()) - Pills im selben Aktiv-Zustand-Rezept wie .billing-module-tab.
   is-active (color:var(--color-accent) + background:var(--color-accent-soft)), hier als
   abgerundete Pillen statt Sidebar-Zeilen, da es eine horizontale Quick-Select-Leiste oberhalb der
   Tabelle ist, keine Navigation. Reine GET-Links auf dieselbe Route (siehe Template) - kein JS. */
.billing-period-bar {
    margin-bottom: 12px;
}

.billing-period-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.billing-period-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 14px;
    border: 1px solid var(--color-border);
    border-radius: 999px;
    background: var(--color-surface);
    color: var(--color-text-muted);
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
    list-style: none;
}

.billing-period-pill:hover { color: var(--color-text); border-color: var(--color-text-muted); }

.billing-period-pill.is-active {
    border-color: var(--color-accent);
    background: var(--color-accent-soft);
    color: var(--color-accent);
}

/* <summary> traegt hier selbst die Pillen-Optik (Custom-Range-Icon) - eigener Browser-Marker aus. */
.billing-period-pill::-webkit-details-marker { display: none; }

.billing-period-pill--custom { padding: 6px 10px; }

.billing-period-custom { position: relative; }

.billing-period-custom-form {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 5;
    padding: 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    box-shadow: var(--shadow-card);
}

.billing-period-custom-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 12px;
    color: var(--color-text-muted);
}

/* KPI-Zeile: 3 flache Karten (Gesamtvolumen/Laeufe/Status) - passen sich dem aktiven Period-Filter
   an, da BillingRunController::runs() sie serverseitig aus den bereits gefilterten $runs neu
   berechnet (siehe dort) - kein eigenes JS fuer die Karten noetig. */
.billing-kpi-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 18px;
}

.billing-kpi-card {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 180px;
    padding: 14px 18px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
}

/* Vertrags-Controlling (Nutzer-Feedback 2026-08-03): "Unprofitable Vertraege"-Karte faellt bei
   Werten > 0 durch einen roten Akzent auf statt still in der Reihe zu stehen wie die neutralen
   Karten daneben. */
.billing-kpi-card--danger {
    border-color: var(--color-danger-border);
    background: var(--color-danger-bg);
}

.billing-kpi-card--danger .billing-kpi-value {
    color: var(--color-danger-text);
}

.billing-kpi-label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-muted);
}

.billing-kpi-value {
    font-size: 20px;
    font-weight: 700;
    color: var(--color-text);
    font-variant-numeric: tabular-nums;
}

.billing-kpi-value--status {
    font-size: 14px;
    font-weight: 600;
}

/* Bearbeiter-Dropdown im Tab "Offene Tickets" (Nutzer-Feedback 2026-08-03) - sitzt zwischen den
   Filter-Chips und der Suche im selben .asset-list-table-toolbar-Flex-Row, feste Breite statt
   flex:1 wie die Suche, damit lange Namen sie nicht auf Kosten der Suche aufblaehen. */
.open-tickets-assignee-select {
    width: auto;
    max-width: 180px;
}

.billing-module-tab {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    color: var(--color-text);
    font-size: 13px;
    line-height: 1.4;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
}

.billing-module-tab:hover { background: var(--color-bg); color: var(--color-text); }

.billing-module-tab.is-active {
    color: var(--color-accent);
    font-weight: 700;
    background: var(--color-accent-soft);
}

.billing-module-tab-badge {
    margin-left: auto;
    display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 6px;
    border-radius: 999px; background: var(--color-surface-alt, #f1f5f9); color: var(--color-text-muted);
    font-size: 11px; font-weight: 700; line-height: 18px;
}
.billing-module-tab.is-active .billing-module-tab-badge { background: var(--color-surface); color: var(--color-accent); }

/* Nicht-seamless Zustand (nur der "Simulation & Abrechnung"-Tab, siehe body_class-Block in
   billing_module.twig): schwebende .card-Bloecke statt einer randlosen Volltabelle, brauchen daher
   echten Innenabstand auf allen Seiten - anders als die geteilte .asset-list-table-pane-Basisregel
   (keine Polsterung, siehe weiter oben), die von randlosen Kombi-Shell-Tabellenseiten ausgeht. Im
   page-seamless-split-Zustand ueberschreibt die gescopte Regel weiter unten dies wieder auf
   "16px 16px 0". */
#billing-module-shell .asset-list-table-pane {
    padding: 16px;
}

/* Nutzer-Feedback 2026-08-03 ("Sub-Sidebar auf /admin/billing sieht falsch aus"): im Nicht-seamless
   Zustand (Simulation-Tab, siehe Kommentar oben) sind .main/.content normale Bloecke ohne definierte
   Hoehe (die .main{display:flex;height:100vh}/.content{flex:1} - Kombination existiert bisher nur
   gescoped unter body.page-seamless-split, siehe Vault-Kommentar weiter oben) - die geteilte
   .asset-list-shell-Basisregel (height:100%, siehe Geraete-Listenansicht) laeuft dadurch ins Leere
   und die Sidebar waechst nur auf Inhaltshoehe statt bis zum unteren Rand, wodurch sie wie eine
   abgeschnittene/geschrumpfte Box wirkt. Fix NUR fuer die Billing-Modul-Shell (ueber :has() gescoped)
   statt .main/.content generell auf Flex umzustellen, damit alle anderen nicht-seamless Admin-/
   Einstellungsseiten ihr normales, inhaltshoehen-basiertes Verhalten behalten - es wird nur die
   fehlende Hoehen-Kette main->content->shell ergaenzt. */
.main:has(#billing-module-shell) {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

/* Zweiter Teil desselben Nutzer-Feedbacks (Vergleichsscreenshot Simulation- vs. Freigabe-Tab bei
   gestapelter Sidebar unter 900px): .content behielt bisher sein globales 24px/28px-Padding, wodurch
   die randvolle Shell dort sichtbar eingerueckt/mit Weissraum umrahmt wirkte statt randlos wie bei
   den seamless Tabs (dort setzt body.page-seamless-split .content padding:0). padding:0 ist hier
   gefahrlos, da die eigentlichen Simulation-Karten ihren Innenabstand ohnehin schon separat ueber
   die unconditional #billing-module-shell .asset-list-table-pane-Regel (16px, siehe oben) bekommen -
   die Karten-Optik bleibt also unveraendert, nur die aeussere Shell wird buendig. */
.content:has(#billing-module-shell) {
    flex: 1;
    min-height: 0;
    padding: 0;
}

/* Collapse-Toggle sitzt hier (anders als bei customers/assets, siehe Kommentar in
   billing_module.twig) als direktes Kind von .asset-list-shell statt im Tabellen-Toolbar oder in
   .asset-list-sidebar-head - deshalb per position:absolute an der Sidebar-Kante verankert (gleiche
   Grundidee wie die fruehere .billing-module-sidebar-toggle-Regel), damit er auch im eingeklappten
   Zustand sichtbar/klickbar bleibt (die Basis-Regel wuerde ihn sonst zusammen mit dem 0px breiten,
   overflow:hidden .asset-list-sidebar wegclippen). */
#billing-module-shell {
    position: relative;
}

#billing-module-shell > .asset-list-sidebar-collapse {
    position: absolute;
    top: 14px;
    left: 204px;
    z-index: 5;
    transition: left .15s ease;
}

#billing-module-shell.is-sidebar-collapsed > .asset-list-sidebar-collapse {
    left: 8px;
}

/* Academy-Verweis + Kombi-Shell-Innenabstand der Abrechnungs-Sidebar leben seit der Kombi-Shell-
   Migration in den geteilten .asset-list-sidebar-help/.asset-list-sidebar-Regeln - hier bleibt nur
   noch die 900px-Sonderregel fuer .billing-module-tabs, da die geteilte Asset-List-Shell-Regel
   (siehe @media (max-width:900px) weiter oben) die Sidebar zwar auf volle Breite stapelt, aber
   .billing-module-tabs nicht automatisch von der vertikalen Nav-Spalte auf eine horizontale
   Tab-Leiste umstellt - bei nur 2 Eintraegen (Liste/Galerie auf customers/assets) fiel das bisher
   nie auf, bei mehreren Abrechnungs-Tabs braucht es die Umstellung. Bewusst auf
   #billing-module-shell gescoped statt als Basis-Regel, um customers/assets nicht mit zu aendern.
   Nutzer-Feedback 2026-08-03 ("Sub-Sidebar auf /admin/billing schneidet Eintraege ab"): urspruenglich
   overflow-x:auto statt flex-wrap (aus der Zeit vor der Gruppierung in mehrere .billing-module-tabs-
   Abschnitte, siehe "Abrechnungs-Sidebar neu gruppiert", v1.747.44) - seit der Gruppierung passen die
   3 Eintraege im Abschnitt "Abrechnungsläufe" bei ca. 460-650px Sidebar-Breite nicht mehr in eine
   Zeile, das Scroll-Overflow hat aber keinerlei sichtbaren Scrollbar/Fade-Hinweis, wirkt also wie
   abgeschnittener/verschwundener Text statt wie scrollbarer Inhalt. flex-wrap laesst ueberschuessige
   Eintraege stattdessen sichtbar in die naechste Zeile umbrechen. */
@media (max-width: 900px) {
    #billing-module-shell .billing-module-tabs { flex-direction: row; flex-wrap: wrap; gap: 6px; }
    #billing-module-shell .billing-module-tab { flex: none; }
}

/* CoreDesk Speedtest (templates/speedtest/*.twig, public/js/speedtest-view.js): Run-Karte mit
   Canvas-Gauge links, Trend-Card rechts - gleiches 2fr/1fr-Grid wie .vault-layout-grid, aber
   eigener Klassenname, da hier keine Seiten-Navigation im rechten Slot sitzt. */
.speedtest-layout {
    display: grid;
    grid-template-columns: 1.3fr 1fr;
    gap: 24px;
    align-items: start;
}

@media (max-width: 900px) {
    .speedtest-layout { grid-template-columns: 1fr; }
}

.speedtest-run-card { display: flex; flex-direction: column; gap: 4px; }

.speedtest-gauge-wrap { position: relative; display: flex; flex-direction: column; align-items: center; margin: 0 auto 8px; }
.speedtest-gauge-wrap canvas { display: block; }
.speedtest-gauge-value { font-size: 26px; font-weight: 700; margin-top: -34px; }
.speedtest-gauge-label { font-size: 12px; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 8px; }

.speedtest-live-metrics {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 8px;
    margin: 8px 0 4px;
}
.speedtest-live-metric { text-align: center; padding: 8px 4px; border-radius: var(--radius-sm); background: var(--color-surface-alt, rgba(148,163,184,.08)); }
.speedtest-live-metric-value { display: block; font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums; }
.speedtest-live-metric-label { display: block; font-size: 10px; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: .03em; margin-top: 2px; }

.speedtest-run-btn { width: 100%; justify-content: center; margin-top: 12px; }

.speedtest-sla-target-note { font-size: 12px; color: var(--color-text-muted); margin: 10px 0 0; }

.speedtest-sla-badge {
    display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; border-radius: var(--radius-sm);
    font-size: 13px; font-weight: 600; margin-top: 10px; width: 100%; box-sizing: border-box;
}
.speedtest-sla-badge--is-ok { background: var(--status-active-soft); color: var(--status-active); }
.speedtest-sla-badge--is-warning { background: var(--status-warning-soft); color: var(--status-warning); }
.speedtest-sla-badge--is-critical { background: var(--color-danger-bg); color: var(--color-danger-text); border: 1px solid var(--color-danger-border); }

.speedtest-trend-card { min-height: 220px; }
.speedtest-trend-svg { width: 100%; height: 140px; }
.speedtest-trend-legend { display: flex; gap: 16px; margin-top: 8px; font-size: 12px; color: var(--color-text-muted); }
.speedtest-trend-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 4px; }
.speedtest-trend-dot--download { background: #3b82f6; }
.speedtest-trend-dot--upload { background: #22d3ee; }

/* ============ CoreDesk Magic Links: oeffentliche /intake/{token}-Seite ============
   templates/layout-intake.twig + templates/intake/*.twig nutzten diese Klassennamen bereits seit
   dem urspruenglichen Feature-Bau, es fehlte aber die CSS-Definition komplett - die Seite kam
   beim Kunden bislang als voellig unformatiertes rohes HTML an (Live-Bugreport). Bewusst NUR fuer
   data-theme="light" (siehe layout-intake.twig), keine Sidebar/Topbar-Abhaengigkeit. */
.intake-body { background: var(--color-bg); min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; box-sizing: border-box; }
.intake-shell { width: 100%; max-width: 480px; }
.intake-header { display: flex; flex-direction: column; align-items: center; gap: 2px; margin-bottom: 16px; text-align: center; }
.intake-brand { font-size: 15px; font-weight: 800; color: var(--color-accent); }
.intake-customer-name { font-size: 13px; color: var(--color-text-muted); }
.intake-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg); box-shadow: var(--shadow-card); padding: 28px 26px; }
.intake-title { font-size: 19px; font-weight: 700; color: var(--color-text); margin: 0 0 6px; }
.intake-subtitle { font-size: 13.5px; color: var(--color-text-muted); margin: 0 0 20px; line-height: 1.5; }
.intake-form .intake-field { margin-bottom: 16px; }
.intake-form .intake-field label { display: block; font-size: 12.5px; font-weight: 600; color: var(--color-text-muted); margin-bottom: 5px; }
.intake-form .intake-field input,
.intake-form .intake-field textarea { width: 100%; box-sizing: border-box; }
.intake-error { margin-bottom: 16px; }
.intake-submit-btn { width: 100%; justify-content: center; margin-top: 4px; }

/* CSAT-Sternebewertung (secure/rating_show.twig, CoreDesk Control) - reines CSS, kein JS: die
   Radio-Buttons liegen in absteigender Reihenfolge (5..1) im DOM, dir:rtl dreht die visuelle
   Anordnung auf 1..5, wodurch der :checked~label-Nachfolger-Selektor (der in DOM-Reihenfolge nach
   VORNE zeigt) visuell alle Sterne LINKS vom gewaehlten mit erfasst - der bekannte Pure-CSS-
   Sternebewertungs-Trick. */
.rating-stars { direction: rtl; display: inline-flex; font-size: 34px; line-height: 1; margin-bottom: 20px; }
.rating-stars input { position: absolute; opacity: 0; pointer-events: none; }
.rating-stars label { color: var(--color-border); cursor: pointer; padding: 0 3px; transition: color .1s; }
.rating-stars input:checked ~ label,
.rating-stars label:hover,
.rating-stars label:hover ~ label { color: #f59e0b; }

/* Magic-Link Reveal-UI im Ticket-Verlauf (templates/_history_entry.twig, Zweig
   'customer_data_requested') - Auge-Icon maskiert/entmaskiert den Klartext-Link,
   Copy-Icon erscheint erst nachdem der Link geladen wurde. */
.intake-reveal-wrap { display: inline-flex; align-items: center; gap: 6px; margin-left: 4px; vertical-align: middle; }
.intake-reveal-value { font-family: var(--font-mono, monospace); font-size: 12px; color: var(--color-text-muted); word-break: break-all; }
.intake-reveal-btn,
.intake-reveal-copy-btn,
.intake-revoke-btn { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; padding: 0; border: none; background: transparent; color: var(--color-text-muted); cursor: pointer; border-radius: var(--radius-sm, 4px); }
.intake-reveal-btn:hover,
.intake-reveal-copy-btn:hover { background: var(--color-surface-hover, rgba(0, 0, 0, 0.05)); color: var(--color-text); }
.intake-revoke-btn:hover { background: var(--color-danger-bg, rgba(220, 38, 38, 0.1)); color: var(--color-danger, #dc2626); }
.intake-reveal-expiry { font-size: 11px; color: var(--color-text-subtle, var(--color-text-muted)); }
.intake-revoked-label { font-size: 12px; color: var(--color-text-subtle, var(--color-text-muted)); font-style: italic; }

/* Bugfix (Nutzer-Feedback): "es fehlt der direkte Link, zum neuen Dokument/eingerichten Daten" -
   Button oeffnet #intake-submission-modal (siehe templates/tickets/show_new.twig) per AJAX
   (IntakeController::viewSubmission()). */
.intake-view-submission-btn { display: inline-flex; align-items: center; gap: 6px; margin-left: 4px; padding: 3px 10px; border: 1px solid var(--color-border); background: var(--color-surface); color: var(--color-text); font-size: 12px; border-radius: var(--radius-sm, 4px); cursor: pointer; vertical-align: middle; }
.intake-view-submission-btn:hover { background: var(--color-surface-hover, rgba(0, 0, 0, 0.05)); }
.intake-submission-status { color: var(--color-text-muted); font-size: 13px; }
.intake-submission-status--error { color: var(--color-danger, #dc2626); }
.intake-submission-fields { display: flex; flex-direction: column; gap: 10px; margin: 0 0 16px; }
.intake-submission-field dt { font-size: 12px; color: var(--color-text-muted); margin-bottom: 2px; }
.intake-submission-field dd { margin: 0; font-size: 14px; color: var(--color-text); word-break: break-word; }
.intake-submission-files-heading { font-size: 12px; color: var(--color-text-muted); margin-bottom: 6px; }
.intake-submission-files { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.intake-submission-file-link { display: inline-flex; align-items: center; gap: 8px; padding: 8px 10px; border: 1px solid var(--color-border); border-radius: var(--radius-sm, 4px); color: var(--color-text); text-decoration: none; font-size: 13px; }
.intake-submission-file-link:hover { background: var(--color-surface-hover, rgba(0, 0, 0, 0.05)); }

/* ============================================================
   CoreDesk Topology Hub (Migration 220) - .th-* Namespace
   ============================================================
   Der Canvas ist immer dunkel, unabhaengig vom globalen Admin-Theme-Toggle - deshalb werden hier
   die exakten UniFi-Dark-Token-*Werte* aus dem [data-theme="unifi"]-Block weiter oben (Zeile
   ~167-209) hart eingetragen statt ueber var(--color-*) zu laufen. Waeren es CSS-Variablen, wuerde
   ein Admin im hellen Theme einen hellen Canvas sehen - das widerspricht dem UniFi-Network-
   Application-Vorbild, das der Nutzer fuer dieses Modul ausdruecklich wollte. */
.th-body {
    margin: 0;
    /* Token-System statt hartcodierter Hex-Werte: hell ist der Standard (Nutzer-Vorgabe), dunkel
       ist ueber [data-th-theme="dark"] auf .th-body zuschaltbar (siehe th-theme.js) - unabhaengig
       vom globalen App-Theme-Toggle (siehe layout.twig), eigener localStorage-Key. Die bisherigen
       Werte (vormals hart auf jedem Selektor) sind jetzt die dark-Variante, siehe unten. */
    --th-canvas-bg: #FFFFFF;
    --th-surface: #FFFFFF;
    --th-border: #E2E8F0;
    --th-text: #0F172A;
    --th-text-muted: #64748B;
    --th-accent: #0284C7;
    --th-accent-hover: #0369A1;
    --th-led-inactive: #94A3B8;
    --th-node-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
    --th-dot-grid: rgba(203, 213, 225, 0.5);
    --th-drawer-row-hover: #EEF2F7;
    /* Nutzer-Feedback: Sidebar (Asset-Bibliothek) und Drawer (Node-Detail-Flyout) sollen die
       Farben der App-weiten Kombi-Shell tragen (siehe .asset-list-sidebar/-shell), statt der
       UniFi-Cyan-Akzentfarbe des Canvas - Canvas/Kanten/Nodes bleiben bewusst UniFi-Cyan (siehe
       vorherige Auftraege). --color-* aus :root greift hier nicht direkt, da diese Standalone-
       Seite kein [data-theme] auf <html> setzt und die Werte sonst immer bei Light haengen
       bleiben wuerden - stattdessen eigene, mit dem lokalen Hell/Dunkel-Toggle synchronisierte
       Tokens, deren Werte 1:1 aus den Kombi-Shell-Themes (:root bzw. [data-theme="unifi"])
       uebernommen sind. */
    --th-sidebar-bg: #F8FAFC;
    --th-sidebar-accent: #0559C9;
    --th-sidebar-accent-hover: #04449E;
    background: var(--th-canvas-bg);
    color: var(--th-text);
    font-family: var(--font-sans);
    font-size: 13px;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
}

.th-body[data-th-theme="dark"] {
    --th-canvas-bg: #0B0F19;
    --th-surface: #111827;
    --th-border: #1F293D;
    --th-text: #F8FAFC;
    --th-text-muted: #94A3B8;
    --th-accent: #0284C7;
    --th-accent-hover: #1D4ED8;
    --th-led-inactive: #4B5563;
    --th-node-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
    --th-dot-grid: #1F293D;
    --th-drawer-row-hover: #29354d;
    /* Entspricht bereits dem Kombi-Shell-"unifi"-Dark-Theme (--color-bg/-accent), daher hier
       keine sichtbare Aenderung gegenueber Canvas/Toolbar - siehe Kommentar oben in .th-body. */
    --th-sidebar-bg: #0B0F19;
    --th-sidebar-accent: #0284C7;
    --th-sidebar-accent-hover: #1D4ED8;
}

.th-shell {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

.th-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-shrink: 0;
    padding: 10px 20px;
    background: var(--th-surface);
    border-bottom: 1px solid var(--th-border);
}

.th-topbar-left {
    display: flex;
    align-items: center;
    gap: 14px;
}

.th-back-link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 6px;
    color: var(--th-text-muted);
}

.th-back-link:hover {
    background: var(--th-border);
    color: var(--th-text);
    text-decoration: none;
}

.th-brand {
    font-size: 14px;
    font-weight: 600;
    color: var(--th-text);
    white-space: nowrap;
}

.th-brand-accent {
    color: var(--th-accent);
}

.th-topbar-divider {
    width: 1px;
    height: 18px;
    background: var(--th-border);
}

.th-customer-name {
    font-size: 13px;
    color: var(--th-text-muted);
}

.th-topbar-right {
    display: flex;
    align-items: center;
    gap: 8px;
}

.th-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: 1px solid var(--th-border);
    background: var(--th-surface);
    color: var(--th-text);
    border-radius: 6px;
    font-size: 13px;
    cursor: pointer;
}

.th-btn:hover {
    background: var(--th-border);
}

.th-btn:disabled {
    opacity: .5;
    cursor: not-allowed;
}

.th-btn-primary {
    background: var(--th-accent);
    border-color: var(--th-accent);
    color: #FFFFFF;
}

.th-btn-primary:hover {
    background: var(--th-accent-hover);
}

/* Theme-Toggle (Sonne/Mond) - eigener, canvas-lokaler Zustand in localStorage, unabhaengig vom
   globalen App-Theme-Toggle (siehe layout.twig/SettingsRepository::globalTheme()). */
.th-btn-icon {
    padding: 6px;
}

.th-module-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 20px;
    background: rgba(245, 158, 11, 0.14);
    color: #F59E0B;
    font-size: 12px;
    flex-shrink: 0;
}

.th-btn-danger {
    color: #EF4444;
    border-color: rgba(239, 68, 68, .4);
}

.th-btn-danger:hover {
    background: rgba(239, 68, 68, .12);
}

/* Logische Topologie / Grundriss-Umschalter (Migration 223) - segmented control, gleiches Muster
   wie andernorts in CoreDesk (z.B. HR Ganztag/Vormittag/Nachmittag). */
.th-mode-toggle {
    display: flex;
    border: 1px solid var(--th-border);
    border-radius: 6px;
    overflow: hidden;
}

.th-mode-toggle-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border: none;
    background: var(--th-surface);
    color: var(--th-text-muted);
    font-size: 13px;
    cursor: pointer;
}

.th-mode-toggle-btn + .th-mode-toggle-btn {
    border-left: 1px solid var(--th-border);
}

.th-mode-toggle-btn.is-active {
    background: var(--th-accent);
    color: #FFFFFF;
}

/* Etagen-Toolbar (nur im Grundriss-Modus sichtbar, siehe setMode() in topology-canvas.js). */
.th-floor-toolbar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 20px;
    border-bottom: 1px solid var(--th-border);
    flex-shrink: 0;
}

.th-floor-select {
    padding: 6px 10px;
    border: 1px solid var(--th-border);
    background: var(--th-surface);
    color: var(--th-text);
    border-radius: 6px;
    font-size: 13px;
}

.th-floor-upload-label {
    margin: 0;
}

/* Grundriss-Hintergrundbild-Ebene - liegt als Geschwister-Element neben .th-canvas-inner (nicht
   darin), da sie kein Pan/Zoom mitmacht (siehe Plan: physische Ebene bewusst ohne Kanten/Pan/
   Zoom-Komplexitaet). */
.th-floor-background {
    position: absolute;
    inset: 0;
    background-position: center;
    background-repeat: no-repeat;
    background-size: contain;
    background-color: var(--th-canvas-bg);
}

.th-floor-empty {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    color: var(--th-text-muted);
    font-size: 13px;
    margin: 0;
}

.th-floor-nodes-layer {
    position: absolute;
    inset: 0;
}

.th-floor-node {
    position: absolute;
    transform: translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    cursor: grab;
    user-select: none;
}

.th-floor-node-photo {
    width: 40px;
    height: 40px;
    object-fit: contain;
    -webkit-user-drag: none;
}

.th-floor-node-label {
    font-size: 11px;
    color: var(--th-text);
    background: var(--th-surface);
    padding: 1px 6px;
    border-radius: 4px;
    white-space: nowrap;
}

.th-body-row {
    display: flex;
    flex: 1;
    min-height: 0;
}

/* Asset-Bibliothek: draggable Karten je Kategorie-Tab (Server/Netze/Clients/Mobile), siehe
   TopologyController::CATEGORY_MAP. Drag-Wiring folgt in Phase 4 - hier nur Markup/Optik. */
/* --th-accent lokal auf den Kombi-Shell-Akzent umgebogen (siehe --th-sidebar-accent in .th-body) -
   faerbt dadurch automatisch alle Kind-Elemente um, die var(--th-accent) nutzen (aktiver Tab,
   Asset-Karten-Hover), ohne Toolbar/Canvas/Kanten anzufassen, die bewusst UniFi-Cyan bleiben. */
.th-sidebar {
    width: 260px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    padding: 16px;
    background: var(--th-sidebar-bg);
    border-right: 1px solid var(--th-border);
    overflow-y: auto;
    --th-accent: var(--th-sidebar-accent);
    --th-accent-hover: var(--th-sidebar-accent-hover);
}

.th-sidebar-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
}

.th-sidebar-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--th-text);
    margin: 0;
}

/* Nur auf Mobile sichtbar (siehe @media-Block unten) - Desktop hat die Sidebar permanent offen. */
.th-sidebar-toggle,
.th-sidebar-close {
    display: none;
}

.th-sidebar-backdrop {
    display: none;
}

.th-sidebar-tabs {
    display: flex;
    gap: 4px;
    margin-bottom: 12px;
    border-bottom: 1px solid var(--th-border);
}

.th-sidebar-tab {
    flex: 1;
    padding: 6px 4px;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    color: var(--th-text-muted);
    font-size: 12px;
    cursor: pointer;
}

.th-sidebar-tab.is-active {
    color: var(--th-text);
    border-bottom-color: var(--th-accent);
}

.th-sidebar-panel {
    display: none;
    flex-direction: column;
    gap: 6px;
}

.th-sidebar-panel.is-active {
    display: flex;
}

.th-sidebar-empty {
    font-size: 12px;
    color: var(--th-text-muted);
    padding: 12px 0;
}

.th-asset-card {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    background: var(--th-surface);
    border: 1px solid var(--th-border);
    border-radius: 6px;
    cursor: grab;
    color: var(--th-text);
}

.th-asset-card:hover {
    border-color: var(--th-accent);
}

.th-asset-card-name {
    flex: 1;
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.th-asset-card-badge {
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 999px;
    background: rgba(16, 185, 129, 0.14);
    color: #10B981;
    flex-shrink: 0;
}

/* Canvas: Dot-Grid-Hintergrund per radial-gradient, Nodes als echte DOM-Elemente (nicht Canvas-
   gezeichnet, siehe Plan-Begruendung) innerhalb von .th-canvas-inner, das JS per transform
   translate/scale fuer Pan/Zoom bewegt - Node-Layer UND SVG-Kanten-Layer liegen beide darin,
   damit sie nie auseinanderdriften. */
.th-canvas-wrap {
    position: relative;
    flex: 1;
    overflow: hidden;
    background-color: var(--th-canvas-bg);
    background-image: radial-gradient(circle, var(--th-dot-grid) 1px, transparent 1px);
    background-size: 20px 20px;
    cursor: grab;
}

.th-canvas-wrap.is-panning {
    cursor: grabbing;
}

.th-canvas-inner {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0 0;
}

.th-edges-layer {
    position: absolute;
    top: 0;
    left: 0;
    overflow: visible;
    pointer-events: none;
}

.th-nodes-layer {
    position: absolute;
    top: 0;
    left: 0;
}

/* Node-Karte: frameless vertikale Darstellung (Produktbild/Icon ueber Name ueber IP/MAC), kein
   dunkler Kasten-Container um das freigestellte Produktbild - nur der Icon-Fallback (kein
   Asset-/Modell-Bild hinterlegt) bekommt noch eine dezente Karte, damit er nicht wie "nichts"
   wirkt. Siehe renderNodes() in topology-canvas.js fuer die Bild-Aufloesungs-Prioritaet
   (Asset-Foto > Modell-Katalog-Bild > Kategorie-Icon). */
.th-node {
    position: absolute;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 112px;
    padding: 6px 4px 4px;
    color: var(--th-text);
    font-size: 12px;
    cursor: grab;
    user-select: none;
    text-align: center;
}

.th-node-visual {
    position: relative;
    width: 56px;
    height: 56px;
    margin: 0 auto 4px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.th-node-photo {
    max-width: 56px;
    max-height: 56px;
    width: auto;
    height: auto;
    object-fit: contain;
    filter: drop-shadow(var(--th-node-shadow));
    /* draggable="false" im Markup reicht in den meisten Browsern, Safari braucht zusaetzlich
       -webkit-user-drag - sonst startet beim Ziehen der native Bild-DnD statt des eigenen
       mousedown-Verschiebens (Symptom: "+"-Cursor statt Hand-Cursor). */
    -webkit-user-drag: none;
    user-select: none;
}

.th-node-icon-wrap {
    width: 48px;
    height: 48px;
    border-radius: 12px;
    background: var(--th-surface);
    border: 1px solid var(--th-border);
    box-shadow: var(--th-node-shadow);
    display: flex;
    align-items: center;
    justify-content: center;
}

.th-node:hover .th-node-icon-wrap {
    border-color: var(--th-accent);
}

.th-node-icon {
    flex-shrink: 0;
    color: var(--th-text);
}

/* Opake Hintergrund-"Halo" hinter Name/IP (Nutzer-Feedback: Verbindungslinien liefen durch den
   Text durch) - die Label-Divs sitzen im DOM zwar bereits ueber dem SVG-Kanten-Layer, waren aber
   selbst transparent, sodass eine Linie durch die Buchstaben "durchschimmerte". Hintergrund =
   Canvas-Farbe statt Card-Farbe, damit es wie eine echte Unterbrechung der Linie wirkt statt wie
   ein Etikett. */
.th-node-label {
    max-width: 112px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    font-size: 11.5px;
    background: var(--th-canvas-bg);
    padding: 1px 5px;
    border-radius: 4px;
}

.th-node-meta {
    max-width: 112px;
    font-size: 10px;
    color: var(--th-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    background: var(--th-canvas-bg);
    padding: 0 5px;
    border-radius: 4px;
}

/* Filigraner statt vormals 10px (Nutzer-Feedback: Status-Punkte sollen dezenter wirken). Positiver
   statt negativer Offset (innerhalb von .th-node-visual statt ueberhaengend) - sitzt dadurch bei
   Produktbild, Icon-Fallback und ISP-Logo an derselben Stelle relativ zur sichtbaren Bildflaeche,
   unabhaengig davon, ob diese den 56px-Visual-Bereich voll ausfuellt (Foto) oder darin zentriert
   ist (48px-Icon-Box). */
.th-node-led {
    position: absolute;
    right: 2px;
    bottom: 2px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--th-led-inactive);
    border: 1.5px solid var(--th-canvas-bg);
}

.th-node-led--active { background: #10B981; box-shadow: 0 0 6px rgba(16, 185, 129, 0.6); }
.th-node-led--warning { background: #F59E0B; box-shadow: 0 0 6px rgba(245, 158, 11, 0.6); }
.th-node-led--offline { background: #FF6B6B; box-shadow: 0 0 6px rgba(255, 107, 107, 0.6); }
.th-node-led--critical { background: #FF6B6B; box-shadow: 0 0 6px rgba(255, 107, 107, 0.8); }
.th-node-led--inactive { background: var(--th-led-inactive); }

.th-edge-path {
    fill: none;
    stroke-width: 1.5;
    cursor: pointer;
    transition: opacity .1s, filter .1s;
}

/* Grundfarbe/Strichelung nach Verbindungsart statt nach Status (Nutzer-Feedback, UniFi-Vorbild:
   Kabel durchgezogen blau, WLAN gestrichelt hellblau, WAN/Internet violett) - siehe
   edgeVisualType() in topology-canvas.js. .th-edge-path--warning/--down werden ZUSAETZLICH
   gesetzt und ueberschreiben nur die Farbe (Kaskaden-Reihenfolge: nach den Medium-Regeln), die
   Strichelung des Mediums bleibt dabei erhalten - so ist "welches Medium" UND "gestoert?"
   gleichzeitig ablesbar. */
.th-edge-path--ethernet { stroke: #3B82F6; color: #3B82F6; stroke-dasharray: none; }
.th-edge-path--wifi { stroke: #60A5FA; color: #60A5FA; stroke-dasharray: 4 4; }
.th-edge-path--wan { stroke: #8B5CF6; color: #8B5CF6; stroke-dasharray: none; }
.th-edge-path--warning { stroke: #F59E0B; color: #F59E0B; }
.th-edge-path--down { stroke: #FF6B6B; color: #FF6B6B; }
.th-edge-path--rubberband { stroke: var(--th-accent); stroke-dasharray: 4 4; pointer-events: none; }

/* Hover/Selektions-Zustand fuer Kanten-Loeschen (siehe topology-canvas.js renderEdges()/
   selectEdge()): staerkere Deckkraft + leichtes Gluehen statt Farbwechsel, damit der Status
   (aktiv/warnung/down) weiterhin an der Grundfarbe erkennbar bleibt. Eine unsichtbare, breitere
   "Hit-Area"-Kopie jeder Kante (.th-edge-hit) sitzt darueber, damit Hover/Klick nicht nur auf der
   duennen 2px-Linie treffen muessen. Hover-Erkennung laeuft rein ueber CSS :hover auf der
   umschliessenden <g class="th-edge-group"> (SVG-Gruppen unterstuetzen :hover normal) - kein JS-
   Mousemove-Tracking noetig. Der Loesch-Button ist nur bei Hover/Selektion sichtbar. */
.th-edges-layer .th-edge-group:hover .th-edge-path,
.th-edge-path.is-selected {
    opacity: 1;
    filter: drop-shadow(0 0 4px currentColor);
}

.th-edge-hit {
    fill: none;
    stroke: transparent;
    stroke-width: 16;
    cursor: pointer;
    pointer-events: stroke;
}

.th-edge-delete-btn {
    cursor: pointer;
    opacity: 0;
    transition: opacity .1s;
    pointer-events: none;
}

/* pointer-events bleibt bewusst NUR in diesen beiden Zustaenden aktiv (nicht global auf
   .th-edge-delete-btn * gesetzt) - sonst waere der unsichtbare Button (opacity:0) im
   Ruhezustand trotzdem klickbar, weil er exakt in der Kanten-Mitte sitzt (deckt sich mit der
   Hit-Area) und ein normaler Klick zur Selektion versehentlich sofort die Kante loeschen wuerde. */
.th-edges-layer .th-edge-group:hover .th-edge-delete-btn,
.th-edge-delete-btn.is-selected {
    opacity: 1;
    pointer-events: auto;
}

.th-edge-delete-btn circle {
    fill: var(--th-surface);
    stroke: var(--th-border);
    stroke-width: 1;
}

.th-edge-delete-btn:hover circle {
    fill: #FF6B6B;
    stroke: #FF6B6B;
}

.th-edge-delete-btn path {
    stroke: var(--th-text-muted);
    stroke-width: 1.6;
    stroke-linecap: round;
}

.th-edge-delete-btn:hover path {
    stroke: #FFFFFF;
}

/* Rechtsklick-Kontextmenue "Verbindung trennen" (siehe topology-canvas.js onEdgeContextMenu()). */
.th-context-menu {
    position: fixed;
    z-index: 50;
    min-width: 180px;
    background: var(--th-surface);
    border: 1px solid var(--th-border);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);
    padding: 4px;
}

.th-context-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 8px 10px;
    background: none;
    border: none;
    border-radius: 6px;
    color: var(--th-text);
    font-size: 12.5px;
    text-align: left;
    cursor: pointer;
}

.th-context-menu-item:hover {
    background: var(--th-border);
}

.th-context-menu-item.is-danger {
    color: #FF6B6B;
}

/* Pulse-Partikel auf aktiven Kanten - rein deklarativ via <animateMotion>, kein JS-Loop. */
.th-edge-pulse {
    fill: #6EE7B7;
    filter: drop-shadow(0 0 3px rgba(16, 185, 129, 0.9));
}

/* Connect-Handle: kleiner Kreis am rechten Rand des Visual-Bereichs, per mousedown zum Verbinden
   ziehen (siehe topology-canvas.js onCanvasMouseDown/onWindowMouseMove state.dragMode ===
   'connect'). Innerhalb von .th-node-visual positioniert (nicht der ganzen Karte), damit die
   Position unabhaengig von der Label-Laenge stabil auf Icon-Hoehe bleibt. Nur sichtbar bei Hover,
   damit die Nodes im Ruhezustand nicht ueberladen wirken. */
.th-node-handle {
    position: absolute;
    right: -8px;
    top: 50%;
    transform: translateY(-50%);
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: var(--th-accent);
    border: 2px solid var(--th-canvas-bg);
    cursor: crosshair;
    opacity: 0;
    transition: opacity .1s;
}

.th-node:hover .th-node-handle {
    opacity: 1;
}

.th-canvas-empty {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    text-align: center;
    pointer-events: none;
}

.th-canvas-empty-title {
    font-size: 14px;
    font-weight: 600;
    color: var(--th-text);
    margin: 0 0 4px;
}

.th-canvas-empty-subtitle {
    font-size: 12px;
    color: var(--th-text-muted);
    margin: 0;
}

.th-minimap {
    position: absolute;
    right: 16px;
    bottom: 16px;
    width: 180px;
    height: 120px;
    background: var(--th-surface);
    opacity: .9;
    border: 1px solid var(--th-border);
    border-radius: 6px;
}

/* Detail-Drawer: Fetch+Render-Wiring aus Phase 5 (siehe topology-canvas.js renderDrawerContent()). */
/* Gleiche Kombi-Shell-Akzent-Umbiegung wie .th-sidebar - Hintergrund bleibt --th-surface (das
   entspricht bereits der Kombi-Shell-Card-Flaeche, siehe Kommentar in .th-body). */
.th-drawer {
    width: 360px;
    flex-shrink: 0;
    background: var(--th-surface);
    border-left: 1px solid var(--th-border);
    overflow-y: auto;
    --th-accent: var(--th-sidebar-accent);
    --th-accent-hover: var(--th-sidebar-accent-hover);
}

.th-drawer[hidden] {
    display: none;
}

.th-drawer-content {
    padding: 20px;
}

.th-drawer-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
}

.th-drawer-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--th-text);
    margin: 0;
    word-break: break-word;
}

.th-drawer-empty {
    font-size: 13px;
    color: var(--th-text-muted);
}

.th-drawer-asset-link {
    display: inline-flex;
    width: 100%;
    justify-content: center;
    margin-bottom: 16px;
}

.th-drawer-fields {
    margin: 0 0 20px;
}

.th-drawer-fields dt {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--th-text-muted);
    margin: 12px 0 2px;
}

.th-drawer-fields dt:first-child {
    margin-top: 0;
}

.th-drawer-fields dd {
    font-size: 13px;
    color: var(--th-text);
    margin: 0;
    word-break: break-word;
}

.th-drawer-subheading {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--th-text-muted);
    margin: 0 0 8px;
}

.th-drawer-ticket-list {
    list-style: none;
    margin: 0 0 20px;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.th-drawer-ticket-list a {
    display: block;
    padding: 8px 10px;
    background: var(--th-border);
    border-radius: 6px;
    color: var(--th-text);
    font-size: 12px;
    text-decoration: none;
}

.th-drawer-ticket-list a:hover {
    background: var(--th-drawer-row-hover);
}

.th-drawer-create-ticket {
    display: inline-flex;
    width: 100%;
    justify-content: center;
}

/* Mobile (iPhone/Tablet-Portrait): Sidebar wird Off-Canvas-Panel statt permanenter Spalte,
   Detail-Drawer wird Vollbild-Overlay statt 360px-Spalte, Minimap verschwindet (zu wenig Platz
   auf dem Canvas). Breakpoint an .archive-view (app.css) angelehnt, dem naechsten strukturellen
   Vorbild fuer ein Sidebar/Canvas/Drawer-3-Spalten-Layout. */
@media (max-width: 900px) {
    .th-sidebar-toggle,
    .th-sidebar-close {
        display: inline-flex;
    }

    /* Kundenname/Divider entfallen (stehen bereits im Body-Titel), Auto-Layout-Button wird
       Icon-only - sonst laeuft .th-topbar-right auf schmalen Displays aus dem Viewport heraus
       (kein flex-wrap/overflow-Handling im Desktop-Grundlayout). */
    .th-topbar {
        padding: 8px 12px;
        gap: 8px;
    }

    .th-topbar-divider,
    .th-customer-name {
        display: none;
    }

    #th-auto-layout .th-btn-label {
        display: none;
    }

    .th-sidebar {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        width: min(85vw, 320px);
        z-index: 200;
        background: var(--th-canvas-bg);
        transform: translateX(-100%);
        transition: transform .25s ease;
    }

    .th-sidebar.is-open {
        transform: translateX(0);
    }

    .th-sidebar-backdrop.is-open {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 190;
        background: rgba(0, 0, 0, .5);
    }

    .th-minimap {
        display: none;
    }

    .th-drawer {
        position: fixed;
        inset: 0;
        z-index: 210;
        width: auto;
        border-left: none;
    }
}

/* Kundenuebergreifende Uebersicht (templates/topology/index.twig). */
.th-index-shell {
    max-width: 800px;
    margin: 0 auto;
    padding: 40px 20px;
}

.th-index-header h1 {
    font-size: 20px;
    margin: 0 0 4px;
}

.th-index-subtitle {
    color: var(--th-text-muted);
    font-size: 13px;
    margin: 0 0 24px;
}

.th-index-empty {
    padding: 24px;
    border: 1px dashed var(--th-border);
    border-radius: 8px;
    color: var(--th-text-muted);
    font-size: 13px;
    text-align: center;
}

.th-index-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 12px;
}

.th-index-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px;
    background: var(--th-surface);
    border: 1px solid var(--th-border);
    border-radius: 8px;
    color: var(--th-text);
}

.th-index-card:hover {
    border-color: var(--th-accent);
    text-decoration: none;
}

.th-index-card-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    color: var(--th-text-muted);
}

/* CoreDesk Campaigns: 3-Schritt-Erstellungs-Wizard - eigenstaendige Seite statt Modal (siehe
   campaign-wizard.js), daher eigener .cw-* Namensraum statt Wiederverwendung von .tdv2-triage-*
   (das ist an ticket-new-design.css/den Ticket-Modal-Kontext gebunden). */
.cw-wizard {
    max-width: 560px;
    margin: 0 auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 12px;
    padding: 32px;
}

.cw-progress {
    display: flex;
    gap: 8px;
    margin-bottom: 24px;
}

.cw-progress span {
    flex: 1;
    height: 4px;
    border-radius: 2px;
    background: var(--color-border);
}

.cw-progress span.is-active {
    background: var(--color-accent);
}

.cw-step h2 {
    font-size: 16px;
    font-weight: 600;
    margin: 0 0 16px;
}

.cw-step .field {
    margin-bottom: 16px;
}

.cw-step .field label {
    display: block;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
    margin-bottom: 4px;
}

.cw-criteria-options {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin-bottom: 16px;
}

.cw-criteria-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    cursor: pointer;
}

.cw-criteria-option:has(input:checked) {
    border-color: var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 8%, transparent);
}

.cw-person-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    margin-bottom: 16px;
    cursor: pointer;
}

.cw-preview-card {
    text-align: center;
    padding: 32px;
    background: var(--color-bg-subtle, #f8fafc);
    border-radius: 8px;
}

.cw-preview-count {
    font-size: 40px;
    font-weight: 700;
    color: var(--color-accent);
    font-variant-numeric: tabular-nums;
}

.cw-preview-label {
    font-size: 13px;
    color: var(--color-text-muted);
    margin-top: 4px;
}

.cw-step-error {
    color: var(--color-danger, #dc2626);
    font-size: 13px;
    margin-top: 8px;
}

.cw-foot {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 24px;
    padding-top: 16px;
    border-top: 1px solid var(--color-border);
}

/* Projekt-Wizard (Phase 1, templates/projects/wizard.twig): erweitert den .cw-*-Namensraum der
   Campaign-Wizard-Seite - braucht mehr Breite (Team-Picker-Kandidatenlisten, Arbeitspaket-Zeilen)
   als das schmale 560px-Campaign-Layout, daher eigener Breiten-Modifier statt .cw-wizard selbst
   zu aendern (wuerde auch die Campaign-Seite beeinflussen). */
.cw-wizard--wide {
    max-width: 760px;
}

.cw-key-preview {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    color: var(--color-text-muted);
    margin: -8px 0 16px;
}

.cw-ticket-selected {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    margin-top: 8px;
    padding: 8px 12px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    font-size: 13px;
}

.cw-optional-hint {
    font-size: 12px;
    font-weight: 500;
    color: var(--color-text-muted);
}

/* Arbeitspaket-Schritt als Master-Detail-Split (Nutzer-Feedback: eine lange, untereinander
   gestapelte Liste wird bei vielen Arbeitspaketen unuebersichtlich) - links der Editor fuer das
   gerade ausgewaehlte Arbeitspaket, rechts eine kompakte, klickbare Liste aller Arbeitspakete.
   Der JS-Zustand (project-wizard.js, state.phases) haelt alle Werte auch fuer nicht sichtbare
   Arbeitspakete vor, nichts geht beim Umschalten verloren. */
.cw-phase-layout {
    display: grid;
    grid-template-columns: 1fr 260px;
    gap: 20px;
    align-items: start;
}

@media (max-width: 720px) {
    .cw-phase-layout {
        grid-template-columns: 1fr;
    }
}

.cw-phase-editor {
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 16px;
    min-height: 120px;
}

.cw-phase-empty {
    color: var(--color-text-muted);
    font-size: 13px;
    text-align: center;
    padding: 24px 8px;
}

.cw-phase-editor-fields .field-grid {
    margin-bottom: 12px;
}

.cw-phase-list-panel {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.cw-phase-add-btn {
    width: 100%;
}

.cw-phase-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    max-height: 420px;
    overflow-y: auto;
}

.cw-phase-list-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    padding: 8px 10px;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    cursor: pointer;
    font-size: 12.5px;
}

.cw-phase-list-item:hover {
    border-color: var(--color-accent);
}

.cw-phase-list-item.is-active {
    border-color: var(--color-accent);
    background: color-mix(in srgb, var(--color-accent) 8%, transparent);
}

.cw-phase-list-item-info {
    min-width: 0;
    flex: 1;
}

.cw-phase-list-item-name {
    display: block;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.cw-phase-list-item-meta {
    display: block;
    color: var(--color-text-muted);
    font-size: 11.5px;
    margin-top: 2px;
}

.cw-phase-list-item-remove {
    flex-shrink: 0;
    border: none;
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    font-size: 15px;
    line-height: 1;
    padding: 2px 4px;
}

.cw-phase-list-item-remove:hover {
    color: var(--color-danger, #dc2626);
}

.cw-budget-summary {
    display: flex;
    gap: 24px;
    margin-top: 16px;
    padding: 16px;
    background: var(--color-bg-subtle, #f8fafc);
    border-radius: 8px;
}

.cw-budget-summary-item {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.cw-budget-summary-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--color-text-muted);
}

.cw-budget-summary-value {
    font-size: 18px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.cw-team-panel {
    margin-bottom: 24px;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--color-border);
}

.cw-team-panel:last-of-type {
    border-bottom: none;
}

.cw-team-panel-title {
    font-size: 13px;
    font-weight: 700;
    margin: 0 0 10px;
}

.cw-summary-card {
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 12px 16px;
    margin-bottom: 16px;
}

.cw-summary-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 0;
    font-size: 13px;
    border-bottom: 1px solid var(--color-border);
}

.cw-summary-row:last-child {
    border-bottom: none;
}

.cw-summary-label {
    color: var(--color-text-muted);
}

.cw-summary-value {
    font-weight: 600;
    text-align: right;
}

.cw-summary-section-title {
    font-size: 13px;
    font-weight: 700;
    margin: 20px 0 8px;
}

.cw-summary-phase-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding: 8px 0;
    font-size: 13px;
    border-bottom: 1px solid var(--color-border);
}

.cw-summary-phase-row:last-child {
    border-bottom: none;
}

/* ================================================================
   CoreDesk Control (Executive-/Operations-Dashboard, Nutzer-Feedback 2026-08-06, Redesign-Runde 2
   nach Mockup-Screenshot) - nutzt ausschliesslich bestehende --color-* und --status-*-Tokens (KEIN
   eigenes Farbschema), damit Dark/Light/UniFi-Theme automatisch funktionieren, exakt wie ueberall
   sonst im Projekt. Das bestehende [data-theme="dark"] IST bereits die vom Nutzer gewuenschte
   "tiefes Dunkelblau + dezente Cards"-UniFi-Optik - kein Grund, eine zweite Palette zu erfinden.
   ================================================================ */
.control-wrap {
    display: flex;
    flex-direction: column;
    gap: 20px;
    padding: 8px 0 48px;
}

.control-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

/* Versionsanzeige (Nutzer-Wunsch 2026-08-17, aus der Sidebar hierher verlagert) - dezent, damit sie
   nicht mit den eigentlichen Toolbar-Aktionen (Perioden-Filter, TV-Modus) konkurriert. */
.control-version-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
    font-size: 12px;
    font-weight: 600;
    color: var(--color-text-muted);
}

.control-version-badge svg { flex-shrink: 0; }

.control-period-toggle {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    overflow: hidden;
}

.control-period-toggle a {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 16px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-surface);
}

.control-period-toggle a:hover { text-decoration: none; background: var(--color-bg); }
.control-period-toggle a.is-active { background: var(--color-accent); color: #FFFFFF; }
.control-period-toggle a + a { border-left: 1px solid var(--color-border); }

.ca-tab-toolbar { margin-bottom: 16px; }

.control-kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 14px;
}

.control-kpi-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 18px 18px 16px;
    box-shadow: var(--shadow-card);
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.control-kpi-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.control-kpi-label { font-size: 11.5px; font-weight: 700; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: .05em; }

.control-kpi-icon {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Feste Farbzuordnung je Kennzahl-Art statt einer Bruch-abhaengigen Ampel (Mockup 2026-08-06) -
   Blau fuer reine Mengen-/Zeit-Kennzahlen, Gruen fuer SLA-Erfuellung, Amber fuer die
   Abrechenbar-Quote (Geld-Kontext). */
.control-kpi-icon--accent { background: var(--color-accent-soft); color: var(--color-accent); }
.control-kpi-icon--success { background: var(--status-active-soft); color: var(--status-active); }
.control-kpi-icon--warning { background: var(--status-warning-soft); color: var(--status-warning); }

.control-kpi-value-row { display: flex; align-items: baseline; gap: 10px; }
.control-kpi-value { font-size: 30px; font-weight: 800; color: var(--color-text); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.control-kpi-chevron { font-size: 12.5px; font-weight: 700; }
.control-kpi-chevron--up { color: var(--status-active); }
.control-kpi-chevron--down { color: var(--color-critical); }
.control-kpi-subtitle { font-size: 12px; color: var(--color-text-muted); }

/* 2:1-Spaltenraster (Mockup 2026-08-06: Chart+Heatmap in einer Zeile, Techniker+Kategorien in der
   naechsten) - jede Zeile ein eigenes .control-panels-grid, items-stretch ist der CSS-Grid-Default
   und sorgt dafuer, dass beide Panels einer Zeile gleich hoch werden. */
.control-panels-grid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 14px;
}

.control-panels-grid + .control-panels-grid { margin-top: 14px; }

@media (max-width: 900px) {
    .control-panels-grid { grid-template-columns: 1fr; }
}

.control-panel {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 18px 20px;
    box-shadow: var(--shadow-card);
}

/* Fuer die schmalere Spalte einer Zeile (Heatmap/Kategorien) - Inhalt oben, aber Fusszeile
   (Heatmap-Empfehlung) via margin-top:auto ans Panel-Ende gedrueckt, wenn die Nachbar-Spalte
   hoeher ist (Techniker-Tabelle/Chart). */
.control-panel--flex { display: flex; flex-direction: column; }

.control-panel h2 {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 700;
    color: var(--color-text);
    margin: 0 0 16px;
}

.control-panel h2 svg { color: var(--color-accent); flex: 0 0 auto; }

.control-panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.control-panel-head h2 { margin-bottom: 0; }

/* Titel+Untertitel-Block (Chart/Kategorien) - h2 verliert seinen sonst ueblichen 16px-Abstand
   (siehe .control-panel h2), stattdessen traegt der Block selbst den Abstand zum Inhalt darunter,
   damit Titel und Untertitel eng zusammenstehen. */
.control-panel-title-group h2 { margin-bottom: 0; }
.control-panel-title-group { margin-bottom: 16px; }
.control-panel-subtitle { font-size: 12px; color: var(--color-text-muted); margin: 2px 0 0; }

.control-panel-link {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--color-accent);
    margin-top: 2px;
}
.control-panel-link:hover { text-decoration: underline; }
.control-panel-link svg { width: 12px; height: 12px; }

.control-trend-chart { width: 100%; height: auto; overflow: visible; margin-top: 4px; }
.control-trend-grid-line { stroke: var(--color-border); stroke-width: 1; stroke-dasharray: 2 3; }
.control-trend-axis-value { fill: var(--color-text-muted); font-size: 8.5px; font-variant-numeric: tabular-nums; }
.control-trend-area { stroke: none; }
.control-trend-line--created { fill: none; stroke: var(--color-accent); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.control-trend-line--resolved { fill: none; stroke: var(--status-active); stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 4 4; }
.control-trend-dot--created { fill: var(--color-accent); stroke: var(--color-surface); stroke-width: 1.25; }
.control-trend-dot--resolved { fill: var(--status-active); stroke: var(--color-surface); stroke-width: 1.25; }
.control-trend-axis-label { fill: var(--color-text-muted); font-size: 9px; }

.control-trend-legend { display: flex; gap: 16px; font-size: 12px; color: var(--color-text-muted); flex: 0 0 auto; }
.control-trend-legend span { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.control-legend-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.control-legend-dot--created { background: var(--color-accent); }
.control-legend-dot--resolved { background: var(--status-active); }

/* Kategorie-Zeile bewusst gestapelt (Label ueber dem Balken) statt nebeneinander mit fixer
   Label-Breite - vermeidet mitten im Wort abgeschnittene lange Kategorienamen (Nutzer-Fund
   2026-08-06), gaengiges Muster fuer horizontale Balkendiagramme mit variabler Beschriftungslaenge. */
.ca-category-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); column-gap: 24px; }

.control-category-row { margin-bottom: 14px; font-size: 13px; }
.control-category-row:last-child { margin-bottom: 0; }
.control-category-row-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 5px; }
.control-category-label { color: var(--color-text); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.control-category-percent { flex: 0 0 auto; color: var(--color-text-muted); font-variant-numeric: tabular-nums; font-size: 12px; }
.control-category-bar-track { height: 8px; background: var(--color-bg); border: 1px solid var(--color-border); border-radius: 4px; overflow: hidden; }
.control-category-bar-fill { height: 100%; border-radius: 4px; min-width: 2px; }
.control-category-bar-fill--primary { background: var(--color-accent); }
.control-category-bar-fill--secondary { background: var(--status-warning); }

/* Echter, berechneter Upselling-Hinweis (siehe ControlDashboardRepository::topCategoryIncreaseByCustomer())
   - erscheint nur, wenn ein Kunde+Kategorie-Paar tatsaechlich einen belastbaren Anstieg zeigt;
   sonst bleibt das Panel ohne diese Box, statt einen erfundenen Platzhalter zu zeigen. */
.control-upsell-box {
    margin-top: 16px;
    padding: 12px 14px;
    border-radius: var(--radius-sm);
    background: var(--status-warning-soft);
    border: 1px solid color-mix(in srgb, var(--status-warning) 40%, transparent);
}
.control-upsell-head { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--status-warning); margin-bottom: 4px; }
.control-upsell-head svg { color: var(--status-warning); }
.control-upsell-box p { margin: 0; font-size: 12.5px; color: var(--color-text); line-height: 1.5; }

.control-heatmap-grid {
    display: grid;
    grid-template-columns: 48px repeat(5, 1fr);
    gap: 6px;
}

.control-heatmap-corner {}
.control-heatmap-col-label,
.control-heatmap-row-label { font-size: 11px; color: var(--color-text-muted); display: flex; align-items: center; justify-content: center; }
.control-heatmap-row-label { justify-content: flex-start; font-weight: 600; }

/* Randlose "Pill"-Zellen statt umrandeter Quadrate mit Zahl (Mockup 2026-08-06) - die Zaehlung
   steckt nur noch im Tooltip, die Flaeche selbst transportiert die Staerke rein ueber die
   Farbintensitaet. Weiterhin eine Basisflaeche (var(--color-bg)) bei Intensitaet 0, damit das
   Grid bei leeren Zeitfenstern nicht komplett unsichtbar wird (Nutzer-Fund 2026-08-06). */
/* Feste Hoehe statt aspect-ratio (Nutzer-Fund 2026-08-06: bei breiten Spalten - z.B. Desktop-
   Breakpoint der Heatmap-Spalte im 2:1-Zeilenraster - wuchs die aspect-ratio-Zellenhoehe proportional
   mit und ergab riesige eiartige Kapseln statt kompakter Kacheln). Flache Rechtecke mit dezenter
   Rundung bleiben unabhaengig von der Spaltenbreite gleich hoch. */
.control-heatmap-cell {
    height: 32px;
    border-radius: var(--radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: color-mix(in srgb, var(--color-text) calc(35% + var(--intensity, 0) * 65%), var(--color-text-muted));
    background: color-mix(in srgb, var(--color-accent) calc(var(--intensity, 0) * 80%), var(--color-bg));
    box-shadow: inset 0 0 0 1px var(--color-border);
    cursor: default;
}

.control-heatmap-legend { display: flex; align-items: center; gap: 8px; margin-top: 12px; font-size: 11px; color: var(--color-text-muted); }
.control-heatmap-legend-scale { display: flex; height: 8px; width: 90px; border-radius: 4px; overflow: hidden; border: 1px solid var(--color-border); }
.control-heatmap-legend-scale span { flex: 1; }

/* margin-top:auto schiebt die Fusszeile (Insight + Empfehlung) ans Ende des Panels, wenn die
   Nachbarspalte (Chart bzw. Techniker-Tabelle) hoeher ist als der Heatmap-Inhalt selbst - nur
   wirksam innerhalb von .control-panel--flex (display:flex;flex-direction:column). */
.control-heatmap-footer { margin-top: auto; padding-top: 14px; }
.control-heatmap-insight { margin: 0; font-size: 12.5px; color: var(--color-text-muted); }
.control-heatmap-insight strong { color: var(--color-text); font-weight: 700; }
.control-heatmap-recommendation-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 6px; }
.control-heatmap-recommendation-label { font-size: 12.5px; color: var(--color-text-muted); }
.control-heatmap-recommendation { font-size: 12.5px; font-weight: 700; color: var(--color-accent); }
.control-heatmap-recommendation:hover { text-decoration: underline; }

.control-tech-name-cell { display: flex; align-items: center; gap: 10px; }

/* Status-Pille fuer die Techniker-Tabelle: uebernimmt dieselben --presence-color/--presence-soft
   Variablen wie der Avatar-Ring (.tech-avatar--{status}, siehe avatar.twig) statt einer zweiten
   Status-Farbzuordnung - eine Quelle der Wahrheit fuer "welche Farbe bedeutet welcher
   Anwesenheitsstatus". */
.control-status-pill {
    display: inline-flex;
    align-items: center;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 700;
    background: var(--presence-soft);
    color: var(--presence-color);
}

.control-percent--good { color: var(--status-active); font-weight: 700; }
.control-percent--warn { color: var(--status-warning); font-weight: 700; }
.control-percent--bad { color: var(--color-critical); font-weight: 700; }

/* TV/Kiosk-Modus: Sidebar/Topbar ausblenden + vollflaechig, fuer Wallboard-Monitore (Nutzer-
   Feedback 2026-08-06). Nutzt die Fullscreen-API statt eines eigenen "?kiosk=1"-Query-Modus wie
   das oeffentliche Wallboard (siehe App\Controllers\WallboardController) - Control ist login-
   pflichtig, ein eigener kiosk-Query-Modus waere hier kein Sicherheitsgewinn. */
body.control-tv-mode .sidebar,
body.control-tv-mode .topbar { display: none !important; }
body.control-tv-mode .control-kpi-value { font-size: 38px; }
body.control-tv-mode #control-tv-exit {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 999;
}

/* ============================================================================
   Kunden-Analytics-Detailseite (/customers/analytics/{id}, Nutzer-Wunsch 2026-08-09) - bewusst
   groesstenteils die vorhandenen .control-kpi-* / .control-panel*-Bausteine wiederverwendet (siehe
   CoreDesk Control weiter oben) statt eigener KPI-/Panel-Klassen, da beide Seiten dasselbe
   "BI-Dashboard"-Vokabular sprechen sollen. Nur die Perioden-Hero-Bar und der Status-Donut sind
   hier neu, weil es dafuer bislang keine Entsprechung gibt.
   ============================================================================ */
/* Begrenzt die Rendergroesse des geteilten .control-trend-chart NUR auf dieser Seite. Zwei
   vorangegangene Nutzer-Funde am 2026-08-11: (1) preserveAspectRatio="none" auf eine feste Hoehe
   gestreckt verzog Schrift/Punkte umso staerker horizontal, je breiter der Browser wurde; (2) die
   Korrektur auf proportionales Skalieren (preserveAspectRatio="meet") loeste die Verzerrung, liess
   aber die eingebettete SVG-<text> auf schmalen Panels bis zur Unleserlichkeit schrumpfen, weil
   Schriftgroesse an denselben Skalierungsfaktor wie die Geometrie gekoppelt war. Jetzt dauerhafte
   Loesung: Achsen-Beschriftung liegt gar nicht mehr im SVG (siehe .ca-trend-yaxis/.ca-trend-xaxis
   unten), sondern als HTML in echten, von der SVG-Skalierung unabhaengigen CSS-Pixeln - das SVG
   selbst enthaelt nur noch Geometrie und darf wieder frei per preserveAspectRatio="none" auf die Box
   strecken (display:block verhindert dabei die sonst uebliche Inline-Baseline-Luecke, die die
   Y-Achsen-Ueberlagerung ein paar Pixel verschieben wuerde). max-height bleibt als Sicherheitsnetz
   gegen ausufernde Hoehe auf extrem breiten Monitoren. */
.ca-trend-chart-compact { display: block; width: 100%; height: auto; max-height: 300px; }

/* Groesserer, unsichtbarer Hover-Trefferbereich um jeden Datenpunkt (r=9 statt der sichtbaren r=3) -
   der sichtbare Punkt bleibt klein, der Tooltip loest aber schon frueher aus (siehe Tooltip-Script
   im Template). */
.ca-trend-hit-area { fill: transparent; cursor: pointer; }

/* HTML-Overlay fuer die Y-Achsen-Werte (0/2/4/6/8) des Trend-Charts - siehe grosser Kommentar zu
   .ca-trend-chart-compact oben. .ca-trend-chart-plot ist der position:relative-Rahmen genau um das
   SVG; die Labels liegen absolut darueber und nutzen dieselben grid.y-Werte wie die SVG-Gitterlinien
   (als Prozent von trendChart.height), bleiben dadurch bei jeder Panel-Breite exakt auf Hoehe ihrer
   Linie, ohne selbst von der SVG-Skalierung betroffen zu sein. */
.ca-trend-chart-plot { position: relative; }
.ca-trend-yaxis { position: absolute; inset: 0; pointer-events: none; }
.ca-trend-yaxis-label {
    position: absolute;
    left: 0;
    transform: translateY(-50%);
    text-align: right;
    font-size: 11px;
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* HTML-Flexbox-Zeile fuer die X-Achsen-Monatslabels, unterhalb des SVGs statt darin (siehe
   .ca-trend-chart-compact-Kommentar). padding-left/-right spiegeln trendChart.paddingLeft/
   paddingRight als Prozent von trendChart.width, damit space-between dieselbe Plot-Breite nutzt wie
   das SVG und jedes Label unter seinem Datenpunkt sitzt. */
.ca-trend-xaxis {
    display: flex;
    justify-content: space-between;
    margin-top: 6px;
    font-size: 11px;
    color: var(--color-text-muted);
}
.ca-trend-xaxis-label { white-space: nowrap; }

.ca-detail-hero {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 16px 20px;
    box-shadow: var(--shadow-card);
    margin-bottom: 16px;
}

.ca-detail-hero-identity { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ca-detail-hero-name { display: flex; align-items: center; gap: 8px; font-size: 18px; font-weight: 700; }
.ca-detail-hero-meta { font-size: 12.5px; color: var(--color-text-muted); }

.ca-detail-hero-actions { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }

.ca-period-form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ca-period-form select,
.ca-period-form input[type="date"] { max-width: 160px; }

.ca-export-actions { display: flex; align-items: center; gap: 8px; }

/* Deutlich groesserer Donut + dickerer Ring + engerer Abstand zur Legende (Nutzer-Feedback
   2026-08-09: wirkte "verloren" in der Card) - Ring-Radius von 54 auf 80 (~1.5x Aussendurchmesser),
   stroke-width von 18 auf 30 (~68% Cutout statt ~71%, ein spuerbar kraeftigerer Ring). Der
   Umfang fuers stroke-dasharray in buildStatusDonut() muss synchron zu r=80 bleiben. */
.ca-donut-wrap { display: flex; align-items: center; justify-content: center; gap: 16px; flex-wrap: wrap; height: 100%; }
.ca-donut-svg { flex: 0 0 auto; transform: rotate(-90deg); }
.ca-donut-segment { fill: none; stroke-width: 30; }
.ca-donut-center-value { font-size: 24px; font-weight: 700; fill: var(--color-text); font-variant-numeric: tabular-nums; }
.ca-donut-center-label { font-size: 11px; fill: var(--color-text-muted); text-transform: uppercase; letter-spacing: .02em; }

.ca-donut-legend { display: flex; flex-direction: column; gap: 8px; font-size: 13px; font-weight: 500; min-width: 0; }
.ca-donut-legend-row { display: flex; align-items: center; gap: 7px; white-space: nowrap; }
.ca-donut-legend-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.ca-donut-legend-label { color: var(--color-text); }
.ca-donut-legend-count { color: var(--color-text-muted); margin-left: auto; padding-left: 10px; font-variant-numeric: tabular-nums; }

/* Knapperes Card-Padding NUR fuer die Donut-Kachel (Nutzer-Feedback 2026-08-09) - .control-panel
   selbst bleibt unveraendert, wird auch von CoreDesk Control genutzt. */
.ca-donut-panel { padding: 16px; }

/* Executive-Ranking-Grid auf /customers/analytics (Nutzer-Wunsch 2026-08-09) - flex-shrink:0 haelt
   den Block auf natuerlicher Hoehe innerhalb des page-seamless-split-Flex-Layouts (siehe
   .asset-list-shell flex:1 weiter oben), die Tabellen-Shell darunter bekommt den Rest des
   Viewports und behaelt ihr eigenes Scrollen. */
.ca-ranking-section {
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 16px 20px 0;
}

.ca-ranking-section-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.ca-ranking-section-title { font-size: 15px; font-weight: 700; color: var(--color-text); }

.ca-ranking-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 14px;
}

.ca-ranking-card { padding: 16px; }

.ca-ranking-list { display: flex; flex-direction: column; gap: 4px; list-style: none; margin: 0; padding: 0; }

.ca-ranking-item {
    display: flex;
    align-items: center;
    gap: 10px;
    height: 34px;
}
.ca-ranking-item + .ca-ranking-item { border-top: 1px solid var(--color-border); }

/* Platzierungs-Abzeichen: 1 hebt sich farblich ab, 2/3 dezent getoent, 4/5 nur die Zahl - bewusst
   zurueckhaltend statt Gold/Silber/Bronze-Hexfarben, damit es zum neutralen Slate-Look passt und
   in beiden Themes funktioniert. */
.ca-rank-badge {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.ca-rank-badge--1 { background: var(--color-accent); color: #fff; }
.ca-rank-badge--2,
.ca-rank-badge--3 { background: var(--color-accent-soft); color: var(--color-accent); }
.ca-rank-badge--plain { color: var(--color-text-muted); }

.ca-ranking-item-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.ca-ranking-item-name { font-size: 13px; font-weight: 600; color: var(--color-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.ca-ranking-item-name:hover { color: var(--color-accent); }
.ca-ranking-item-sub { font-size: 11.5px; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ca-ranking-item-value { flex: 0 0 auto; font-size: 13px; font-weight: 700; color: var(--color-text); font-variant-numeric: tabular-nums; }
.ca-ranking-item-value--warn { color: var(--status-warning); }

.ca-effort-bar-track {
    display: flex;
    height: 14px;
    border-radius: 4px;
    overflow: hidden;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
}
.ca-effort-bar-segment--billable { background: var(--status-active); }
.ca-effort-bar-segment--included { background: var(--status-warning); }
.ca-effort-bar-segment--internal { background: var(--color-accent); }

.ca-effort-legend { display: flex; gap: 18px; margin-top: 8px; font-size: 12.5px; color: var(--color-text-muted); flex-wrap: wrap; }
.ca-effort-legend span { display: inline-flex; align-items: center; gap: 6px; }
.ca-effort-legend .ca-donut-legend-dot--billable { background: var(--status-active); }
.ca-effort-legend .ca-donut-legend-dot--included { background: var(--status-warning); }
.ca-effort-legend .ca-donut-legend-dot--internal { background: var(--color-accent); }

/* Abrechnungstyp-Badge je Ticket in der "Tickets im Zeitraum"-Tabelle (Nutzer-Wunsch
   2026-08-15) - dieselben drei Farben wie der Aufwandsbalken/die Legende oben, damit sich
   Balken und Tabelle optisch als ein System lesen. */
.ca-billing-badge--billable { background: color-mix(in srgb, var(--status-active) 16%, transparent); color: var(--status-active); }
.ca-billing-badge--included { background: color-mix(in srgb, var(--status-warning) 16%, transparent); color: var(--status-warning); }
.ca-billing-badge--internal { background: var(--color-accent-soft); color: var(--color-accent); }
.ca-billing-badge--mixed { background: var(--status-neutral-soft); color: var(--status-neutral); }

@media (max-width: 640px) {
    .ca-detail-hero { flex-direction: column; align-items: flex-start; }
}

/* ============================================================================
   Admin-Sidebar-Verwaltung (/admin/settings/sidebar, Migration 258, Nutzer-Feedback 2026-08-08) -
   3 Drag&Drop-Spalten (Oben/Module/Utility), natives HTML5-DnD (siehe admin-sidebar-settings.js).
   ============================================================================ */
.sidebar-settings-columns {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
    margin-top: 16px;
    align-items: start;
}

@media (max-width: 900px) {
    .sidebar-settings-columns { grid-template-columns: 1fr; }
}

.sidebar-settings-card { padding: 16px; }

.sidebar-settings-card-title {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--color-text-muted);
    margin: 0 0 12px;
}

.sidebar-settings-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-height: 40px;
}

.sidebar-settings-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    cursor: grab;
}

.sidebar-settings-item.is-dragging {
    opacity: .4;
}

.sidebar-settings-item.is-hidden {
    opacity: .55;
}

.sidebar-settings-item-handle {
    color: var(--color-text-muted);
    display: inline-flex;
    flex-shrink: 0;
}

.sidebar-settings-item-icon {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--color-text-muted);
}

.sidebar-settings-item-label {
    flex: 1;
    font-size: 13px;
    color: var(--color-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-settings-item-visibility {
    background: none;
    border: none;
    padding: 4px;
    cursor: pointer;
    color: var(--color-text-muted);
    display: inline-flex;
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}

.sidebar-settings-item-visibility:hover {
    background: var(--color-accent-tint);
    color: var(--color-accent);
}

.sidebar-settings-item.is-hidden .sidebar-settings-item-visibility {
    color: var(--color-danger-text);
}

.sidebar-settings-save-hint {
    margin-top: 12px;
    font-size: 12.5px;
    color: var(--status-active);
    font-weight: 600;
}

.license-assignee-list {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-bottom: 6px;
}

.license-assignee-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 4px 2px 8px;
    border-radius: 999px;
    background: var(--color-bg-subtle, #f8fafc);
    color: var(--color-text-secondary, #555);
    font-size: 11.5px;
    white-space: nowrap;
}

.license-assignee-chip-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 16px;
    height: 16px;
    border: none;
    background: transparent;
    color: inherit;
    cursor: pointer;
    border-radius: 50%;
    opacity: .6;
}

.license-assignee-chip-remove:hover {
    opacity: 1;
    background: rgba(0, 0, 0, .08);
}

/* Multi-Select Tag-Input je Lizenz-Zeile (Refactoring weg vom Dropdown+Bestaetigen-Klick) -
   .license-assign-picker haelt Add-Button, Such-Feld und Kandidaten-Dropdown; position:relative
   als Anker fuer die absolut positionierte Kandidatenliste. */
.license-assign-picker {
    position: relative;
    margin-top: 4px;
}

.license-assign-add-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    border: none;
    background: transparent;
    color: var(--color-accent);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    padding: 0;
}

.license-assign-search {
    width: 100%;
    font-size: 12.5px;
    padding: 5px 8px;
}

.license-assign-search[hidden] {
    display: none;
}

.license-assign-candidates {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    right: 0;
    z-index: 20;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-card);
    max-height: 200px;
    overflow-y: auto;
}

.license-assign-candidates[hidden] {
    display: none;
}

.license-assign-candidate-row {
    display: block;
    width: 100%;
    text-align: left;
    border: none;
    background: transparent;
    padding: 7px 10px;
    font-size: 12.5px;
    color: var(--color-text);
    cursor: pointer;
}

.license-assign-candidate-row:hover {
    background: var(--color-surface-2);
}

.license-assign-candidate-empty {
    padding: 8px 10px;
    font-size: 12px;
    color: var(--color-text-muted);
}

.license-assign-error {
    margin-top: 4px;
    font-size: 11.5px;
    color: #C0392B;
}

.license-assign-error[hidden] {
    display: none;
}

/* Bulk-Assignment-Matrix-Modal (Checkbox-Grid Personen/Funktionskonten x Lizenzen). */
.license-bulk-matrix {
    border-collapse: collapse;
    width: 100%;
    font-size: 12.5px;
}

.license-bulk-matrix th,
.license-bulk-matrix td {
    border: 1px solid var(--color-border);
    padding: 6px 10px;
    text-align: center;
    white-space: nowrap;
}

.license-bulk-matrix thead th:first-child,
.license-bulk-matrix tbody td:first-child {
    text-align: left;
    position: sticky;
    left: 0;
    background: var(--color-surface);
    z-index: 1;
}

.license-bulk-matrix thead th {
    background: var(--color-surface-2);
    font-weight: 600;
    vertical-align: top;
    padding-top: 8px;
}

.license-bulk-matrix thead th:first-child {
    z-index: 2;
}

.license-bulk-col-capacity {
    display: block;
    font-size: 10.5px;
    font-weight: 400;
    color: var(--color-text-muted);
    margin-top: 2px;
}

.license-bulk-matrix td.has-error {
    background: rgba(192, 57, 43, .12);
}

.license-bulk-status {
    margin-top: 10px;
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    font-size: 12.5px;
}

.license-bulk-status[hidden] {
    display: none;
}

.license-bulk-status.is-success {
    background: rgba(46, 158, 91, .12);
    color: #2e9e5b;
}

.license-bulk-status.is-error {
    background: rgba(192, 57, 43, .12);
    color: #C0392B;
}

.wan-connection-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.wan-connection-row {
    padding: 10px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

.wan-connection-row-head {
    display: flex;
    align-items: center;
    gap: 6px;
}

.wan-connection-row-actions {
    margin-left: auto;
    display: flex;
    gap: 4px;
}

/* ============ Ansprechpartner-Detailseite Redesign (Nutzer-Mockup, UniFi+Personio-Hybrid) ============ */

.contact-quick-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
}

.contact-quick-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 9px;
    border-radius: 999px;
    background: var(--color-surface-2);
    color: var(--color-text-secondary, #555);
    font-size: 12px;
    text-decoration: none;
    transition: background .15s ease;
}

.contact-quick-pill:hover {
    background: var(--color-border);
}

/* Microsoft Teams Deep-Links (Nutzer-Wunsch) - dezenter Teams-Indigo-Akzent statt des neutralen
   Pill-Grautons, damit die beiden Teams-Pills auf den ersten Blick als "externe App" erkennbar
   sind, ohne die restliche Pill-Reihe optisch zu sprengen. Farbwert ist Microsofts eigener
   Teams-Markenton (#5b5fc7), nicht der App-Akzent von CoreDesk selbst. */
.contact-quick-pill--teams {
    background: rgba(91, 95, 199, .12);
    color: #5b5fc7;
}

.contact-quick-pill--teams:hover {
    background: rgba(91, 95, 199, .22);
}

[data-theme="dark"] .contact-quick-pill--teams {
    background: rgba(131, 135, 255, .18);
    color: #a5a8ff;
}

[data-theme="dark"] .contact-quick-pill--teams:hover {
    background: rgba(131, 135, 255, .28);
}

.contact-detail-grid {
    display: grid;
    grid-template-columns: 7fr 5fr;
    gap: 20px;
    align-items: start;
    margin-top: 16px;
}

@media (max-width: 1000px) {
    .contact-detail-grid { grid-template-columns: 1fr; }
}

.contact-detail-col-main,
.contact-detail-col-side {
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-width: 0;
}

.contact-tab-card { max-width: none; }

.contact-tabs {
    display: flex;
    gap: 4px;
    border-bottom: 1px solid var(--color-border);
    margin: -20px -20px 16px;
    padding: 0 16px;
}

.contact-tab-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: transparent;
    padding: 12px 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text-muted);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}

.contact-tab-btn.is-active {
    color: var(--color-accent);
    border-bottom-color: var(--color-accent);
}

.contact-tab-panel { display: none; }
.contact-tab-panel.is-active { display: block; }

/* UniFi-Event-Style Timeline: durchgehende vertikale Linie + farbige Punkte je Ereignis-Typ,
   statt einzelner verschachtelter Karten wie vorher (.timeline-entry). */
.contact-unifi-timeline {
    position: relative;
    padding-left: 22px;
}

.contact-unifi-timeline::before {
    content: '';
    position: absolute;
    left: 5px;
    top: 6px;
    bottom: 6px;
    width: 2px;
    background: var(--color-border);
}

.contact-timeline-event {
    position: relative;
    margin-bottom: 20px;
}

.contact-timeline-event:last-child { margin-bottom: 0; }

.contact-timeline-dot {
    position: absolute;
    left: -22px;
    top: 4px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--color-accent);
    box-shadow: 0 0 0 3px var(--color-surface);
}

.contact-timeline-dot--note { background: #d4a72c; }
.contact-timeline-dot--sent { background: var(--status-active, #2e9e5b); }
.contact-timeline-dot--failed { background: var(--color-danger-text, #C0392B); }

.contact-timeline-event-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 10px;
}

.contact-timeline-event-title {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
}

.contact-timeline-event-head time {
    font-size: 11.5px;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.contact-timeline-event-sub {
    font-size: 12px;
    color: var(--color-text-muted);
    margin: 4px 0;
}

.contact-timeline-event-body {
    font-size: 13px;
    color: var(--color-text);
    margin-top: 2px;
    white-space: pre-line;
}

.contact-side-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid var(--color-border);
    padding-bottom: 8px;
    margin-bottom: 10px;
}

.contact-side-card-head h3 {
    margin: 0;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    gap: 6px;
}

.contact-side-card-divider {
    border: none;
    border-top: 1px solid var(--color-border);
    margin: 14px 0;
}

.contact-device-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.contact-device-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    border-radius: var(--radius-sm);
    background: var(--color-surface-2);
    text-decoration: none;
    color: inherit;
}

.contact-device-row:hover { background: var(--color-border); }

.contact-device-row-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.contact-device-row-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--color-text);
}

.contact-device-row-sub {
    font-size: 11.5px;
    color: var(--color-text-muted);
}

.contact-device-row-ip {
    font-family: var(--font-mono, monospace);
    font-size: 11px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

.contact-kv-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px 16px;
}

.contact-kv-label {
    display: block;
    font-size: 11px;
    color: var(--color-text-muted);
    margin-bottom: 2px;
}

.contact-kv-value {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--color-text);
}

.contact-kv-value.is-positive { color: var(--status-active, #2e9e5b); }

.contact-kv-value-sub {
    font-weight: 400;
    color: var(--color-text-muted);
}

.contact-kv-add-link {
    border: none;
    background: transparent;
    color: var(--color-accent);
    font-size: 12.5px;
    font-weight: 600;
    cursor: pointer;
    padding: 0;
}

.contact-kv-footer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-top: 10px;
    margin-top: 10px;
    border-top: 1px solid var(--color-border);
}
