﻿/* Phase 7: colours come from tokens.css (design-token layer). This file holds only structural and
   component styles that reference those tokens - no raw hex values (the one deliberate exception is
   the last-resort .blazor-error-boundary banner, kept at a fixed critical red that stays readable in
   both modes). The font-family now lives in tokens.css (html, body). */

a, .btn-link {
    color: var(--accent);
}

/* Phase 7: compact heading scale (direction C). Overrides Bootstrap's larger defaults. */
h1, .h1 { font-size: 1.3rem; font-weight: 600; }
h2, .h2 { font-size: 1.075rem; font-weight: 600; }
h3, .h3 { font-size: 0.95rem; font-weight: 600; }
h4, h5, h6 { font-weight: 600; }

/* Bare <legend> as a section heading (e.g. Mein Konto: Stammdaten / Kennwort). Bootstrap renders it
   floated and oversized; make it read like an h2 (Markus, 2026-08-20). The wizard/interface legends
   have their own, more specific rules and are unaffected. */
legend {
    float: none;
    width: auto;
    font-size: 1.075rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 0.75rem;
}

/* Phase 7: bind Bootstrap's own CSS variables to our design tokens, so every Bootstrap component
   (tables, cards, forms, alerts, tabs, badges, list groups) adopts the palette in BOTH modes without
   touching each page. Targeting [data-bs-theme] (always set by theme.js) at equal specificity to
   Bootstrap's own theme rules, loaded after bootstrap.css, so these win; the values are token vars
   that already flip with data-theme, so light/dark follow automatically. */
[data-bs-theme] {
    --bs-body-bg: var(--surface-0);
    --bs-body-color: var(--text-primary);
    --bs-body-font-family: var(--font-sans);
    --bs-emphasis-color: var(--text-primary);
    --bs-secondary-color: var(--text-secondary);
    --bs-secondary-bg: var(--surface-1);
    --bs-tertiary-bg: var(--surface-2);
    --bs-border-color: var(--border);
    --bs-border-color-translucent: var(--border);
    --bs-heading-color: var(--text-primary);
    --bs-link-color: var(--accent);
    --bs-link-hover-color: var(--accent);
    --bs-link-color-rgb: var(--accent-rgb);
}

/* Waehrend eines Themenwechsels zur Laufzeit liegen ALLE Uebergaenge still - theme.js setzt dazu
   .theme-switching auf <html> und nimmt es im naechsten Bild wieder weg.

   Der Grund ist ein gemessener Fehler, nicht Geschmack: wechselt der Wert einer CSS-Variablen,
   waehrend auf der betroffenen Eigenschaft ein transition liegt, behaelt das Element den ALTEN
   Wert - dauerhaft, bis etwas anderes es neu berechnet (ein Neuladen etwa; ein erzwungener
   Repaint reicht nicht). Betroffen waren genau die Elemente, denen Bootstrap eine
   color-Transition mitgibt: .nav-link (die Eintraege der Seitenleiste) und .btn ("Abmelden").
   Die Seitenleiste behielt nach dem Umschalten die Schriftfarbe des vorherigen Modus - dunkel
   auf dunkel, gemessene 2,62:1 (2026-09-03).

   Nebeneffekt, der so gewollt ist: der Wechsel schaltet hart um statt die ganze Seite
   ueberzublenden. Die Hover-Uebergaenge sind unmittelbar danach wieder aktiv. */
html.theme-switching,
html.theme-switching *,
html.theme-switching *::before,
html.theme-switching *::after {
    transition: none !important;
}

/* Bootstrap .table: in this theme bundle --bs-table-color resolves empty, so cells fall back to a
   baked #212529 (dark-on-dark in dark mode). Bind the table's own variables + cell colour to the
   tokens so every Bootstrap table on every page is readable in both modes. */
.table {
    --bs-table-color: var(--text-primary);
    --bs-table-bg: transparent;
    --bs-table-border-color: var(--border);
    --bs-table-striped-color: var(--text-primary);
    --bs-table-hover-color: var(--text-primary);
    color: var(--text-primary);
}

.table > :not(caption) > * > * {
    color: var(--text-primary);
    border-color: var(--border);
}

.table > thead > tr > th {
    color: var(--text-secondary);
    border-color: var(--border);
}

/* Bootstrap .card also bakes a literal white bg that ignores the theme - bind to tokens. */
.card {
    --bs-card-bg: var(--surface-1);
    --bs-card-color: var(--text-primary);
    --bs-card-border-color: var(--border);
    --bs-card-cap-bg: var(--surface-2);
    background-color: var(--surface-1);
    color: var(--text-primary);
    border-color: var(--border);
}

/* Bootstrap alerts: bind each variant to the status tokens so they theme (readable dark alerts in
   dark mode) instead of staying fixed pale-light with dark text. */
.alert { border-width: 0.5px; }
.alert-danger { color: var(--danger); background-color: var(--danger-bg); border-color: var(--danger-bg); }
.alert-warning { color: var(--warn); background-color: var(--warn-bg); border-color: var(--warn-bg); }
.alert-success { color: var(--ok); background-color: var(--ok-bg); border-color: var(--ok-bg); }
.alert-info, .alert-primary, .alert-secondary { color: var(--accent); background-color: var(--accent-bg); border-color: var(--accent-bg); }

/* Form fields (Markus, 2026-08-20: inputs must read as clearly DARK wells in dark mode, not blend
   with the card). Bootstrap's .form-control bakes --bs-body-bg; point it and the Syncfusion inputs at
   --field-bg (the darkest surface in dark mode / white in light) so every field is a sunken well. */
.form-control, .form-select {
    background-color: var(--field-bg);
    color: var(--text-primary);
    border-color: var(--border);
}

.form-control::placeholder { color: var(--text-muted); }

.input-group-text {
    background-color: var(--surface-2);
    color: var(--text-secondary);
    border-color: var(--border);
}

.btn-primary {
    color: var(--primary-fg);
    background-color: var(--primary);
    border-color: var(--primary);
    transition: background-color 0.12s ease, border-color 0.12s ease, filter 0.12s ease;
}

.btn-primary:hover, .btn-primary:focus {
    color: var(--primary-fg);
    background-color: var(--primary-hover);
    border-color: var(--primary-hover);
}

/* Druck-Feedback: beim Klick deutlich dunkler + leichter Innenschatten (wirkt eingedrückt),
   bewusst ohne Bewegung. */
.btn-primary:active {
    filter: brightness(0.85);
    box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.5);
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 2px var(--accent);
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

/* Ueberschrift einer Hauptseite mit dem Zeichen des Menueeintrags davor (Markus, 10.09.2026).
   Das Zeichen kommt aus PageGlyphs, damit Menue und Ueberschrift dieselbe Quelle haben.

   Kein Flex: eine Ueberschrift ist eine Textzeile, und als Flex-Behaelter wuerde jeder Ausdruck
   darin (z. B. "Willkommen, @m_name") zu einem eigenen Element mit eigenem Wortabstand. Das
   Zeichen bleibt inline, der Ausgleich laeuft ueber vertical-align. */
.page-heading {
    display: block;
}

.page-glyph {
    color: var(--accent);
    font-size: 0.85em;
    margin-right: 0.5rem;
    /* Gemessen, nicht geschaetzt (10.09.2026, dieselbe Rechnung wie bei .nav-glyph): das Zeichen
       sitzt auf der Grundlinie und stand 0.80px unter der Versalmitte der Ueberschrift. 0.085em
       heben es auf 0.00px. In em, damit der Ausgleich mit der Schriftgroesse mitwaechst. */
    vertical-align: 0.085em;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--ok);
}

.invalid {
    outline: 1px solid var(--danger);
}

.validation-message {
    color: var(--danger);
}

/* Inline-Validierungsmeldung direkt unter dem Feld (FieldValidationMessage.razor, in den
   Lizenz-Assistenten). Rot, klein, mit Warnglyph; role="alert" traegt sie zusaetzlich an
   Screenreader. */
.field-error {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.35rem;
    color: var(--danger);
    font-size: 0.875rem;
}

    .field-error i {
        font-size: 0.9em;
        flex-shrink: 0;
    }

/* Bestaetigungs-Modal (navguard.js, Ungespeichert-Warnung) - in unserem Stil ueber die Design-Tokens,
   damit es in Hell und Dunkel passt. Wird per JS erzeugt, nicht in einer .razor gerendert. */
/* align-items: flex-start + margin: auto am Dialog statt align-items: center (Markus,
   14.09.2026): ein zentriertes Flex-Element, das hoeher ist als sein Behaelter, waechst nach
   OBEN aus dem Bild heraus, und dieser Teil ist durch Scrollen nicht mehr erreichbar. Mit
   margin: auto zentriert der Dialog genauso, bleibt bei Platzmangel aber vollstaendig
   erreichbar. overflow-y ist das Netz fuer den Extremfall, dass schon Kopf und Fuss allein
   nicht mehr hineinpassen. */
.app-modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 1060;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    overflow-y: auto;
    padding: 1rem;
    background: rgba(15, 23, 32, 0.55);
}

/* max-height + Flex-Spalte sind der Kern: OHNE sie war nur der RUMPF gedeckelt (65vh), Kopf und
   Fuss kamen obendrauf, und der Dialog wurde hoeher als das Fenster. Gemessen am 14.09.2026 bei
   600x280: Dialog 306px hoch, Oberkante bei -13px, der Fuss mit "Bearbeiten" und "Lizenz
   erstellen" 13px unter dem unteren Rand - die Knoepfe waren nicht erreichbar. Jetzt gibt die
   Hoehe des Fensters die Obergrenze vor, Kopf und Fuss behalten ihren Platz, und der Rumpf
   nimmt den Rest. */
.app-modal {
    width: 100%;
    max-width: 30rem;
    max-height: 100%;
    margin: auto;
    display: flex;
    flex-direction: column;
    background: var(--surface-1);
    color: var(--text-primary);
    border: 0.5px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.35);
    overflow: hidden;
    animation: app-modal-in 0.12s ease-out;
}

.app-modal-header {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 1.1rem 1.25rem 0.5rem;
    font-size: 1.15rem;
    font-weight: 600;
}

    .app-modal-header .app-modal-icon {
        color: var(--danger);
        font-size: 1.05em;
        flex-shrink: 0;
    }

.app-modal-body {
    padding: 0.25rem 1.25rem 1.1rem;
    color: var(--text-secondary);
    line-height: 1.55;
}

.app-modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0.5rem 1.25rem 1.25rem;
}

.app-modal-danger {
    background: var(--danger);
    border: 1px solid var(--danger);
    color: #fff;
}

    .app-modal-danger:hover,
    .app-modal-danger:focus {
        background: var(--danger);
        border-color: var(--danger);
        color: #fff;
        filter: brightness(0.92);
    }

/* Breitere Variante fuer den Bestaetigungsdialog (detaillierte Zusammenfassung) mit scrollbarem
   Body, falls die Liste laenger wird. */
.app-modal-wide {
    max-width: 46rem;
}

/* min-height: 0 ist die Zeile, ohne die das Ganze nicht wirkt: ein Flex-Element schrumpft von
   sich aus NICHT unter seine Inhaltshoehe. Ohne sie bliebe der Rumpf so hoch wie sein Inhalt und
   schoebe den Fuss weiterhin aus dem Bild - die Hoehenbegrenzung am Dialog waere wirkungslos.

   max-height: 65vh bleibt als Obergrenze fuer GROSSE Bildschirme: dort soll ein Dialog nicht die
   ganze Hoehe einnehmen. Auf kleinen entscheidet jetzt die Flex-Schrumpfung. */
.app-modal-scroll {
    max-height: 65vh;
    min-height: 0;
    overflow-y: auto;
}

.app-modal-icon-accent {
    color: var(--accent);
    font-size: 1.05em;
    flex-shrink: 0;
}

/* Zusaetze fuer FormModal (Eingabeformulare im Dialog). Der Koerper eines Bestaetigungsdialogs ist
   Lesetext (--text-secondary); ein Formular braucht die volle Textfarbe, sonst wirken die
   Feldbeschriftungen ausgegraut wie gesperrte Felder. */
.app-modal-body-form {
    color: var(--text-primary);
}

    /* Der letzte Feldabstand faellt weg - der Fussbereich bringt seinen eigenen Rand mit. */
    .app-modal-body-form .row > [class^="col"] > .mb-3:last-child,
    .app-modal-body-form > .row:last-child {
        margin-bottom: 0;
    }

/* Fehlerzeile zwischen Koerper und Aktionen: ausserhalb des scrollbaren Bereichs, damit sie beim
   Speicherfehler sichtbar ist und nicht unter dem Bildlauf verschwindet. */
.app-modal-alert {
    padding: 0 1.25rem 0.25rem;
}

/* Von/Bis-Zeile der Protokollseiten (Mailprotokoll, Aktivitaetsprotokoll). Die Felder bleiben
   schmal - ein Datum braucht keine halbe Bildschirmbreite -, die Anzahl steht rechts daneben auf
   Feldhoehe (align-items: end richtet an der Unterkante aus, damit die Beschriftungen darueber
   nicht mitwandern). */
.log-filters {
    display: flex;
    align-items: end;
    gap: 1rem;
    margin-bottom: 1rem;
    flex-wrap: wrap;
}

.log-field {
    width: 11rem;
}

    .log-field .form-label {
        margin-bottom: 0.5rem;
    }

.log-filters-count {
    /* Auf Hoehe der Feldunterkante statt an der Grundlinie der Beschriftung. */
    padding-bottom: 0.5rem;
}

/* Kontrollkaestchen als FELDZEILE. Eine nackte .form-check unter einem Eingabefeld sitzt auf der
   Hoehe der Beschriftung der Nachbarspalte und wirkt dadurch verrutscht (Markus, 2026-09-03). Diese
   Klasse gibt ihr die Hoehe eines Eingabefelds (2.5rem, Hausmass) und zentriert den Inhalt darin -
   zusammen mit einem .form-label darueber liegt sie damit auf einer Linie mit den Feldern daneben.
   Bootstrap positioniert .form-check-input per float mit negativem Rand; beides wird hier
   zurueckgenommen, das Aussehen des Kaestchens bleibt. */
.check-field {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2.5rem;
}

    .check-field .form-check-input {
        float: none;
        margin: 0;
        flex-shrink: 0;
    }

    .check-field label {
        margin: 0;
        cursor: pointer;
    }

/* Kennwortfeld und "Vorschlagen" in einer Zeile (Benutzerverwaltung). Das Textfeld nimmt den
   Platz, der Knopf bleibt so breit wie sein Text - deshalb flex und kein Raster. */
.user-password-row {
    display: flex;
    gap: 0.5rem;
    align-items: stretch;
}

    .user-password-row > .e-control-wrapper,
    .user-password-row > .e-input-group {
        flex: 1 1 auto;
        min-width: 0;
    }

    .user-password-row > .btn {
        flex: 0 0 auto;
        white-space: nowrap;
    }

/* Kreuz in der Kopfzeile. Schliesst wie "Abbrechen" (siehe FormModal.razor). */
.app-modal-close {
    flex-shrink: 0;
    padding: 0.15rem 0.4rem;
    border: none;
    border-radius: 0.25rem;
    background: transparent;
    color: var(--text-secondary);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

    .app-modal-close:hover:not(:disabled),
    .app-modal-close:focus-visible {
        background: var(--surface-2);
        color: var(--text-primary);
    }

    .app-modal-close:disabled {
        opacity: 0.5;
        cursor: default;
    }

/* Zusammenfassungs-Raster im Bestaetigungsdialog: Label-Spalte + Wert, mit feiner Trennlinie je
   Zeile. Auf schmalen Viewports untereinander. */
.confirm-summary {
    display: flex;
    flex-direction: column;
}

.confirm-row {
    display: grid;
    grid-template-columns: 12rem 1fr;
    gap: 0.15rem 1rem;
    padding: 0.45rem 0;
    border-top: 1px solid var(--border);
}

    .confirm-row:first-child {
        border-top: none;
    }

.confirm-label {
    color: var(--text-secondary);
    font-weight: 500;
}

.confirm-value {
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

/* Free text keeps the line breaks it was typed with (Markus, 14.09.2026). HTML collapses them,
   so a note entered as a list arrived as one run-on paragraph - on the detail page, in the print
   view, in the confirmation dialog before creating a licence and in the before/after table of the
   history alike. The same fix the licence mail already got.

   pre-wrap and not pre-line: what someone typed into a textarea is shown as typed, indentation
   included. This requires the expression to sit TIGHT in its element (<td>@Display(...)</td>);
   with the value on its own line, the indentation of the markup would become visible text.

   Used on every cell that carries free text - not only on the two fields that prompted it. A
   field that keeps its line breaks and a neighbouring one that does not would be the kind of
   difference nobody can explain later. */
.text-multiline {
    white-space: pre-wrap;
}

@media (max-width: 32rem) {
    .confirm-row {
        grid-template-columns: 1fr;
        gap: 0.1rem;
    }
}

@keyframes app-modal-in {
    from { opacity: 0; transform: translateY(0.5rem); }
    to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
    .app-modal {
        animation: none;
    }
}

/* Aufgabe 10, Schritt 1: Reconnect-Anzeige (Markup in MainLayout.razor). Blazor setzt auf
   #components-reconnect-modal selbst eine der vier Zustandsklassen; welche der drei Kindzeilen
   sichtbar ist, entscheidet ausschliesslich diese Kombination aus Selektoren. */
#components-reconnect-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1050;
    align-items: center;
    justify-content: center;
    background: rgba(20, 20, 30, 0.55);
}

    #components-reconnect-modal.components-reconnect-show,
    #components-reconnect-modal.components-reconnect-failed,
    #components-reconnect-modal.components-reconnect-rejected {
        display: flex;
    }

    #components-reconnect-modal .reconnect-message {
        display: none;
    }

    #components-reconnect-modal.components-reconnect-show .reconnect-message-show,
    #components-reconnect-modal.components-reconnect-failed .reconnect-message-failed,
    #components-reconnect-modal.components-reconnect-rejected .reconnect-message-rejected {
        display: flex;
        align-items: center;
        gap: 0.75rem;
    }

    #components-reconnect-modal .reconnect-message {
        background: var(--surface-1);
        color: var(--text-primary);
        border: 0.5px solid var(--border);
        border-radius: var(--radius);
        box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.3);
        padding: 1rem 1.5rem;
        max-width: 90vw;
    }

    #components-reconnect-modal .reconnect-message-failed,
    #components-reconnect-modal .reconnect-message-rejected {
        color: var(--danger);
        background: var(--danger-bg);
    }

    #components-reconnect-modal .reconnect-spinner {
        display: inline-block;
        width: 1rem;
        height: 1rem;
        flex-shrink: 0;
        border: 0.15rem solid var(--border-strong);
        border-top-color: var(--accent);
        border-radius: 50%;
        animation: reconnect-spin 0.8s linear infinite;
    }

@keyframes reconnect-spin {
    to {
        transform: rotate(360deg);
    }
}

/* Last-resort unhandled-error banner (Blazor framework element). Deliberate fixed critical red -
   readable with white text in both light and dark, exempt from theming. */
.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "Es ist ein Fehler aufgetreten."
    }

.darker-border-checkbox.form-check-input {
    border-color: var(--border-strong);
}

/* Skip-to-content link (Task 3): visually hidden until focused, never display:none. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 2000;
    padding: 0.5rem 1rem;
    background: var(--surface-1);
    color: var(--accent);
    border: 0.5px solid var(--border-strong);
    border-radius: var(--radius);
}

.skip-link:focus {
    left: 0.5rem;
    top: 0.5rem;
}

/* Kennzahlenkarten (Übersicht, Lizenzliste). Baustein StatCard nutzt .stat-card. */
.stat-card {
    display: flex;
    flex-direction: column;
    min-width: 200px;
    padding: 1rem 1.25rem;
    border: 0.5px solid var(--border);
    border-radius: var(--radius-card);
    background: var(--surface-1);
}

.license-summary-cards {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-md);
    margin-bottom: 1.5rem;
}

.stat-card-value {
    font-size: 1.5rem;
    font-weight: 600;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
}

.stat-card-label {
    color: var(--text-secondary);
    font-size: 0.8rem;
}

.stat-card-delta { font-size: 0.75rem; margin-top: 0.35rem; }
.stat-card-delta.ok { color: var(--ok); }
.stat-card-delta.warn { color: var(--warn); }
.stat-card-delta.neutral { color: var(--text-muted); }

/* Dichte Hairline-Tabelle (Baustein, Aufgabe 4/5+). */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}

.data-table th {
    text-align: left;
    font-weight: 500;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    background: var(--surface-2);
    border-bottom: 0.5px solid var(--border);
    padding: 0.5rem 0.75rem;
}

.data-table td {
    padding: 0.55rem 0.75rem;
    border-bottom: 0.5px solid var(--border);
    color: var(--text-primary);
}

.data-table tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover td { background: var(--surface-2); }
.data-table .num { font-variant-numeric: tabular-nums; text-align: right; }

/* Ghost-Knopf (sekundär). Also used on anchors (e.g. "Kennwort ändern", "Zur Lizenzliste") - hence
   inline-flex + no underline, so a link styled as a button never shows the link underline. */
.btn-ghost {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    background: transparent;
    color: var(--text-primary);
    border: 0.5px solid var(--border-strong);
    border-radius: var(--radius);
    padding: 0.35rem 0.75rem;
    text-decoration: none;
}

/* .btn-ghost/.hub-tile sind <a>-Elemente. Ohne explizite Hover-Farbe gewinnt Bootstraps globales
   a:hover (Spezifität 0,1,1 > unsere Klasse 0,1,0) und färbt den Text auf die Link-Hover-Farbe
   (Blau) - schlecht lesbar auf dem aufgehellten Grund. Darum die Textfarbe im Hover explizit
   festnageln (Markus, 2026-08-31). */
.btn-ghost:hover { background: var(--surface-2); color: var(--text-primary); text-decoration: none; }

/* Produkt-Chip (Aufgabe 4). Einzige Stelle mit Produktfarben. */
.product-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.72rem;
    font-weight: 600;
    padding: 0.15rem 0.55rem;
    border-radius: 0.375rem;
}

.product-chip .dot { width: 7px; height: 7px; border-radius: 50%; }
.product-chip.ax { background: var(--prod-aidax-bg); color: var(--prod-aidax-fg); }
.product-chip.ax .dot { background: var(--prod-aidax); }
.product-chip.at { background: var(--prod-aidatouch-bg); color: var(--prod-aidatouch-fg); }
.product-chip.at .dot { background: var(--prod-aidatouch); }
.product-chip.dc { background: var(--prod-datacenter-bg); color: var(--prod-datacenter-fg); }
.product-chip.dc .dot { background: var(--prod-datacenter); }

/* Status-Pill (Aufgabe 4). Immer Icon + Text, nie Farbe allein. */
.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.75rem;
    font-weight: 500;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
}

.status-pill.ok { background: var(--ok-bg); color: var(--ok); }
.status-pill.warn { background: var(--warn-bg); color: var(--warn); }
.status-pill.danger { background: var(--danger-bg); color: var(--danger); }
.status-pill.neutral { background: var(--surface-2); color: var(--text-secondary); border: 0.5px solid var(--border); }
.status-pill-icon { font-size: 0.7rem; line-height: 1; }

/* Ueberschrift mit Abzeichen (Lizenzdetail). Ohne Flexbox kleben Produktchip und Statuspille
   am Namen und aneinander, weil beide inline-flex sind und die Ueberschrift keinen Abstand
   dazwischen setzt (Markus, 10.09.2026). Zwei Werte bei gap: 0.45rem Zeilenabstand, falls der
   Betriebsname umbricht, 0.6rem zwischen den Abzeichen. align-items: center statt baseline -
   die Pillen haben eigene Innenabstaende, an der Grundlinie ausgerichtet saehen sie versetzt aus. */
.license-detail-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem 0.6rem;
}

/* Die Tooltip-Huelle um den Produktchip. Ohne diese Regel ist sie ein blockartiges Flex-Element
   in Zeilenhoehe, und der Chip darin sitzt auf ihrer Textgrundlinie - gemessen 3 px unter der
   Mitte, waehrend die Statuspille daneben mittig sass. Genau dieser Versatz sah "nicht
   zentriert" aus. Kein Griff an die Ueberschrift selbst: deren erstes Kind traegt den
   Betriebsnamen, und ein inline-flex darum wuerde den Umbruch langer Namen aendern. */
.license-detail-title-chip {
    display: inline-flex;
    align-items: center;
}

/* Lange Freitexte in einer Gitterzelle lesbar machen (Mailprotokoll: Empfaenger und Fehler).
   Syncfusion setzt white-space: nowrap auf .e-rowcell; weil white-space vererbt wird, genuegt
   es, den Wert am KIND wieder zu setzen - dafuer braucht es keine !important-Regel gegen das
   Thema. Die Zeile waechst dann in die Hoehe, was in einem Protokoll richtig ist: ein
   abgeschnittener Fehlertext ist kein Fehlertext.
   word-break statt overflow-wrap: eine SMTP-Meldung enthaelt oft eine lange Zeichenkette ohne
   Leerzeichen (Adresse, Ablaufverfolgung), die sonst als ein einziges Wort ueber den Rand liefe. */
.grid-cell-wrap {
    display: block;
    white-space: normal;
    word-break: break-word;
    line-height: 1.35;
}

/* Vorschaurahmen fuer die Mail im Mailprotokoll. Weisser Grund AUCH im Dunkelmodus - und das
   ist kein Versehen: der Empfaenger sieht die Mail in seinem Mailprogramm, nicht im Portalthema.
   Faerbte der Rahmen mit, zeigte die Vorschau etwas, das so nie ankommt. */
.mail-preview-frame {
    width: 100%;
    height: 26rem;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: #ffffff;
}

/* Phase 7: signed-out "box" pages (AuthLayout). Centred card on the plain page background. */
.auth-shell {
    position: relative;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: var(--surface-0);
    padding: 2rem;
}

.auth-toolbar {
    position: absolute;
    top: 1rem;
    right: 1rem;
}

.auth-card {
    width: min(420px, 94vw);
    background: var(--surface-1);
    border: 0.5px solid var(--border);
    border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
    padding: 2rem;
}

.auth-card-wide {
    width: min(600px, 94vw);
}

.auth-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.7rem;
    font-weight: 700;
    font-size: 1.6rem;
    color: var(--text-primary);
    margin-bottom: 1.75rem;
}

/* The brand mark is now the favicon SVG (its own blue tile), so no background/letter here - just
   size it and round the corners (Markus, 2026-08-20). */
.auth-brand-mark {
    display: block;
    width: 60px;
    height: 60px;
    border-radius: 15px;
    flex-shrink: 0;
}

.auth-title {
    font-size: 1.15rem;
    margin-bottom: 1.25rem;
}

.auth-links {
    margin-top: 2rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    font-size: 0.875rem;
}

/* Impressum link under the centred auth box (signed-out pages). */
.auth-footer {
    margin-top: 1.5rem;
    font-size: 0.8rem;
    /* Die Huelle zentriert den Fuss als Ganzes (.auth-shell ist eine Flex-Spalte mit
       align-items: center), er ist also so breit wie seine breiteste Zeile. Ohne text-align
       stuende die kuerzere Zeile darin linksbuendig - gemessen am 11.09.2026: Impressum 66 px in
       einem 163 px breiten Fuss, also sichtbar aus der Mitte (Markus). */
    text-align: center;
}

/* Abgemeldete Fassung von /impressum (PublicPageLayout, Markus 2026-09-04): dieselbe Huelle wie
   die Anmeldeseiten, aber OBEN beginnend statt senkrecht mittig. Der Grund ist gemessen: der
   Impressumstext ist hoeher als das Fenster, und ein zentriertes Flex-Element, das ueberlaeuft,
   schiebt seinen Anfang aus dem Bild - man kann dann nach oben nicht mehr scrollen. Die
   Anmeldekarte ist klein genug, dass dort die Mitte richtig bleibt. */
.public-shell {
    justify-content: flex-start;
}

/* Lesbare Breite, sonst laufen die Zeilen ueber die ganze Fensterbreite. Sonst wie eine Karte:
   dieselbe Flaeche, derselbe Rand, dieselbe Rundung wie .card. */
.public-card {
    width: min(46rem, 94vw);
    background: var(--surface-1);
    border: 0.5px solid var(--border);
    border-radius: var(--radius-card);
    padding: 1.5rem 1.75rem;
}

    .public-card h1 {
        margin-top: 0;
    }

    .public-card address {
        font-style: normal;
    }

/* NUR direkte Kinder: seit 2026-09-04 steht im Fuss auch das <dialog> des Impressums, und dessen
   Verweise (www.xss.at, office@xss.at) hatten sonst die gedaempfte, nicht unterstrichene
   Fussfarbe geerbt - gemessen #a4b5c9 statt des Akzents, sie sahen also nicht wie Verweise aus.
   Der Dialog rendert in der Top-Layer, steckt im DOM aber weiterhin im Fuss. */.auth-footer > a { color: var(--text-secondary); text-decoration: none; }
.auth-footer > a:hover { color: var(--accent); }

/* Accent (primary call-to-action) button - e.g. the login submit. One per view. */
.btn-accent {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    background: var(--accent-solid);
    color: var(--on-accent);
    border: none;
    border-radius: var(--radius);
    padding: 0.55rem 1rem;
    font-size: 0.95rem;
    cursor: pointer;
    transition: background-color 0.12s ease, filter 0.12s ease;
}

.btn-accent:hover { background: var(--accent-solid-hover); color: var(--on-accent); }

/* Druck-Feedback: beim Klick deutlich dunkler + leichter Innenschatten (wirkt eingedrückt),
   bewusst ohne Bewegung. */
.btn-accent:active {
    filter: brightness(0.85);
    box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.5);
}

/* Sekundäre Buttons (Bootstrap-Basis) bekommen dasselbe Druck-Feedback. */
/* Bootstrap laesst .form-text und .btn-outline-secondary auf seinem eingebackenen Grau #6c757d
   stehen - die beiden lesen die Tokens NICHT. Nachgemessen faellt dieses Grau in BEIDEN Modi
   durch: 4,26:1 auf der hellen Karte und 3,19:1 auf dem alten Anthrazit (heute 3,91:1 auf
   "Mitternacht"). Es traegt das "Abbrechen" in FormModal und ConfirmDialog, also jeden Dialog,
   dazu "Vorschlagen" und die Assistenten-Knoepfe - insgesamt elf Stellen. Aufgefallen beim
   Nachmessen der neuen Palette (2026-09-03), war aber vorher schon so.
   Die Werte spiegeln .btn-ghost: derselbe Zweck, jetzt auch dieselbe Farbe.
   ACHTUNG - hier stehen echte Eigenschaften und keine --bs-btn-*-Variablen: dieses
   Bootstrap-Bundle schreibt bei .btn-outline-* noch direkte Farben (color/border-color), die
   Variablen-Schnittstelle der Knoepfe kam erst mit 5.2. Eine Fassung mit --bs-btn-color setzt
   die Variable brav, aendert aber nichts - gemessen. */
.form-text {
    color: var(--text-muted);
}

/* Dasselbe Grau, dritte Stelle: Bootstrap setzt .text-muted als
   "color: #6c757d !important" - deshalb braucht der Token hier ebenfalls !important, sonst
   gewinnt die Hilfsklasse. 22 Stellen in acht Dateien (LicenseDetail, die zwei Assistenten,
   MailLog, ActivityLog, DataBrowser, Account, InterfacePicker). Gemessen auf "Mitternacht":
   4,20:1 vorher, 7,55:1 danach. */
.text-muted {
    color: var(--text-muted) !important;
}

.btn-outline-secondary {
    color: var(--text-secondary);
    border-color: var(--border-strong);
}

.btn-outline-secondary:hover,
.btn-outline-secondary:active,
.btn-outline-secondary.active {
    color: var(--text-primary);
    background-color: var(--surface-2);
    border-color: var(--border-strong);
}

.btn-outline-secondary.disabled,
.btn-outline-secondary:disabled {
    color: var(--text-muted);
    background-color: transparent;
    border-color: var(--border);
}

.btn-secondary { transition: background-color 0.12s ease, border-color 0.12s ease, filter 0.12s ease; }
.btn-secondary:active { filter: brightness(0.88); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35); }

/* Lizenzen-Hub (/licenses): große, klickbare Auswahl-Kacheln als Einstieg in die Lizenz-Aktionen
   (Markus, 2026-08-31). Hover = dezente Fläche + Akzentrahmen; Klick = dasselbe Press-Feedback
   (Abdunklung + Innenschatten) wie die übrigen Buttons, ohne Bewegung. */
.hub-intro {
    color: var(--text-secondary);
    margin-bottom: 1.25rem;
}

/* Breiter als die ursprünglichen 220/900px (Markus, 2026-09-07): in der Entwurf-Zone stehen jetzt
   Bezeichnung, Zeitpunkt und zwei Knöpfe, und dafür war eine 289px-Kachel eng. min(340px, 100%)
   statt 340px, damit auf einem Handy (375px) keine Spalte breiter als das Fenster wird - eine
   feste Untergrenze in minmax() erzeugt genau diesen waagrechten Überlauf.

   ALLE KACHELN GLEICH HOCH (Markus, 2026-09-07) - also die Voreinstellung stretch, kein
   align-items: start. Hier stand zwischenzeitlich start, weil mit stretch .hub-tile-main (flex: 1)
   wächst und die Entwurf-Zone an den unteren Kartenrand schiebt: bei 10 Aida.X- und einem
   Aida.Touch-Entwurf stand der Touch-Entwurf mehrere hundert Pixel unter seiner eigenen
   Überschrift. Das war behoben, kostete aber die gleiche Höhe. Beides geht, wenn die
   überschüssige Höhe woanders landet - siehe die drei flex-Regeln bei .hub-tile-main und
   .hub-tile-draft weiter unten. */
.hub-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
    gap: 1rem;
    max-width: 1120px;
}

.hub-tile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 1.5rem;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-1);
    color: var(--text-primary);
    text-decoration: none;
    transition: background-color 0.12s ease, border-color 0.12s ease, filter 0.12s ease;
}

.hub-tile:hover {
    background: var(--surface-2);
    border-color: var(--accent);
    color: var(--text-primary);
}

.hub-tile:active { filter: brightness(0.88); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35); }

.hub-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 10px;
    /* Gefüllter Akzent-Chip mit Kontrastfarbe: das Icon (weiß auf Akzent) ist so klar sichtbar,
       der zarte --accent-bg-Hintergrund war zu kontrastarm (Markus, 2026-08-31). */
    background: var(--accent-solid);
    color: var(--on-accent);
    font-size: 1.35rem;
}

.hub-tile-title {
    font-size: 1.05rem;
    font-weight: 600;
}

.hub-tile-desc {
    color: var(--text-secondary);
    font-size: 0.875rem;
    line-height: 1.35;
}

/* "Neue Lizenz"-Kacheln, die einen offenen Entwurf haben können: EINE Karte aus klickbarem
   Hauptbereich (hub-tile-main) und - falls ein Entwurf vorliegt - einer integrierten Entwurf-Zone
   unten (hub-tile-draft). Die Karte trägt Rahmen, Rundung und Hintergrund; die Kinder blenden sich
   ein (Markus, 2026-09-02). overflow:hidden beschneidet die getönte Entwurf-Zone auf die Rundung. */
.hub-tile-card {
    display: flex;
    flex-direction: column;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-1);
    overflow: hidden;
    transition: border-color 0.12s ease;
}

.hub-tile-card:hover {
    border-color: var(--accent);
}

/* Klickbarer Hauptbereich (führt zum Assistenten). Gleiches Innenlayout wie .hub-tile.

   WOHIN DIE ÜBERSCHÜSSIGE HÖHE GEHT (Markus, 2026-09-07). Das Raster streckt alle Karten auf die
   Höhe der höchsten; irgendein Kind muss den Rest aufnehmen. Drei Regeln entscheiden das:

     1. Hier flex: 0 0 auto - der Hauptbereich bleibt so hoch wie sein Inhalt. Nur so klebt die
        Entwurf-Zone DIREKT unter der Beschreibung statt am Kartenboden.
     2. .hub-tile-main:last-child bekommt flex: 1 1 auto - hat die Karte gar keine Entwürfe, ist
        der Hauptbereich das letzte Kind und darf die Karte füllen. Sonst blieb bei einer Karte
        ohne Entwürfe ein unerklärlicher leerer Streifen unter dem Text.
     3. .hub-tile-draft bekommt flex: 1 1 auto - mit Entwürfen nimmt die getönte Zone den Rest
        auf. Ihre Liste sitzt oben, der Überschuss liegt getönt darunter und sieht damit gewollt
        aus statt wie ein Loch. */
.hub-tile-main {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 1.5rem;
    flex: 0 0 auto;
    color: var(--text-primary);
    text-decoration: none;
    transition: background-color 0.12s ease;
}

.hub-tile-main:last-child {
    flex: 1 1 auto;
}

.hub-tile-main:hover {
    background: var(--surface-2);
    color: var(--text-primary);
    text-decoration: none;
}

.hub-tile-main:active { filter: brightness(0.88); }

/* Integrierte Entwurf-Zone am unteren Rand DERSELBEN Karte: leicht getönt und nur durch eine
   Trennlinie oben abgesetzt (keine eigene Umrandung/Rundung - die Karte klippt). Horizontal wie der
   Hauptbereich eingerückt (1.5rem), damit Text und Titel bündig stehen. Enthält seit 2026-09-07 die
   Entwurfsliste (.draft-list) statt eines einzelnen Streifens. */
.hub-tile-draft {
    flex: 1 1 auto;
    padding: 0.75rem 1.5rem 1rem;
    border-top: 0.5px solid var(--border);
    background: var(--accent-bg);
    font-size: 0.85rem;
    color: var(--text-secondary);
}

/* Entwurfsliste (Components/Shared/LicenseDraftList.razor): eine Zeile je Entwurf, links
   Bezeichnung und Zeitpunkt, rechts die Aktionen. Verwendet in den Hub-Kacheln UND im
   Angebot-Banner der beiden Assistenten (Markus, 2026-09-07, als aus einem Entwurf je Produkt
   mehrere wurden).

   Alles in EINER Zeile - Bezeichnung, Trennpunkt, Zeitpunkt -, weil die Rückfrage "wirklich
   verwerfen?" ebenfalls einzeilig ist: die Zeile behält damit ihre Höhe, ohne dass eine
   min-height nötig wäre (die stand hier vorher, als Zeitpunkt und Text zweizeilig waren). */
.draft-list {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.draft-list-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.5rem;
}

.draft-list-text {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.35rem;
    line-height: 1.2;
    min-width: 0;
}

    .draft-list-text i {
        color: var(--accent);
        margin-top: 0.1em;
    }

    /* Das Warndreieck der "wirklich verwerfen?"-Rückfrage in Danger-Farbe statt Akzent. */
    .draft-list-text i.fa-triangle-exclamation {
        color: var(--danger);
    }

/* Die Bezeichnung ist der Betriebsname und darf umbrechen, statt die Zeile zu sprengen -
   overflow-wrap greift auch mitten im Wort, weil ein Betriebsname keine Leerzeichen haben muss. */
.draft-list-label {
    font-weight: 600;
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

/* --text-secondary, nicht --text-muted: gemessen am 07.09.2026 auf dem getönten Grund der
   Entwurf-Zone (--accent-bg) kam der Zeitpunkt mit --text-muted im hellen Thema auf 4,55:1 - über
   der Schwelle, aber ohne Luft. Mit --text-secondary sind es 6,11:1, und die Abstufung zur
   fetten Bezeichnung (--text-primary) bleibt trotzdem erkennbar. */
.draft-list-sep,
.draft-list-saved {
    color: var(--text-secondary);
}

/* margin-left:auto schiebt die Aktionen an den rechten Rand - auch dann noch, wenn sie in eine
   eigene Zeile umbrechen (die schmalen Hub-Kacheln), denn eine automatische Außenkante wirkt je
   Flex-Zeile. Genau so soll es sein: die Knöpfe stehen immer rechts, ob neben oder unter dem Text. */
.draft-list-actions {
    display: flex;
    gap: 0.5rem;
    margin-left: auto;
}

/* In der Rückfrage trennt der Punkt nichts mehr ("Hotel Sonnblick wirklich verwerfen?" ist ein
   Satz, keine zwei Angaben) - deshalb weg, während Aufbau und Höhe der Zeile gleich bleiben. */
.draft-list-row-confirm .draft-list-sep {
    display: none;
}

/* In der Hub-Kachel ist deutlich weniger Breite als im Banner des Assistenten: dort steht die
   Zeile einzeilig, hier bekommt die zweite Angabe ihre eigene Zeile (flex-basis: 100% in einer
   umbrechenden Flex-Zeile) und der Trennpunkt entfällt. Das gilt für BEIDE Zustände - Zeitpunkt
   wie Rückfrage -, und nur deshalb springt die Kachel beim Klick auf "Verwerfen" nicht mehr:
   zwei Zeilen bleiben zwei Zeilen. */
.hub-tile-draft .draft-list-sep {
    display: none;
}

.hub-tile-draft .draft-list-saved {
    flex-basis: 100%;
}

/* Angebot-Banner im Assistenten: der einleitende Satz über der Entwurfsliste. Es bleibt ein
   Bootstrap-.alert-info, nur der Inhalt ist jetzt zweistufig statt einzeilig. */
.draft-offer {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.draft-offer-lead {
    font-weight: 600;
}

/* Neuigkeiten (Übersicht): kleiner Release-Notes-Feed - je Eintrag eine Karte mit gefülltem
   Akzent-Icon (wie die Hub-Kacheln, damit das Glyph klar sichtbar ist), Titel + Datum und den
   Punkten als Aufzählung (Markus, 2026-08-31). */
.news-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.news-item {
    display: flex;
    gap: 1rem;
    padding: 1.25rem;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-1);
}

.news-item-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 10px;
    background: var(--accent-solid);
    color: var(--on-accent);
    font-size: 1.1rem;
}

.news-item-body {
    min-width: 0;
}

.news-item-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.news-item-title {
    font-size: 1.2rem;
    font-weight: 600;
    color: var(--text-primary);
}

/* Version und Datum einer Neuigkeit, mit einem Mittelpunkt getrennt (Markus, 16.09.2026). Der
   Trenner steckt im ::before des Datums und entfaellt damit von selbst, wenn keine Version
   dabeisteht - kein Satzzeichen ohne Text davor. */
.news-item-meta {
    display: inline-flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.4rem;
    font-size: 0.8rem;
    color: var(--text-secondary);
}

.news-item-version {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.news-item-version + .news-item-date::before {
    content: "·";
    margin-right: 0.4rem;
}

.news-item-date {
    font-size: 0.8rem;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.news-item-points {
    margin: 0;
    padding-left: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.news-item-points li {
    position: relative;
    padding-left: 1.4rem;
    color: var(--text-secondary);
    line-height: 1.4;
}

/* Runder Akzent-Punkt als Aufzählungszeichen (Markus, 2026-08-31). */
.news-item-points li::before {
    content: "\2022";
    position: absolute;
    left: 0.35rem;
    top: -0.03em;
    color: var(--accent);
    font-size: 1.15em;
    line-height: inherit;
}

.news-item-text {
    margin: 0;
    color: var(--text-secondary);
    line-height: 1.4;
    white-space: pre-line;
}

/* Extra breathing room above the login submit (between the password field and the button). */
.auth-submit { margin-top: 0.75rem; }

/* Phase 7: quiet footer with the always-visible Impressum link. */
.app-footer {
    display: flex;
    align-items: center;
    gap: var(--gap-lg);
    padding: 0.75rem 1.5rem;
    border-top: 0.5px solid var(--border);
    color: var(--text-muted);
    font-size: 0.8rem;
}

/* NUR direkte Kinder: seit 2026-09-04 steht im Fuss auch das <dialog> des Impressums, und dessen
   Verweise (www.xss.at, office@xss.at) hatten sonst die gedaempfte, nicht unterstrichene
   Fussfarbe geerbt - gemessen #a4b5c9 statt des Akzents, sie sahen also nicht wie Verweise aus.
   Der Dialog rendert in der Top-Layer, steckt im DOM aber weiterhin im Fuss. */.app-footer > a { color: var(--text-secondary); }
.app-footer > a:hover { color: var(--accent); }

/* Die eigene Versionsangabe (Markus, 11.09.2026). Im angemeldeten Fuss rechts aussen, damit sie
   das Impressum nicht bedraengt; in der kargen Huelle der Anmeldeseiten in einer eigenen Zeile
   darunter, weil dort alles zentriert steht und nebeneinander gedraengt wirkte. Leise gefaerbt:
   sie ist Auskunft fuer den Support, kein Bedienelement. */
.app-version {
    color: var(--text-muted);
}

.app-footer .app-version {
    margin-left: auto;
}

.auth-footer .app-version {
    display: block;
    margin-top: 0.35rem;
}

/* Sichtbarkeits-Editor im Partner-Dialog (Markus, 2026-09-04): je anderer Partner eine Zeile mit
   zwei Haekchen - "sehen" und "bearbeiten".

   Eigener Rahmen mit Bildlauf statt einer offenen Liste: bei zehn Partnern passt es knapp, bei
   dreissig waere der Dialog sonst laenger als der Bildschirm. Die Hoehe ist bewusst in rem
   angegeben und nicht in Zeilen - ACHTUNG, 1rem sind in diesem Projekt 16px (Browservorgabe),
   nicht die 18px von myAida.LIVE. */
.visibility-list {
    max-height: 14rem;
    overflow-y: auto;
    padding: 0.35rem 0.6rem;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--field-bg);
}

.visibility-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap-md);
    padding: 0.15rem 0;
}

    .visibility-row + .visibility-row {
        border-top: 0.5px solid var(--border);
    }

/* Das zweite Haekchen tritt zurueck: es ist die Ausnahme, nicht die Regel. Feste Breite, damit
   die Spalte ueber alle Zeilen an derselben Stelle steht. */
.visibility-write {
    flex: 0 0 8rem;
    color: var(--text-muted);
    font-size: 0.85rem;
}

    /* Gesperrt, solange "sehen" nicht gesetzt ist - das muss man sehen und nicht erst durch
       Klicken merken. */
    .visibility-write input:disabled + label {
        opacity: 0.5;
    }

/* Impressum im Seitenfuss: ein Knopf, der wie der frueher hier stehende Verweis aussieht - der
   Text oeffnet seit 2026-09-04 einen Dialog statt die Seite zu wechseln (siehe
   ImpressumLink.razor). Deshalb muss ihm die Knopf-Optik abgenommen werden: kein Rahmen, keine
   Flaeche, Schriftgroesse und Farbe vom Fuss geerbt. */
.impressum-link {
    padding: 0;
    border: none;
    background: none;
    font: inherit;
    color: var(--text-secondary);
    cursor: pointer;
}

.impressum-link:hover {
    color: var(--accent);
}

/* Der Dialog selbst ist ein natives <dialog>. Das erspart den Ueberzug samt z-index: showModal()
   setzt ihn in die Top-Layer des Browsers, also OBERHALB jedes Stapelkontexts - genau die Falle,
   die .app-modal-overlay mit z-index: 1060 umgehen muss. Escape, Fokusfalle und ::backdrop
   bringt er mit.

   Die Masse folgen .app-modal (gleiche Rundung, gleicher Rand, gleicher Schatten), damit der
   Dialog nicht wie ein Fremdkoerper neben dem Blazor-Dialog steht. Schriftgroesse und Farbe
   werden AUSDRUECKLICH gesetzt: als Kind des Fusses wuerde er sonst dessen 0,8rem und
   --text-muted erben. */
.impressum-dialog {
    width: min(34rem, 92vw);
    max-height: 85vh;
    padding: 0;
    background: var(--surface-1);
    color: var(--text-primary);
    font-size: 0.875rem;
    line-height: 1.55;
    border: 0.5px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.35);
    overflow: hidden;
}

.impressum-dialog::backdrop {
    background: rgba(15, 23, 32, 0.55);
}

.impressum-dialog-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap-md);
    padding: 0.85rem 1.25rem;
    border-bottom: 0.5px solid var(--border);
}

    .impressum-dialog-head h2 {
        margin: 0;
        font-size: 1.15rem;
        font-weight: 600;
    }

.impressum-dialog-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: none;
    border-radius: var(--radius);
    background: none;
    color: var(--text-secondary);
    font-size: 1rem;
    cursor: pointer;
}

    .impressum-dialog-close:hover {
        background: var(--surface-2);
        color: var(--text-primary);
    }

/* Eigener Bildlauf, damit der Kopf mit dem Schliessen-Knopf stehen bleibt. */
.impressum-dialog-body {
    max-height: calc(85vh - 4rem);
    overflow-y: auto;
    padding: 1.1rem 1.25rem 1.25rem;
    color: var(--text-secondary);
}

    .impressum-dialog-body address {
        font-style: normal;
    }

/* Die Bezeichnungsliste stammt von der Seite /impressum, wo sie in Bootstraps Raster laeuft
   (dt = col-sm-3). Im Dialog ist das zu eng: gemessen lief "Firmenbuchnummer" in seinen Wert
   hinein. Statt einer geratenen Prozentzahl bekommt die Liste hier ein eigenes Raster, dessen
   erste Spalte max-content breit ist - sie richtet sich also nach dem laengsten Begriff und kann
   per Konstruktion nicht kollidieren, auch wenn spaeter ein laengerer dazukommt.
   Die Regeln fuer dt/dd heben Bootstraps Rasterbreiten und -abstaende auf. */
.impressum-dialog-body dl.row {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: var(--gap-lg);
    row-gap: 0.3rem;
    margin: 0 0 1rem;
}

    .impressum-dialog-body dl.row > dt,
    .impressum-dialog-body dl.row > dd {
        width: auto;
        max-width: none;
        flex: none;
        padding: 0;
        margin: 0;
    }

    .impressum-dialog-body dl.row > dt {
        color: var(--text-primary);
    }

/* Automatische Abmeldung bei Untätigkeit (IdleLogout.razor, Markus 2026-09-07). Dieselbe Hülle wie
   der Impressum-Dialog - natives <dialog>, Rahmen, Rundung, ::backdrop -, nur schmaler und ohne
   scrollenden Körper: es sind zwei Sätze und ein Countdown. Absichtlich KEIN Schließen-Kreuz und
   kein Klick-auf-den-Hintergrund: hier gibt es zwei echte Entscheidungen, und "weggeklickt" wäre
   keine davon. Escape schließt trotzdem (Standardaktion des Browsers) - das ist in Ordnung, denn
   eine Taste ist Aktivität und setzt die Uhr ohnehin zurück. */
.idle-dialog {
    width: min(26rem, 92vw);
    padding: 0;
    background: var(--surface-1);
    color: var(--text-primary);
    font-size: 0.875rem;
    line-height: 1.55;
    border: 0.5px solid var(--border);
    border-radius: var(--radius-card);
    box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.35);
    overflow: hidden;
}

.idle-dialog::backdrop {
    background: rgba(15, 23, 32, 0.55);
}

.idle-dialog-head {
    padding: 0.85rem 1.25rem;
    border-bottom: 0.5px solid var(--border);
}

    .idle-dialog-head h2 {
        display: flex;
        align-items: center;
        gap: 0.55rem;
        margin: 0;
        font-size: 1.05rem;
        font-weight: 600;
    }

    .idle-dialog-head i {
        color: var(--warn);
    }

.idle-dialog-body {
    padding: 1.1rem 1.25rem 0.35rem;
    color: var(--text-secondary);
}

    .idle-dialog-body p {
        margin-bottom: 0.9rem;
    }

/* Der Countdown ist die einzige Zahl im Dialog und darf sich nicht bewegen: tabular-nums hält die
   Ziffernbreite fest, sonst zappelt "01:09" gegen "01:10". */
.idle-countdown {
    display: flex;
    align-items: baseline;
    gap: 0.45rem;
    color: var(--text-primary);
}

    .idle-countdown i {
        color: var(--accent);
        font-size: 0.95rem;
    }

.idle-countdown-value {
    font-size: 1.35rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.idle-countdown-label {
    color: var(--text-secondary);
}

.idle-dialog-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0.85rem 1.25rem 1.1rem;
}

/* Phase 7: colour-scheme switch (ThemeToggle) in the top bar. */
.theme-toggle {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-1);
}

.theme-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 26px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease, filter 0.12s ease;
}

.theme-toggle-btn:hover { background: var(--surface-2); color: var(--text-primary); }
.theme-toggle-btn:active { filter: brightness(0.88); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35); }

/* Active state driven purely by <html data-theme> (kept correct by theme.js across every
   navigation), NOT by a JS-toggled class - the class was wiped when Blazor patched the DOM on
   enhanced navigation, leaving no button marked (Markus, 2026-08-20). The .active class is still set
   by aidaThemeMark() for aria-pressed, but the visual no longer depends on it. */
.theme-toggle-btn.active,
:root[data-theme="light"] .theme-toggle-btn[data-theme-pref="light"],
:root[data-theme="dark"] .theme-toggle-btn[data-theme-pref="dark"] {
    background: var(--accent-bg);
    color: var(--accent);
}

/* Phase 7: bind Syncfusion's OWN CSS variables to our tokens GLOBALLY. Every Syncfusion component
   (grid, inputs/SfTextBox, buttons, popups) reads these --color-sf-* vars, which the single
   bootstrap5.3.css bundle bakes for LIGHT only (they do NOT follow data-bs-theme). Mapping them once
   here themes every Syncfusion component in both modes - one place to change. Scoped to [data-bs-theme]
   (always set by theme.js) so it wins over Syncfusion's own equal-specificity theme rules. */
[data-bs-theme] {
    --color-sf-content-bg-color: var(--surface-1);
    --color-sf-content-bg-color-alt1: var(--surface-2);
    --color-sf-content-bg-color-alt2: var(--surface-2);
    --color-sf-content-bg-color-alt3: var(--surface-2);
    --color-sf-content-bg-color-hover: var(--surface-2);
    --color-sf-content-bg-color-focus: var(--surface-2);
    --color-sf-table-bg-color-hover: var(--surface-2);
    --color-sf-content-text-color: var(--text-primary);
    --color-sf-content-text-color-alt1: var(--text-secondary);
    --color-sf-content-text-color-hover: var(--text-primary);
    --color-sf-content-text-color-selected: var(--text-primary);
    --color-sf-icon-color: var(--text-secondary);
    --color-sf-border: var(--border);
    --color-sf-border-light: var(--border);
    --color-sf-border-default: var(--border);
    --color-sf-border-hover: var(--border-strong);
    --color-sf-border-focus: var(--accent);
    --color-sf-flyout-bg-color: var(--surface-1);
    --color-sf-flyout-bg-color-hover: var(--surface-2);
}

/* Syncfusion inputs (SfTextBox). Two Markus requests (2026-08-20): (1) a dark input WELL in dark mode
   - point the group background at --field-bg rather than the content surface; (2) match Bootstrap's
   .form-control height (they rendered noticeably shorter) - a 38px min-height on the group and
   Bootstrap's own 0.375rem/0.75rem padding on the inner input. */
.e-input-group.e-control-wrapper,
.e-input-group.e-control-wrapper:not(.e-disabled),
.e-input-group:not(.e-disabled) {
    background: var(--field-bg);
    min-height: 38px;
}

.e-input-group.e-control-wrapper input.e-control,
.e-input-group input.e-control {
    background: transparent;
    padding: 0.375rem 0.75rem;
}

/* SfNumericTextBox-Spin-Buttons nach dem myAida.LIVE-Muster (Markus, 2026-08-31): Das
   bootstrap5.3-Theme gibt den Spin-Buttons im Hover einen dunklen Hintergrund - auf dunklem Grund
   wird das Glyph dann schwarz-auf-schwarz und unlesbar. Wir setzen daher eine definierte Farbe in
   BEIDEN Zuständen (Ruhe + Hover) und ein stabiles Flex-Layout; dadurch entfällt der schwarze
   Hover UND das Umfärb-/Relayout-Flackern beim Klicken (der Ripple bleibt bewusst erhalten, wie
   in myAida). Grid/Pager sind ausgenommen, damit deren kompakte Spinner unangetastet bleiben. */
.e-input-group:not(.e-grid *):not(.e-pager *):has(input.e-numerictextbox) .e-input-group-icon.e-spin-up,
.e-input-group:not(.e-grid *):not(.e-pager *):has(input.e-numerictextbox) .e-input-group-icon.e-spin-down {
    flex: 1 1 0;
    height: auto;
    min-height: 0;
    max-height: none;
    margin: 0;
    padding: 0;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
}

.e-input-group:not(.e-grid *):not(.e-pager *):has(input.e-numerictextbox) .e-input-group-icon.e-spin-up:hover,
.e-input-group:not(.e-grid *):not(.e-pager *):has(input.e-numerictextbox) .e-input-group-icon.e-spin-down:hover {
    color: var(--accent);
    background: var(--accent-bg);
}

/* Fokus-Ring der Syncfusion-Inputs an unseren Akzent angleichen (statt des theme-eigenen blauen
   Schattens), ebenfalls nach dem myAida.LIVE-Muster. */
.e-input-group.e-input-focus {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
    box-shadow: none !important;
}

/* SfDropDownList/ComboBox: Aufklapp-Pfeil (Caret) nach demselben myAida.LIVE-Muster wie die
   Spin-Buttons - definierte Farbe in Ruhe (--text-secondary) und Hover (--accent auf --accent-bg).
   Sonst gibt das bootstrap5.3-Theme im Hover einen dunklen Hintergrund und der Pfeil wird
   schwarz-auf-schwarz/unlesbar. Grid/Pager/Popup ausgenommen (deren Steuerelemente behalten die
   kompakte Theme-Optik). */
.e-ddl.e-input-group:not(.e-grid *):not(.e-pager *):not(.e-popup *) .e-ddl-icon {
    color: var(--text-secondary);
    background: transparent !important;
}

.e-ddl.e-input-group:not(.e-grid *):not(.e-pager *):not(.e-popup *) .e-ddl-icon:hover {
    color: var(--accent);
    background: var(--accent-bg) !important;
}

.e-grid {
    border-color: var(--border);
    background-color: var(--surface-1);
}

/* Der Kopf hebt sich zu wenig vom Inhalt ab (Markus, 2026-09-03). Drei Mittel zusammen: die eigene
   Kopffarbe (--grid-header-bg, im Dunkelmodus bewusst kraeftiger als die uebrigen Flaechen - siehe
   tokens.css), eine deutliche Trennlinie darunter und halbfette Beschriftung. Der Kopf ist NICHT
   der Zebrastreifen der ersten Zeile - er soll auch beim Scrollen als eigene Leiste lesbar
   bleiben. */
.e-grid .e-gridheader {
    background-color: var(--grid-header-bg);
    border-bottom: 2px solid var(--border-strong);
}

    .e-grid .e-gridheader .e-headercell,
    .e-grid .e-gridheader .e-detailheadercell,
    .e-grid .e-gridheader .e-filterbarcell {
        background-color: var(--grid-header-bg);
    }

    .e-grid .e-gridheader .e-headercelldiv,
    .e-grid .e-gridheader .e-headertext {
        color: var(--text-primary);
        font-weight: 600;
    }

/* His request: give the toolbar (search / column chooser / export) breathing room from the frame,
   and vertically centre the toolbar buttons (Excel export etc.) within the bar. */
.e-grid .e-toolbar {
    padding: 0.4rem 1rem;
}

.e-grid .e-toolbar .e-toolbar-items,
.e-grid .e-toolbar .e-toolbar-item,
.e-grid .e-toolbar .e-toolbar-item .e-tbar-btn {
    display: inline-flex;
    align-items: center;
    height: auto;
}

/* The .e-toolbar-items container is absolutely positioned at right:-16px, so it overhangs the
   toolbar's right edge and overflow:hidden clips the Columns button against the frame (Markus,
   2026-08-20). Pull its right anchor inside the toolbar so the right-most item keeps a clear gap. */
.e-grid .e-toolbar .e-toolbar-items {
    right: 0.5rem !important;
}

/* Slight zebra striping so long lists are easier to scan (Markus, 2026-08-20). */
.e-grid .e-altrow .e-rowcell {
    background-color: var(--row-alt);
}

/* The Syncfusion toolbar icons render with an EMPTY glyph in this theme bundle (the e-icons font
   loads, but the ::before codepoint mappings for excel/csv/columnchooser are not in the flat
   bootstrap5.3.css) - the "Excel Export glyph error" Markus saw, and the empty glyph also made the
   button look off-centre. Draw them with FontAwesome (already loaded) instead. */
.e-grid .e-toolbar .e-excelexport::before,
.e-grid .e-toolbar .e-csvexport::before,
.e-grid .e-toolbar .e-columnchooser-btn::before {
    font-family: "Font Awesome 6 Free" !important;
    font-weight: 900 !important;
}

.e-grid .e-toolbar .e-excelexport::before { content: "\f1c3" !important; }        /* fa-file-excel */
.e-grid .e-toolbar .e-csvexport::before { content: "\f6dd" !important; }          /* fa-file-csv */
.e-grid .e-toolbar .e-columnchooser-btn::before { content: "\f0db" !important; }  /* fa-table-columns */

/* Keep the toolbar buttons' icon + label vertically centred (Markus, 2026-08-20). */
.e-grid .e-toolbar .e-tbar-btn {
    display: inline-flex;
    align-items: center;
}

/* A data cell is not clickable text (Markus, 2026-08-20): default arrow cursor, no text caret, no
   selection highlight. The action buttons in the first column keep their own pointer cursor. */
.e-grid .e-rowcell {
    cursor: default;
}

.e-grid .e-row .e-rowcell.e-active,
.e-grid .e-row.e-active .e-rowcell,
.e-grid td.e-selectionbackground {
    background-color: transparent;
}

/* Schnittstellen-Spalte darf umbrechen statt abzuschneiden (Markus, 2026-09-02). Grid-Zellen sind
   sonst white-space:nowrap; die Klasse kommt per CustomAttributes nur auf diese Spalte, alle
   anderen Spalten bleiben einzeilig. vertical-align:top, damit mehrzeilige Zellen sauber oben
   ausgerichtet in der Zeile stehen. */
.e-grid .e-rowcell.license-wrap-cell {
    white-space: normal;
    word-break: break-word;
    line-height: 1.4;
    vertical-align: top;
}

/* ... aber hoechstens drei Zeilen (Markus, 10.09.2026). Eine Lizenz mit vielen Schnittstellen
   riss die Zeile sonst auf 30 Zeilen auf; seit das Gitter eine feste Hoehe hat, blieb daneben
   nur eine grosse graue Flaeche stehen, die wie ein Fehler aussieht.
   -webkit-line-clamp mit -webkit-box ist die einzige Art, die in allen hier benutzten Browsern
   sowohl kuerzt ALS AUCH drei Punkte setzt; die Praefixe sind kein Altbestand, sondern die
   gueltige Schreibweise (auch Firefox versteht genau diese). Der ganze Text steht im
   title-Attribut der Zelle. */
.license-interfaces {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}

/* Die Infotexte des Pagers ("1 von 1 Seiten", "(11 Elemente)") bringt das Syncfusion-Thema mit
   einem festen rgba(33, 37, 41, 0.5) mit - also dunkler Tinte auf halber Deckkraft. Im
   Dunkelmodus ergibt das gemessene 1,08:1, der Text ist praktisch unsichtbar; das galt schon
   vor dem Wechsel auf "Mitternacht", weil #212529 dem alten Anthrazit #232730 fast gleicht.
   Aufgefallen ist es erst beim Nachmessen der neuen Palette (2026-09-03). */
.e-grid .e-pager .e-pagenomsg,
.e-grid .e-pager .e-pagecountmsg {
    color: var(--text-muted);
}

/* Die NICHT gewaehlten Seitenzahlen tragen Syncfusions eigenes --color-sf-primary (#0d6efd).
   Auf dem tiefen Grund sind das gemessene 4,07:1, vor der Umstellung waren es 3,38:1 - beides
   unter der Grenze. Sie gehoeren ohnehin auf denselben Akzent wie jeder andere Link im Portal
   (--accent, 7,4:1), zumal die gewaehlte Zahl unten schon auf --accent-solid liegt. */
.e-grid .e-pager .e-numericitem {
    color: var(--accent);
}

/* Current page number in the accent colour. */
.e-grid .e-pager .e-currentitem,
.e-grid .e-pager .e-currentitem:hover {
    background-color: var(--accent-solid) !important;
    color: var(--on-accent) !important;
}

/* Spec 9: rows for an inactive licence stay visible, only visually dampened. */
.license-row-inactive {
    color: var(--text-muted);
    font-style: italic;
}

.license-loan-expiring {
    color: var(--warn);
    font-weight: 600;
}

/* Lizenzschlüssel (LicenseKeyDisplay) - Mono, Blockformat erhalten. */
.license-key {
    font-family: var(--font-mono);
    margin-right: 0.5rem;
}

.license-key-missing {
    font-family: inherit;
    font-style: italic;
    color: var(--text-muted);
}

/* Brief "copied" state on the copy button itself (clipboard.js swaps the glyph to a check). */
.license-key-copy-done,
.license-key-copy-done:hover {
    color: var(--ok);
    border-color: var(--ok);
}

/* Floating "copied" confirmation (clipboard.js appends it to <body> so the grid's row overflow
   cannot clip it, unlike the old inline label). Fades in above the copy button. */
.copy-toast {
    position: fixed;
    z-index: 2000;
    padding: 0.4rem 0.7rem;
    background: var(--surface-2);
    color: var(--text-primary);
    border: 0.5px solid var(--border-strong);
    border-radius: var(--radius);
    font-size: 0.8rem;
    white-space: nowrap;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.15s ease, transform 0.15s ease;
    pointer-events: none;
}

.copy-toast-show {
    opacity: 1;
    transform: translateY(0);
}

/* Erfolgsmeldung nach dem Speichern (AppToast.razor, Markus 10.09.2026).
   Oben mittig statt unten rechts (Markus, gleicher Tag, nach dem ersten Sehen): nach dem
   Speichern schaut man auf die Liste, nicht in die untere rechte Ecke - eine Bestaetigung, die
   man nicht sieht, ist wertlos. Der Preis ist bekannt und angenommen: sie liegt 3,5 Sekunden
   lang ueber dem Seitentitel.
   translateX(-50%) statt eines Rahmens ueber die volle Breite: der Zettel soll nur so breit
   sein wie sein Text und trotzdem mittig sitzen.
   z-index ueber dem Dialogueberzug (2000), damit sie auch dann sichtbar ist, wenn ein Dialog
   noch schliesst - siehe .copy-toast, die dieselbe Ebene benutzt. */
.app-toast {
    position: fixed;
    top: 1rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2100;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.65rem 0.95rem;
    background: var(--ok-bg);
    color: var(--ok);
    border: 0.5px solid var(--ok);
    border-radius: var(--radius);
    font-size: 0.9rem;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
    animation: app-toast-ein 0.15s ease;
}

/* Die Verschiebung um die halbe eigene Breite muss in JEDEM Schritt der Bewegung drinstehen -
   sonst springt der Zettel beim Einblenden aus der Mitte nach rechts. */
@keyframes app-toast-ein {
    from { opacity: 0; transform: translate(-50%, -6px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}

/* Icon-only copy button next to a licence key (Phase 7). */
.license-key-copy {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 26px;
    padding: 0;
    border: 0.5px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface-1);
    color: var(--text-secondary);
    cursor: pointer;
    vertical-align: middle;
}

.license-key-copy:hover { background: var(--surface-2); color: var(--accent); }

/* Grid row action buttons (Ändern / Drucken) - icon-only. */
.grid-actions { display: inline-flex; gap: 4px; }

.grid-action-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 28px;
    padding: 0;
    border: 0.5px solid var(--border-strong);
    border-radius: var(--radius);
    background: var(--surface-1);
    color: var(--text-secondary);
    cursor: pointer;
}

.grid-action-btn:hover { background: var(--surface-2); color: var(--accent); }

/* New-licence wizard summary: a card that sticks and scrolls along with the form on desktop
   (Markus, 2026-08-20). On narrow screens the Bootstrap col stacks it below the form (col-lg-4),
   where sticky is harmless. Capped to the viewport so a long summary scrolls inside itself. */
.license-summary-panel {
    background: var(--surface-1);
    border: 0.5px solid var(--border);
    border-radius: var(--radius-card);
    padding: 1rem 1.25rem;
}

/* Kleben und eigener Bildlauf sind Mittel fuer die Spalte NEBEN dem Formular - deshalb erst ab
   der Breite, ab der es diese Spalte gibt (Bootstrap col-lg, 992px). Darunter bewirkte das Kleben
   nichts, und sobald die Zusammenfassung laenger wurde als max-height, entstand ein Scrollkasten
   IM Scrollkasten: ein 764px hohes Fenster mitten in einer 6459px langen Seite (Markus,
   14.09.2026 am Handy gemessen). */
@media (min-width: 992px) {
    .license-summary-panel {
        position: sticky;
        top: 1.5rem;
        max-height: calc(100vh - 3rem);
        overflow: auto;
    }
}

/* Unterhalb der zweispaltigen Ansicht faellt die Zusammenfassung ganz weg (Markus, 14.09.2026).

   Sie landete dort UNTER der Aktionsleiste - gemessen bei 375px: Knoepfe ab 5780px, Zusammenfassung
   ab 5959px auf einer 6459px langen Seite. Man scrollte also an den Knoepfen vorbei, um zu pruefen,
   und wieder zurueck, um zu speichern.

   Es geht dabei nichts verloren: vor dem Erstellen zeigt der Bestaetigungsdialog dieselben Angaben
   vollstaendig (ConfirmSummaryRows), und zwar an der Stelle, an der entschieden wird. Ausgeblendet
   wird die SPALTE, nicht nur das Panel - sonst bliebe der leere Spaltenrahmen samt Innenabstand
   stehen. */
@media (max-width: 991.98px) {
    .aidatouch-wizard > .col-lg-4,
    .aidax-wizard > .col-lg-4 {
        display: none;
    }
}

/* New-licence wizards (AidaX / AidaTouch): each step becomes a card that lifts off the page
   background, with clear spacing between the steps (Markus, 2026-08-20). The .mb-4 already on each
   fieldset supplies the gap; this gives them their surface, border and header rule. */
.aidax-wizard-step,
.aidatouch-wizard-step {
    background: var(--surface-1);
    /* A whole 1px border (not 0.5px): a half-pixel rendered asymmetrically left vs. right, which is
       the 1px/2px header overhang Markus saw. overflow:hidden clips the header band to the radius. */
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    padding: 0 1.25rem 1.25rem;
    overflow: hidden;
}

/* A real card header: a full-width band across the top of the card, a shade up from the card
   surface, with a soft hairline below it - then the content (Markus, 2026-08-20). A plain <div>
   (not a <legend>): the legend carries a fieldset-specific top-corner rendering quirk that left a
   1px notch even under overflow-clip; a div clips cleanly to the card radius. width:100%+2*padding
   with equal -padding side margins spans it symmetrically to both padding edges. */
.aidax-wizard-step > .wizard-step-header,
.aidatouch-wizard-step > .wizard-step-header {
    display: block;
    width: calc(100% + 2.5rem);
    margin: 0 -1.25rem 1.25rem;
    padding: 0.7rem 1.25rem;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    font-size: 1.05rem;
    font-weight: 600;
    color: var(--text-primary);
}

/* Interface grouping headings inside the AidaTouch Schnittstellen step (InterfacePicker) - the
   category legends were too quiet (Markus, 2026-08-20). Give them a clear sub-heading weight. */
.interface-picker-group {
    margin-top: 1.25rem;
}

.interface-picker-group > legend {
    float: none;
    display: block;
    width: 100%;
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--accent);
    margin-bottom: 0.6rem;
    padding-bottom: 0.35rem;
    border-bottom: 1px solid var(--border);
}

/* Segmented control (Versionshistorie product + Aktuell/Archiv switches): replaces the cramped
   Bootstrap btn-group with a pill toggle that matches the theme switch (Markus, 2026-08-20). */
.segmented {
    display: inline-flex;
    gap: 2px;
    padding: 2px;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-1);
}

.segmented-option {
    border: none;
    background: transparent;
    color: var(--text-secondary);
    padding: 0.4rem 0.9rem;
    border-radius: 6px;
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: background-color 0.12s ease, color 0.12s ease, filter 0.12s ease;
}

.segmented-option:hover { background: var(--surface-2); color: var(--text-primary); }
.segmented-option.active { background: var(--accent-solid); color: var(--on-accent); }
/* Druck-Feedback wie bei den primären Buttons: dunkler + leichter Innenschatten beim Klick. */
.segmented-option:active { filter: brightness(0.88); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35); }

/* Sub-filter variant (e.g. Zeitraum Aktuell/Archiv): smaller and slate-toned when active, so it
   reads as secondary to a primary (accent) segmented control above it. */
.segmented-sub .segmented-option { padding: 0.3rem 0.75rem; font-size: 0.82rem; }
.segmented-sub .segmented-option.active { background: var(--primary); color: var(--primary-fg); }

/* Versionshistorie: each version is a card with a prominent header (version + Download button) and
   roomier task lists (Markus, 2026-08-20). */
/* Quick-nav chips that jump to a version card (Markus, 2026-08-20). */
.version-quicknav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 1.25rem;
}

.version-quicknav-link {
    display: inline-flex;
    padding: 0.25rem 0.65rem;
    border: 0.5px solid var(--border);
    border-radius: 999px;
    background: var(--surface-1);
    color: var(--text-secondary);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    text-decoration: none;
    transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease, filter 0.12s ease;
}

.version-quicknav-link:hover {
    background: var(--surface-2);
    color: var(--accent);
    border-color: var(--accent);
}

.version-quicknav-link:active { filter: brightness(0.88); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35); }

/* Toggle-Pille, wenn mehr Versionen sichtbar sind als die Quick-Nav zeigt: blendet die älteren
   ein bzw. wieder aus. Bewusst in Akzentfarbe und mit gestricheltem Rahmen, damit sie sich als
   Aktion von den (durchgezogenen) Sprung-Pillen abhebt. */
.version-quicknav-toggle {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem 0.65rem;
    border: 0.5px dashed var(--accent);
    border-radius: 999px;
    background: transparent;
    color: var(--accent);
    font-size: 0.8rem;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    transition: background-color 0.12s ease, border-color 0.12s ease, filter 0.12s ease;
}

.version-quicknav-toggle:hover {
    background: var(--surface-2);
    border-style: solid;
}

.version-quicknav-toggle:active { filter: brightness(0.88); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35); }

/* Offset the jump target so the sticky top bar does not cover the version's header. */
.version-card {
    scroll-margin-top: 5rem;
}

.version-card-header {
    display: flex;
    /* Auf der SCHRIFTLINIE, nicht auf der Mitte (Markus, 16.09.2026: „sitzt nicht ganz bündig -
       zumindest das Datum"). Die Ueberschriftgruppe ist hoch (1,15rem), die Pruefangaben daneben
       sind klein (0,85rem); zentriert man die beiden Bloecke, liegen ihre Schriftlinien
       zwangslaeufig verschieden hoch - und genau das sieht man dem Datum an. Baseline legt sie
       auf eine Linie. */
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    /* Umbruch erlaubt (Markus, 15.09.2026): am Handy passen Nummer, Datum und Download-Knopf nicht
       nebeneinander. Ohne Umbruch bricht nur die Nummer-Datum-Gruppe, und der Knopf steht dann
       MITTEN zwischen Nummer und Datum ("Version 4.8.9721 [Download] vom 13.08.2026"). So rutscht
       stattdessen der Knopf als Ganzes unter die Ueberschrift. */
    flex-wrap: wrap;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}

/* Nummer und Datum als eine Gruppe links im Kopf; der Download-Knopf bleibt dadurch rechts.
   Umbruch erlaubt, damit das Datum auf schmalen Schirmen unter die Nummer rutscht statt sie
   zu quetschen (Markus, 15.09.2026). */
.version-heading {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.25rem 0.6rem;
    min-width: 0;
}

.version-title {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--text-primary);
}

/* Das Versionsdatum: leiser als die Nummer, damit die Nummer die Ueberschrift bleibt. */
.version-date {
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--text-secondary);
    white-space: nowrap;
}

.version-download {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    background: transparent;
    color: var(--accent);
    font-size: 0.85rem;
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color 0.12s ease, color 0.12s ease, filter 0.12s ease;
}

.version-download:hover { background: var(--accent-solid); color: var(--on-accent); }

.version-download:active { filter: brightness(0.88); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.35); }

/* Groesse und Pruefsumme unter dem Download-Knopf (Markus, 16.09.2026). Rechtsbuendig unter dem
   Knopf, damit der Kopf seine Form behaelt: er ist ein space-between-Flex, der Knopf sitzt rechts,
   und die Angaben haengen sich unter ihn statt daneben. Leise gesetzt - sie sind eine Kontrolle
   fuer den Zweifelsfall, nicht die Botschaft der Karte. */
/* `flex: 1 1 0` statt fester Maße (Markus, 16.09.2026: „warum lassen wir ihn nicht einfach
   automatisch umbrechen, wenn es sich nicht ausgeht"). Die Basis 0 laesst den Block nie von sich
   aus umbrechen - der Umbruch eines Flex-Behaelters entscheidet sich an der BASISBREITE, und eine
   Pruefsumme waere mit ihren 64 Zeichen ueber 500 px breit. Mit Basis 0 nimmt der Block danach,
   was neben der Versionsnummer frei ist: am breiten Schirm steht die Pruefsumme in EINER Zeile,
   am schmalen bricht sie von selbst um, und niemand rechnet eine Zahl aus.

   KEIN `min-width: 0` hier: ohne diese Zeile ist die kleinste Breite des Blocks seine
   Inhaltsbreite - also die des Download-Knopfes und der Groessenzeile, die beide nicht umbrechen.
   Genau das ist die richtige Untergrenze, und sie kommt aus dem Inhalt statt aus einer Annahme.
   Die Pruefsumme darf dank `overflow-wrap: anywhere` an jeder Stelle brechen und zaehlt deshalb
   nicht mit. */
/* Angaben links, Knopf rechts daneben - die Pruefangaben stehen VOR dem Knopf (Markus,
   16.09.2026), weil man sie prueft, bevor man klickt. Am Handy stapeln sie sich (Medienabfrage
   unten). */
.version-download-block {
    display: flex;
    flex-wrap: wrap;
    /* Ebenfalls Schriftlinie: der Block gibt seine eigene an den Kartenkopf weiter, und die soll
       die der Groessenangabe sein - nicht die Mitte eines Kastens, in dem auch ein Knopf steht. */
    align-items: baseline;
    /* Bündig rechts: passt alles in eine Zeile, steht der Knopf in der Ecke des Kartenkopfs;
       reicht der Platz nicht, rutscht er unter die Angaben - und zwar wieder an den rechten Rand
       statt irgendwo in die Mitte (bei 1200 px gemessen). */
    justify-content: flex-end;
    gap: 0.35rem 0.9rem;
    flex: 1 1 0;
}

/* Die Angaben nehmen den freien Platz und duerfen bis auf ihre Zeichenbreite schrumpfen
   (`min-width: 0`): dann bricht die Pruefsumme von selbst um, statt den Knopf aus der Zeile zu
   schieben. Der Knopf schrumpft NICHT - er ist die Handlung, und eine halb abgeschnittene
   Beschriftung waere das Letzte, woran gespart werden darf. */
.version-download-facts {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    justify-content: flex-start;
    gap: 0.2rem 0.6rem;
    font-size: 0.85rem;
    color: var(--text-muted);
    flex: 1 1 auto;
    min-width: 0;
}

.version-download-block > .version-download {
    flex: 0 0 auto;
}

/* Am Handy untereinander statt nebeneinander: 375 px reichen nicht fuer Angaben UND Knopf, und
   ein Knopf, der sich die Zeile mit einer dreizeiligen Pruefsumme teilt, sieht aus wie ein
   Versehen. Reihenfolge bleibt: erst die Angaben, dann der Knopf. */
@media (max-width: 991.98px) {
    .version-download-block {
        flex-direction: column;
        align-items: flex-start;
    }

    .version-download-block > .version-download {
        align-self: flex-start;
    }
}

.version-download-size { white-space: nowrap; }

/* Beschriftung und Pruefsumme bleiben beieinander, wenn die Zeile umbricht. */
.version-download-hashline {
    display: inline-flex;
    align-items: baseline;
    gap: 0.4rem;
    min-width: 0;
}

/* Das Verfahren ausgeschrieben neben der Zeichenkette: 64 Hexziffern sagen fuer sich genommen
   nicht, womit man sie vergleichen soll (Markus, 16.09.2026). */
.version-download-label {
    flex: 0 0 auto;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    color: var(--text-muted);
}

/* Die Pruefsumme sind 64 Zeichen - am Handy muss sie umbrechen duerfen, sonst zieht sie die
   Karte in die Breite. */
.version-download-hash {
    font-family: var(--font-mono);
    font-size: 0.85rem;
    color: var(--text-secondary);
    /* Darf an JEDER Stelle brechen - eine Pruefsumme hat keine Wortgrenzen. Damit zaehlt sie bei
       der kleinsten Breite des Blocks nicht mit und bestimmt nicht, wann der Kopf umbricht. */
    overflow-wrap: anywhere;
    min-width: 0;
}


/* Kopfdaten einer Version: Uebersetzungsstand und verbaute Fremdkomponenten (Markus,
   2026-09-04). Beides stand schon im XML und wurde nicht angezeigt.

   Als Zeilen mit Beschriftung LINKS und Plaketten rechts - nicht als Tabelle, weil die Zahl der
   Plaketten je Version schwankt (vier Sprachen bis sechs, vier Komponenten) und ein Raster dann
   entweder umbricht oder Luecken reisst. Bei schmalem Fenster rutscht die Beschriftung ueber die
   Werte (siehe Medienabfrage unten). */
.version-meta {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-bottom: 0.25rem;
}

.version-meta-row {
    display: flex;
    align-items: baseline;
    gap: var(--gap-md);
}

.version-meta-label {
    flex: 0 0 7.5rem;
    color: var(--text-muted);
    font-size: 0.9rem;
}

/* Etwas groesser als die Plaketten sonst im Haus (Markus, 16.09.2026): Uebersetzungsstand,
   Komponenten und Pruefsumme sind hier Lesestoff und keine Statusmarke am Rand einer Tabelle.
   Bewusst NUR in der Versionskarte hochgesetzt - .status-pill traegt sonst die Lizenzlisten. */
.version-meta .status-pill {
    font-size: 0.85rem;
    padding: 0.15rem 0.6rem;
}

.version-meta-values {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    min-width: 0;
}

/* Die Zahl gehoert zur Plakette. Hier stand zuerst opacity: 0.75, damit sie hinter dem
   Sprachnamen zuruecktritt - GEMESSEN war das ein Fehler: die Deckkraft mischt die Schriftfarbe
   in den Plakettengrund und drueckte den Kontrast im Lichtmodus auf 3,27:1 (gruen) bzw. 3,05:1
   (gelb), beide unter 4,5. Und die Zahl ist die eigentliche Aussage ("2198/2562"), also die
   letzte Stelle, an der man Kontrast sparen darf. Jetzt volle Farbe, Abstand statt Blaesse. */
.version-meta-count {
    margin-left: 0.15rem;
    font-variant-numeric: tabular-nums;
}

.version-meta-note {
    color: var(--text-secondary);
    font-size: 0.85rem;
}

@media (max-width: 640px) {
    .version-meta-row {
        flex-direction: column;
        gap: 0.2rem;
    }

    .version-meta-label {
        flex: none;
    }
}

/* Aktionsleiste am Ende eines Formulars (Markus, 14.09.2026). Vorher standen die Knoepfe nackt im
   Block: ohne Rand, ohne Abstand, nur durch das Leerzeichen zwischen zwei Inline-Elementen
   getrennt. Am Handy gemessen (375 px): "Lizenz speichern" rutschte in eine eigene Zeile und die
   beiden Zeilen beruehrten sich auf 0 px, waagrecht blieben 4,5 px.

   gap statt Raendern an den Knoepfen: der Abstand gehoert der Leiste, nicht den Kindern - so
   stimmt er auch, wenn ein Knopf wegfaellt oder einer dazukommt. */
.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    margin-top: 1rem;
}

@media (max-width: 640px) {
    /* Am Handy untereinander und ueber die volle Breite: nebeneinander bleiben von 375 px nach
       Abzug der Raender keine zwei brauchbaren Tippflaechen uebrig, und ein Knopf, der in die
       naechste Zeile faellt, sieht aus wie ein Fehler. */
    .form-actions {
        flex-direction: column;
        align-items: stretch;
    }

    .form-actions > .btn {
        width: 100%;
    }
}

.version-group-title {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    margin-top: 1.25rem;
    margin-bottom: 0.5rem;
    font-size: 0.9rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.version-group-features { color: var(--ok); }
.version-group-bugs { color: var(--warn); }

.version-task-list {
    margin-bottom: 0;
}

.version-task-list li {
    margin-bottom: 0.4rem;
}

/* Zweite Ebene der Versionsliste: im XML tragen 748 der 5088 <desc> eine Kopfzeile plus eine
   <ul> (Markus, 2026-09-03). Der Kopftext ist der Bullet, die Punkte darunter Unterbullets.

   Bewusst zurueckhaltend gehalten: kleinerer Punkt (circle statt disc), etwas gedaempfte Schrift
   und weniger Abstand zwischen den Punkten als auf der ersten Ebene. Die Einrueckung uebernimmt
   der Browser; der eigene margin-top trennt die Unterpunkte nur von ihrer Kopfzeile. */
.version-task-sublist {
    list-style-type: circle;
    margin: 0.25rem 0 0;
    color: var(--text-secondary);
}

.version-task-sublist li {
    margin-bottom: 0.15rem;
}

/* Required-field marker (Markus, 2026-08-20): a red asterisk after the label. aria-hidden on the
   span itself; the field's own validation carries the accessible requirement. */
.required-mark {
    color: var(--danger);
    margin-left: 0.15rem;
    font-weight: 700;
}

.form-required-hint {
    color: var(--text-muted);
    font-size: 0.8rem;
    margin-bottom: 1rem;
}

/* "Zur Lizenzliste" as a proper button, not a bare link (Markus, 2026-08-20). */
.license-back {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    text-decoration: none;
}

/* Aktionsleiste oben auf der Lizenz-Detailseite: Zurück links, Druckansicht rechts. Beide sind
   <a>-Elemente - Unterstreichung explizit aus, sonst gewinnt Bootstraps a:hover (Markus, 2026-09-01). */
.license-detail-actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

    .license-detail-actions a,
    .license-detail-actions a:hover {
        text-decoration: none;
    }

/* Hide the native number-input spinners until they are styled (Markus, 2026-08-20): the default
   up/down arrows clashed with the theme. Applies to Kundennummer fields (activity log filter,
   Partner/Benutzer visibility/recipient inputs) and any other type=number field. */
input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Syncfusion inputs: larger text, vertically centred on single-line fields, and a readable floating
   label (Markus, 2026-08-20: the Konto field labels and text were too small). */
.e-input-group:not(.e-multi-line-input) {
    display: flex;
    align-items: center;
}

.e-input-group input.e-control,
.e-input-group.e-control-wrapper input.e-control {
    font-size: 0.95rem;
}

/* Field labels were far too small (Markus, 2026-08-20). Syncfusion bakes the resting label at the
   input size and shrinks the floated-up label to 12px; both are lifted here. !important because the
   bundle's own rules match at equal-or-higher specificity. */
.e-float-input .e-float-text,
.e-float-input.e-control-wrapper .e-float-text {
    font-size: 0.95rem !important;
}

.e-float-input .e-float-text.e-label-top,
.e-float-input.e-control-wrapper .e-float-text.e-label-top {
    font-size: 0.85rem !important;
}

/* Phase 3, Task 7: LicenseDetail.razor. Phase 7 (Markus, 2026-08-20): each section is a card with a
   header band, so the groups (Stammdaten -> Umfang -> ...) read as clearly separated blocks. The
   <h2> is a normal block, so auto width + equal negative side margins full-bleed it cleanly. */
/* Abschnittskarte mit eigener Kopfleiste. Entstanden auf der Lizenz-Detailseite, seit dem
   16.09.2026 auch unter dem neutralen Namen `.page-section` - der Systemzustand brauchte dasselbe
   (Markus: „irgendwie heben sich die Bereiche dort wenig ab"), und eine Klasse namens
   `license-detail-*` auf einer Verwaltungsseite waere eine Luege. Eine Beschreibung, zwei Namen;
   der alte bleibt, damit die Lizenzseite unberuehrt bleibt. */
.page-section,
.license-detail-section {
    margin-bottom: 1.5rem;
    background: var(--surface-1);
    border: 1px solid var(--border);
    border-radius: var(--radius-card);
    padding: 0 1.25rem 1rem;
    overflow: hidden;
}

.page-section > h2,
.license-detail-section > h2 {
    margin: 0 -1.25rem 1rem;
    padding: 0.7rem 1.25rem;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
    font-size: 1.05rem;
    font-weight: 600;
}

.page-section > h2:first-child,
.license-detail-section > h2:first-child {
    margin-top: 0;
}

/* Das Glyph in der Kopfleiste: gedaempft, damit es die Ueberschrift begleitet und nicht mit ihr
   konkurriert. */
.page-section > h2 > .fa-solid {
    margin-right: 0.5rem;
    color: var(--text-muted);
}

/* Die Erlaeuterung direkt unter der Kopfleiste (z. B. „Gezaehlt werden Verbindungen, nicht
   Personen") sitzt sonst im luftleeren Raum zwischen Leiste und Tabelle. */
.page-section-note {
    margin: -0.25rem 0 0.9rem;
    color: var(--text-muted);
    font-size: 0.9rem;
}

/* Tabelle innerhalb einer Abschnittskarte: bis an die Kartenraender, damit sie nicht wie ein
   eingelegtes Blatt wirkt, und ohne unteren Rand an der letzten Zeile. */
.page-section > .table {
    margin-bottom: 0;
}

.page-section > .table > :not(caption) > * > * {
    padding-left: 0;
}

.page-section > .table > :not(caption) > * > *:last-child {
    padding-right: 0;
}

/* History chain (LicenseDetail): keep the "aktuelle/aufgerufene Fassung" badges from crowding the
   entry text (Markus, 2026-08-20 - they nearly touched it). */
.license-history-entry .badge {
    margin-left: 0.5rem;
    vertical-align: middle;
}

.license-detail-table th {
    width: 260px;
    white-space: nowrap;
}

.license-history-list {
    list-style: none;
    padding-left: 0;
}

.license-history-entry {
    padding: 0.75rem 1rem;
    margin-bottom: 0.5rem;
    border: 0.5px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface-1);
}

.license-history-entry-current {
    border-color: var(--accent);
    background: var(--accent-bg);
}

.license-history-masked {
    font-style: italic;
    color: var(--text-muted);
}

/* Ersteller einer Fassung in der Historie - gedämpft neben Nummer/Datum (Markus, 2026-09-01). */
.license-history-author {
    color: var(--text-muted);
}

/* "Mail erneut senden"-Bereich auf der Lizenz-Detailseite (Markus, 2026-09-02). */
.license-mail-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.75rem;
    flex-wrap: wrap;
}

.license-mail-result {
    color: var(--text-secondary);
    font-size: 0.9rem;
}

.license-history-missing-successor {
    margin-top: 0.5rem;
    margin-bottom: 0;
}

.license-diff-table {
    margin-top: 0.5rem;
}

/* Ampel des Systemzustands (/admin/system). Die Farbe traegt die Aussage, das Symbol traegt
   sie noch einmal - Rot und Gruen sind fuer einen Teil der Leute nicht unterscheidbar
   (dieselbe Regel wie bei StatusPill). */
.health-lamp {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.75rem 1rem;
    border-radius: var(--radius);
    border: 0.5px solid var(--border);
}

.health-green { background: var(--ok-bg); color: var(--ok); border-color: var(--ok); }
.health-yellow { background: var(--warn-bg); color: var(--warn); border-color: var(--warn); }
.health-red { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }

/* Knopf und Zeitstempel in einer Zeile, mit Abstand dazwischen. */
.health-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

/* Protokollausgabe: Festbreitenschrift, in sich scrollbar, damit eine lange Datei nicht die
   Seite sprengt. */
.log-view {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    line-height: 1.35;
    max-height: 24rem;
    overflow: auto;
    padding: 0.6rem 0.8rem;
    background: var(--surface-2);
    border-radius: var(--radius);
    white-space: pre-wrap;
    word-break: break-word;
}

.log-error {
    padding: 0.6rem 0;
    border-top: 1px solid var(--border);
}

/* Kopfzeile eines Fehlers: Zeitpunkt und Quelle links, Zeilenzahl und Knopf rechts. Der Knopf
   steht am rechten Rand wie in der Protokolltabelle darueber - eine Bedienung, eine Stelle. */
.log-error-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}

.log-error-actions {
    display: inline-flex;
    align-items: baseline;
    gap: 0.6rem;
    flex-shrink: 0;
    font-size: 0.85rem;
}

/* Der erste Fehler in seinem Abschnitt braucht keine Trennlinie nach oben - die Kopfleiste der
   Karte hat schon eine. */
.page-section > .log-error:first-of-type {
    border-top: none;
    padding-top: 0;
}

/* Zahlenspalten rechtsbuendig: so stehen Ziffern und Plaketten untereinander statt gestaffelt,
   und der Blick faellt die Spalte hinunter statt sie abzusuchen. */
.health-table .health-num {
    text-align: right;
}

/* Senkrecht MITTIG statt auf der Schriftlinie (Markus, 16.09.2026: „die vertikale Ausrichtung
   stimmt auch hier nicht ganz"). In einer Zeile stehen nebeneinander: schlichter Text (Datum),
   Plaketten mit Polsterung und ein Knopf. Auf der Schriftlinie ausgerichtet - die Vorgabe fuer
   Tabellenzellen - sitzen die drei unterschiedlich hoch, weil ihre Polsterung unterschiedlich
   tief unter die Linie reicht. Bei gemischten Kaestchen ist die Mitte die ruhigere Wahl. */
.health-table > :not(caption) > * > * {
    vertical-align: middle;
}

/* Die Zahl in den Plaketten der Protokolltabelle mittig und gleich breit, damit "0" und "533"
   gleich viel Platz einnehmen und die Spalte nicht zappelt. */
.health-table .status-pill {
    display: inline-flex;
    justify-content: center;
    min-width: 3.2rem;
    font-variant-numeric: tabular-nums;
}

/* Die Rolle ist eine Beschriftung, keine Zahl - die Mindestbreite von oben passt ihr nicht. */
.health-table td:nth-child(2) .status-pill {
    min-width: 0;
}

.health-session-user {
    font-weight: 600;
    color: var(--text-primary);
}

/* „Sie" neben dem eigenen Namen: leise, aber auffindbar. Kein Farbklecks - die Markierung soll
   die Zeile nicht wichtiger aussehen lassen, als sie ist. */
.health-session-self {
    margin-left: 0.4rem;
    padding: 0.05rem 0.4rem;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    color: var(--text-muted);
    font-size: 0.7rem;
    font-weight: 500;
}

/* Der aufgeklappte Protokolltag: die Zeile mit dem Knopf und der lange Textblock darunter gehoeren
   zusammen, sonst schwebt der Block ohne erkennbare Zugehoerigkeit in der Tabelle.

   Der Akzentton, NICHT surface-2: die neutralen Zahlenplaketten haben selbst surface-2 als Grund
   und verschwanden in der hervorgehobenen Zeile spurlos (in der Vorschau gesehen). */
.health-table tr.health-day-open > td {
    background: var(--accent-bg);
}
