/* 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 `FullScreen = IsMobile` an den beiden Aufrufstellen in
   ChatComponent; hier haengt nur noch an MudBlazors eigener Vollbild-Klasse. Damit gibt es
   KEINE zweite Mobile-Definition neben auraViewport.js. */
.chat-attachment-viewer-body {
    width: 100%;
    height: 80dvh;
}

.chat-attachment-viewer-dialog.mud-dialog-fullscreen {
    display: flex !important;
    flex-direction: column !important;
}

.chat-attachment-viewer-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;
}

.chat-attachment-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;
    padding-left: calc(8px + env(safe-area-inset-left, 0px)) !important;
    padding-right: calc(8px + 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 {
    height: auto;
    flex: 1 1 auto;
    min-height: 0;
}

.chat-attachment-viewer-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;
}

.chat-attachment-viewer-dialog.mud-dialog-fullscreen .mud-button-root {
    min-height: 48px;
}

/* 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;
    }
}

/* 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);
}
