/* General application-wide styles */

/* Zentrale Stellschraube für die Bildbreite im Chat-Verlauf. Generierte Bilder, Markdown-Bilder
   und die darunter liegenden Fußzeilen (Herkunft, Aktions-Buttons) richten sich alle danach —
   ohne gemeinsamen Wert laufen Bild und Fußzeile bei einer Änderung auseinander. */
:root {
    --aura-chat-image-max-width: 420px;

    /* Sichtbare Höhe der App und Höhe der Bildschirmtastatur. Beide Werte setzt
       js/auraKeyboard.js bei offener Tastatur als Pixelwert am <html>-Element; hier stehen
       die Ruhewerte. Alles, was die volle Höhe einnimmt, MUSS --aura-viewport-height statt
       100dvh/100vh benutzen — nur so schrumpft die Seite beim Aufklappen der Tastatur nach
       oben weg (Kopfbereich bleibt stehen, Eingabezeile sitzt auf der Tastatur), statt
       hinter die Tastatur zu rutschen oder verschoben zu werden. */
    --aura-viewport-height: 100dvh;
    --aura-keyboard-inset: 0px;
}

/* Force the entire document to be constrained to viewport */
html, body {
    height: 100% !important;
    overflow: hidden !important;
    margin: 0 !important;
    padding: 0 !important;
    /* Kein Überziehen/Gummiband am Rand: auf iOS konnte die Seite dadurch beim Tippen
       mitgezogen werden und der Kopfbereich verschwand nach oben. */
    overscroll-behavior: none;
}

#app {
    height: 100% !important;
    overflow: hidden !important;
}

/* Ensure MudBlazor components respect height constraints */
.mud-layout {
    height: var(--aura-viewport-height) !important;
    overflow: hidden !important;
}

/* iOS zoomt beim Fokussieren automatisch in ein Eingabefeld hinein, sobald dessen
   Schriftgröße unter 16px liegt — sichtbar als "die Seite zoomt/quetscht sich beim Tippen".
   max(16px, 1em) hebt nur die zu kleinen Felder an und lässt bewusst größere Schrift
   (z.B. Überschriften-Eingaben) unangetastet. Checkbox/Radio/Range tragen keinen Text und
   würden durch eine Schriftgröße nur in ihrer Größe verzerrt.
   Die contenteditable-Zeilen sind kein Beiwerk: die Chat-Eingabe ist KEIN <textarea>, sondern
   ein contenteditable-<div> (.custom-chat-input, 0.875rem) — genau das Feld, an dem der Zoom
   aufgefallen ist. */
:where([data-aura-view="mobile"]) input:not([type="checkbox"]):not([type="radio"]):not([type="range"]),
:where([data-aura-view="mobile"]) textarea,
:where([data-aura-view="mobile"]) select,
:where([data-aura-view="mobile"]) [contenteditable="true"],
:where([data-aura-view="mobile"]) [contenteditable=""] {
    font-size: max(16px, 1em) !important;
}

.mud-drawer-container {
    height: 100% !important;
    overflow: hidden !important;
}

/* MudMainContent should NOT have overflow hidden - it needs to contain scrollable content */
.mud-main-content {
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
}

/* Drag-and-Drop Zone Styles */
.drag-drop-wrapper {
    width: 100%;
}

.drag-drop-zone {
    position: relative;
    transition: all 0.3s ease-in-out;
}

.drag-drop-zone.drag-over {
    border: 3px dashed var(--mud-palette-primary);
    background-color: rgba(var(--mud-palette-primary-rgb), 0.05);
    transform: scale(1.01);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}

.drag-drop-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background-color: rgba(var(--mud-palette-primary-rgb), 0.15);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    pointer-events: none;
    border-radius: 4px;
}

.drag-drop-overlay * {
    pointer-events: none;
}

.drag-drop-overlay .mud-icon-root {
    color: var(--mud-palette-primary);
    font-size: 3rem;
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
}

.drag-drop-overlay .mud-typography {
    color: var(--mud-palette-primary);
    text-align: center;
    font-weight: 600;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}

.mud-menu-list {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
}

/* Force dark-mode text color in HTML editor, overriding inline styles from email templates */
.html-editor-content,
.html-editor-content * {
    color: var(--mud-palette-text-primary) !important;
}

/* PhotoCaptureDialog: Desktop-Standardwerte */
.photo-capture-video-container {
    max-height: 400px;
    overflow: hidden;
    background: #000;
}

.photo-capture-video {
    max-width: 100%;
    max-height: 400px;
    object-fit: contain;
}

.photo-capture-preview-container {
    max-height: 300px;
    overflow: hidden;
}

.photo-capture-preview-img {
    max-width: 100%;
    max-height: 300px;
    object-fit: contain;
}

/* PhotoCaptureDialog: Fullscreen auf Mobile (via MudBlazor FullScreen=true + Feintuning) */
.photo-capture-dialog.mud-dialog-fullscreen {
    display: flex !important;
    flex-direction: column !important;
}

.photo-capture-dialog.mud-dialog-fullscreen .mud-dialog-title {
    flex-shrink: 0;
    padding-top: calc(8px + env(safe-area-inset-top, 0px)) !important;
    padding-left: calc(16px + env(safe-area-inset-left, 0px)) !important;
    padding-right: calc(16px + env(safe-area-inset-right, 0px)) !important;
}

.photo-capture-dialog.mud-dialog-fullscreen .mud-dialog-content {
    flex: 1 !important;
    display: flex !important;
    flex-direction: column !important;
    overflow-y: auto !important;
    padding-left: calc(16px + env(safe-area-inset-left, 0px)) !important;
    padding-right: calc(16px + env(safe-area-inset-right, 0px)) !important;
}

.photo-capture-dialog.mud-dialog-fullscreen .mud-dialog-actions {
    flex-shrink: 0;
    padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px)) !important;
    padding-left: calc(16px + env(safe-area-inset-left, 0px)) !important;
    padding-right: calc(16px + env(safe-area-inset-right, 0px)) !important;
}

.photo-capture-dialog.mud-dialog-fullscreen .photo-capture-video-container {
    max-height: none !important;
    flex: 1 !important;
    min-height: 200px;
}

.photo-capture-dialog.mud-dialog-fullscreen .photo-capture-video {
    max-height: none !important;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.photo-capture-dialog.mud-dialog-fullscreen .photo-capture-preview-container {
    max-height: none !important;
    flex: 1 !important;
    min-height: 200px;
}

.photo-capture-dialog.mud-dialog-fullscreen .photo-capture-preview-img {
    max-height: none !important;
    width: 100%;
    height: 100%;
    object-fit: contain;
}

.photo-capture-dialog.mud-dialog-fullscreen .mud-button-root {
    min-height: 48px;
}


/* ChatAttachmentViewerModal: Betrachter fuer PDF, GAEB, E-Mail, Audio und Video eines
   Chat-Anhangs (Bilder gehen ins ImageAnnotatorOverlay, siehe AuraMobileDesign.md).
   Was war: `<div style="height: 80vh">` im Markup und ein Dialog mit MaxWidth.ExtraLarge
   ohne FullScreen. Auf dem Telefon war das ein gerahmter Kasten mit der kompletten
   PDF.js-Werkzeugleiste darin, und die 80vh rechnen mit der GROSSEN Viewporthoehe (ohne
   eingeklappte Browserleisten) — der Kasten lief unten unter die Adressleiste.
   Der Formfaktor kommt aus der IsMobile-Cascade, die beide Betrachter selbst auswerten (siehe
   ihre OnParametersSetAsync). Hier haengt alles an MudBlazors eigener Vollbild-Klasse. Damit
   gibt es KEINE zweite Mobile-Definition neben auraViewport.js — insbesondere KEINE
   Breiten-Media-Query, die ein Tablet im Querformat als Desktop lesen wuerde. */
/* --------------------------------------------------------------------------------------
   Kopfzeile der Dokument-Betrachter (Anhang-Betrachter und Projektdokument-Betrachter).

   Schlanke Leiste mit den Symbolen auf einer Linie, wie die Fensterknoepfe unter Windows.

   **Das eigene Symbol haengt absolut an derselben Kante wie MudBlazors Schliessen-X**
   (`.mud-dialog .mud-dialog-title .mud-button-close { position:absolute; top:8px; right:8px }`)
   und NICHT im Fluss der Titelzeile. Im Fluss wurde es mittig der Zeile ausgerichtet, das X
   sass aber 8px unter der Oberkante — bei einer 80px hohen Zeile ergab das einen sichtbaren
   Versatz nach unten. Mit `top: 8px` an beiden ist die Ausrichtung unabhaengig davon, wie hoch
   die Zeile durch den Titel wird.

   **Was war:** MudBlazors Standard `padding: 16px 64px 16px 24px` plus die Zeilenhoehe von
   `Typo.h6` samt dessen Abstand nach unten — zusammen 80px nur fuer Dateiname und zwei
   Symbole.
   -------------------------------------------------------------------------------------- */
.chat-attachment-viewer-dialog .mud-dialog-title,
.document-viewer-dialog .mud-dialog-title {
    /* Bezugsrahmen fuer die beiden absolut gesetzten Knoepfe. MudBlazor positioniert die
       Titelzeile selbst NICHT — ohne das rechnet `top: 50%` gegen die Hoehe des ganzen
       Dialogs statt gegen die Kopfzeile. */
    position: relative;
    /* Kein Abstand oben/unten: die Hoehe bestimmen allein die 40px der Knoepfe bzw. der
       Textzeile. Beides ist gleich hoch, es bleibt also nichts ueber, was zu polstern waere. */
    padding-top: 0 !important;
    padding-bottom: 0 !important;
}

/* Beide Knoepfe mittig der Kopfzeile statt an ihrer Oberkante.

   Sie sind 48x48 gross, die Textzeile daneben nur 40px hoch. An einer gemeinsamen Oberkante
   (MudBlazors `top: 8px`) sass ihre Mitte deshalb bei 32px, die des Titels bei 28px — sichtbar
   4px Versatz. `top: 50%` plus `translateY(-50%)` macht die Ausrichtung unabhaengig von beiden
   Hoehen und haelt auch, wenn ein langer Dateiname die Zeile umbrechen laesst.

   `!important`: MudBlazors eigene Regel `.mud-dialog .mud-dialog-title .mud-button-close`
   traegt dieselbe Spezifitaet (0,3,0) und wird spaeter geladen. */
.chat-attachment-viewer-dialog .mud-dialog-title .mud-button-close,
.document-viewer-dialog .mud-dialog-title .mud-button-close,
.viewer-kopf .viewer-kopf__aktion {
    top: 50% !important;
    transform: translateY(-50%) !important;
    /* 8px statt der 12px von MudBlazor: mit 24px Symbol ergibt das 40px statt 48px. Die
       Knopfgroesse ist die Untergrenze der Kopfzeile — ohne diesen Schritt ragten die Knoepfe
       aus der schlankeren Leiste heraus, statt dass sie schlanker wird. */
    padding: 8px !important;
}

/* Freiraum fuer BEIDE Symbole, sonst laeuft ein langer Dateiname darunter durch. `:has()`
   spart ihn dort, wo es das eigene Symbol nicht gibt (Audio, Text, GAEB, E-Mail) — dann
   reicht MudBlazors eigene Aussparung fuer das X. */
.chat-attachment-viewer-dialog .mud-dialog-title:has(.viewer-kopf__aktion),
.document-viewer-dialog .mud-dialog-title:has(.viewer-kopf__aktion) {
    /* 8px Rand + 40px X + 8px Luft + 40px Symbol + 8px Luft zum Titel. */
    padding-right: 104px !important;
}

.viewer-kopf {
    display: flex;
    align-items: center;
    /* So hoch wie die Symbole daneben — schlanker wird die Kopfzeile nicht, ohne dass die
       Symbole aus ihr herausragen. */
    min-height: 40px;
}

.viewer-kopf__titel {
    flex: 1 1 auto;
    /* Pflicht bei Flex: sonst bleibt die Inhaltsbreite die Mindestbreite und ein langer
       Dateiname schiebt das Symbol aus der Zeile. */
    min-width: 0;
    overflow-wrap: anywhere;
    /* MudBlazor gibt h6 einen Abstand nach unten. In einer einzeiligen Kopfzeile ist das
       nur Luft — und sie verschiebt den Titel gegenueber den Symbolen nach oben. */
    margin-bottom: 0 !important;
}

/* 56px = 8px Randabstand des X, dessen 40px Breite und 8px Luft dazwischen.

   Zwei Klassen im Selektor UND `!important` auf `position`: MudBlazor setzt
   `.mud-button-root { position: relative }` — gleiche Spezifitaet (0,1,0) wie eine einzelne
   eigene Klasse, und MudBlazor.min.css wird spaeter geladen. Mit nur einer Klasse blieb der
   Knopf im Fluss; die Verankerung griff nie, und `top`/`right` waren wirkungslos. */
.viewer-kopf .viewer-kopf__aktion {
    position: absolute !important;
    right: 56px;
}

.chat-attachment-viewer-body,
.document-viewer-body {
    width: 100%;
    /* dvh statt vh: vh ist auf dem Telefon die GROSSE Viewporthoehe (mit eingeklappten
       Browserleisten), ein Kasten mit 80vh liefe dort unter die Adressleiste. */
    height: 80dvh;
}

.chat-attachment-viewer-dialog.mud-dialog-fullscreen,
.document-viewer-dialog.mud-dialog-fullscreen {
    display: flex !important;
    flex-direction: column !important;
    /* Der Abstand zur Statusleiste haengt am DIALOG, nicht an der Kopfzeile. Als deren
       padding-top verschob er den Titel gegenueber den absolut mittig gesetzten Symbolen:
       `top: 50%` rechnet gegen den Polsterkasten, der Text sitzt im Inhaltskasten darunter —
       bei 8px Polsterung sassen sie 4px auseinander. Hier gilt er fuer alles darunter
       gleichermassen und laesst die Kopfzeile symmetrisch. */
    padding-top: env(safe-area-inset-top, 0px);
}

.chat-attachment-viewer-dialog.mud-dialog-fullscreen .mud-dialog-title,
.document-viewer-dialog.mud-dialog-fullscreen .mud-dialog-title {
    flex-shrink: 0;
    padding-left: calc(16px + env(safe-area-inset-left, 0px)) !important;
    padding-right: calc(16px + env(safe-area-inset-right, 0px)) !important;
}

/* Die Aussparung fuer die beiden Symbole muss NACH der Vollbild-Regel stehen: beide tragen die
   gleiche Spezifitaet (0,3,0 — `:has()` zaehlt sein Argument mit), und bei Gleichstand entscheidet
   die Reihenfolge. Weiter oben verlor sie, und auf dem Telefon blieben nur 16px stehen: ein langer
   Dateiname waere unter die Symbole gelaufen. */
.chat-attachment-viewer-dialog.mud-dialog-fullscreen .mud-dialog-title:has(.viewer-kopf__aktion),
.document-viewer-dialog.mud-dialog-fullscreen .mud-dialog-title:has(.viewer-kopf__aktion) {
    padding-right: calc(104px + env(safe-area-inset-right, 0px)) !important;
}

.chat-attachment-viewer-dialog.mud-dialog-fullscreen .mud-dialog-content,
.document-viewer-dialog.mud-dialog-fullscreen .mud-dialog-content {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    overflow: hidden !important;
    /* Volle Breite: nur der Notch-Abstand bleibt, keine zusaetzliche Polsterung. Die 8px
       erzeugten links und rechts einen weissen Streifen neben der PDF-Werkzeugleiste. Im
       Hochformat sind die Insets 0, im Querformat halten sie den Inhalt aus der Kerbe. */
    padding-left: env(safe-area-inset-left, 0px) !important;
    padding-right: env(safe-area-inset-right, 0px) !important;
}

/* Im Vollbild bestimmt der Dialog die Hoehe, nicht der Kasten — mit seinen 80dvh ragte er
   sonst unter der Aktionsleiste heraus. `min-height: 0` ist Pflicht: ohne das weigert sich
   das Flex-Kind zu schrumpfen, das innere `overflow` greift nie und der Betrachter schiebt
   den ganzen Dialog auf (Anti-Pattern in AuraMobileDesign.md). */
.chat-attachment-viewer-dialog.mud-dialog-fullscreen .chat-attachment-viewer-body,
.document-viewer-dialog.mud-dialog-fullscreen .document-viewer-body {
    height: auto;
    flex: 1 1 auto;
    min-height: 0;
}

/* Der untere Sicherheitsabstand haengt jetzt am Inhalt: eine Aktionsleiste, die ihn vorher
   getragen hat, gibt es nicht mehr (Schliessen ueber das X, „Seitlich im Chat oeffnen" in der
   Kopfzeile). Ohne das endete das Dokument auf dem Home-Indikator. */
.chat-attachment-viewer-dialog.mud-dialog-fullscreen .mud-dialog-content,
.document-viewer-dialog.mud-dialog-fullscreen .mud-dialog-content {
    padding-bottom: env(safe-area-inset-bottom, 0px) !important;
}

/* PhotoCaptureDialog: Handy-spezifisch */
@media (max-width: 480px) {
    .photo-capture-dialog .mud-dialog-title {
        padding-top: calc(4px + env(safe-area-inset-top, 0px)) !important;
        padding-bottom: 4px !important;
    }

    .photo-capture-dialog .mud-dialog-content {
        padding-left: calc(12px + env(safe-area-inset-left, 0px)) !important;
        padding-right: calc(12px + env(safe-area-inset-right, 0px)) !important;
    }

    .photo-capture-dialog .mud-dialog-actions {
        padding-left: calc(12px + env(safe-area-inset-left, 0px)) !important;
        padding-right: calc(12px + env(safe-area-inset-right, 0px)) !important;
    }

    .photo-capture-dialog .photo-capture-action-buttons {
        flex-direction: column !important;
    }

    .photo-capture-dialog .photo-capture-action-buttons > .mud-button-root {
        flex: unset !important;
        width: 100%;
    }

    .photo-capture-dialog .photo-capture-thumbnails img {
        height: 48px !important;
        width: 48px !important;
    }
}

/* --------------------------------------------------------------------------------------
   PhotoCaptureDialog: Vollbild-Kamera auf dem Handy.

   Die Klasse `--kompakt` setzt der Dialog, wenn die Aufrufstelle einen Vollbild-Aufbau
   verlangt UND der Formfaktor mobil ist. Bautagebuch und jede Desktop-Breite bleiben davon
   unberuehrt.

   **`:has(.pc-kompakt)` statt einer zustandsabhaengigen Klasse.** Im Seriemodus weicht die
   Kamera nach „Verwenden" der klassischen Uebersicht, und die braucht ihre Kopfzeile und ihre
   Aktionsleiste zurueck. Die Klasse am Dialog-Papier kann das nicht tragen: MudBlazor (8.15)
   liest `Class` beim Render des Dialog-Containers, und der laeuft nicht mit, wenn nur der
   Inhalt neu zeichnet — die Uebersicht erschien dadurch schwarz und OHNE Aktionsleiste, der
   Nutzer kam nicht mehr zum Speichern. `.pc-kompakt` steht dagegen im Inhalt und ist bei
   jedem Render aktuell. Nur die Hoehe haengt an der Klasse allein: sie gilt in beiden
   Zustaenden.

   Zwei Klassen im Selektor: die Vollbild-Regeln weiter oben stehen auf
   `.photo-capture-dialog.mud-dialog-fullscreen` und tragen damit zwei Klassen. Mit nur einer
   verloeren diese Regeln hier trotz `!important` gegen die spezifischeren Vollbild-Regeln.
   -------------------------------------------------------------------------------------- */
.photo-capture-dialog.photo-capture-dialog--kompakt {
    /* Nicht 100vh: das ist auf dem Telefon die GROSSE Viewporthoehe, der Dialog liefe unter
       die Browserleiste. --aura-viewport-height traegt zusaetzlich die offene Tastatur. */
    height: var(--aura-viewport-height) !important;
    max-height: var(--aura-viewport-height) !important;
}

.photo-capture-dialog.photo-capture-dialog--kompakt:has(.pc-kompakt) {
    background: #000 !important;
}

/* Kopfzeile und Aktionsleiste entfallen, solange die Kamera zu sehen ist: Abbrechen sitzt als
   X ueber der Buehne, das Uebernehmen in der Bedienzeile. Zwei Aktionsleisten uebereinander
   waren der Grund. In der Uebersicht des Seriemodus kommen beide zurueck. */
.photo-capture-dialog.photo-capture-dialog--kompakt:has(.pc-kompakt) .mud-dialog-title,
.photo-capture-dialog.photo-capture-dialog--kompakt:has(.pc-kompakt) .mud-dialog-actions {
    display: none !important;
}

.photo-capture-dialog.photo-capture-dialog--kompakt:has(.pc-kompakt) .mud-dialog-content {
    flex: 1 1 auto !important;
    /* Pflicht bei Flex: ohne das weigert sich das Kind zu schrumpfen, das innere overflow
       greift nie und die Buehne schiebt die Bedienzeile aus dem Bild. */
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    padding: 0 !important;
    overflow: hidden !important;
}

.pc-kompakt {
    position: relative;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    background: #000;
}

/* Zwei Klassen im Selektor und !important auf der Positionierung, obwohl die eigene Klasse
   allein eindeutig waere: MudBlazor.min.css wird in App.razor NACH dieser Datei geladen
   (Zeile 70 gegen Zeile 53). Bei gleicher Spezifitaet gewinnt damit immer MudBlazor — hier
   konkret `.mud-button-root { position: relative }`, was das X im Fluss liess, auf volle
   Breite streckte und sein Icon mittig statt oben rechts zeigte. */
.pc-kompakt > .pc-kompakt__schliessen {
    position: absolute !important;
    top: calc(8px + env(safe-area-inset-top, 0px)) !important;
    right: calc(8px + env(safe-area-inset-right, 0px)) !important;
    left: auto !important;
    z-index: 3;
    color: #fff !important;
    background: rgba(0, 0, 0, 0.45);
}

.pc-kompakt > .pc-kompakt__meldung {
    position: absolute !important;
    top: calc(8px + env(safe-area-inset-top, 0px)) !important;
    left: calc(8px + env(safe-area-inset-left, 0px)) !important;
    /* Platz fuer das X daneben freihalten. */
    right: calc(56px + env(safe-area-inset-right, 0px)) !important;
    z-index: 2;
}

.pc-kompakt__buehne {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

/* contain und NICHT cover: aufgenommen wird das volle Kamerabild
   (auraCapturePhoto.captureFrame zeichnet den ganzen Frame auf ein Canvas). Bei cover zeigte
   die Vorschau einen Ausschnitt, das Foto enthielte aber mehr — der Nutzer bekaeme etwas
   anderes, als er sieht. Die Raender bleiben schwarz statt weiss, damit die Flaeche als
   Kamerabild und nicht als Luecke liest. */
.pc-kompakt__medium {
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #000;
}

.pc-kompakt__rueckfall {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 16px;
}

.pc-kompakt__laufzeit {
    position: absolute;
    top: calc(12px + env(safe-area-inset-top, 0px));
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-variant-numeric: tabular-nums;
}

/* Drei Plaetze, feste Mitte: die 1fr-Spalten aussen halten den Ausloeser mittig, auch wenn
   links der Umschalter fehlt (kein Video erlaubt) oder rechts der Kamerawechsel (nur eine
   Kamera am Geraet). */
.pc-kompakt__leiste {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 8px;
    padding: 12px calc(16px + env(safe-area-inset-right, 0px))
             calc(12px + env(safe-area-inset-bottom, 0px)) calc(16px + env(safe-area-inset-left, 0px));
    background: #000;
}

/* Fuss der Vorschau: Knopfzeile plus optionale Beschreibung. Der untere Sicherheitsabstand haengt
   hier und nicht an der Knopfzeile — sonst zaehlte er doppelt, sobald die Beschreibung darunter
   steht. */
.pc-kompakt__fuss {
    background: #000;
    padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}

.pc-kompakt__fuss .pc-kompakt__leiste {
    padding-bottom: 0;
}

.pc-kompakt__beschreibung {
    padding: 0 calc(16px + env(safe-area-inset-right, 0px)) 0 calc(16px + env(safe-area-inset-left, 0px));
}

/* Das Eingabefeld steht auf Schwarz; die MudBlazor-Palette rechnet mit dem Dialog-Hintergrund und
   faerbt Text, Beschriftung und Rahmen sonst dunkel auf dunkel. Zwei Klassen im Selektor und
   !important aus demselben Grund wie beim Abbrechen-X: MudBlazor.min.css wird spaeter geladen. */
.pc-kompakt .pc-kompakt__beschreibung .mud-input-root,
.pc-kompakt .pc-kompakt__beschreibung .mud-input-label {
    color: #fff !important;
}

.pc-kompakt .pc-kompakt__beschreibung .mud-input-outlined-border {
    border-color: rgba(255, 255, 255, 0.45) !important;
}

.pc-kompakt__leiste--entscheidung {
    grid-template-columns: 1fr 1fr;
}

.pc-kompakt__leiste--entscheidung .mud-button-root {
    min-height: 48px;
}

/* Auf Schwarz braucht der Umriss-Knopf eigene Farben, die MudBlazor-Palette rechnet mit dem
   Dialog-Hintergrund. !important aus demselben Grund wie beim X weiter oben: MudBlazors
   `.mud-button-outlined.mud-button-outlined-default` traegt dieselbe Spezifitaet und wird
   spaeter geladen. */
.pc-kompakt__leiste.pc-kompakt__leiste--entscheidung .mud-button-outlined {
    color: #fff !important;
    border-color: rgba(255, 255, 255, 0.6) !important;
}

.pc-kompakt__modus {
    justify-self: start;
    display: flex;
    border-radius: 999px;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.15);
}

.pc-kompakt__modus-knopf {
    border: 0;
    background: transparent;
    color: #fff;
    font: inherit;
    min-height: 40px;
    padding: 8px 14px;
    cursor: pointer;
}

.pc-kompakt__modus-knopf--aktiv {
    background: #fff;
    color: #000;
}

.pc-kompakt__wechsel {
    justify-self: end;
}

.pc-kompakt__wechsel .mud-icon-button {
    color: #fff !important;
}

/* Ausloeser wie in der Kamera-App des Telefons: Ring aussen, Kern innen. Der Kern traegt den
   Modus (weiss = Foto, rot = Video, roter Block = laeuft), damit ein Blick genuegt. */
.pc-ausloeser {
    justify-self: center;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    padding: 0;
    border: 4px solid #fff;
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
}

.pc-ausloeser:disabled {
    opacity: 0.5;
    cursor: default;
}

.pc-ausloeser__kern {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: #fff;
    transition: width 0.15s, height 0.15s, border-radius 0.15s, background-color 0.15s;
}

.pc-ausloeser__kern--video {
    background: var(--mud-palette-error);
}

.pc-ausloeser__kern--stopp {
    width: 28px;
    height: 28px;
    border-radius: 6px;
    background: var(--mud-palette-error);
}

.recording-pulse {
    animation: pulse-recording 1.2s ease-in-out infinite;
}

@keyframes pulse-recording {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
}

/* --------------------------------------------------------------------------------------
   Reduzierte Bewegung respektieren.

   Die Systemeinstellung (Windows: Barrierefreiheit -> Animationseffekte) wurde bisher
   nirgends im Chat ausgewertet. Ein laengerer AgentMode-Lauf laesst jede neu erscheinende
   Tool-Zeile einfliegen; zusammen mit den Endlos-Spinnern ergibt das ein unruhiges Bild
   genau fuer die Nutzer, die das ausdruecklich abgewaehlt haben.

   Die Regel steht bewusst global und mit !important: die Animationen sind überwiegend in
   Blazor-Scoped-Stylesheets definiert, deren Selektoren durch das b-Attribut höhere
   Spezifität haben. Sie trifft damit auch MudBlazor-eigene Übergänge (Dialoge, Ripple).

   Bewusst NICHT mit abgeschaltet wird der Aufnahme-Indikator: er ist ein Statuszeichen,
   kein Schmuck. Statt zu blinken steht er still auf voller Deckkraft, bleibt also sichtbar.

   Ebenfalls ausgenommen: funktionale Fortschrittsanzeigen, siehe den Block weiter unten.
   Sie sind der Grund, warum diese Regel nicht einfach „alles anhalten" heißen darf.
   -------------------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }

    .recording-pulse {
        animation: none !important;
        opacity: 1;
    }

    /* ----------------------------------------------------------------------------------
       Funktionale Fortschrittsanzeigen laufen weiter — app-weit, nicht nur im Chat.

       Eine stehende Fortschrittsanzeige ist keine ausgelassene Dekoration, sie ist eine
       Falschaussage: Sie behauptet „hier hängt etwas", während der Vorgang in Ruhe
       weiterläuft. Genau so wurde es gemeldet — „Passung aktualisieren" in der
       Vergabe-Übersicht zeigte einen stehenden Spinner, und zwar alle Spinner der Seite
       gleichzeitig, weil die Regel oben mit `*` auf jedes Element wirkt.

       Vorher war diese Ausnahme auf `.chat-messages-container` eingeschränkt. Damit stand
       JEDER Spinner außerhalb des Chats still, sobald im Betriebssystem
       (Windows: Barrierefreiheit -> Visuelle Effekte -> Animationseffekte) die
       Bewegungsreduzierung eingeschaltet ist.

       Dauer und Wiederholung müssen hier konkret stehen: Die Regel oben setzt beide mit
       !important, und es gibt kein CSS-Schlüsselwort, das auf den Wert einer anderen
       Autoren-Regel zurückfällt — `revert` und `unset` landen beim Browser-Vorgabewert
       (0s bzw. 1) und würden den Spinner erst recht anhalten. Die Werte stammen aus
       MudBlazor 8.15.0. Weicht eine künftige Fassung ab, dreht der Spinner nur
       geringfügig anders schnell; kaputt geht dabei nichts.

       Eigene Spinner (kein MudBlazor) nehmen sich in ihrem eigenen Stylesheet aus, direkt
       neben ihrer `animation`-Zeile — dort steht die Dauer schon, und zwei Zeilen
       auseinander kann sie nicht auseinanderlaufen. Muster: siehe
       `Components/Documents/DocumentListComponent.razor.css`.
       ---------------------------------------------------------------------------------- */
    .mud-progress-circular.mud-progress-indeterminate,
    .mud-progress-circular-indeterminate-child,
    .mud-progress-circular-circle.mud-progress-indeterminate {
        animation-duration: 1.4s !important;
        animation-iteration-count: infinite !important;
    }

    /* Der unbestimmte Balken (MudProgressLinear) besteht aus zwei Laufbalken mit eigener
       Verzögerung. Die Verzögerung rührt die Regel oben nicht an, deshalb genügen Dauer
       und Wiederholung. */
    .mud-progress-linear.mud-progress-indeterminate .mud-progress-linear-bar,
    .mud-progress-linear .mud-progress-linear-bar.mud-progress-linear-1-indeterminate,
    .mud-progress-linear .mud-progress-linear-bar.mud-progress-linear-2-indeterminate {
        animation-duration: 2.1s !important;
        animation-iteration-count: infinite !important;
    }

    /* Bootstrap-Spinner: In AURA werden sie an rund vierzig Stellen benutzt (Dokumentgenerator,
       Inventar, Grundriss-Ansicht …) und standen bis 1.1.1186 genauso still wie die von
       MudBlazor. Hier ist keine Zahl nötig: Bootstrap legt die Dauer als eigene Variable ab,
       also wird genau der Wert weiterverwendet, den die Klasse selbst mitbringt — `.spinner-grow`
       setzt ihn auf 1,5 s und bekommt damit automatisch die richtige Dauer. */
    .spinner-border,
    .spinner-grow {
        animation-duration: var(--bs-spinner-animation-speed) !important;
        animation-iteration-count: infinite !important;
    }
}

/* Spinner in Buttons ("Analyse läuft…"): Das Button-Label ist ein Flex-Container. Wird der Button
   schmal (kleines Fenster, lange Beschriftung), staucht Flexbox den Spinner in der Breite — er
   dreht sich dann als Ellipse und eiert. flex-shrink: 0 hält ihn quadratisch; die Beschriftung
   daneben bricht stattdessen um. Gilt für alle MudProgressCircular in MudButtons, das Muster
   steht in rund 50 Komponenten. */
.mud-button-root .mud-progress-circular {
    flex-shrink: 0;
}

/* Kompakt-Ansicht: enger gefasste vertikale Abstaende.
   Wird ueber UserSettingsState.ChatViewMode == Compact aktiviert. */

.chat-messages-container.compact-view .chat-messages-inner {
    padding: 2px 8px !important;
}

.chat-messages-container.compact-view .chat-message-wrapper {
    margin-bottom: 1px !important;
}

.chat-messages-container.compact-view .system-message {
    margin-bottom: 1px !important;
}

.chat-messages-container.compact-view .message-group {
    margin-bottom: 0 !important;
}

.chat-messages-container.compact-view .chat-bubble-content {
    padding: 0.5rem 0.75rem 0.1rem !important;
    line-height: 1.35 !important;
}

.chat-messages-container.compact-view .chat-timestamp {
    margin-bottom: 1px !important;
}

.chat-messages-container.compact-view .chat-bubble-content p {
    margin-bottom: 0.15rem !important;
    line-height: 1.35 !important;
}

.chat-messages-container.compact-view .chat-bubble-content h1,
.chat-messages-container.compact-view .chat-bubble-content h2,
.chat-messages-container.compact-view .chat-bubble-content h3,
.chat-messages-container.compact-view .chat-bubble-content h4,
.chat-messages-container.compact-view .chat-bubble-content h5,
.chat-messages-container.compact-view .chat-bubble-content h6 {
    margin: 0.25rem 0 0.15rem 0 !important;
}

.chat-messages-container.compact-view .chat-bubble-content ul,
.chat-messages-container.compact-view .chat-bubble-content ol {
    margin: 0.15rem 0 !important;
}

.chat-messages-container.compact-view .chat-bubble-content li {
    margin: 0.05rem 0 !important;
    line-height: 1.35 !important;
}

.chat-messages-container.compact-view .chat-bubble-content pre,
.chat-messages-container.compact-view .chat-bubble-content blockquote {
    margin: 0.25rem 0 !important;
}

.chat-main-area:has(.chat-messages-container.compact-view) .chat-input-wrapper {
    padding: 4px !important;
}

.chat-main-area:has(.chat-messages-container.compact-view) .custom-chat-input {
    min-height: 38px !important;
    padding: 6px 14px !important;
}

/* Workaround: Diese Regeln sitzen bewusst in app.css statt im scoped MainLayout.razor.css.
   Grund: Aturis.Aura.Ui.styles.css (Blazor Scoped CSS Bundle für die Aura.Ui RCL) wird im
   Tenant-Hosting nicht ausgeliefert (404), darum landen scoped CSS-Regeln aus MainLayout.razor.css
   nicht beim Client. Bis das Static-Asset-Setup gefixt ist, müssen UI-kritische Panel-Regeln hier. */

/* Items im Two-Column-Nav-Panel: Default-Browser-Link-Styling (blau + Underline) hat
   sich durchgesetzt, weil MudBlazor's .mud-nav-link-Reset im Panel-Kontext nicht greift.
   Hart per !important auf den Mud-Theme-Text zurücksetzen — gilt für Web und Mobile. */
.two-col-panel-overlay .mud-nav-link,
.two-col-panel-overlay a {
    color: var(--mud-palette-text-primary) !important;
    text-decoration: none !important;
}

/* Web (Desktop, >= 960 px): Panel sitzt direkt unter der AppBar (flush), aber HINTER dem AppBar-
   z-index. Dadurch erscheint der AppBar-Box-Shadow ON TOP des Panels — Schatten bleibt sichtbar.
   z-index 1099 < AppBar 1100 = MudAppBar überlagert Panel im überlappenden x/y-Bereich. */
:where([data-aura-view="desktop"]) .two-col-panel-overlay {
    top: var(--mud-appbar-height, 64px) !important;
    z-index: 1099 !important;
}

/* MailView- und MailsOverview-Toolbar auf Mobile (< 960 px): Buttons zu Icon-only kompaktieren.
   MudBlazor v8 wickelt die Button-Struktur als <button>→<.mud-button-label>→[icon, text]; ein
   display:none auf .mud-button-label würde auch das Icon mit verstecken (das sitzt drin).
   Lösung: Text-Knoten per font-size:0 kollabieren, Icon-Größe ist über fixe SVG-width/height
   gesetzt und bleibt sichtbar. Padding reduzieren damit die Buttons nicht ungleich groß sind.
   Such-Feld in MailsOverview-Toolbar bleibt sichtbar — nimmt restliche Breite. */
:where([data-aura-view="mobile"]) .mailview-toolbar .mud-button-root,
:where([data-aura-view="mobile"]) .mailsoverview-toolbar .mud-button-root {
    min-width: 0 !important;
    padding: 6px !important;
    margin-right: 0 !important;
    font-size: 0 !important;
}
:where([data-aura-view="mobile"]) .mailview-toolbar .mud-button-root .mud-button-icon-start,
:where([data-aura-view="mobile"]) .mailsoverview-toolbar .mud-button-root .mud-button-icon-start {
    margin: 0 !important;
    font-size: initial !important;
}
/* Such-Feld nimmt den verbleibenden Platz in der MailsOverview-Toolbar */
:where([data-aura-view="mobile"]) .mailsoverview-toolbar .mud-input-text-field,
:where([data-aura-view="mobile"]) .mailsoverview-toolbar .mud-input-control {
    max-width: none !important;
    flex: 1 1 auto;
}

/* Mobile (< 960 px): Panel über volle Bildschirmhöhe.
   Bewusst KEINE explizite height — top:0 + bottom:0 lassen den Browser die Höhe aus dem Viewport
   ausrechnen. 100vh / 100dvh haben auf Mobile (besonders im DevTools-Emulator) öfter Macken und
   ergeben Werte, die ein paar % unter dem tatsächlichen Viewport liegen — das Panel hört dann
   früher auf als die Rail-Drawer daneben.
   Padding-top:8px = Rail-Padding-top auf Mobile (Drawer-Temporary sitzt bei top:0, Rail hat 8px
   internes padding-top), damit der Panel-Header auf der gleichen Höhe wie das erste Rail-Icon
   startet. z-index 1400 > AppBar/Backdrop, damit Panel über allem liegt und Klicks ankommen.
   Bottom hängt an --aura-keyboard-inset: öffnet die Tastatur (z.B. Projektsuche im Panel),
   endet das Panel an der Tastaturkante statt dahinter weiterzulaufen. */
:where([data-aura-view="mobile"]) .two-col-panel-overlay {
    position: fixed !important;
    top: 0 !important;
    bottom: var(--aura-keyboard-inset, 0px) !important;
    height: auto !important;
    padding-top: 8px !important;
    padding-bottom: 8px !important;
    z-index: 1400 !important;
}
/* Default-Margins der Kinder zurücksetzen, sonst rutscht der Header durch das User-Agent-
   Stylesheet (z.B. <ul>-Default-Margin in MudNavMenu) zusätzlich nach unten. */
:where([data-aura-view="mobile"]) .two-col-panel-overlay .two-col-panel-header {
    margin-top: 0 !important;
    padding-top: 0 !important;
}
:where([data-aura-view="mobile"]) .two-col-panel-overlay .mud-navmenu,
:where([data-aura-view="mobile"]) .two-col-panel-overlay ul {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

/* Kalender (Heron.MudCalendar) auf Mobile (< 960 px):
   - Heron-interne View-Auswahl-Buttons werden via CSS ausgeblendet (Show*=false hat das Rendering
     der View selbst blockiert — leerer Day-Content). Ihre Funktion uebernimmt das eigene
     Overlay-MudMenu.
   - Overlay-Icon (.cal-view-switch-overlay) absolut rechts oben auf der Heron-Toolbar. z-index 2,
     damit es ueber den Toolbar-Inhalten liegt, aber unter Popups.
   - Heron-Toolbar erlaubt Flex-Wrap, falls die Nav-Buttons (Chevrons/Heute/Datum) bei sehr engen
     Geraeten nicht in eine Zeile passen. */
.cal-view-switch-overlay {
    position: absolute;
    top: 8px; /* visuell zentriert mit Heron's Nav-Buttons (Toolbar padding-top ~8 px + Button-Center ~20 px) */
    right: 8px;
    z-index: 2;
}

:where([data-aura-view="mobile"]) .cal-wrapper-mobile .mud-cal-toolbar {
    flex-wrap: wrap;
    gap: 4px;
    padding-right: 56px; /* Platz fuer das absolut positionierte View-Switch-Overlay (Size.Large) */
}
/* Heron's interne View-Auswahl-Buttons (Tag/Arbeitswoche/Woche/Monat) verstecken.
   `.mud-cal-toolbar-nav` ist Heron's NAVIGATION-Block (Chevrons/Datum/Heute) — den BEHALTEN.
   Die View-Buttons sitzen als zweiter direkter Kind-Container daneben (ohne eigene Klasse),
   darum :not() zum Invertieren: alle direkten Kinder verstecken, AUSSER toolbar-nav. */
:where([data-aura-view="mobile"]) .cal-wrapper-mobile .mud-cal-toolbar > *:not(.mud-cal-toolbar-nav) {
    display: none !important;
}

/* Mobile-AppBar-Suchzeile: fixed direkt unter der AppBar, sichtbar wenn _appbarSearchOpen.
   z-index 1099 < AppBar (1100), damit AppBar-Schatten und Dropdowns drueber bleiben.
   Box-Shadow ahmt eine eigene flache Trennung zur Page nach. Full-Width Padding,
   Such-DynamicComponent darin nimmt flex-grow:1, Close-Icon rechts. */
.mobile-search-row {
    position: fixed;
    top: var(--mud-appbar-height, 64px);
    left: 0;
    right: 0;
    z-index: 1099;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 4px 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
}

/* Nested MudMenu-Activators (z.B. TaskMonitor "Aufgaben" im UserAvatarDropdown) bekommen von
   MudBlazor v8 die Klasse `mud-menu-sub-menu-activator`. Per Default rendert das Wrapper-Element
   mit groesseren Horizontal-Gutters als die Geschwister-MudMenuItems im selben Menue (die hier
   8 px / 12 px verwenden), was den Text gegenueber den anderen Eintraegen einrueckt. Wir
   ueberschreiben mit den exakten Sibling-Werten (8 px vertical / 12 px horizontal) und resetten
   die browser-default `<p>`-Margins der `.mud-menu-item-text`, damit "Aufgaben" pixelgenau mit
   "Konto/Hilfe/..." flushed. */
.mud-menu-sub-menu-activator {
    padding: 8px 12px !important;
}

.mud-menu-sub-menu-activator > .mud-menu-item-text,
.mud-menu-sub-menu-activator > p {
    margin: 0 !important;
    margin-block: 0 !important;
    margin-inline: 0 !important;
    padding: 0 !important;
    padding-block: 0 !important;
    padding-inline: 0 !important;
    text-indent: 0 !important;
}

/* ReportTemplateDetailPage: auf Mobile blenden wir den nativen MudTabs-Header (mit den drei
   Tabs nebeneinander) aus und ersetzen ihn durch einen eigenen Single-Tab-Navigator mit
   Chevron-Pfeilen + zentriertem Label. Sonst werden Labels wie "Automatisierung" truncated zu
   "AUT" weil zu wenig Platz da ist. */
/* MudBlazor v8 rendert die Tab-Leiste je nach Version unter wechselnden Klassen — wir matchen
   alle bekannten Varianten, damit auf Mobile der native Header zuverlaessig ausgeblendet ist. */
.report-tabs-mobile-no-header > .mud-tabs-toolbar,
.report-tabs-mobile-no-header .mud-tabs-tabbar,
.report-tabs-mobile-no-header .mud-tabs-tabbar-wrapper,
.report-tabs-mobile-no-header .mud-tabs-toolbar-content,
.report-tabs-mobile-no-header .mud-tabs-header {
    display: none !important;
}

/* ReportView Mobile: Aktions-Buttons in einer fixierten Bottom-Bar. Toggle-Header schaltet
   die Buttons sichtbar/unsichtbar — kollabiert ist nur die Header-Zeile (~40 px) am unteren
   Bildschirmrand, expanded liegen die Buttons darüber. */
.report-actions-bottom-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 100;
    background: var(--mud-palette-surface);
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);
    padding: 4px 8px 8px;
}

.report-actions-bottom-bar .actions-toggle-header {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    cursor: pointer;
    padding: 6px;
    user-select: none;
    border-bottom: 1px solid var(--mud-palette-divider);
    color: var(--mud-palette-primary);
    font-weight: 600;
}

.report-actions-bottom-bar .actions-toggle-header .mud-icon-root {
    color: var(--mud-palette-primary);
}

.report-actions-bottom-bar .actions-content {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding-top: 8px;
}

/* MudMenu mit ActivatorContent wickelt seinen Inhalt in einen inline-block-Container ein —
   eine FullWidth-MudButton-Activator wuerde sonst trotzdem nur Content-Breit. Die Klasse
   `mobile-fullwidth-menu` (auf MudMenu im Mobile-Zustand gesetzt) zwingt nur den Menu-Wrapper
   und dessen direkte Kinder auf block + 100%; den inneren MudButton lassen wir bei seinem
   natuerlichen inline-flex (sonst stacken Icon + Text vertikal statt nebeneinander). */
.mobile-fullwidth-menu,
.mobile-fullwidth-menu > * {
    display: block !important;
    width: 100% !important;
}

/* SmartIndexPromptEditor Dialog: Switch + Beschreibungs-Zeile auf Mobile vertikal stacken.
   Per @media weil CascadingValue IsMobile aus MainLayout Dialog-Content nicht erreicht (Dialogs
   leben ausserhalb von @Body im MudDialogProvider).
   Doppel-Klassen-Selektor (.web-search-switch-row.flex-row) hat Specificity (0,2,0) und gewinnt
   gegen MudBlazors `.flex-row { flex-direction: row !important; }` (0,1,0), unabhaengig von der
   Stylesheet-Ladereihenfolge. */
:where([data-aura-view="mobile"]) .web-search-switch-row.flex-row {
    flex-direction: column !important;
    align-items: flex-start !important;
}

/* Beschreibungstext am linken Rand des Switches ausrichten (NICHT am Label). MudSwitch hat
   eine kleine padding-left (~12 px) fuer den Focus-Ring vor dem eigentlichen Track — den
   gleichen Offset geben wir dem p darunter, damit beide am gleichen X starten. */
:where([data-aura-view="mobile"]) .web-search-switch-row.flex-row > p.mud-typography {
    padding-left: 12px;
}

/* Generator-Page Mobile: VoiceControls als fixierte Bottom-Bar mit 3 Buttons nebeneinander
   (Assistent Start / Stop / Sprach-Monitor-Toggle). */
.gen-voice-controls-mobile {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 100;
    background: var(--mud-palette-surface);
    box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);
    padding: 8px;
    display: flex;
    gap: 6px;
}

.gen-voice-controls-mobile .gen-voice-btn {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
}

/* Bild-Annotator: laufende Texteingabe.
   Diese beiden Elemente werden von wwwroot/js/imageAnnotator.js erzeugt und tragen daher
   KEIN Scoped-CSS-Attribut — die Stile muessen global stehen, nicht in
   ImageAnnotatorOverlay.razor.css. Position, Schriftgroesse und Farbe setzt das Skript
   inline in Bildschirm-Pixeln, damit der Text exakt dort landet, wo er beim
   Festschreiben auf den Canvas gezeichnet wird. */
.image-annotator-text-entry {
    position: absolute;
    z-index: 2;
    margin: 0;
    /* 1px Rand + 2px Padding = TEXT_BOX_INSET (3) in imageAnnotator.js */
    padding: 0 2px;
    /* Leicht abgedunkelt, damit auch weisser Text auf einem weissen Plan sichtbar bleibt —
       der Kasten verschwindet beim Festschreiben wieder */
    background: rgba(0, 0, 0, 0.3);
    border: 1px dashed rgba(255, 255, 255, 0.9);
    border-radius: 2px;
    outline: none;
    white-space: pre;
    box-sizing: content-box;
    /* Rand + Padding gehen als TEXT_BOX_INSET in imageAnnotator.js ein — beim Aendern
       dort mit anpassen, sonst verschiebt sich der Text beim Festschreiben.
       line-height setzt das Skript ueber die font-Kurzschreibweise. */
}

.image-annotator-text-entry::placeholder {
    color: rgba(255, 255, 255, 0.55);
    font-weight: normal;
}

/* Eigener Griff zum Verschieben: ein Klick IN den Kasten setzt den Textcursor,
   gezogen wird ausschliesslich am Griff */
.image-annotator-text-handle {
    position: absolute;
    z-index: 3;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.9);
    border-radius: 3px;
    cursor: move;
    touch-action: none;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
}

/* Vier-Wege-Pfeil als Verschiebe-Andeutung, ohne Abhaengigkeit von einer Icon-Bibliothek */
.image-annotator-text-handle::before {
    content: "✥";
    color: #202020;
    font-size: 0.8em;
    line-height: 1;
}

/* Utility fuer MudTextField: hebt MudBlazors `resize: none` auf. Nur vertikal, sonst wird
   die Textarea breiter als ihr Dialog. Details: AuraMobileDesign.md, Mobile-Patterns. */
.aura-resizable-textarea textarea.mud-input-root {
    resize: vertical !important;
    max-width: 100%;
}

/* Utility für die aufgeklappten Detailzeilen des Kalkulationsassistenten (Phase 4 und 5;
   Phase 2 setzt denselben Wert direkt auf ihre Detailzeile, siehe VergabePhase.razor.css).
   Die Detailzeilen spannen per colspan über alle Spalten ihrer Tabelle; in Phase 4 und 5 ist
   die breiter als das Fenster. Über diese volle Breite gezogen wäre der Inhalt — Langtext,
   Angebotsübersicht, Kalkulations-Aufschlüsselung — nur mit horizontalem Scrollen lesbar,
   deshalb bleibt er auf Lesebreite und klebt beim Scrollen am linken Rand, wie die
   eingefrorenen Pos.- und Kurztext-Spalten. Global statt scoped, weil dieselbe Regel in
   mehreren Phasen gilt.

   Die Breite ist zusätzlich an den verfügbaren Platz gekoppelt: eine feste Obergrenze allein
   reicht nicht, weil die Tabelle horizontal scrollt. Ist der Block breiter als der sichtbare
   Bereich, läuft er rechts heraus und genau das Scrollen wäre wieder nötig, das die Begrenzung
   verhindern soll.

   Maßstab ist dabei 100cqi — die Breite des Phasen-Rumpfes (.preisspiegel-root bzw.
   .angebotserstellung-root, dort als Container deklariert), NICHT die Fensterbreite: der
   Navigationsbereich links gehört nicht zum verfügbaren Platz, mit einer vw-Angabe lief der
   Inhalt auf schmalen Bildschirmen weiterhin über. Die vw-Zeile davor bleibt als Rückfallebene
   für Browser ohne Container-Queries stehen — ohne sie fiele dort die ganze Deklaration weg
   und die Zeile hätte gar keine Begrenzung mehr.

   Die 900 px sind derselbe Wert, auf den Phase 2 ihre Detailzeile begrenzt
   (VergabePhase.razor.css) — die Detailzeilen lesen sich damit in allen Phasen gleich. */
.calc-detail-lesebreite {
    max-width: min(900px, 85vw);
    max-width: min(900px, 100cqi);
    position: sticky;
    left: 0;
}

/* Kurztext-Zelle mit Aufklapp-Indikator (Kalkulationsassistent Phase 4 und 5).
   Das Chevron steht in einer eigenen Spalte links neben dem Text statt im Textfluss: sonst
   rücken die Icons die erste Zeile ein und ein umgebrochener Kurztext beginnt darunter
   versetzt. Global statt scoped, weil MudIcon das Scope-Attribut nicht mitrendert und
   dieselbe Regel in beiden Phasen gilt. */
.calc-kurztext-zeile {
    display: flex;
    align-items: center;
    gap: 6px;
}

.calc-kurztext-aufklapper {
    flex: 0 0 18px;
    width: 18px;
    height: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    opacity: 0.55;
}

.calc-kurztext-aufklapper .mud-icon-root {
    width: 18px;
    height: 18px;
    font-size: 18px;
}

/* min-width: 0 lässt den Text im Flex-Container umbrechen, statt die Zelle aufzudehnen. */
.calc-kurztext-inhalt {
    min-width: 0;
}

/* --- DialogHeader (Components/Shared/DialogHeader.razor) -------------------------------------
   Der farbige Balken soll die volle Dialogbreite einnehmen. Er hat sich früher mit
   margin:-16px -24px aus dem Padding des Titelbereichs herausgezogen — das passt aber nur,
   solange der Titel wirklich 24px rechtes Padding hat. Sobald der Dialog einen Schließen-Button
   bekommt, setzt MudBlazor `.mud-dialog-title:has(.mud-button-close) { padding-inline-end: 64px }`,
   und der Balken endete 40px vor dem rechten Rand. Statt die Werte nachzurechnen, bekommt der
   Titelbereich hier gar kein Padding mehr — der Balken bringt es selbst mit. */
.mud-dialog .mud-dialog-title:has(> .aura-dialog-header) {
    padding: 0 !important;
}

.aura-dialog-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 24px;
    background: var(--mud-palette-primary);
    border-top-left-radius: inherit;
    border-top-right-radius: inherit;
}

/* Der Schließen-Button liegt absolut oben rechts im Dialog. Titel und optionale Header-Aktionen
   brauchen deshalb rechts Platz, sonst laufen sie darunter. */
.mud-dialog .mud-dialog-title:has(.mud-button-close) > .aura-dialog-header {
    padding-inline-end: 64px;
}

/* Der Schließen-Button sitzt jetzt auf dem farbigen Balken und braucht dessen Kontrastfarbe. */
.mud-dialog .mud-dialog-title:has(> .aura-dialog-header) .mud-button-close {
    color: var(--mud-palette-primary-text, #fff);
}

/* --------------------------------------------------------------------------------------
   PDF-Vergleich: Kopfzeile wie bei den uebrigen Betrachtern, Fusszeile entfallen.

   Was war: MudBlazors Vorgabe fuer die Titelzeile (`padding: 16px 64px 16px 24px`) plus eine
   eigene Fusszeile mit „Neu generieren" und „Schliessen" — zusammen gut 120px, die den beiden
   PDFs fehlten. „Neu generieren" steht jetzt als Symbol in der Kopfzeile, „Schliessen" ist
   ersatzlos weg: das X oben rechts tut dasselbe.

   Unterschied zu ChatAttachmentViewerModal/DocumentViewerModal: Dort steht EINE Aktion neben
   dem X, und sie wird absolut auf eine feste Rechtsposition gesetzt (`.viewer-kopf__aktion`).
   Hier sind es zwei (Gleichlauf, Neu generieren) — bei jedem weiteren Symbol braeuchte man eine
   weitere Festposition. Sie stehen deshalb als normale Flex-Kinder in der Zeile; ausgespart
   wird nur das X, das MudBlazor selbst absolut setzt.
   -------------------------------------------------------------------------------------- */
.pdf-compare-dialog .mud-dialog-title {
    /* Bezugsrahmen fuer das absolut gesetzte X von MudBlazor. */
    position: relative;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    /* 8px Randabstand des X + dessen 40px + 8px Luft zur letzten eigenen Aktion. */
    padding-right: 56px !important;
}

/* Wie bei den anderen Betrachtern: X mittig der Zeile statt an ihrer Oberkante, und 8px statt
   MudBlazors 12px Polsterung, damit es 40px statt 48px misst. `!important`, weil MudBlazors
   eigene Regel dieselbe Spezifitaet traegt und spaeter geladen wird. */
.pdf-compare-dialog .mud-dialog-title .mud-button-close {
    top: 50% !important;
    transform: translateY(-50%) !important;
    padding: 8px !important;
}

.viewer-kopf__aktionen {
    display: flex;
    align-items: center;
    gap: 4px;
    /* Ohne das drueckt ein langer Titel die Symbole zusammen. */
    flex: 0 0 auto;
}

/* 40px statt MudBlazors 48px — die Knopfgroesse ist die Untergrenze der Kopfzeile. */
.viewer-kopf__aktionen .mud-icon-button {
    padding: 8px;
}

/* Zwei gleich breite Spalten ueber die volle Dialogbreite.

   85dvh statt der 80dvh der uebrigen Betrachter: hier ist die Fusszeile entfallen, und zwei
   nebeneinanderstehende PDFs brauchen jede Zeile. dvh statt vh aus demselben Grund wie bei
   .chat-attachment-viewer-body — vh ist auf dem Telefon die GROSSE Viewporthoehe. */
.pdf-compare-body {
    display: flex;
    gap: 8px;
    width: 100%;
    height: 85dvh;
}

.pdf-compare-seite {
    flex: 1;
    display: flex;
    flex-direction: column;
    /* Pflicht bei Flex: sonst bleibt die Inhaltsbreite die Mindestbreite und die beiden
       Spalten teilen sich nicht gleichmaessig. */
    min-width: 0;
}

.pdf-compare-seite__kopf {
    display: flex;
    align-items: center;
    gap: 4px;
    /* Seitlicher Abstand, den der Dialog durch `pa-0` nicht mehr mitbringt. */
    padding: 0 8px;
    min-height: 32px;
}

.pdf-compare-rahmen {
    flex: 1;
    /* Ohne min-height waechst der Rahmen mit seinem Inhalt statt zu scrollen. */
    min-height: 0;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
}

/* --------------------------------------------------------------------------------------
   PDF-Vergleich auf dem Telefon: Vollbild und EINE Seite zur Zeit.

   Was war: zwei Spalten auch auf 390px Breite — je gut 190px, in denen das gerenderte PDF
   unleserlich klein wurde. Statt nebeneinander steht jetzt eine Fassung ueber die volle
   Breite, umgeschaltet ueber die Zeile darueber. Der Gleichlauf laeuft weiter, beide Rahmen
   stehen also an derselben Stelle, wenn man wechselt.
   -------------------------------------------------------------------------------------- */
.pdf-compare-umschalter {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 8px;
    /* Darf im Vollbild-Flex nicht mitschrumpfen, sonst frisst der Betrachter die Zeile auf. */
    flex-shrink: 0;
}

.pdf-compare-body--einzeln {
    display: block;
    /* Bezugsrahmen fuer die beiden uebereinanderliegenden Seiten. */
    position: relative;
}

.pdf-compare-body--einzeln .pdf-compare-seite {
    position: absolute;
    inset: 0;
    /* `visibility` statt `display: none`: Ein ausgeblendetes iframe hat clientWidth 0, und die
       Seite darin rechnet ihre Skalierung genau daraus (`containerWidth` im eingebetteten
       Viewer-HTML). Beim Laden waere die gerade nicht gewaehlte Fassung damit auf Breite 0
       gerendert und bliebe leer, bis man neu generiert. Mit `visibility` bleibt sie im Layout,
       wird nur nicht gezeichnet — und ist zugleich nicht anklickbar. */
    visibility: hidden;
}

.pdf-compare-body--einzeln .pdf-compare-seite.aktiv {
    visibility: visible;
}

.pdf-compare-dialog.mud-dialog-fullscreen {
    display: flex !important;
    flex-direction: column !important;
    /* Der Abstand zur Statusleiste haengt am DIALOG, nicht an der Kopfzeile — als deren
       padding-top verschoebe er den Titel gegenueber dem absolut mittig gesetzten X. */
    padding-top: env(safe-area-inset-top, 0px);
}

.pdf-compare-dialog.mud-dialog-fullscreen .mud-dialog-title {
    flex-shrink: 0;
    padding-left: calc(16px + env(safe-area-inset-left, 0px)) !important;
    /* 56px Aussparung fuer das X, plus Notch im Querformat. */
    padding-right: calc(56px + env(safe-area-inset-right, 0px)) !important;
}

.pdf-compare-dialog.mud-dialog-fullscreen .mud-dialog-content {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    overflow: hidden !important;
    /* Volle Breite, nur der Notch-Abstand bleibt. */
    padding-left: env(safe-area-inset-left, 0px) !important;
    padding-right: env(safe-area-inset-right, 0px) !important;
    /* Der untere Sicherheitsabstand haengt am Inhalt: eine Fusszeile, die ihn vorher getragen
       haette, gibt es hier nicht mehr. */
    padding-bottom: env(safe-area-inset-bottom, 0px) !important;
}

/* Im Vollbild bestimmt der Dialog die Hoehe, nicht der Kasten — mit seinen 85dvh ragte er
   sonst unter der Umschaltzeile heraus. `min-height: 0` ist Pflicht, sonst weigert sich das
   Flex-Kind zu schrumpfen und schiebt den ganzen Dialog auf. */
.pdf-compare-dialog.mud-dialog-fullscreen .pdf-compare-body {
    height: auto;
    flex: 1 1 auto;
    min-height: 0;
}

/* --------------------------------------------------------------------------------------
   Protokoll- und Layoutvorschau: Kopfzeile wie die uebrigen Betrachter, Fusszeile entfaellt.

   Was war: MudBlazors Vorgabe fuer die Titelzeile, darunter zwei Hinweisabsaetze untereinander
   und eine Fusszeile mit "Schliessen" und "Word-Vorschau herunterladen" — zusammen rund 120px
   ueber und unter einem Betrachter, der ohnehin nur 65vh bekam. "Schliessen" ist entfallen (das
   X tut dasselbe), der Word-Download steht als Symbol im Kopf, die Hinweise stehen einzeilig.

   Die Kopfzeile benutzt dieselben Klassen wie der PDF-Vergleich (.viewer-kopf,
   .viewer-kopf__aktionen) — dort steht auch, warum die Aktionen als Flex-Kinder und nicht
   absolut gesetzt sind.
   -------------------------------------------------------------------------------------- */
.protokoll-vorschau-dialog .mud-dialog-title {
    /* Bezugsrahmen fuer das absolut gesetzte X von MudBlazor. */
    position: relative;
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    /* 8px Randabstand des X + dessen 40px + 8px Luft zur eigenen Aktion. */
    padding-right: 56px !important;
}

.protokoll-vorschau-dialog .mud-dialog-title .mud-button-close {
    top: 50% !important;
    transform: translateY(-50%) !important;
    padding: 8px !important;
}

/* dvh statt vh aus demselben Grund wie bei .chat-attachment-viewer-body — vh ist auf dem
   Telefon die GROSSE Viewporthoehe. 80dvh wie dort; die 65vh von vorher liessen oben und
   unten Luft, die mit der Fusszeile mitgegangen ist. */
.protokoll-vorschau-body {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 80dvh;
}

/* Linksbuendig mit dem Titel darueber: MudBlazor gibt der Titelzeile `padding: 16px 24px`, und
   die 24px links bleiben stehen — ueberschrieben werden nur oben, unten und rechts. Mit eigenen
   12px stand der Hinweis sichtbar weiter links als die Ueberschrift.

   Oben kein Abstand: `.viewer-kopf` ist mit `min-height: 40px` hoeher als seine Textzeile (die
   Hoehe kommt von den Symbolen daneben) und bringt die Luft schon mit. Zusaetzliche 4px liessen
   die beiden Zeilen auseinanderfallen. */
.protokoll-vorschau-hinweis {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 0 24px 6px;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
    flex-shrink: 0;
}

.protokoll-vorschau-rahmen {
    flex: 1;
    /* Ohne min-height waechst der Rahmen mit seinem Inhalt, statt intern zu scrollen. */
    min-height: 0;
}

/* --- Vollbild auf dem Telefon --- */
.protokoll-vorschau-dialog.mud-dialog-fullscreen {
    display: flex !important;
    flex-direction: column !important;
    /* Der Abstand zur Statusleiste haengt am DIALOG, nicht an der Kopfzeile — als deren
       padding-top verschoebe er den Titel gegenueber dem absolut mittig gesetzten X. */
    padding-top: env(safe-area-inset-top, 0px);
}

.protokoll-vorschau-dialog.mud-dialog-fullscreen .mud-dialog-title {
    flex-shrink: 0;
    padding-left: calc(16px + env(safe-area-inset-left, 0px)) !important;
    padding-right: calc(56px + env(safe-area-inset-right, 0px)) !important;
}

.protokoll-vorschau-dialog.mud-dialog-fullscreen .mud-dialog-content {
    flex: 1 1 auto !important;
    min-height: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    overflow: hidden !important;
    padding-left: env(safe-area-inset-left, 0px) !important;
    padding-right: env(safe-area-inset-right, 0px) !important;
    /* Der untere Sicherheitsabstand haengt am Inhalt: eine Fusszeile, die ihn vorher getragen
       haette, gibt es nicht mehr. */
    padding-bottom: env(safe-area-inset-bottom, 0px) !important;
}

/* Im Vollbild bestimmt der Dialog die Hoehe, nicht der Kasten. `min-height: 0` ist Pflicht,
   sonst weigert sich das Flex-Kind zu schrumpfen und schiebt den ganzen Dialog auf. */
/* Im Vollbild traegt die Kopfzeile 16px plus Notch, und der Inhaltsbereich hat den Notch-Abstand
   bereits — hier bleiben also glatte 16px, sonst zaehlte der Notch doppelt. */
.protokoll-vorschau-dialog.mud-dialog-fullscreen .protokoll-vorschau-hinweis {
    padding-left: 16px;
    padding-right: 16px;
}

.protokoll-vorschau-dialog.mud-dialog-fullscreen .protokoll-vorschau-body {
    height: auto;
    flex: 1 1 auto;
    min-height: 0;
}