/* ==========================================================================
   Zertifikat-Portal (Hermes)
   --------------------------------------------------------------------------
   Übernimmt die Gestaltung der Website (PKMT/pkmt-theme) und legt sie über
   Radzen. Dieselben Grundsätze, damit ein Kunde, der von my.pkmt.de kommt,
   nicht das Gefühl hat, das Haus gewechselt zu haben:

   · Gemischte Schreibweise durchgehend — keine Versalien, keine Sperrung
   · Große, eng laufende Überschriften; ruhiger Fließtext in 17 px
   · Weiche Radien, Pillen-Buttons, Flächen statt Rahmenlinien
   · Viel Weißraum, wenige Trennlinien

   Radzen bleibt der Baukasten; hier werden nur seine Token umgebogen und die
   eigenen Bauteile (.portal-*) gestaltet.

   Nur helle Erscheinung — eine Umschaltung gab es einmal und ist entfallen
   (siehe App.razor). Die Website tritt hell auf, und für ein Portal, das man
   ein paar Mal im Jahr für einen Download öffnet, war der Schalter ein
   Bedienelement ohne Anlass. Wer sie zurückholt, braucht wieder beides: die
   Farbtoken für Dunkel *und* einen Weg, wie das Stylesheet und diese Token
   gemeinsam umschalten — Radzen tauscht nur sein eigenes.
   ========================================================================== */

/* --------------------------------------------------------------- 1. Tokens */

:root {
    --brand: #033E8C;
    --brand-lift: #0A5FCC;
    --brand-soft: #E8EFF9;

    --bg: #FFFFFF;
    --bg-alt: #F2F2F2;
    --bg-card: #FFFFFF;
    --bg-tint: rgba(0, 0, 0, .045);

    /* Die milchige Scheibe von Kopf- und Fußzeile. Als Token, weil beide Leisten sie brauchen und
       zusätzlich ihre Verlängerungen über den Fensterrand hinaus — stünde der Wert viermal da,
       liefen sie beim nächsten Anfassen auseinander, und genau die Kante zwischen Leiste und
       Verlängerung verrät den Balken. */
    --bar: color-mix(in srgb, #FFFFFF 78%, transparent);

    --ink: #1A1A1C;
    --ink-2: #6B6B73;
    --ink-3: #8E8E96;

    --link: var(--brand-lift);
    --hairline: rgba(0, 0, 0, .10);

    --shadow-card: 0 2px 8px rgba(0, 0, 0, .04), 0 12px 32px rgba(0, 0, 0, .06);
    --shadow-lift: 0 4px 12px rgba(0, 0, 0, .06), 0 24px 56px rgba(0, 0, 0, .11);

    --ff: -apple-system, BlinkMacSystemFont, "SF Pro Text", "SF Pro Display",
          "Segoe UI Variable Text", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

    /* Eine Leiter mit gleichmäßigen Schritten (rund 1,2×). Vorher sprang der Seitentitel mit 48 px
       auf Kartenüberschriften von 23 px — dazwischen fehlte alles. */
    --fs-h1: clamp(1.75rem, 3.4vw, 2.5rem);
    --fs-h2: clamp(1.375rem, 2.2vw, 1.75rem);
    --fs-h3: clamp(1.125rem, 1.6vw, 1.3125rem);
    --fs-lead: clamp(1.0625rem, 1.4vw, 1.1875rem);
    --fs-body: 1.0625rem;
    --fs-sm: 0.9375rem;
    --fs-cap: 0.8125rem;

    --r-sm: 10px;
    --r-card: 18px;
    --r-lg: 26px;
    --r-pill: 980px;

    --pad: clamp(1rem, 4vw, 2rem);
    --ease: cubic-bezier(.32, .72, 0, 1);

    /* Statusfarben — hergeleitet, damit sie in beiden Modi lesbar bleiben. */
    --ok: #1B8A4B;
    --warn: #9A6400;
    --danger: #C0341D;
}

/* Radzen auf dieselben Werte ziehen. Das ist der Hebel, mit dem alle
   Radzen-Bauteile (Grid, Dialog, Slider, Knöpfe) die Hausfarbwelt bekommen,
   ohne sie einzeln anzufassen. */
:root {
    --rz-text-font-family: var(--ff);
    --rz-body-font-size: var(--fs-body);
    --rz-body-line-height: 1.53;
    --rz-body-background-color: var(--bg);
    --rz-base-background-color: var(--bg);
    --rz-card-background-color: var(--bg-card);
    --rz-text-color: var(--ink);
    --rz-text-secondary-color: var(--ink-2);
    --rz-text-tertiary-color: var(--ink-3);
    --rz-border-color: var(--hairline);
    --rz-primary: var(--brand);
    --rz-primary-light: var(--brand-lift);
    --rz-primary-dark: var(--brand);
    --rz-link-color: var(--link);
    --rz-link-hover-color: var(--link);
    --rz-success: var(--ok);
    --rz-warning: var(--warn);
    --rz-danger: var(--danger);
    --rz-input-background-color: var(--bg-tint);
    --rz-border-radius: var(--r-sm);

    /* Radzens Textstufen auf dieselbe Leiter. Ohne das rendert jedes <RadzenText
       TextStyle="Body2"> in Radzens Maß (0.875rem) statt in unserem (--fs-sm) — die Seite hätte
       zwei Größensysteme nebeneinander, und genau das sieht man. Dazu die Laufweite: Radzen sperrt
       positiv (0,25–1,5 px), die Hausschrift läuft eng, also negativ. */
    --rz-text-h4-font-size: var(--fs-h1);
    --rz-text-h4-font-weight: 600;
    --rz-text-h4-letter-spacing: -0.028em;
    --rz-text-h4-line-height: 1.1;

    --rz-text-h5-font-size: var(--fs-h2);
    --rz-text-h5-font-weight: 600;
    --rz-text-h5-letter-spacing: -0.026em;
    --rz-text-h5-line-height: 1.15;

    --rz-text-h6-font-size: var(--fs-h3);
    --rz-text-h6-font-weight: 600;
    --rz-text-h6-letter-spacing: -0.022em;
    --rz-text-h6-line-height: 1.2;

    --rz-text-body1-font-size: var(--fs-body);
    --rz-text-body1-letter-spacing: -0.011em;
    --rz-text-body1-line-height: 1.53;

    --rz-text-body2-font-size: var(--fs-sm);
    --rz-text-body2-letter-spacing: -0.008em;
    --rz-text-body2-line-height: 1.5;

    --rz-text-caption-font-size: var(--fs-cap);
    --rz-text-caption-letter-spacing: -0.004em;
    --rz-text-caption-line-height: 1.45;

    /* Overline steht bei Radzen auf 0,625 rem in Versalien mit 1,5 px Sperrung — beides
       widerspricht der Hausschrift. Benutzt wird die Stufe für den Kicker über dem Titel. */
    --rz-text-overline-font-size: var(--fs-sm);
    --rz-text-overline-font-weight: 600;
    --rz-text-overline-letter-spacing: -0.008em;
}

.rz-text-overline { text-transform: none; }

/* ---------------------------------------------------------------- 2. Basis */

*, *::before, *::after { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

/* Der Grundton liegt auf <html>, nicht auf <body>: das Hintergrundfoto hängt an `body::before`
   mit `z-index: -1`, und ein solches Element liegt zwar über der Zeichenfläche, aber unter dem
   Hintergrund seines eigenen Elternblocks. Ein deckendes Weiß am Körper würde es verdecken. */
html {
    -webkit-text-size-adjust: 100%;
    background-color: var(--bg);
    /* Kein Gummiband. Beim Überscrollen rutschen Kopf- und Fußzeile vom Fensterrand weg, während
       das Hintergrundfoto fest im Fenster steht — dahinter kommt das Bild zum Vorschein und beide
       sehen aus wie Balken, die davor schweben. Die Leisten werden dagegen zwar über den Rand
       hinaus verlängert (siehe `.portal-topbar::before` und der Schatten an `.portal-foot`), aber
       das trägt nur, wo der Browser das Überstehende auch malt: die Kopfzeile liegt dank
       `position: sticky` ohnehin auf einer eigenen Compositor-Ebene, die Fußzeile nicht, und was
       über die Dokumentgrenze hinausragt, fällt beim Rastern weg. In Chrome ließ sich das noch mit
       `will-change: transform` erzwingen, in WebKit auf dem iPad nicht.

       Ohne Gummiband öffnet sich die Lücke gar nicht erst. Der Preis ist das Nachfedern, das macOS
       und iPadOS sonst zeigen; das ist es hier wert. Die Verlängerungen bleiben trotzdem stehen —
       sie kosten nichts und tragen in Browsern, die diese Zeile nicht kennen (Safari vor 16). */
    overscroll-behavior-y: none;
}

body {
    font-family: var(--ff);
    color: var(--ink);
    font-size: var(--fs-body);
    line-height: 1.53;
    letter-spacing: -0.011em;
    /* Durchsichtig, damit das Foto durchkommt — siehe die Notiz an <html> oben. Muss hier stehen
       bleiben: das Radzen-Theme setzt `body { background-color: … }` und app.css wird danach
       geladen, überschreibt es also. */
    background-color: transparent;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Notnagel gegen seitliches Wegziehen auf dem Handy — wie auf der Website. */
    overflow-x: hidden;
    overflow-x: clip;
}

.rz-body { --rz-body-padding: 0; }

/* Überschriften: groß, eng, ausgewogen umbrechend. Deutsche Komposita
   („Vor-Ort-Kalibrierung") sind auf dem Handy breiter als die Spalte. */
h1, h2, h3, h4 {
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -0.028em;
}

/* Gilt für eigene wie für Radzen-Überschriften: deutsche Komposita
   („Vor-Ort-Kalibrierung") sind auf dem Handy breiter als die Spalte. */
h1, h2, h3, h4,
.portal-main .rz-text-h4,
.portal-main .rz-text-h5,
.portal-main .rz-text-h6 {
    text-wrap: balance;
    hyphens: auto;
    overflow-wrap: break-word;
}

a { color: var(--link); text-decoration: none; }
a:hover, a:focus { color: var(--link); }

:focus-visible {
    outline: 3px solid var(--link);
    outline-offset: 3px;
    border-radius: 6px;
}

/* FocusOnNavigate (Routes.razor) fokussiert die <h1> beim Laden. */
h1:focus { outline: none; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        transition-duration: .001ms !important;
    }
}

/* -------------------------------------------------------- 3. Radzen-Bauteile */

/* Pillen-Knöpfe wie auf der Website, mit dem kleinen Druckpunkt beim Klicken. */
.rz-button {
    border-radius: var(--r-pill);
    font-family: var(--ff);
    font-weight: 500;
    letter-spacing: -0.012em;
    text-transform: none;
    box-shadow: none;
    transition: background-color .25s var(--ease), color .25s var(--ease),
                transform .25s var(--ease), opacity .25s var(--ease);
}

.rz-button:active:not(:disabled) { transform: scale(.97); }

.rz-button.rz-primary { background: var(--brand); color: #fff; }
.rz-button.rz-primary:not(:disabled):hover { background: var(--brand-lift); }

.rz-button.rz-light,
.rz-button.rz-secondary {
    background: var(--bg-tint);
    color: var(--ink);
}

.rz-button.rz-light:not(:disabled):hover,
.rz-button.rz-secondary:not(:disabled):hover {
    background: color-mix(in srgb, var(--ink) 14%, transparent);
}

/* Karten tragen Fläche und Schatten statt einer Rahmenlinie. */
.rz-card {
    background: var(--bg-card);
    border: 0;
    border-radius: var(--r-card);
    box-shadow: var(--shadow-card);
    padding: clamp(1.25rem, 2.2vw, 1.75rem);
}

/* Eingabefelder: getönte Fläche, Rahmen erst beim Hineinklicken. */
.rz-textbox,
.rz-textarea,
.portal-main input[type="text"],
.portal-main input[type="email"] {
    background: var(--bg-tint);
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    color: var(--ink);
    font-family: var(--ff);
    font-size: var(--fs-body);
    padding: .875rem 1rem;
    transition: border-color .2s var(--ease), background-color .2s var(--ease);
}

.rz-textbox:focus,
.rz-textarea:focus {
    outline: none;
    border-color: var(--link);
    background: color-mix(in srgb, var(--ink) 9%, transparent);
    box-shadow: none;
}

/* Ein Feld in einem RadzenFormField zeigte den Fokus überhaupt nicht — zwei Ursachen, die sich
   gegenseitig verdeckt haben. Erstens nimmt das Theme der Eingabe darin ihren Rahmen
   (.rz-form-field-content input:…:focus { border: none }, Spezifität 0,2,1), gegen die die Regel
   oben (0,1,1) nicht ankommt. Zweitens zeichnet Radzen den Fokus des Feldes über die Klasse
   .rz-state-focused — und die setzt es mit einem **inline** onfocusin/onfocusout im Markup von
   RadzenFormField, was unsere CSP verbietet (siehe CLAUDE.md, „Härtung der öffentlichen Seite").
   Der Ausfall war stumm: kein Fehler, nur ein Formular ohne erkennbaren Fokus, und das trifft die
   Reklamation und die Empfängerpflege.

   :focus-within kommt ohne Skript aus. Der Ring sitzt auf .rz-form-field-content, der sichtbaren
   Fläche — .rz-form-field selbst ist ein unsichtbarer Kasten ohne Rundung, ein Ring darum stünde
   als zweiter Rahmen daneben. box-shadow statt outline, damit er der Rundung des Feldes folgt. */
.rz-form-field:focus-within .rz-form-field-content,
.rz-form-field.rz-state-focused .rz-form-field-content {
    box-shadow: 0 0 0 2px var(--link);
}

.rz-textbox::placeholder,
.rz-textarea::placeholder { color: var(--ink-3); }

/* Datentabellen: keine Gitternetzlinien, nur eine Haarlinie je Zeile. */
.portal-main .rz-datatable,
.portal-main .rz-data-grid {
    background: transparent;
    border: 0;
}

.portal-main .rz-datatable { overflow-x: auto; }

.portal-main .rz-grid-table th,
.portal-main .rz-grid-table td {
    border: 0;
    border-bottom: 1px solid var(--hairline);
    background: transparent;
}

.portal-main .rz-grid-table th {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink-2);
    letter-spacing: -0.008em;
}

/* Spaltenbreite ziehen: die Köpfe haben keine Trennlinien, der Griff wäre also nicht zu finden.
   Zeigt man auf die Kopfzeile, erscheint an jeder Kante eine Haarlinie, unter dem Zeiger in der
   Linkfarbe. */
.portal-main .rz-grid-table thead:hover .rz-column-resizer::after {
    width: 2px;
    background-color: var(--hairline);
}

.portal-main .rz-grid-table .rz-column-resizer:hover::after,
.portal-main .rz-grid-table .rz-column-resizer:active::after {
    background-color: var(--link);
}

.portal-main .rz-grid-table tbody tr:last-child td { border-bottom: 0; }
.portal-main .rz-grid-table tbody tr:hover td { background: var(--bg-tint); }

/* ---- Reconnect-Overlay ---------------------------------------------------- */
.reconnect-modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 100000;
}

.reconnect-modal.components-reconnect-show,
.reconnect-modal.components-reconnect-failed,
.reconnect-modal.components-reconnect-rejected {
    display: flex;
    align-items: center;
    justify-content: center;
}

.reconnect-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(18px) saturate(160%);
    -webkit-backdrop-filter: blur(18px) saturate(160%);
}

.reconnect-dialog {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.25rem;
    padding: 2.5rem 3rem;
    border-radius: var(--r-lg);
    background: var(--bg-card);
    box-shadow: var(--shadow-lift);
}

.reconnect-spinner {
    width: 40px;
    height: 40px;
    border: 3px solid var(--bg-tint);
    border-top-color: var(--brand);
    border-radius: 50%;
    animation: reconnect-spin 0.8s linear infinite;
}

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

.reconnect-text {
    margin: 0;
    font-size: var(--fs-sm);
    color: var(--ink-2);
}

/* --------------------------------------------------------------- 4. Gerüst */

/* Kopf und Fuß sind eigene Bänder über die volle Breite, der Inhalt dazwischen läuft in einer
   Spalte — dieselbe Aufteilung wie auf der Website (.nav / .wrap / .foot). Der Fuß bleibt auf
   kurzen Seiten unten, weil der Körper eine Spalte ist und der Inhalt den Rest nimmt. */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* Hinter der Seite liegt das Labor statt einer weißen Fläche: die Endmaße aus dem Bildbestand der
   Website (pkmt-theme/assets/img/labor/mg-endmasse.jpg, hier als WebP). Darüber ein Schleier aus
   Weiß, der ihm fast allen Kontrast nimmt — die Karten sind deckend, der Text muss sich also
   nirgends gegen das Bild behaupten; zu sehen ist es in den Rändern und unter dem Inhalt.

   Ein festes Pseudo-Element statt `background-attachment: fixed`: iOS Safari bezieht das Bild
   dabei auf das ganze Dokument statt auf das Fenster und skaliert es auf langen Seiten ins
   Absurde. Ein `position: fixed`-Element ist ohnehin fensterhoch — die Frage stellt sich nicht.

   Die Verlaufsschicht liegt oben dichter: dort sitzt die Kopfzeile, und ihre milchige Scheibe
   allein trägt einen ruhigen Sitz nicht. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background-image:
        linear-gradient(180deg,
            rgba(255, 255, 255, .95) 0,
            rgba(255, 255, 255, .82) 220px,
            rgba(255, 255, 255, .82) 100%),
        url("../img/hintergrund-endmasse.webp");
    background-size: auto, cover;
    background-position: center, center;
    background-repeat: no-repeat, no-repeat;
}

.portal-wrap {
    width: 100%;
    max-width: 1040px;
    margin-inline: auto;
    padding-inline: var(--pad);
}

.portal-content {
    flex: 1;
    padding-bottom: clamp(3rem, 6vw, 4.5rem);
}

/* ---- Kopfzeile ----------------------------------------------------------- */

.portal-topbar {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--bar);
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    border-bottom: 1px solid var(--hairline);
    margin-bottom: clamp(1.75rem, 4vw, 2.75rem);
}

/* Beim Gummiband-Scrollen (Touchpad auf dem Mac, Wischen auf dem Handy) rutscht die Leiste vom
   oberen Fensterrand weg, während das Hintergrundfoto fest im Fenster steht: darüber käme das
   Bild zum Vorschein, und die Kopfzeile sähe aus wie ein Balken, der davor schwebt. Deshalb
   wird sie nach oben über die Fensterhöhe hinaus verlängert — das Stück gehört zur Leiste und
   wandert mit ihr, oben bleibt Weiß.

   Ein echtes Kästchen und kein `box-shadow`: ein Schatten ohne Streuung ist bloß eine versetzte
   Kopie der Leiste, zwischen ihr und dem Fensterrand bliebe alles offen. Nach oben wächst dabei
   kein Scrollbereich — der entsteht nur nach unten und nach rechts; am Fuß geht es deshalb nicht
   genauso, siehe dort. */
.portal-topbar::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 100%;
    height: 100vh;
    /* `inherit` und nicht `var(--bg)`: reines Weiß setzt sich als Kante von der milchigen Leiste
       ab, und genau die Kante verrät wieder den Balken. So ist es dieselbe Fläche — dass das Foto
       dahinter durchscheint, fällt nicht auf, weil der Schleier oben am dichtesten ist (95 %) und
       davon noch einmal nur ein Fünftel durchkommt. */
    background: inherit;
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
}

.portal-topbar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    height: 82px;
}

/* Wortmarke plus eine ruhige Ortsangabe. Der Firmenname steht in der Marke selbst und wird
   daneben nicht wiederholt. */
.portal-brand {
    display: flex;
    align-items: center;
    gap: .75rem;
    min-width: 0;
    color: inherit;
}

.portal-brand:hover, .portal-brand:focus { color: inherit; }

/* Die Wortmarke ist ein Inline-SVG (Logo.razor) und folgt der Schriftfarbe. Ihre Maße kommen
   als Style-Parameter aus MainLayout — die Komponente bringt eine eigene #Ebene_2-Regel mit,
   gegen die eine Klassenregel hier nicht ankäme. */
.portal-brand svg { flex: 0 0 auto; }

.portal-brand-label {
    font-size: var(--fs-sm);
    color: var(--ink-3);
    white-space: nowrap;
    /* Steht auf einer Grundlinie mit der Wortmarke, durch eine feine Linie getrennt statt durch
       eine zweite Zeile. */
    padding-left: .75rem;
    border-left: 1px solid var(--hairline);
}

.portal-main { min-width: 0; }

/* ---- Fußzeile ------------------------------------------------------------ */

/* Eine Zeile, mehr nicht: Betreiber, Anschrift, Impressum, Datenschutz. Dieselbe milchige Scheibe
   wie die Kopfzeile, damit die Seite oben und unten gleich abschließt — aber **ohne deren
   Weichzeichner**: unter dem Fuß scrollt nichts durch, er hätte also nichts zu tun außer das
   Hintergrundfoto weichzuzeichnen. Genau daran ist die Verlängerung unten sichtbar geworden — der
   Fuß zeigte das Bild verwaschen, der Schatten darunter scharf, und die Kante dazwischen war
   wieder der Balken. Ein Schatten kann nicht weichzeichnen; also tut es der Fuß auch nicht.

   Verlängert wird sie nach unten als Schatten und nicht wie oben als Kästchen: ein Element unter
   dem Fuß lässt den Scrollbereich um seine Höhe wachsen — nachgemessen, die Seite war danach
   doppelt so hoch wie ihr Inhalt und man scrollte in eine leere Fläche hinein. Ein Schatten tut
   das nie. Nach oben stellt sich die Frage nicht: dorthin wächst kein Scrollbereich.

   Versatz und Streuung sind gleich groß, damit das Rechteck an der Oberkante des Fußes beginnt und
   von dort nach unten durchläuft. Mit Versatz allein (`0 100vh 0 var(--bar)`) wäre es nur eine
   versetzte Kopie der Leiste, und die Lücke dazwischen bliebe offen — genau daran ist der erste
   Anlauf gescheitert. Zur Seite läuft es mit hinaus; das fängt `overflow-x` am Körper ab. */
.portal-foot {
    border-top: 1px solid var(--hairline);
    padding-block: 1.5rem;
    background: var(--bar);
    box-shadow: 0 100vh 0 100vh var(--bar);
}

.portal-foot-bar {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 2rem;
    justify-content: space-between;
    align-items: baseline;
    font-size: var(--fs-cap);
    color: var(--ink-3);
}

.portal-foot-links {
    display: flex;
    gap: 1.25rem;
}

.portal-foot-links a {
    color: var(--ink-3);
    transition: color .2s var(--ease);
}

.portal-foot-links a:hover { color: var(--ink); }

/* ---- Navigation ---------------------------------------------------------- */

.portal-nav {
    display: flex;
    align-items: center;
    gap: .25rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* Der Sprachumschalter: drei kurze Kürzel, ruhig gesetzt. Kein Auswahlfeld — es sind drei
   Möglichkeiten, und ein Link kann das Cookie setzen, ohne dass ein Circuit laufen muss. */
.portal-lang {
    display: inline-flex;
    gap: .15rem;
    margin-right: .35rem;
    padding: .15rem;
    border-radius: var(--r-pill);
    background: var(--bg-tint);
}

.portal-lang-option {
    padding: .2rem .5rem;
    border-radius: var(--r-pill);
    font-size: var(--fs-cap);
    font-weight: 500;
    color: var(--ink-3);
    text-decoration: none;
    transition: background-color .2s var(--ease), color .2s var(--ease);
}

.portal-lang-option:hover,
.portal-lang-option:focus-visible { color: var(--ink); }

.portal-lang-option--active {
    background: var(--bg-card);
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
}

.portal-nav-link {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem .9rem;
    border-radius: var(--r-pill);
    font-size: var(--fs-sm);
    font-weight: 500;
    letter-spacing: -0.012em;
    color: var(--ink-2);
    white-space: nowrap;
    transition: background-color .2s var(--ease), color .2s var(--ease);
}

.portal-nav-link:hover,
.portal-nav-link:focus-visible {
    background: var(--bg-tint);
    color: var(--ink);
}

.portal-nav-link > i { font-size: 1.15rem; }

.portal-nav-link--quiet { color: var(--ink-3); }

/* Wer angemeldet ist — eine ruhige Zeile über dem Inhalt. Sie steht ohne Karte darunter direkt
   auf dem Hintergrundfoto; deshalb --ink-2 statt des sonst üblichen --ink-3. */
.portal-whoami {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin-bottom: 1.5rem;
    font-size: var(--fs-sm);
    color: var(--ink-2);
}

.portal-whoami > i { font-size: 1rem; }

.portal-whoami-mail::before {
    content: "·";
    margin-right: .5rem;
}

/* --------------------------------------------------------- 5. Hinweiszeilen */

.portal-note {
    padding: .875rem 1rem;
    border-radius: var(--r-sm);
    font-size: var(--fs-sm);
    line-height: 1.5;
    overflow-wrap: anywhere;
}

.portal-note--info {
    background: var(--brand-soft);
    color: var(--ink);
}

.portal-note--ok {
    background: color-mix(in srgb, var(--ok) 12%, transparent);
    color: color-mix(in srgb, var(--ok) 70%, var(--ink));
}

.portal-note--error {
    background: color-mix(in srgb, var(--danger) 10%, transparent);
    color: color-mix(in srgb, var(--danger) 72%, var(--ink));
}

/* ------------------------------------------------------------ 6. Anmeldung */

.portal-login {
    max-width: 28rem;
    margin: clamp(1rem, 5vw, 3rem) auto 0;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.portal-login-card {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.portal-login-head {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    min-width: 0;
}

.portal-login-hint {
    color: var(--ink-2);
    font-size: var(--fs-sm);
    line-height: 1.5;
}

/* Steht unter der Karte frei auf dem Hintergrundfoto — --ink-2 wie bei .portal-whoami. */
.portal-login-foot {
    text-align: center;
    color: var(--ink-2);
    font-size: var(--fs-cap);
    padding: 0 1rem;
    line-height: 1.5;
}

/* ------------------------------------------------------ 6a. Suche (Geräte) */

/* Das Suchfeld: die Lupe sitzt im Feld, nicht daneben. Radzen bringt dafür nichts mit, also ein
   Wrapper mit Innenabstand am Eingabefeld — deshalb die !important-freie, aber hohe Spezifität
   über den Nachfahrenselektor. */
.portal-search {
    position: relative;
    display: flex;
    align-items: center;
}

.portal-search .rz-textbox {
    padding-left: 2.75rem;
}

.portal-search-icon {
    position: absolute;
    left: .9rem;
    color: var(--ink-3);
    font-size: 1.25rem;
    pointer-events: none;
}

.portal-search-busy {
    position: absolute;
    right: .9rem;
}

/* Im Auftrag steht das Feld über der Tabelle und braucht Luft nach unten. */
.portal-search--inline {
    margin-bottom: 1rem;
}

/* ---- Trefferkarte eines Geräts ------------------------------------------- */

.portal-instrument {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.portal-instrument-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.portal-instrument-title { min-width: 0; }

.portal-instrument-count {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .3rem .7rem;
    border-radius: 999px;
    background: var(--bg-tint);
    font-size: var(--fs-cap);
    color: var(--ink-2);
    white-space: nowrap;
}

.portal-instrument-count > i { font-size: .95rem; }

/* Stammdaten als Definitionsliste: Bezeichnung über Wert, nebeneinander umbrechend. Eine Tabelle
   wäre hier zu viel — es sind höchstens drei Angaben, und manche fehlen. */
.portal-instrument-facts {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 2rem;
    margin: 0;
}

.portal-instrument-facts dt {
    font-size: var(--fs-cap);
    color: var(--ink-3);
}

.portal-instrument-facts dd {
    margin: 0;
    font-size: var(--fs-sm);
}

/* ---- Zertifikatshistorie ------------------------------------------------- */

.portal-cert-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--fs-sm);
}

.portal-cert-table th {
    text-align: left;
    font-weight: 500;
    font-size: var(--fs-cap);
    color: var(--ink-3);
    padding: 0 .75rem .5rem 0;
    border-bottom: 1px solid var(--hairline);
}

.portal-cert-table td {
    padding: .6rem .75rem .6rem 0;
    border-bottom: 1px solid var(--hairline);
    vertical-align: middle;
}

.portal-cert-table tr:last-child td { border-bottom: none; }

.portal-cert-action { text-align: right; padding-right: 0; }

.portal-tag {
    display: inline-block;
    margin-left: .5rem;
    padding: .1rem .45rem;
    border-radius: 999px;
    background: var(--bg-tint);
    font-size: var(--fs-cap);
    color: var(--ink-3);
}

/* Für Spaltenköpfe, die nur der Vorlesesoftware gelten — die Download-Spalte hat keine sichtbare
   Überschrift, braucht aber eine. */
.portal-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ----------------------------------------------------------- 6b. 404-Seite */

/* Dieselbe schmale, mittige Spalte wie die Anmeldung, nur etwas breiter für den Fließtext.
   Bewusst eigene Regeln statt geteilter Klassen: die beiden Seiten haben nichts miteinander zu
   tun und sollen sich unabhängig ändern lassen. */
.portal-notfound {
    max-width: 32rem;
    margin: clamp(1rem, 5vw, 3rem) auto 0;
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.portal-notfound-card {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}

.portal-notfound-head {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    min-width: 0;
}

.portal-notfound-hint {
    color: var(--ink-2);
    font-size: var(--fs-sm);
    line-height: 1.5;
}

.portal-notfound-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}

/* Steht unter der Karte frei auf dem Hintergrundfoto — --ink-2 wie bei .portal-whoami. */
.portal-notfound-foot {
    text-align: center;
    color: var(--ink-2);
    font-size: var(--fs-cap);
    padding: 0 1rem;
    line-height: 1.6;
}

/* --------------------------------------------------- 7. Auftragskopf (Hero) */

.portal-hero {
    /* Kein Farbverlauf, kein Akzentbalken mehr: die Website arbeitet mit
       ruhigen Flächen, und die Kachel steht auch ohne Dekoration für sich. */
    background: var(--bg-card);
}

.portal-hero-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
}

.portal-hero-head {
    display: flex;
    align-items: center;
    gap: 1rem;
    min-width: 0;
}

.portal-hero-icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    border-radius: 14px;
    background: var(--brand-soft);
    color: var(--brand-lift);
}

.portal-hero-icon > i { font-size: 1.6rem; }

.portal-hero-heading { min-width: 0; }

/* Die Auftragsart unter dem Kundennamen in der Auftragskarte. */
.portal-hero-kind { margin-top: .5rem; }

/* Der Kicker über der Überschrift — gemischt geschrieben, in der Hausfarbe.
   Vorher stand hier gesperrte Großschrift; genau davon lebt die Website nicht. */
.portal-hero-eyebrow {
    display: block;
    margin-bottom: .25rem;
    font-size: var(--fs-sm);
    font-weight: 600;
    letter-spacing: -0.008em;
    text-transform: none;
    color: var(--brand-lift);
    opacity: 1;
}

.portal-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    align-items: flex-start;
}

.portal-hero-cta { flex: 0 0 auto; }

/* Auswahlmenü der Datenformate. Eigenbau statt RadzenSplitButton, weil dessen Aufklapp-Knopf ein
   inline onclick rendert und unsere CSP das verbietet (siehe Portal.razor). */
.portal-menu { position: relative; display: inline-flex; }

.portal-menu .portal-menu-button {
    border: 0;
    background: var(--bg-tint);
    color: var(--ink);
}

.portal-menu .portal-menu-button:hover {
    background: color-mix(in srgb, var(--ink) 14%, transparent);
}

/* Fängt den Klick daneben ab. Unsichtbar, aber über allem außer dem Menü selbst. */
.portal-menu-backdrop {
    position: fixed;
    inset: 0;
    z-index: 20;
}

.portal-menu-list {
    position: absolute;
    top: calc(100% + .35rem);
    left: 0;
    z-index: 21;
    min-width: max(100%, 15rem);
    padding: .35rem;
    border-radius: var(--r-fn, 6px);
    background: var(--bg);
    box-shadow: 0 10px 30px rgb(0 0 0 / 14%), 0 1px 3px rgb(0 0 0 / 10%);
}

.portal-menu-item {
    display: flex;
    align-items: center;
    gap: .55rem;
    width: 100%;
    padding: .55rem .7rem;
    border: 0;
    border-radius: calc(var(--r-fn, 6px) - 2px);
    background: transparent;
    color: var(--ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.portal-menu-item:hover,
.portal-menu-item:focus-visible {
    background: var(--bg-tint);
}

/* ------------------------------------------------------------- 8. Kennzahlen */

.portal-stats {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: 1.75rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--hairline);
}

.portal-stat {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .7rem 1rem;
    border-radius: var(--r-sm);
    background: var(--bg-tint);
    min-width: 0;
}

.portal-stat > i { font-size: 1.4rem; color: var(--ink-3); }

.portal-stat-body {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    min-width: 0;
}

.portal-stat-value {
    font-size: var(--fs-lead);
    font-weight: 600;
    letter-spacing: -0.022em;
}

.portal-stat-label { font-size: var(--fs-cap); color: var(--ink-3); }

.portal-stat--warn { background: color-mix(in srgb, var(--warn) 13%, transparent); }
.portal-stat--warn > i,
.portal-stat--warn .portal-stat-value { color: var(--warn); }

.portal-stat--danger { background: color-mix(in srgb, var(--danger) 12%, transparent); }
.portal-stat--danger > i,
.portal-stat--danger .portal-stat-value { color: var(--danger); }

.portal-stat--new { background: color-mix(in srgb, var(--ok) 13%, transparent); }
.portal-stat--new > i,
.portal-stat--new .portal-stat-value { color: var(--ok); }

/* --------------------------------------------------------------- 9. Plaketten */

.portal-badge-warn,
.portal-badge-danger,
.portal-badge-new,
.portal-badge-ok,
.portal-badge-doc,
.portal-badge-kind {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .2rem .65rem;
    border-radius: var(--r-pill);
    font-size: var(--fs-cap);
    font-weight: 500;
    line-height: 1.6;
    white-space: nowrap;
}

.portal-badge-warn {
    color: var(--warn);
    background: color-mix(in srgb, var(--warn) 14%, transparent);
}

/* Unkalibriert zurück: die Kalibrierung war nicht möglich, ein Schein kommt nicht. Rot, weil der
   Kunde ein Gerät zurückbekommt, das er so nicht einsetzen darf — das muss auffallen. */
.portal-badge-danger {
    color: var(--danger);
    background: color-mix(in srgb, var(--danger) 14%, transparent);
}

.portal-row--uncalibrated > td {
    background: color-mix(in srgb, var(--danger) 7%, transparent) !important;
}

.portal-uncalibrated-reason {
    margin-top: .25rem;
    font-size: var(--fs-cap);
    color: color-mix(in srgb, var(--danger) 72%, var(--ink));
    white-space: normal;
}

.portal-badge-new {
    color: var(--ok);
    background: color-mix(in srgb, var(--ok) 14%, transparent);
}

.portal-badge-ok {
    color: var(--ink-3);
    background: var(--bg-tint);
}

/* Ein weiteres PDF neben dem Kalibrierschein (Messprotokoll, Abnahme). Ruhig wie „eingetroffen":
   es ordnet die Zeile ein und meldet nichts, was zu tun wäre. Es steht vor der Bezeichnung, sonst
   läse sich „Messprotokoll" wie eine Gerätebezeichnung. */
.portal-badge-doc {
    color: var(--ink-3);
    background: var(--bg-tint);
    margin-right: .4rem;
}

/* Die Art des Auftrags („Kleinmessmittel" / „Vor-Ort"). Ruhig gehalten und in beiden Fällen
   gleich: sie ordnet ein, sie warnt nicht. Die farbigen Plaketten daneben („neu", „ausstehend")
   melden etwas, was zu tun oder zu erwarten ist — sie sollen nicht mit ihr um Aufmerksamkeit
   ringen. Auf der Auftragskarte steht sie auf var(--bg-tint), deshalb die Kartenfläche als Grund
   und nicht dieselbe Tönung, sonst verschwände sie darin. */
.portal-badge-kind {
    color: var(--ink-2);
    background: var(--bg-card);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink-3) 22%, transparent);
}

.portal-badge-kind > i { color: var(--ink-3); }

.portal-warn-icon { color: var(--warn); }
.portal-ok-icon { color: var(--ok); }

/* ------------------------------------------------------ 10. Auftragsliste */

.portal-order-list {
    display: flex;
    flex-direction: column;
    gap: .6rem;
}

/* Karte statt Tabellenzeile: auf dem Telefon ist eine siebenspaltige Tabelle
   unlesbar, und der Auftrag ist ohnehin ein Ganzes, das man anklickt. */
.portal-order {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 1rem 1.125rem;
    border-radius: var(--r-card);
    background: var(--bg-tint);
    color: inherit;
    transition: background-color .25s var(--ease), transform .45s var(--ease),
                box-shadow .45s var(--ease);
}

.portal-order:hover,
.portal-order:focus-visible {
    background: var(--bg-card);
    box-shadow: var(--shadow-lift);
    transform: translateY(-2px);
    color: inherit;
}

.portal-order-main { flex: 1 1 12rem; min-width: 0; }

.portal-order-number {
    font-weight: 600;
    font-size: var(--fs-lead);
    letter-spacing: -0.022em;
    overflow-wrap: anywhere;
}

.portal-order-meta {
    display: flex;
    gap: .4rem;
    flex-wrap: wrap;
    font-size: var(--fs-sm);
    color: var(--ink-3);
}

.portal-order-figures { display: flex; gap: 1rem; flex: 0 0 auto; }

.portal-order-figure {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.portal-order-figure > i { font-size: 1.1rem; color: var(--ink-3); }

.portal-order-figure-label {
    font-weight: 400;
    font-size: var(--fs-sm);
    color: var(--ink-3);
}

.portal-order-badges { display: flex; gap: .35rem; flex-wrap: wrap; }

.portal-order-chevron { color: var(--ink-3); flex: 0 0 auto; }

/* Weg zurück zur Übersicht, über der Auftragskarte. */
.portal-back {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    align-self: flex-start;
    font-size: var(--fs-sm);
    font-weight: 500;
    color: var(--ink-2);
    transition: color .2s var(--ease);
}

.portal-back:hover, .portal-back:focus-visible { color: var(--ink); }

/* ------------------------------------------------------- 11. Kundenauswahl */

.portal-choice-list { display: flex; flex-direction: column; gap: .5rem; }

.portal-choice {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: 1rem 1.125rem;
    border-radius: var(--r-card);
    background: var(--bg-tint);
    color: inherit;
    transition: background-color .25s var(--ease), box-shadow .45s var(--ease);
}

.portal-choice:hover,
.portal-choice:focus-visible {
    background: var(--bg-card);
    box-shadow: var(--shadow-lift);
    color: inherit;
}

.portal-choice > i { color: var(--ink-3); }
.portal-choice-body { flex: 1 1 auto; min-width: 0; }
.portal-choice-name { display: block; font-weight: 600; font-size: var(--fs-lead); letter-spacing: -0.022em; }
.portal-choice-number { display: block; font-size: var(--fs-sm); color: var(--ink-3); }

/* ------------------------------------------------------- 12. Einstellungen */

/* Die Aufstufung („sudo"): schmal wie die Anmeldekarte, damit sie als Zwischenschritt lesbar ist
   und nicht als halbe Einstellungsseite. */
.portal-elevate {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    max-width: 32rem;
}

.portal-recipients { display: flex; flex-direction: column; gap: .5rem; }

/* Die Empfängertabelle. Auf dem Telefon passen fünf Spalten nicht nebeneinander — sie darf
   deshalb in ihrem eigenen Kasten scrollen, statt die Seite seitlich wegzuziehen. */
.portal-table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.portal-recipient-table { min-width: 34rem; }

/* Die beiden Häkchenspalten so schmal wie ihre Überschrift und mittig — die Häkchen sollen
   untereinander stehen, damit man eine Spalte auf einen Blick lesen kann. */
.portal-recipient-table .portal-col-check {
    width: 1%;
    white-space: nowrap;
    text-align: center;
    padding-right: 1rem;
}

.portal-recipient-name {
    font-weight: 500;
    margin-right: .4rem;
}

/* Abteilung, Adresse, Telefon: eigene Zeile, ruhiger als der Name. */
.portal-recipient-sub {
    display: block;
    font-size: var(--fs-cap);
    color: var(--ink-3);
}

/* Das Formular für einen neuen oder geänderten Ansprechpartner, direkt unter der Tabelle.
   Abgesetzt, weil es sonst wie eine weitere Zeile aussieht — es ist aber ein eigener Vorgang. */
.portal-add-recipient {
    padding: 1rem;
    border-radius: var(--r-sm);
    background: var(--bg-tint);
}

/* Die Platzhalter als anklickbare Pillen: abtippen wäre die häufigste Fehlerquelle
   (%certificatenumber% statt %certificateNumber%), und die Prüfung soll niemand brauchen. */
.portal-placeholders {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
}

.portal-placeholder {
    padding: .2rem .6rem;
    border: none;
    border-radius: var(--r-pill);
    background: var(--bg-tint);
    color: var(--ink-2);
    font: inherit;
    font-size: var(--fs-cap);
    cursor: pointer;
    transition: background-color .2s var(--ease), color .2s var(--ease);
}

.portal-placeholder:hover,
.portal-placeholder:focus-visible {
    background: color-mix(in srgb, var(--brand) 12%, transparent);
    color: var(--brand);
}

.portal-switches {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: .6rem;
}

.portal-switch {
    display: flex;
    align-items: flex-start;
    gap: .7rem;
    padding: .875rem 1rem;
    border-radius: var(--r-sm);
    background: var(--bg-tint);
    cursor: pointer;
    transition: background-color .2s var(--ease);
}

.portal-switch:hover { background: color-mix(in srgb, var(--ink) 8%, transparent); }

.portal-switch-label {
    display: block;
    font-weight: 500;
}

/* Kostenpflichtige Leistungen kennzeichnen: der Kunde hakt hier etwas an, das auf der Rechnung
   auftaucht. Gedeckt gehalten — es ist ein Hinweis, keine Warnung. */
.portal-switch-cost {
    display: inline-block;
    margin-left: .4rem;
    padding: .05rem .45rem;
    border-radius: var(--r-pill);
    background: var(--brand-soft);
    color: var(--brand);
    font-size: var(--fs-cap);
    font-weight: 500;
    letter-spacing: -0.004em;
    vertical-align: .05em;
}

.portal-switch-hint {
    display: block;
    font-size: var(--fs-sm);
    color: var(--ink-3);
    line-height: 1.4;
}

.portal-save-bar {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-wrap: wrap;
    margin-top: 1.25rem;
}

/* -------------------------------------------------------- 13. Reklamation */

.portal-complaint-teaser {
    display: flex;
    align-items: center;
    gap: .875rem;
    flex-wrap: wrap;
}

.portal-complaint-teaser > i { font-size: 1.6rem; color: var(--ink-3); }
.portal-complaint-teaser > div { flex: 1 1 14rem; min-width: 0; }

/* --------------------------------------------------------- 14. Ladebalken */

.portal-progress {
    margin-top: 1.5rem;
    padding: 1rem 1.125rem;
    border-radius: var(--r-card);
    background: var(--bg-tint);
}

.portal-progress-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .6rem;
}

.portal-progress-icon { font-size: 1.15rem; color: var(--ink-3); }

.portal-progress-title {
    font-weight: 500;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Die Laufzeit steht rechts außen — sie ist der Wert, an dem man das Tempo abliest. */
.portal-progress-time {
    margin-left: auto;
    font-variant-numeric: tabular-nums;
    color: var(--ink-3);
    white-space: nowrap;
}

.portal-progress-bar { width: 100%; height: 6px; }

.portal-progress-detail {
    margin-top: .5rem;
    font-size: var(--fs-sm);
    color: var(--ink-3);
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------- 15. Schmale Geräte */

@media (max-width: 640px) {
    .portal-topbar-inner { height: 60px; }

    /* Die Ortsangabe neben der Wortmarke fällt weg — auf dem Telefon zählt die Fläche. */
    .portal-brand-label { display: none; }

    .portal-nav { gap: 0; }

    /* Auf dem Telefon zählt die Fläche: die Beschriftungen fallen weg, die
       Symbole bleiben. */
    .portal-nav-link > span { display: none; }
    .portal-nav-link { padding: .5rem .55rem; }

    .portal-hero-actions,
    .portal-hero-cta { width: 100%; }

    .portal-stat { flex: 1 1 calc(50% - .75rem); }

    .portal-order { gap: .5rem 1rem; }
    .portal-order-main { flex: 1 1 100%; }
    .portal-order-figure-label { display: none; }

    .portal-switches { grid-template-columns: 1fr; }
    .portal-save-bar > * { flex: 1 1 auto; }
}

/* ---- Fortschritt einer Sendung (UPS) --------------------------------------------------------
   Vier Stufen — versandbereit, unterwegs, in Zustellung, zugestellt —, die erreichte Strecke in
   der Markenfarbe. Dieselben Regeln stehen in Hermes und in Ashrise, damit Büro und Kunde
   dasselbe Bild sehen. Die Linie hängt an ::before jeder Stufe und reicht zurück bis zur Mitte
   der vorigen; so braucht es kein Rechnen mit Breiten. */
.ship-steps {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    margin: .75rem 0 .25rem;
    padding: 0;
    list-style: none;
}

.ship-step {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .4rem;
    padding: 0 .25rem;
    text-align: center;
    font-size: var(--fs-cap);
    line-height: 1.3;
    color: var(--ink-3);
}

.ship-step::before {
    content: "";
    position: absolute;
    top: .5rem;
    right: 50%;
    width: 100%;
    height: 3px;
    transform: translateY(-50%);
    background: var(--hairline);
}

.ship-step:first-child::before { display: none; }

.ship-step-dot {
    position: relative;
    z-index: 1;
    width: 1rem;
    height: 1rem;
    border-radius: 50%;
    background: var(--bg-card);
    box-shadow: inset 0 0 0 2px var(--hairline);
}

.ship-step--done { color: var(--ink-2); }
.ship-step--done::before,
.ship-step--current::before { background: var(--brand); }
.ship-step--done .ship-step-dot { background: var(--brand); box-shadow: none; }
.ship-step--current { color: var(--ink); font-weight: 600; }
.ship-step--current .ship-step-dot {
    background: var(--brand);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--brand) 18%, transparent);
}

.ship-steps--delivered .ship-step::before { background: var(--ok); }
.ship-steps--delivered .ship-step-dot { background: var(--ok); }
.ship-steps--delivered .ship-step--current .ship-step-dot {
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--ok) 20%, transparent);
}

.ship-steps--problem .ship-step--current .ship-step-dot {
    background: var(--warn);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--warn) 20%, transparent);
}

/* ---- Laufende Aufträge ------------------------------------------------------------------------
   Ein Kleinmessmittel-Auftrag in Bearbeitung oder mit Sendung unterwegs steht oben unter eigener
   Überschrift und hebt sich durch Höhe ab, nicht durch Farbe: weiß mit Schatten statt der grauen
   Fläche der übrigen — die Hover-Darstellung, nur dauerhaft. Eine blaue Fläche mit Kante gab es
   hier zuerst; sie war ein Fremdkörper in einer Gestaltung, die sonst keine farbigen Karten kennt.
   Erst mit der Zustellung rutscht er zu den abgeschlossenen. */
.portal-order-section {
    margin: .25rem 0 .6rem;
    color: var(--ink-2);
    font-weight: 600;
}

.portal-order-list + .portal-order-section { margin-top: 1.5rem; }

.portal-order--open {
    background: var(--bg-card);
    box-shadow: var(--shadow-card);
}

.portal-order--open:hover,
.portal-order--open:focus-visible {
    box-shadow: var(--shadow-lift);
}

.portal-shipment { margin-top: .5rem; }

.portal-shipment-facts {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem 1.25rem;
    margin: .75rem 0 .5rem;
    font-size: var(--fs-sm);
    color: var(--ink-2);
}

.portal-shipment-facts strong { color: var(--ink); font-weight: 600; }

.portal-shipment-link {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-weight: 600;
}

.portal-shipment-link .rzi { font-size: 1.05rem; }

/* ------------------------------------------------- 12. Prüfmittelübersicht */

/* Die Seite „Geräte" (Instruments.razor): Master-Detail über den Kleinmessmittel-Bestand.

   Breiter als der Rest des Portals. 1040 px reichen für eine Auftragsliste, aber nicht für
   eine Tabelle mit sechs Spalten UND eine Detailspalte daneben. Die Fläche bricht deshalb aus
   .portal-wrap aus — über den Rand und nicht über eine Körperklasse, weil eine interaktive Seite
   den <body> nicht anfasst. Was über 100vw hinausragt (die Bildlaufleiste), fängt das
   `overflow-x: clip` am Körper ab. */
.inv {
    --inv-w: min(1440px, calc(100vw - 2 * var(--pad)));
    width: var(--inv-w);
    margin-left: calc(50% - var(--inv-w) / 2);
    display: flex;
    flex-direction: column;
    gap: 1.5rem;

    /* Ampel. Ein Ton je Zustand, gemischt in drei Stärken: Kante, Tönung, Plakette. Rot ist
       „gesperrt", Orange „Kalibrierung notwendig" — beide verlangen etwas, aber nicht dasselbe. */
    --st-usable: var(--ok);
    --st-soon: #B7791F;
    --st-due: #D9480F;
    --st-locked: var(--danger);
    --st-lab: var(--brand-lift);
    --st-retired: var(--ink-3);
    --st-unknown: var(--ink-3);
}

.inv-state--usable  { --st: var(--st-usable); }
.inv-state--soon    { --st: var(--st-soon); }
.inv-state--due     { --st: var(--st-due); }
.inv-state--locked  { --st: var(--st-locked); }
.inv-state--lab     { --st: var(--st-lab); }
.inv-state--retired { --st: var(--st-retired); }
.inv-state--unknown { --st: var(--st-unknown); }

/* ---- Kacheln = Filter ----------------------------------------------------- */

.inv-states {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: 1.25rem;
}

.inv-state-chip {
    --st: var(--ink-2);
    display: inline-flex;
    align-items: baseline;
    gap: .5rem;
    padding: .55rem 1rem;
    border: 0;
    border-radius: var(--r-pill);
    background: var(--bg-tint);
    color: var(--ink);
    font: inherit;
    font-size: var(--fs-sm);
    cursor: pointer;
    transition: background-color .15s var(--ease), box-shadow .15s var(--ease);
}

.inv-state-chip::before {
    content: "";
    width: .6rem;
    height: .6rem;
    border-radius: 50%;
    background: var(--st);
    align-self: center;
}

/* „Alle" trägt keinen Zustand und damit keinen Punkt. */
.inv-state-chip:not([class*="inv-state--"])::before {
    display: none;
}

.inv-state-chip:hover { background: color-mix(in srgb, var(--st) 12%, var(--bg-card)); }

.inv-state-chip.is-active {
    background: color-mix(in srgb, var(--st) 16%, var(--bg-card));
    box-shadow: inset 0 0 0 1.5px var(--st);
}

.inv-state-chip:focus-visible,
.inv-sort:focus-visible,
.inv-row:focus-visible,
.inv-detail-back:focus-visible,
.inv-detail-close:focus-visible {
    outline: 2px solid var(--brand-lift);
    outline-offset: 2px;
}

.inv-state-count {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.inv-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem 1.5rem;
    margin-top: 1rem;
}

.inv-search { flex: 1 1 22rem; }

.inv-retired-toggle {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-size: var(--fs-sm);
    color: var(--ink-2);
    cursor: pointer;
}

.inv-retired-toggle input { accent-color: var(--brand); width: 1rem; height: 1rem; }

/* ---- Master-Detail ------------------------------------------------------- */

.inv-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(320px, 400px);
    gap: 1.5rem;
    align-items: start;
}

.inv-list,
.inv-detail-card,
.inv-detail-empty {
    background: var(--bg-card);
    border-radius: var(--r-card);
    box-shadow: var(--shadow-card);
}

.inv-list { overflow: hidden; }

.inv-list-head {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 1rem 1.25rem .75rem;
    font-size: var(--fs-cap);
    color: var(--ink-2);
}

.inv-asof { color: var(--ink-3); }

.inv-table-wrap { overflow-x: auto; }

.inv-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--fs-sm);
}

.inv-table th {
    background: var(--bg-card);
    text-align: left;
    padding: .5rem .75rem;
    border-bottom: 1px solid var(--hairline);
    white-space: nowrap;
}

.inv-sort {
    display: inline-flex;
    align-items: center;
    gap: .25rem;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: var(--fs-cap);
    font-weight: 500;
    color: var(--ink-3);
    cursor: pointer;
}

.inv-sort.is-active,
.inv-sort:hover { color: var(--ink); }

.inv-sort .rzi { font-size: .9rem; }

.inv-table th:not(:has(.inv-sort)) {
    font-size: var(--fs-cap);
    font-weight: 500;
    color: var(--ink-3);
}

/* Jede Zeile trägt ihre Ampel zweimal: als Kante links (auch im Augenwinkel, beim Scrollen) und
   als leichte Tönung. Die Tönung bleibt schwach — sonst wäre eine Liste mit 300 überfälligen
   Geräten eine orange Wand, und die Plakette davor ginge darin unter. */
.inv-row {
    cursor: pointer;
    background: color-mix(in srgb, var(--st) 5%, var(--bg-card));
    transition: background-color .12s var(--ease);
}

.inv-row td {
    padding: .65rem .75rem;
    border-bottom: 1px solid var(--hairline);
    vertical-align: middle;
}

.inv-row td:first-child { box-shadow: inset 4px 0 0 var(--st); padding-left: 1rem; }

.inv-row:hover { background: color-mix(in srgb, var(--st) 11%, var(--bg-card)); }

.inv-row.is-selected {
    background: color-mix(in srgb, var(--st) 18%, var(--bg-card));
}

.inv-row.is-selected td:first-child { box-shadow: inset 6px 0 0 var(--st); }

.inv-state--retired.inv-row td:not(:first-child) { color: var(--ink-3); }

.inv-ident { font-weight: 600; white-space: nowrap; }

.inv-date { white-space: nowrap; font-variant-numeric: tabular-nums; }

.inv-state--due .inv-date,
.inv-state--soon .inv-date { color: color-mix(in srgb, var(--st) 80%, var(--ink)); font-weight: 600; }

.inv-pill {
    display: inline-block;
    padding: .2rem .6rem;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--st) 15%, var(--bg-card));
    color: color-mix(in srgb, var(--st) 78%, var(--ink));
    font-size: var(--fs-cap);
    font-weight: 600;
    white-space: nowrap;
}

.inv-pill--lg {
    padding: .3rem .85rem;
    font-size: var(--fs-sm);
}

.inv-empty,
.inv-detail-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    padding: 2.5rem 1.5rem;
    text-align: center;
    color: var(--ink-2);
    font-size: var(--fs-sm);
}

.inv-empty .rzi,
.inv-detail-empty .rzi { font-size: 2rem; color: var(--ink-3); }

.inv-more {
    display: flex;
    justify-content: center;
    padding: 1rem;
}

/* ---- Detailspalte ------------------------------------------------------- */

/* Klebt unter der Kopfleiste mit, damit sie bei einer langen Liste nicht oben zurückbleibt; wird
   sie selbst länger als das Fenster, scrollt sie in sich. */
.inv-detail {
    position: sticky;
    top: calc(82px + 1rem);
    max-height: calc(100vh - 82px - 2rem);
    overflow-y: auto;
    border-radius: var(--r-card);
}

.inv-detail-card {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    padding: 1.25rem 1.5rem 1.5rem;
    box-shadow: var(--shadow-card), inset 0 4px 0 var(--st);
}

.inv-detail-top {
    display: flex;
    justify-content: flex-end;
    margin-bottom: -.75rem;
}

.inv-detail-back,
.inv-detail-close {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .35rem;
    border: 0;
    border-radius: var(--r-pill);
    background: none;
    color: var(--ink-2);
    font: inherit;
    font-size: var(--fs-sm);
    cursor: pointer;
}

.inv-detail-back:hover,
.inv-detail-close:hover { background: var(--bg-tint); color: var(--ink); }

.inv-detail-back { display: none; margin-right: auto; padding-inline: .5rem .75rem; }

.inv-detail-sub {
    color: var(--ink-2);
    font-size: var(--fs-sm);
}

.inv-facts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .85rem 1.25rem;
    margin: 0;
    padding: 1rem 0;
    border-block: 1px solid var(--hairline);
}

.inv-facts dt {
    font-size: var(--fs-cap);
    color: var(--ink-3);
}

.inv-facts dd {
    margin: .1rem 0 0;
    font-size: var(--fs-sm);
    overflow-wrap: anywhere;
}

.inv-certs {
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

.inv-certs-count {
    display: inline-block;
    margin-left: .4rem;
    padding: 0 .5rem;
    border-radius: var(--r-pill);
    background: var(--bg-tint);
    font-size: var(--fs-cap);
    font-weight: 500;
    color: var(--ink-2);
    vertical-align: middle;
}

/* Die Scheine als Zeitstrahl: eine Linie, ein Punkt je Kalibrierung, jüngste oben. */
.inv-timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}

.inv-timeline li {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .6rem 0 .6rem 1.5rem;
}

.inv-timeline li::before {
    content: "";
    position: absolute;
    left: .3rem;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--hairline);
}

.inv-timeline li:first-child::before { top: 50%; }
.inv-timeline li:last-child::before { bottom: 50%; }
.inv-timeline li:only-child::before { display: none; }

.inv-timeline li::after {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: .65rem;
    height: .65rem;
    margin-top: -.325rem;
    border-radius: 50%;
    background: var(--bg-card);
    box-shadow: inset 0 0 0 2px var(--brand-lift);
}

.inv-timeline li:first-child::after { background: var(--brand-lift); }

.inv-timeline-main {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.inv-timeline-date {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.inv-timeline-number {
    font-size: var(--fs-cap);
    color: var(--ink-2);
    overflow-wrap: anywhere;
}

/* Unter 1100 px ist neben der Tabelle kein Platz für die Detailspalte: dann eine Spalte, und ein
   gewähltes Gerät ersetzt die Liste — mit „Zur Liste" zurück. Hersteller und Seriennummer gehen
   aus der Tabelle, sie stehen ja in der Detailansicht. */
@media (max-width: 1100px) {
    .inv-layout { grid-template-columns: minmax(0, 1fr); }
    .inv-detail { position: static; max-height: none; overflow: visible; }
    .inv-layout:not(.inv-layout--detail) .inv-detail { display: none; }
    .inv-layout--detail .inv-list { display: none; }
    .inv-detail-back { display: inline-flex; }
    .inv-detail-close { display: none; }
}

@media (max-width: 760px) {
    .inv-col-optional { display: none; }
    .inv-facts { grid-template-columns: minmax(0, 1fr); }
    .inv-table th, .inv-row td { padding-inline: .5rem; }
}

/* ---- verWalter: Marke, Beta, Startseite -------------------------------- */

.inv-brand {
    display: flex;
    align-items: center;
    gap: .5rem;
}

/* „Beta" als ruhige Plakette neben dem Namen — in Gemischtschreibung wie alles im Portal. */
.inv-beta {
    padding: .05rem .5rem;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--st-soon, #B7791F) 16%, var(--bg-card));
    color: color-mix(in srgb, #B7791F 75%, var(--ink));
    font-size: var(--fs-cap);
    font-weight: 600;
}

.inv-trial {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: 1rem;
}

.inv-gate {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    max-width: 44rem;
    margin-inline: auto;
}

.inv-gate-points {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .75rem;
}

.inv-gate-points li {
    display: flex;
    align-items: flex-start;
    gap: .75rem;
}

.inv-gate-points .rzi { color: var(--brand-lift); }

.inv-gate-action {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: .5rem;
}

/* ---- verWalter: Bearbeiten, Filter, Verlauf ---------------------------- */

.inv-hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
}

/* Filter als gewöhnliche Felder — RadzenDropDown mit Filter bringt inline-Handler mit, die die CSP
   verbietet. Beschriftung über dem Feld, wie in der Detailspalte. */
.inv-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .75rem 1rem;
    margin-top: 1rem;
}

.inv-filter {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    min-width: 10rem;
    flex: 0 1 13rem;
    font-size: var(--fs-cap);
    color: var(--ink-3);
}

.inv-input {
    width: 100%;
    box-sizing: border-box;
    padding: .5rem .7rem;
    border: 1px solid var(--hairline);
    border-radius: var(--r-sm);
    background: var(--bg-card);
    color: var(--ink);
    font: inherit;
    font-size: var(--fs-sm);
}

.inv-input:focus,
.inv-input:focus-within {
    outline: 2px solid var(--brand-lift);
    outline-offset: 1px;
    border-color: transparent;
}

.inv-input:disabled {
    background: var(--bg-tint);
    color: var(--ink-2);
}

textarea.inv-input { resize: vertical; }

.inv-form {
    display: flex;
    flex-direction: column;
    gap: .9rem;
}

.inv-form-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .75rem 1rem;
}

.inv-field {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    font-size: var(--fs-cap);
    color: var(--ink-3);
}

.inv-field--wide { grid-column: 1 / -1; }

.inv-form-section {
    margin: .5rem 0 0;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink);
}

.inv-form-hint {
    margin: 0;
    font-size: var(--fs-cap);
    color: var(--ink-2);
}

.inv-checks {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 1.5rem;
    font-size: var(--fs-sm);
}

.inv-checks label,
.inv-confirm {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    cursor: pointer;
}

.inv-checks input,
.inv-confirm input { accent-color: var(--brand); width: 1rem; height: 1rem; }

.inv-own-field {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) auto;
    gap: .5rem;
    align-items: center;
}

.inv-own-field--single { grid-template-columns: minmax(0, 1fr) auto; }

.inv-icon-button,
.inv-link-button {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .35rem .5rem;
    border: 0;
    border-radius: var(--r-pill);
    background: none;
    color: var(--ink-2);
    font: inherit;
    font-size: var(--fs-sm);
    cursor: pointer;
}

.inv-link-button { color: var(--link); align-self: flex-start; }

.inv-icon-button:hover,
.inv-link-button:hover { background: var(--bg-tint); }

.inv-icon-button:focus-visible,
.inv-link-button:focus-visible {
    outline: 2px solid var(--brand-lift);
    outline-offset: 2px;
}

.inv-filter-reset { align-self: flex-end; }

.inv-form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
}

.inv-confirm {
    flex-direction: column;
    align-items: flex-start;
}

/* „eigen" — leise neben der Identnummer: die Ausnahme wird gekennzeichnet, nicht die Regel. */
.inv-source {
    display: inline-block;
    margin-left: .4rem;
    padding: 0 .45rem;
    border-radius: var(--r-pill);
    background: color-mix(in srgb, var(--brand-lift) 10%, var(--bg-card));
    color: color-mix(in srgb, var(--brand-lift) 80%, var(--ink));
    font-size: var(--fs-cap);
    font-weight: 500;
}

.inv-source--lg {
    margin-left: 0;
    padding: .3rem .85rem;
    font-size: var(--fs-sm);
}

.inv-detail-badges {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.inv-detail-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .75rem;
}

.inv-detail-notes {
    margin: 0;
    font-size: var(--fs-sm);
    color: var(--ink-2);
    white-space: pre-line;
}

/* Verlauf: Scheine von uns blau, eigene Einträge grau umrandet, nicht bestanden rot. */
.inv-event--own::after { box-shadow: inset 0 0 0 2px var(--ink-3); }
.inv-event--own:first-child::after { background: var(--ink-3); }
.inv-event.is-failed::after { box-shadow: inset 0 0 0 2px var(--danger); }
.inv-event.is-failed:first-child::after { background: var(--danger); }
.inv-event.is-failed .inv-timeline-date { color: color-mix(in srgb, var(--danger) 80%, var(--ink)); }

.inv-timeline-notes {
    font-size: var(--fs-cap);
    color: var(--ink-2);
    white-space: pre-line;
}

.inv-event-actions {
    display: flex;
    gap: .25rem;
    flex-shrink: 0;
}

@media (max-width: 760px) {
    .inv-form-grid { grid-template-columns: minmax(0, 1fr); }
    .inv-filter { flex: 1 1 100%; }
}

/* Die Teile eines Satzes in der Detailspalte: je eine Zeile mit Ampel, Nummer und Fälligkeit. */
.inv-members {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.inv-members li {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: .75rem;
    align-items: center;
    padding: .5rem 0 .5rem .6rem;
    border-bottom: 1px solid var(--hairline);
    box-shadow: inset 3px 0 0 var(--st);
}

.inv-members li:last-child { border-bottom: none; }

.inv-member-main {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
