/* ================================================================
   RAXLYN — Item Inventory Management
   Design: Monochrome Professional Dark
   ================================================================ */

/* Kein @import von Google Fonts (entfernt 2026-07-30, Audit Block 8).

   Der Import konnte unter der eigenen CSP nie greifen. main.py setzt
   `style-src 'self'`; im Browser gegen genau diesen Header nachgemessen,
   löst ein @import auf fonts.googleapis.com einen CSP-Verstoß der Direktive
   `style-src-elem` aus, das Stylesheet wird nicht geladen. Damit kam auch nie
   ein @font-face an, das die Schriftdateien hätte anfordern können.
   In abgeschotteten Kunden-LANs kam ein aussichtsloser DNS-Versuch dazu.

   Gerendert wird also das, was in --font/--mono unten hinter 'Inter' bzw.
   'JetBrains Mono' steht — bzw. Inter selbst, falls es auf dem Client-Rechner
   lokal installiert ist. Am sichtbaren Ergebnis ändert das Entfernen nichts.

   Sollen Inter / JetBrains Mono verlässlich ausgeliefert werden, gehören die
   Font-Dateien nach static/vendor/ und hier per @font-face eingebunden (siehe
   static/vendor/README.md, „Kein CDN zur Laufzeit"). Das ist eine sichtbare
   Design-Änderung und damit eine Produktentscheidung, keine Audit-Korrektur. */

/* ================================================================
   TOKENS
   ================================================================ */
:root {
    --bg: #0e0e0e;
    --bg-2: #141414;
    --bg-3: #1a1a1a;
    --bg-4: #212121;
    --bg-hover: #272727;

    --border: #2a2a2a;
    --border-2: #333333;

    --text-1: #f0f0f0;
    --text-2: #a0a0a0;
    /* W-10 (web audit 2026-09-23): #5a5a5a / #3a3a3a measured 2.5:1 and 1.6:1 —
       table headers and hints were practically invisible. On this near-black
       palette AA (4.5:1) starts around #858585; tests/test_web_audit_0923_visual.py
       computes every token pair. */
    --text-3: #8e8e8e;
    --text-4: #858585;

    --accent: #ffffff;
    --accent-dim: rgba(255, 255, 255, 0.06);
    --accent-dim2: rgba(255, 255, 255, 0.10);

    /* R5-01 (audit round 5): the keyboard focus indicator.
       Twelve rules in this file remove the browser's own ring with
       `outline: none`. Seven of them replace it with `--border` → `--border-2`,
       which on this palette is #2a2a2a → #333333 — measured at the running
       system, a contrast ratio of **1.14:1**. Two remove it in every state with
       no replacement at all. On a near-black theme that is not a weak
       indicator, it is no indicator: a keyboard user cannot tell which field
       they are typing into, including on the login mask.
       White against the field background (#1a1a1a) is 17.3:1 and is already
       the theme's own accent, so the ring is loud without being off-brand. */
    --focus-ring: var(--accent);

    --success: #4ade80;
    --warning: #facc15;
    --danger: #f87171;
    --info: #60a5fa;

    --success-bg: rgba(74, 222, 128, 0.06);
    --warning-bg: rgba(250, 204, 21, 0.06);
    --danger-bg: rgba(248, 113, 113, 0.06);
    --info-bg: rgba(96, 165, 250, 0.06);

    --font: 'Inter', -apple-system, sans-serif;
    --mono: 'JetBrains Mono', monospace;

    --shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
    --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.7);

    --r: 6px;
    --r-lg: 10px;
    --r-xl: 14px;

    --ease: cubic-bezier(0.16, 1, 0.3, 1);

    /* W-46: native controls rendered in light mode inside the dark theme. */
    color-scheme: dark;
}

/* W-41: links were the browser's default blue (#0000ee on near-black, ~1.3:1). */
a {
    color: var(--text-1);
    text-decoration: underline;
    text-decoration-color: var(--text-3);
    text-underline-offset: 2px;
}
a:hover {
    text-decoration-color: var(--text-1);
}
a:visited {
    color: var(--text-2);
}
a.btn,
a.btn:visited {
    text-decoration: none;
    color: inherit;
}

/* W-02: an invalid field is marked, not only described in an alert. */
input[aria-invalid="true"],
select[aria-invalid="true"],
textarea[aria-invalid="true"] {
    border-color: var(--danger) !important;
}

/* ================================================================
   RESET
   ================================================================ */
*,
*::before,
*::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

body {
    font-family: var(--font);
    background: var(--bg);
    color: var(--text-1);
    min-height: 100vh;
    display: flex;
    justify-content: center;
    /* GUI-02 (audit 2026-07-30): plain `center` is a trap once a flex item is
       wider than the row — the item overflows on BOTH sides and the browser
       clamps scrollLeft at 0, so the left overhang can never be reached. On a
       390 px phone #mainApp started at x = -149 px and those 149 px were dead.
       `safe center` falls back to start alignment exactly in that case.
       The plain `center` above stays as the fallback for engines that do not
       understand the `safe` keyword — they simply ignore this line. */
    justify-content: safe center;
    align-items: flex-start;
    /* One source of truth for the frame around the app: #mainApp derives its
       viewport-filling height from it (R4-01), so the two can never drift. */
    --app-pad: 16px;
    padding: var(--app-pad);
    font-size: 14px;
    line-height: 1.5;
}

/* Login / register / password change: the main app is hidden → centre vertically.
   Driven by the `auth-mode` class that showScreen() sets (js/inline_app.js).
   It used to hang off the literal string `display: none` inside #mainApp's
   inline style attribute, which silently stopped matching the moment that
   style changed to `display: block` — see GUI-01. */
body.auth-mode {
    align-items: center;
}

/* Verhindert Scrollen im Hintergrund, wenn ein Modal geöffnet ist */
body:has(.modal.active) {
    overflow: hidden;
}

/* W-54 / W-40 (web audit 2026-09-23): this block only sized the scrollbar and
   gave it no thumb style — Chrome then paints NO thumb, so every scroll area in
   the app (the user list: 28 users, 7 visible) looked complete. And the one
   styled area (.p14-hscroll) painted a white thumb. One themed scrollbar for
   all: `scrollbar-color` (standard, inherited; Chrome 121+ prefers it over the
   ::-webkit-* rules) plus the ::-webkit-* fallback for older engines. */
:root {
    --scrollbar-thumb: #4a4a4a;
    --scrollbar-thumb-hover: #6a6a6a;
    scrollbar-color: var(--scrollbar-thumb) transparent;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: transparent;
}

::-webkit-scrollbar-thumb {
    background-color: var(--scrollbar-thumb);
    border: 2px solid transparent;
    border-radius: 6px;
    background-clip: padding-box;
}

::-webkit-scrollbar-thumb:hover {
    background-color: var(--scrollbar-thumb-hover);
}

/* ================================================================
   ANIMATIONS
   ================================================================ */
@keyframes fadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@keyframes slideUp {
    from {
        opacity: 0;
        transform: translateY(12px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-6px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes spin {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}

@keyframes pulse {

    0%,
    100% {
        opacity: 1;
    }

    50% {
        opacity: 0.4;
    }
}

@keyframes bannerIn {
    from {
        max-height: 0;
        opacity: 0;
        padding-top: 0;
        padding-bottom: 0;
    }

    to {
        max-height: 60px;
        opacity: 1;
        padding-top: 10px;
        padding-bottom: 10px;
    }
}

/* ================================================================
   CONTAINER
   ================================================================ */
.container {
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--r-xl);
    width: 100%;
    max-width: 1480px;
    overflow: hidden;
    box-shadow: var(--shadow-lg);
    animation: slideUp 0.3s var(--ease);
}

/* Auth mode: no wide outer frame — the shell hugs the login card. */
body.auth-mode #appContainer {
    width: fit-content;
    max-width: min(440px, calc(100vw - 32px));
    background: transparent;
    border: none;
    box-shadow: none;
    overflow: visible;
}

/* GUI-01/GUI-02: #mainApp is a SIBLING of #appContainer (both are direct flex
   children of <body>; #appContainer is closed inside phase14Panel.html, which
   is why the indentation in index.html suggests otherwise). It carried no
   width rule at all, so as a flex item it shrink-wrapped to its content —
   min-content 675 px, which is wider than any phone in portrait.
   `width: 100%` + `min-width: 0` makes it behave like .container: fill the
   row, and shrink below the content width when there is less room. The wide
   parts inside (the items table) already scroll in their own container. */
#appContainer,
#mainApp {
    min-width: 0;
}

#mainApp {
    width: 100%;
    max-width: 1480px;
}

/* R4-01 (audit round 4): let the items table have the screen.
   `.table-container` was a flat `height: 400px`, so on a 1346x861 window the
   primary work surface of an inventory application showed 9 of 500 loaded rows
   and left 461 px of empty page below it — and the page itself did not scroll,
   because <body> is only min-height:100vh. A bigger monitor changed nothing.
   The <=768px breakpoint already computed a viewport-relative height; the
   desktop rule simply never got the same treatment.

   `align-items: stretch` on <body> would NOT do it — measured: with an
   `auto` height and only `min-height: 100vh`, the flex line's cross size is
   the tallest item, so "stretch" stretches to the content and the table stayed
   at 400 px. A `min-height` on the item itself is definite, so the column below
   really does have room to distribute. It reads the body padding back out of
   `--app-pad` instead of repeating `16px`, so mobile (padding 0) stays right.

   From there it is a plain flex column: banners and header take what they
   need, the table takes the rest. That keeps working when the certificate /
   licence banners appear or the action buttons wrap to a second line — a
   `calc(100vh - <constant here>)` would be wrong in exactly those cases. */
#mainApp {
    min-height: calc(100vh - 2 * var(--app-pad, 16px));
    display: flex;
    flex-direction: column;
}

/* ================================================================
   HEADER
   ================================================================ */
.header {
    background: var(--bg-2);
    padding: 12px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    border-bottom: 1px solid var(--border);
}

.header h1 {
    font-family: var(--font);
    font-size: 15px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-1);
    display: flex;
    align-items: center;
    gap: 10px;
}

.user-info {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    flex-wrap: wrap;
    color: var(--text-2);
}

/* Rechts oben: etwas hellerer Block, Schrift klar lesbar */
.header .user-info {
    background: var(--bg-4);
    border: 1px solid var(--border-2);
    border-radius: var(--r-lg);
    padding: 8px 14px;
    gap: 8px;
}

.header .user-info #userDisplay {
    color: var(--text-1);
    font-weight: 500;
}

.header .user-info .header-lang-select {
    margin-right: 6px;
    padding: 5px 8px;
    font-size: 12px;
    font-family: var(--font);
    cursor: pointer;
    /* R5-01: `outline: none` sat here on the BASE rule, not on :focus — the
       ring was gone in every state, so tabbing to the language selector showed
       nothing at all. Removed; the :focus-visible rule at the end of this file
       governs. */
    border-radius: var(--r);
    background: var(--bg-3);
    color: var(--text-1);
    border: 1px solid var(--border-2);
}

.header .user-info .header-lang-select:hover {
    border-color: #3f3f3f;
    background: var(--bg-hover);
}

.status-indicator {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--success);
    animation: pulse 3s ease infinite;
}

/* ================================================================
   BUTTONS
   ================================================================ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    padding: 7px 13px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    cursor: pointer;
    font-family: var(--font);
    font-size: 12px;
    font-weight: 500;
    line-height: 1.4;
    white-space: nowrap;
    transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
    text-decoration: none;
    background: var(--bg-3);
    color: var(--text-2);
}

.btn:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

.btn:active {
    background: var(--bg-4);
}

.btn:disabled {
    opacity: 0.35;
    cursor: not-allowed;
}

/* Primary — dezentes Hellgrau, ins dunkle UI eingebettet */
.btn-primary {
    background: var(--bg-4);
    color: var(--text-1);
    border-color: var(--border-2);
    font-weight: 600;
}

.btn-primary:hover {
    background: var(--bg-hover);
    border-color: #3f3f3f;
    color: var(--text-1);
}

.btn-primary:active {
    background: #2a2a2a;
}

/* Suchen: stärkerer Kontrast (ehemaliges „weißes“ Primary) */
.btn-search {
    background: var(--text-1);
    color: var(--bg);
    border-color: var(--text-1);
    font-weight: 600;
}

.btn-search:hover {
    background: #e0e0e0;
    border-color: #e0e0e0;
    color: var(--bg);
}

.btn-search:active {
    background: #c8c8c8;
}

/* Success — neutral hervorgehoben, kein Signal-Grün */
.btn-success {
    background: var(--bg-4);
    color: var(--text-1);
    border-color: var(--border-2);
    font-weight: 600;
}

.btn-success:hover {
    background: var(--bg-hover);
    border-color: #3f3f3f;
    color: var(--text-1);
}

.btn-success:active {
    background: #2a2a2a;
}

/* Danger — W-35 (web audit 2026-09-23): "Delete" was byte-identical to
   "Rename", so it now carries the danger colour (text + frame, no fill). */
.btn-danger {
    background: var(--bg-3);
    color: var(--danger);
    border-color: rgba(248, 113, 113, 0.35);
    font-weight: 500;
}

.btn-danger:hover {
    background: rgba(248, 113, 113, 0.10);
    border-color: var(--danger);
    color: var(--danger);
}

.btn-danger:active {
    background: rgba(248, 113, 113, 0.16);
}

/* Secondary = default */
.btn-secondary {
    background: var(--bg-3);
    color: var(--text-2);
    border-color: var(--border);
}

.btn-secondary:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

/* Warning — wie übrige neutrale Aktionen (kein Gelb-Akzent) */
.btn-warning {
    background: var(--bg-3);
    color: var(--text-2);
    border-color: var(--border);
}

.btn-warning:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

/* Purple */
.btn-purple {
    background: var(--bg-3);
    color: var(--text-2);
    border-color: var(--border);
}

.btn-purple:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

/* Teal */
.btn-teal {
    background: var(--bg-3);
    color: var(--text-2);
    border-color: var(--border);
}

.btn-teal:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

.btn-small,
.btn-sm {
    padding: 4px 9px;
    font-size: 11px;
    border-radius: 4px;
}

/* Oben rechts: alle 4 Header-Buttons wie „Datei hochladen“ (etwas heller als Toolbar) */
.header .user-info .btn {
    background: var(--bg-4);
    color: var(--text-1);
    border-color: var(--border-2);
    font-weight: 600;
}

.header .user-info .btn:hover {
    background: var(--bg-hover);
    border-color: #3f3f3f;
    color: var(--text-1);
}

.header .user-info .btn:active {
    background: #2a2a2a;
}

/* Unten: Aktionsleiste, Tabelle, Modale — neutraler Ton wie Löschen (nicht Header, nicht Suchen) */
.main-content .action-buttons .btn,
.table-container .btn,
.modal .btn {
    background: var(--bg-3);
    color: var(--text-2);
    border-color: var(--border-2);
    font-weight: 500;
}

.main-content .action-buttons .btn:hover,
.table-container .btn:hover,
.modal .btn:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

.main-content .action-buttons .btn:active,
.table-container .btn:active,
.modal .btn:active {
    background: #2a2a2a;
}

/* The neutral zone above (specificity 0,2,0) beat every variant (0,1,0): in a
   dialog "Delete" was grey again and "Save" looked exactly like "Cancel".
   Measured live 2026-09-23 on #btnArticleDelete: rgb(160,160,160). */
.main-content .action-buttons .btn-danger,
.table-container .btn-danger,
.modal .btn-danger {
    color: var(--danger);
    border-color: rgba(248, 113, 113, 0.35);
}

.main-content .action-buttons .btn-danger:hover,
.table-container .btn-danger:hover,
.modal .btn-danger:hover {
    background: rgba(248, 113, 113, 0.10);
    border-color: var(--danger);
    color: var(--danger);
}

.main-content .action-buttons .btn-danger:active,
.table-container .btn-danger:active,
.modal .btn-danger:active {
    background: rgba(248, 113, 113, 0.16);
}

.modal .btn-primary,
.modal .btn-success {
    background: var(--bg-4);
    color: var(--text-1);
    font-weight: 600;
}

/* ================================================================
   LOGIN
   ================================================================ */
.login-container {
    max-width: 440px;
    /* W-03: a long message widened the card (measured 325 → 440 px): the
       parent is a shrink-wrapped flex item, so the card sets its own width —
       against the viewport, which a percentage here would not do. */
    width: min(440px, calc(100vw - 32px));
    box-sizing: border-box;
    margin: 48px auto;
    padding: 40px;
    background: var(--bg-2);
    border-radius: var(--r-xl);
    border: 1px solid var(--border);
    animation: slideUp 0.3s var(--ease);
}

.login-header {
    text-align: center;
    margin-bottom: 28px;
}

.login-header h2 {
    font-size: 28px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-1);
    margin-bottom: 8px;
}

.login-header p {
    color: var(--text-3);
    font-size: 12px;
    letter-spacing: 0.02em;
}

/* W-64: local and Windows sign-in share one grid cell — the card keeps the
   height of the taller form instead of jumping when the mode changes. */
.login-forms {
    display: grid;
}

.login-forms > form {
    grid-area: 1 / 1;
}

.login-forms > form.is-hidden {
    visibility: hidden;
    pointer-events: none;
}

/* The registration heading was the 28px uppercase brand size next to a 16px
   icon — a title, not a logo. */
#registerScreen .login-header h2 {
    font-size: 18px;
    letter-spacing: 0.04em;
    display: inline-flex;
    align-items: center;
    gap: 8px;
}

#registerScreen .login-header h2 svg {
    width: 20px;
    height: 20px;
}

.registration-toggle {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: var(--text-2);
    margin-bottom: 10px;
}

/* Login Mode Switcher (Local / LDAP) */
.login-mode-switcher {
    display: flex;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 3px;
    margin-bottom: 20px;
    gap: 3px;
}

.login-mode-btn {
    flex: 1;
    padding: 10px 16px;
    border: none;
    border-radius: var(--r);
    background: transparent;
    color: var(--text-3);
    font-family: var(--font);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.25s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    letter-spacing: 0.02em;
}

.login-mode-btn:hover {
    color: var(--text-2);
    background: rgba(255, 255, 255, 0.04);
}

.login-mode-btn.active {
    background: var(--bg-4);
    color: var(--text-1);
    font-weight: 600;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}

/* ================================================================
   FORMS
   ================================================================ */
.form-group {
    margin-bottom: 14px;
}

.form-group label {
    display: block;
    margin-bottom: 6px;
    color: var(--text-2);
    font-weight: 500;
    font-size: 11px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.form-group input,
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 9px 12px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-family: var(--font);
    font-size: 13px;
    color: var(--text-1);
    transition: border-color 150ms ease, background 150ms ease;
    line-height: 1.5;
}

.form-group input::placeholder,
.form-group textarea::placeholder {
    color: var(--text-4);
    font-size: 13px;
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--border-2);
    background: var(--bg-4);
}

.form-group select option {
    background: var(--bg-3);
    color: var(--text-1);
}

/* Phase14 panel inputs — same theme as .form-group input */
.form-input,
select.form-input,
textarea.form-input {
    width: 100%;
    padding: 9px 12px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-family: var(--font);
    font-size: 13px;
    color: var(--text-1);
    transition: border-color 150ms ease, background 150ms ease;
    line-height: 1.5;
    box-sizing: border-box;
}
.form-input::placeholder,
textarea.form-input::placeholder {
    color: var(--text-4);
    font-size: 13px;
}
.form-input:focus,
select.form-input:focus,
textarea.form-input:focus {
    outline: none;
    border-color: var(--border-2);
    background: var(--bg-4);
}
select.form-input option {
    background: var(--bg-3);
    color: var(--text-1);
}

.checkbox-group {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 12px 0;
}

.checkbox-group input[type="checkbox"] {
    width: 14px;
    height: 14px;
    accent-color: var(--text-1);
    cursor: pointer;
}

.checkbox-group label {
    font-size: 12px;
    color: var(--text-3);
    cursor: pointer;
    text-transform: none;
    letter-spacing: 0;
    font-weight: 400;
}

/* ================================================================
   PASSWORD REQUIREMENTS
   ================================================================ */
.password-requirements {
    margin-top: 8px;
    padding: 12px;
    background: var(--bg-3);
    border-radius: var(--r);
    border: 1px solid var(--border);
}

.password-requirements div {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 4px 0;
    color: var(--text-4);
    font-size: 12px;
    transition: color 150ms ease;
    font-family: var(--mono);
}

.password-requirements div.met {
    color: var(--success);
}

.password-requirements div::before {
    content: '–';
    flex-shrink: 0;
    color: var(--text-4);
}

.password-requirements div.met::before {
    content: '✓';
    color: var(--success);
}

/* ================================================================
   MAIN CONTENT
   ================================================================ */
.main-content {
    padding: 20px;
    display: none;
}

.main-content.active {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;   /* without this a flex item refuses to shrink below its content */
    animation: fadeIn 0.2s ease;
}

/* Only the table absorbs the leftover height (R4-01). Everything above it keeps
   its natural size — otherwise a short window squashes the search bar and the
   action buttons instead of the thing that actually scrolls. */
.main-content.active > *:not(.table-container) {
    flex: 0 0 auto;
}

/* ================================================================
   SEARCH BAR
   ================================================================ */
.search-bar {
    background: var(--bg-2);
    padding: 12px 16px;
    border-radius: var(--r-lg);
    margin-bottom: 16px;
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
    border: 1px solid var(--border);
}

.search-bar input {
    flex: 1;
    min-width: 180px;
    padding: 8px 12px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    font-size: 13px;
    color: var(--text-1);
    font-family: var(--font);
    transition: border-color 150ms ease;
}

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

.search-hint {
    font-size: 11px;
    color: var(--text-4);
    flex-basis: 100%;
    font-family: var(--mono);
}

.search-bar .search-bar-icon {
    font-weight: 600;
    color: var(--text-3);
}

/* Monospace-Ausgaben (z. B. UUID) im dunklen Theme */
.input-mono-dark {
    background: var(--bg-3) !important;
    color: var(--text-1) !important;
    border: 1px solid var(--border) !important;
    font-family: var(--mono);
}

.muted-text-secondary {
    color: var(--text-3);
}

/* ================================================================
   ACTION BUTTONS
   ================================================================ */
.action-buttons {
    display: flex;
    gap: 5px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

/* ================================================================
   TABLE
   ================================================================ */
.table-container {
    background: var(--bg-2);
    border-radius: var(--r-lg);
    overflow: auto;
    /* Flex basis, not a fixed size: #mainApp is a flex column (R4-01), so this
       is the starting height and the container grows into whatever the header,
       search bar and action buttons leave over. `min-height` keeps a usable
       window on a very short viewport — below that the page scrolls, which is
       better than a table with no rows in it. */
    height: 400px;
    flex: 1 1 auto;
    min-height: 240px;
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
}

/* Items-Tabelle immer volle Breite füllen — sonst schrumpft sie bei leeren Daten
   auf die Spaltenbreite und der colspan-Empty-State hängt links statt mittig.
   Breitere Tabellen laufen weiterhin über .table-container overflow:auto. */
.table-container table {
    width: 100%;
}

/* Empty-State mittig im Tabellenbereich: horizontal via text-align:center
   (.empty-state), vertikal via hohe Zelle + vertical-align:middle. */
.table-container .empty-state {
    height: 320px;
    vertical-align: middle;
}

@media (max-width: 768px) {
    .table-container {
        overflow: auto;
        height: calc(100vh - 280px);
        /* Mobile keeps the explicit height it has always had (R4-01) — the real
           value comes from `body.mobile-mode .table-container` further down,
           which is `350px !important`. Without `flex: 0 0 auto` the new
           flex-grow from the desktop rule would stretch that height, so the one
           layout that was already viewport-aware would be the one this round
           changed. The page scrolls on mobile; the desktop column does not. */
        flex: 0 0 auto;
        border-radius: var(--r-lg) !important;
    }
}

/* Modals Table Container */
.table-scroll-container {
    max-height: 50vh;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--r);
    margin-bottom: 12px;
    background: transparent;
}

.table-scroll-container table {
    margin-bottom: 0;
    border: none;
    min-width: 100%;
}

/* Opt-out: Tabellen mit dieser Klasse haben keine sticky Header (sonst klebt der
   thead der oberen Tabelle in der Modal-Scroll-Ansicht über der nächsten Sektion). */
.no-sticky-head thead,
.no-sticky-head thead th {
    position: static;
    background: transparent;
    z-index: auto;
}

.table-scroll-container thead th {
    position: sticky;
    top: 0;
    z-index: 10;
    background: var(--bg-3);
    color: var(--text-2);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}

table {
    width: 100%;
    border-collapse: collapse;
}

@media (max-width: 768px) {
    /* Allow table to scroll horizontally instead of overlapping text */
    th,
    td {
        white-space: nowrap;
    }
}

thead {
    background: var(--bg-3);
    position: sticky;
    top: 0;
    z-index: 10;
}

th {
    padding: 10px 14px;
    text-align: left;
    font-size: 11px;
    font-weight: 500;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: 0.07em;
    white-space: nowrap;
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    background: var(--bg-3);
    z-index: 10;
    /* Optional: Use box-shadow instead of border-bottom for sticky elements in collapsed tables if border disappears */
    box-shadow: inset 0 -1px 0 var(--border);
}

td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    font-size: 13px;
    color: var(--text-2);
}

tbody tr {
    cursor: pointer;
    transition: background 100ms ease;
}

tbody tr:hover td {
    background: var(--bg-3);
    color: var(--text-1);
}

/* An empty-state row is not a record: no hand cursor, no hover band (seen
   live 2026-09-23 — "No data loaded" lit up like a clickable row). */
tbody tr:has(> td.empty-state) {
    cursor: default;
}

tbody tr:hover td.empty-state {
    background: transparent;
    color: var(--text-3);
}

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

/* W-11 (web audit 2026-09-23): the 2 px accent sat on EVERY cell, so each cell
   drew its own left bar — dark "dividers" no other row had. One band, one
   accent on the row's first cell. */
tbody tr.selected td {
    background: var(--accent-dim);
    color: var(--text-1);
}

tbody tr.selected td:first-child {
    box-shadow: inset 3px 0 0 var(--text-3);
}

/* ================================================================
   MODAL
   ================================================================ */
.modal {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.75);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    z-index: 1000;
    justify-content: center;
    align-items: center;
    padding: 16px;
}

.modal.active {
    display: flex;
    animation: fadeIn 0.15s ease;
}

.modal-content {
    background: var(--bg-2);
    border-radius: var(--r-xl);
    border: 1px solid var(--border);
    padding: 0;
    max-width: 600px;
    width: 100%;
    max-height: 88vh;
    display: flex;
    flex-direction: column;
    /* Container scrollt NICHT mehr selbst — Header & Footer sind feste Flex-Items,
       nur der .modal-body dazwischen scrollt → Footer deckt den Boden lückenlos,
       kein Durchscheinen/Scrollbar-Gutter-Problem mehr. */
    overflow: hidden;
    box-shadow: var(--shadow-lg);
    animation: slideUp 0.22s var(--ease);
}

.modal-header {
    padding: 18px 22px;
    border-bottom: 1px solid var(--border);
    flex-shrink: 0;
    background: var(--bg-2);
    z-index: 10;
}

.modal-header h3 {
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-1);
}

/* Scrollable middle area of the modal (if modal-body is used) */
.modal-body {
    flex: 1;
    overflow-y: auto;
    min-height: 0;
}

/* Das phase14-Panel bringt eine EIGENE Flex-Spalten-Struktur mit (sticky Tabs +
   fuellender #phase14Content mit eigenem Scroll). _wrapModalBodies() wickelt das in
   eine .modal-body — ohne flex-column waere #phase14Content's flex:1 inert und der
   ganze Body wuerde scrollen statt nur der Content-Bereich. Gezielt wiederherstellen. */
#phase14Modal .modal-body {
    display: flex;
    flex-direction: column;
}

/* Tab-Leiste klebt oben im scrollenden Body (Header ist jetzt außerhalb des Scrolls) */
.modal-body>.tabs {
    flex-shrink: 0;
    position: sticky;
    top: 0;
    background: var(--bg-2);
    z-index: 9;
}

/* Removed broken rule that applied flex:1 and overflow-y:auto to all modal children like .form-group */

.modal-footer {
    padding: 14px 22px;
    border-top: 1px solid var(--border);
    display: flex;
    gap: 8px;
    justify-content: flex-end;
    flex-wrap: wrap;
    background: var(--bg-3);
    flex-shrink: 0;
    z-index: 10;
    /* festes Flex-Item am Boden, volle Breite, untere Ecken wie das Modal gerundet */
    border-radius: 0 0 var(--r-xl) var(--r-xl);
}

/* Horizontal-Scroll-Container für breite Tabellen in den Panel-Tabs (z.B.
   Bewegungen): erzwingt einen SICHTBAREN Scrollbalken. Chrome/Windows blendet
   den Overlay-Scrollbalken sonst aus → die rechten Spalten (z.B. der Storno-
   Button) wirken unerreichbar, obwohl der Bereich scrollbar ist. */
.p14-hscroll {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
    /* W-40: was var(--accent) = white — a glaring white bar in a dark table. */
    scrollbar-color: var(--scrollbar-thumb-hover) var(--bg-3);
}
.p14-hscroll::-webkit-scrollbar {
    height: 12px;
}
.p14-hscroll::-webkit-scrollbar-track {
    background: var(--bg-3);
    border-radius: 6px;
}
.p14-hscroll::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb-hover);
    border-radius: 6px;
    border: 2px solid var(--bg-3);
}

/* W-40: a column was as wide as its unwrapped header ("Bestand nach Buchung"
   took 187px), so the movements table overflowed by ~150px in German and the
   Reverse buttons sat off-screen. Headers wrap now, and the last (action)
   column stays in view when a narrow window still has to scroll. */
.p14-hscroll th {
    white-space: normal;
}
.p14-hscroll tr > :last-child {
    position: sticky;
    right: 0;
    background: var(--bg-2);
}

.modal-body>.form-group,
.modal-body>form>.form-group,
#addCustomFields .form-group,
#dupCustomFields .form-group {
    padding: 0 22px;
}

/* W-57 (web audit 2026-09-23): R6-01 wrapped the change-password fields in a
   <form>, which made them grandchildren of .modal-body — the rule above no
   longer matched and the labels and inputs touched the dialog border. The
   form's alert area gets the same inset. */
.modal-body>form>[id$="AlertContainer"] {
    padding: 0 22px;
}

/* ...and the first field sat ON the header rule: 0 px measured live on
   2026-09-23 in seven dialogs (add item, duplicate, change password, audit,
   create group, add member, add column). The others bring their own inset. */
.modal-body>.form-group:first-child,
.modal-body>form:first-child {
    padding-top: 18px;
}

/* Picker-dialog rows (location tree / item picker) */
.p14-pick-row {
    transition: background 0.1s ease;
}

.p14-pick-row:hover {
    background: var(--bg-hover);
}

.p14-pick-row input[type="checkbox"] {
    flex-shrink: 0;
    cursor: pointer;
}

/* Indeterminate loading bar (e.g. while an inventory run is being created) */
.p14-loading-bar {
    height: 3px;
    margin-top: 8px;
    background: var(--bg-4);
    border-radius: 2px;
    overflow: hidden;
}

.p14-loading-bar>div {
    height: 100%;
    width: 40%;
    background: var(--accent);
    border-radius: 2px;
    animation: p14-loading-slide 1s ease-in-out infinite;
}

@keyframes p14-loading-slide {
    0% {
        margin-left: -40%;
    }

    100% {
        margin-left: 100%;
    }
}

/* Laufender Rahmen als Lade-Feedback an den Import-/Export-Kästen (2026-05-29).
   Der Export geht jetzt über mehrere MQTT-Häppchen und kann ein paar Sekunden
   dauern → sichtbares Feedback. Ein Akzent-Segment wandert per rotierendem
   Conic-Gradient um den Rahmen; die Doppel-Maske lässt nur den 2px-Ring stehen,
   die Kasten-Inhalte bleiben sichtbar. Ohne @property-Support bleibt das Segment
   statisch (immer noch ein klarer „läuft gerade"-Hinweis). */
@property --rx-export-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}

.io-loading-box,
.rx-loading-box {
    position: relative;
}

.io-loading-box.is-loading::before,
.rx-loading-box.is-loading::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 6px;
    padding: 2px;
    background: conic-gradient(from var(--rx-export-angle), var(--accent), transparent 120deg);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    animation: rx-export-border-spin 1.1s linear infinite;
    pointer-events: none;
}

@keyframes rx-export-border-spin {
    to {
        --rx-export-angle: 360deg;
    }
}

@media (prefers-reduced-motion: reduce) {
    .io-loading-box.is-loading::before,
    .rx-loading-box.is-loading::before {
        animation: none;
    }
}

/* ================================================================
   LOADING
   ================================================================ */
.loading {
    display: none;
    text-align: center;
    padding: 48px 20px;
    color: var(--text-3);
}

.loading.active {
    display: block;
}

.spinner {
    border: 2px solid var(--border);
    border-top: 2px solid var(--text-2);
    border-radius: 50%;
    width: 28px;
    height: 28px;
    animation: spin 0.8s linear infinite;
    margin: 0 auto 14px;
}

/* ================================================================
   ALERTS
   ================================================================ */
.alert {
    padding: 10px 14px;
    border-radius: var(--r);
    margin-bottom: 12px;
    display: none;
    font-size: 12px;
    font-weight: 500;
    border: 1px solid;
    animation: slideDown 0.2s var(--ease);
}

.alert.active {
    display: block;
}

/* A floating error must be closable; the container itself lets clicks through. */
.floating-alerts .alert {
    pointer-events: auto;
}

.alert {
    position: relative;
}

.alert-close {
    position: absolute;
    top: 6px;
    right: 8px;
    background: none;
    border: none;
    color: var(--text-2);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
}

.alert-close:hover {
    color: var(--text-1);
}

.alert:has(.alert-close) {
    padding-right: 30px;
}

/* Toasts: dezentes Dunkel-UI (kein grellgrün/-rot) */
/* W-02 (web audit 2026-09-23): an error was grey on grey — it read like an
   input field above the username box. A coloured left edge (restrained, no
   glow) says what kind of message it is; the text stays readable. */
.alert-success {
    background: var(--bg-3);
    color: var(--text-1);
    border-color: var(--border-2);
    border-left: 3px solid var(--success);
}

.alert-error {
    background: var(--danger-bg);
    color: var(--text-1);
    border-color: var(--border-2);
    border-left: 3px solid var(--danger);
}

.alert-info {
    background: var(--bg-3);
    color: var(--text-2);
    border-color: var(--border);
    border-left: 3px solid var(--text-3);
}

.alert-warning {
    background: var(--warning-bg);
    color: var(--text-1);
    border-color: var(--border-2);
    border-left: 3px solid var(--warning);
}

/* ================================================================
   TOAST NOTIFICATIONS
   ================================================================ */
/* W-08 (web audit 2026-09-23): bottom right — at the top every toast sat on
   the header's Logout button for its lifetime. */
.toast-container {
    position: fixed;
    bottom: 20px;
    right: 20px;
    z-index: 10000;
    display: flex;
    flex-direction: column;
    gap: 10px;
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--bg-2);
    border: 1px solid var(--border-2);
    border-radius: var(--r-lg);
    padding: 12px 16px;
    box-shadow: var(--shadow-lg);
    transform: translateX(120%);
    opacity: 0;
    transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease;
}

.toast.active {
    transform: translateX(0);
    opacity: 1;
}

.toast-success .toast-icon {
    color: var(--success);
}

.toast-error .toast-icon {
    color: var(--danger);
}

.toast-info .toast-icon {
    color: var(--info);
}

.toast-msg {
    color: var(--text-1);
    font-size: 13px;
    font-weight: 500;
    letter-spacing: 0.01em;
}

/* ================================================================
   EMPTY STATE
   ================================================================ */
.empty-state {
    text-align: center;
    padding: 60px 24px;
    color: var(--text-4);
}

.empty-state h3 {
    font-size: 13px;
    font-weight: 500;
    margin-bottom: 5px;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.empty-state p {
    font-size: 12px;
    color: var(--text-4);
}

.empty-state-icon {
    font-size: 28px;
    margin-bottom: 16px;
    opacity: 0.2;
    display: block;
}

/* ================================================================
   TABS
   ================================================================ */
.tabs {
    display: flex;
    gap: 1px;
    flex-wrap: wrap;
    background: var(--border);
    border-radius: var(--r);
    padding: 1px;
    margin-bottom: 16px;
}

.tab {
    padding: 7px 16px;
    border: none;
    background: transparent;
    border-radius: 5px;
    cursor: pointer;
    font-family: var(--font);
    font-weight: 500;
    font-size: 12px;
    transition: all 150ms ease;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.tab:hover {
    color: var(--text-1);
    background: var(--bg-3);
}

.tab.active {
    background: var(--bg-4);
    color: var(--text-1);
}

/* W-37 / W-15 / W-31 / W-36 (web audit 2026-09-23): a dialog that opens with a
   tab strip was centred, so every tab of a different height moved the strip
   under the cursor — measured 284 px in the module dialog, 98 px in user
   management, 24 px in the Data Matrix generator. Anchored at the top the
   strip stays put; the three multi-module dialogs also keep ONE height, so
   their footer does not move either. */
.modal:has(> .modal-content > .modal-body > .tabs:first-child) {
    align-items: flex-start;
}

.modal:has(> .modal-content > .modal-body > .tabs:first-child) > .modal-content {
    margin-top: 4vh;
    max-height: calc(96vh - 32px);
}

#phase14Modal .modal-content,
#userManagementModal .modal-content,
#optionsModal .modal-content {
    height: calc(96vh - 32px);
}

/* Ten modules needed 1141 px of 1098 and "HELP" wrapped onto a row of its
   own. One row: slightly narrower tabs, and the strip scrolls sideways when a
   language needs more room (phase14SwitchTab scrolls the active tab in). */
#phase14Tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: thin;
}

#phase14Tabs .tab {
    padding: 7px 12px;
    flex-shrink: 0;
    white-space: nowrap;
}

/* The ring was drawn 2 px OUTSIDE the tab and the dialog edge cut it off. */
body .tabs .tab:focus-visible {
    outline-offset: -2px;
}

.tab-content {
    display: none;
    padding: 18px;
    background: var(--bg-2);
    border-radius: var(--r);
    border: 1px solid var(--border);
}

.tab-content.active {
    display: block;
    animation: fadeIn 0.15s ease;
}

/* ================================================================
   COLUMN LIST
   ================================================================ */
.column-list {
    max-height: 200px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--r);
    margin-bottom: 14px;
    background: var(--bg-3);
}

.column-item {
    padding: 9px 14px;
    border-bottom: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 12px;
    color: var(--text-2);
    transition: background 100ms ease;
}

.column-item:last-child {
    border-bottom: none;
}

.column-item:hover {
    background: var(--bg-4);
}

.column-item.column-hidden {
    opacity: 0.4;
}

/* W-36 (web audit 2026-09-23): inside the Options dialog both column lists
   scrolled in a 200 px box inside the scrolling dialog (4 rows each). The
   dialog has a fixed height now and scrolls once; the lists are just lists. */
#optionsModal .column-list {
    max-height: none;
    overflow: visible;
}

/* At the display limit the row stays readable; only the button is off. The
   old row opacity multiplied with the button's own, to 18 %. */
.column-item.column-at-limit strong {
    color: var(--text-3);
}

.column-item.column-at-limit .btn:disabled {
    opacity: 0.55;
}

.column-source {
    font-size: 11px;
    color: var(--text-3);
}

.options-scope-hint {
    font-size: 12px;
    color: var(--text-3);
    margin-bottom: 6px;
}

/* Dropdown tree editor: name + path take the width, actions form one
   right-aligned column (they started wherever each label ended). */
.opt-tree {
    list-style: none;
    padding: 0;
    margin: 0;
}

.opt-tree-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 3px 6px 3px calc(6px + var(--depth, 0) * 18px);
    border-radius: var(--r);
}

.opt-tree-row:hover,
.opt-tree-row:focus-within {
    background: var(--bg-3);
}

.opt-tree-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.opt-tree-path {
    margin-left: 6px;
    font-size: 11px;
    color: var(--text-3);
}

.opt-tree-actions {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
}

/* W-34: ID/Name cannot be removed; removed columns can be restored. */
.column-locked {
    font-size: 11px;
    color: var(--text-4);
}

.column-removed-title {
    padding: 10px 14px 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-3);
    border-top: 1px solid var(--border-2);
}

.column-item.column-removed > span {
    color: var(--text-3);
}

.column-item.column-selected {
    background: var(--bg-4);
    border-left: 3px solid var(--accent);
}

/* ================================================================
   OPTIONS EDITOR
   ================================================================ */
.options-editor {
    margin-bottom: 16px;
    padding: 14px;
    background: var(--bg-3);
    border-radius: var(--r);
    border: 1px solid var(--border);
}

.options-editor h4 {
    margin-bottom: 9px;
    color: var(--text-2);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
}

.options-editor textarea {
    width: 100%;
    height: 72px;
    resize: vertical;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: 9px 12px;
    font-family: var(--mono);
    font-size: 12px;
    color: var(--text-1);
}

.options-editor textarea:focus {
    outline: none;
    border-color: var(--border-2);
}

/* ================================================================
   AUDIT LOG
   ================================================================ */
.audit-log-container {
    max-height: 400px;
    overflow-y: auto;
}

/* W-32: filter bar + pager. The search box used to touch the header divider. */
.audit-filters {
    padding-top: 12px;
}

.audit-filter-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 8px;
    align-items: center;
}

.audit-filter-row select,
.audit-filter-row input {
    flex: 1 1 130px;
    min-width: 0;
}

.audit-filter-row .btn {
    flex: 0 0 auto;
}

.audit-pager {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-right: auto;
    font-size: 12px;
    color: var(--text-2);
}

.audit-entry {
    padding: 11px 14px;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    transition: background 100ms ease;
}

.audit-entry:last-child {
    border-bottom: none;
}

.audit-entry:hover {
    background: var(--bg-3);
}

.audit-timestamp {
    color: var(--text-4);
    font-size: 11px;
    font-family: var(--mono);
    margin-bottom: 2px;
}

.audit-field {
    font-weight: 600;
    color: var(--text-1);
}

.audit-old {
    color: var(--danger);
    text-decoration: line-through;
    opacity: 0.6;
}

.audit-new {
    color: var(--success);
    font-weight: 500;
}

/* ================================================================
   SCANNER
   ================================================================ */
.input-with-scanner {
    display: flex;
    gap: 7px;
    align-items: center;
}

.input-with-scanner input {
    flex: 1;
}

.btn-scan {
    padding: 0;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    cursor: pointer;
    font-size: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 150ms ease;
    min-width: 38px;
    height: 38px;
    color: var(--text-2);
}

.btn-scan:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

/* ================================================================
   QR GENERATOR
   ================================================================ */
.qr-generator-container {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-top: 16px;
}

.qr-input-section,
.qr-preview-section {
    padding: 18px;
    background: var(--bg-3);
    border-radius: var(--r);
    border: 1px solid var(--border);
}

.qr-preview-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 240px;
}

#qrCodeDisplay {
    background: #fff;
    padding: 14px;
    border-radius: var(--r);
    margin-bottom: 12px;
}

.qr-empty-state {
    text-align: center;
    color: var(--text-4);
    font-size: 13px;
}

.qr-empty-state-icon {
    font-size: 36px;
    margin-bottom: 8px;
    opacity: 0.15;
}

.qr-button-group {
    display: flex;
    gap: 8px;
    margin-top: 12px;
}

/* W-31: the generated ID with its copy button on one line */
.qr-id-row {
    display: flex;
    gap: 6px;
}

.qr-id-row input {
    flex: 1;
    min-width: 0;
}

.qr-id-row .btn {
    flex-shrink: 0;
}

/* ================================================================
   WARNING BANNER
   ================================================================ */
.warning-banner {
    display: none;
    background: var(--bg-3);
    border-bottom: 1px solid var(--border-2);
    padding: 10px 18px;
    text-align: center;
    font-size: 12px;
    color: var(--text-2);
    font-weight: 500;
    position: relative;
    animation: bannerIn 0.3s var(--ease);
    letter-spacing: 0.01em;
}

.warning-banner.active {
    display: block;
}

.warning-banner .banner-close {
    position: absolute;
    top: 50%;
    right: 14px;
    transform: translateY(-50%);
    background: none;
    border: none;
    font-size: 14px;
    cursor: pointer;
    color: var(--text-3);
    padding: 3px 7px;
    border-radius: 4px;
    transition: background 150ms ease;
}

.warning-banner .banner-close:hover {
    background: rgba(255, 255, 255, 0.05);
    color: var(--text-1);
}

/* ================================================================
   EXPIRED SCREEN (Zertifikat / Lizenz)
   ================================================================ */
.expired-screen {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 10050;
    min-height: 100vh;
    justify-content: center;
    align-items: center;
    padding: 20px;
    background: var(--bg);
    box-sizing: border-box;
}

.expired-screen.active {
    display: flex;
}

.expired-card {
    background: var(--bg-2);
    border-radius: var(--r-xl);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
    max-width: 420px;
    width: 100%;
    overflow: hidden;
    animation: slideUp 0.3s var(--ease);
}

.expired-card .expired-header {
    background: var(--bg-3);
    padding: 20px;
    text-align: center;
    border-bottom: 1px solid var(--border);
}

.expired-card .expired-header h1 {
    font-size: 16px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-1);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.expired-card .expired-body {
    padding: 36px 28px;
    text-align: center;
}

.expired-card .expired-icon {
    font-size: 40px;
    margin-bottom: 18px;
    display: block;
    opacity: 0.4;
}

.expired-card .expired-title {
    font-size: 18px;
    font-weight: 600;
    color: var(--text-1);
    margin-bottom: 10px;
}

.expired-card .expired-text {
    font-size: 13px;
    color: var(--text-3);
    line-height: 1.7;
    margin-bottom: 24px;
}

.expired-card .btn-support {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: var(--bg-4);
    color: var(--text-1);
    padding: 10px 24px;
    border: 1px solid var(--border-2);
    border-radius: var(--r);
    font-family: var(--font);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background 150ms ease, border-color 150ms ease;
    text-decoration: none;
}

.expired-card .btn-support:hover {
    background: var(--bg-hover);
    border-color: #3f3f3f;
}

/* ================================================================
   USER MANAGEMENT
   ================================================================ */
.request-card {
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: 14px;
    margin-bottom: 10px;
    transition: border-color 150ms ease;
}

.request-card:hover {
    border-color: var(--border-2);
}

.request-card.pending {
    border-left: 2px solid var(--warning);
}

.request-card.approved {
    border-left: 2px solid var(--success);
}

.request-card.rejected {
    border-left: 2px solid var(--danger);
}

.request-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 6px;
}

.request-username {
    font-size: 14px;
    font-weight: 600;
    color: var(--text-1);
}

.request-status {
    padding: 2px 10px;
    border-radius: 4px;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.request-status.pending {
    background: rgba(250, 204, 21, 0.08);
    color: var(--warning);
}

.request-status.approved {
    background: rgba(74, 222, 128, 0.08);
    color: var(--success);
}

.request-status.rejected {
    background: rgba(248, 113, 113, 0.08);
    color: var(--danger);
}

.request-meta {
    font-size: 11px;
    color: var(--text-4);
    margin-bottom: 8px;
    font-family: var(--mono);
}

.request-actions {
    display: flex;
    gap: 6px;
}

.error-state {
    text-align: center;
    padding: 36px 20px;
    color: var(--text-4);
}

.error-state-icon {
    font-size: 32px;
    margin-bottom: 12px;
    opacity: 0.25;
}

/* ================================================================
   INFO MODAL
   ================================================================ */
.info-modal-content {
    text-align: center;
    padding: 22px;
}

.info-logo {
    font-size: 40px;
    margin-bottom: 14px;
    display: block;
    opacity: 0.7;
}

/* The chemistry-era beaker (16 px, left over from an emoji) became the product mark. */
.info-logo img {
    height: 36px;
    width: auto;
}

.info-grid {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 9px 18px;
    text-align: left;
    padding: 16px;
    background: var(--bg-3);
    border-radius: var(--r);
    border: 1px solid var(--border);
    margin: 14px 0;
}

.info-label {
    font-weight: 500;
    color: var(--text-2);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.07em;
}

.info-value {
    color: var(--text-1);
    font-size: 13px;
}

.info-version {
    font-family: var(--mono);
    font-size: 13px;
    font-weight: 500;
    color: var(--text-2);
}

/* ================================================================
   CODES
   ================================================================ */
.codes-list {
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--bg-3);
}

.code-item {
    padding: 9px 14px;
    border-bottom: 1px solid var(--border);
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    transition: background 100ms ease;
}

.code-item:last-child {
    border-bottom: none;
}

.code-item:hover {
    background: var(--bg-4);
}

.code-info {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    flex: 1;
}

.code-type-badge {
    padding: 2px 9px;
    border-radius: 4px;
    font-size: 10px;
    font-weight: 600;
    white-space: nowrap;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    background: var(--bg-4);
    color: var(--text-3);
    border: 1px solid var(--border);
}

.code-type-barcode {
    background: var(--bg-4);
    color: var(--text-2);
}

.code-type-qr {
    background: var(--bg-4);
    color: var(--text-2);
}

.code-type-datamatrix {
    background: var(--bg-4);
    color: var(--text-2);
}

.code-type-other {
    background: var(--bg-4);
    color: var(--text-3);
}

.code-value {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--text-2);
    word-break: break-all;
}

.code-date {
    font-size: 10px;
    color: var(--text-4);
    font-family: var(--mono);
}

.add-code-form {
    padding: 14px;
    background: var(--bg-3);
    border-radius: var(--r);
    border: 1px solid var(--border);
    margin-top: 8px;
}

/* ================================================================
   IMAGE UPLOAD
   ================================================================ */
.image-upload-zone {
    border: 1px dashed var(--border-2);
    border-radius: var(--r-lg);
    padding: 28px 20px;
    text-align: center;
    cursor: pointer;
    transition: all 150ms ease;
    background: var(--bg-3);
    position: relative;
}

.image-upload-zone:hover {
    border-color: var(--text-3);
    background: var(--bg-4);
}

.image-upload-zone.dragover {
    border-color: var(--text-2);
    background: var(--bg-4);
}

.image-upload-zone .upload-icon {
    font-size: 28px;
    margin-bottom: 8px;
    opacity: 0.3;
}

.image-upload-zone .upload-text {
    color: var(--text-3);
    font-size: 12px;
}

.image-upload-zone input[type="file"] {
    position: absolute;
    inset: 0;
    opacity: 0;
    cursor: pointer;
}

.image-preview-container {
    margin-top: 14px;
    text-align: center;
    position: relative;
    display: inline-block;
}

.image-preview-container img {
    max-width: 180px;
    max-height: 180px;
    border-radius: var(--r);
    border: 1px solid var(--border);
    object-fit: contain;
}

.image-preview-container .remove-image {
    position: absolute;
    top: -7px;
    right: -7px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--bg-4);
    color: var(--text-2);
    border: 1px solid var(--border);
    cursor: pointer;
    font-size: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 150ms ease;
}

.image-preview-container .remove-image:hover {
    background: var(--danger-bg);
    color: var(--danger);
    border-color: rgba(248, 113, 113, 0.3);
}

.table-image-thumb {
    width: 24px;
    height: 24px;
    border-radius: 4px;
    object-fit: cover;
    vertical-align: middle;
    margin-right: 6px;
    border: 1px solid var(--border);
}

/* ================================================================
   ITEM DETAIL
   ================================================================ */
.item-detail-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    margin-bottom: 18px;
    padding: 0 22px;
}

.item-detail-item {
    padding: 9px 12px;
    background: var(--bg-3);
    border-radius: var(--r);
    border: 1px solid var(--border);
}

.item-detail-item label {
    font-size: 10px;
    color: var(--text-2);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    display: block;
    margin-bottom: 3px;
}

.item-detail-item span {
    font-size: 13px;
    color: var(--text-1);
    font-weight: 400;
}

/* ================================================================
   LANGUAGE SWITCHER
   ================================================================ */
.language-switcher {
    position: relative;
    display: inline-block;
}

.language-switcher select {
    appearance: none;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-2);
    padding: 5px 24px 5px 9px;
    font-family: var(--font);
    font-size: 12px;
    cursor: pointer;
    transition: all 150ms ease;
}

.language-switcher select:hover {
    border-color: var(--border-2);
    color: var(--text-1);
}

.language-switcher select:focus {
    outline: none;
    border-color: var(--border-2);
}

.language-switcher::after {
    content: '▾';
    position: absolute;
    right: 7px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 10px;
    color: var(--text-3);
    pointer-events: none;
}

/* ================================================================
   UPLOAD UI
   ================================================================ */
.upload-drop-area {
    border: 1px dashed var(--border-2);
    border-radius: var(--r-lg);
    padding: 28px 20px;
    text-align: center;
    cursor: pointer;
    transition: all 150ms ease;
    background: var(--bg-3);
    margin-bottom: 12px;
}

.upload-drop-area:hover,
.upload-drop-area.dragover {
    border-color: var(--text-3);
    background: var(--bg-4);
}

.upload-drop-area-icon {
    font-size: 28px;
    margin-bottom: 8px;
    opacity: 0.3;
}

.upload-drop-area p {
    color: var(--text-3);
    font-size: 12px;
}

.file-list {
    max-height: 280px;
    overflow-y: auto;
}

.file-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 9px 12px;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    color: var(--text-2);
    transition: background 100ms ease;
}

.file-item:last-child {
    border-bottom: none;
}

.file-item:hover {
    background: var(--bg-3);
    color: var(--text-1);
}

.file-item-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
}

.file-item-name {
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.file-item-meta {
    font-size: 10px;
    color: var(--text-4);
    font-family: var(--mono);
}

.file-item-actions {
    display: flex;
    gap: 5px;
    flex-shrink: 0;
    margin-left: 8px;
}

.upload-progress {
    height: 2px;
    background: var(--border);
    border-radius: 2px;
    margin-top: 8px;
    overflow: hidden;
    display: none;
}

.upload-progress.active {
    display: block;
}

.upload-progress-bar {
    height: 100%;
    background: var(--text-2);
    border-radius: 2px;
    transition: width 0.3s;
    width: 0%;
}

.upload-usage {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 9px 12px;
    background: var(--bg-3);
    border-radius: var(--r);
    font-size: 11px;
    color: var(--text-3);
    margin-bottom: 12px;
    border: 1px solid var(--border);
    font-family: var(--mono);
}

.info-icon-cell {
    text-align: center;
    cursor: pointer;
    font-size: 13px;
    user-select: none;
    color: var(--text-3);
    transition: color 150ms ease;
}

.info-icon-cell:hover {
    color: var(--text-1);
}

.upload-limit-input {
    display: flex;
    align-items: center;
    gap: 7px;
}

.upload-limit-input input {
    width: 72px;
    padding: 5px 9px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: 4px;
    font-family: var(--mono);
    font-size: 13px;
    color: var(--text-1);
    text-align: center;
}

/* ================================================================
   TAG EDITOR
   ================================================================ */
.tag-editor {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    padding: 8px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    min-height: 40px;
    margin-bottom: 7px;
}

.tag-item {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 2px 9px;
    background: var(--bg-4);
    border: 1px solid var(--border-2);
    color: var(--text-2);
    border-radius: 4px;
    font-size: 11px;
    font-weight: 500;
}

.tag-item .tag-text {
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tag-item .tag-delete {
    background: none;
    border: none;
    color: var(--text-4);
    cursor: pointer;
    font-size: 11px;
    padding: 0 3px;
    transition: color 150ms ease;
}

.tag-item .tag-delete:hover {
    color: var(--text-1);
}

.tag-empty {
    color: var(--text-4);
    font-size: 11px;
    font-style: italic;
}

.tag-input-row {
    display: flex;
    gap: 7px;
}

.tag-input-row input {
    flex: 1;
}

/* ================================================================
   DETAIL SECTION
   ================================================================ */
.detail-section {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}

.detail-section h4 {
    color: var(--text-2);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 7px;
}

/* ================================================================
   RESPONSIVE
   ================================================================ */
@media (max-width: 768px) {
    body {
        padding: 8px;
    }

    .header {
        flex-direction: column;
        text-align: center;
        padding: 12px;
    }

    .header h1 {
        font-size: 13px;
    }

    .search-bar {
        flex-direction: column;
    }

    .search-bar input {
        min-width: 100%;
    }

    .action-buttons {
        justify-content: center;
    }

    .btn {
        flex: 1 1 calc(50% - 3px);
        justify-content: center;
        font-size: 11px;
        padding: 7px 8px;
    }

    .modal-content {
        max-width: 100%;
    }

    th,
    td {
        padding: 8px 10px;
        font-size: 12px;
    }

    .warning-banner {
        padding: 10px 40px 10px 14px;
        text-align: left;
    }

    .expired-card .expired-body {
        padding: 24px 18px;
    }

    .qr-generator-container {
        grid-template-columns: 1fr;
    }

    .item-detail-grid {
        grid-template-columns: 1fr;
        padding: 0 16px;
    }

    .login-container {
        padding: 24px 18px;
        margin: 0 auto;
    }
}

@media (max-width: 480px) {
    body {
        padding: 0;
    }

    .container {
        border-radius: 0;
        border-left: none;
        border-right: none;
    }

    .btn {
        flex: 1 1 100%;
    }
}

/* ================================================================
   RBAC — Groups, Permissions, Visibility
   ================================================================ */

/* Group Grid */
.group-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 12px;
}

/* Group Card */
.group-card {
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: 16px;
    cursor: pointer;
    transition: all 180ms ease;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.group-card:hover {
    border-color: var(--border-2);
    background: var(--bg-4);
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.group-card-header {
    display: flex;
    align-items: center;
    gap: 8px;
}

.group-card-icon {
    font-size: 18px;
    opacity: 0.7;
}

.group-card-name {
    font-weight: 600;
    font-size: 14px;
    color: var(--text-1);
    letter-spacing: 0.02em;
}

.group-card-desc {
    font-size: 12px;
    color: var(--text-3);
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.group-card-meta {
    display: flex;
    align-items: center;
    gap: 10px;
}

.group-card-members {
    font-size: 11px;
    color: var(--text-2);
    font-family: var(--mono);
}

.group-card-perms {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin-top: 2px;
}

/* Permission Badges */
.perm-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 2px 7px;
    border-radius: 3px;
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.02em;
    background: var(--accent-dim);
    color: var(--text-2);
    border: 1px solid var(--border);
}

.perm-badge.perm-read {
    color: var(--info);
    border-color: rgba(96, 165, 250, 0.2);
    background: var(--info-bg);
}

.perm-badge.perm-write {
    color: var(--success);
    border-color: rgba(74, 222, 128, 0.2);
    background: var(--success-bg);
}

.perm-badge.perm-public_entry {
    color: var(--warning);
    border-color: rgba(250, 204, 21, 0.2);
    background: var(--warning-bg);
}

.perm-badge.perm-full_access {
    color: #c084fc;
    border-color: rgba(192, 132, 252, 0.2);
    background: rgba(192, 132, 252, 0.06);
}

/* W-55: "No rights" was grey on grey — a state worth seeing */
.perm-badge.perm-none {
    color: var(--warning);
    border-color: rgba(250, 204, 21, 0.3);
    background: var(--warning-bg);
}

/* Role Badges */
.role-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px 8px;
    border-radius: 3px;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.role-badge.role-admin {
    background: rgba(192, 132, 252, 0.1);
    color: #c084fc;
    border: 1px solid rgba(192, 132, 252, 0.2);
}

.role-badge.role-user {
    background: var(--accent-dim);
    color: var(--text-2);
    border: 1px solid var(--border);
}

/* Group Detail */
.group-detail {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.group-detail-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}

.group-detail-title {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
}

.group-detail-icon {
    font-size: 24px;
    opacity: 0.7;
}

.group-detail-title h3 {
    font-size: 16px;
    font-weight: 600;
    color: var(--text-1);
    margin: 0;
    text-transform: none;
    letter-spacing: 0;
}

.group-detail-desc {
    font-size: 12px;
    color: var(--text-3);
    display: block;
}

.group-detail-actions {
    display: flex;
    gap: 6px;
}

/* Group Section */
.group-section {
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
}

.group-section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
    gap: 8px;
}

.group-section-header h4 {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-1);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin: 0;
}

/* Permission Matrix */
.permission-matrix {
    display: flex;
    flex-direction: column;
}

.permission-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border);
    transition: background 100ms ease;
}

.permission-row:last-child {
    border-bottom: none;
}

.permission-row:hover {
    background: var(--bg-4);
}

.permission-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.permission-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-1);
}

.permission-desc {
    font-size: 11px;
    color: var(--text-3);
}

/* Toggle Switch */
.permission-toggle {
    position: relative;
    display: inline-block;
    width: 40px;
    height: 22px;
    flex-shrink: 0;
}

.permission-toggle input {
    opacity: 0;
    width: 0;
    height: 0;
}

.toggle-slider {
    position: absolute;
    cursor: pointer;
    inset: 0;
    background: var(--bg-4);
    border: 1px solid var(--border-2);
    border-radius: 22px;
    transition: all 200ms ease;
}

.toggle-slider::before {
    content: '';
    position: absolute;
    height: 16px;
    width: 16px;
    left: 2px;
    bottom: 2px;
    background: var(--text-3);
    border-radius: 50%;
    transition: all 200ms ease;
}

.permission-toggle input:checked+.toggle-slider {
    background: var(--bg-hover);
    border-color: var(--text-2);
}

.permission-toggle input:checked+.toggle-slider::before {
    transform: translateX(18px);
    background: var(--text-1);
}

/* Member Role Select */
.member-role-select {
    padding: 3px 6px;
    border-radius: 4px;
    font-size: 11px;
    background: var(--bg-3);
    color: var(--text-1);
    border: 1px solid var(--border);
    /* R5-01: same blanket `outline: none` as the header language selector —
       removed. This dropdown changes a member's role; losing track of which one
       has focus is not a cosmetic problem. */
    cursor: pointer;
    font-family: var(--font);
}

.member-role-select:hover {
    border-color: var(--border-2);
}

.member-role-select option {
    background: var(--bg-3);
    color: var(--text-1);
}

/* Members List */
/* W-55: a 300 px inner scroll inside the scrolling dialog; the first row slid
   under the search field. The dialog scrolls once. */
.members-list {
    overflow: visible;
}

/* RBAC Empty State */
.rbac-empty-state {
    text-align: center;
    padding: 40px 20px;
    color: var(--text-3);
}

.rbac-empty-state .rbac-empty-icon {
    font-size: 32px;
    opacity: 0.2;
    margin-bottom: 12px;
}

.rbac-empty-state h3 {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-3);
    margin-bottom: 4px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.rbac-empty-state p {
    font-size: 12px;
    color: var(--text-4);
}

/* ================================================================
   VISIBILITY DROPDOWN
   ================================================================ */
.visibility-dropdown {
    position: relative;
    display: inline-block;
}

.visibility-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--bg-3);
    color: var(--text-2);
    font-family: var(--font);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 150ms ease;
    white-space: nowrap;
}

.visibility-trigger:hover {
    background: var(--bg-hover);
    border-color: var(--border-2);
    color: var(--text-1);
}

.visibility-count {
    padding: 1px 6px;
    background: var(--accent-dim2);
    border-radius: 3px;
    font-size: 10px;
    font-weight: 600;
    color: var(--text-1);
}

.visibility-arrow {
    font-size: 8px;
    opacity: 0.5;
    transition: transform 150ms ease;
}

.visibility-options {
    position: absolute;
    bottom: calc(100% + 6px);
    /* rechtsbündig zum Trigger (der im Footer rechts sitzt) — sonst läuft das
       min-width:300px-Popover über den rechten Modal-/Viewport-Rand hinaus. */
    right: 0;
    max-width: calc(100vw - 32px);
    background: var(--bg-2);
    border: 1px solid var(--border-2);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);
    min-width: 300px;
    max-height: 360px;
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 100;
    padding: 6px;
    animation: slideDown 0.15s var(--ease);
}

.visibility-search {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border-2);
    border-radius: var(--r);
    background: var(--bg-3);
    color: var(--text-1);
    font-size: 13px;
    margin-bottom: 6px;
    box-sizing: border-box;
}

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

.visibility-search::placeholder {
    color: var(--text-3);
}

.visibility-option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 7px 10px;
    border-radius: var(--r);
    font-size: 12px;
    color: var(--text-2);
    cursor: pointer;
    transition: background 100ms ease;
}

.visibility-option:hover {
    background: var(--bg-4);
    color: var(--text-1);
}

.visibility-option input[type="checkbox"] {
    width: 14px;
    height: 14px;
    accent-color: var(--text-1);
    cursor: pointer;
    flex-shrink: 0;
}

.visibility-divider {
    height: 1px;
    background: var(--border);
    margin: 4px 0;
}

/* Groups search inside tab */
.groups-toolbar {
    display: flex;
    gap: 10px;
    padding: 0 0 14px 0;
    align-items: center;
    flex-wrap: wrap;
    position: relative;
}

.groups-toolbar input {
    flex: 1;
    min-width: 160px;
    padding: 6px 10px;
    font-size: 12px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-1);
    font-family: var(--font);
}

.groups-toolbar input:focus {
    outline: none;
    border-color: var(--border-2);
}

.groups-toolbar .group-user-search {
    flex: 1;
    min-width: 160px;
    padding: 6px 10px;
    font-size: 12px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-1);
    font-family: var(--font);
}

.member-search-container {
    padding: 8px 0;
}

.member-search-input {
    width: 100%;
    padding: 8px 12px;
    font-size: 13px;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-1);
    font-family: var(--font);
    box-sizing: border-box;
}

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

.user-dropdown-item {
    padding: 8px 12px;
    cursor: pointer;
    color: var(--text-1);
    font-size: 13px;
}

#groupUserSearchDropdown {
    position: fixed;
    min-width: 220px;
    max-height: 300px;
    overflow-y: auto;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    z-index: 10000;
}

#groupUserSearchDropdown .user-dropdown-item {
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
}

#groupUserSearchDropdown .user-dropdown-item:last-child {
    border-bottom: none;
}

#groupUserSearchDropdown .user-dropdown-item:hover {
    background: var(--bg-3);
}

/* Responsive: Group Grid */
@media (max-width: 600px) {
    .group-grid {
        grid-template-columns: 1fr;
    }

    .group-detail-header {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* ================================================================
   MY GROUPS — Normal User View (Read-Only)
   ================================================================ */

/* Group card for normal members */
.my-group-card {
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    margin-bottom: 16px;
    animation: slideUp 0.25s var(--ease);
}

.my-group-card-header {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-4);
}

.my-group-card-icon {
    font-size: 24px;
    opacity: 0.8;
    flex-shrink: 0;
}

.my-group-card-info {
    flex: 1;
    min-width: 0;
}

.my-group-card-info h3 {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-1);
    margin: 0;
    text-transform: none;
    letter-spacing: 0.01em;
}

.my-group-card-info p {
    font-size: 12px;
    color: var(--text-3);
    margin: 2px 0 0;
    line-height: 1.4;
}

.my-group-role-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 500;
    background: var(--accent-dim);
    color: var(--text-2);
    border: 1px solid var(--border);
    white-space: nowrap;
    flex-shrink: 0;
}

/* Members container inside my-group-card */
.my-group-members-container {
    padding: 0;
}

.my-group-members-header {
    padding: 12px 20px;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.my-group-members-list {
    display: flex;
    flex-direction: column;
    max-height: 280px;
    overflow-y: auto;
}

.my-group-member-row {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 20px;
    border-bottom: 1px solid var(--border);
    transition: background 100ms ease;
}

.my-group-member-row:last-child {
    border-bottom: none;
}

.my-group-member-row:hover {
    background: var(--bg-4);
}

.my-group-member-avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--bg-hover);
    border: 1px solid var(--border-2);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 600;
    color: var(--text-1);
    flex-shrink: 0;
    text-transform: uppercase;
    font-family: var(--font);
}

.my-group-member-info {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.my-group-member-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--text-1);
}

.my-group-member-role {
    font-size: 11px;
    color: var(--text-3);
}

.my-group-member-role.is-admin {
    color: #c084fc;
}

.my-group-members-empty {
    text-align: center;
    padding: 24px 20px;
    color: var(--text-3);
}

.my-group-members-empty span {
    font-size: 24px;
    display: block;
    margin-bottom: 8px;
    opacity: 0.3;
}

.my-group-members-empty p {
    font-size: 12px;
    color: var(--text-4);
}

/* ================================================================
   GROUP ADMIN — Tabbed View (Gruppen & Rechte)
   ================================================================ */

.ga-tab-content {
    display: none;
    padding: 16px 20px;
    animation: fadeIn 0.2s ease;
}

.ga-tab-content.active {
    display: block;
}

/* Permission group card for group admin */
.ga-perm-group {
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
    margin-bottom: 16px;
    animation: slideUp 0.25s var(--ease);
}

.ga-perm-group-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 16px;
    background: var(--bg-4);
    border-bottom: 1px solid var(--border);
}

.ga-perm-group-header span {
    font-size: 16px;
}

.ga-perm-group-header h4 {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-1);
    margin: 0;
    text-transform: none;
    letter-spacing: 0.01em;
}

/* Responsive */
@media (max-width: 600px) {
    .my-group-card-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }

    .my-group-member-row {
        padding: 8px 14px;
    }
}

/* ================================================================
   ACTIVE DIRECTORY — Login & Admin Styles
   ================================================================ */

/* AD login button. W-56/W-64 (web audit 2026-09-23): was a saturated
   "Windows blue" gradient with a glow — the only colour in a monochrome
   product. Same look as the local Login button now. */
.btn-ad {
    background: var(--bg-4);
    color: var(--text-1);
    border: 1px solid var(--border-2);
    font-weight: 600;
    transition: all 0.3s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
}

.btn-ad:hover {
    background: var(--bg-hover);
    border-color: #3f3f3f;
    color: var(--text-1);
}

.btn-ad:active {
    background: var(--bg-3);
}

/* Login Divider ("or") */
.login-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 14px 0;
    color: var(--text-4);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.login-divider::before,
.login-divider::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--border);
}

/* AD Status Badge */
.ad-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 10px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.03em;
}

.ad-status-badge.status-connected {
    background: rgba(74, 222, 128, 0.1);
    color: var(--success);
    border: 1px solid rgba(74, 222, 128, 0.25);
}

.ad-status-badge.status-error {
    background: rgba(248, 113, 113, 0.1);
    color: var(--danger);
    border: 1px solid rgba(248, 113, 113, 0.25);
}

.ad-status-badge.status-disabled,
.ad-status-badge.status-loading {
    background: var(--accent-dim);
    color: var(--text-3);
    border: 1px solid var(--border);
}

.ad-status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    display: inline-block;
}

/* No glow halos on the dots (restrained UI rule). */
.status-connected .ad-status-dot {
    background: var(--success);
}

.status-error .ad-status-dot {
    background: var(--danger);
}

.status-disabled .ad-status-dot,
.status-loading .ad-status-dot {
    background: var(--text-4);
}

/* W-56: the reason of an error, on the card itself. */
.ad-status-reason {
    font-size: 12px;
    color: var(--text-2);
}

.ad-ssl-warning {
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-2);
    padding: 8px 12px;
    border: 1px solid var(--border-2);
    border-left: 3px solid var(--warning);
    border-radius: var(--r);
    background: var(--bg-3);
    margin-bottom: 8px;
}

.ad-ssl-warning[hidden] {
    display: none;
}

/* AD Config Form */
.ad-config-section {
    padding: 20px;
}

.ad-config-status-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 18px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    margin-bottom: 20px;
    flex-wrap: wrap;
    gap: 10px;
}

.ad-config-status-info {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    color: var(--text-2);
}

.ad-config-status-info .ad-host {
    font-family: var(--mono);
    font-size: 12px;
    color: var(--text-3);
}

.ad-config-form {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.ad-config-form .form-group {
    margin-bottom: 0;
}

.ad-config-form .form-group.full-width {
    grid-column: 1 / -1;
}

.ad-config-form label {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
    margin-bottom: 4px;
    display: block;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.ad-config-form input,
.ad-config-form select {
    width: 100%;
    padding: 8px 12px;
    font-size: 13px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-1);
    font-family: var(--font);
    box-sizing: border-box;
}

.ad-config-form input:focus {
    outline: none;
    border-color: var(--border-2);
    box-shadow: 0 0 0 2px var(--accent-dim2);
}

.ad-config-actions {
    display: flex;
    gap: 10px;
    margin-top: 20px;
    flex-wrap: wrap;
}

/* AD Enable Toggle */
.ad-toggle-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 18px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    margin-bottom: 20px;
}

.ad-toggle-label {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.ad-toggle-label strong {
    font-size: 13px;
    color: var(--text-1);
}

.ad-toggle-label span {
    font-size: 11px;
    color: var(--text-3);
}

/* AD Users Table */
.ad-users-section {
    margin-top: 24px;
}

.ad-users-section h4 {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: 12px;
}

.ad-user-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 7px;
    border-radius: 3px;
    font-size: 10px;
    font-weight: 500;
    background: var(--accent-dim);
    color: var(--text-2);
    border: 1px solid var(--border-2);
}

/* AD Login Form specific */
.ad-login-back {
    background: none;
    border: none;
    color: var(--text-3);
    font-size: 12px;
    cursor: pointer;
    padding: 8px 0;
    display: flex;
    align-items: center;
    gap: 4px;
    transition: color 0.2s;
    font-family: var(--font);
}

.ad-login-back:hover {
    color: var(--text-1);
}

@media (max-width: 600px) {
    .ad-config-form {
        grid-template-columns: 1fr;
    }

    .ad-config-status-bar {
        flex-direction: column;
        align-items: flex-start;
    }
}

/* ================================================================
   LDAP BROWSER
   ================================================================ */

.ldap-browser-section {
    margin-top: 24px;
    background: var(--bg-3);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    overflow: hidden;
}

.ldap-browser-section h4 {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-2);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    padding: 14px 18px;
    margin: 0;
    border-bottom: 1px solid var(--border);
    background: var(--bg-4);
}

.ldap-browser-tabs {
    display: flex;
    border-bottom: 1px solid var(--border);
    background: var(--bg-3);
}

.ldap-tab {
    flex: 1;
    padding: 10px 16px;
    font-size: 12px;
    font-weight: 500;
    color: var(--text-3);
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    font-family: var(--font);
    transition: all 0.2s ease;
}

.ldap-tab:hover {
    color: var(--text-1);
    background: var(--bg-4);
}

.ldap-tab.active {
    color: var(--text-1);
    border-bottom-color: var(--text-1);
    background: var(--bg-4);
}

.ldap-tab-content {
    padding: 16px;
}

.ldap-search-bar {
    display: flex;
    gap: 8px;
    margin-bottom: 14px;
}

.ldap-search-bar input {
    flex: 1;
    padding: 8px 12px;
    font-size: 13px;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-1);
    font-family: var(--font);
    box-sizing: border-box;
}

.ldap-search-bar input:focus {
    outline: none;
    border-color: var(--border-2);
    box-shadow: 0 0 0 2px var(--accent-dim2);
}

.btn-small {
    padding: 6px 12px !important;
    font-size: 12px !important;
}

.ldap-results {
    max-height: 400px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.ldap-result-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 14px;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    transition: all 0.15s ease;
    gap: 12px;
}

.ldap-result-item:hover {
    border-color: var(--border-2);
    background: var(--bg-4);
}

.ldap-result-info {
    flex: 1;
    min-width: 0;
}

.ldap-result-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-1);
    margin-bottom: 2px;
}

.ldap-result-display {
    font-weight: 400;
    color: var(--text-3);
    font-size: 12px;
}

.ldap-result-meta {
    font-size: 11px;
    color: var(--text-3);
    margin-bottom: 2px;
}

.ldap-result-dn {
    font-size: 10px;
    color: var(--text-4);
    font-family: var(--mono);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 350px;
}

.ldap-result-actions {
    display: flex;
    gap: 6px;
    align-items: center;
    flex-shrink: 0;
}

.ldap-imported-badge {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 4px 10px;
    border-radius: 20px;
    font-size: 11px;
    font-weight: 500;
    background: rgba(74, 222, 128, 0.1);
    color: var(--success);
    border: 1px solid rgba(74, 222, 128, 0.2);
    white-space: nowrap;
}

.ldap-empty-hint {
    text-align: center;
    padding: 30px 20px;
    color: var(--text-3);
    font-size: 13px;
}

.ldap-loading {
    text-align: center;
    padding: 24px;
    color: var(--text-3);
    font-size: 13px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
}

.ldap-error {
    text-align: center;
    padding: 20px;
    color: var(--danger);
    font-size: 13px;
}

/* LDAP Import Preview */
.ldap-preview-header {
    margin-bottom: 14px;
}

.ldap-preview-header h4 {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-1);
    margin: 0 0 4px;
}

.ldap-preview-header p {
    font-size: 12px;
    color: var(--text-3);
    margin: 0;
}

.ldap-preview-members {
    border: 1px solid var(--border);
    border-radius: var(--r);
    overflow: hidden;
}

.ldap-preview-member {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-3);
}

.ldap-preview-member:last-child {
    border-bottom: none;
}

.ldap-preview-member:hover {
    background: var(--bg-4);
}

.ldap-preview-avatar {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--bg-hover);
    border: 1px solid var(--border-2);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-1);
    flex-shrink: 0;
    text-transform: uppercase;
    font-family: var(--font);
}

.ldap-preview-member-info {
    display: flex;
    flex-direction: column;
    gap: 1px;
    flex: 1;
    min-width: 0;
}

.ldap-preview-member-info strong {
    font-size: 13px;
    color: var(--text-1);
}

.ldap-preview-member-info span {
    font-size: 11px;
    color: var(--text-3);
}

/* Toggle Switch */
.toggle-switch {
    position: relative;
    display: inline-block;
    width: 44px;
    height: 24px;
    flex-shrink: 0;
}

.toggle-switch input {
    opacity: 0;
    width: 0;
    height: 0;
}

.toggle-slider {
    position: absolute;
    cursor: pointer;
    inset: 0;
    background: var(--bg-4);
    border: 1px solid var(--border-2);
    border-radius: 24px;
    transition: all 0.3s ease;
}

.toggle-slider::before {
    content: '';
    position: absolute;
    height: 18px;
    width: 18px;
    left: 2px;
    bottom: 2px;
    background: var(--text-3);
    border-radius: 50%;
    transition: all 0.3s ease;
}

.toggle-switch input:checked+.toggle-slider {
    background: var(--text-2);
    border-color: var(--text-2);
}

.toggle-switch input:checked+.toggle-slider::before {
    transform: translateX(20px);
    background: #fff;
}

/* ================================================================
   MOBILE MODE OPTIMIZATIONS (Aggressive Overrides)
   ================================================================ */

body.mobile-mode {
    --app-pad: 0px;   /* keeps #mainApp's calc(100vh - 2 * --app-pad) honest */
    padding: 0 !important;
}

body.mobile-mode .header {
    padding: 12px 12px !important;
}

body.mobile-mode .header h1 {
    font-size: 1.2rem !important;
}

body.mobile-mode .user-info {
    width: 100% !important;
    justify-content: center !important;
    flex-direction: column !important;
    align-items: stretch !important;
    gap: 8px !important;
    background: transparent !important;
    border: none !important;
}

body.mobile-mode .user-info .btn {
    width: 100% !important;
    justify-content: center !important;
    padding: 10px !important;
}

/* Hide admin-only or less critical mobile elements with absolute priority */
body.mobile-mode #userManagementBtn,
body.mobile-mode button[onclick*="showUserManagement"],
body.mobile-mode .action-buttons .btn-purple,
body.mobile-mode .action-buttons .btn-warning,
body.mobile-mode .action-buttons .btn-secondary[onclick*="showInfoModal"],
body.mobile-mode .action-buttons button[onclick*="showOptionsModal"],
body.mobile-mode .action-buttons button[onclick*="showAuditLog"],
body.mobile-mode .action-buttons button[onclick*="showInfoModal"] {
    display: none !important;
}

/* Removed ta language option on mobile via JS in checkMobileMode */

body.mobile-mode .input-with-scanner {
    display: flex !important;
    width: 100% !important;
    position: relative !important;
    min-width: unset !important;
}

body.mobile-mode .input-with-scanner input {
    width: 100% !important;
    padding-right: 48px !important;
    /* Space for the floating camera button */
}

/* Fix: Kamera-Button IN das Suchfeld legen */
body.mobile-mode .btn-scan {
    position: absolute !important;
    right: 4px !important;
    top: 50% !important;
    transform: translateY(-50%) !important;
    border: none !important;
    background: transparent !important;
    height: 32px !important;
    width: 32px !important;
    z-index: 5 !important;
}

body.mobile-mode .btn-search {
    width: 100% !important;
    margin-top: 5px !important;
}

body.mobile-mode .action-buttons {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 8px !important;
    padding: 8px !important;
}

body.mobile-mode .action-buttons .btn {
    width: 100% !important;
    padding: 12px 6px !important;
    font-size: 12px !important;
    flex: unset !important;
}

/* Show multi-select actions on mobile */
body.mobile-mode #btnAppDelete {
    /* Takes the 4th spot in the grid */
}

body.mobile-mode #btnAppDuplicate {
    grid-column: 1 / -1 !important;
}

body.mobile-mode .table-container {
    height: 350px !important;
    border-radius: var(--r-lg) !important;
}

/* Remove top padding so header border touches screen edge on mobile */
body.mobile-mode .main-content {
    padding-top: 8px !important;
}

/* Show only essential columns on mobile table - show name, quantity, unit (first 3) */
/* JS now dynamically controls visible columns, CSS hiding is removed to allow user configuration */

body.mobile-mode .modal-content {
    width: 98% !important;
    margin: 5px auto !important;
}

/* ================================================================
   CUSTOM SELECT (In-flow Dropdown)
   ================================================================ */
.custom-select-container {
    position: relative;
    width: 100%;
    user-select: none;
    cursor: pointer;
}

.custom-select-display {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 12px;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-radius: var(--r);
    color: var(--text-1);
    transition: border-color 0.2s var(--ease);
    font-size: 14px;
}

.custom-select-container.open .custom-select-display {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
    border-color: var(--border-2);
}

.custom-select-arrow {
    transition: transform 0.3s var(--ease);
    color: var(--text-2);
}

.custom-select-container.open .custom-select-arrow {
    transform: rotate(180deg);
}

.custom-select-options {
    display: none;
    background: var(--bg-2);
    border: 1px solid var(--border);
    border-top: none;
    border-radius: 0 0 var(--r) var(--r);
    overflow: hidden;
    /* Removed absolute positioning to allow it to push content down */
}

.custom-select-container.open .custom-select-options {
    display: block;
    animation: fadeIn 0.2s var(--ease);
}

.custom-select-option {
    padding: 10px 12px;
    color: var(--text-1);
    transition: background 0.2s;
    font-size: 14px;
}

.custom-select-option:hover {
    background: var(--bg-hover);
}

/* ================================================================
   EMPTY STATE FOR TABLES
   ================================================================ */
.empty-state {
    text-align: center !important;
    padding: 60px 20px !important;
    color: var(--text-3);
}

.empty-state .empty-state-icon {
    color: var(--text-4);
    margin-bottom: 16px;
}

.empty-state .empty-state-icon svg {
    width: 32px;
    height: 32px;
    opacity: 0.6;
}

.empty-state h3 {
    color: var(--text-2);
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 8px;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}

.empty-state p {
    color: var(--text-3);
    font-size: 13px;
    margin: 0;
}

/* Schritt 3 (Enterprise-Hardening): Hover-/Active-Effekt für klickbare Dashboard-KPI-Kacheln */
.phase14-kpi-tile:hover {
    background: var(--bg-2, var(--bg-3));
    border-color: var(--accent, #3b82f6);
}
.phase14-kpi-tile:active {
    transform: translateY(1px);
}

/* ================================================================
   KEYBOARD FOCUS — R5-01 (audit round 5)
   ================================================================
   One ring for the whole application, deliberately LAST in the file and
   deliberately specific enough to outrank the `outline: none` declarations
   above it.

   Why one generic rule rather than repairing twelve: the twelve are not a
   style, they are the same mistake twelve times. Each one removed the
   browser's ring and (at best) put back a `--border` → `--border-2` change
   worth **1.14:1** — measured at the running system, on the login mask.
   Repairing them one by one leaves the thirteenth to be written tomorrow.

   `:focus-visible`, not `:focus`, so a mouse click on a button does not draw a
   ring — that is the behaviour people expect, and it is why the original
   `outline: none` was reached for in the first place. Keyboard users get the
   ring; mouse users see nothing change.

   Specificity: `body :is(…):focus-visible` computes to (0,2,2), which beats
   `.form-group input:focus` and `.ad-config-form input:focus` at (0,2,1).
   The two rules that carried `outline: none` on their BASE declaration —
   `.header .user-info .header-lang-select` (0,3,0) and `.member-role-select` —
   could not be outranked this way and had the declaration removed instead.

   `outline-offset` keeps the ring off the control's own border so it stays
   readable on the dark surfaces; `outline` does not affect layout, so nothing
   shifts when it appears. */
body :is(
    input:not([type="hidden"]),
    select,
    textarea,
    button,
    a[href],
    [tabindex]:not([tabindex="-1"]),
    [role="button"],
    [role="tab"]
):focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

/* Table rows are the one focus target that must NOT take an offset ring: the
   rows sit flush against each other and against the sticky header, so an
   offset ring is clipped on both edges. Inset instead (R3-08 made these rows
   focusable in the first place). */
body #tableBody tr:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: -2px;
}

/* ================================================================
   USER MANAGEMENT (W-52 / W-53 / W-54, web audit 2026-09-23)
   ================================================================ */
.badge {
    display: inline-block;
    font-size: 10px;
    line-height: 1.4;
    padding: 1px 6px;
    border-radius: 3px;
    border: 1px solid var(--border-2);
    color: var(--text-2);
    background: var(--bg-3);
    vertical-align: middle;
    white-space: nowrap;
}

.badge-muted {
    margin-left: 5px;
}

.badge-ok {
    color: var(--success);
    border-color: rgba(74, 222, 128, 0.35);
    background: var(--success-bg);
}

.badge-danger {
    color: var(--danger);
    border-color: rgba(248, 113, 113, 0.4);
    background: var(--danger-bg);
}

.user-role-select {
    padding: 2px 4px;
    font-size: 11px;
    border-radius: 4px;
}

.user-role-note,
.user-self-note {
    font-size: 11px;
    color: var(--text-2);
    margin-top: 2px;
}

.user-actions {
    display: flex;
    gap: 6px;
    flex-wrap: nowrap;   /* stacked, the three buttons tripled every row's height */
    align-items: center;
    white-space: nowrap;
}

.user-row-disabled td {
    color: var(--text-3);
}

.users-count {
    font-size: 12px;
    color: var(--text-2);
    white-space: nowrap;
}

.admin-requests-panel {
    margin: 12px 22px 0;
    padding: 10px 12px;
    border: 1px solid var(--border-2);
    border-left: 3px solid var(--warning);
    border-radius: var(--r);
    background: var(--warning-bg);
}

.admin-requests-title {
    font-size: 12px;
    font-weight: 600;
    margin-bottom: 6px;
}

.admin-request-row {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
    font-size: 12px;
    padding: 4px 0;
}

/* W-38: the expiry list names what its day filter hides */
.notif-hidden-hint {
    padding: 8px 10px;
    color: var(--warning);
    font-size: 12px;
}

.notif-hidden-hint .btn {
    margin-left: 8px;
}

/* W-20: the error sits under the field that caused it */
.field-error {
    margin-top: 6px;
    font-size: 12px;
    color: var(--danger);
}

/* W-20: every required field says so, not only the name */
label.is-required::after {
    content: " *";
}

/* W-19: the default unit, next to the units it chooses from */
.default-unit-row {
    margin-top: 10px;
    max-width: 360px;
}

/* W-07: where the list is, and the way to the rest */
.result-bar {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 6px 2px 8px;
    font-size: 12px;
    color: var(--text-2);
}

.result-bar[hidden] {
    display: none;
}

.result-bar .result-range {
    color: var(--text-1);
    font-weight: 500;
}

.result-bar .result-nav {
    margin-left: auto;
    display: flex;
    gap: 6px;
}

/* W-16 (web audit 2026-09-23), item dialog: the field cards were inset 22 px,
   the GHS box, the tab strip and the tab panels sat on the dialog edge — two
   left margins in one dialog — and the first row of cards touched the header. */
#itemInfoModal .item-detail-grid {
    padding-top: 16px;
}

#itemInfoModal #ghsSymbolsContainer,
#itemInfoModal .modal-body > .tabs,
#itemInfoModal .modal-body > .tab-content {
    margin-left: 22px;
    margin-right: 22px;
}

/* The item name is data: "pH-Meter" must not be shown as "PH-METER". */
#itemInfoModal #itemInfoTitle {
    text-transform: none;
    letter-spacing: 0;
    padding-right: 36px;
}

/* A way out at the top, not only "Close" at the end of a long scroll. */
.modal-close-x {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--r);
    color: var(--text-3);
    cursor: pointer;
}

.modal-close-x:hover {
    color: var(--text-1);
    border-color: var(--border-2);
    background: var(--bg-3);
}

/* W-17: the access save under the table it saves */
.access-save-row {
    display: flex;
    justify-content: flex-end;
    margin-top: 12px;
}

/* W-22: the custom fields are a section of their own */
.form-section {
    margin: 4px 22px 14px;
    border-top: 1px solid var(--border);
}

.form-section > summary {
    cursor: pointer;
    padding: 12px 0 10px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-2);
}

.form-section > .form-group {
    padding: 0;
}

.detail-section-title {
    grid-column: 1 / -1;
    margin-top: 10px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-2);
}

/* W-65: the GHS picker — inset like every dialog body, tiles are buttons */
#ghsSelectionGrid {
    padding: 0 22px;
}

.ghs-selectable-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 10px 8px;
    border: 2px solid var(--border);
    border-radius: 8px;
    background: var(--bg-3);
    color: var(--text-2);
    cursor: pointer;
    font-family: var(--font);
    transition: border-color 150ms ease, background 150ms ease;
}

.ghs-selectable-item img {
    width: 56px;
    height: 56px;
    object-fit: contain;
}

.ghs-selectable-item .ghs-name {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-1);
    text-align: center;
    line-height: 1.3;
}

.ghs-selectable-item .ghs-code {
    font-size: 10px;
    color: var(--text-3);
}

.ghs-selectable-item.selected {
    border-color: var(--text-2);
    background: var(--accent-dim2);
}

/* W-03 (web audit 2026-09-23): the sign-in message pushed every field (and the
   Login button under the cursor) down when it appeared and back up when it
   went. It has a reserved slot now; the card height does not change. */
#loginScreen #alertContainer {
    min-height: 46px;
    display: flex;
    align-items: center;
}

#loginScreen #alertContainer .alert {
    width: 100%;
    margin: 0;
    overflow-wrap: anywhere;
}

/* W-30: no camera — the reason, and typing the code instead */
.scanner-fallback {
    padding: 18px 22px 4px;
}

.scanner-fallback .scanner-error {
    text-align: center;
    color: var(--text-2);
    margin-bottom: 16px;
}

.scanner-fallback .form-group {
    padding: 0;
}

.scanner-manual-row {
    display: flex;
    gap: 8px;
}

.scanner-manual-row input {
    flex: 1;
    min-width: 0;
}

/* W-43: filters say what they filter; expired batches say they are */
.filter-field {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--text-2);
}

.badge-expired {
    margin-left: 6px;
    padding: 1px 6px;
    border: 1px solid var(--danger);
    border-radius: 10px;
    font-size: 10px;
    color: var(--danger);
}

.p14-pick-row .pick-meta {
    margin-left: auto;
    font-size: 11px;
    color: var(--text-3);
}

/* W-49: past one-off checks, folded */
.past-rules > summary {
    cursor: pointer;
    padding: 4px 0;
    font-size: 12px;
    color: var(--text-3);
    border-top: 1px solid var(--border);
}

/* W-55: the upload-limit pencils zig-zagged after text of varying width */
.limit-cell {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

/* W-59: the upload dialog's parts sat on the dialog edge */
#uploadModal .upload-purpose {
    margin: 16px 22px 10px;
    font-size: 12px;
    color: var(--text-3);
}

#uploadModal .upload-usage,
#uploadModal .upload-drop-area,
#uploadModal .upload-progress,
#uploadModal .modal-body > h4,
#uploadModal .file-list {
    margin-left: 22px;
    margin-right: 22px;
}

/* W-50: a webhook to a public request inspector says so */
.badge-public-hook {
    margin-left: 6px;
    padding: 1px 6px;
    border: 1px solid var(--warning);
    border-radius: 10px;
    font-size: 10px;
    color: var(--warning);
    cursor: help;
}

/* W-54 (web audit 2026-09-23): the user table sat in a 50vh inner scroll box
   ("7 of 28 users" at a glance). The dialog keeps one height now (W-37), so the
   table takes all of it instead of stopping half-way with empty space below. */
#userManagementModal .modal-body {
    display: flex;
    flex-direction: column;
}

#userManagementModal .modal-body > .tabs {
    flex-shrink: 0;
}

#usersTab.active {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

#usersTab.active > #usersContainer {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

#usersTab.active .table-scroll-container {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    height: auto;
}

/* M-03: visibility is an explicit choice — Everyone / Only selected groups */
.vis-mode { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.vis-mode-option { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; margin: 0; font-size: 13px; }
.vis-mode-option input { margin-top: 3px; }
.vis-mode-option small { color: var(--text-2); }
