/* ============================================================
   FleetForge — Application Design System
   Geist Sans (UI) · Geist Mono (data) · Alpine.js v3 · Heroicons

   Sections
   ─────────────────────────────────────────────────
   00. Self-hosted Fonts (S-LUX-1: Geist)
   01. Reset & Base
   02. CSS Custom Properties (Design Tokens)
   03. Typography
   04. App Layout Shell
   05. Sidebar
   06. Topbar
   07. Page Content & Panels
   08. Buttons
   09. Badges & Status Chips
   10. Forms
   11. Tables
   12. Modals
   13. Toast Notifications
   14. Notification Dropdown
   15. Global Search Modal
   16. Skeleton Loaders
   17. Utilities
   18. Alpine.js Helpers & Transitions
   19. Responsive / Mobile
   ============================================================ */


/* ============================================================
   00. Self-hosted Fonts (S-LUX-1 2026-07-11)

   Geist Sans + Geist Mono v1.7.2 — variable-weight WOFF2,
   self-hosted from the official Vercel release
   (github.com/vercel/geist-font, SIL OFL 1.1 — license committed
   at public/assets/fonts/OFL.txt alongside the font files).

   URLs are relative to this file (public/assets/css/app.css →
   ../fonts/...). Weight axis 100–900 in a single file per family;
   italics render as synthetic oblique (acceptable — italic use in
   the app is incidental). The previous DM Sans/DM Mono files remain
   under public/assets/vendor/fonts/ but are no longer referenced.

   Every font-family in this file resolves through --font-sans /
   --font-mono (defined in section 02) — do not hardcode stacks.
   ============================================================ */

@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/Geist[wght].woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('../fonts/GeistMono[wght].woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ============================================================
   01. Reset & Base
   ============================================================ */

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

html {
    font-size: 16px;
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    tab-size: 4;
}

body {
    font-family: var(--font-sans);
    font-size: 0.9375rem; /* 15px */
    line-height: 1.6;
    background-color: var(--bg-body);
    color: var(--text-primary);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

img, svg { display: block; max-width: 100%; }

a {
    color: var(--color-primary);
    text-decoration: none;
    transition: color var(--transition-fast);   /* S-LUX-3: smooth link colour */
}
a:hover { text-decoration: underline; }

ul, ol { list-style: none; }

button {
    font-family: inherit;
    cursor: pointer;
    border: none;
    background: none;
}

input, textarea, select, button {
    font-family: inherit;
    font-size: inherit;
}

hr {
    border: none;
    border-top: 1px solid var(--border-color);
    margin: 1.5rem 0;
}

/* ============================================================
   S-LUX-3 MOTION DOCTRINE (D-LUX3-1) — reduced-motion kill switch
   ============================================================
   When the OS/browser asks for reduced motion, collapse every
   transition + animation to a near-instant, single-frame change and
   disable smooth-scroll. Uses 0.01ms (not 0) so animationend/
   transitionend listeners still fire and JS state machines that wait
   on them (toast dismiss, Alpine x-transition) don't hang. Scoped
   broadly on purpose — this is the one place motion is globally
   governed; the app's decorative-only reduced-motion block in
   animations.css remains and is a strict subset of this. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    /* Film grain is static already, but make sure nothing lingers. */
    body::after { animation: none !important; }
}


/* ============================================================
   02. CSS Custom Properties (Design Tokens)
   ============================================================ */

:root {
    /* ── Typography (S-LUX-1) ────────────────────── */
    --font-sans: "Geist", -apple-system, "Segoe UI", Roboto, sans-serif;
    --font-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;

    /* ── Brand Colours ────────────────────────────── */
    --color-primary:         #fbbe24;   /* Northland amber — brand */
    --color-primary-hover:   #dda720;   /* 12% darker (matches api/v1/settings/brand.php ff_darken) */
    --color-primary-light:   rgba(251, 190, 36, 0.14);
    --color-primary-text:    #fbbe24;   /* amber on dark surfaces = 11.4:1 */
    /* Foreground for anything sitting ON a --color-primary fill.
       NOT decorative: white on this amber is 1.68:1 (WCAG AA needs 4.5),
       black is 11.4:1. Every brand-filled component reads this token so a
       future brand change only has to set one value. */
    --color-on-primary:      #0f0f0f;

    --color-success:         #22c55e;
    --color-success-light:   rgba(34, 197, 94, 0.13);
    --color-success-text:    #4ade80;

    --color-warning:         #eab308;
    --color-warning-light:   rgba(234, 179, 8, 0.13);
    --color-warning-text:    #facc15;

    --color-danger:          #ef4444;
    --color-danger-light:    rgba(239, 68, 68, 0.13);
    --color-danger-text:     #f87171;

    --color-info:            #06b6d4;
    --color-info-light:      rgba(6, 182, 212, 0.12);
    --color-info-text:       #22d3ee;

    /* ── Atelier Dark Surfaces (S-LUX-1, default theme) ──
       D-LUX-1: these values are duplicated VERBATIM in the
       [data-theme="dark"] block below — default and explicit dark
       are one palette. Change them in BOTH places or not at all. */
    --bg-body:               #060403;
    --bg-surface:            #1E1913;
    --bg-surface-2:          #2A241B;
    --bg-surface-hover:      #342D22;
    --bg-muted:              #2A241B;
    --bg-secondary:          #2A241B;
    --bg-input:              #2A241B;
    --bg-hover:              #342D22;

    /* ── Text ────────────────────────────────────── */
    --text-primary:          #F4F1EA;
    --text-secondary:        #A6A094;
    --text-tertiary:         #736D61;
    --text-disabled:         #5C564B;
    /* Alias of the canonical, contrast-derived token. Was a hardcoded
       #ffffff that silently contradicted --color-on-primary 36 lines above
       and was invisible to the brand-override injector. */
    --text-on-primary:       var(--color-on-primary);
    --text-on-danger:        #ffffff;

    /* ── Borders ─────────────────────────────────── */
    --border-color:          #322D24;
    --border-color-strong:   #4A4438;
    --border-focus:          var(--color-primary);

    /* ── Sidebar ─────────────────────────────────── */
    --sidebar-width:         240px;
    --sidebar-width-collapsed: 64px;
    --sidebar-bg:            #0A0908;
    --sidebar-border:        rgba(255, 255, 255, 0.06);
    --sidebar-text:          #A6A094;
    --sidebar-text-muted:    #736D61;
    --sidebar-text-hover:    #F4F1EA;
    --sidebar-text-active:   #ffffff;
    --sidebar-item-hover-bg: rgba(255, 255, 255, 0.05);
    --sidebar-item-active-bg: color-mix(in srgb, var(--color-primary) 22%, transparent);
    --sidebar-hover:         rgba(255, 255, 255, 0.05);
    --sidebar-active:        color-mix(in srgb, var(--color-primary) 22%, transparent);
    --sidebar-icon:          #736D61;
    --sidebar-icon-active:   var(--color-primary);
    --sidebar-section-text:  #8A8478;
    --sidebar-brand-text:    #F4F1EA;

    /* ── Topbar (glass — see chrome section) ─────── */
    --topbar-height:         60px;
    --topbar-bg:             rgba(6, 4, 3, 0.72);
    --topbar-border:         #26231D;
    --topbar-text:           #F4F1EA;

    /* ── Scrollbar / Table ───────────────────────── */
    --scrollbar-bg:          transparent;
    --scrollbar-thumb:       #36322A;
    --table-header-bg:       #1C1A16;
    --table-row-hover:       #232019;
    --table-border:          #26231D;
    --table-stripe:          #191713;

    /* ── Shadows (deeper on dark bg) ─────────────── */
    --shadow-xs:  0 1px 2px rgba(0, 0, 0, 0.30);
    --shadow-sm:  0 1px 4px rgba(0, 0, 0, 0.40), 0 1px 2px rgba(0, 0, 0, 0.25);
    --shadow-md:  0 4px 12px rgba(0, 0, 0, 0.45), 0 2px 4px rgba(0, 0, 0, 0.25);
    --shadow-lg:  0 10px 24px rgba(0, 0, 0, 0.55), 0 4px 8px rgba(0, 0, 0, 0.30);
    --shadow-xl:  0 20px 40px rgba(0, 0, 0, 0.60), 0 8px 16px rgba(0, 0, 0, 0.35);
    --shadow-glow-primary: 0 0 20px rgba(249, 115, 22, 0.18);

    /* ── Radius (S-LUX-1: lg/xl bumped, 2xl new) ─── */
    --radius-sm:   4px;
    --radius-md:   6px;
    --radius-lg:   10px;
    --radius-xl:   14px;
    --radius-2xl:  20px;
    --radius-full: 9999px;

    /* ── Transitions ─────────────────────────────── */
    --transition-fast:   120ms ease;
    --transition-base:   200ms ease;
    --transition-slow:   280ms ease;
    /* S-LUX-3 motion doctrine (D-LUX3-1): one decisive ease for chrome
       motion — panels, modals, toasts, sidebar collapse. Transform/opacity
       only. Durations reuse the tokens above. */
    --ease-out-quart:    cubic-bezier(0.25, 1, 0.5, 1);

    /* ── Z-index Stack ───────────────────────────── */
    --z-sidebar:   40;
    --z-topbar:    30;
    --z-dropdown:  50;
    --z-modal:     60;
    --z-toast:     70;

    /* ── Form ────────────────────────────────────── */
    /* S-DESIGN-INPUTS (2026-05-03): Apple-aesthetic input tokens.
       Inputs render as borderless tinted-gray pills; on focus the
       background brightens and a 2px brand-orange ring appears.
       Dark mode uses warm-tinted alphas to match the brownish-black
       palette (rgba 140,130,115); light mode uses Apple's cool gray
       (rgba 118,118,128) verbatim. Labels use --label-text muted alpha.
       --input-border is retained for .form-check (checkbox borders)
       which intentionally keep the bordered look. */
    --input-height:      40px;
    --input-height-sm:   32px;
    --input-height-lg:   48px;
    --input-bg:          rgba(140, 130, 115, 0.18);
    --input-bg-focus:    rgba(140, 130, 115, 0.32);
    --input-focus-bg:    rgba(140, 130, 115, 0.32);
    --input-border:      #36322A;
    --input-text:        rgba(255, 255, 255, 0.96);
    --input-placeholder: rgba(235, 230, 220, 0.40);
    --label-text:        rgba(235, 230, 220, 0.66);
    --label-text-strong: rgba(255, 255, 255, 0.85);

    /* ── FIX #28: Spec-name aliases (both conventions work) ─── */
    /* Design spec uses different names than what was implemented */
    --bg-page:      var(--bg-body);
    --bg-card:      var(--bg-surface);
    --color-accent: var(--color-primary);
    --color-accent-hover: var(--color-primary-hover);
    --text-muted:   var(--text-tertiary);
    --text-inverse: var(--text-on-primary);
    --border-strong: var(--border-color-strong);
    --bg-selected:  color-mix(in srgb, var(--color-primary) 12%, transparent);  /* FIX #35 */
    /* S-LUX-5: legacy accounting/QBO views reference these names with LIGHT
       hex fallbacks (`var(--border-default, #e2e8f0)`, `var(--bg-elev, #f8fafc)`)
       — undefined, so 320+ borders rendered as currentColor and elevated
       panels went transparent (or light in the 2 fallback cases). Alias them
       to the real Atelier tokens so every one of those references resolves
       theme-correctly in one place. */
    --border-default: var(--border-color);
    --bg-elev:        var(--bg-surface-2);
    --bg-elevated:    var(--bg-surface-2);
    --bg-subtle:      var(--bg-surface-2);

    /* ── Atelier material tokens (S-LUX-1) ────────────────────
       --card-sheen: 1px inner top highlight — the "machined edge"
         that separates a card from the void without a heavy border.
       --shadow-ambient: contact shadow + long soft falloff; pairs
         with --card-sheen on every raised surface.
       --gradient-brand: reserved for hero/accent moments only.
       --grain-opacity: film-grain intensity (body::after). 0 = off. */
    --card-sheen:      inset 0 1px 0 rgba(255, 255, 255, 0.05);
    --shadow-ambient:  0 1px 2px rgba(0, 0, 0, 0.5), 0 12px 32px -16px rgba(0, 0, 0, 0.6);
    --gradient-brand:  linear-gradient(135deg, #FB923C 0%, #EA580C 100%);
    --tracking-tight:  -0.02em;
    --tracking-label:  0.08em;
    --grain-opacity:   0.025;
}

/* Light mode override (opt-in via data-theme="light") */
[data-theme="light"] {
    --color-primary:         #fbbe24;   /* Northland amber */
    --color-primary-hover:   #dda720;
    --color-primary-light:   #fff9e9;
    /* Deepened amber: the brand hex as TEXT on white is 1.68:1. This is the
       darkest amber that still clears AA (4.58:1) on a white surface. */
    --color-primary-text:    #947015;
    --color-on-primary:      #0f0f0f;

    --color-success:         #16a34a;
    --color-success-light:   #dcfce7;
    --color-success-text:    #15803d;

    --color-warning:         #d97706;
    --color-warning-light:   #fef3c7;
    --color-warning-text:    #b45309;

    --color-danger:          #dc2626;
    --color-danger-light:    #fee2e2;
    --color-danger-text:     #b91c1c;

    --color-info:            #0891b2;
    --color-info-light:      #cffafe;
    --color-info-text:       #0e7490;

    /* ── Atelier warm paper (S-LUX-1) — light parity for the warm
       dark palette; slate/cool grays replaced with warm neutrals. */
    --bg-body:               #F7F5F1;
    --bg-surface:            #ffffff;
    --bg-surface-2:          #F1EEE8;
    --bg-surface-hover:      #F1EEE8;

    /* S-NOTIF-LIGHT-HOVER (2026-06-23): the light palette was missing
       --bg-muted / --bg-hover — they were defined ONLY under
       [data-theme="dark"] (#1f1e1d / #2a2927). Every light-mode rule using
       var(--bg-muted | --bg-hover, <dark literal>) therefore fell through to
       a near-black literal. Most visible symptom: the hovered unread
       notification row (.notif-item--unread:hover → var(--bg-hover, #2a2a2a))
       rendered a near-black box with dark text in DAY mode. Mirror the
       dark-theme relationship (muted == surface-2; hover one step beyond) with
       on-palette slate values so all hover surfaces resolve light. */
    --bg-muted:              #F1EEE8;  /* mirrors --bg-surface-2 */
    --bg-hover:              #E9E5DD;  /* one step beyond, perceptible row/item hover */
    --bg-secondary:          #F1EEE8;
    --bg-input:              #F1EEE8;
    --bg-selected:           color-mix(in srgb, var(--color-primary) 10%, transparent);

    --text-primary:          #1A1815;
    --text-secondary:        #57534A;
    --text-tertiary:         #8A857A;
    --text-disabled:         #A8A296;

    --border-color:          #E7E3DB;
    --border-color-strong:   #D6D1C6;
    --border-focus:          var(--color-primary);

    --topbar-bg:             rgba(247, 245, 241, 0.72);
    --topbar-border:         #E7E3DB;
    --topbar-text:           #1A1815;

    --scrollbar-bg:          transparent;
    --scrollbar-thumb:       #D6D1C6;
    --table-header-bg:       #F1EEE8;
    --table-row-hover:       #F1EEE8;
    --table-border:          #E7E3DB;
    --table-stripe:          #FAF8F4;

    --shadow-xs:  0 1px 2px rgba(0, 0, 0, 0.05);
    --shadow-sm:  0 1px 3px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04);
    --shadow-md:  0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.04);
    --shadow-lg:  0 10px 15px -3px rgba(0, 0, 0, 0.10), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
    --shadow-xl:  0 20px 25px -5px rgba(0, 0, 0, 0.10), 0 10px 10px -5px rgba(0, 0, 0, 0.04);

    /* S-DESIGN-INPUTS: light-mode Apple input tokens (cool gray tints).
       --label-text bumped from Apple's 0.60 to 0.76 to pass WCAG AA
       contrast (4.5:1) for 13px label text on white AND on the warm
       --bg-surface-2 (#F1EEE8) card wells (S-LUX-1: 0.72 was 4.38:1 there). */
    --input-bg:          rgba(118, 118, 128, 0.12);
    --input-bg-focus:    rgba(118, 118, 128, 0.22);
    --input-focus-bg:    rgba(118, 118, 128, 0.22);
    --input-border:      #D6D1C6;
    --input-text:        rgba(0, 0, 0, 0.92);
    --input-placeholder: rgba(60, 60, 67, 0.40);
    --label-text:        rgba(60, 60, 67, 0.76);
    --label-text-strong: rgba(0, 0, 0, 0.85);

    /* ── Atelier material tokens — light variants (S-LUX-1) ── */
    --card-sheen:      inset 0 1px 0 rgba(255, 255, 255, 0.9);
    --shadow-ambient:  0 1px 2px rgba(0, 0, 0, 0.06), 0 12px 32px -16px rgba(0, 0, 0, 0.14);
}

/* ============================================================
   S-LUX-1 (2026-07-11) — Atelier dark ≡ default (D-LUX-1)
   Supersedes THEME-1 (2026-04-08). The explicit dark toggle and
   the :root default are now ONE palette: every value below is a
   VERBATIM copy of the :root token above it. The block still
   exists (rather than being deleted) because users carry
   data-theme="dark" in their profile and third-party/page-level
   rules target [data-theme="dark"] for specificity.
   --bg-page/--bg-card stay :root aliases of --bg-body/--bg-surface,
   which re-resolve correctly under this scope — no literal needed.
   Semantic --color-* tokens are never redefined here.
   ============================================================ */
[data-theme="dark"] {
    /* ── Page backgrounds ─────────────────────────── */
    --bg-body:               #060403;
    --bg-surface:            #1E1913;
    --bg-surface-2:          #2A241B;
    --bg-muted:              #2A241B;
    --bg-secondary:          #2A241B;
    --bg-input:              #2A241B;
    --bg-surface-hover:      #342D22;
    --bg-hover:              #342D22;
    --bg-selected:           color-mix(in srgb, var(--color-primary) 12%, transparent);

    /* ── Sidebar — always dark regardless of theme ── */
    --sidebar-bg:            #0A0908;
    --sidebar-border:        rgba(255, 255, 255, 0.06);
    --sidebar-text:          #A6A094;
    --sidebar-text-muted:    #736D61;
    --sidebar-section-text:  #8A8478;
    --sidebar-text-hover:    #F4F1EA;
    --sidebar-text-active:   #ffffff;
    --sidebar-hover:         rgba(255, 255, 255, 0.05);
    --sidebar-active:        color-mix(in srgb, var(--color-primary) 22%, transparent);
    --sidebar-item-hover-bg: rgba(255, 255, 255, 0.05);
    --sidebar-item-active-bg: color-mix(in srgb, var(--color-primary) 22%, transparent);
    --sidebar-icon:          #736D61;
    --sidebar-icon-active:   var(--color-primary);
    --sidebar-brand-text:    #F4F1EA;

    /* ── Text colors ─────────────────────────────── */
    --text-primary:          #F4F1EA;
    --text-secondary:        #A6A094;
    --text-tertiary:         #736D61;
    --text-muted:            #736D61;
    --text-inverse:          var(--text-on-primary);
    --text-disabled:         #5C564B;

    /* ── Border colors ───────────────────────────── */
    --border-color:          #322D24;
    --border-color-strong:   #4A4438;
    --border-strong:         #36322A;
    --border-focus:          var(--color-primary);

    /* ── Topbar (glass) ──────────────────────────── */
    --topbar-bg:             rgba(6, 4, 3, 0.72);
    --topbar-border:         #26231D;
    --topbar-text:           #F4F1EA;

    /* ── Input fields ────────────────────────────── */
    --input-bg:              rgba(140, 130, 115, 0.18);
    --input-bg-focus:        rgba(140, 130, 115, 0.32);
    --input-border:          #36322A;
    --input-text:            rgba(255, 255, 255, 0.96);
    --input-placeholder:     rgba(235, 230, 220, 0.40);
    --input-focus-bg:        rgba(140, 130, 115, 0.32);
    --label-text:            rgba(235, 230, 220, 0.66);
    --label-text-strong:     rgba(255, 255, 255, 0.85);

    /* ── Scrollbar ───────────────────────────────── */
    --scrollbar-bg:          transparent;
    --scrollbar-thumb:       #36322A;

    /* ── Table ───────────────────────────────────── */
    --table-header-bg:       #1C1A16;
    --table-row-hover:       #232019;
    --table-border:          #26231D;
    --table-stripe:          #191713;
}


/* ============================================================
   03. Typography
   ============================================================ */

h1, h2, h3, h4, h5, h6 {
    font-weight: 600;
    line-height: 1.25;
    color: var(--text-primary);
}

/* S-LUX-1: page-level headings get tight tracking (display sizes read
   looser than they should at default spacing; -0.02em snaps them in) */
h1 { font-size: 1.5rem;  letter-spacing: var(--tracking-tight); }   /* 24px */
h2 { font-size: 1.25rem; letter-spacing: var(--tracking-tight); }   /* 20px */
h3 { font-size: 1.125rem;  }   /* 18px */
h4 { font-size: 1rem;      }   /* 16px */
h5 { font-size: 0.9375rem; }   /* 15px */
h6 { font-size: 0.875rem;  }   /* 14px */

p { margin-bottom: 0.75rem; }
p:last-child { margin-bottom: 0; }

small, .text-sm { font-size: 0.875rem; }
.text-xs        { font-size: 0.8125rem; }

code, kbd, pre {
    font-family: var(--font-mono);
    font-size: 0.875em;
}

code {
    background: var(--bg-surface-2);
    padding: 0.1em 0.35em;
    border-radius: var(--radius-sm);
    color: var(--color-danger);
}

kbd {
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-sm);
    padding: 0.1em 0.4em;
    font-size: 0.8125rem;
    color: var(--text-secondary);
    box-shadow: 0 1px 0 var(--border-color-strong);
}

.text-primary   { color: var(--text-primary) !important; }
.text-secondary { color: var(--text-secondary) !important; }
.text-tertiary  { color: var(--text-tertiary) !important; }
.text-danger    { color: var(--color-danger) !important; }
.text-success   { color: var(--color-success) !important; }
.text-warning   { color: var(--color-warning) !important; }
.text-mono      { font-family: var(--font-mono); }

/* S-LUX-1 — tabular (fixed-width) figures on every data surface so
   columns of money/dates/counts align vertically. .tabular-nums is the
   opt-in utility for one-off amount cells outside these selectors. */
.tabular-nums,
.table, .data-table,
.stat-value,
.font-mono, .text-mono {
    font-variant-numeric: tabular-nums;
}


/* ============================================================
   04. App Layout Shell
   ============================================================ */

.app-layout {
    display: flex;
    min-height: 100vh;
    background-color: var(--bg-body);
}

/* Main content column: topbar + page content */
.app-main {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;   /* prevent flex blowout */
    min-height: 100vh;
    transition: margin-left var(--transition-slow);
}

/* S-HELP-DRAWER-PUSH: only the content + footer shift right — topbar stays full-width */
.page-content,
.app-footer {
    transition: margin-right var(--transition-slow);
}

/* ── S-HELP-DRAWER-PUSH: desktop push layout ──────────────────────────────
   When the help drawer is open on desktop (≥1024px), shrink the main content
   by the drawer width rather than overlaying it. body[data-help-drawer-open]
   is set by FF_HelpDrawer.open() / cleared by close().
   Mobile (<1024px): overlay mode — no margin, backdrop shown instead.
*/
@media (min-width: 1024px) {
    /* No backdrop on desktop — page must stay fully interactive */
    body[data-help-drawer-open] .help-drawer-backdrop {
        display: none !important;
    }
    /* Panel is a side column, not a modal on desktop */
    .help-drawer-panel {
        box-shadow: -2px 0 16px rgba(0, 0, 0, 0.18);
    }
    /* Stat tiles reflow to fill the narrower content column.
       The viewport-width media queries don't know 500px is now a drawer,
       so auto-fill with a sensible minimum prevents text truncation. */
    body[data-help-drawer-open] .stat-grid {
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    }
}


/* ============================================================
   05. Sidebar
   ============================================================ */

.sidebar {
    width: var(--sidebar-width);
    flex-shrink: 0;
    background-color: var(--sidebar-bg);
    border-right: 1px solid var(--sidebar-border);
    display: flex;
    flex-direction: column;
    height: 100vh;
    position: sticky;
    top: 0;
    /* SIDEBAR-SCROLL-1: outer sidebar clips, inner .sidebar-nav owns the scroll.
       Previously this element scrolled as a whole, which dragged the brand
       header off-screen on short viewports. Now .sidebar-brand + .sidebar-footer
       stay pinned and only the middle .sidebar-nav scrolls. */
    overflow: hidden;
    /* S-LUX-3: decisive ease on the collapse/expand width transition */
    transition: width 280ms var(--ease-out-quart);
    z-index: var(--z-sidebar);
}

/* ── Brand / Logo ──────────────────────── */
/* S-DESIGN-LOGO-BIGGER: brand area decoupled from --topbar-height
   so the logo can render at a presence-worthy size regardless of
   the topbar's 60px constraint. 96px tall gives a wide wordmark
   room to breathe in the open sidebar. The topbar still anchors
   at 60px, producing a deliberate L-step that reads as "brand
   block extends below header" — common premium-app pattern. */
.sidebar-brand {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 12px 12px 16px;
    height: 96px;
    border-bottom: 1px solid var(--sidebar-border);
    flex-shrink: 0;
}

.sidebar-brand-link {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    overflow: hidden;
    min-width: 0;
    /* S-DESIGN-LOGO-WIDE: claim the full horizontal track so a width:100%
       child img actually grows to fill the sidebar (minus padding and the
       collapse button to its right). Without flex:1 the link sized to its
       single child's natural width, leaving dead space. */
    flex: 1;
    justify-content: center;
}

/* S-DESIGN-LOGO-WIDE: logo fills the available horizontal space in
   the brand area (was previously height-driven, leaving dead space
   to the right of narrower-aspect wordmarks). width:100% claims the
   full flex width; max-height caps total visual presence; height:auto
   preserves the source aspect ratio. object-fit:contain ensures no
   cropping when the natural width is less than 100%. */
.sidebar-logo {
    width: 100%;
    height: auto;
    max-height: 80px;
    border-radius: var(--radius-md);
    object-fit: contain;
}

.sidebar-brand-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    background: var(--color-primary);
    border-radius: var(--radius-md);
    flex-shrink: 0;
}

.brand-icon {
    width: 32px;
    height: 32px;
    color: var(--color-on-primary);  /* on the amber .sidebar-brand-icon tile */
}

.sidebar-brand-name {
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: var(--tracking-tight);   /* S-LUX-3: tighten the wordmark */
    color: var(--sidebar-brand-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    transition: opacity var(--transition-base), max-width var(--transition-slow);
    max-width: 160px;
}

.sidebar-collapse-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-md);
    color: var(--sidebar-text);
    flex-shrink: 0;
    transition: background var(--transition-fast), color var(--transition-fast),
                transform var(--transition-slow);
}

.sidebar-collapse-btn:hover {
    background: var(--sidebar-item-hover-bg);
    color: var(--sidebar-text-hover);
}

.collapse-icon {
    width: 16px;
    height: 16px;
    transition: transform var(--transition-slow);
}

/* ── Navigation ─────────────────────────── */
/* SIDEBAR-SCROLL-1: this is the real scroll container inside the flex column.
   `min-height: 0` is REQUIRED — without it a `flex: 1` child refuses to
   shrink below its content height, so `overflow: auto` never triggers and
   bottom items (Settings, Users, etc.) get cut off on short viewports.
   Scrollbar is transparent until hover for a cleaner look. */
.sidebar-nav {
    flex: 1 1 0;
    min-height: 0;
    padding: 12px 8px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    overflow-y: auto;
    overflow-x: hidden;
    scroll-behavior: smooth;
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
    /* S-LUX-3: soft fade masks top + bottom so overflowing nav content
       dissolves into the sidebar rather than hard-cutting at the edges.
       12px feathers; the middle 100% stays fully opaque. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
            mask-image: linear-gradient(to bottom, transparent 0, #000 12px, #000 calc(100% - 12px), transparent 100%);
}

/* SIDEBAR-SCROLL-1: lock direct children to their natural size. When the
   parent has `min-height: 0` and the total children height exceeds the
   container, default `flex-shrink: 1` would squish each nav item vertically.
   This was the regression that made the sidebar look compressed on tall
   desktops after the initial scroll fix. */
.sidebar-nav > * {
    flex-shrink: 0;
}

.sidebar-nav:hover {
    scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
}

.sidebar-nav::-webkit-scrollbar {
    width: 4px;
}
.sidebar-nav::-webkit-scrollbar-track {
    background: transparent;
}
.sidebar-nav::-webkit-scrollbar-thumb {
    background: transparent;
    border-radius: 4px;
    transition: background 0.2s ease;
}
.sidebar-nav:hover::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.18);
}

.nav-section-label {
    font-size: 0.6875rem;  /* 11px */
    font-weight: 600;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--sidebar-section-text);
    padding: 12px 8px 4px;
    /* S-LUX-3: more air above each section group (except the first). */
    margin-top: 8px;
    white-space: nowrap;
    overflow: hidden;
    transition: opacity var(--transition-base);
}
.sidebar-nav > .nav-section-label:first-child { margin-top: 0; }

.nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-md);
    color: var(--sidebar-text);
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    transition: background var(--transition-fast), color var(--transition-fast);
    position: relative;
}

.nav-item:hover {
    background: var(--sidebar-item-hover-bg);
    color: var(--sidebar-text-hover);
    text-decoration: none;
}

.nav-item.is-active {
    background: var(--sidebar-item-active-bg);
    color: var(--sidebar-text-active);
}

/* S-LUX-1: 2px brand rail on the active nav item — the tinted bg says
   "here-ish", the rail says exactly here. Rides the item's own
   overflow:hidden so it clips to the rounded corners. */
.nav-item.is-active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 3px;   /* S-LUX-LOUD: thicker, more assertive brand rail */
    background: var(--color-primary);
}

.nav-item-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    flex-shrink: 0;
}

.nav-icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.nav-item .nav-icon { color: inherit; }
.nav-item.is-active .nav-icon { color: var(--color-primary); }

.nav-item-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    /* S-LUX-3: labels fade + slide (transform/opacity only) on collapse. */
    transition: opacity var(--transition-base), transform var(--transition-base);
}

.nav-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    background: var(--color-danger);
    color: #ffffff;
    font-size: 0.6875rem;
    font-weight: 700;
    border-radius: var(--radius-full);
    flex-shrink: 0;
    transition: opacity var(--transition-base);
}

/* ── Nav groups (collapsible parent + children) ── */
.nav-group {
    display: flex;
    flex-direction: column;
}

.nav-group > .nav-item {
    position: relative;
}

.nav-group-arrow {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    color: var(--sidebar-text);
    opacity: 0.5;
    transition: transform var(--transition-fast), opacity var(--transition-fast);
}

.nav-group.is-open > .nav-item .nav-group-arrow {
    transform: rotate(90deg);
    opacity: 0.7;
}

.nav-children {
    display: none;
    flex-direction: column;
    padding-left: 14px;
}

.nav-group.is-open > .nav-children {
    display: flex;
}

.nav-item--child {
    font-size: 0.825rem;
    padding: 6px 10px;
}

.nav-item--child .nav-item-icon {
    width: 16px;
    height: 16px;
}

.nav-item--child .nav-icon {
    width: 14px;
    height: 14px;
}

/* ────────── S-SIDEBAR-LOCK-ALL-RESTRICTED ──────────
   Locked nav items render in place of hidden ones so every role sees
   the full module map. .nav-item base rules still apply (flex layout,
   gap, padding, border-radius) — the modifier overrides only what
   needs to change for the locked state. */
.nav-item--locked {
    cursor: not-allowed;
    pointer-events: none;
    user-select: none;
    opacity: 0.38;
    color: var(--text-muted);
}
.nav-icon--locked {
    opacity: 0.6;
    flex-shrink: 0;
}
.nav-lock-icon {
    margin-left: auto;
    flex-shrink: 0;
    opacity: 0.7;
    display: flex;
    align-items: center;
}
/* Defensive: section separators never wrap a lock icon today, but
   keep this rule in case the structure changes. */
.nav-section-label .nav-lock-icon { display: none; }
/* Collapsed-state rules live alongside the existing
   .sidebar:not(.is-open) block — see further down in the file
   for the matching @media (min-width: 1024px) overrides. */
/* ────────── S-SIDEBAR-LOCK-ALL-RESTRICTED END ────────── */

/* ── User Footer ─────────────────────────── */
.sidebar-footer {
    border-top: 1px solid var(--sidebar-border);
    padding: 12px 10px;
    flex-shrink: 0;
}

.sidebar-user {
    display: flex;
    align-items: center;
    gap: 8px;
}

.sidebar-user-info {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    transition: opacity var(--transition-base), max-width var(--transition-slow);
    max-width: 160px;
}

.sidebar-user-name {
    display: block;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--sidebar-text-hover);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-user-role {
    display: block;
    font-size: 0.75rem;
    color: var(--sidebar-section-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-user-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
}

/* ── Theme toggle icons ──────────────────── */
.theme-icon { width: 18px; height: 18px; }

/* Light mode: show moon (click → switch to dark), hide sun */
.theme-icon--light { display: none;  }
.theme-icon--dark  { display: block; }

/* Dark mode: show sun (click → switch to light), hide moon */
[data-theme="dark"] .theme-icon--light { display: block; }
[data-theme="dark"] .theme-icon--dark  { display: none;  }

/* ── Collapsed sidebar (desktop) ─────────── */
@media (min-width: 1024px) {
    .sidebar:not(.is-open) {
        width: var(--sidebar-width-collapsed);
    }

    .sidebar:not(.is-open) .sidebar-brand-name,
    .sidebar:not(.is-open) .nav-item-label,
    .sidebar:not(.is-open) .nav-badge,
    .sidebar:not(.is-open) .nav-group-arrow,
    .sidebar:not(.is-open) .sidebar-user-info {
        opacity: 0;
        pointer-events: none;
        max-width: 0;
    }
    /* S-LUX-3: the nav label also slides left 4px as it fades (transform
       only — no layout cost; max-width above handles the reflow). */
    .sidebar:not(.is-open) .nav-item-label {
        transform: translateX(-4px);
    }

    /* Elements that must FULLY collapse out of layout in icons-only
       mode — opacity:0 + max-width:0 alone leaves vertical padding +
       child heights behind, accumulating into 60-200px of empty
       vertical space between top-level icons (operator surfaced
       2026-05-20).

       Section separators: ~16px of padding per separator (12+4 vertical).
       Nav-children: the active group has .is-open which forces
       display:flex on its .nav-children container, expanding to the
       full natural height of the child links (~36px each × N children).
       Both are decorative-only in collapsed mode, so display:none is
       the cleanest fix. */
    .sidebar:not(.is-open) .nav-section-label,
    .sidebar:not(.is-open) .nav-children {
        display: none;
    }

    /* S-SIDEBAR-COLLAPSED-BADGE-DOT — keep notification badges visible
       in collapsed mode but pin them to the top-right of the nav item
       as a small iOS-style dot. position:absolute removes the badge
       from the flex flow so it cannot reserve a gap slot that pushes
       the icon off center. Override every "hide" property the shared
       rule above sets (opacity / pointer-events / max-width) to make
       sure the badge actually renders. Smaller dimensions + tighter
       padding + smaller font appropriate for the 64px collapsed track. */
    .sidebar:not(.is-open) .nav-badge {
        position: absolute;
        top: 4px;
        right: 8px;
        min-width: 16px;
        height: 16px;
        padding: 0 4px;
        font-size: 0.5625rem;    /* 9px */
        line-height: 1;
        opacity: 1;
        pointer-events: auto;
        max-width: none;
        visibility: visible;
        z-index: 1;
    }

    .sidebar:not(.is-open) .sidebar-brand {
        justify-content: center;
        padding: 0 8px;
    }

    /* S-DESIGN-LOGO-BIGGER: cap the bigger logo when the sidebar
       collapses to icon-only so a wide wordmark doesn't overflow
       the 64px-wide collapsed track. Brand area also returns to
       topbar-height in collapsed mode so the L-step disappears
       when the user has compacted the chrome. */
    .sidebar:not(.is-open) .sidebar-brand {
        height: var(--topbar-height);
        padding: 0 8px;
    }
    .sidebar:not(.is-open) .sidebar-logo {
        max-width: 44px;
        height: 40px;
    }
    .sidebar:not(.is-open) .sidebar-brand-icon {
        width: 40px;
        height: 40px;
    }
    .sidebar:not(.is-open) .brand-icon {
        width: 24px;
        height: 24px;
    }

    .sidebar:not(.is-open) .sidebar-collapse-btn {
        display: none; /* hide collapse btn when collapsed; topbar menu btn takes over */
    }

    .sidebar:not(.is-open) .nav-item {
        justify-content: center;
        padding: 8px;
        /* S-SIDEBAR-COLLAPSED-ICON-FIX — zero the inter-child gap so
           any hidden-but-flex-present child (e.g. .nav-item-label
           with max-width:0) can't reserve a 10px slot that pushes
           the icon off center. */
        gap: 0;
    }
    .sidebar:not(.is-open) .nav-item-icon,
    .sidebar:not(.is-open) .nav-icon {
        margin: 0 auto;
        flex-shrink: 0;
    }

    /* S-SIDEBAR-LOCK-ALL-RESTRICTED — collapsed-mode locked items.
       The base .nav-item collapsed rule already centres the icon
       (justify-content:center, gap:0, padding:8px) and hides the
       label (opacity:0, max-width:0). The lock icon needs to escape
       the flex flow and pin to the bottom-right corner of the
       collapsed rail (next to the centred module icon) as a tiny
       indicator. */
    .sidebar:not(.is-open) .nav-lock-icon {
        position: absolute;
        bottom: 4px;
        right: 8px;
        width: 8px;
        height: 8px;
        margin: 0;
        opacity: 0.8;
    }

    .sidebar:not(.is-open) .sidebar-user {
        justify-content: center;
    }

    .sidebar:not(.is-open) .sidebar-user-actions {
        flex-direction: column;
    }

    .sidebar:not(.is-open) .collapse-icon {
        transform: rotate(180deg);
    }
}


/* ============================================================
   06. Topbar
   ============================================================ */

.topbar {
    position: sticky;
    top: 0;
    z-index: var(--z-topbar);
    height: var(--topbar-height);
    /* S-LUX-1 chrome glass: --topbar-bg is translucent; blur+saturate
       frosts whatever scrolls beneath (solid fallback in @supports below) */
    background: var(--topbar-bg);
    -webkit-backdrop-filter: blur(14px) saturate(1.4);
    backdrop-filter: blur(14px) saturate(1.4);
    /* S-LUX-LOUD: brand-tinted 2px underline — a signature stroke of colour on
       every page (follows the white-label chain via color-mix). */
    border-bottom: 2px solid color-mix(in srgb, var(--color-primary) 55%, var(--border-color));
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 20px 0 24px;
    gap: 16px;
    flex-shrink: 0;
}

/* Glass fallback — no backdrop-filter support means the translucent
   --topbar-bg would ghost page content through the bar; go solid. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .topbar { background: #111009; }
    [data-theme="light"] .topbar { background: #F7F5F1; }
}

.topbar-left {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
}

.topbar-right {
    display: flex;
    align-items: center;
    gap: 8px;
    /* S-TOPBAR-FIT: was `flex-shrink: 0`, which made this group resolve to
       its max-content width no matter how narrow the bar got. Its children's
       own `min-width: 0` was therefore inert — they never saw negative free
       space — and the whole deficit escaped past .topbar (which has no
       overflow) into the document, giving EVERY admin page a horizontal
       scrollbar between ~1024-1170px and below ~890px.
       `min-width: 0` lets the group shrink; the shrink then lands on the
       two children that can absorb it gracefully (.search-wrapper, already
       flex:1 1 auto, and .user-trigger-name, already ellipsis-capable)
       because every other child is pinned flex-shrink:0. */
    min-width: 0;
}

.topbar-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--topbar-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* S-TOPBAR-MODULE-LINK — the page title links back to its module's dashboard.
   Inherits the title look; reveals it's clickable on hover/focus. */
.topbar-title-link {
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    border-radius: 4px;
}
.topbar-title-link:hover,
.topbar-title-link:focus-visible {
    text-decoration: underline;
    text-underline-offset: 3px;
}
.topbar-title-link:focus-visible {
    outline: 2px solid var(--color-primary, #2563eb);
    outline-offset: 2px;
}

/* S-DESIGN-LOGO-TOPBAR — company name as breadcrumb root in topbar.
   Sized so it reads as the dominant brand element on every page;
   page title (.topbar-title) sits to the right as a secondary label
   separated by a › chevron. */
.topbar-company-name {
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--topbar-text);
    text-decoration: none;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 280px;
    letter-spacing: -0.01em;
    transition: color var(--transition-fast);
}
.topbar-company-name:hover { color: var(--color-primary); }

.topbar-breadcrumb-sep {
    color: var(--text-muted);
    font-size: 1rem;
    font-weight: 400;
    margin: 0 -2px;
    flex-shrink: 0;
}

@media (max-width: 767px) {
    /* On phones the topbar is tight — drop the breadcrumb so only
       the page title shows. Company name is still visible in the
       sidebar / hamburger menu for context. */
    .topbar-company-name,
    .topbar-breadcrumb-sep { display: none; }
}

/* Mobile menu toggle — hidden on desktop */
.topbar-menu-btn {
    display: none;
}

/* S-MOBILE-CHROME-CLEANUP — mobile-only search icon button.
   Hidden on desktop+tablet where .search-wrapper renders inline;
   revealed at <768px (see the mobile media block) where it opens
   the existing ⌘K global search modal.

   S-TOPBAR-FIT: scoped to `.topbar-right` so it BEATS `.btn-icon
   { display: inline-flex }` (~line 2737). The markup is
   `class="btn-icon topbar-search-icon-btn"`, so an unscoped
   `.topbar-search-icon-btn` (0,1,0) tied `.btn-icon` (0,1,0) and lost
   on source order — the magnifier was rendering at EVERY width, a
   duplicate of the inline search box beside it, costing 44px of a
   topbar that had none to spare. Same specificity trap already
   documented for the double hamburger below. Do not use !important
   here: the ≤767px reveal is scoped to match, not to out-shout. */
.topbar-right .topbar-search-icon-btn {
    display: none;
}

/* Search trigger */
.topbar-search-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-full);
    color: var(--text-tertiary);
    font-size: 0.875rem;
    transition: background var(--transition-fast), border-color var(--transition-fast),
                color var(--transition-fast);
    cursor: pointer;
}

.topbar-search-btn:hover {
    background: var(--bg-surface);
    border-color: var(--border-color-strong);
    color: var(--text-secondary);
}

.topbar-search-btn .search-icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

.search-hint {
    display: flex;
    align-items: center;
    gap: 6px;
    white-space: nowrap;
}

.search-kbd {
    display: inline-flex;
    align-items: center;
    gap: 1px;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    padding: 1px 5px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-sm);
    box-shadow: 0 1px 0 var(--border-color-strong);
    color: var(--text-tertiary);
}

/* Notifications bell */
.topbar-notifications {
    position: relative;
}

.topbar-bell-btn {
    position: relative;
}

.notification-dot {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    background: var(--color-danger);
    color: #ffffff;
    font-size: 0.6875rem;
    font-weight: 700;
    border-radius: var(--radius-full);
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--topbar-bg);
    pointer-events: none;
}

.notification-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: 360px;
    /* S-LUX-3 panel recipe (see .user-dropdown). */
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--card-sheen), var(--shadow-lg);
    transform-origin: top right;
    z-index: var(--z-dropdown);
    overflow: hidden;
}

.notification-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px 12px;
    border-bottom: 1px solid var(--border-color);
}

.notification-heading {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--text-primary);
}

.notification-list {
    max-height: 360px;
    overflow-y: auto;
}

.notification-empty {
    padding: 24px;
    text-align: center;
    color: var(--text-tertiary);
    font-size: 0.875rem;
}

/* Individual notification items — rendered by app.js */
.notification-item {
    display: flex;
    gap: 12px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-color);
    transition: background var(--transition-fast);
    text-decoration: none;
    color: inherit;
}

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

.notification-item:hover {
    background: var(--bg-surface-hover);
    text-decoration: none;
}

.notification-item.is-unread {
    background: var(--color-primary-light);
}

.notification-item-body {
    flex: 1;
    min-width: 0;
}

.notification-item-text {
    font-size: 0.875rem;
    color: var(--text-primary);
    line-height: 1.45;
}

.notification-item-time {
    font-size: 0.75rem;
    color: var(--text-tertiary);
    margin-top: 2px;
}

.notification-footer {
    padding: 10px 16px;
    border-top: 1px solid var(--border-color);
    text-align: center;
}

/* Mac / non-Mac shortcut hints */
.mac-only     { display: none; }
.non-mac-only { display: inline; }

/* ── Home button (between menu toggle and page title) ───────────────────────── */
.topbar-home-btn {
    color: var(--text-secondary);
    text-decoration: none;
}
.topbar-home-btn:hover {
    color: var(--text-primary);
    background: var(--bg-surface-2);
}

/* ── Theme toggle button ────────────────────────────────────────────────────── */
/* Reuses .btn-icon sizing; just a label for specificity / hover overrides.     */
.topbar-theme-btn {
    color: var(--text-secondary);
}
.topbar-theme-btn:hover {
    color: var(--text-primary);
    background: var(--bg-surface-2);
}

/* ── Quick-create "+" dropdown ──────────────────────────────────────────────── */
.topbar-create {
    position: relative;
    /* S-TOPBAR-FIT: now that .topbar-right can shrink, pin the fixed-size
       controls so the deficit is absorbed by the search box and the user
       name (both designed for it) instead of squashing a button. */
    flex-shrink: 0;
}

.topbar-create-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 13px 5px 10px;
    background: var(--color-primary);
    color: var(--color-on-primary);
    border: none;
    border-radius: var(--radius-full);
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition-fast), box-shadow var(--transition-fast);
    white-space: nowrap;
    height: 32px;
}
.topbar-create-btn:hover {
    background: var(--color-primary-dark, color-mix(in srgb, var(--color-primary) 85%, black));
    box-shadow: 0 1px 6px rgba(0,0,0,0.18);
}
.topbar-create-btn:focus-visible {
    outline: 2px solid var(--border-focus);
    outline-offset: 2px;
}

.topbar-create-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    min-width: 192px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
    z-index: var(--z-dropdown);
    overflow: hidden;
    padding: 6px 0;
}

.topbar-dropdown-label {
    padding: 6px 14px 4px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin: 0;
}

.topbar-create-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    font-size: 0.875rem;
    color: var(--text-primary);
    text-decoration: none;
    transition: background var(--transition-fast);
}
.topbar-create-item:hover {
    background: var(--bg-surface-hover);
    text-decoration: none;
    color: var(--text-primary);
}
.topbar-create-item svg,
.topbar-create-item .nav-icon {
    color: var(--text-secondary);
    flex-shrink: 0;
}

/* ══════════════════════════════════════════════════════════════
   PERM-1 — Display settings popover (font size + density)
   The trigger sits in the topbar before the notifications bell;
   the dropdown floats below using the standard topbar dropdown
   positioning conventions.
   ══════════════════════════════════════════════════════════════ */
.topbar-display {
    position: relative;
    display: flex;
    align-items: center;
    flex-shrink: 0;   /* S-TOPBAR-FIT — see .topbar-create */
}

.topbar-display-btn {
    color: var(--text-secondary);
}
.topbar-display-btn:hover {
    color: var(--text-primary);
    background: var(--bg-surface-hover);
}

.topbar-display-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    min-width: 248px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 12px;
    z-index: 100;
}

.topbar-display-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 4px 2px;
}

.topbar-display-value {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary);
    min-width: 48px;
    text-align: center;
}

.topbar-display-density {
    display: flex;
    gap: 6px;
    padding: 6px 2px 4px;
}

.topbar-display-chip {
    flex: 1 1 0;
    padding: 6px 8px;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-secondary);
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all var(--transition-fast);
}
.topbar-display-chip:hover:not(:disabled) {
    border-color: var(--color-primary);
    color: var(--text-primary);
}
.topbar-display-chip.is-active {
    background: color-mix(in srgb, var(--color-primary) 14%, transparent);
    border-color: var(--color-primary);
    color: var(--color-primary);
    font-weight: 600;
}
.topbar-display-chip:disabled {
    cursor: not-allowed;
    opacity: 0.6;
}

.topbar-display-reset {
    width: 100%;
    margin-top: 4px;
    padding: 7px 10px;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-secondary);
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all var(--transition-fast);
}
.topbar-display-reset:hover:not(:disabled) {
    background: var(--bg-surface-hover);
    color: var(--text-primary);
    border-color: var(--text-secondary);
}
.topbar-display-reset:disabled {
    cursor: not-allowed;
    opacity: 0.5;
}

/* ══════════════════════════════════════════════════════════════
   PERM-1 — Density variants for .page-content.
   <body data-density="..."> is set in includes/header.php from the
   user's saved preference. These rules tweak the spacing of cards,
   tables, and form controls within the main content area only —
   never the sidebar/topbar/footer/modals.
   ══════════════════════════════════════════════════════════════ */

/* COMPACT — tighter padding, smaller gaps */
body[data-density="compact"] .page-content .card-body { padding: 12px 14px; }
body[data-density="compact"] .page-content .card-header { padding: 10px 14px; }
body[data-density="compact"] .page-content .table th,
body[data-density="compact"] .page-content .table td { padding: 6px 10px; }
body[data-density="compact"] .page-content .form-group { margin-bottom: 10px; }
body[data-density="compact"] .page-content .page-header { margin-bottom: 12px; }
body[data-density="compact"] .page-content .form-control { padding: 6px 9px; }

/* COMFORTABLE — current defaults (no overrides needed) */
/* Intentionally empty so the rule cascade is explicit and easy to grep */
body[data-density="comfortable"] .page-content { /* default spacing */ }

/* SPACIOUS — looser padding, taller rows for easier scanning */
body[data-density="spacious"] .page-content .card-body { padding: 24px 26px; }
body[data-density="spacious"] .page-content .card-header { padding: 18px 26px; }
body[data-density="spacious"] .page-content .table th,
body[data-density="spacious"] .page-content .table td { padding: 14px 18px; }
body[data-density="spacious"] .page-content .form-group { margin-bottom: 22px; }
body[data-density="spacious"] .page-content .page-header { margin-bottom: 28px; }
body[data-density="spacious"] .page-content .form-control { padding: 11px 14px; }

/* ── User avatar + dropdown ─────────────────────────────────────────────────── */
.topbar-user {
    position: relative;
    /* S-TOPBAR-FIT: without this the chip is stuck at its max-content width
       (up to 242px — the name/role meta is capped at 160px), so it cannot
       give ground when the bar is tight. The ellipsis on .user-trigger-name
       only engages once this can actually narrow. */
    min-width: 0;
}

/* Base avatar circle — initials badge */
.user-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex-shrink: 0;   /* S-TOPBAR-FIT: keep the circle round when the chip narrows */
    border-radius: var(--radius-full);
    background: var(--color-primary);
    color: var(--color-on-primary);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    border: 2px solid transparent;
    cursor: pointer;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
    flex-shrink: 0;
    user-select: none;
    /* Reset button defaults when used as <button> */
    padding: 0;
    line-height: 1;
}
.user-avatar:hover {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 20%, transparent);
}
.user-avatar:focus-visible {
    outline: 2px solid var(--border-focus);
    outline-offset: 2px;
}

/* Large variant — used inside the dropdown header */
.user-avatar--lg {
    width: 42px;
    height: 42px;
    font-size: 0.9rem;
    pointer-events: none;  /* decorative inside the dropdown */
}

/* ── User trigger button (avatar + name + role) ─────────────
   NOTIF-1 follow-up: shows the logged-in user's name + role
   in the topbar next to their initials. The button itself is
   the dropdown trigger. The .user-trigger-meta block collapses
   on mobile (≤767px) to keep the topbar from overflowing. */
.user-trigger {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 4px 12px 4px 4px;
    border: 1px solid var(--border-color);
    background: var(--bg-surface);
    border-radius: var(--radius-full);
    cursor: pointer;
    color: var(--text-primary);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast),
                background var(--transition-fast);
    font-family: inherit;
    line-height: 1;
    /* S-TOPBAR-FIT: every link in the chain has to be able to shrink —
       .topbar-right → .topbar-user → .user-trigger → -meta → -name — or the
       ellipsis on .user-trigger-name never engages and the chip punches out
       of the bar as document-wide overflow.
       `max-width: 100%` is the load-bearing half: .topbar-user is a BLOCK, so
       once it narrows nothing constrains this inline-flex child to it — the
       button just overflowed its own parent at its 242px max-content width.
       `min-width: 0` then lets the button's own flex children give ground. */
    min-width: 0;
    max-width: 100%;
}
.user-trigger:hover {
    border-color: var(--color-primary);
    background: var(--bg-surface-hover, var(--bg-muted));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 22%, transparent);
}

/* S-LUX-3: account-menu chevron — muted, and flips 180° when the menu is
   open (keyed on the trigger's own aria-expanded, so no Alpine binding). */
.user-trigger-chevron {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    color: var(--text-tertiary);
    transition: transform var(--transition-base) var(--ease-out-quart);
}
.user-trigger[aria-expanded="true"] .user-trigger-chevron {
    transform: rotate(180deg);
}
.user-trigger:focus-visible {
    outline: 2px solid var(--border-focus, var(--color-primary));
    outline-offset: 2px;
}
.user-trigger .user-avatar {
    width: 30px;
    height: 30px;
    cursor: inherit;
    border: none;
    box-shadow: none;
}
.user-trigger:hover .user-avatar {
    border: none;
    box-shadow: none;
}
.user-trigger-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    text-align: left;
    min-width: 0;
    max-width: 160px;
}
.user-trigger-name {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}

/* S-TOPBAR-FIT — avatar-only account chip below 1280px.
   Between 1024 and ~1170 the sidebar is in flow at 240px, so .topbar-right
   had ~110px less room than it needed. The flex-shrink repair alone stopped
   the overflow but paid for it out of the two elastic children: at 1024 the
   search box collapsed to 125px and the name ellipsised to a useless 40px
   stub. Dropping the name/role label instead frees up to 160px in one step
   and keeps the search box usable — the avatar still shows who is signed in,
   and the full name + role are in the dropdown it opens.

   MUST sit AFTER the .user-trigger-meta base rule above: a media query adds
   no specificity, so an earlier-placed copy of this loses the source-order
   tie to `display: flex` and silently does nothing.
   NOT a min-width on .search-wrapper — that would re-block the shrink and
   reintroduce the document overflow. */
@media (max-width: 1279px) {
    .user-trigger-meta {
        display: none;
    }
}
.user-trigger-role {
    font-size: 0.6875rem;
    font-weight: 500;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
@media (max-width: 767px) {
    .user-trigger {
        padding: 0;
        border: none;
        background: transparent;
        gap: 0;
    }
    .user-trigger:hover {
        background: transparent;
        box-shadow: none;
    }
    .user-trigger .user-avatar {
        width: 34px;
        height: 34px;
    }
    .user-trigger-meta {
        display: none;
    }
}

/* User dropdown panel */
.user-dropdown {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: 240px;
    /* S-LUX-3 panel recipe: raised surface-2 + strong hairline + radius-lg +
       shadow-lg + card sheen; transform-origin top for the enter animation. */
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--card-sheen), var(--shadow-lg);
    transform-origin: top right;
    z-index: var(--z-dropdown);
    overflow: hidden;
}

/* Header — avatar + name + role + email */
.user-dropdown-header {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px 12px;
}

.user-dropdown-identity {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
    flex: 1;
}

.user-dropdown-name {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-dropdown-meta {
    display: flex;
    align-items: center;
    gap: 6px;
}

.user-dropdown-email {
    font-size: 0.75rem;
    color: var(--text-tertiary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Divider line */
.user-dropdown-divider {
    height: 1px;
    background: var(--border-color);
    margin: 4px 0;
}

/* Menu items — S-LUX-3: inset hover surface with a 6px radius (breathes 6px
   from the panel edge) instead of a full-bleed bar. */
.user-dropdown-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 10px;
    margin: 0 6px;
    border-radius: var(--radius-md);
    font-size: 0.875rem;
    color: var(--text-primary);
    text-decoration: none;
    transition: background var(--transition-fast);
}
.user-dropdown-item:hover,
.user-dropdown-item:focus-visible {
    background: var(--bg-surface-hover);
    text-decoration: none;
    color: var(--text-primary);
    outline: none;
}
.user-dropdown-item svg,
.user-dropdown-item .nav-icon {
    color: var(--text-secondary);
    flex-shrink: 0;
}

/* Sign Out — danger tint on hover */
.user-dropdown-item--danger { color: var(--color-danger); }
.user-dropdown-item--danger svg,
.user-dropdown-item--danger .nav-icon { color: var(--color-danger); }
.user-dropdown-item--danger:hover {
    background: color-mix(in srgb, var(--color-danger) 8%, transparent);
    color: var(--color-danger);
}


/* ============================================================
   07. Page Content & Panels
   ============================================================ */

.page-content {
    flex: 1;
    padding: 28px 32px 0;   /* bottom padding removed — app-footer provides spacing */
    min-width: 0;
}

/* ── Tab table scroll containers ────────────────────────────────────────────
   WHY: Keeps show-page sub-tables within the viewport height so the page
   header and action buttons stay visible. Sticky thead pins column headers
   while scrolling. .tab-filter-bar holds filter/sort controls above the table.
   .tab-table-footer shows row count + optional Load more button below.
*/
.tab-table-container {
    max-height: calc(100vh - 320px);
    overflow-y: auto;
}
.tab-table-container thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    /* S-LUX-2: glass sticky header — frosts the rows scrolling beneath,
       matching the topbar recipe. Solid fallback where backdrop-filter
       is unsupported. */
    background: color-mix(in srgb, var(--bg-surface-2) 82%, transparent);
    -webkit-backdrop-filter: blur(10px) saturate(1.3);
    backdrop-filter: blur(10px) saturate(1.3);
    box-shadow: 0 1px 0 var(--border-color-strong);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .tab-table-container thead th { background: var(--bg-surface-2); }
}
.tab-filter-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    border-bottom: 1px solid var(--border-color);
    flex-wrap: wrap;
    background: var(--bg-surface);
}
.tab-table-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 16px;
    font-size: 0.75rem;
    color: var(--text-muted);
    border-top: 1px solid var(--border-color);
    background: var(--bg-surface);
}

/* ── App footer ──────────────────────────────────────────────────────────────
   Normal flow element at the end of .app-main. Visible only when the user
   scrolls to the bottom of the page — not sticky, not fixed.
*/
/* S-LEGAL-FOOTER-COMMERCIAL: admin footer extended with a middle slot
   for legal links. Outer flex row stays slim; inner pieces wrap on
   narrow screens so the layout degrades gracefully without overflow. */
/* S-SIDEBAR-COLLAPSED-TOOLTIP — body-positioned tooltip for collapsed
   sidebar nav items. Appended to <body> at runtime by the script in
   includes/sidebar.php; that's what lets it escape the sidebar's
   overflow:hidden clipping context (a pure-CSS tooltip can't escape
   without breaking the inner sidebar-nav scroll). position:fixed +
   coordinates calculated from each nav-item's bounding rect on hover. */
.ff-nav-tooltip {
    position: fixed;
    background: var(--bg-surface-2);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 6px 12px;
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1.3;
    white-space: nowrap;
    pointer-events: none;
    z-index: 9999;
    box-shadow: var(--shadow-md);
    transform: translateY(-50%);
    opacity: 0;
    transition: opacity 80ms ease;
    letter-spacing: -0.005em;
}
.ff-nav-tooltip.is-visible {
    opacity: 1;
}

.app-footer {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 24px;
    padding: 10px 32px;
    border-top: 1px solid var(--border-color);
    font-size: 0.6875rem;
    color: var(--text-muted);
    margin-top: auto;
}
.app-footer-left      { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.app-footer-brand strong { color: var(--text-secondary); font-weight: 500; }
.app-footer-divider   { opacity: 0.4; }
.app-footer-legal     { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.app-footer-legal-link {
    color: var(--text-muted);
    text-decoration: none;
    transition: color var(--transition-fast);
}
.app-footer-legal-link:hover { color: var(--color-primary); }
.app-footer-right     { color: var(--text-muted); }
@media (max-width: 900px) {
    .app-footer       { flex-direction: column; gap: 8px; padding: 12px 20px; text-align: center; }
    .app-footer-legal { justify-content: center; }
}

/* Page header (title + actions row) — sticky below the topbar */
.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
    padding-top: 10px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border-color);
    flex-wrap: wrap;
    position: sticky;
    top: var(--topbar-height);
    z-index: calc(var(--z-topbar) - 1);
    background: var(--bg-body);
    margin-top: -10px;
}

.page-header-title {
    margin: 0;
    /* S-LUX-LOUD: bigger, heavier, solid-bright page title — the gradient-fade
       to --text-secondary was softening it; a solid --text-primary at 700 reads
       as a confident headline. */
    font-size: 2rem;             /* 32px — S-LUX-LOUD louder headline */
    font-weight: 700;
    letter-spacing: var(--tracking-tight);
    color: var(--text-primary);
}

.page-header-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    /* S-HEADER-ACTIONS-WRAP: never clip a header toolbar. flex-wrap lets a long
       action row (e.g. the invoice page's 11 buttons) flow onto a second line;
       min-width:0 (replacing flex-shrink:0) lets the actions area shrink so the
       wrap actually engages instead of overflowing off the right edge. Pages
       with a few buttons are unaffected — wrapping only kicks in on overflow. */
    flex-wrap: wrap;
    justify-content: flex-end;
    min-width: 0;
}

/* Card / Panel */
.card {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xl);
    overflow: hidden;
    /* S-LUX-1 Atelier material: 1px inner top sheen + ambient falloff */
    box-shadow: var(--card-sheen), var(--shadow-ambient);
    transition: box-shadow var(--transition-base), border-color var(--transition-base);
}

.card:hover {
    border-color: var(--border-color-strong);
    box-shadow: var(--card-sheen), var(--shadow-ambient), var(--shadow-md);
}

.card-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-color);
    gap: 12px;
    background: var(--bg-surface-2);
}

.card-title {
    /* S-LUX-1: card headers read as section micro-labels (matches the
       uppercase table-header treatment) rather than competing with data */
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-secondary);
    margin: 0;
}

.card-body {
    padding: 20px;
}

.card-footer {
    padding: 14px 20px;
    border-top: 1px solid var(--border-color);
    background: var(--bg-surface-2);
}

/* ── Spec card — elevated key/value table card ──────────────
   Used for showing structured "label: value" data in detail
   modals and profile pages. Promoted from app/admin/equipment/show.php
   so the same look can be reused across the fixed-assets module
   and any other place that needs a labeled property list. */
.spec-card {
    border: 1px solid var(--border-color-strong);
    box-shadow: 0 2px 8px rgba(0,0,0,0.06), 0 0 0 1px rgba(0,0,0,0.03);
}
.spec-card .card-header {
    background: var(--color-primary);
    border-bottom: none;
    padding: 12px 16px;
}
.spec-card .card-header .card-title {
    color: var(--color-on-primary);  /* header band is --color-primary */
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
}
.spec-card .card-body { padding: 0; }

.spec-table {
    width: 100%;
    border-collapse: collapse;
}
.spec-table tr { border-bottom: 1px solid var(--border-color); }
.spec-table tr:last-child { border-bottom: none; }
.spec-table tr:nth-child(even) { background: var(--bg-surface-2); }
.spec-table td {
    padding: 11px 16px;
    font-size: 0.875rem;
    vertical-align: baseline;
}
.spec-table .spec-label {
    width: 160px;
    color: var(--text-tertiary);
    font-size: 0.8125rem;
    font-weight: 500;
    white-space: nowrap;
}
.spec-table .spec-value { color: var(--text-primary); font-weight: 500; }
.spec-table td.text-right { text-align: right; }

/* ── Cost Structure cards (equipment payoff) ───────────────
   The global 160px fixed label column starved the value cell on
   these narrow auto-fit cards, so values containing a space
   ("$1,380.00 /mo", "May 1, 2026") wrapped — or, once forced to a
   single line, overflowed the card edge and got clipped. Fix:
   the grid (set inline) uses a larger 320px minimum so cards break
   to fewer columns before getting too tight; here we trim the label
   column + padding + font so the label and value comfortably share
   one readable line. */
.cost-structure-grid .spec-table td {
    padding: 11px 14px;
    font-size: 0.8125rem;
}
.cost-structure-grid .spec-table .spec-label {
    width: auto;          /* hug content instead of a fixed 160px */
    padding-right: 10px;
}
.cost-structure-grid .spec-table td.text-right {
    white-space: nowrap;  /* keep "$1,380.00 /mo" and dates on one line */
    padding-left: 10px;
}
/* Very small screens: relax nowrap so a single full-width column can
   never clip a value off the card edge. */
@media (max-width: 359px) {
    .cost-structure-grid .spec-table td.text-right { white-space: normal; }
}
/* Earnings & Expenses card: its labels are long descriptive phrases
   ("− Fixed Costs Paid (22 mo @ $627.00)") that would clip under the
   nowrap label rule, so let them wrap; the value stays on one line. */
.cost-structure-grid .earnings-summary .spec-table .spec-label {
    white-space: normal;
}

/* ── Section header inside a card or modal ──────────────────
   Slim divider with an uppercase eyebrow label. Used to break
   up long forms (Create/Edit modals) into visual groups. */
.section-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 24px 0 12px 0;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-color);
}
.section-header:first-child { margin-top: 0; }
.section-header .section-title {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-secondary);
    margin: 0;
}
.section-header .section-hint {
    font-size: 0.75rem;
    color: var(--text-tertiary);
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
}

/* ============================================================
   Stats / Summary tiles
   ------------------------------------------------------------
   Two structures share these styles:
     1. Legacy:  <div class="stat-card stat-card--<color>">
                   <span class="stat-icon stat-icon--<color>">…</span>
                   <div class="stat-label">…</div>
                   <div class="stat-value">…</div>
                   <div class="stat-delta">…</div>
                 </div>
     2. Modern:  <a class="stat-card stat-card--link"> with
                 <div class="stat-card__header"> wrapping
                 <.stat-label> + <.stat-card__icon> (flex row).
   Both must comfortably fit currency values up to ~$1,234,567.89
   without truncating — that's the whole reason this block was
   re-tuned: the previous 200px minimum + 1.875rem value font +
   60px right-padding combo was clipping seven-digit dollar values.
   ============================================================ */
.stat-grid {
    display: grid;
    /* WHY repeat(6): 12-tile dashboard = 2 complete rows of 6 regardless
       of sidebar state. auto-fit shifts column count when the sidebar
       opens/closes, producing orphaned tiles. Fixed 6 keeps both states
       identical at desktop widths. */
    grid-template-columns: repeat(6, 1fr);
    gap: 14px;
    margin-bottom: 20px;
}
@media (max-width: 1100px) {
    .stat-grid { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 700px) {
    .stat-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 480px) {
    .stat-grid { grid-template-columns: repeat(2, 1fr); }
}

/* Invoice addresses: Bill To + Invoice Details cards side-by-side.
   WHY: Extracted from inline style so responsive rules can override it. */
.invoice-addresses {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 24px;
    margin-bottom: 24px;
}

.stat-card {
    /* S-LUX-2: Atelier card recipe (was a hardcoded cream island). Now a
       token-driven surface — sheen + ambient shadow + radius-xl, identical
       material to .card — so tiles read as part of one system and follow
       the theme + brand override. */
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xl);
    padding: 18px;
    box-shadow: var(--card-sheen), var(--shadow-ambient);
    position: relative;
    overflow: hidden;
    min-width: 0;
    transition: box-shadow var(--transition-base), transform var(--transition-base), border-color var(--transition-base);
}

/* S-LUX-LOUD: the coloured top accent bar was removed per operator feedback
   ("looks tacky") — tiles carry their accent via the icon only. */
.stat-card::before {
    display: none;
}

.stat-card:hover {
    /* S-LUX-2: lift 1px + deepen shadow (Atelier card hover language). */
    border-color: var(--border-color-strong);
    box-shadow: var(--card-sheen), var(--shadow-ambient), var(--shadow-md);
    transform: translateY(-1px);
}

/* S-LUX-2: text follows tokens (the tile is now a themed surface, not a
   fixed cream card) — so labels/values read correctly in BOTH themes and
   the old dark-on-cream literals are gone. */
.stat-card .stat-label { color: var(--text-tertiary); }
.stat-card .stat-value { color: var(--text-primary); }
.stat-card .stat-delta { color: var(--text-tertiary); }

/* Per-card accent color variants */
.stat-card--green  { --stat-accent: #22c55e; }
.stat-card--blue   { --stat-accent: #3b82f6; }
.stat-card--amber  { --stat-accent: #f59e0b; }
.stat-card--red    { --stat-accent: #ef4444; }
.stat-card--purple { --stat-accent: #8b5cf6; }
.stat-card--teal   { --stat-accent: #14b8a6; }
.stat-card--slate  { --stat-accent: #64748b; }
.stat-card--orange { --stat-accent: #f97316; }

/* Decorative icon container — positioned top-right (LEGACY structure).
   Modern .stat-card__header / .stat-card__icon are defined separately
   below the dashboard variants block. */
.stat-icon {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.stat-icon svg {
    width: 18px;
    height: 18px;
}
.stat-icon--green  { background: rgba(34,197,94,0.12);  color: #22c55e; }
.stat-icon--blue   { background: rgba(59,130,246,0.12); color: #60a5fa; }
.stat-icon--amber  { background: rgba(245,158,11,0.12); color: #f59e0b; }
.stat-icon--red    { background: rgba(239,68,68,0.12);  color: #f87171; }
.stat-icon--purple { background: rgba(139,92,246,0.12); color: #a78bfa; }
.stat-icon--teal   { background: rgba(20,184,166,0.12); color: #2dd4bf; }
.stat-icon--slate  { background: rgba(100,116,139,0.12);color: #94a3b8; }
.stat-icon--orange { background: rgba(249,115,22,0.12); color: #f97316; }

.stat-label {
    /* S-LUX-2: uppercase micro-label — matches thead / card-title language. */
    font-size: 0.6875rem;           /* 11px */
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-tertiary);
    line-height: 1.3;
    margin-bottom: 6px;
    /* WHY: reserve room for absolute .stat-icon in legacy tile structure.
       Tiles that use the flex .stat-card__header (icon in-flow, e.g. the
       dashboard KPIs) zero this out below so the label gets full width and
       never clips — see .stat-card__header .stat-label. */
    padding-right: 38px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* S-LUX-2-FIX (no-crop): when the icon sits in the flex header it is
   in-flow, so the label needs no right reservation — reclaim the full
   width so labels like "OPEN WORK ORDERS" / "ACTIVE RESERVATIONS" show
   in full instead of clipping to "OPEN WORK…". */
.stat-card__header .stat-label {
    padding-right: 0;
}

.stat-value {
    /* S-LUX-2: large display numeral — weight 600, tight tracking, tabular
       figures (via the .stat-value group in the tabular-nums rule). Sans
       (not mono) reads as a headline figure.
       S-LUX-2-FIX (no-crop): 24px (was 28px) so wide money values like
       "$55,050.00" fit the 6-up dashboard tile (~144px value box) without
       clipping; the ellipsis clip is removed so a figure never renders as
       "$55,050…". Narrow enough at 24px that the full number always shows;
       the #kpi-grid mobile rule shrinks it further to 1.125rem. */
    font-size: 1.5rem;              /* 24px */
    font-weight: 600;
    letter-spacing: var(--tracking-tight);
    color: var(--text-primary);
    font-family: var(--font-sans);
    line-height: 1.15;
    margin-bottom: 4px;
    min-width: 0;
    white-space: nowrap;
}

/* WHY: Dates like "Aug 31, 2026" clip at 1.5rem bold mono — drop to 1.125rem to fit */
.stat-value--date {
    font-size: 1.125rem;
    overflow: visible;
    text-overflow: unset;
}

/* WHY: VINs are 17 chars and overflow the tile at 1.5rem bold — shrink to fit.
   Kept on ONE line: `word-break: break-all` used to split a VIN mid-string
   ("...20011 / 66"), which is both ugly and hard to read back against a
   physical plate. At 0.875rem a 17-char VIN fits every tile width the grid
   produces down to the 2-column tablet breakpoint; anything longer ellipsises
   with the full value available on hover (the markup sets a title). */
.stat-value--vin {
    font-size: 0.875rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1.4;
}

.stat-delta {
    font-size: 0.75rem;              /* 12px — was 13px */
    color: var(--text-tertiary);
    line-height: 1.3;
    /* S-LUX-2-FIX (no-crop): let long deltas like "$60,332.85 outstanding"
       wrap to a second line instead of being trimmed to an ellipsis — they
       break cleanly at the space, never mid-amount. */
    white-space: normal;
    overflow-wrap: anywhere;
}

/* S-LUX-2 — delta CHIP variants (opt-in via .stat-delta--up / --down).
   Base .stat-delta stays a plain subtitle for descriptive sub-lines
   ("Active lease rates", "19 of 49 units"); pages that show a real
   +/- movement add the modifier to get a badge-language pill. */
.stat-delta--up,
.stat-delta--down {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 1px 7px;
    border-radius: var(--radius-full);
    font-size: 0.6875rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1.5;
}
.stat-delta--up   { background: var(--color-success-light); color: var(--color-success-text); }
.stat-delta--down { background: var(--color-danger-light);  color: var(--color-danger-text); }
.stat-delta--up::before   { content: '▲'; font-size: 0.5625rem; }
.stat-delta--down::before { content: '▼'; font-size: 0.5625rem; }

/* S-LUX-2 — sparkline hook. A fixed-height slot at the tile foot for a
   later session to populate (kpi sparkline data queries are out of scope
   here — D-LUX2-4). Empty today; reserves rhythm without shifting layout. */
.kpi-spark {
    margin-top: 10px;
    height: 34px;
    min-height: 34px;
}
.kpi-spark:empty { display: none; }

/* Clickable stat-card KPI tiles — active ring + pointer affordance */
.stat-card[style*="cursor:pointer"],
.stat-card.stat-card--link {
    cursor: pointer;
    /* WHY: <a> tags need explicit reset so the inner text doesn't underline. */
    text-decoration: none;
    color: inherit;
    display: block;
}
.stat-card.stat-card--link:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}
.stat-card.ring-active {
    box-shadow: 0 0 0 2px var(--stat-accent, var(--color-primary)), var(--shadow-md);
    border-color: var(--stat-accent, var(--color-primary));
}

/* ── Rate-item cards (shared: rates dashboard + rate-card editor) ──────────
   Cream cards on the dark shell; white + crisp shadow in day mode. */
/* S-LUX-5: rate cards were the LAST hardcoded cream light-island (like the
   stat-card island S-LUX-2 retired) — a light card floating in the dark rates
   dashboard. Rebased to the Atelier card recipe + tokens so it adapts to the
   theme; the dark-secondary button override is dropped (the standard
   .btn-secondary now reads correctly on the token surface); pills use the
   semantic light/text token pairs. This also makes __empty theme-correct. */
.rate-item-card {
    background: var(--bg-surface); color: var(--text-primary);
    border: 1px solid var(--border-color); border-radius: var(--radius-xl); overflow: hidden;
    box-shadow: var(--card-sheen), var(--shadow-ambient);
    transition: transform .16s ease, box-shadow .16s ease;
}
.rate-item-card:hover { transform: translateY(-2px); box-shadow: var(--card-sheen), var(--shadow-ambient), var(--shadow-md); }
.rate-item-card__head { display: flex; justify-content: space-between; align-items: center; gap: 8px;
                        padding: 13px 15px; border-bottom: 1px solid var(--border-color); }
.rate-item-card__type { font-weight: 600; font-size: 0.9375rem; line-height: 1.25; color: var(--text-primary); }
.rate-item-card__type--name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rate-item-card__cur  { font-size: 0.68rem; font-weight: 700; letter-spacing: .02em; color: var(--text-tertiary);
                        background: var(--bg-surface-2); border-radius: 999px; padding: 3px 9px;
                        flex-shrink: 0; white-space: nowrap; }
.rate-item-card__rows { padding: 14px 15px; display: grid; grid-template-columns: auto 1fr; gap: 9px 16px;
                        font-size: 0.9rem; align-items: baseline; }
.rate-item-card__k    { color: var(--text-secondary); font-weight: 500; }
.rate-item-card__v    { text-align: right; color: var(--text-primary); font-variant-numeric: tabular-nums; }
.rate-item-card__empty{ grid-column: 1 / -1; color: var(--text-tertiary); font-style: italic; }
.rate-item-card__foot { padding: 11px 15px; border-top: 1px solid var(--border-color); display: flex; gap: 8px; }
/* Status pills (dashboard rate cards) + scope link — semantic token pairs. */
.rate-item-card__pill { font-size: 0.68rem; font-weight: 700; letter-spacing: .02em;
                        border-radius: 999px; padding: 3px 9px; flex-shrink: 0; white-space: nowrap; }
.rate-item-card__pill--active   { background: var(--color-success-light); color: var(--color-success-text); }
.rate-item-card__pill--inactive { background: var(--bg-surface-2);        color: var(--text-tertiary); }
.rate-item-card__pill--default  { background: var(--color-info-light);    color: var(--color-info-text); }
.rate-item-card__pill--global   { background: var(--bg-surface-2);        color: var(--text-tertiary); }
.rate-item-card a.rcard-link      { color: var(--color-primary); font-weight: 500; }
.rate-item-card a.rcard-link:hover{ text-decoration: underline; }
/* Day mode: white + crisp shadow (cream blends into the light page). */
[data-theme="light"] .rate-item-card { background: #ffffff; border-color: rgba(0,0,0,0.08);
    box-shadow: 0 1px 3px rgba(0,0,0,0.07), 0 6px 18px rgba(0,0,0,0.06); }
[data-theme="light"] .rate-item-card:hover {
    box-shadow: 0 8px 22px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.08); }


/* ============================================================
   08. Buttons
   ============================================================ */

/* Base button */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    font-family: inherit;
    font-weight: 500;
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    /* Fallback text colour. WHY: the global `button {}` reset (background:none,
       border:none) leaves `color` unset, so any .btn missing a colour variant
       (e.g. a stray `class="btn btn-sm"` or an undefined variant) falls through
       to the UA system colour `buttontext` — which our CSS-variable dark theme
       never adapts, rendering black text on the dark surface (unreadable).
       Every colour variant below is defined after .btn and overrides this. */
    color: var(--text-primary);
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--transition-fast), color var(--transition-fast),
                border-color var(--transition-fast), box-shadow var(--transition-fast),
                transform var(--transition-fast), opacity var(--transition-fast);
    line-height: 1;
    vertical-align: middle;
    user-select: none;
}

/* S-LUX-3: every button springs on press (transform only), not just
   .btn-primary. Disabled buttons stay still. */
.btn:not(:disabled):not([disabled]):not(.is-disabled):active {
    transform: scale(0.985);
}

.btn:focus-visible {
    outline: 2px solid var(--border-focus);
    outline-offset: 2px;
}

.btn:disabled, .btn[disabled], .btn.is-disabled {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* Sizes */
.btn-xs {
    font-size: 0.75rem;
    padding: 4px 8px;
    height: 26px;
    border-radius: var(--radius-sm);
    gap: 4px;
}

.btn-sm {
    font-size: 0.8125rem;
    padding: 5px 12px;
    height: 32px;
}

.btn-md {
    font-size: 0.875rem;
    padding: 7px 16px;
    height: 38px;
}

.btn-lg {
    font-size: 0.9375rem;
    padding: 10px 20px;
    height: 46px;
    gap: 8px;
}

.btn-xl {                                                    /* FIX #35 */
    font-size: 1rem;
    padding: 12px 24px;
    height: 54px;
    gap: 10px;
}

/* Default .btn with no modifier = medium */
.btn:not(.btn-xs):not(.btn-sm):not(.btn-lg):not(.btn-icon):not(.btn-link) {
    font-size: 0.875rem;
    padding: 7px 16px;
    height: 38px;
}

/* Variants */
.btn-primary {
    background: var(--color-primary);
    color: var(--color-on-primary);
    border-color: var(--color-primary);
    /* S-LUX-1: subtle top inner highlight — "lit from above" */
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15);
    transition: background var(--transition-fast), border-color var(--transition-fast),
                transform var(--transition-fast), box-shadow var(--transition-fast);
}
.btn-primary:hover {
    background: var(--color-primary-hover);
    border-color: var(--color-primary-hover);
    text-decoration: none;
    color: var(--color-on-primary);
}
.btn-primary:active {
    transform: scale(0.985);
}

.btn-secondary {
    background: var(--bg-surface);
    color: var(--text-secondary);
    border-color: var(--border-color-strong);
}
.btn-secondary:hover {
    background: var(--bg-surface-hover);
    color: var(--text-primary);
    border-color: var(--border-color-strong);
    text-decoration: none;
}

.btn-danger {
    background: var(--color-danger);
    color: #ffffff;
    border-color: var(--color-danger);
}
.btn-danger:hover {
    background: #b91c1c;
    border-color: #b91c1c;
    text-decoration: none;
    color: #ffffff;
}

.btn-success {
    background: var(--color-success);
    color: #ffffff;
    border-color: var(--color-success);
}
.btn-success:hover {
    background: #16a34a;
    text-decoration: none;
    color: #ffffff;
}

.btn-warning {
    background: var(--color-warning);
    color: #0f172a;
    border-color: var(--color-warning);
}
.btn-warning:hover {
    background: #ca8a04;
    border-color: #ca8a04;
    text-decoration: none;
    color: #0f172a;
}

.btn-ghost {
    background: transparent;
    color: var(--text-secondary);
    border-color: var(--border-color);
}
.btn-ghost:hover {
    background: var(--bg-surface-hover);
    color: var(--text-primary);
    border-color: var(--border-color-strong);
    text-decoration: none;
}

/* Soft-danger outline for inline table destructive actions —
   less aggressive than solid .btn-danger, appropriate for row-level Remove/Delete */
.btn-outline-danger {
    background: transparent;
    color: var(--color-danger);
    border-color: color-mix(in srgb, var(--color-danger) 35%, transparent);
}
.btn-outline-danger:hover {
    background: var(--color-danger-light);
    border-color: var(--color-danger);
    color: var(--color-danger);
    text-decoration: none;
}

/* Soft-warning outline — amber accent for cautionary, non-destructive actions
   (e.g. "Reopen Lease"). Mirrors .btn-outline-danger. Text uses
   --color-warning-text (not raw --color-warning) because the bare amber is too
   low-contrast as text; --color-warning-text is tuned for legibility in BOTH
   themes (#facc15 on dark, #b45309 on light). Previously undefined, so the
   button fell back to bare .btn and rendered unreadable in dark mode. */
.btn-outline-warning {
    background: transparent;
    color: var(--color-warning-text);
    border-color: color-mix(in srgb, var(--color-warning) 40%, transparent);
}
.btn-outline-warning:hover {
    background: var(--color-warning-light);
    border-color: var(--color-warning);
    color: var(--color-warning-text);
    text-decoration: none;
}

/* Icon-only button */
.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    border: none;
    background: transparent;
    cursor: pointer;
    /* S-LUX-3: add transform so :active can spring (transform/opacity only). */
    transition: background var(--transition-fast), color var(--transition-fast),
                transform var(--transition-fast);
    flex-shrink: 0;
}

.btn-icon:hover {
    background: var(--bg-surface-hover);
    color: var(--text-primary);
}

/* S-LUX-3: tactile press on every icon button. */
.btn-icon:active {
    transform: scale(0.94);
}

.btn-icon:focus-visible {
    outline: 2px solid var(--border-focus);
    outline-offset: 2px;
}

/* S-LUX-3: topbar icon buttons read as circular hit areas (36px pills). */
.topbar-right .btn-icon,
.topbar-left .btn-icon {
    border-radius: var(--radius-full);
}

.btn-icon .nav-icon {
    width: 18px;
    height: 18px;
}

/* Link-styled button */
.btn-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: none;
    border: none;
    padding: 0;
    color: var(--color-primary);
    font-size: inherit;
    font-weight: 500;
    cursor: pointer;
    text-decoration: none;
    transition: color var(--transition-fast);
}

.btn-link:hover {
    color: var(--color-primary-hover);
    text-decoration: underline;
}

.btn-link.btn-sm { font-size: 0.8125rem; }
.btn-link.btn-xs { font-size: 0.75rem; }

/* Button with icon
   WHY: previously used `:first-child:not(:only-child)` to skip icon-only
   buttons. That selector incorrectly treated text-node-sibling SVGs as
   :only-child (CSS :only-child ignores text nodes), so buttons emitted
   as `<button> <svg> Label </button>` escaped the 16×16 sizing and
   ballooned to full button width once RESPONSIVE-1 made `.btn { width:
   100% }` on mobile. Switched to `.btn > svg` which sizes any direct
   SVG child; icon-only buttons should use the dedicated `.btn-icon`
   class which doesn't match `.btn > svg` here. The chat widget send
   button has inline `style="width:14px;height:14px"` so its inline
   styles still win. */
.btn .btn-icon-left,
.btn > svg {
    width: 16px;
    height: 16px;
}

/* Loading state */
.btn.is-loading {
    pointer-events: none;
    opacity: 0.75;
    position: relative;
}

.btn.is-loading::after {
    content: '';
    position: absolute;
    width: 14px;
    height: 14px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: ff-spin 0.6s linear infinite;
}

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


/* ============================================================
   09. Badges & Status Chips
   ============================================================ */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: var(--radius-full);
    /* S-LUX-1: tightened micro-label — 11px caps with wide tracking */
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    white-space: nowrap;
    line-height: 1.6;
}

.badge-primary  { background: var(--color-primary-light);  color: var(--color-primary-text);  }
.badge-success  { background: var(--color-success-light);  color: var(--color-success-text);  }
.badge-warning  { background: var(--color-warning-light);  color: var(--color-warning-text);  }
.badge-danger   { background: var(--color-danger-light);   color: var(--color-danger-text);   }
.badge-info     { background: var(--color-info-light);     color: var(--color-info-text);     }
.badge-neutral  { background: var(--bg-surface-2);         color: var(--text-secondary);      border: 1px solid var(--border-color); }
.badge-purple   { background: rgba(139, 92, 246, 0.14);    color: #a78bfa;                    }

/* Dot indicator */
.badge::before {
    content: '';
    display: inline-block;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
}

.badge-no-dot::before { display: none; }

/* Lease / invoice status shorthands */
.status-active     { @extend .badge-success; }
.status-pending    { background: var(--color-warning-light); color: var(--color-warning-text); }
.status-overdue    { background: var(--color-danger-light);  color: var(--color-danger-text);  }
.status-draft      { background: var(--bg-surface-2);        color: var(--text-tertiary);       }
.status-closed     { background: var(--bg-surface-2);        color: var(--text-tertiary);       }
.status-void       { background: var(--bg-surface-2);        color: var(--text-tertiary);       }


/* ============================================================
   10. Forms
   ============================================================ */

.form-group {
    /* S-LUX-2.5: field vertical rhythm 20px (density variants still override) */
    margin-bottom: 20px;
}

.form-group:last-child {
    margin-bottom: 0;
}

/* S-DESIGN-INPUTS (2026-05-03): Apple macOS aesthetic.
   Labels render in muted alpha (--label-text). Inputs are borderless
   tinted-gray pills. Focus brightens the bg and adds a 2px brand-orange
   ring via box-shadow (no border, so no layout shift on focus). */
.form-label {
    display: block;
    /* S-LUX-1: uppercase micro-label — 11px caps, wide tracking */
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--label-text);
    margin-bottom: 6px;
    letter-spacing: var(--tracking-label);
}

.form-label .required {
    /* S-LUX-2.5: quieter required marker — danger-text at 0.7 so it reads as
       a hint, not an alarm, on an unfilled field. */
    color: var(--color-danger-text);
    opacity: 0.7;
    margin-left: 2px;
}

/* Text inputs, selects, textareas */
/* .form-input and .form-textarea are aliases for .form-control — used in several modules */
.form-control,
.form-input,
.form-select {
    display: block;
    width: 100%;
    height: var(--input-height);
    padding: 0 12px;
    background: var(--input-bg);
    border: none;
    border-radius: 8px;
    color: var(--input-text);
    font-size: 0.9rem;
    font-family: inherit;
    line-height: 1;
    transition: background var(--transition-fast), box-shadow var(--transition-fast);
    appearance: none;
    -webkit-appearance: none;
    outline: none;
}

.form-control::placeholder,
.form-input::placeholder,
.form-select::placeholder { color: var(--input-placeholder); }

/* S-LUX-2.5 (D-LUX25-1): quiet hover — the pill bg nudges ~60% toward its
   focus fill so the field feels responsive before you commit to it, without
   a border or a jump. Suppressed while focused (the ring owns that state) and
   while disabled. */
.form-control:hover:not(:focus):not(:disabled),
.form-input:hover:not(:focus):not(:disabled),
.form-select:hover:not(:focus):not(:disabled) {
    background: color-mix(in srgb, var(--input-bg-focus) 60%, var(--input-bg));
}

.form-control:focus,
.form-input:focus,
.form-select:focus {
    outline: none;
    background: var(--input-bg-focus);
    box-shadow: 0 0 0 2px var(--color-primary);
}

.form-control:disabled,
.form-input:disabled,
.form-select:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Invalid state — danger ring via box-shadow (no border to color). */
.form-control.is-invalid,
.form-input.is-invalid,
.form-select.is-invalid {
    box-shadow: 0 0 0 2px var(--color-danger);
}

.form-control.is-invalid:focus,
.form-input.is-invalid:focus,
.form-select.is-invalid:focus {
    box-shadow: 0 0 0 2px var(--color-danger);
}

.form-control-sm,
.form-input-sm   { height: var(--input-height-sm); font-size: 0.8125rem; padding: 0 10px; }
.form-control-lg { height: var(--input-height-lg); font-size: 1rem; padding: 0 14px; }
.form-select-sm  { height: var(--input-height-sm); font-size: 0.8125rem; padding: 0 10px; padding-right: 32px; }

/* Select chevron — S-LUX-2.5: warm --text-tertiary (was a cool slate #94a3b8).
   A data-URI can't read a CSS var, so the stroke is the resolved warm-dark
   tertiary #736D61; the [data-theme="light"] override below swaps in the
   light tertiary #8A857A so the chevron tracks the theme.
   Applies to every native <select> pill — not just .form-select — because the
   base .form-control sets appearance:none, which strips the browser's own
   arrow; a bare select.form-control (e.g. the HH/MM time pickers) would
   otherwise have no dropdown affordance at all. Multiselects (list boxes)
   are excluded. */
.form-select,
select.form-control:not([multiple]) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23736D61'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m19.5 8.25-7.5 7.5-7.5-7.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 16px;
    padding-right: 36px;
}
[data-theme="light"] .form-select,
[data-theme="light"] select.form-control:not([multiple]) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='%238A857A'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m19.5 8.25-7.5 7.5-7.5-7.5'/%3E%3C/svg%3E");
}

/* Textarea — S-LUX-2.5: min-height 80px, pill radius parity, vertical resize only */
textarea.form-control,
textarea.form-input {
    height: auto;
    min-height: 80px;
    padding: 10px 12px;
    border-radius: 8px;
    resize: vertical;
    line-height: 1.5;
}

/* S-LUX-2.5: native date/time picker indicator — invert on the dark theme so
   the black glyph reads on the warm-dark pill; leave normal on light. */
.form-control::-webkit-calendar-picker-indicator {
    opacity: 0.6;
    cursor: pointer;
    transition: opacity var(--transition-fast);
}
.form-control:hover::-webkit-calendar-picker-indicator { opacity: 0.9; }
:root:not([data-theme="light"]) .form-control::-webkit-calendar-picker-indicator {
    filter: invert(1) brightness(0.85);
}

/* S-LUX-2.5: money inputs (.font-mono number fields) drop the spinner buttons —
   the up/down arrows are meaningless on a dollar amount and crowd the pill.
   Scoped to .font-mono so plain count fields (e.g. billing periods) keep them.
   tabular-nums already applies via the .font-mono group in §00. */
.form-control.font-mono::-webkit-outer-spin-button,
.form-control.font-mono::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.form-control.font-mono[type="number"] { -moz-appearance: textfield; }

/* Form hint / helper text — S-LUX-2.5: 12px, --text-tertiary, 4px gap */
.form-hint {
    margin-top: 4px;
    font-size: 0.75rem;
    color: var(--text-tertiary);
}

/* Error message — S-LUX-2.5: 12px, --color-danger-text, 4px gap (no motion) */
.form-error {
    margin-top: 4px;
    font-size: 0.75rem;
    color: var(--color-danger-text);
}

/* ============================================================
   VALID-2 — Field-level + form-level validation messaging
   ============================================================
   .field-error          per-input error shown directly below
   .form-error-banner    form-level banner (e.g. unbalanced JE)
   .form-control.ff-invalid  same as .is-invalid, added by FF_Validate
   ============================================================ */
.field-error {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    /* S-LUX-2.5: 12px, 4px gap (matches .form-error) */
    margin-top: 4px;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--color-danger-text);
    line-height: 1.4;
}
.field-error .icon {
    flex-shrink: 0;
    font-size: 0.9375rem;
    line-height: 1;
    margin-top: 1px;
}
.field-error:empty { display: none; }

.form-error-banner {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 12px 16px;
    margin-bottom: 16px;
    background: var(--badge-danger-bg);
    color: var(--badge-danger-text);
    border: 1px solid rgba(220, 38, 38, 0.3);
    border-radius: var(--radius-md);
    font-size: 0.875rem;
    line-height: 1.5;
}
.form-error-banner strong { font-weight: 600; }
.form-error-banner .icon {
    flex-shrink: 0;
    font-size: 1.125rem;
    line-height: 1;
    margin-top: 1px;
}
.form-error-banner:empty { display: none; }

.form-control.ff-invalid,
.form-input.ff-invalid,
.form-select.ff-invalid {
    box-shadow: 0 0 0 2px var(--color-danger);
}

/* S-LUX-2.5 (D-LUX25-4): the same 2px danger ring on the OTHER control types,
   applied to each control's outer box. Covers both the native .is-invalid and
   FF_Validate's .ff-invalid. No motion — the ring is the whole error state. */
.form-check-input.is-invalid,
.form-check-input.ff-invalid {
    box-shadow: 0 0 0 2px var(--color-danger);
    border-color: var(--color-danger);
}
.ff-picker.is-invalid .ff-picker-input,
.ff-picker.ff-invalid .ff-picker-input,
.ff-picker-input.is-invalid,
.ff-picker-input.ff-invalid {
    box-shadow: 0 0 0 2px var(--color-danger);
}
.ff-segment-control.is-invalid,
.ff-segment-control.ff-invalid {
    box-shadow: 0 0 0 2px var(--color-danger);
}

/* Inline alert banners — used in forms and modals */
.alert {
    padding: 12px 16px;
    border-radius: var(--radius-md);
    font-size: 0.875rem;
    line-height: 1.5;
    border: 1px solid transparent;
}
/* S-LUX-5: the .alert-* variants referenced undefined --badge-*-bg/-text tokens,
   so every .alert rendered a transparent, bg-less box (51 markup consumers) — the
   very reason accounting screens hardcoded amber/red inline. Rebased onto the real
   --color-*-light / --color-*-text token pairs (theme-adaptive) with a color-mix
   border. This is the root fix behind the S-LUX-5 banner-idiom swaps. */
.alert-danger  { background: var(--color-danger-light);  color: var(--color-danger-text);  border-color: color-mix(in srgb, var(--color-danger) 28%, transparent); }
.alert-success { background: var(--color-success-light); color: var(--color-success-text); border-color: color-mix(in srgb, var(--color-success) 28%, transparent); }
.alert-warning { background: var(--color-warning-light); color: var(--color-warning-text); border-color: color-mix(in srgb, var(--color-warning) 28%, transparent); }
.alert-info    { background: var(--color-info-light);    color: var(--color-info-text);    border-color: color-mix(in srgb, var(--color-info) 28%, transparent); }

/* Checkbox & Radio */
.form-check {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    cursor: pointer;
}

/* S-LUX-2.5 (D-LUX25-2): checkboxes/radios keep their border (the
   S-DESIGN-INPUTS carve-out) but get the full Atelier custom pass — a
   1.5px hairline box that fills with the brand colour and shows a white
   glyph when checked. appearance:none removes the platform control so the
   glyph is our own SVG; the checked fill consumes var(--color-primary), so
   the white-label brand-override chain recolours it. */
.form-check-input {
    appearance: none;
    -webkit-appearance: none;
    width: 16px;
    height: 16px;
    border: 1.5px solid var(--border-color-strong);
    border-radius: var(--radius-sm);
    background: var(--input-bg);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 100% 100%;
    cursor: pointer;
    flex-shrink: 0;
    margin-top: 2px;
    transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

.form-check-input[type="radio"] { border-radius: 50%; }

.form-check-input:checked {
    background-color: var(--color-primary);
    border-color: var(--color-primary);
    /* white check glyph */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%230f0f0f' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E");
    background-size: 15px 15px;
}
.form-check-input[type="radio"]:checked {
    /* white centre dot instead of a check */
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3.25' fill='%230f0f0f'/%3E%3C/svg%3E");
    background-size: 16px 16px;
}
.form-check-input:indeterminate {
    background-color: var(--color-primary);
    border-color: var(--color-primary);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Crect x='3.5' y='7' width='9' height='2' rx='1' fill='%230f0f0f'/%3E%3C/svg%3E");
    background-size: 15px 15px;
}
.form-check-input:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--bg-surface), 0 0 0 4px var(--color-primary);
}
.form-check-input:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.form-check-label {
    font-size: 0.9rem;
    /* S-LUX-2.5: quiet by default, brightens to primary text when its box is
       checked — a subtle confirmation without a colour shout. */
    color: var(--text-secondary);
    cursor: pointer;
    line-height: 1.4;
    transition: color var(--transition-fast);
}
.form-check:has(.form-check-input:checked) .form-check-label {
    color: var(--text-primary);
}
.form-check-input:disabled + .form-check-label,
.form-check:has(.form-check-input:disabled) .form-check-label {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Form row / grid */
/* WHY display:grid on every variant: the column classes were previously
   relied on standalone (e.g. <div class="form-row-2">) but only .form-row
   carried display:grid — so those rows silently stacked into one column.
   Setting it on all variants makes form-row-2/3/4 work on their own. */
.form-row,
.form-row-2,
.form-row-3,
.form-row-4 {
    display: grid;
    gap: 16px;
}

.form-row-2 { grid-template-columns: 1fr 1fr; }
.form-row-3 { grid-template-columns: 1fr 1fr 1fr; }
.form-row-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }

/* Input with icon prefix */
.input-group {
    position: relative;
    display: flex;
    align-items: stretch;
}

/* S-DESIGN-INPUTS: prefix/suffix render as part of one continuous Apple
   pill with the input — same bg, no border, joined radii. */
.input-group-prefix,
.input-group-suffix {
    display: flex;
    align-items: center;
    padding: 0 12px;
    background: var(--input-bg);
    border: none;
    color: var(--text-secondary);
    font-size: 0.875rem;
    white-space: nowrap;
    flex-shrink: 0;
}

.input-group-prefix {
    border-radius: 8px 0 0 8px;
}

.input-group-suffix {
    border-radius: 0 8px 8px 0;
}

.input-group .form-control {
    flex: 1;
    border-radius: 0;
}

.input-group:has(.input-group-prefix) .form-control { border-radius: 0 8px 8px 0; }
.input-group:has(.input-group-suffix) .form-control { border-radius: 8px 0 0 8px; }
.input-group:has(.input-group-prefix):has(.input-group-suffix) .form-control { border-radius: 0; }


/* ============================================================
   11. Tables
   ============================================================ */

.table-wrapper {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: var(--radius-xl);
    border: 1px solid var(--border-color);
    box-shadow: var(--shadow-sm);
}

.table,
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
    background: var(--bg-surface);
}

.table thead th,
.data-table thead th {
    /* S-LUX-2: micro-label thead — weight 600, tracking token, stronger
       bottom hairline. Row rhythm bumped to 14px (was 12) to match tbody. */
    padding: 14px 16px;
    text-align: left;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-tertiary);
    background: var(--bg-surface-2);
    border-bottom: 1px solid var(--border-color-strong);
    white-space: nowrap;
}

.table thead th:first-child,
.data-table thead th:first-child { border-radius: var(--radius-xl) 0 0 0; }
.table thead th:last-child,
.data-table thead th:last-child  { border-radius: 0 var(--radius-xl) 0 0; }

.table tbody tr,
.data-table tbody tr {
    border-bottom: 1px solid var(--border-color);
    transition: background var(--transition-fast);
}

.table tbody tr:last-child,
.data-table tbody tr:last-child { border-bottom: none; }

.table tbody tr:hover,
.data-table tbody tr:hover {
    background: var(--bg-surface-hover);
}

.table tbody td,
.data-table tbody td {
    /* S-LUX-2: row rhythm 13→14px (comfortable). compact/spacious density
       rules carry higher specificity (body[data-density] .page-content …)
       so they still win — compact stays compact. */
    padding: 14px 16px;
    color: var(--text-primary);
    vertical-align: middle;
}

.table .text-right,
.table th.text-right,
.table td.text-right,
.data-table .text-right,
.data-table th.text-right,
.data-table td.text-right { text-align: right; }

.table .text-center,
.table th.text-center,
.table td.text-center,
.data-table .text-center,
.data-table th.text-center,
.data-table td.text-center { text-align: center; }

/* Sortable column header */
.th-sortable {
    cursor: pointer;
    user-select: none;
}

.th-sortable:hover { color: var(--text-primary); }

/* Table toolbar (search + filters + actions) */
.table-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}

.table-toolbar-left {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: nowrap; /* keep filters on one row */
    flex: 1;
    min-width: 0;
}

/* Prevent any single filter control from dominating the row */
.table-toolbar-left .form-control,
.table-toolbar-left .form-select {
    flex: 1;
    min-width: 0;
    width: auto;
}

.table-toolbar-right {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: nowrap;
    flex-shrink: 0;
}

/* Pagination */
.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px 16px;
    border-top: 1px solid var(--border-color);
    font-size: 0.875rem;
    color: var(--text-secondary);
    flex-wrap: wrap;
    gap: 12px;
}

/* Top-of-table pagination bar: divider sits below it, not above. */
.pagination-top { border-top: none; border-bottom: 1px solid var(--border-color); }

.pagination-info { white-space: nowrap; }

.pagination-controls {
    display: flex;
    align-items: center;
    gap: 4px;
}

/* "Jump to page" control (Page [__] of N) — center group of the bar. */
.pagination-jump {
    display: flex;
    align-items: center;
    gap: 6px;
}

.pagination-jump .pagination-info {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 0;
}

.pagination-input {
    width: 4em;
    height: 32px;
    padding: 0 6px;
    text-align: center;
    font-size: 0.875rem;
    color: var(--text-primary);
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    -moz-appearance: textfield;
}
/* Keep the native number spinners (up/down arrows) — they aid quick stepping. */
.pagination-input:focus {
    outline: none;
    border-color: var(--color-primary);
}

.page-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 32px;
    padding: 0 6px;
    border-radius: var(--radius-md);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-secondary);
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}

.page-btn:hover {
    background: var(--bg-surface-2);
    color: var(--text-primary);
}

.page-btn.is-active {
    /* S-LUX-2: active page = brand TEXT on a hairline, not a solid fill
       (quieter, matches the Atelier neutral-chrome language). */
    background: transparent;
    color: var(--color-primary);
    font-weight: 600;
    border-color: var(--border-color-strong);
}

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


/* ============================================================
   12. Modals
   ============================================================ */

.modal-overlay {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
}

/* ============================================================
   FF_LookupPicker — searchable dropdown w/ manual-entry fallback
   ============================================================ */
.ff-picker {
    position: relative;
}
.ff-picker-input-wrap {
    position: relative;
    display: flex;
    align-items: center;
    gap: 6px;
}
.ff-picker-input {
    flex: 1;
    min-width: 0;
}
.ff-picker-toggle {
    flex: 0 0 auto;
    padding: 4px 8px;
    font-size: 0.7rem;
    border-radius: var(--radius-sm);
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--transition-fast), color var(--transition-fast);
}
.ff-picker-toggle:hover {
    background: var(--bg-surface);
    color: var(--text-primary);
}
.ff-picker-toggle--active {
    /* S-LUX-2.5: brand fill (was a stale #2563eb blue fallback) so the active
       toggle follows the white-label chain. */
    background: var(--color-primary);
    color: var(--color-on-primary);
    border-color: var(--color-primary);
}
.ff-picker-clear {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    background: transparent;
    border: none;
    color: var(--text-tertiary);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
    padding: 2px 6px;
}
.ff-picker-clear:hover { color: var(--text-primary); }
.ff-picker-dropdown {
    position: absolute;
    z-index: 10;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    max-height: 260px;
    overflow-y: auto;
    /* S-LUX-2.5: match the panel recipe used by the other picker block —
       raised surface-2 + strong hairline + radius-lg + shadow-lg. */
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}
.ff-picker-item {
    padding: 8px 12px;
    font-size: 0.825rem;
    cursor: pointer;
    border-bottom: 1px solid var(--border-color);
    transition: background var(--transition-fast);
}
.ff-picker-item:last-child { border-bottom: none; }
.ff-picker-item:hover,
.ff-picker-item--highlight {
    background: var(--bg-surface-hover);
}
.ff-picker-empty {
    padding: 10px 12px;
    font-size: 0.8125rem;
    color: var(--text-tertiary);
    text-align: center;
}
.ff-picker-loading {
    padding: 10px 12px;
    font-size: 0.8125rem;
    color: var(--text-secondary);
    text-align: center;
}
.ff-picker-selected-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px;
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-full);
    font-size: 0.7rem;
    color: var(--text-secondary);
    margin-top: 4px;
}

/*
 * .modal-backdrop serves double-duty: dark overlay + flex centering
 * container for the .modal child. Most accounting pages use this class
 * directly without a wrapping .modal-overlay. Bumping z-index from 0
 * → var(--z-modal) ensures it sits above sidebar (40) + topbar (30);
 * flex centering keeps the modal in viewport regardless of viewport
 * width or sidebar state.
 */
.modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
}

.modal {
    position: relative;
    /* S-LUX-3: Atelier card recipe at modal scale — radius-2xl + sheen + xl shadow. */
    background: var(--bg-surface);
    border-radius: var(--radius-2xl);
    border: 1px solid var(--border-color);
    box-shadow: var(--card-sheen), var(--shadow-xl);
    width: 100%;
    max-width: 560px;
    max-height: calc(100vh - 48px);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* Must sit ABOVE a sibling .modal-backdrop. Commit d43649d raised
       .modal-backdrop from z-index:0 to var(--z-modal) (60) to center the
       child-pattern accounting modals (.modal-backdrop > .modal). But the
       sibling-pattern modals (.modal-overlay > [.modal-backdrop, .modal] —
       the global email-compose modal + the footer confirm modal) then had
       the blurred backdrop (60) painting OVER the panel (old z-index:2),
       so the whole modal rendered blurred. calc(var(--z-modal) + 1) lifts
       the panel above the backdrop in the sibling case; child-pattern
       modals are unaffected (a child always paints above its parent's
       backdrop-filter regardless of z-index). */
    z-index: calc(var(--z-modal) + 1);
}

.modal-sm    { max-width: 420px; }
.modal-md    { max-width: 560px; }                           /* FIX #29 */
.modal-lg    { max-width: 720px; }
.modal-full  { max-width: min(90vw, 1100px); }               /* FIX #29 */
.modal-xl    { max-width: 960px; }
.modal-email { max-width: min(1000px, 92vw); }               /* FIXPACK-EMAIL-PREVIEW: email compose + preview */

.modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 18px 20px 16px;
    border-bottom: 1px solid var(--border-color);
    flex-shrink: 0;
}

.modal-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
}

.modal-close-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-md);
    color: var(--text-tertiary);
    background: transparent;
    border: none;
    cursor: pointer;
    flex-shrink: 0;
    transition: background var(--transition-fast), color var(--transition-fast);
}

.modal-close-btn:hover {
    background: var(--bg-surface-2);
    color: var(--text-primary);
}

.modal-body {
    padding: 20px;
    overflow-y: auto;
    flex: 1;
    /* FIXPACK-EMAIL-SCROLL-BTN: flex items default to min-height:auto, which
       means the body refuses to shrink below content height even with flex:1.
       The parent modal's overflow:hidden then clips the excess instead of the
       modal-body scrolling.  min-height:0 lets the flex child shrink to fit its
       allocated slot so overflow-y:auto actually fires a scrollbar. */
    min-height: 0;
}

.modal-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 14px 20px;
    border-top: 1px solid var(--border-color);
    background: var(--bg-surface-2);
    flex-shrink: 0;
    flex-wrap: wrap;   /* prevent buttons from overflowing on narrow viewports */
}
/* Ensure every modal-footer button shows its full label — no truncation. */
.modal-footer .btn {
    white-space: nowrap;
    min-width: max-content;
    flex-shrink: 0;
}

/* Modal entry animation (used by Alpine x-transition) — S-LUX-3:
   enter = fade + rise 6px + scale up from 0.97, 200ms ease-out-quart;
   exit = quick fade, 120ms. Transform/opacity only. */
.modal-enter {
    transition: opacity 200ms var(--ease-out-quart), transform 200ms var(--ease-out-quart);
}
.modal-enter-start {
    opacity: 0;
    transform: scale(0.97) translateY(6px);
}
.modal-enter-end {
    opacity: 1;
    transform: scale(1) translateY(0);
}
.modal-leave {
    transition: opacity 120ms ease;
}
.modal-leave-start {
    opacity: 1;
}
.modal-leave-end {
    opacity: 0;
}


/* ============================================================
   13. Toast Notifications
   ============================================================ */

/* Position: fixed top-right, 16px from edges (spec §5) */
#ff-toast-container {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-width: 360px;
    width: 100%;
    pointer-events: none;
}

.toast {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 16px;
    /* S-LUX-3: Atelier card recipe + slide-in from 16px with the chrome ease. */
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--card-sheen), var(--shadow-lg);
    overflow: hidden;   /* clip the bottom progress hairline to the rounded corners */
    pointer-events: all;
    transition: opacity 200ms var(--ease-out-quart), transform 200ms var(--ease-out-quart);
    animation: toast-in 200ms var(--ease-out-quart) forwards;
}

@keyframes toast-in {
    from { opacity: 0; transform: translateX(16px); }
    to   { opacity: 1; transform: translateX(0); }
}

.toast.is-leaving {
    animation: toast-out 160ms var(--ease-out-quart) forwards;
}

@keyframes toast-out {
    from { opacity: 1; transform: translateX(0); max-height: 100px; }
    to   { opacity: 0; transform: translateX(16px); max-height: 0; margin: 0; padding: 0; }
}

/* S-LUX-3: auto-dismiss progress — a 2px hairline pinned to the bottom that
   shrinks over the toast's lifetime. The JS sets `animation-duration` inline
   to the real dismiss time (added only when duration > 0), so it stays in sync;
   the semantic colour comes from the toast-* variant below. */
.toast-progress {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 2px;
    width: 100%;
    transform-origin: left center;
    background: var(--color-info);
    animation-name: toast-progress;
    animation-timing-function: linear;
    animation-fill-mode: forwards;
}
@keyframes toast-progress {
    from { transform: scaleX(1); }
    to   { transform: scaleX(0); }
}

.toast-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    margin-top: 1px;
}

.toast-body {
    flex: 1;
    min-width: 0;
}

.toast-title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.3;
}

.toast-message {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    margin-top: 2px;
    line-height: 1.4;
}

.toast-close {
    color: var(--text-tertiary);
    flex-shrink: 0;
    opacity: 0.7;
    transition: opacity var(--transition-fast);
    margin-top: 1px;
}

.toast-close:hover { opacity: 1; }

/* Colour variants — S-LUX-3: 4px semantic left rail + matching icon + the
   progress hairline picks up the same semantic colour. */
.toast-success { border-left: 4px solid var(--color-success); }
.toast-warning { border-left: 4px solid var(--color-warning); }
.toast-danger  { border-left: 4px solid var(--color-danger);  }
.toast-info    { border-left: 4px solid var(--color-info);    }

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

.toast-success .toast-progress { background: var(--color-success); }
.toast-warning .toast-progress { background: var(--color-warning); }
.toast-danger  .toast-progress { background: var(--color-danger);  }
.toast-info    .toast-progress { background: var(--color-info);    }


/* ============================================================
   14. Notification Dropdown Transitions (Alpine x-transition)
   ============================================================ */

/* S-LUX-3: enter = fade + drop 4px + scale up from 0.98, 120ms ease-out-quart
   from the top; exit = quick fade only, 80ms. Transform/opacity only. */
.dropdown-enter {
    transition: opacity 120ms var(--ease-out-quart), transform 120ms var(--ease-out-quart);
}
.dropdown-enter-start {
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
}
.dropdown-enter-end {
    opacity: 1;
    transform: translateY(0) scale(1);
}
.dropdown-leave {
    transition: opacity 80ms ease;
}
.dropdown-leave-start {
    opacity: 1;
}
.dropdown-leave-end {
    opacity: 0;
}


/* ============================================================
   14b. Accounting Sub-Navigation Bar (acc-topnav)
   ============================================================
   Grouped horizontal nav rendered by includes/partials/accounting-nav.php
   on every accounting page. Top-level groups with sub-pages render as
   click-to-open dropdowns; standalone destinations render as direct links.

   Layout: flex row that wraps on narrow viewports.
   The "pin-right" modifier pushes Periods + Settings to the right side.

   WHY no overflow-x: per CSS spec, setting `overflow-x: auto` silently
   forces `overflow-y` to be non-visible too, which would CLIP the
   absolutely-positioned dropdown panels that sit below the bar. Since
   8 top-level items comfortably fit on any reasonable viewport, we use
   flex-wrap instead to handle edge cases without creating a clipping
   context.
   ============================================================ */

/* ── Container — frosted-pill shelf housing the buttons ────────────
   Apple-style depth: 1px inset highlight on top edge + soft drop
   shadow underneath gives the bar a sense of physical lift, while a
   subtle vertical gradient suggests light raking across the surface.
   Wrap behaviour kept (flex-wrap) so dropdowns can still escape
   vertically — but visual rhythm is restored by uniform button
   sizing and consistent row-gap. */
.acc-topnav {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
    row-gap: 6px;
    padding: 6px;
    margin-bottom: 20px;
    background:
        linear-gradient(180deg, rgba(255,255,255,0.025), rgba(255,255,255,0) 60%),
        var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 14px;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.03),
        0 1px 2px rgba(0,0,0,0.18),
        0 6px 18px -10px rgba(0,0,0,0.35);
    /* overflow MUST remain visible so absolute-positioned dropdowns can escape */
    overflow: visible;
}

/* Wrapper around groups so the dropdown can be position:absolute */
.acc-topnav-group {
    position: relative;
    flex-shrink: 0;
}

/* The leaf link OR the dropdown trigger button — same luxe button skin.
   Apple-ish key-cap aesthetic: rounded squircle, subtle border that only
   reveals on hover, gentle inset top highlight on active to read as
   "depressed" rather than "tinted". Spring easing keeps interactions
   feeling tactile without being bouncy. */
.acc-topnav-link {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px;
    border-radius: 10px;
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: -0.005em;
    white-space: nowrap;
    text-decoration: none;
    color: var(--text-secondary);
    /* Soft "key cap" tint so inactive tabs read as physical buttons sitting
       on the shelf — not as plain text links. The gradient is barely visible
       on top of bg-card; just enough to suggest a beveled surface. */
    background: linear-gradient(180deg,
        rgba(255,255,255,0.045),
        rgba(255,255,255,0) 65%
    );
    border: 1px solid rgba(255,255,255,0.04);
    cursor: pointer;
    transition:
        background 220ms cubic-bezier(0.23, 1, 0.32, 1),
        color     180ms ease,
        border-color 180ms ease,
        box-shadow 220ms cubic-bezier(0.23, 1, 0.32, 1),
        transform 120ms ease;
    flex-shrink: 0;
    font-family: inherit;
    line-height: 1.2;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.025);
}
.acc-topnav-link:hover {
    background: linear-gradient(180deg,
        rgba(255,255,255,0.085),
        rgba(255,255,255,0.015) 65%
    );
    border-color: rgba(255,255,255,0.09);
    color: var(--text-primary);
    text-decoration: none;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.05),
        0 1px 2px rgba(0,0,0,0.2);
}
.acc-topnav-link:active {
    transform: scale(0.97);
    transition-duration: 80ms;
}
.acc-topnav-link svg {
    flex-shrink: 0;
    color: currentColor;
    opacity: 0.9;
}

/* Active state — applies to both leaf links and group triggers.
   The "lit key" treatment: brand fill, top-edge inset highlight that
   reads as a glass dome, and a soft outer shadow that lifts the button
   off the shelf. */
.acc-topnav-link.acc-topnav-active {
    background:
        linear-gradient(180deg, rgba(255,255,255,0.12), rgba(255,255,255,0) 55%),
        var(--color-primary);
    color: var(--color-on-primary);
    border-color: transparent;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.22),
        inset 0 -1px 0 rgba(0,0,0,0.08),
        0 1px 2px rgba(0,0,0,0.20),
        0 4px 10px -4px var(--color-primary);
    font-weight: 600;
}
.acc-topnav-link.acc-topnav-active:hover {
    background:
        linear-gradient(180deg, rgba(255,255,255,0.14), rgba(255,255,255,0) 55%),
        var(--color-primary-hover, var(--color-primary));
    color: var(--color-on-primary);
}
.acc-topnav-link.acc-topnav-active svg { opacity: 1; }

/* Dropdown trigger — button is open but page is NOT the active section */
.acc-topnav-link.acc-topnav-open:not(.acc-topnav-active) {
    background: var(--bg-surface-2);
    border-color: var(--border-color);
    color: var(--text-primary);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.04);
}

/* Right-pinned items (Periods, Settings).
   pin-right only when the bar is wide enough to fit on a single row.
   On wrapped layouts the auto-margin creates an awkward gap that the
   user reads as "items thrown about" — drop it under the wrap
   threshold so wrapped buttons flow with consistent rhythm. */
.acc-topnav-pin-right {
    margin-left: auto;
}
/* When two items are both pin-right, only the first needs auto margin */
.acc-topnav-pin-right ~ .acc-topnav-pin-right {
    margin-left: 0;
}
@media (max-width: 1100px) {
    .acc-topnav-pin-right,
    .acc-topnav-pin-right ~ .acc-topnav-pin-right {
        margin-left: 0;
    }
}

/* ── Mobile layout: a uniform 2-column grid of equal buttons ───────
   On narrow viewports flex-wrap creates uneven columns because button
   widths vary (Dashboard is short, "General Ledger ▾" is long). The
   result is a ragged grid that reads as "thrown together." Switching
   to CSS Grid with 2 equal columns gives every button the same width
   and a clean rhythm — the bar reads as a deliberate control panel.

   .acc-topnav-group wraps button+dropdown, so it must stretch its
   button to 100% width to fill the grid cell.  */
@media (max-width: 767px) {
    .acc-topnav {
        display: grid !important;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 6px;
        padding: 8px;
    }
    .acc-topnav-group { width: 100%; min-width: 0; }
    .acc-topnav-link {
        width: 100%;
        justify-content: flex-start;
        padding: 10px 10px;
        gap: 6px;
        min-width: 0;
        font-size: 0.75rem;
    }
    /* Icon stays present but slightly smaller — preserves visual rhythm
       (every cell has icon + label + optional chevron) without eating
       label width when labels are long like "General Ledger". */
    .acc-topnav-link > svg {
        width: 14px;
        height: 14px;
    }
    /* Hide the chevron caret on mobile so the label always has room.
       The dropdown still opens on tap; users discover it by tapping
       the button itself (matches the iOS pattern of icon+label tabs
       without secondary affordances). */
    .acc-topnav-chevron { display: none !important; }
    /* Truncate long labels with ellipsis instead of clipping the chevron */
    .acc-topnav-link > span:not(.acc-topnav-chevron) {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        min-width: 0;
        flex: 1 1 auto;
    }
    /* Drop pin-right entirely on mobile — every cell is the same. */
    .acc-topnav-pin-right { margin-left: 0; }
    /* Dropdown panels stretch to viewport width on mobile so labels
       are readable even with long item names. */
    .acc-topnav-dropdown {
        min-width: 75vw;
        max-width: calc(100vw - 24px);
    }
    /* Chevron sits flush right inside the wide button. */
    .acc-topnav-chevron {
        margin-left: auto;
        padding-left: 4px;
        flex-shrink: 0;
    }
}

/* Chevron caret next to dropdown trigger labels */
.acc-topnav-chevron {
    display: inline-flex;
    align-items: center;
    margin-left: 1px;
    transition: transform 240ms cubic-bezier(0.23, 1, 0.32, 1);
    opacity: 0.55;
}
.acc-topnav-link:hover .acc-topnav-chevron { opacity: 0.85; }
.acc-topnav-link.acc-topnav-active .acc-topnav-chevron { opacity: 0.9; }
.acc-topnav-chevron-open {
    transform: rotate(180deg);
}

/* Dropdown panel — same shadow/radius treatment as other dropdowns */
.acc-topnav-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    min-width: 220px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
    z-index: var(--z-dropdown);
    padding: 6px 0;
    overflow: hidden;
}
/* Right-pinned dropdowns flush right so they don't overflow off-screen */
.acc-topnav-pin-right .acc-topnav-dropdown {
    left: auto;
    right: 0;
}

.acc-topnav-dropdown-label {
    padding: 6px 14px 4px;
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
    margin: 0;
}

.acc-topnav-dropdown-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 14px;
    font-size: 0.875rem;
    color: var(--text-primary);
    text-decoration: none;
    transition: background var(--transition-fast);
    line-height: 1.2;
}
.acc-topnav-dropdown-item:hover {
    background: var(--bg-surface-hover);
    text-decoration: none;
    color: var(--text-primary);
}
.acc-topnav-dropdown-item svg {
    color: var(--text-secondary);
    flex-shrink: 0;
}

/* Active child highlight inside an open dropdown */
.acc-topnav-dropdown-item.acc-topnav-dropdown-item-active {
    background: var(--color-primary-light, rgba(249, 115, 22, 0.12));
    color: var(--color-primary-text, var(--color-primary));
    font-weight: 600;
}
.acc-topnav-dropdown-item.acc-topnav-dropdown-item-active svg {
    color: var(--color-primary-text, var(--color-primary));
}

/* x-cloak hides Alpine elements until Alpine has finished initialising */
[x-cloak] { display: none !important; }


/* ============================================================
   15. Global Search Modal
   ============================================================ */

.search-modal {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding-top: 80px;
}

.search-overlay {
    position: fixed;
    inset: 0;
    /* S-LUX-3: deeper scrim + 4px blur, matching the modal backdrop. */
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.search-panel {
    position: relative;
    width: 100%;
    max-width: 600px;
    /* S-LUX-3 panel recipe at modal scale — raised surface-2 + strong
       hairline + shadow-xl + sheen. */
    background: var(--bg-surface-2);
    border-radius: var(--radius-xl);
    border: 1px solid var(--border-color-strong);
    box-shadow: var(--card-sheen), var(--shadow-xl);
    overflow: hidden;
    z-index: 1;
}

.search-input-wrap {
    display: flex;
    align-items: center;
    padding: 0 16px;
    border-bottom: 1px solid var(--border-color);
    gap: 12px;
}

.search-panel-icon {
    width: 18px;
    height: 18px;
    color: var(--text-tertiary);
    flex-shrink: 0;
}

.search-input {
    flex: 1;
    height: 52px;
    border: none;
    background: transparent;
    color: var(--text-primary);
    font-size: 1rem;
    font-family: inherit;
    outline: none;
}

.search-input::placeholder { color: var(--text-tertiary); }

.search-esc-hint {
    flex-shrink: 0;
    font-family: var(--font-mono);
    font-size: 0.75rem;
    padding: 2px 6px;
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-sm);
    color: var(--text-tertiary);
    box-shadow: 0 1px 0 var(--border-color-strong);
}

.search-results {
    max-height: 400px;
    overflow-y: auto;
}

/* Search result items — rendered by app.js */
.search-result-group-label {
    padding: 10px 16px 4px;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-tertiary);
}

.search-result-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 10px;
    margin: 0 8px;
    border-radius: var(--radius-md);
    cursor: pointer;
    text-decoration: none;
    color: var(--text-primary);
    transition: background var(--transition-fast);
}

/* S-LUX-3: keyboard-focused row matches the hover surface (inset 6px radius). */
.search-result-item:hover,
.search-result-item.is-focused {
    background: var(--bg-surface-hover);
    text-decoration: none;
}

.search-result-icon {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-md);
    background: var(--bg-surface-2);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--text-secondary);
}

.search-result-icon svg { width: 16px; height: 16px; }

.search-result-body { flex: 1; min-width: 0; }

.search-result-title {
    font-size: 0.9rem;
    font-weight: 500;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.search-result-meta {
    font-size: 0.8125rem;
    color: var(--text-tertiary);
}

.search-recent {
    padding: 8px 0;
}

.search-empty {
    padding: 32px 16px;
    text-align: center;
    color: var(--text-tertiary);
    font-size: 0.9rem;
}


/* ============================================================
   16. Skeleton Loaders
   ============================================================ */

@keyframes ff-shimmer {
    0%   { background-position: -400px 0; }
    100% { background-position: 400px 0; }
}

.skeleton {
    /* S-LUX-2: highlight stop is --border-color-strong (not --bg-surface-hover)
       — in LIGHT mode --bg-surface-hover == --bg-surface-2, so the old sweep
       had zero contrast and was invisible. border-strong contrasts in both. */
    background: linear-gradient(
        90deg,
        var(--bg-surface-2) 25%,
        var(--border-color-strong) 50%,
        var(--bg-surface-2) 75%
    );
    background-size: 800px 100%;
    animation: ff-shimmer 1.4s ease infinite;
    border-radius: var(--radius-sm);
}

.skeleton-text {
    height: 14px;
    border-radius: var(--radius-sm);
    margin-bottom: 8px;
}

.skeleton-text:last-child { margin-bottom: 0; }
.skeleton-text-sm { height: 11px; }
.skeleton-text-lg { height: 20px; }

.skeleton-title {
    height: 22px;
    width: 60%;
    margin-bottom: 12px;
    border-radius: var(--radius-sm);
}

.skeleton-avatar {
    width: 36px;
    height: 36px;
    border-radius: 50%;
    flex-shrink: 0;
}

.skeleton-btn {
    height: 36px;
    width: 100px;
    border-radius: var(--radius-md);
}

/* Table row skeleton */
.skeleton-row {
    display: grid;
    align-items: center;
    gap: 16px;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border-color);
}


/* ============================================================
   17. Utilities
   ============================================================ */

/* Spacing */
.mt-0  { margin-top: 0; }       .mb-0  { margin-bottom: 0; }
.mt-1  { margin-top: 4px; }     .mb-1  { margin-bottom: 4px; }
.mt-2  { margin-top: 8px; }     .mb-2  { margin-bottom: 8px; }
.mt-3  { margin-top: 12px; }    .mb-3  { margin-bottom: 12px; }
.mt-4  { margin-top: 16px; }    .mb-4  { margin-bottom: 16px; }
.mt-5  { margin-top: 20px; }    .mb-5  { margin-bottom: 20px; }
.mt-6  { margin-top: 24px; }    .mb-6  { margin-bottom: 24px; }

.gap-1 { gap: 4px; }   .gap-2 { gap: 8px; }   .gap-3 { gap: 12px; }
.gap-4 { gap: 16px; }  .gap-5 { gap: 20px; }  .gap-6 { gap: 24px; }

/* Flex */
.d-flex         { display: flex; }
.flex-col       { flex-direction: column; }
.flex-wrap      { flex-wrap: wrap; }
.align-center   { align-items: center; }
.align-start    { align-items: flex-start; }
.justify-center { justify-content: center; }
.justify-end    { justify-content: flex-end; }
.justify-between { justify-content: space-between; }
.flex-1         { flex: 1; min-width: 0; }

/* Grid */
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; }

/* Text */
.text-left    { text-align: left; }
.text-center  { text-align: center; }
.text-right   { text-align: right; }
.font-mono    { font-family: var(--font-mono); }
.font-medium  { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold    { font-weight: 700; }
.truncate     { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Display */
.d-none   { display: none !important; }
.d-block  { display: block !important; }
.invisible { visibility: hidden; }

/* Misc */
.w-full   { width: 100%; }
.mw-none  { max-width: none; }
.rounded  { border-radius: var(--radius-md); }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-full { border-radius: var(--radius-full); }
.shadow   { box-shadow: var(--shadow-sm); }
.border   { border: 1px solid var(--border-color); }
.divider  { border-top: 1px solid var(--border-color); margin: 16px 0; }

/* Currency / number display */
.currency {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Empty state */
/* ============================================================
   S-LUX-4 (D-LUX4-6) — ONE canonical empty-state recipe.
   icon (48px, --text-tertiary, 1.5px stroke) → title (15px/600) →
   body (13px, --text-secondary) → optional action. Vertically
   centered, max-width 360px. `.empty-state` is the dominant class
   (90+ admin uses) so rebasing it here reaches them all CSS-only;
   `.ff-empty*` are aliases for the same recipe on new markup, and
   `.portal-empty*` (below) inherit the same values. */
.empty-state,
.ff-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 360px;
    margin-inline: auto;
    padding: 48px 24px;
    color: var(--text-tertiary);
}
/* Table-context sits inside a card/row — tighter; page-context is the
   default roomy version above. */
.ff-empty--table { padding: 32px 24px; }
.ff-empty--page  { padding: 64px 24px; }

.empty-state-icon,
.ff-empty-icon {
    width: 48px;
    height: 48px;
    margin: 0 auto 16px;
    color: var(--text-tertiary);
    opacity: 0.9;
}
/* CSS stroke-width reaches inline heroicon SVGs (stroke=currentColor). */
.empty-state-icon svg,
.ff-empty-icon svg {
    width: 100%;
    height: 100%;
    stroke-width: 1.5;
}

.empty-state-title,
.ff-empty-title {
    font-size: 0.9375rem;   /* 15px */
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 6px;
}

.empty-state-text,
.ff-empty-text {
    font-size: 0.8125rem;   /* 13px */
    color: var(--text-secondary);
    margin-bottom: 20px;
    line-height: 1.5;
}
.empty-state-text:last-child,
.ff-empty-text:last-child { margin-bottom: 0; }

/* Focus visible — global accessible focus ring */
:focus-visible {
    outline: 2px solid var(--border-focus);
    outline-offset: 2px;
}


/* ============================================================
   18. Alpine.js Helpers & Transitions
   ============================================================ */

/* Hide elements until Alpine initialises — prevents flash */
[x-cloak] { display: none !important; }

/* Smooth tab/panel transitions — used by Alpine x-transition:enter (x-show)
   and .ff-tab-animated keyframe animation (x-if template insertion) */
.ff-tab-enter        { transition: opacity 120ms ease-out; }
.ff-tab-enter-from   { opacity: 0; }
.ff-tab-enter-to     { opacity: 1; }

@keyframes ff-tab-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}
.ff-tab-animated { animation: ff-tab-fade-in 120ms ease-out; }

/* Icon placeholder when SVG file not found */
.icon-missing {
    display: inline-block;
    width: 18px;
    height: 18px;
    background: var(--bg-surface-2);
    border-radius: var(--radius-sm);
    vertical-align: middle;
}

/* Generic fade transition used by Alpine */
.fade-enter { transition: opacity 200ms ease; }
.fade-enter-start { opacity: 0; }
.fade-enter-end   { opacity: 1; }
.fade-leave       { transition: opacity 150ms ease; }
.fade-leave-start { opacity: 1; }
.fade-leave-end   { opacity: 0; }

/* Slide-down */
.slide-down-enter { transition: opacity 200ms ease, transform 200ms ease; }
.slide-down-enter-start { opacity: 0; transform: translateY(-8px); }
.slide-down-enter-end   { opacity: 1; transform: translateY(0); }
.slide-down-leave       { transition: opacity 150ms ease, transform 150ms ease; }
.slide-down-leave-start { opacity: 1; transform: translateY(0); }
.slide-down-leave-end   { opacity: 0; transform: translateY(-8px); }


/* ============================================================
   19. Responsive / Mobile
   ============================================================ */

@media (max-width: 1023px) {
    /* On mobile, sidebar becomes a fixed overlay */
    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        height: 100%;
        transform: translateX(-100%);
        transition: transform var(--transition-slow), width var(--transition-slow);
        width: var(--sidebar-width);
    }

    .sidebar.is-open {
        transform: translateX(0);
        box-shadow: var(--shadow-xl);
    }

    /* App main takes full width on mobile */
    .app-main {
        width: 100%;
    }

    /* S-MOBILE-CHROME-CLEANUP: removed the .topbar-menu-btn display
       override here. The legacy menu button stays display:none across
       all breakpoints — .hamburger-btn (declared at line ~5608) is the
       sole topbar hamburger and is visible only at <768px. On tablet
       (768-1023) the sidebar is a permanent 64px rail so no toggle is
       needed; on desktop the sidebar's own .sidebar-collapse-btn
       (chevron) handles the open/collapsed toggle. */

    /* Search hint text condensed on mobile */
    .search-hint span:not(.search-kbd) {
        display: none;
    }

    /* Full-width search modal on small screens */
    .search-modal { padding: 0; align-items: flex-start; }
    .search-panel { border-radius: 0 0 var(--radius-xl) var(--radius-xl); }

    /* Page content padding reduced */
    .page-content { padding: 20px 16px; }

    /* Form row stacks on mobile */
    .form-row-2,
    .form-row-3,
    .form-row-4 { grid-template-columns: 1fr; }

    /* Grid stacks. The `> *` reset is required because some pages set
       `style="grid-column: span 2"` inline on cards that should stretch
       across both desktop columns. With grid-template-columns:1fr that
       inline span forces the browser to auto-create a second implicit
       column, breaking the stack — items end up at 50px / 253px instead
       of 100% width. Forcing every child back to grid-column:auto keeps
       the single-column stack intact. */
    .grid-2, .grid-3 { grid-template-columns: 1fr; }
    .grid-2 > *,
    .grid-3 > * { grid-column: auto !important; }

    /* Notification dropdown full width */
    .notification-dropdown {
        width: calc(100vw - 32px);
        right: -8px;
    }
}

/* Overlay backdrop when mobile sidebar is open */
.sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: calc(var(--z-sidebar) - 1);
}

@media (max-width: 1023px) {
    .sidebar-overlay.is-visible { display: block; }
}

/* Tablet adjustments */
@media (min-width: 768px) and (max-width: 1023px) {
    .form-row-2 { grid-template-columns: 1fr 1fr; }
    .grid-2     { grid-template-columns: 1fr 1fr; }
    /* S-FORM-LAYOUT: 3/4-up rows degrade to 2-up on tablet rather than collapsing
       straight to one column, so the multi-column layout is visible below the
       1024px desktop breakpoint (short number/rate fields fit fine two-up here). */
    .form-row-3,
    .form-row-4 { grid-template-columns: 1fr 1fr; }
}

/* Mac-only shortcut hints */
@media (pointer: fine) {
    /* Detected via JS in app.js and class added to <html> */
}

html.is-mac .mac-only     { display: inline; }
html.is-mac .non-mac-only { display: none; }

/* Print
   PRINT-DARK-FIX: Force light-theme colors during print regardless of the
   user's `data-theme` preference. Without this override, a dark-theme user
   prints white text (--text-primary: #ffffff) onto white paper because
   Chrome's default print setting "Background graphics" is OFF — the dark
   card backgrounds are dropped but the white text stays white, leaving
   the page visually empty. We override the CSS variables themselves so
   every consumer (cards, stats, tables, headers, links) gets readable
   ink-on-paper colors automatically. */
@media print {
    /* Force light-theme palette for both :root and the dark-theme scope */
    :root,
    [data-theme="dark"] {
        --bg-body:            #ffffff !important;
        --bg-page:            #ffffff !important;
        --bg-surface:         #ffffff !important;
        --bg-surface-2:       #ffffff !important;
        --bg-card:            #ffffff !important;
        --bg-muted:           #f5f5f5 !important;
        --bg-secondary:       #ffffff !important;
        --bg-input:           #ffffff !important;
        --bg-hover:           #ffffff !important;
        --bg-selected:        #ffffff !important;
        --bg-surface-hover:   #ffffff !important;

        --text-primary:       #000000 !important;
        --text-secondary:     #333333 !important;
        --text-tertiary:      #555555 !important;
        --text-muted:         #555555 !important;
        --text-disabled:      #888888 !important;
        --text-inverse:       #ffffff !important;

        --border-color:        #cccccc !important;
        --border-color-strong: #999999 !important;
        --border-strong:       #999999 !important;
    }

    /* Force every element to ink-on-paper colors as a defensive backstop
       for elements that hard-code white text (e.g. inline `style="color:#fff"`)
       or that override --text-primary. Then individual selectors below
       restore brand accents that should remain coloured. */
    html, body { background: #ffffff !important; color: #000000 !important; }

    /* S-INVOICE-DISPLAY-COMPREHENSIVE T1 fix (2026-05-11): extend hide list
       with global overlay containers. The `.search-modal` modal is rendered
       on every admin page via includes/footer.php and uses Alpine x-show to
       toggle visibility. If the modal is open at print time (or Alpine fails
       to fire before print captures the DOM), its `.search-overlay` child
       paints rgba(0,0,0,0.5) over the entire viewport, producing a dark
       semi-transparent overlay across the printed invoice. Same pattern
       applies to `.modal-overlay` (void/delete/confirm/email modals via
       footer.php) and `.modal-backdrop` (child element). The `.sidebar-overlay`
       is the mobile sidebar dim — only painted when the sidebar is open at
       print time. Hide all of them defensively — they should never appear
       on a printed billing artifact. */
    .sidebar, .topbar, #ff-toast-container, .chat-widget,
    .search-modal, .search-overlay,
    .modal-overlay, .modal-backdrop,
    .sidebar-overlay { display: none !important; }
    .app-main { margin-left: 0 !important; }
    .page-content { padding: 0; }
    .card { border: 1px solid #ccc; box-shadow: none !important; background: #ffffff !important; color: #000000 !important; }

    /* PRINT-TABLE-FIX: Force tables back to proper table layout.
       WHY: app.js auto-adds .table-stack to every table in the app so that
       on mobile (max-width:767px) each row renders as a stacked label/value
       card. But when the browser prints to letter paper, the paper width
       (~720px after ~0.5in margins) falls BELOW the 767px breakpoint, so
       every table on every printed page collapses into a vertical stack —
       an 8-column line-items table becomes 8 block rows per item, blowing
       a one-page invoice out to four pages of wasted whitespace. Force
       display:table / table-row / table-cell in print so tables stay
       horizontal regardless of the mobile query. */
    .table-stack,
    .table-stack.table,
    table.table {
        display: table !important;
        width: 100% !important;
        border-collapse: collapse !important;
    }
    .table-stack thead, table.table thead { display: table-header-group !important; }
    .table-stack tbody, table.table tbody { display: table-row-group !important; }
    .table-stack tfoot, table.table tfoot { display: table-footer-group !important; }
    .table-stack tr, table.table tr {
        display: table-row !important;
        margin: 0 !important;
        padding: 0 !important;
        border: none !important;
        background: transparent !important;
    }
    .table-stack td, .table-stack th,
    table.table td, table.table th {
        display: table-cell !important;
        vertical-align: top !important;
    }
    /* Kill the ::before pseudo-label that table-stack uses on mobile */
    .table-stack td::before { content: none !important; display: none !important; }
    /* Undo the mobile min-width that forces horizontal scroll */
    .table-wrapper, .table-responsive { overflow: visible !important; }
    table.table, table.data-table { min-width: 0 !important; }
}

/* ============================================================
   20. Dashboard & Page-Specific Components
   ============================================================ */

/* Stat card variants */
.stat-card--link {
    cursor: pointer;
    text-decoration: none;
    display: block;
    transition: box-shadow var(--transition-base), transform var(--transition-base);
}
.stat-card--link:hover {
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
    text-decoration: none;
}
.stat-card--danger::before  { background: var(--color-danger); }
.stat-card--warning::before { background: var(--color-warning); }
.stat-card--error {
    border-left: 3px solid var(--color-danger);
    opacity: 0.7;
}

/* Inline skeleton for stat card value area (S-LUX-2: border-strong highlight
   for both-theme contrast; taller + tabular to match the new 28px value). */
.stat-skeleton {
    height: 30px;
    border-radius: var(--radius-sm);
    background: var(--bg-surface-2);
    animation: ff-shimmer 1.5s ease infinite;
    background-size: 800px 100%;
    background-image: linear-gradient(90deg, var(--bg-surface-2) 25%, var(--border-color-strong) 50%, var(--bg-surface-2) 75%);
    width: 60%;
    margin: 4px 0;
}

/* Chart loading placeholder (S-LUX-2: border-strong shimmer + faint grid-line
   placeholders so a loading chart reads as a chart, not a blank bar). */
.chart-skeleton {
    width: 100%;
    border-radius: var(--radius-sm);
    background: var(--bg-surface-2);
    animation: ff-shimmer 1.5s ease infinite;
    background-size: 800px 100%;
    background-image:
        repeating-linear-gradient(to bottom, transparent 0, transparent 47px, var(--border-color) 47px, var(--border-color) 48px),
        linear-gradient(90deg, var(--bg-surface-2) 25%, var(--border-color-strong) 50%, var(--bg-surface-2) 75%);
}

/* 2/3 + 1/3 grid layout */
.grid-2-1 {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: 24px;
}
@media (max-width: 1023px) {
    .grid-2-1 { grid-template-columns: 1fr; }
}

/* ════════════════════════════════════════════════════════════
   S-DASHBOARD-CHART-POLISH — dashboard chart + widget layout.

   Existing pages used .card with optional .card--accent-* modifiers,
   producing inconsistent 3px coloured top borders on the dashboard.
   These new classes give every chart card / widget card a uniform
   appearance and properly contain the ApexCharts SVG so it can flex
   to fit when the sidebar collapses/expands.
   ════════════════════════════════════════════════════════════ */

/* Dashboard row grid wrappers — semantic aliases over .grid-2 / .grid-2-1.
   Kept separate so dashboard.php doesn't accidentally inherit unrelated
   adjustments to those generic utility classes from other modules. */
.dashboard-grid {
    display: grid;
    gap: 20px;
}
.dashboard-grid--charts  { grid-template-columns: 2fr 1fr; gap: 20px; }
.dashboard-grid--equal   { grid-template-columns: 1fr 1fr; gap: 20px; }
.dashboard-grid--widgets { grid-template-columns: 1fr 1fr; gap: 20px; }


/* Chart card — the uniform wrapper around every chart panel.
   Flex column so card-body can fill remaining height and the chart
   inside it (.chart-wrap) can shrink past content height via min-height:0. */
.chart-card {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border-color);
    /* S-LUX-LOUD: align dashboard chart cards to the full Atelier card recipe
       (sheen + ambient + radius-xl) so they lift off the deeper background like
       the KPI tiles. */
    border-radius: var(--radius-xl);
    background: var(--bg-surface);
    box-shadow: var(--card-sheen), var(--shadow-ambient);
    overflow: hidden;
}
/* S-LUX-LOUD: no header "band" — the title sits on the card surface, so the
   card reads as one clean panel on the page background (operator ask). */
.chart-card .card-header {
    padding: 16px 20px 12px;
    background: transparent;
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}
.chart-card .card-header h3,
.chart-card .card-header .card-title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    letter-spacing: 0;
}
.chart-card .card-body {
    flex: 1;
    display: flex;
    flex-direction: column;
    padding: 16px 20px;
}

/* Chart wrap — sits inside .card-body and holds the ApexCharts target.
   min-height:0 is the critical flex-shrink unlock that lets the chart
   reflow to a smaller width when the sidebar expands and reclaims space. */
.chart-wrap {
    flex: 1;
    min-height: 0;
    position: relative;
}
.chart-wrap > div { width: 100% !important; }

/* ApexCharts inline overflow:hidden was clipping tooltip + datalabel
   overflow into the next card. Visible at the wrap level is fine because
   the parent .chart-card already has overflow:hidden for the skeleton. */
.chart-wrap .apexcharts-canvas { overflow: visible !important; }

/* S-LUX-2 — Atelier chart tooltip. ApexCharts renders the tooltip DOM
   with .apexcharts-theme-{dark,light}; we override the surface with
   tokens so it matches every card (surface-2 fill, strong hairline,
   radius-lg, ambient shadow, Geist, tabular figures) AND follows the
   brand override + theme toggle for free. Covers both theme classes. */
.apexcharts-tooltip.apexcharts-theme-dark,
.apexcharts-tooltip.apexcharts-theme-light {
    background: var(--bg-surface-2) !important;
    border: 1px solid var(--border-color-strong) !important;
    border-radius: var(--radius-lg) !important;
    box-shadow: var(--shadow-md) !important;
    color: var(--text-primary) !important;
    font-family: var(--font-sans) !important;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}
.apexcharts-tooltip-title {
    background: var(--bg-surface-hover) !important;
    border-bottom: 1px solid var(--border-color) !important;
    font-family: var(--font-sans) !important;
    font-weight: 600;
}
.apexcharts-tooltip-series-group { font-variant-numeric: tabular-nums; }
.apexcharts-xaxistooltip,
.apexcharts-yaxistooltip {
    background: var(--bg-surface-2) !important;
    border-color: var(--border-color-strong) !important;
    color: var(--text-primary) !important;
    font-family: var(--font-sans) !important;
}
.apexcharts-xaxistooltip::before,
.apexcharts-xaxistooltip::after { border-bottom-color: var(--border-color-strong) !important; }

/* Dashboard list widgets (recent activity, pickups, etc.) — uniform shell. */
.dashboard-widget {
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    background: var(--bg-card);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

/* ── Dashboard widget table density ───────────────────────────────
   S-DASHBOARD-CHART-POLISH (follow-up): when two widget tables sit
   side-by-side in dashboard-grid--widgets, each card only has
   ~510px of width at a 1280px viewport with the sidebar expanded.
   The default .table padding (13/16px) plus monospace contract
   numbers like "MTTS-9JJ3A-2026" push the table past that, forcing
   the .table-wrapper's overflow-x:auto scrollbar to engage.

   These rules tighten the cell metrics enough that the table fits
   natively at common viewport widths, AND make the fallback
   scrollbar visible (not the system-default near-invisible track)
   so users know to scroll when content truly doesn't fit. */
.dashboard-widget .table,
.dashboard-widget .data-table { font-size: 0.8125rem; }

.dashboard-widget .table thead th,
.dashboard-widget .data-table thead th {
    padding: 10px 12px;
    font-size: 0.625rem;
    letter-spacing: 0.06em;
}

.dashboard-widget .table tbody td,
.dashboard-widget .data-table tbody td {
    padding: 10px 12px;
}

/* Contract anchor truncation — when the column is still narrower than
   the natural mono text width, hide the trailing characters with an
   ellipsis rather than clipping from the left (which would hide the
   prefix that distinguishes "CN-…" from "MTTS-…"). */
.dashboard-widget .table tbody td .font-mono {
    display: inline-block;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
}

/* Visible horizontal scrollbar inside dashboard widget tables — the
   underlying .table-wrapper already has overflow-x:auto; this styles
   the scrollbar so users can actually see they're able to scroll. */
.dashboard-widget .table-wrapper { scrollbar-width: thin; }
.dashboard-widget .table-wrapper::-webkit-scrollbar { height: 8px; }
.dashboard-widget .table-wrapper::-webkit-scrollbar-track {
    background: var(--bg-surface-2);
}
.dashboard-widget .table-wrapper::-webkit-scrollbar-thumb {
    background: var(--border-color-strong, var(--border-color));
    border-radius: 4px;
}
.dashboard-widget .table-wrapper::-webkit-scrollbar-thumb:hover {
    background: var(--text-tertiary);
}

/* Drop the trailing "View" action column when the widget sits inside
   the side-by-side dashboard-grid--widgets row. At ~510px per card the
   column gets clipped on the right; the contract number in the first
   column is already a styled link to the same detail page so the View
   button is duplicate navigation. Scoped to --widgets only so the
   full-width Active Leases table at the top of the dashboard keeps its
   per-row View button intact. */
.dashboard-grid--widgets .dashboard-widget .table thead th:last-child,
.dashboard-grid--widgets .dashboard-widget .table tbody td:last-child {
    display: none;
}

/* ── Responsive: tablet ── */
@media (max-width: 1023px) {
    .dashboard-grid--charts,
    .dashboard-grid--equal,
    .dashboard-grid--widgets { grid-template-columns: 1fr; }
}

/* ── Responsive: mobile ── */
@media (max-width: 767px) {
    .dashboard-grid { gap: 12px; }
    .chart-card .card-body { padding: 12px 14px; }
}

/* Activity feed — timeline design */
.activity-feed {
    list-style: none;
    margin: 0;
    padding: 8px 16px 4px;
}
.activity-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 10px 0;
    position: relative;
}
/* Vertical timeline line between items */
.activity-item:not(:last-child)::after {
    content: '';
    position: absolute;
    left: 3px;
    top: 24px;
    bottom: -4px;
    width: 2px;
    background: var(--border-color);
    opacity: 0.7;
}
.activity-dot {
    flex-shrink: 0;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-top: 5px;
    position: relative;
    z-index: 1;
    box-shadow: 0 0 0 3px var(--bg-surface);
}
/* Module-specific dot colors */
.activity-dot--customers   { background: var(--color-primary); }
.activity-dot--equipment   { background: var(--color-success); }
.activity-dot--leases      { background: #8b5cf6; }
.activity-dot--invoices    { background: var(--color-warning); }
.activity-dot--payments    { background: var(--color-success); }
.activity-dot--compliance  { background: var(--color-danger); }
.activity-dot--maintenance { background: #f97316; }
.activity-dot--users       { background: #06b6d4; }
.activity-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.activity-desc {
    font-size: 0.875rem;
    color: var(--text-primary);
    line-height: 1.4;
}
.activity-meta {
    font-size: 0.75rem;
    color: var(--text-tertiary);
}

/* Utility: padding shorthand */
.p-4 { padding: 16px; }

/* Utility: muted text color */
.text-muted { color: var(--text-tertiary); }

/* Inline hyperlink style — subtle underline so links are obvious in tables */
.link {
    color: var(--color-primary);
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--color-primary) 35%, transparent);
    text-underline-offset: 2px;
    text-decoration-thickness: 1px;
    transition: text-decoration-color 0.15s;
}
.link:hover {
    text-decoration-color: var(--color-primary);
}

/* ── Tab bar — modern segmented-pill style ─────────────────
   WHY display:flex (not inline-flex): inline-flex sizes to its
   content width, so a long tab list will overflow the parent and
   stretch the page. Block-level flex respects max-width:100%.

   WHY flex-wrap:wrap (not nowrap + horizontal scroll): we'd
   rather have a long tab list flow onto a second row inside the
   same pill frame than make the user scroll a hidden region.
   row-gap gives a small breathing space between wrapped rows. */
/* ── Tab Bar — same luxe "key-cap shelf" treatment as accounting nav.
   The container is a frosted shelf; each .tab-btn renders like a
   physical button with subtle border-on-hover, inset highlight on
   active, and spring transitions for tactile feel. */
.tab-bar {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
    gap: 4px;
    row-gap: 6px;
    padding: 6px;
    background:
        linear-gradient(180deg, rgba(255,255,255,0.025), rgba(255,255,255,0) 60%),
        var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-radius: 14px;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.03),
        0 1px 2px rgba(0,0,0,0.18),
        0 6px 18px -10px rgba(0,0,0,0.35);
    margin-bottom: 1.5rem;
}

.tab-btn {
    padding: 8px 16px;
    background: linear-gradient(180deg,
        rgba(255,255,255,0.045),
        rgba(255,255,255,0) 65%
    );
    border: 1px solid rgba(255,255,255,0.04);
    border-radius: 10px;
    cursor: pointer;
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: -0.005em;
    white-space: nowrap;
    color: var(--text-secondary);
    transition:
        color 180ms ease,
        background 220ms cubic-bezier(0.23, 1, 0.32, 1),
        border-color 180ms ease,
        box-shadow 220ms cubic-bezier(0.23, 1, 0.32, 1),
        transform 120ms ease;
    font-family: inherit;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    position: relative;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.025);
}
.tab-btn:hover {
    color: var(--text-primary);
    background: linear-gradient(180deg,
        rgba(255,255,255,0.085),
        rgba(255,255,255,0.015) 65%
    );
    border-color: rgba(255,255,255,0.09);
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.05),
        0 1px 2px rgba(0,0,0,0.2);
}
.tab-btn:active { transform: scale(0.97); transition-duration: 80ms; }
.tab-btn.is-active,
.tab-btn[aria-selected="true"] {
    background:
        linear-gradient(180deg, rgba(255,255,255,0.12), rgba(255,255,255,0) 55%),
        var(--color-primary);
    color: var(--color-on-primary);
    font-weight: 600;
    border-color: transparent;
    box-shadow:
        inset 0 1px 0 rgba(255,255,255,0.22),
        inset 0 -1px 0 rgba(0,0,0,0.08),
        0 1px 2px rgba(0,0,0,0.20),
        0 4px 10px -4px var(--color-primary);
}
.tab-btn.is-active:hover,
.tab-btn[aria-selected="true"]:hover {
    background:
        linear-gradient(180deg, rgba(255,255,255,0.14), rgba(255,255,255,0) 55%),
        var(--color-primary-hover, var(--color-primary));
    color: var(--color-on-primary);
}
.tab-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 6px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-full);
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--text-tertiary);
    transition: background var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
}
.tab-btn.is-active .tab-badge,
.tab-btn[aria-selected="true"] .tab-badge {
    background: rgba(0,0,0,0.16);
    border-color: rgba(0,0,0,0.10);
    color: var(--color-on-primary);
}

/* Locked tab — visible but non-interactive, matches sidebar locked pattern */
.tab-btn--locked {
    cursor: not-allowed !important;
    pointer-events: none;
    opacity: 0.42;
    user-select: none;
}
.tab-btn--locked .tab-lock-icon {
    display: inline-flex;
    align-items: center;
    margin-left: 5px;
    opacity: 0.8;
    vertical-align: -2px;
}

/* ── Breadcrumb ──────────────────────────────────────────── */
.breadcrumb {
    display: flex;
    align-items: center;
    gap: 2px;
    font-size: 0.75rem;   /* S-LUX-3: 12px breadcrumb */
    color: var(--text-tertiary);
    margin-bottom: 10px;
    flex-wrap: wrap;
}
.breadcrumb a {
    color: var(--text-secondary);
    text-decoration: none;
    padding: 2px 4px;
    border-radius: var(--radius-sm);
    transition: color var(--transition-fast), background var(--transition-fast);
}
.breadcrumb a:hover {
    color: var(--text-primary);
    background: var(--bg-surface-2);
    text-decoration: none;
}
.breadcrumb-sep {
    color: var(--text-tertiary);
    opacity: 0.4;
    font-size: 0.75rem;
    padding: 0 2px;
    user-select: none;
}
.breadcrumb-current {
    color: var(--text-primary);
    font-weight: 500;
    padding: 2px 4px;
}

/* ── Stat card icons (MODERN structure used by main dashboard) ── */
.stat-card__header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    /* WHY: shrunk 12 → 6 to match the tightened .stat-label margin
       in the legacy block above so both structures look the same. */
    margin-bottom: 6px;
    gap: 8px;
}
.stat-card__icon {
    width: 34px;
    height: 34px;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.stat-card__icon svg { width: 18px; height: 18px; }
.stat-card__icon--primary { background: var(--color-primary-light);  color: var(--color-primary-text); }
.stat-card__icon--success { background: var(--color-success-light);  color: var(--color-success-text); }
.stat-card__icon--warning { background: var(--color-warning-light);  color: var(--color-warning-text); }
.stat-card__icon--danger  { background: var(--color-danger-light);   color: var(--color-danger-text);  }
.stat-card__icon--info    { background: var(--color-info-light);     color: var(--color-info-text);    }
.stat-card__icon--purple  { background: rgba(139,92,246,0.14);       color: #a78bfa;                   }

/* S-LUX-2: the "darker glyphs for a light tile" override block was removed.
   The tile is now a themed surface (dark in dark mode), so the __icon
   variants correctly fall back to their token colours above (bright glyphs
   on dark, dark glyphs on light — the semantic tokens already flip per
   theme). Legacy .stat-icon--{green,blue,…} keep their base defs. Only the
   light-theme darkening that the cream tile needed is dropped. */
[data-theme="light"] .stat-card__icon--primary, [data-theme="light"] .stat-icon--primary { background: var(--color-primary-light); color: var(--color-primary-text); }
[data-theme="light"] .stat-card__icon--success, [data-theme="light"] .stat-icon--success,
[data-theme="light"] .stat-icon--green                               { background: rgba(34,197,94,0.16);  color: #15803d; }
[data-theme="light"] .stat-card__icon--warning, [data-theme="light"] .stat-icon--warning,
[data-theme="light"] .stat-icon--amber                               { background: rgba(245,158,11,0.18); color: #b45309; }
[data-theme="light"] .stat-card__icon--danger,  [data-theme="light"] .stat-icon--danger,
[data-theme="light"] .stat-icon--red                                 { background: rgba(239,68,68,0.15);  color: #b91c1c; }
[data-theme="light"] .stat-card__icon--info,    [data-theme="light"] .stat-icon--info,
[data-theme="light"] .stat-icon--blue                                { background: rgba(59,130,246,0.15); color: #1d4ed8; }
[data-theme="light"] .stat-card__icon--purple,  [data-theme="light"] .stat-icon--purple   { background: rgba(139,92,246,0.16); color: #6d28d9; }
[data-theme="light"] .stat-icon--teal                                { background: rgba(20,184,166,0.16); color: #0f766e; }
[data-theme="light"] .stat-icon--slate                               { background: rgba(100,116,139,0.16);color: #475569; }

/* ── Chart card accent borders ───────────────────────────── */
.card--accent-primary { border-top: 3px solid var(--color-primary); }
.card--accent-success { border-top: 3px solid var(--color-success); }
.card--accent-warning { border-top: 3px solid var(--color-warning); }
.card--accent-danger  { border-top: 3px solid var(--color-danger);  }
.card--accent-info    { border-top: 3px solid var(--color-info);    }
.card--accent-purple  { border-top: 3px solid #8b5cf6;              }

/* ── Summary strip (above tables) ───────────────────────── */
.summary-strip {
    display: flex;
    align-items: center;
    gap: 0;
    margin-bottom: 14px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xl);
    padding: 12px 20px;
    flex-wrap: wrap;
    box-shadow: var(--shadow-xs);
}
.summary-strip__item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 20px 4px 0;
    margin-right: 20px;
    border-right: 1px solid var(--border-color);
}
.summary-strip__item:last-child {
    border-right: none;
    margin-right: 0;
    padding-right: 0;
}
.summary-strip__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
}
.summary-strip__value {
    font-weight: 700;
    font-family: var(--font-mono);
    color: var(--text-primary);
    font-size: 0.9375rem;
}
.summary-strip__label {
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

/* Skip navigation — WCAG 2.4.1 bypass blocks (S025) */
.skip-nav {
    position: absolute;
    top: -100%;
    left: 1rem;
    z-index: 9999;
    padding: 0.5rem 1rem;
    background: var(--color-primary, #2563eb);
    color: var(--color-on-primary);
    border-radius: 0 0 0.375rem 0.375rem;
    font-size: 0.875rem;
    font-weight: 600;
    text-decoration: none;
    transition: top 0.15s ease;
}
.skip-nav:focus {
    top: 0;
    outline: 2px solid var(--color-on-primary);  /* WCAG 2.4.1 control on a brand fill */
    outline-offset: 2px;
}

/* ── Utility classes ──────────────────────────────────────────── */
.line-through { text-decoration: line-through; }            /* FIX #34: void badges */
.font-mono    { font-family: var(--font-mono); }        /* FIX #33: amounts/dates */

/* ── Detail grid — labelled key/value pairs in cards (S009) ─── */
/* Used on payment show, lease show, customer show detail cards.  */
dl.detail-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 6px 16px;
    margin: 0;
    font-size: 0.875rem;
    line-height: 1.5;
}
dl.detail-grid dt {
    color: var(--text-secondary);
    font-weight: 500;
    padding: 2px 0;
    white-space: nowrap;
}
dl.detail-grid dd {
    margin: 0;
    color: var(--text-primary);
    padding: 2px 0;
    word-break: break-word;
}

/* ============================================================
   SECTION 30 — CUSTOMER PORTAL STYLES (S024)
   Separate layout for customer self-service portal.
   Uses same CSS variable system as admin but with distinct layout.
   ============================================================ */

/* ── Portal Layout Shell ────────────────────────────────────── */
.portal-layout {
    display: flex;
    min-height: 100vh;
    background: var(--bg-page);
}
.portal-sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 260px;
    background: var(--bg-card);
    border-right: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    z-index: 40;
    transition: transform 0.25s ease;
}
.portal-sidebar.is-closed {
    transform: translateX(-100%);
}
.portal-main {
    margin-left: 260px;
    flex: 1;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    transition: margin-left 0.25s ease;
}
.portal-main.sidebar-hidden {
    margin-left: 0;
}

/* ── Portal Sidebar Brand ───────────────────────────────────── */
.portal-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 20px 20px 16px;
    border-bottom: 1px solid var(--border-color);
}
.portal-brand-icon {
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: var(--color-accent);
    color: var(--color-on-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.portal-brand-icon svg {
    width: 20px;
    height: 20px;
}
.portal-brand-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.portal-brand-name {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.portal-brand-label {
    font-size: 0.6875rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 500;
}

/* ── Portal Sidebar Nav ─────────────────────────────────────── */
.portal-nav {
    flex: 1;
    padding: 12px 10px;
    overflow-y: auto;
}
.portal-nav-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 14px;
    border-radius: 8px;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--text-secondary);
    text-decoration: none;
    transition: all 0.15s ease;
    margin-bottom: 2px;
}
.portal-nav-item:hover {
    background: var(--bg-hover);
    color: var(--text-primary);
}
/* S-LUX-4: active portal nav adopts the admin-sidebar language — a brand
   tint + 2px left rail instead of a solid orange block. Consumes
   var(--color-primary) so the white-label chain recolours it. */
.portal-nav-item {
    position: relative;
}
.portal-nav-item.is-active {
    background: color-mix(in srgb, var(--color-primary) 14%, transparent);
    color: var(--text-primary);
    font-weight: 600;
}
.portal-nav-item.is-active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 6px;
    bottom: 6px;
    width: 2px;
    border-radius: 0 2px 2px 0;
    background: var(--color-primary);
}
.portal-nav-item.is-active svg,
.portal-nav-item.is-active .portal-nav-icon {
    opacity: 1;
    color: var(--color-primary);
}
.portal-nav-icon {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    opacity: 0.7;
}
.portal-nav-item.is-active .portal-nav-icon {
    opacity: 1;
}
.portal-nav-badge {
    margin-left: auto;
    background: var(--badge-danger-bg);
    color: var(--badge-danger-text);
    font-size: 0.6875rem;
    font-weight: 600;
    padding: 1px 7px;
    border-radius: 10px;
    min-width: 18px;
    text-align: center;
}
/* S-LUX-4: active nav is now tinted (not a solid block), so the badge keeps
   its normal danger colours — the old white-on-fill override is dropped. */
.portal-nav-divider {
    height: 1px;
    background: var(--border-color);
    margin: 8px 14px;
}

/* ── Portal Sidebar Footer (User Section) ───────────────────── */
.portal-sidebar-footer {
    padding: 14px 16px;
    border-top: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap: 10px;
}
.portal-user-avatar {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--color-accent);
    color: var(--color-on-primary);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.75rem;
    font-weight: 600;
    flex-shrink: 0;
}
.portal-user-info {
    flex: 1;
    min-width: 0;
}
.portal-user-name {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary);
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.portal-user-company {
    font-size: 0.6875rem;
    color: var(--text-muted);
    display: block;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ── Portal Topbar ──────────────────────────────────────────── */
.portal-topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 24px;
    height: 56px;
    /* S-LUX-4: glass header matching the admin topbar recipe. */
    background: var(--topbar-bg);
    -webkit-backdrop-filter: blur(14px) saturate(1.4);
    backdrop-filter: blur(14px) saturate(1.4);
    border-bottom: 1px solid var(--border-color);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .portal-topbar { background: var(--bg-card); }
}
.portal-topbar-left {
    display: flex;
    align-items: center;
    gap: 12px;
}
.portal-topbar-title {
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
}
.portal-topbar-right {
    display: flex;
    align-items: center;
    gap: 8px;
}
/* ── Topbar greeting (name + date) ─────────────────────────────────────────── */
.portal-topbar-greeting {
    display: flex;
    align-items: center;
    gap: 6px;
    padding-right: 12px;
    border-right: 1px solid var(--border-color);
    margin-right: 4px;
    white-space: nowrap;
    font-size: 0.8125rem;
    color: var(--text-secondary);
    line-height: 1;
}
.portal-topbar-greeting-name {
    font-weight: 500;
    color: var(--text-primary);
}
.portal-topbar-greeting-sep {
    color: var(--text-muted);
    font-size: 0.75rem;
}
.portal-topbar-greeting-date {
    color: var(--text-muted);
    font-size: 0.75rem;
}
.portal-mobile-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    border: none;
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
}
.portal-mobile-toggle:hover {
    background: var(--bg-hover);
}
.portal-mobile-toggle svg {
    width: 22px;
    height: 22px;
}

/* ── Portal Content ─────────────────────────────────────────── */
.portal-content {
    flex: 1;
    padding: 24px;
    max-width: 1200px;
    width: 100%;
    margin: 0 auto;
}
/* S-LEGAL-FOOTER-COMMERCIAL: customer portal footer — 2-column
   commercial layout. Branding (logo + tagline + Avi attribution) on
   the left, legal + support link columns on the right, copyright +
   version pill across the bottom. Wraps to single column under 768px. */
.portal-footer {
    background: var(--bg-surface-2, #1e293b);
    border-top: 1px solid var(--border-color);
    margin-top: auto;
    color: var(--text-muted);
}
.portal-footer-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 40px 32px 24px;
    display: flex;
    justify-content: space-between;
    gap: 40px;
    flex-wrap: wrap;
}
.portal-footer-brand   { max-width: 320px; }
.portal-footer-logo    { display: flex; align-items: center; gap: 10px; }
.portal-footer-logo-img { max-height: 40px; max-width: 200px; object-fit: contain; }
.portal-footer-name    { font-size: 1.0625rem; font-weight: 600; color: var(--text-primary); }
.portal-footer-tagline { color: var(--text-muted); font-size: 0.875rem; margin: 12px 0 4px; line-height: 1.5; }
.portal-footer-sub     { font-size: 0.75rem; color: var(--text-muted); margin: 0; }
.portal-footer-sub strong { color: var(--text-secondary); font-weight: 500; }
.portal-footer-links   { display: flex; gap: 48px; flex-wrap: wrap; }
.portal-footer-col     { display: flex; flex-direction: column; gap: 10px; min-width: 140px; }
.portal-footer-col h4  {
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);   /* S-LUX-4: token */
    color: var(--text-tertiary);
    margin: 0 0 4px;
}
/* S-LUX-4: version reads as a hairline mono chip, not plain text. */
.portal-footer-version {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    color: var(--text-tertiary);
    padding: 2px 8px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-full);
    background: var(--bg-surface);
}
.portal-footer-col a   {
    font-size: 0.875rem;
    color: var(--text-secondary);
    text-decoration: none;
    transition: color var(--transition-fast);
}
.portal-footer-col a:hover { color: var(--color-primary); }
.portal-footer-bottom  {
    max-width: 1200px;
    margin: 0 auto;
    padding: 16px 32px;
    border-top: 1px solid var(--border-color);
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 0.6875rem;
    color: var(--text-muted);
}
@media (max-width: 768px) {
    .portal-footer-inner  { flex-direction: column; padding: 32px 20px 20px; }
    .portal-footer-links  { gap: 32px; }
    .portal-footer-bottom { flex-direction: column; gap: 6px; text-align: center; padding: 14px 20px; }
}

/* ── Portal Overdue Banner ──────────────────────────────────── */
.portal-alert-banner {
    padding: 10px 24px;
    font-size: 0.8125rem;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 8px;
}
.portal-alert-banner svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}
.portal-alert-banner--danger {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-text);
    border-bottom: 1px solid var(--color-danger);
}
.portal-alert-banner--warning {
    background: var(--badge-warning-bg);
    color: var(--badge-warning-text);
    border-bottom: 1px solid var(--color-warning);
}

/* ── Portal KPI Tiles ───────────────────────────────────────── */
.portal-kpi-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 16px;
    margin-bottom: 24px;
}
.portal-kpi-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 6px;
    text-decoration: none;
    transition: all 0.15s ease;
    position: relative;
    overflow: hidden;
}
.portal-kpi-card:hover {
    border-color: var(--color-accent);
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}
.portal-kpi-card::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
}
.portal-kpi-card--blue::before { background: var(--color-accent); }
.portal-kpi-card--green::before { background: var(--color-success); }
.portal-kpi-card--red::before { background: var(--color-danger); }
.portal-kpi-card--amber::before { background: var(--color-warning); }
.portal-kpi-card--purple::before { background: #8b5cf6; }

.portal-kpi-label {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.portal-kpi-value {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--text-primary);
    font-family: var(--font-mono);
    line-height: 1.2;
}
.portal-kpi-sub {
    font-size: 0.75rem;
    color: var(--text-secondary);
}

/* ── Portal Section Headings ────────────────────────────────── */
.portal-section {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    overflow: hidden;
    margin-bottom: 20px;
}
.portal-section-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-color);
}
.portal-section-title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 8px;
}
.portal-section-title svg {
    width: 18px;
    height: 18px;
    color: var(--text-muted);
}
.portal-section-body {
    padding: 20px;
}
.portal-section-body--flush {
    padding: 0;
}

/* ── Portal Tables ──────────────────────────────────────────── */
.portal-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.8125rem;
}
.portal-table th {
    text-align: left;
    padding: 10px 16px;
    font-weight: 600;
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    background: var(--bg-muted);
    border-bottom: 1px solid var(--border-color);
    white-space: nowrap;
}
.portal-table td {
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-primary);
    vertical-align: middle;
}
.portal-table tr:last-child td {
    border-bottom: none;
}
.portal-table tr:hover td {
    background: var(--bg-hover);
}
.portal-table .font-mono {
    font-family: var(--font-mono);
}
.portal-table .text-right {
    text-align: right;
}
.portal-table .text-center {
    text-align: center;
}
.portal-table-link {
    color: var(--color-accent);
    text-decoration: none;
    font-weight: 500;
}
.portal-table-link:hover {
    text-decoration: underline;
}

/* ── Portal Tab Navigation ──────────────────────────────────── */
/* WHY display:flex + flex-wrap (not inline-flex):
   inline-flex sizes to its content width and would overflow the
   viewport when the tab list is long. Block flex with wrapping
   keeps the bar inside its container and lets long tab lists
   flow onto a second row. */
.portal-tabs {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
    background: var(--bg-muted);
    border-radius: 10px;
    padding: 4px;
    gap: 2px;
    row-gap: 6px;
    margin-bottom: 16px;
}
.portal-tab-btn {
    padding: 7px 16px;
    border-radius: 7px;
    border: none;
    background: transparent;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--text-secondary);
    cursor: pointer;
    transition: all 0.15s ease;
    white-space: nowrap;
}
.portal-tab-btn:hover {
    color: var(--text-primary);
    background: var(--bg-hover);
}
.portal-tab-btn.is-active {
    background: var(--color-accent);
    color: var(--color-on-primary);
    font-weight: 600;
    box-shadow: var(--shadow-sm);
}
.portal-tab-count {
    font-size: 0.6875rem;
    margin-left: 4px;
    opacity: 0.75;
}

/* ── Portal Cards Grid (Equipment) ──────────────────────────── */
.portal-cards-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 16px;
}
.portal-equip-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 20px;
    transition: all 0.15s ease;
}
.portal-equip-card:hover {
    border-color: var(--color-accent);
    box-shadow: var(--shadow-md);
}
.portal-equip-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 14px;
}
.portal-equip-unit {
    font-size: 1rem;
    font-weight: 700;
    color: var(--text-primary);
}
.portal-equip-type {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    margin-top: 2px;
}
.portal-equip-meta {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    font-size: 0.8125rem;
    margin-bottom: 14px;
}
.portal-equip-meta-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.portal-equip-meta-label {
    font-size: 0.6875rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    font-weight: 500;
}
.portal-equip-meta-value {
    font-weight: 600;
    color: var(--text-primary);
}

/* ── Mileage Progress Bar ───────────────────────────────────── */
.portal-mileage-bar {
    margin-top: 8px;
}
.portal-mileage-header {
    display: flex;
    justify-content: space-between;
    font-size: 0.75rem;
    color: var(--text-secondary);
    margin-bottom: 6px;
}
.portal-mileage-track {
    height: 8px;
    background: var(--bg-muted);
    border-radius: 4px;
    overflow: hidden;
    position: relative;
}
.portal-mileage-fill {
    height: 100%;
    border-radius: 4px;
    transition: width 0.5s ease;
    background: var(--color-success);
}
.portal-mileage-fill--warning {
    background: var(--color-warning);
}
.portal-mileage-fill--danger {
    background: var(--color-danger);
}

/* ── Portal Detail Page ─────────────────────────────────────── */
.portal-detail-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    margin-bottom: 24px;
    gap: 16px;
    flex-wrap: wrap;
}
.portal-detail-title {
    font-size: 1.375rem;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 4px;
}
.portal-detail-subtitle {
    font-size: 0.875rem;
    color: var(--text-secondary);
}
.portal-detail-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
}
.portal-detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 20px;
    margin-bottom: 24px;
}

/* ── Portal Detail Info Card ────────────────────────────────── */
.portal-info-card {
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    overflow: hidden;
}
.portal-info-card-header {
    padding: 14px 18px;
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    background: var(--bg-muted);
    border-bottom: 1px solid var(--border-color);
}
.portal-info-list {
    padding: 0;
    margin: 0;
    list-style: none;
}
.portal-info-list li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 18px;
    border-bottom: 1px solid var(--border-color);
    font-size: 0.8125rem;
}
.portal-info-list li:last-child {
    border-bottom: none;
}
.portal-info-label {
    color: var(--text-secondary);
    font-weight: 500;
}
.portal-info-value {
    color: var(--text-primary);
    font-weight: 600;
    text-align: right;
}

/* ── Portal Timeline ────────────────────────────────────────── */
.portal-timeline {
    padding: 20px;
}
.portal-timeline-item {
    display: flex;
    gap: 12px;
    padding-bottom: 20px;
    position: relative;
}
.portal-timeline-item:last-child {
    padding-bottom: 0;
}
.portal-timeline-item:not(:last-child)::after {
    content: '';
    position: absolute;
    left: 11px;
    top: 26px;
    bottom: 0;
    width: 2px;
    background: var(--border-color);
}
.portal-timeline-dot {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--bg-muted);
    border: 2px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    z-index: 1;
}
.portal-timeline-dot svg {
    width: 12px;
    height: 12px;
    color: var(--text-muted);
}
.portal-timeline-dot--blue { border-color: var(--color-accent); background: #eff6ff; }
.portal-timeline-dot--green { border-color: var(--color-success); background: #dcfce7; }
.portal-timeline-dot--amber { border-color: var(--color-warning); background: #fef3c7; }
[data-theme="dark"] .portal-timeline-dot--blue { background: #1e2a3a; }
[data-theme="dark"] .portal-timeline-dot--green { background: #052e16; }
[data-theme="dark"] .portal-timeline-dot--amber { background: #451a03; }
.portal-timeline-content {
    flex: 1;
    min-width: 0;
}
.portal-timeline-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary);
}
.portal-timeline-date {
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-top: 2px;
}

/* ── Portal Service Request Thread ──────────────────────────── */
.portal-thread {
    padding: 20px;
}
.portal-thread-message {
    padding: 16px;
    border-radius: 10px;
    margin-bottom: 12px;
    font-size: 0.8125rem;
    line-height: 1.6;
}
.portal-thread-message--customer {
    background: var(--bg-muted);
    margin-left: 24px;
}
.portal-thread-message--admin {
    background: #eff6ff;
    border: 1px solid #bfdbfe;
    margin-right: 24px;
}
[data-theme="dark"] .portal-thread-message--admin {
    background: #1e2a3a;
    border-color: #2e3a4a;
}
.portal-thread-author {
    font-weight: 600;
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    gap: 8px;
}
.portal-thread-time {
    font-weight: 400;
    font-size: 0.75rem;
    color: var(--text-muted);
}

/* ── Portal Activity Feed ───────────────────────────────────── */
.portal-activity-list {
    padding: 0;
    margin: 0;
    list-style: none;
}
.portal-activity-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px 20px;
    border-bottom: 1px solid var(--border-color);
    font-size: 0.8125rem;
}
.portal-activity-item:last-child {
    border-bottom: none;
}
.portal-activity-icon {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.portal-activity-icon svg {
    width: 16px;
    height: 16px;
}
.portal-activity-icon--blue { background: #eff6ff; color: var(--color-accent); }
.portal-activity-icon--green { background: #dcfce7; color: var(--color-success); }
.portal-activity-icon--amber { background: #fef3c7; color: var(--color-warning); }
.portal-activity-icon--red { background: #fee2e2; color: var(--color-danger); }
[data-theme="dark"] .portal-activity-icon--blue { background: #1e2a3a; }
[data-theme="dark"] .portal-activity-icon--green { background: #052e16; }
[data-theme="dark"] .portal-activity-icon--amber { background: #451a03; }
[data-theme="dark"] .portal-activity-icon--red { background: #450a0a; }
.portal-activity-text {
    flex: 1;
    min-width: 0;
}
.portal-activity-desc {
    color: var(--text-primary);
    line-height: 1.4;
}
.portal-activity-time {
    font-size: 0.75rem;
    color: var(--text-muted);
    margin-top: 2px;
}

/* ── Portal Empty State ─────────────────────────────────────── */
/* S-LUX-4: portal empty state aligned to the canonical D-LUX4-6 recipe. */
.portal-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    max-width: 360px;
    margin-inline: auto;
    padding: 48px 20px;
}
.portal-empty svg {
    width: 48px;
    height: 48px;
    color: var(--text-tertiary);
    stroke-width: 1.5;
    margin-bottom: 16px;
    opacity: 0.9;
}
.portal-empty-title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 4px;
}
.portal-empty-text {
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

/* ── Portal Login Page ──────────────────────────────────────── */
.portal-login-page {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bg-page);
    padding: 20px;
}
.portal-login-card {
    width: 100%;
    max-width: 420px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 16px;
    padding: 40px 36px;
    box-shadow: var(--shadow-lg);
}
.portal-login-brand {
    text-align: center;
    margin-bottom: 32px;
}
.portal-login-logo {
    width: 48px;
    height: 48px;
    border-radius: 12px;
    background: var(--color-accent);
    color: var(--color-on-primary);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 12px;
}
.portal-login-logo svg {
    width: 26px;
    height: 26px;
}
.portal-login-title {
    font-size: 1.25rem;
    font-weight: 700;
    color: var(--text-primary);
    margin: 0 0 4px;
}
.portal-login-subtitle {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    margin: 0;
}
.portal-form-group {
    margin-bottom: 16px;
}
/* S-DESIGN-INPUTS: portal mirrors admin Apple aesthetic. */
.portal-form-label {
    display: block;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--label-text);
    margin-bottom: 6px;
    letter-spacing: 0.01em;
}
.portal-form-input {
    width: 100%;
    padding: 10px 14px;
    border: none;
    border-radius: 8px;
    font-size: 0.875rem;
    color: var(--input-text);
    background: var(--input-bg);
    transition: background 0.15s ease, box-shadow 0.15s ease;
    outline: none;
    font-family: inherit;
    box-sizing: border-box;
}
.portal-form-input:focus {
    background: var(--input-bg-focus);
    box-shadow: 0 0 0 2px var(--color-primary);
}
.portal-form-input::placeholder {
    color: var(--input-placeholder);
}
.portal-form-textarea {
    resize: vertical;
    min-height: 100px;
}
.portal-form-select {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b6b66' d='M3 4.5L6 8l3-3.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 12px;
    padding-right: 32px;
}
.portal-form-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 20px;
}
.portal-form-check {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8125rem;
    color: var(--text-secondary);
}
.portal-form-check input[type="checkbox"] {
    width: 16px;
    height: 16px;
    border-radius: 4px;
    accent-color: var(--color-accent);
}
.portal-form-link {
    font-size: 0.8125rem;
    color: var(--color-accent);
    text-decoration: none;
    font-weight: 500;
}
.portal-form-link:hover {
    text-decoration: underline;
}
.portal-login-btn {
    width: 100%;
    padding: 11px 20px;
    border-radius: 8px;
    border: none;
    background: var(--color-accent);
    color: var(--color-on-primary);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s ease;
    font-family: inherit;
}
.portal-login-btn:hover {
    background: var(--color-accent-hover);
}
.portal-login-btn:active {
    transform: scale(0.98);
}
.portal-login-error {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-text);
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 0.8125rem;
    font-weight: 500;
    margin-bottom: 16px;
    display: flex;
    align-items: center;
    gap: 8px;
}
.portal-login-error svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}
.portal-login-success {
    background: var(--badge-success-bg);
    color: var(--badge-success-text);
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 0.8125rem;
    font-weight: 500;
    margin-bottom: 16px;
}

/* ── Portal Filter/Search Bar ───────────────────────────────── */
.portal-filters {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}
/* S-DESIGN-INPUTS: portal search/filter use Apple pill aesthetic. */
.portal-search-input {
    flex: 1;
    min-width: 200px;
    max-width: 320px;
    padding: 8px 12px 8px 34px;
    border: none;
    border-radius: 8px;
    font-size: 0.8125rem;
    color: var(--input-text);
    background: var(--input-bg) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='%239c9c96'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z'/%3E%3C/svg%3E") no-repeat 10px center / 16px;
    outline: none;
    transition: background 0.15s ease, box-shadow 0.15s ease;
}
.portal-search-input::placeholder { color: var(--input-placeholder); }
.portal-search-input:focus {
    background-color: var(--input-bg-focus);
    box-shadow: 0 0 0 2px var(--color-primary);
}
.portal-filter-select {
    padding: 8px 32px 8px 12px;
    border: none;
    border-radius: 8px;
    font-size: 0.8125rem;
    color: var(--input-text);
    background: var(--input-bg);
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%236b6b66' d='M3 4.5L6 8l3-3.5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 12px;
    outline: none;
    cursor: pointer;
    transition: background-color 0.15s ease, box-shadow 0.15s ease;
}
.portal-filter-select:focus {
    background-color: var(--input-bg-focus);
    box-shadow: 0 0 0 2px var(--color-primary);
}

/* ── Portal Pagination ──────────────────────────────────────── */
.portal-pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 16px;
}
.portal-pagination-btn {
    padding: 6px 12px;
    border-radius: 6px;
    border: 1px solid var(--border-color);
    background: var(--bg-card);
    color: var(--text-secondary);
    font-size: 0.8125rem;
    cursor: pointer;
    transition: all 0.15s ease;
}
.portal-pagination-btn:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
}
.portal-pagination-btn.is-active {
    background: var(--color-accent);
    color: var(--color-on-primary);
    border-color: var(--color-accent);
}
.portal-pagination-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* ── Portal Responsive ──────────────────────────────────────── */
@media (max-width: 1024px) {
    .portal-sidebar {
        transform: translateX(-100%);
    }
    .portal-sidebar.is-open {
        transform: translateX(0);
    }
    .portal-main {
        margin-left: 0;
    }
    .portal-mobile-toggle {
        display: flex;
    }
}
@media (max-width: 768px) {
    .portal-kpi-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .portal-content {
        padding: 16px;
    }
    .portal-detail-grid {
        grid-template-columns: 1fr;
    }
    .portal-cards-grid {
        grid-template-columns: 1fr;
    }
    .portal-topbar {
        padding: 0 16px;
    }
    /* Greeting hidden on mobile — topbar too narrow for name + date + icons */
    .portal-topbar-greeting {
        display: none;
    }
    .portal-equip-meta {
        grid-template-columns: 1fr;
    }
    .portal-filters {
        flex-direction: column;
        align-items: stretch;
    }
    .portal-search-input {
        max-width: none;
    }
}
@media (max-width: 480px) {
    .portal-kpi-grid {
        grid-template-columns: 1fr;
    }
    .portal-login-card {
        padding: 28px 20px;
    }
    /* portal-tabs already wraps via flex-wrap; no scroll needed. */
}

/* ── Portal Mobile Overlay ──────────────────────────────────── */
.portal-sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    z-index: 35;
}
@media (max-width: 1024px) {
    .portal-sidebar-overlay.is-visible {
        display: block;
    }
}


/* ═══════════════════════════════════════════════════════════════
   RESPONSIVE-1 — SESSION 2026-04-08
   Tablet + mobile responsive rules and theme-toggle visibility fix.
   STRICTLY ADDITIVE — does not modify any existing selectors.
   Breakpoints:
     Desktop : 1024px+  (unchanged)
     Tablet  : 768..1023px  (sidebar collapses to icon rail)
     Mobile  : ≤767px  (sidebar slides in, hamburger shown)
   ═══════════════════════════════════════════════════════════════ */

/* ── FIX 1 — Theme toggle icon visibility ─────────────────────
   Bordered pill with contrasting background so sun/moon icons
   never blend into the topbar. Icon sized to 20×20 per spec.
   WHY svg descendant selector: heroicon() is per-name cached so
   the svg inside .topbar-theme-btn may inherit the sidebar's
   .theme-icon class — we target svg directly to be class-agnostic
   and never fight the existing sidebar-scoped display rules. */
.topbar-theme-btn {
    border: 1px solid var(--border-color-strong);
    background-color: var(--bg-surface-2);
    color: var(--text-primary);
    min-width: 36px;
    min-height: 36px;
    /* WHY: skip background transition so the background recomputes
       instantly from --bg-surface-2 on data-theme change. The inherited
       .btn-icon transition otherwise caches the start value and the
       button visually stays on the previous theme's surface tint. */
    transition: border-color var(--transition-fast), color var(--transition-fast);
}
.topbar-theme-btn svg {
    width: 20px !important;
    height: 20px !important;
    color: var(--text-primary);
    stroke: currentColor;
    stroke-width: 2;
    display: block !important;
}
.topbar-theme-btn:hover {
    border-color: var(--color-primary);
    background-color: var(--bg-surface-hover);
}
.topbar-theme-btn:hover svg {
    color: var(--color-primary);
}
[data-theme="light"] .topbar-theme-btn svg { color: var(--text-primary); }
[data-theme="dark"]  .topbar-theme-btn svg { color: var(--text-primary); }

/* S-LUX-2: the [data-theme="light"] .stat-card cream/white override was
   removed — .stat-card now uses the token-driven Atelier recipe
   (var(--bg-surface) + --card-sheen + --shadow-ambient), whose light
   variants already give a white tile with a soft ambient shadow. One
   surface definition for both themes. */

/* ── FIX 2 — Shared utility classes (new, non-conflicting) ─── */

/* KPI grid — auto columns that collapse gracefully */
.kpi-grid,
.stat-grid--4 {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 16px;
}
/* WHY --5: the base .stat-grid is locked to repeat(6,1fr) for the 12-tile
   dashboard (2 clean rows of 6). A 5-tile row inheriting that leaves a whole
   empty column and squeezes each tile to 1/6 of the width — enough to wrap a
   17-char VIN and a brand/model onto a second line. Sizing to the real tile
   count reclaims ~26% width per tile. */
.stat-grid--5 {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 16px;
}
.stat-grid--3 {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
}
.stat-grid--2 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
}

/* Form responsive grid */
.form-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
}
.form-group--full { grid-column: 1 / -1; }

/* Table-responsive wrapper (additive — complements existing .table-wrapper) */
.table-responsive {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border-radius: var(--radius-xl);
}

/* ── Hamburger button (mobile only) ─────────────────────────── */
.hamburger-btn {
    display: none;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border-color);
    background: var(--bg-surface-2);
    color: var(--text-primary);
    cursor: pointer;
    flex-shrink: 0;
}
.hamburger-btn:hover {
    background: var(--bg-surface-hover);
    border-color: var(--color-primary);
}
.hamburger-btn .nav-icon {
    width: 22px;
    height: 22px;
}

/* ═══════════════════════════════════════════════════════════════
   TABLET — 768..1023px
   Sidebar collapses to an always-visible 64px icon rail.
   Overrides existing max-width:1023px rules via later source order.
   ═══════════════════════════════════════════════════════════════ */
@media (min-width: 768px) and (max-width: 1023px) {
    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        width: var(--sidebar-width-collapsed);
        height: 100%;
        transform: translateX(0);
        /* SIDEBAR-SCROLL-1: was `overflow: visible` — which killed sidebar-nav
           scrolling because the outer element refused to clip, so the inner
           scroll container never had a bounded parent to scroll against.
           Clip here; inner .sidebar-nav still scrolls via its own overflow. */
        overflow: hidden;
    }
    .sidebar .sidebar-brand-name,
    .sidebar .nav-item-label,
    .sidebar .nav-group-arrow,
    .sidebar .sidebar-user-info {
        opacity: 0;
        pointer-events: none;
        max-width: 0;
        visibility: hidden;
    }

    /* Section separators + nav-children must fully collapse out of flow
       in tablet icon-rail mode. opacity/visibility/max-width alone leaves
       block-level height behind: ~34px per .nav-section-label, plus the
       full natural height of any .nav-group.is-open > .nav-children
       (~40px × N children) since base CSS forces display:flex on the
       open group. Mirrors the desktop collapsed rule at line ~988.
       Specificity note: `.nav-group .nav-children` (0,3,0) is needed to
       match `.nav-group.is-open > .nav-children` (0,3,0) so source order
       wins — `.sidebar .nav-children` alone (0,2,0) loses to the open
       rule defined earlier in the file. */
    .sidebar .nav-section-label,
    .sidebar .nav-group .nav-children {
        display: none;
    }
    /* S-SIDEBAR-COLLAPSED-BADGE-DOT — pin notification badges to the
       top-right of the nav item as a small iOS-style dot. Mirrors the
       desktop collapsed rule at line ~947. position:absolute removes
       the badge from the flex flow so it cannot reserve a gap slot. */
    .sidebar .nav-badge {
        position: absolute;
        top: 4px;
        right: 8px;
        min-width: 16px;
        height: 16px;
        padding: 0 4px;
        font-size: 0.5625rem;
        line-height: 1;
        opacity: 1;
        pointer-events: auto;
        max-width: none;
        visibility: visible;
        z-index: 1;
    }
    .sidebar .sidebar-brand {
        justify-content: center;
        padding: 0 8px;
        /* S-DESIGN-LOGO-BIGGER: tablet sidebar is always icon-only,
           so the brand area should match topbar height (no L-step). */
        height: var(--topbar-height);
    }
    .sidebar .sidebar-logo {
        max-width: 44px;
        height: 40px;
    }
    .sidebar .sidebar-brand-icon {
        width: 40px;
        height: 40px;
    }
    .sidebar .brand-icon {
        width: 24px;
        height: 24px;
    }
    .sidebar .sidebar-collapse-btn {
        display: none;
    }
    .sidebar .nav-item {
        justify-content: center;
        padding: 10px 8px;
        /* S-SIDEBAR-COLLAPSED-ICON-FIX — zero inter-child gap so the
           hidden .nav-item-label can't reserve a 10px slot that
           pushes the icon off center. */
        gap: 0;
    }
    .sidebar .nav-item-icon,
    .sidebar .nav-icon {
        margin: 0 auto;
        flex-shrink: 0;
    }
    .sidebar .sidebar-user {
        justify-content: center;
    }
    .sidebar .sidebar-user-actions {
        flex-direction: column;
    }

    /* Main content sits to the right of the 64px rail */
    .app-main {
        margin-left: var(--sidebar-width-collapsed);
    }

    /* Hamburger hidden on tablet — rail is always visible */
    .topbar-menu-btn,
    .hamburger-btn {
        display: none;
    }

    /* S-TOPBAR-FIT — one rung up from the ≤767px S-MOBILE-CHROME-CLEANUP block.
       The topbar previously had only TWO tablet rules in the whole file (the
       hamburger hide above and a search max-width), so every icon rendered at
       desktop size from 768px up: .topbar-right asked for ~852px against a
       ~776px ceiling and the excess became a document-wide scrollbar.

       Drop the same once-set preference controls the mobile block drops.
       KEPT deliberately: ＋New, search, team chat (its unread badge has no
       other always-visible home), the bell, and the avatar. The text-size /
       density control also lives on the profile page, so hiding it here
       strands nothing. */
    .topbar-theme,
    .topbar-theme-btn,
    .topbar-display,
    .topbar-display-btn,
    .sound-toggle-btn,
    .topbar-ai,
    .topbar-ai-btn {
        display: none !important;
    }

    /* (The avatar-only account chip is handled by the ≤1279px rule below,
       which spans this band too.) */

    /* KPI + stat grids collapse to 2 columns */
    .kpi-grid,
    .stat-grid--5,
    .stat-grid--4,
    .stat-grid--3 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* WHY: at tablet widths (768-1023px) many pages have 6-7 action buttons
       that overflow the single-row header. Give the actions bar full width so
       buttons wrap to a second line rather than overflowing the container. */
    .page-header-actions {
        flex-wrap: wrap;
        width: 100%;
    }
}

/* ═══════════════════════════════════════════════════════════════
   MOBILE — ≤767px
   Sidebar hidden by default; slides in via hamburger toggle.
   Overlay + body-no-scroll handled by Alpine state.
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
    /* Hamburger visible; hide desktop search bar */
    .hamburger-btn {
        display: inline-flex;
    }
    .topbar-search-btn {
        display: none;
    }
    /* S-FIX-DOUBLE-HAMBURGER-MOBILE: hide the legacy hamburger on mobile so only
       `.hamburger-btn` shows. The button is `class="topbar-menu-btn btn-icon"`, and
       the base `.topbar-menu-btn { display:none }` (line ~1200) was silently
       overridden by `.btn-icon { display:inline-flex }` (equal specificity, declared
       later) — so the legacy plain hamburger rendered alongside the boxed
       `.hamburger-btn`, giving TWO hamburgers at phone widths. `.hamburger-btn` has
       no `.btn-icon` class so it was never clobbered. The `.topbar-left` scope
       (0,2,0) outranks `.btn-icon` (0,1,0) to win the cascade here.
       NOTE: this is deliberately MOBILE-ONLY — on desktop (≥1024px) a COLLAPSED
       sidebar hides its own `.sidebar-collapse-btn` chevron and relies on
       `.topbar-menu-btn` as its expand control (see the rule at the
       `.sidebar:not(.is-open) .sidebar-collapse-btn` block), so the legacy button
       must stay available there; tablet already hides both (permanent rail). */
    .topbar-left .topbar-menu-btn {
        display: none;
    }
    .topbar {
        padding: 0 12px;
    }
    .topbar-left {
        gap: 6px;
    }
    .topbar-right {
        gap: 4px;
    }
    .topbar-title {
        font-size: 1rem;
        font-weight: 600;
    }

    /* ════════════════════════════════════════════════════════════
       S-MOBILE-CHROME-CLEANUP — mobile topbar simplification

       Goal: only show what's essential on a phone-width topbar.
       Keep visible:
         hamburger · page title · + (icon-only pill) · 🔍 · 🔔 · avatar
       Hide on mobile (still available on desktop/tablet):
         home button, theme toggle, A+ display, sound toggle,
         team chat, AI sparkles, inline search input
       Rationale: AI + chat live in the sidebar nav / floating bubble;
       theme + display + sound are once-set preferences that don't
       need to be on the topbar of a phone; search collapses to an
       icon that opens the existing ⌘K modal full-screen.
       ════════════════════════════════════════════════════════════ */

    /* + New: drop the text label, keep just the "+" icon for a
       compact pill that still hints at the quick-create dropdown. */
    .topbar-create-btn__label { display: none; }
    .topbar-create-btn {
        padding: 8px;
        min-width: 36px;
        justify-content: center;
    }

    /* Hide the inline search input; show the magnifying-glass icon
       that opens the existing ⌘K global search modal. */
    .search-wrapper { display: none; }
    /* S-TOPBAR-FIT: scoped to match the base hide's new specificity. */
    .topbar-right .topbar-search-icon-btn { display: inline-flex; }

    /* Hide secondary topbar controls — they live elsewhere or are
       once-set preferences not needed on every page-load on mobile. */
    .topbar-home-btn,
    .topbar-theme,          /* S-TOPBAR-FIT: wrapper too, not just the button — */
    .topbar-theme-btn,      /* an empty wrapper still consumes a flex gap.       */
    .topbar-display,
    .topbar-display-btn,
    .sound-toggle-btn,
    .topbar-chat-btn,
    .topbar-ai,             /* S-TOPBAR-FIT */
    .topbar-ai-btn,
    .topbar-company-name,
    .topbar-breadcrumb-sep {
        display: none !important;
    }
    .topbar-home-btn {
        display: none;
    }

    /* Sidebar overlay (new div injected from header.php) */
    .sidebar-overlay {
        display: none;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.5);
        z-index: calc(var(--z-sidebar) - 1);
    }
    .sidebar-overlay.is-visible {
        display: block;
    }

    /* KPI + stat grids collapse to single column */
    .kpi-grid,
    .stat-grid--5,
    .stat-grid--4,
    .stat-grid--3,
    .stat-grid--2 {
        grid-template-columns: 1fr;
    }

    /* QBO Dashboard / Customers / Drift KPI rows opt INTO a 2x2 mobile
       layout (instead of the system default 1x4 stacked column). The
       4 cards on these pages are compact enough — short numeric values
       + 1-2 lines of supporting text — that a 2x2 layout reads better
       than a 1x4 tall stack at mobile widths. Other modules can opt in
       by adding the same modifier; operator surfaced 2026-05-21. */
    .kpi-grid--qbo {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* Invoice address cards stack vertically on mobile */
    .invoice-addresses {
        grid-template-columns: 1fr;
    }

    /* Forms collapse to a single column */
    .form-grid {
        grid-template-columns: 1fr;
    }
    .form-group--full { grid-column: 1; }
    .form-row,
    .form-row-2,
    .form-row-3,
    .form-row-4 {
        grid-template-columns: 1fr;
    }

    /* Form actions — stack, reverse, full width */
    .form-actions {
        flex-direction: column-reverse;
        gap: 8px;
        align-items: stretch;
    }
    .form-actions .btn {
        width: 100%;
    }

    /* Page header — stack title above actions */
    .page-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 12px;
    }
    .page-header-actions {
        width: 100%;
        flex-wrap: wrap;
    }
    .page-header .btn,
    .page-header-actions .btn {
        width: 100%;
    }

    /* Modals — bottom-sheet style */
    .modal-overlay {
        align-items: flex-end;
    }
    .modal {
        width: 100%;
        max-width: 100%;
        margin: 0;
        border-radius: 16px 16px 0 0;
        max-height: 90vh;
        overflow-y: auto;
    }
    /* On mobile (bottom-sheet), stack modal footer buttons full-width
       in reverse order so the primary action stays at the top. */
    .modal-footer {
        flex-direction: column-reverse;
        align-items: stretch;
    }
    .modal-footer .btn {
        width: 100%;
        justify-content: center;
        min-width: 0;
    }

    /* Tabs — horizontal scroll, hide scrollbar */
    .tab-nav,
    .tabs,
    .tabs-list {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        flex-wrap: nowrap;
    }
    .tab-nav::-webkit-scrollbar,
    .tabs::-webkit-scrollbar,
    .tabs-list::-webkit-scrollbar { display: none; }
    .tab-btn,
    .tab-item,
    .tab {
        white-space: nowrap;
        flex-shrink: 0;
    }

    /* Filter bars — stack vertically */
    .filter-bar,
    .table-toolbar {
        flex-direction: column;
        align-items: stretch;
    }
    .filter-bar select,
    .filter-bar input,
    .filter-bar .btn,
    .table-toolbar-left,
    .table-toolbar-right {
        width: 100%;
    }
    .table-toolbar-left .form-control,
    .table-toolbar-right .form-control,
    .table-toolbar-left .btn,
    .table-toolbar-right .btn {
        width: 100%;
    }

    /* Ensure every table scrolls horizontally on mobile — safety net
       for tables whose surrounding markup was not updated this pass. */
    .table-wrapper,
    .table-responsive,
    .card > .table,
    .card > .data-table {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    /* 640px min-width is the desktop safety floor that lets a table
       horizontally scroll inside a narrow wrapper. Excluded: tables that
       have been transformed into mobile cards (.table-stack) or spec
       key/value tables (no <thead>) — those flow to viewport width and
       would only get clipped at 640px since the parent .card has
       overflow:hidden. Override to min-width:0 for them. */
    table.table,
    table.data-table {
        min-width: 640px;
    }
    table.table.table-stack,
    .card .table:not(.table-stack):not([data-labelled]) {
        min-width: 0 !important;
        width: 100% !important;
    }

    /* Larger tap targets on mobile */
    .btn {
        min-height: 44px;
    }
    .btn-xs,
    .btn-sm {
        min-height: 36px;
    }
    .btn-icon,
    .action-btn,
    .nav-item,
    .pagination-btn,
    .page-btn,
    .hamburger-btn {
        min-height: 44px;
    }

    /* Page content padding tightened (existing rule already does 20/16; keep) */
    .page-content {
        padding: 16px 12px;
    }

    /* Notification dropdown — already full-width via existing rules */
}

/* ═══════════════════════════════════════════════════════════════
   TABLE-STACK — responsive horizontal CAROUSEL of card-rows.
   app.js auto-applies .table-stack + reads data-label from <th>, so
   every table in the app picks up this treatment automatically on
   narrow viewports.

   The mobile experience matches the .carousel-card pattern on the
   main dashboard (Active Leases / Pending Activations): each <tr>
   becomes a swipeable card with label-above-value rows, and the
   tbody becomes the horizontal scroll track with scroll-snap so
   each card lands flush at the start.

   Cells stay block-stacked inside the card (label eyebrow + value
   beneath) because that pattern survives the widest variety of <td>
   children — anchors with secondary <div>s, conditional badges in
   <template x-if>, <span>s with x-text — and avoids Safari/iOS
   formatting-context quirks where a flexbox child of <table> is
   silently ignored.
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
    /* Make the TABLE itself the horizontal carousel container.
       WHY not tbody: iOS Safari's UA stylesheet enforces
       `tbody { display: table-row-group }` and ignores author
       `display:flex !important` in many contexts. The reliable
       workaround is to flex the <table> directly and collapse
       <tbody> via `display:contents` so its <tr> children become
       direct flex children of the table. */
    .table-stack {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: nowrap !important;
        gap: 12px !important;
        width: 100% !important;
        overflow-x: auto !important;
        overflow-y: visible !important;
        -webkit-overflow-scrolling: touch;
        scroll-snap-type: x mandatory;
        scrollbar-width: none;
        padding: 4px 20px 8px 0 !important;
        scroll-padding-left: 4px;
        margin: 0 !important;
        border-collapse: separate !important;
        border-spacing: 0 !important;
    }
    .table-stack::-webkit-scrollbar { display: none; }
    .table-stack thead  { display: none !important; }
    .table-stack tbody  { display: contents !important; }
    .table-stack tfoot  { display: contents !important; }

    /* Each row is a carousel card flex-item, directly under the
       <table> flex container thanks to tbody/tfoot display:contents.
       Dimensions and border mirror .carousel-card on the main
       dashboard so the visual language is consistent. */
    .table-stack tr {
        display: flex !important;
        flex-direction: column !important;
        flex: 0 0 78vw !important;
        max-width: 320px !important;
        min-width: 220px !important;
        scroll-snap-align: start;
        margin: 0 !important;
        padding: 18px 20px !important;
        border: 1px solid var(--border-color) !important;
        border-radius: 14px !important;
        background: var(--bg-card) !important;
        box-shadow:
            inset 0 1px 0 rgba(255,255,255,0.025),
            0 1px 2px rgba(0,0,0,0.18);
        transition: border-color 200ms ease, box-shadow 200ms ease;
        box-sizing: border-box;
    }
    .table-stack tr:hover {
        border-color: var(--color-primary);
    }

    /* tfoot row visually marked as a summary card with brand-tinted
       top border. tfoot itself is display:contents above. */
    .table-stack tfoot tr {
        border-top: 2px solid var(--color-primary) !important;
    }

    /* Each cell becomes a stacked label-above-value row inside the card.
       !important is required because .table tbody td (specificity
       0,1,2) beats .table-stack td (0,1,1) and would otherwise hold
       the desktop padding + table-cell display. */
    .table-stack td {
        display: block !important;
        padding: 6px 0 !important;
        border: none !important;
        text-align: left !important;
        font-size: 0.875rem;
        color: var(--text-primary);
        white-space: normal !important;
        overflow-wrap: anywhere;
        word-break: break-word;
        min-width: 0;
    }
    .table-stack td:first-child { padding-top: 0 !important; }
    .table-stack td:last-child  { padding-bottom: 0 !important; }

    /* Skip cells that have no label — typically the trailing actions <td>
       which has an empty <th>. Render with a hairline divider so action
       buttons sit visually separated from the card data. */
    .table-stack td:not([data-label]),
    .table-stack td[data-label=""] {
        padding-top: 12px !important;
        margin-top: 4px;
        border-top: 1px solid var(--border-color) !important;
    }
    .table-stack td:not([data-label]):first-child,
    .table-stack td[data-label=""]:first-child {
        border-top: 0 !important;
        margin-top: 0;
    }

    /* The label sits above the value as a small uppercase eyebrow —
       same typography as .cc-footer-label on the dashboard carousel. */
    .table-stack td[data-label]:not([data-label=""])::before {
        content: attr(data-label);
        display: block;
        margin-bottom: 3px;
        font-size: 0.625rem;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        color: var(--text-muted);
    }

    /* Reset any text-right/text-center alignment that desktop tables use —
       on mobile every cell reads top-down, left-aligned. */
    .table-stack td.text-right,
    .table-stack td.text-center { text-align: left !important; }

    /* Empty-state row (single <td colspan="N">No results</td>) gets
       full carousel-card width so the "no data" message doesn't look
       like a misaligned narrow card. */
    .table-stack tbody tr:only-child td[colspan] {
        text-align: center !important;
        padding: 12px 0 !important;
    }

    /* ── SPEC TABLES (key/value, no <thead>) ─────────────────────────
       Show pages render two-column key/value layouts as `<table class="table">`
       with no <thead> — the first <td> is the label, second is the value.
       The auto-labeller skips these (it requires <thead><th>), so on mobile
       the table keeps its desktop width (~640px) and gets clipped by the
       parent card's overflow, leaving only the left column visible.

       Detect them via :not([data-labelled]) + :not(.table-stack) and stack
       each row's two cells the same way as auto-stack tables. */
    .card .table:not(.table-stack):not([data-labelled]),
    .card .table:not(.table-stack):not([data-labelled]) tbody {
        display: block !important;
        width: 100% !important;
    }
    .card .table:not(.table-stack):not([data-labelled]) tr {
        display: block !important;
        padding: 8px 0 !important;
        border: none !important;
        border-bottom: 1px solid var(--border-color) !important;
    }
    .card .table:not(.table-stack):not([data-labelled]) tr:last-child {
        border-bottom: none !important;
    }
    .card .table:not(.table-stack):not([data-labelled]) td {
        display: block !important;
        padding: 2px 0 !important;
        border: none !important;
        text-align: left !important;
        white-space: normal !important;
        overflow-wrap: anywhere;
        word-break: break-word;
    }
    /* First cell = label (small uppercase eyebrow). Detect via the
       .text-secondary class that every spec-table uses for the label
       column, and fall back to :first-child for tables that don't. */
    .card .table:not(.table-stack):not([data-labelled]) td:first-child,
    .card .table:not(.table-stack):not([data-labelled]) td.text-secondary {
        font-size: 0.6875rem !important;
        font-weight: 600 !important;
        text-transform: uppercase;
        letter-spacing: 0.06em;
        color: var(--text-tertiary) !important;
        margin-bottom: 2px;
    }
}

/* ═══════════════════════════════════════════════════════════════
   EQUIPMENT LIST — explicit mobile card view
   WHY: the auto-stack CSS transform (table → stacked cards) has a
   timing race with Alpine x-for. Instead, the equipment index uses
   two separate templates: .eq-table-desktop (hidden ≤767px) and
   .eq-cards-mobile (hidden ≥768px). Values are plain x-text bindings
   so there is no dependency on data-label injection timing.
   ═══════════════════════════════════════════════════════════════ */

/* Desktop table: hidden on mobile */
.eq-table-desktop { display: block; }

/* Mobile card list: hidden on desktop */
.eq-cards-mobile  { display: none; }

@media (max-width: 767px) {
    .eq-table-desktop { display: none !important; }
    .eq-cards-mobile  { display: block; padding: 12px; }
}

/* Individual unit card */
.eq-mobile-card {
    display: block;
    text-decoration: none;
    color: inherit;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: 14px 16px;
    margin-bottom: 10px;
    transition: background var(--transition-fast), box-shadow var(--transition-fast);
}
.eq-mobile-card:hover,
.eq-mobile-card:active {
    background: var(--bg-surface-hover);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    color: inherit;
}

/* Header row: unit number (left) + status badge (right) */
.eq-mc-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border-color);
}
.eq-mc-unit {
    font-size: 1rem;
    font-weight: 700;
    color: var(--text-primary);
}

/* Key / value rows */
.eq-mc-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 4px 0;
    font-size: 0.8125rem;
}
.eq-mc-label {
    color: var(--text-tertiary);
    font-weight: 600;
    flex-shrink: 0;
    white-space: nowrap;
}
.eq-mc-value {
    color: var(--text-primary);
    text-align: right;
    min-width: 0;
    word-break: break-word;
}

/* ═══════════════════════════════════════════════════════════════
   TABLET — kpi + stat grids for tablet
   ═══════════════════════════════════════════════════════════════ */
@media (min-width: 768px) and (max-width: 1023px) {
    .stat-grid--2 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    .form-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    .page-content {
        padding: 18px 14px;
    }
}


/* ═══════════════════════════════════════════════════════════════
   SEARCH-1 — SESSION 2026-04-08
   Inline topbar search widget (replaces the legacy button pattern).
   All selectors scoped under .search-wrapper so they never collide
   with the existing modal search panel (.search-panel .search-input).
   ═══════════════════════════════════════════════════════════════ */

.search-wrapper {
    position: relative;
    flex: 1 1 auto;
    max-width: 420px;
    min-width: 0;
}

.search-wrapper .search-input-wrap {
    position: relative;
    display: flex;
    align-items: center;
    padding: 0;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-full);
    background: var(--bg-surface-2);
    transition: border-color var(--transition-fast),
                background var(--transition-fast),
                box-shadow var(--transition-fast);
    gap: 0;
    height: 36px;
}

.search-wrapper .search-input-wrap:focus-within {
    border-color: var(--color-primary);
    background: var(--bg-surface);
    box-shadow: 0 0 0 3px var(--color-primary-light);
}

.search-wrapper .search-input-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: none;
    background: transparent;
    color: var(--text-tertiary);
    cursor: pointer;
    border-radius: var(--radius-full) 0 0 var(--radius-full);
    flex-shrink: 0;
}
.search-wrapper .search-input-icon:hover {
    color: var(--text-primary);
}
.search-wrapper .search-input-icon .search-icon {
    width: 16px;
    height: 16px;
}

.search-wrapper .search-input {
    flex: 1 1 auto;
    height: 34px;
    padding: 0 8px 0 0;
    border: none;
    background: transparent;
    color: var(--text-primary);
    font-size: 0.875rem;
    font-family: inherit;
    outline: none;
    min-width: 0;
}
.search-wrapper .search-input::placeholder {
    color: var(--text-tertiary);
}

.search-wrapper .search-kbd--inline {
    display: inline-flex;
    align-items: center;
    font-family: var(--font-mono);
    font-size: 0.6875rem;
    padding: 2px 6px;
    margin-right: 6px;
    background: var(--bg-surface);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-sm);
    color: var(--text-tertiary);
    flex-shrink: 0;
    white-space: nowrap;
}

/* CSS spinner — uses currentColor so it theme-adapts */
.search-wrapper .search-spinner {
    width: 14px;
    height: 14px;
    margin-right: 8px;
    border: 2px solid var(--border-color);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: ff-search-spin 0.8s linear infinite;
    flex-shrink: 0;
}
@keyframes ff-search-spin {
    to { transform: rotate(360deg); }
}

/* ── Dropdown ────────────────────────────────────────────────── */
.search-wrapper .search-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
    z-index: var(--z-dropdown);
    max-height: 480px;
    overflow-y: auto;
    padding: 4px 0;
}

.search-wrapper .search-group + .search-group {
    border-top: 1px solid var(--border-color);
    margin-top: 4px;
    padding-top: 4px;
}

.search-wrapper .search-group-label {
    padding: 8px 14px 4px;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-tertiary);
}

.search-wrapper .search-result-item {
    display: block;
    padding: 8px 14px;
    text-decoration: none;
    color: var(--text-primary);
    transition: background var(--transition-fast);
}
.search-wrapper .search-result-item:hover {
    background: var(--bg-surface-hover);
    text-decoration: none;
}

.search-wrapper .search-result-main {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.search-wrapper .search-result-title {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
    flex: 1 1 auto;
}

.search-wrapper .search-result-sub {
    font-size: 0.75rem;
    color: var(--text-tertiary);
    margin-top: 2px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.search-wrapper .search-empty,
.search-wrapper .search-loading {
    padding: 20px 16px;
    text-align: center;
    color: var(--text-tertiary);
    font-size: 0.8125rem;
}

.search-wrapper .search-see-all {
    display: block;
    width: 100%;
    padding: 10px 14px;
    text-align: center;
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--color-primary);
    background: transparent;
    border: none;
    border-top: 1px solid var(--border-color);
    cursor: pointer;
    margin-top: 4px;
}
.search-wrapper .search-see-all:hover {
    background: var(--bg-surface-hover);
}

/* ── Responsive ──────────────────────────────────────────────── */
@media (min-width: 768px) and (max-width: 1023px) {
    /* On tablet the topbar is compact — shrink the search max-width
       and drop the keyboard hint so the input still fits. */
    .search-wrapper {
        max-width: 240px;
    }
    .search-wrapper .search-kbd--inline {
        display: none;
    }
}

@media (max-width: 767px) {
    /* Mobile: hide the inline search entirely — the hamburger gives
       enough real estate. Users can still invoke ⌘K via the modal
       if they're on a keyboard-connected tablet. */
    .search-wrapper {
        display: none;
    }
}


/* ═══════════════════════════════════════════════════════════════
   THEME-1 — SESSION 2026-04-08
   Spacing tokens (theme-agnostic) + scrollbar styling.
   These rules are strictly additive — they introduce new tokens
   in :root and lightweight global rules; existing selectors are
   unchanged unless explicitly overridden below.
   ═══════════════════════════════════════════════════════════════ */

:root {
    /* New spacing tokens — used by .page-content / .card / .section /
       .kpi-grid / table cells. Defined here (not in [data-theme=*])
       because spacing is theme-agnostic. */
    --section-gap:    28px;
    --card-gap:       20px;
    --card-padding:   24px;
    --page-padding:   28px;
    --table-cell-px:  16px;
    --table-cell-py:  12px;
}

/* Apply spacing tokens to layout primitives. Source order > existing
   .page-content / .card rules above so these win the cascade. */
.page-content,
.main-content {
    padding: var(--page-padding);
}
.card,
.card-body {
    padding: var(--card-padding);
}
.section + .section {
    margin-top: var(--section-gap);
}
.kpi-grid,
.stat-grid--5,
.stat-grid--4,
.stat-grid--3,
.stat-grid--2 {
    gap: var(--card-gap);
}
table th,
table td {
    padding: var(--table-cell-py) var(--table-cell-px);
}

/* ── Global scrollbar styling (Firefox + WebKit) ─────────────────
   Variables fall back to the previous look in light mode where
   --scrollbar-bg / --scrollbar-thumb are not defined (browser
   uses its native scrollbar). Dark mode uses the warm palette. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scrollbar-thumb, #4a4845)
                     var(--scrollbar-bg, transparent);
}
*::-webkit-scrollbar { width: 6px; height: 6px; }
*::-webkit-scrollbar-track {
    background: var(--scrollbar-bg, transparent);
}
*::-webkit-scrollbar-thumb {
    background: var(--scrollbar-thumb, #4a4845);
    border-radius: 3px;
}

/* ============================================================
   NOTIF-1 — Notifications system (bell, dropdown, page)
   ============================================================
   Lives in its own block at the end so it can override the
   legacy .topbar-bell-btn / .notification-* rules above when
   the topbar markup migrates to .notif-* (NOTIF-1).
   ============================================================ */

.notif-wrapper {
    position: relative;
    flex-shrink: 0;   /* S-TOPBAR-FIT — see .topbar-create */
}

.notif-bell-btn {
    position: relative;
}
.notif-bell-btn.has-unread {
    color: var(--color-accent, #3b82f6);
}
.notif-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    border-radius: 9px;
    background: var(--color-danger, #dc2626);
    color: #ffffff;
    font-size: 10px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--bg-page, #0f0f0f);
    pointer-events: none;
    line-height: 1;
}

.notif-dropdown {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    width: 380px;
    background: var(--bg-card, var(--bg-surface, #1a1a1a));
    border: 1px solid var(--border-color, #2e2e2e);
    border-radius: var(--radius-xl, 12px);
    box-shadow: var(--shadow-lg, 0 4px 16px rgba(0, 0, 0, .4));
    z-index: 500;
    max-height: 520px;
    overflow-y: auto;
}
.notif-dropdown-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 16px;
    border-bottom: 1px solid var(--border-color, #2e2e2e);
    position: sticky;
    top: 0;
    background: var(--bg-card, var(--bg-surface, #1a1a1a));
    z-index: 1;
}
.notif-dropdown-title {
    font-weight: 600;
    font-size: 14px;
    color: var(--text-primary, #e8e8e4);
}
.notif-mark-all {
    font-size: 12px;
    color: var(--color-accent, #3b82f6);
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
}
.notif-mark-all:hover {
    text-decoration: underline;
}

.notif-loading,
.notif-empty {
    padding: 28px 16px;
    text-align: center;
    color: var(--text-muted, #6b6b66);
    font-size: 13px;
}
.notif-empty p {
    margin: 8px 0 0;
}

.notif-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 12px 16px;
    text-decoration: none;
    color: var(--text-primary, #e8e8e4);
    border-bottom: 1px solid var(--border-color, #2e2e2e);
    transition: background 0.15s;
    position: relative;
}
.notif-item:last-child {
    border-bottom: none;
}
.notif-item:hover {
    background: var(--bg-muted, var(--bg-surface-hover, #242424));
    text-decoration: none;
}
.notif-item--unread {
    background: var(--bg-selected, #1e2a3a);
    border-left: 3px solid var(--color-accent, #3b82f6);
}
.notif-item--unread:hover {
    background: var(--bg-hover, #2a2a2a);
}

.notif-icon {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: var(--bg-muted, #242424);
    color: var(--text-secondary, #9c9c96);
}
.notif-icon svg {
    width: 18px;
    height: 18px;
}
.notif-icon--leases       { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }
.notif-icon--invoices     { background: rgba(168, 85, 247, 0.15); color: #c084fc; }
.notif-icon--payments     { background: rgba(34, 197, 94, 0.15);  color: #4ade80; }
.notif-icon--customers    { background: rgba(148, 163, 184, 0.15); color: #94a3b8; }
.notif-icon--equipment    { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }
.notif-icon--compliance   { background: rgba(250, 204, 21, 0.15); color: #facc15; }
.notif-icon--maintenance  { background: rgba(148, 163, 184, 0.15); color: #94a3b8; }
.notif-icon--damage       { background: rgba(239, 68, 68, 0.15);  color: #f87171; }
.notif-icon--reservations { background: rgba(59, 130, 246, 0.15); color: #60a5fa; }
.notif-icon--samsara      { background: rgba(14, 165, 233, 0.15); color: #38bdf8; }
.notif-icon--accounting   { background: rgba(168, 85, 247, 0.15); color: #c084fc; }
.notif-icon--system       { background: rgba(148, 163, 184, 0.15); color: #94a3b8; }
.notif-icon--info         { background: rgba(14, 165, 233, 0.15); color: #38bdf8; }
.notif-icon--warning      { background: rgba(245, 158, 11, 0.20); color: #fbbf24; }
.notif-icon--danger       { background: rgba(239, 68, 68, 0.20);  color: #f87171; }

.notif-content {
    flex: 1;
    min-width: 0;
}
.notif-title {
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
    margin-bottom: 2px;
    color: var(--text-primary, #e8e8e4);
}
.notif-item--unread .notif-title {
    font-weight: 600;
}
.notif-message {
    font-size: 12px;
    color: var(--text-secondary, #9c9c96);
    line-height: 1.4;
    margin-bottom: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.notif-time {
    font-size: 11px;
    color: var(--text-muted, #6b6b66);
    font-family: var(--font-mono);
}
.notif-unread-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--color-accent, #3b82f6);
    flex-shrink: 0;
    margin-top: 6px;
}

.notif-dropdown-footer {
    display: block;
    padding: 12px 16px;
    text-align: center;
    font-size: 13px;
    color: var(--color-accent, #3b82f6);
    text-decoration: none;
    border-top: 1px solid var(--border-color, #2e2e2e);
    position: sticky;
    bottom: 0;
    background: var(--bg-card, var(--bg-surface, #1a1a1a));
}
.notif-dropdown-footer:hover {
    background: var(--bg-muted, var(--bg-surface-hover, #242424));
    text-decoration: none;
}

/* ── Full notifications page ──────────────────────────────── */
.notif-page-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.notif-page-item {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 16px 20px;
    border-bottom: 1px solid var(--border-color, #2e2e2e);
    position: relative;
}
.notif-page-item:last-child {
    border-bottom: none;
}
.notif-page-item--unread {
    background: var(--bg-selected, #1e2a3a);
    border-left: 3px solid var(--color-accent, #3b82f6);
}
.notif-page-content {
    flex: 1;
    min-width: 0;
}
.notif-page-title {
    font-size: 14px;
    font-weight: 500;
    color: var(--text-primary, #e8e8e4);
    margin-bottom: 4px;
}
.notif-page-item--unread .notif-page-title {
    font-weight: 600;
}
.notif-page-message {
    font-size: 13px;
    color: var(--text-secondary, #9c9c96);
    line-height: 1.5;
    margin-bottom: 6px;
}
.notif-page-meta {
    font-size: 11px;
    color: var(--text-muted, #6b6b66);
    font-family: var(--font-mono);
}
.notif-page-actions {
    display: flex;
    gap: 8px;
    flex-shrink: 0;
    align-items: center;
}
.notif-page-empty {
    padding: 64px 24px;
    text-align: center;
    color: var(--text-muted, #6b6b66);
}
.notif-page-empty .nav-icon {
    width: 48px;
    height: 48px;
    margin: 0 auto 12px;
    color: var(--text-muted, #6b6b66);
}
.notif-page-empty-title {
    font-size: 16px;
    font-weight: 500;
    color: var(--text-primary, #e8e8e4);
    margin: 0 0 4px;
}
.notif-page-empty-sub {
    font-size: 13px;
    color: var(--text-muted, #6b6b66);
    margin: 0;
}

/* ── Page-header helpers used by both /notifications pages ─── */
.notif-page-header-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
/* Generic page subtitle — also used by other admin pages
   (documents, analytics, reports). Was previously rendered
   without a CSS rule. */
.page-subtitle {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--text-secondary, #9c9c96);
    font-weight: 400;
}

/* ── Filter card on /notifications ─────────────────────────── */
.notif-filter-card {
    margin-bottom: 20px;
}
.notif-filter-form {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    align-items: flex-end;
}
.notif-filter-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 140px;
}
.notif-filter-field--grow {
    flex: 1 1 220px;
    min-width: 220px;
}
.notif-filter-field .form-label {
    font-size: 0.75rem;
    margin: 0;
    color: var(--text-secondary, #9c9c96);
    font-weight: 500;
}
.notif-filter-actions {
    display: flex;
    gap: 8px;
    align-items: center;
}

/* ── List card body should have zero padding so items hug edges ─ */
.notif-list-body {
    padding: 0 !important;
}

/* ── Pagination footer ─────────────────────────────────────── */
.notif-page-footer {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 12px 16px;
    flex-wrap: wrap;
    gap: 8px;
}
.notif-page-footer-text {
    font-size: 0.8125rem;
}
.notif-page-footer-actions {
    display: flex;
    gap: 6px;
}

/* ── Mobile: full-width dropdown under topbar ──────────────── */
@media (max-width: 767px) {
    .notif-dropdown {
        position: fixed;
        top: 60px;
        left: 0;
        right: 0;
        width: 100%;
        border-radius: 0;
        max-height: calc(100vh - 60px);
    }
    .notif-page-item {
        flex-wrap: wrap;
    }
    .notif-page-actions {
        width: 100%;
        justify-content: flex-end;
        margin-top: 8px;
    }
    .notif-filter-form {
        flex-direction: column;
        align-items: stretch;
    }
    .notif-filter-field,
    .notif-filter-field--grow {
        min-width: 0;
        width: 100%;
    }
}


/* ============================================================
   EMAIL-1 — Global Email Compose Modal
   Used by includes/partials/email-compose-modal.php
   ============================================================ */

#ff-email-compose .modal-icon {
    width: 18px;
    height: 18px;
    display: inline-block;
    vertical-align: middle;
    margin-right: 6px;
}
#ff-email-compose .modal-title {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* Quick contact chips ----------------------------------------- */
.email-chip-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}
.email-chip-label {
    font-size: 11px;
    color: var(--text-secondary);
    margin-right: 4px;
}
.email-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border: 1px solid var(--border-color);
    background: var(--bg-muted);
    color: var(--text-primary);
    border-radius: 999px;
    font-size: 11.5px;
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.email-chip:hover {
    background: var(--bg-hover);
    border-color: var(--color-accent);
}
.email-chip-meta {
    color: var(--text-secondary);
    font-weight: 400;
}
.email-chip-tag {
    margin-left: 4px;
    padding: 1px 6px;
    background: var(--badge-info-bg);
    color: var(--badge-info-text);
    border-radius: 999px;
    font-size: 9.5px;
    font-weight: 600;
    text-transform: uppercase;
}

/* Variables panel --------------------------------------------- */
.email-variables-panel {
    margin-top: 8px;
    padding: 10px;
    background: var(--bg-muted);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
}
.email-variables-label {
    font-size: 11px;
    color: var(--text-secondary);
    margin-bottom: 6px;
}
.email-variables-list {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.email-variable-chip {
    display: inline-block;
    padding: 4px 10px;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    color: var(--color-accent);
    font-family: var(--font-mono);
    font-size: 11px;
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}
.email-variable-chip:hover {
    background: var(--bg-hover);
    border-color: var(--color-accent);
}

/* Attachment list --------------------------------------------- */
.email-attachment-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 8px;
}
.email-attachment-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 12px;
    background: var(--bg-muted);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    font-size: 12.5px;
}
.email-attachment-icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    color: var(--text-secondary);
}
.email-attachment-name {
    flex: 1;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.email-attachment-size {
    color: var(--text-secondary);
    font-size: 11px;
    flex-shrink: 0;
}
.email-attachment-remove {
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 2px;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    flex-shrink: 0;
}
.email-attachment-remove:hover {
    background: var(--bg-hover);
    color: var(--color-danger);
}
.email-attachment-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.email-attachment-actions .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.email-attachment-actions .btn-icon {
    width: 14px;
    height: 14px;
}

/* Picker drawers ---------------------------------------------- */
.email-picker-drawer {
    margin-top: 8px;
    background: var(--bg-muted);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    overflow: hidden;
}
.email-picker-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px 12px;
    background: var(--bg-card);
    border-bottom: 1px solid var(--border-color);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-primary);
}
.email-picker-search {
    padding: 8px 12px;
    border-bottom: 1px solid var(--border-color);
}
.email-picker-list {
    max-height: 220px;
    overflow-y: auto;
    padding: 4px;
}
.email-picker-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background var(--transition-fast);
}
.email-picker-item:hover {
    background: var(--bg-hover);
}
.email-picker-icon {
    width: 18px;
    height: 18px;
    color: var(--text-secondary);
    flex-shrink: 0;
}
.email-picker-item-body {
    flex: 1;
    min-width: 0;
}
.email-picker-item-name {
    font-size: 12.5px;
    color: var(--text-primary);
    font-weight: 500;
}
.email-picker-item-meta {
    font-size: 11px;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    gap: 6px;
}
.email-picker-add {
    font-size: 11px;
    color: var(--color-accent);
    font-weight: 600;
}
.email-picker-empty {
    padding: 16px;
    text-align: center;
    color: var(--text-secondary);
    font-size: 12px;
}

/* Live preview frame ------------------------------------------ */
.email-preview-frame {
    margin-top: 12px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    overflow: hidden;
    background: #f5f5f4;
}
.email-preview-label {
    padding: 8px 12px;
    background: var(--bg-muted);
    color: var(--text-secondary);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
}
.email-preview-body {
    padding: 16px;
    color: #1c1c1a;
    background: #ffffff;
    font-size: 13px;
    line-height: 1.6;
    /* FIXPACK-EMAIL-SCROLL-BTN: removed max-height + overflow-y.
       Root cause: email-preview-frame has overflow:hidden (for border-radius);
       that clips the scrollbar of a bounded inner div entirely — no scroll
       handle appears and the email is hard-cut.  Fix: render email-preview-body
       at natural height; modal-body (flex:1 + overflow-y:auto) is the ONE
       scroll container.  email-preview-frame overflow:hidden is harmless
       because the frame has no bounded height — it simply wraps its content. */
}

/* History tab on customer / invoice show pages ---------------- */
.email-history-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.email-history-item {
    display: grid;
    grid-template-columns: auto 1fr auto auto;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    background: var(--bg-card);
}
.email-history-item .badge {
    flex-shrink: 0;
}
.email-history-subject {
    font-weight: 500;
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13px;
}
.email-history-meta {
    font-size: 11px;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 2px;
}
.email-history-actions {
    flex-shrink: 0;
}


/* ============================================================
   MEDIA-1 — Sound mute toggle (topbar)
   Matches the visual language of .chat-topbar-btn so the
   group of topbar icons stays cohesive.
   ============================================================ */
/* S-MOBILE-CHROME-CLEANUP: normalised to match .btn-icon — transparent
   surface + hover-only background fill. The previous solid bg-card +
   1px border made this button look visually distinct from theme / A+ /
   bell / avatar in the topbar group, breaking visual consistency. */
.sound-toggle-btn {
  position: relative;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
  flex-shrink: 0;
}
.sound-toggle-btn:hover {
  background: var(--bg-surface-2);
  color: var(--text-primary);
}
.sound-toggle-btn.is-muted {
  color: var(--text-muted);
  opacity: 0.6;
}
.sound-toggle-btn .nav-icon {
  width: 18px;
  height: 18px;
}


/* ============================================================
   CHAT-1 — Team Chat styles
   chat-topbar-btn, chat-badge, chat-layout, chat-sidebar,
   chat-main, chat-compose, chat-message, chat-widget
   ============================================================ */

/* ── Topbar chat icon ──────────────────────────────────────── */
/* S-MOBILE-CHROME-CLEANUP: normalised to match .btn-icon — transparent
   surface + hover-only fill. Previously had solid bg-card + 1px border
   that made chat + AI topbar buttons look like distinct boxed elements
   in the topbar group while theme / A+ / bell / avatar rendered flat. */
.chat-topbar-btn {
  position: relative;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  border: none;
  background: transparent;
  color: var(--text-secondary);
  text-decoration: none;
  transition: background var(--transition-fast), color var(--transition-fast);
  flex-shrink: 0;
}
.chat-topbar-btn:hover {
  background: var(--bg-surface-2);
  color: var(--text-primary);
}
.chat-topbar-btn.has-unread {
  color: var(--color-accent);
}
.chat-badge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--color-danger);
  color: #fff;
  font-size: 10px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--bg-page);
  pointer-events: none;
}

/* ── Full chat page layout ────────────────────────────────── */
.chat-page-content {
  padding: 0 !important;
  height: calc(100vh - 56px); /* 56px = topbar height */
  overflow: hidden;
}
.chat-layout {
  display: flex;
  height: 100%;
  background: var(--bg-page);
}

/* ── Left sidebar ────────────────────────────────────────── */
.chat-sidebar {
  width: 240px;
  flex-shrink: 0;
  background: var(--bg-sidebar);
  color: #c8c7c4;
  display: flex;
  flex-direction: column;
  border-right: 1px solid rgba(255,255,255,0.06);
  overflow-y: auto;
}
.chat-sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 14px 10px;
  border-bottom: 1px solid rgba(255,255,255,0.06);
}
.chat-sidebar-title {
  font-size: 15px;
  font-weight: 700;
  color: #fff;
}
.chat-sidebar-section {
  padding: 12px 0 4px;
}
.chat-sidebar-label {
  padding: 0 14px 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.07em;
  color: rgba(255,255,255,0.4);
  text-transform: uppercase;
}
.chat-sidebar-item {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 5px 14px;
  background: none;
  border: none;
  color: rgba(255,255,255,0.72);
  font-size: 13px;
  cursor: pointer;
  text-align: left;
  border-radius: 0;
  transition: background 0.1s, color 0.1s;
}
.chat-sidebar-item:hover,
.chat-sidebar-item.is-active {
  background: rgba(255,255,255,0.08);
  color: #fff;
}
.chat-sidebar-item.has-unread {
  color: #fff;
  font-weight: 600;
}
.chat-channel-hash {
  color: rgba(255,255,255,0.4);
  font-size: 14px;
  width: 14px;
  text-align: center;
  flex-shrink: 0;
}
.chat-dm-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #22c55e;
  flex-shrink: 0;
}
.chat-sidebar-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-unread-badge {
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--color-danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.chat-sidebar-browse {
  display: block;
  width: 100%;
  padding: 4px 14px;
  background: none;
  border: none;
  color: rgba(255,255,255,0.4);
  font-size: 12px;
  cursor: pointer;
  text-align: left;
}
.chat-sidebar-browse:hover { color: rgba(255,255,255,0.8); }

/* ── Main chat area ──────────────────────────────────────── */
.chat-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--bg-card);
}
.chat-empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--text-muted);
}
.chat-empty-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--text-primary);
}
.chat-empty-sub { font-size: 13px; color: var(--text-secondary); }

.chat-channel-view {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* Channel header */
.chat-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border-color);
  background: var(--bg-card);
  flex-shrink: 0;
}
.chat-header-info {
  display: flex;
  align-items: center;
  gap: 6px;
}
.chat-header-hash {
  font-size: 18px;
  color: var(--text-muted);
}
.chat-header-name {
  font-size: 15px;
  font-weight: 700;
  color: var(--text-primary);
}
.chat-header-desc {
  font-size: 12px;
  color: var(--text-secondary);
  margin-left: 4px;
}
.chat-header-actions { display: flex; gap: 4px; }

/* Search bar */
.chat-search-bar {
  border-bottom: 1px solid var(--border-color);
  padding: 8px 16px;
  background: var(--bg-muted);
  flex-shrink: 0;
  position: relative;
}
.chat-search-input {
  width: 100%;
  height: 32px;
  padding: 0 12px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-primary);
  font-size: 13px;
}
.chat-search-results {
  position: absolute;
  left: 16px; right: 16px;
  top: 100%;
  z-index: 50;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  max-height: 300px;
  overflow-y: auto;
}
.chat-search-result {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 8px;
  align-items: center;
  padding: 8px 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--border-color);
}
.chat-search-result:hover { background: var(--bg-hover); }
.chat-search-result-user { font-size: 11px; font-weight: 600; color: var(--text-secondary); }
.chat-search-result-msg  { font-size: 12px; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-search-result-time { font-size: 10px; color: var(--text-muted); }

/* Messages area */
.chat-messages-area {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.chat-load-more {
  text-align: center;
  padding: 8px 0 16px;
}
.chat-loading {
  padding: 16px 0;
}
.chat-channel-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 13px;
  padding: 32px;
}

/* Individual message */
.chat-message {
  display: flex;
  gap: 10px;
  padding: 4px 8px;
  border-radius: 6px;
  position: relative;
}
.chat-message:hover {
  background: var(--bg-hover);
}
.chat-message:hover .chat-message-actions {
  display: flex;
}
.chat-message--highlight {
  background: rgba(59,130,246,0.12) !important;
  transition: background 0.3s;
}
.chat-message--system {
  background: var(--bg-muted);
  border-left: 3px solid var(--border-strong);
  padding-left: 12px;
  margin: 4px 0;
}

/* Avatar */
.chat-avatar {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: var(--color-accent);
  color: var(--color-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
  margin-top: 2px;
}

/* Message body */
.chat-message-body { flex: 1; min-width: 0; }
.chat-message-header {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 2px;
}
.chat-message-author {
  font-size: 13px;
  font-weight: 700;
  color: var(--text-primary);
}
.chat-message-time {
  font-size: 11px;
  color: var(--text-muted);
}
.chat-edited-marker {
  font-size: 10px;
  color: var(--text-muted);
  font-style: italic;
}
.chat-message-text {
  font-size: 13px;
  color: var(--text-primary);
  line-height: 1.5;
  word-break: break-word;
}
.chat-message-deleted {
  font-size: 12px;
  color: var(--text-muted);
  font-style: italic;
}
.chat-mention {
  color: var(--color-accent);
  font-weight: 500;
}
.chat-link {
  color: var(--color-accent);
  text-decoration: underline;
}

/* Reply context */
.chat-reply-context {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  margin-bottom: 4px;
  background: var(--bg-muted);
  border-left: 3px solid var(--color-accent);
  border-radius: 0 4px 4px 0;
  font-size: 11px;
}
.chat-reply-name { font-weight: 600; color: var(--text-primary); }
.chat-reply-text { color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 300px; }

/* Attachment card in message */
.chat-attachment-card {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 6px;
  padding: 10px 12px;
  background: var(--bg-muted);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  max-width: 380px;
}
.chat-attachment-icon { font-size: 20px; flex-shrink: 0; }
.chat-attachment-body { flex: 1; min-width: 0; }
.chat-attachment-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chat-attachment-sub {
  font-size: 11px;
  color: var(--text-secondary);
  margin-top: 1px;
}
.chat-attachment-link { flex-shrink: 0; }

/* Reactions */
.chat-reactions {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}
.chat-reaction-btn {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px;
  border: 1px solid var(--border-color);
  border-radius: 12px;
  background: var(--bg-muted);
  font-size: 12px;
  cursor: pointer;
  transition: all 0.15s;
}
.chat-reaction-btn:hover { background: var(--bg-hover); border-color: var(--color-accent); }
.chat-reaction-btn.mine  { background: rgba(59,130,246,0.1); border-color: var(--color-accent); }
.chat-reaction-add {
  padding: 2px 6px;
  border: 1px dashed var(--border-color);
  border-radius: 12px;
  background: none;
  font-size: 11px;
  cursor: pointer;
  color: var(--text-muted);
}
.chat-reaction-add:hover { border-color: var(--color-accent); color: var(--color-accent); }

/* Message hover actions */
.chat-message-actions {
  display: none;
  position: absolute;
  top: 4px;
  right: 8px;
  gap: 2px;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 2px 4px;
  box-shadow: var(--shadow-md);
  z-index: 10;
}
.chat-action-btn {
  padding: 3px 5px;
  background: none;
  border: none;
  cursor: pointer;
  border-radius: 4px;
  font-size: 13px;
  line-height: 1;
  color: var(--text-secondary);
}
.chat-action-btn:hover { background: var(--bg-hover); }
.chat-action-btn--danger:hover { background: var(--badge-danger-bg); }

/* Emoji picker */
.chat-emoji-picker {
  position: fixed;
  z-index: 200;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 200px;
  box-shadow: var(--shadow-lg);
  bottom: 120px;
  right: 16px;
}
.chat-emoji-btn {
  font-size: 20px;
  padding: 4px;
  background: none;
  border: none;
  cursor: pointer;
  border-radius: 4px;
}
.chat-emoji-btn:hover { background: var(--bg-hover); }

/* ── Compose area ────────────────────────────────────────── */
.chat-compose {
  border-top: 1px solid var(--border-color);
  padding: 10px 16px 12px;
  background: var(--bg-card);
  flex-shrink: 0;
  position: relative;
}
.chat-reply-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  margin-bottom: 8px;
  background: var(--bg-muted);
  border-left: 3px solid var(--color-accent);
  border-radius: 0 6px 6px 0;
  font-size: 12px;
  color: var(--text-secondary);
}
.chat-reply-banner--edit { border-color: var(--color-warning); }
.chat-reply-preview {
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 300px;
}
.chat-reply-cancel {
  margin-left: auto;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted);
  font-size: 13px;
  padding: 0 4px;
}
.chat-reply-cancel:hover { color: var(--color-danger); }

/* Pending attachments */
.chat-pending-attachments {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
.chat-pending-att {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 4px 8px;
  background: var(--bg-muted);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-size: 12px;
}
.chat-pending-att-icon { font-size: 14px; }
.chat-pending-att-name { color: var(--text-primary); max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-pending-att-remove {
  background: none; border: none; cursor: pointer;
  color: var(--text-muted); font-size: 12px; padding: 0 2px;
}
.chat-pending-att-remove:hover { color: var(--color-danger); }

/* Mention dropdown */
.chat-mention-dropdown {
  position: absolute;
  bottom: 100%;
  left: 16px;
  right: 16px;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 100;
  max-height: 200px;
  overflow-y: auto;
}
.chat-mention-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  font-size: 13px;
  color: var(--text-primary);
}
.chat-mention-item:hover { background: var(--bg-hover); }
.chat-mention-avatar {
  width: 24px;
  height: 24px;
  border-radius: 4px;
  background: var(--color-accent);
  color: var(--color-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  flex-shrink: 0;
}

/* Record picker */
.chat-record-picker {
  position: absolute;
  bottom: 100%;
  left: 16px;
  right: 16px;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 100;
  padding: 10px;
}
.chat-record-types {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.chat-record-type-btn {
  padding: 5px 10px;
  background: var(--bg-muted);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  font-size: 12px;
  cursor: pointer;
  color: var(--text-primary);
}
.chat-record-type-btn:hover { background: var(--bg-hover); border-color: var(--color-accent); }
.chat-record-search-header {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 8px;
}
.chat-record-search-input {
  flex: 1;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-primary);
  font-size: 12px;
}
.chat-record-results {
  max-height: 180px;
  overflow-y: auto;
}
.chat-record-result {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 6px 10px;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border-color);
  cursor: pointer;
  text-align: left;
}
.chat-record-result:hover { background: var(--bg-hover); }
.chat-record-result-title { font-size: 13px; font-weight: 500; color: var(--text-primary); }
.chat-record-result-sub { font-size: 11px; color: var(--text-secondary); }
.chat-record-empty { padding: 12px; font-size: 12px; color: var(--text-muted); text-align: center; }

/* Compose input row */
.chat-compose-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  background: var(--bg-input);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-md);
  padding: 6px 8px;
  transition: border-color 0.15s;
}
.chat-compose-row:focus-within { border-color: var(--color-accent); }
.chat-compose-input {
  flex: 1;
  border: none;
  background: none;
  color: var(--text-primary);
  font-size: 13px;
  outline: none;
  line-height: 1.5;
  min-height: 22px;
  max-height: 160px;
  padding: 0;
  font-family: inherit;
}
.chat-compose-actions { display: flex; align-items: center; gap: 4px; }
.chat-attach-btn { color: var(--text-muted); }
.chat-attach-btn:hover { color: var(--color-accent); }
.chat-send-btn { padding: 6px 10px !important; height: 32px; }
.chat-compose-hint {
  margin-top: 5px;
  font-size: 10px;
  color: var(--text-muted);
}
.chat-compose-hint kbd {
  font-size: 10px;
  padding: 1px 4px;
  background: var(--bg-muted);
  border: 1px solid var(--border-color);
  border-radius: 3px;
  font-family: var(--font-mono);
}

/* ── Right info panel ────────────────────────────────────── */
.chat-info-panel {
  width: 280px;
  flex-shrink: 0;
  background: var(--bg-card);
  border-left: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.chat-info-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  border-bottom: 1px solid var(--border-color);
  font-weight: 700;
  font-size: 14px;
}
.chat-info-desc {
  padding: 10px 14px;
  font-size: 12px;
  color: var(--text-secondary);
  border-bottom: 1px solid var(--border-color);
}
.chat-info-section { padding: 10px 14px; }
.chat-info-label {
  font-size: 11px;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: 8px;
}
.chat-info-member {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border-color);
  font-size: 12px;
}
.chat-info-member-avatar {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  background: var(--color-accent);
  color: var(--color-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  flex-shrink: 0;
}
.chat-info-member-name { flex: 1; color: var(--text-primary); font-weight: 500; }
.chat-info-member-role { flex-shrink: 0; }

/* Browse channel modal items */
.chat-browse-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-color);
}
.chat-browse-info { flex: 1; min-width: 0; }
.chat-browse-name { font-size: 13px; font-weight: 600; color: var(--text-primary); }
.chat-browse-desc { font-size: 11px; color: var(--text-secondary); display: block; margin-top: 1px; }
.chat-browse-count { font-size: 10px; color: var(--text-muted); }

/* DM user picker */
.chat-dm-user-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 0;
  background: none;
  border: none;
  border-bottom: 1px solid var(--border-color);
  cursor: pointer;
  text-align: left;
  font-size: 13px;
  color: var(--text-primary);
}
.chat-dm-user-item:hover { color: var(--color-accent); }
.chat-dm-user-avatar {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: var(--color-accent);
  color: var(--color-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  flex-shrink: 0;
}

/* ── Mini chat widget ────────────────────────────────────── */
.chat-widget {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 300;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 12px;
}
.chat-widget-panel {
  width: 320px;
  height: 480px;
  background: var(--bg-card);
  border: 1px solid var(--border-color);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.chat-widget-enter { transition: all 0.2s ease; }
.chat-widget-enter-start { opacity: 0; transform: translateY(12px) scale(0.95); }
.chat-widget-enter-end   { opacity: 1; transform: translateY(0) scale(1); }
.chat-widget-leave { transition: all 0.15s ease; }
.chat-widget-leave-start { opacity: 1; transform: translateY(0); }
.chat-widget-leave-end   { opacity: 0; transform: translateY(8px); }

.chat-widget-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  background: var(--bg-sidebar);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  flex-shrink: 0;
  border-radius: 16px 16px 0 0;
}
.chat-widget-channels {
  flex: 1;
  overflow-y: auto;
  padding: 8px 0;
}
.chat-widget-section-label {
  padding: 4px 14px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  text-transform: uppercase;
}
.chat-widget-channel-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 14px;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 12px;
  color: var(--text-primary);
  border-radius: 0;
}
.chat-widget-channel-item:hover { background: var(--bg-hover); }
.chat-widget-unread {
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 8px;
  background: var(--color-danger);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.chat-widget-messages {
  flex: 1;
  overflow-y: auto;
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.chat-widget-message {}
.chat-widget-msg-header {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 2px;
}
.chat-widget-msg-author { font-size: 11px; font-weight: 700; color: var(--text-primary); }
.chat-widget-msg-time   { font-size: 10px; color: var(--text-muted); }
.chat-widget-msg-text   { font-size: 12px; color: var(--text-primary); line-height: 1.4; word-break: break-word; }
.chat-widget-msg-deleted { font-size: 11px; color: var(--text-muted); font-style: italic; }
.chat-widget-compose {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  padding: 8px 10px;
  border-top: 1px solid var(--border-color);
  flex-shrink: 0;
}
.chat-widget-input {
  flex: 1;
  border: 1px solid var(--border-color);
  border-radius: var(--radius-sm);
  background: var(--bg-input);
  color: var(--text-primary);
  font-size: 12px;
  padding: 5px 8px;
  outline: none;
  font-family: inherit;
  min-height: 30px;
  max-height: 80px;
}
.chat-widget-input:focus { border-color: var(--color-accent); }

/* Widget toggle button */
.chat-widget-toggle {
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--color-accent);
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-on-primary);
  box-shadow: var(--shadow-lg);
  transition: all 0.2s;
  position: relative;
  flex-shrink: 0;
}
.chat-widget-toggle:hover { background: var(--color-accent-hover); transform: scale(1.05); }
.chat-widget-toggle.has-unread { background: var(--color-danger); }
.chat-widget-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 9px;
  background: #fff;
  color: var(--color-danger);
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px solid var(--color-danger);
  pointer-events: none;
}

/* Hide widget on full chat page */
.chat-page-content ~ * #ff-chat-widget,
body:has(.chat-page-content) #ff-chat-widget {
  display: none !important;
}

/* ============================================================
   MSGR-1 — Customer Messenger (admin side)
   Builds on .chat-* layout primitives. Adds Facebook-style
   thread rows, contact picker rows, and the admin/portal side
   labels that distinguish "Team" vs "Customer" messages.
   ============================================================ */
.msgr-thread-item {
  padding: 8px 12px !important;
  gap: 10px;
  align-items: flex-start !important;
}
.msgr-thread-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-on-primary);
  font-weight: 700;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  text-transform: uppercase;
}
.msgr-thread-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.msgr-thread-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 6px;
}
.msgr-thread-name {
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}
.msgr-thread-time {
  font-size: 11px;
  color: rgba(255,255,255,0.4);
  flex-shrink: 0;
}
.msgr-thread-preview {
  font-size: 12px;
  color: rgba(255,255,255,0.55);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.msgr-thread-item.has-unread .msgr-thread-name,
.msgr-thread-item.has-unread .msgr-thread-preview {
  color: #fff;
  font-weight: 600;
}

/* Side labels — small "Team" / "Customer" pill on each message */
.msgr-side-label {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-left: 4px;
}
.msgr-side-label--admin {
  background: rgba(59,130,246,0.15);
  color: #3b82f6;
}
.msgr-side-label--portal {
  background: rgba(34,197,94,0.15);
  color: #22c55e;
}

/* Highlight portal customer messages with a faint left rail so admins
   can scan a long thread and see customer replies at a glance. */
.msgr-message--portal {
  background: rgba(34,197,94,0.04);
  border-left: 2px solid rgba(34,197,94,0.4);
}

/* ── New thread modal — contact picker rows ─────────────── */
.msgr-contact-list {
  max-height: 50vh;
  overflow-y: auto;
  padding-right: 4px;
}
.msgr-contact-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 10px 12px;
  background: none;
  border: 1px solid transparent;
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  transition: background 0.1s, border-color 0.1s;
}
.msgr-contact-item:hover {
  background: var(--bg-hover);
  border-color: var(--border-color);
}
.msgr-contact-avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-on-primary);
  font-weight: 700;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  text-transform: uppercase;
}
.msgr-contact-body {
  flex: 1;
  min-width: 0;
}
.msgr-contact-name {
  font-weight: 600;
  font-size: 14px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.msgr-contact-sub {
  font-size: 12px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.msgr-contact-badge {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 10px;
  background: rgba(59,130,246,0.15);
  color: #3b82f6;
}
.msgr-contact-badge--user {
  background: rgba(168,85,247,0.15);
  color: #a855f7;
}

/* ============================================================
   MSGR-1 — Portal Messenger (customer side)
   Lighter, simpler 2-column layout. No dark sidebar.
   Customers can read + reply but not start new threads.
   ============================================================ */
.portal-msgr-page {
  padding: 0 !important;
  margin: 0 !important;
  max-width: none !important;
  width: 100%;
  height: calc(100vh - 56px); /* topbar */
  overflow: hidden;
}
.portal-content:has(> .portal-msgr-page) {
  padding: 0;
  max-width: none;
}
.portal-msgr-layout {
  display: flex;
  height: 100%;
  background: var(--bg-page);
}

/* ── Left sidebar — thread list ─────────────────────────── */
.portal-msgr-sidebar {
  width: 320px;
  flex-shrink: 0;
  background: var(--bg-card);
  border-right: 1px solid var(--border-color);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.portal-msgr-sidebar-header {
  padding: 18px 20px;
  border-bottom: 1px solid var(--border-color);
}
.portal-msgr-sidebar-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
}
.portal-msgr-thread-list {
  flex: 1;
  overflow-y: auto;
  padding: 6px 0;
}
.portal-msgr-thread-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  width: 100%;
  padding: 12px 18px;
  background: none;
  border: none;
  border-left: 3px solid transparent;
  cursor: pointer;
  text-align: left;
  transition: background 0.1s, border-color 0.1s;
}
.portal-msgr-thread-item:hover {
  background: var(--bg-hover);
}
.portal-msgr-thread-item.is-active {
  background: var(--bg-hover);
  border-left-color: var(--color-accent);
}
.portal-msgr-thread-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--color-accent);
  color: var(--color-on-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.portal-msgr-thread-avatar svg {
  width: 22px;
  height: 22px;
}
.portal-msgr-thread-avatar--lg {
  width: 44px;
  height: 44px;
}
.portal-msgr-thread-avatar--lg svg {
  width: 24px;
  height: 24px;
}
.portal-msgr-thread-body {
  flex: 1;
  min-width: 0;
}
.portal-msgr-thread-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 2px;
}
.portal-msgr-thread-name {
  font-weight: 600;
  font-size: 14px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
}
.portal-msgr-thread-time {
  font-size: 11px;
  color: var(--text-muted);
  flex-shrink: 0;
}
.portal-msgr-thread-preview {
  font-size: 13px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.portal-msgr-thread-prefix {
  color: var(--text-secondary);
  font-weight: 500;
}
.portal-msgr-thread-item.has-unread .portal-msgr-thread-name,
.portal-msgr-thread-item.has-unread .portal-msgr-thread-preview {
  color: var(--text-primary);
  font-weight: 600;
}
.portal-msgr-unread-badge {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 10px;
  background: var(--color-danger);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-top: 10px;
}

/* ── Right pane — active thread ─────────────────────────── */
.portal-msgr-main {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--bg-page);
}
.portal-msgr-empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  text-align: center;
  padding: 40px;
}
.portal-msgr-empty-title {
  font-size: 17px;
  font-weight: 600;
  color: var(--text-primary);
  margin: 0;
}
.portal-msgr-empty-sub {
  font-size: 13px;
  color: var(--text-muted);
  margin: 0;
}
.portal-msgr-empty {
  padding: 32px 20px;
  text-align: center;
}

.portal-msgr-thread-view {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.portal-msgr-thread-header {
  padding: 14px 20px;
  border-bottom: 1px solid var(--border-color);
  background: var(--bg-card);
  flex-shrink: 0;
}
.portal-msgr-thread-header-info {
  display: flex;
  align-items: center;
  gap: 14px;
}
.portal-msgr-thread-header-name {
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
}
.portal-msgr-thread-header-sub {
  font-size: 12px;
  color: var(--text-muted);
}

.portal-msgr-messages-area {
  flex: 1;
  overflow-y: auto;
  padding: 24px 20px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.portal-msgr-load-more {
  text-align: center;
  margin-bottom: 12px;
}
.portal-msgr-thread-empty {
  text-align: center;
  color: var(--text-muted);
  padding: 32px;
}

/* Message bubbles — Facebook Messenger style. Customer's own
   messages on the right, support team's messages on the left. */
.portal-msgr-message {
  display: flex;
  width: 100%;
  margin-bottom: 4px;
}
.portal-msgr-message--mine {
  justify-content: flex-end;
}
.portal-msgr-message--theirs {
  justify-content: flex-start;
}
.portal-msgr-message-bubble {
  max-width: 75%;
  padding: 9px 14px;
  border-radius: 18px;
  position: relative;
  word-wrap: break-word;
  font-size: 14px;
  line-height: 1.4;
}
.portal-msgr-message--mine .portal-msgr-message-bubble {
  background: var(--color-accent);
  color: var(--color-on-primary);
  border-bottom-right-radius: 4px;
}
.portal-msgr-message--theirs .portal-msgr-message-bubble {
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-color);
  border-bottom-left-radius: 4px;
}
.portal-msgr-message-author {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  margin-bottom: 3px;
}
.portal-msgr-message-text {
  white-space: pre-wrap;
}
.portal-msgr-message-deleted {
  font-style: italic;
  opacity: 0.6;
  font-size: 13px;
}
.portal-msgr-message-time {
  font-size: 10px;
  margin-top: 4px;
  opacity: 0.7;
}
.portal-msgr-message--mine .portal-msgr-message-time {
  text-align: right;
}

/* Compose footer — sticky to the bottom of the messages pane */
.portal-msgr-compose {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--border-color);
  background: var(--bg-card);
  flex-shrink: 0;
}
.portal-msgr-compose-input {
  flex: 1;
  resize: none;
  overflow: hidden;
  border: 1px solid var(--border-color);
  border-radius: 20px;
  padding: 10px 16px;
  font-family: inherit;
  font-size: 14px;
  background: var(--bg-page);
  color: var(--text-primary);
  outline: none;
  min-height: 40px;
  max-height: 160px;
  transition: border-color 0.15s;
}
.portal-msgr-compose-input:focus {
  border-color: var(--color-accent);
}
.portal-msgr-send-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Mobile — collapse sidebar to a top picker */
@media (max-width: 768px) {
  .portal-msgr-layout {
    flex-direction: column;
  }
  .portal-msgr-sidebar {
    width: 100%;
    max-height: 35vh;
    border-right: none;
    border-bottom: 1px solid var(--border-color);
  }
  .portal-msgr-message-bubble {
    max-width: 85%;
  }
}


/* ============================================================
   FF_RecordPicker — reusable search-as-you-type record selector
   Used by admin forms (invoices, leases, payments, maintenance,
   reservations, inspections, mileage, damage claims, credit notes)
   to replace static <select> dropdowns with a debounced autocomplete.
   ============================================================ */

.ff-picker                 { position: relative; width: 100%; }
.ff-picker-input           { width: 100%; padding-right: 36px; }
.ff-picker-input.is-selected {
    background-color: var(--bg-muted, rgba(0,0,0,.02));
    font-weight: 500;
}
.ff-picker-clear {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted);
    padding: 2px 6px;
    font-size: 14px;
    line-height: 1;
    border-radius: 4px;
}
.ff-picker-clear:hover {
    background: var(--bg-muted, rgba(0,0,0,.05));
    color: var(--text-primary);
}

/* S-LUX-2.5: floating panel recipe (LUX-3 not shipped yet) — raised
   --bg-surface-2 with a strong hairline, radius-lg and --shadow-lg so the
   dropdown reads as a distinct layer above the pill it drops from. */
.ff-picker-dropdown {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    margin-top: 4px;
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    z-index: 1000;
    max-height: 320px;
    overflow-y: auto;
}
.ff-picker-dropdown::-webkit-scrollbar { width: 6px; }
.ff-picker-dropdown::-webkit-scrollbar-thumb { background: var(--border-color-strong); border-radius: 3px; }

.ff-picker-option {
    padding: 10px 14px;
    cursor: pointer;
    border-bottom: 1px solid var(--border-color);
    transition: background var(--transition-fast);
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.ff-picker-option:last-child { border-bottom: none; }
.ff-picker-option:hover,
.ff-picker-option.is-highlighted {
    background: var(--bg-surface-hover);
}
.ff-picker-option-label {
    font-size: 14px;
    font-weight: 500;
    color: var(--text-primary);
}
.ff-picker-option-sub {
    font-size: 12px;
    color: var(--text-muted);
}
.ff-picker-option-badge {
    display: inline-block;
    font-size: 10px;
    padding: 1px 6px;
    border-radius: 3px;
    background: var(--bg-muted);
    color: var(--text-secondary);
    margin-left: 6px;
    vertical-align: middle;
}

.ff-picker-empty {
    padding: 14px;
    text-align: center;
    font-size: 13px;
    color: var(--text-muted);
}
.ff-picker-loading {
    padding: 10px 14px;
    font-size: 13px;
    color: var(--text-muted);
    display: flex;
    align-items: center;
    gap: 8px;
}
.ff-picker-loading::before {
    content: '';
    width: 12px;
    height: 12px;
    border: 2px solid var(--border-color);
    border-top-color: var(--color-accent);
    border-radius: 50%;
    animation: ff-picker-spin .8s linear infinite;
}
@keyframes ff-picker-spin {
    to { transform: rotate(360deg); }
}

/* ── FF picker — chevron affordance (S-PICKER-DISCOVERABILITY) ─────────────
   A down-chevron appears when nothing is selected so the control reads as a
   picker/combobox, not a plain text input. It and the clear (✕) button share
   the same absolute slot — they never overlap because they are mutually
   exclusive (chevron: x-show="!selected", clear: x-show="selected").
   Rotates 180° when the dropdown is open. ──────────────────────────────── */
.ff-picker-chevron {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-muted, #9ca3af);
    padding: 2px 4px;
    display: flex;
    align-items: center;
    border-radius: 3px;
    transition: color 120ms ease;
    line-height: 0;
    pointer-events: all;
    flex-shrink: 0;
}
.ff-picker-chevron svg {
    transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ff-picker-chevron.is-open svg {
    transform: rotate(180deg);
}
.ff-picker-chevron:hover { color: var(--text-secondary, #6b7280); }

/* The input itself should look interactive even before typing */
.ff-picker-input:not(.is-selected) { cursor: pointer; }
.ff-picker-input.is-selected       { cursor: text; }

/* Hint text shown when dropdown is open but no query typed yet */
.ff-picker-hint {
    padding: 10px 14px;
    font-size: 12px;
    color: var(--text-muted);
    font-style: italic;
}

/* ════════════════════════════════════════════════════════════════════
   UI-AUDIT-1 — Missing utility classes
   ────────────────────────────────────────────────────────────────────
   Source-scan audit turned up ~50 class names referenced in PHP files
   that had no CSS rule anywhere (not in app.css, not in inline <style>
   blocks). Some of them silently did nothing (heading variants, badge
   color modifiers); others caused visibly unstyled UI (modal close
   buttons, form-section wrappers, invalid-feedback error text).
   Rather than churn 100+ template files to rename classes, we define
   the missing selectors here so the templates start rendering correctly.
   WHY here and not inline: centralising them keeps the design tokens
   consistent and prevents per-page drift.
   ════════════════════════════════════════════════════════════════════ */

/* ── N1 — Heading utility classes ──────────────────────────────
   <h1 class="h4"> should render at h4 size, etc. CSS previously
   only defined the element selectors (h4 {}), so the class was inert. */
.h1 { font-size: 1.5rem;   line-height: 1.25; font-weight: 700; }
.h2 { font-size: 1.25rem;  line-height: 1.3;  font-weight: 700; }
.h3 { font-size: 1.125rem; line-height: 1.35; font-weight: 600; }
.h4 { font-size: 1rem;     line-height: 1.4;  font-weight: 600; }
.h5 { font-size: 0.9375rem;line-height: 1.4;  font-weight: 600; }
.h6 { font-size: 0.875rem; line-height: 1.4;  font-weight: 600; }

/* ── N2 — Form validation feedback ──────────────────────────────
   Bootstrap-style .invalid-feedback displayed under an invalid input.
   Used by rates/create.php and any form that wants inline per-field
   error text. */
.invalid-feedback {
    display: block;
    width: 100%;
    margin-top: 4px;
    font-size: 0.75rem;
    color: var(--color-danger-text);
    line-height: 1.4;
}
.valid-feedback {
    display: block;
    width: 100%;
    margin-top: 4px;
    font-size: 0.75rem;
    color: var(--color-success-text);
    line-height: 1.4;
}

/* ── N3 — Form section wrappers ──────────────────────────────
   Used by maintenance_work_orders/create.php to group related
   form fields under a subheading. Matches the existing .card look
   but without the outer border so it can nest inside a card. */
.form-section {
    /* S-LUX-2.5: section rhythm 32px */
    margin-bottom: 32px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border-color);
}
.form-section:last-child {
    border-bottom: 0;
    padding-bottom: 0;
    margin-bottom: 0;
}
.form-section-title {
    /* S-LUX-1: micro-label treatment — matches .card-title / table headers */
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--tracking-label);
    color: var(--text-secondary);
    margin: 0 0 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border-color);
}

/* ── Form control static (read-only display cell) ──────────────
   Bootstrap-style "form-control-static" — renders a value where an
   input would normally live without the input chrome. */
.form-control-static {
    display: block;
    padding: 8px 12px;
    font-family: inherit;
    font-size: 0.875rem;
    color: var(--text-primary);
    background: transparent;
    border: 0;
    min-height: 38px;
    line-height: 1.5;
}

/* ── N5 — Bare .btn-outline (used only in settings/system.php) ──
   The main outline variant we use elsewhere is .btn-outline-danger.
   Plain .btn-outline is a neutral outline button. */
.btn-outline {
    background: transparent;
    color: var(--text-primary);
    border: 1px solid var(--border-color-strong);
}
.btn-outline:hover {
    background: var(--bg-surface-2);
    border-color: var(--text-tertiary);
}

/* ── N7 — Additional badge variants ──────────────────────────────
   Some pages (accounting/fixed-assets, users/*, profile) use colour
   names we hadn't defined as badge variants. Add them as aliases of
   the semantic ones (blue=info, green=success, red=danger). */
.badge-blue   { background: var(--color-info-light);    color: var(--color-info-text);    }
.badge-green  { background: var(--color-success-light); color: var(--color-success-text); }
.badge-red    { background: var(--color-danger-light);  color: var(--color-danger-text);  }
.badge-yellow { background: var(--color-warning-light); color: var(--color-warning-text); }
.badge-gray   { background: rgba(148, 163, 184, 0.12);  color: var(--text-secondary);     }

/* Pill-shaped badge (fully rounded) — used on profile/users pages. */
.badge-pill {
    border-radius: 999px;
    padding-left: 10px;
    padding-right: 10px;
}

/* Smaller badge variant for inline list display. */
.badge-sm {
    font-size: 0.6875rem;
    padding: 1px 6px;
    line-height: 1.4;
}

/* ── Missing table utility classes ──────────────────────────────
   .table-hover — row hover highlight (4 files use this).
   .table-sm    — compact vertical padding.
   .table-row-link — make whole <tr> look clickable when used with @click.
   .table-pagination — style for pagination control row under a table. */
.table-hover tbody tr:hover {
    background: var(--bg-surface-hover);
    transition: background-color var(--transition-fast);
}
.table-sm th,
.table-sm td {
    padding-top: 6px;
    padding-bottom: 6px;
    font-size: 0.8125rem;
}
.table-row-link {
    cursor: pointer;
}
.table-row-link:hover {
    background: var(--bg-surface-hover);
}

/* ============================================================
   FF Bulk Select — Apple-inspired multi-selection UI
   Shared across customers, leases, invoices, equipment, users.
   ============================================================ */

/* ── Custom checkbox ─────────────────────────────────────────
   Replaces browser default. Rounded square, brand-orange when
   checked, smooth scale-on-press like iOS. */
.ff-checkbox {
    appearance: none;
    -webkit-appearance: none;
    width: 17px;
    height: 17px;
    border-radius: 5px;
    border: 1.5px solid rgba(255, 255, 255, 0.2);
    background: transparent;
    cursor: pointer;
    position: relative;
    flex-shrink: 0;
    transition: background 140ms ease, border-color 140ms ease, box-shadow 140ms ease;
    vertical-align: middle;
    display: inline-block;
    outline-offset: 2px;
}
.ff-checkbox:hover:not(:checked) {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px var(--color-primary-light);
}
.ff-checkbox:checked {
    background: var(--color-primary);
    border-color: var(--color-primary);
}
.ff-checkbox:checked::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 5px;
    width: 4px;
    height: 8px;
    border: 1.5px solid var(--color-on-primary);  /* checkmark on the amber checked fill */
    border-top: none;
    border-left: none;
    transform: rotate(45deg);
}
.ff-checkbox:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}
.ff-checkbox:active { transform: scale(0.88); }

/* Light-mode overrides */
[data-theme="light"] .ff-checkbox { border-color: rgba(0,0,0,0.22); }
[data-theme="light"] .ff-checkbox:hover:not(:checked) { border-color: var(--color-primary); }

/* ── Checkbox column sizing ──────────────────────────────────
   Keeps the column tight and vertically centred. */
.th-checkbox {
    width: 44px !important;
    min-width: 44px !important;
    padding: 0 4px 0 14px !important;
    text-align: center !important;
}
.td-checkbox {
    padding: 0 4px 0 14px !important;
    text-align: center !important;
    vertical-align: middle !important;
}

/* ── Selected row highlight ──────────────────────────────────
   S-LUX-2: brand-derived tint (via color-mix so the white-label brand
   override retints it — no hardcoded orange) + a 2px brand rail on the
   first cell, mirroring the sidebar active-item rail. */
tr.ff-row-selected > td {
    background: color-mix(in srgb, var(--color-primary) 8%, transparent) !important;
}
[data-theme="light"] tr.ff-row-selected > td {
    background: color-mix(in srgb, var(--color-primary) 7%, transparent) !important;
}
tr.ff-row-selected > td:first-child {
    box-shadow: inset 2px 0 0 var(--color-primary);
}

/* ── Floating bulk-action bar ────────────────────────────────
   Frosted-glass pill that slides in above the table when any
   rows are checked. Spring entrance, fade exit. */
.ff-bulk-bar {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 6px 8px 6px 14px;
    background: rgba(20, 22, 30, 0.9);
    backdrop-filter: blur(20px) saturate(160%);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
    border: 1px solid rgba(255, 255, 255, 0.09);
    border-radius: 12px;
    box-shadow: 0 4px 28px rgba(0, 0, 0, 0.4), 0 1px 0 rgba(255,255,255,0.04) inset;
    margin-bottom: 10px;
}
[data-theme="light"] .ff-bulk-bar {
    background: rgba(255, 255, 255, 0.94);
    border-color: rgba(0, 0, 0, 0.07);
    box-shadow: 0 4px 24px rgba(0,0,0,0.09), 0 1px 0 rgba(0,0,0,0.03);
}

.ff-bulk-bar-count {
    font-size: 13px;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.82);
    letter-spacing: -0.01em;
    padding-right: 4px;
    white-space: nowrap;
}
[data-theme="light"] .ff-bulk-bar-count { color: rgba(0,0,0,0.78); }

.ff-bulk-bar-sep {
    width: 1px;
    height: 16px;
    background: rgba(255, 255, 255, 0.1);
    flex-shrink: 0;
    margin: 0 4px;
}
[data-theme="light"] .ff-bulk-bar-sep { background: rgba(0,0,0,0.1); }

/* Shared action button base */
.ff-bulk-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 5px 11px;
    border-radius: 7px;
    font-size: 13px;
    font-weight: 500;
    border: none;
    cursor: pointer;
    transition: background 120ms ease, color 120ms ease;
    letter-spacing: -0.01em;
    line-height: 1;
    background: transparent;
}
.ff-bulk-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Delete action */
.ff-bulk-btn-delete {
    background: rgba(239, 68, 68, 0.14);
    color: #f87171;
}
.ff-bulk-btn-delete:hover:not(:disabled) {
    background: rgba(239, 68, 68, 0.24);
    color: #fca5a5;
}
[data-theme="light"] .ff-bulk-btn-delete { background: rgba(220,38,38,0.1); color: #dc2626; }
[data-theme="light"] .ff-bulk-btn-delete:hover:not(:disabled) { background: rgba(220,38,38,0.18); }

/* Clear (×) button — icon-only, pushed to far right */
.ff-bulk-btn-clear {
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: rgba(255,255,255,0.38);
    margin-left: auto;
}
.ff-bulk-btn-clear:hover { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.7); }
[data-theme="light"] .ff-bulk-btn-clear { color: rgba(0,0,0,0.35); }
[data-theme="light"] .ff-bulk-btn-clear:hover { background: rgba(0,0,0,0.06); color: rgba(0,0,0,0.7); }

/* Alpine enter/leave transitions — spring in, fade out */
.ff-bulk-enter { transition: opacity 220ms ease, transform 220ms cubic-bezier(0.34, 1.56, 0.64, 1); }
.ff-bulk-enter-from { opacity: 0; transform: translateY(-10px) scale(0.96); }
.ff-bulk-enter-to   { opacity: 1; transform: translateY(0)    scale(1); }
.ff-bulk-leave { transition: opacity 140ms ease, transform 140ms ease; }
.ff-bulk-leave-from { opacity: 1; transform: translateY(0)    scale(1); }
.ff-bulk-leave-to   { opacity: 0; transform: translateY(-6px) scale(0.97); }
.table-pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 16px;
    border-top: 1px solid var(--border-color);
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

/* ── Breadcrumb ─────────────────────────────────────────────────
   Generic breadcrumb item — used across several detail pages. */
.breadcrumb {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8125rem;
    color: var(--text-tertiary);
    margin-bottom: 12px;
}
.breadcrumb-item {
    color: var(--text-tertiary);
    text-decoration: none;
}
.breadcrumb-item:hover {
    color: var(--text-secondary);
}
.breadcrumb-item + .breadcrumb-item::before {
    content: '/';
    margin: 0 6px;
    color: var(--text-tertiary);
}
.breadcrumb-item.is-active {
    color: var(--text-primary);
    font-weight: 500;
}

/* ── Toolbar row (generic flex row with left/right groups) ─────
   Used by filter bars on list pages that didn't adopt the
   .table-toolbar naming. Alias .toolbar → flexible spacer layout. */
.toolbar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 16px;
}
.toolbar-left {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 auto;
    min-width: 0;
}
.toolbar-right {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
}

/* Filter-bar BEM variants (2 files used these modifier names). */
.filter-bar__left  { display: flex; align-items: center; gap: 8px; }
.filter-bar__right { display: flex; align-items: center; gap: 8px; margin-left: auto; }

/* ── Sort link (clickable table header) ────────────────────────── */
.sort-link {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--text-secondary);
    text-decoration: none;
    font-weight: 600;
    cursor: pointer;
}
.sort-link:hover { color: var(--text-primary); }
.sort-link.is-active { color: var(--text-primary); }

/* ── Tab button (non-FF flavoured tab trigger) ─────────────────
   Alias for the .tab-btn pattern already used elsewhere so a single
   legacy template keeps working. */
.tab-button {
    background: transparent;
    border: 0;
    padding: 8px 14px;
    font-size: 0.875rem;
    color: var(--text-secondary);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    transition: color var(--transition-fast), border-color var(--transition-fast);
}
.tab-button:hover { color: var(--text-primary); }
.tab-button.is-active {
    color: var(--color-primary);
    border-bottom-color: var(--color-primary);
    font-weight: 600;
}
.tab-count {
    display: inline-block;
    margin-left: 4px;
    padding: 1px 6px;
    font-size: 0.6875rem;
    background: var(--bg-surface-2);
    color: var(--text-tertiary);
    border-radius: 999px;
}

/* ── Spinner (text-mode loading indicator) ─────────────────────
   Used in 11 files. Small inline spinning circle. */
.spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-color);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: ff-picker-spin 0.8s linear infinite;
    vertical-align: middle;
}
.spinner-sm { width: 12px; height: 12px; border-width: 2px; }
.spinner-lg { width: 28px; height: 28px; border-width: 3px; }

/* ── Skeleton loading cell ─────────────────────────────────────
   For placeholder rows while data is loading. */
.skeleton-cell {
    display: inline-block;
    width: 100%;
    height: 14px;
    /* S-LUX-2: border-strong highlight so the sweep is visible in light mode. */
    background: linear-gradient(90deg,
        var(--bg-surface-2) 0%,
        var(--border-color-strong) 50%,
        var(--bg-surface-2) 100%);
    background-size: 200% 100%;
    border-radius: 4px;
    animation: ff-skeleton-shimmer 1.2s ease-in-out infinite;
}
@keyframes ff-skeleton-shimmer {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* ── Notif list card (inbox-style list row) ─────────────────── */
.notif-list-card {
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md, 8px);
    padding: 0;
}

/* ── Detail grid / field-value pair ────────────────────────────
   Used in a few show pages that render "Label: Value" rows without
   going through the <dl> pattern. */
.detail-grid {
    display: grid;
    grid-template-columns: minmax(140px, 220px) 1fr;
    gap: 8px 16px;
    font-size: 0.875rem;
}
.field-value {
    color: var(--text-primary);
    font-weight: 500;
}
.spec-label {
    color: var(--text-secondary);
    font-weight: 500;
}
.spec-value {
    color: var(--text-primary);
    font-weight: 500;
}

/* ── Text helpers that were missing ──────────────────────────── */
.text-info  { color: var(--color-info-text);  }
.text-link  { color: var(--color-primary-text); text-decoration: none; }
.text-link:hover { text-decoration: underline; }

/* Toast error alias (some templates still use .toast-error — the
   primary variant we ship is .toast-danger). */
.toast-error { /* alias */ }
.toast-error .toast-icon { color: var(--color-danger-text); }

/* ── Page title helper ─────────────────────────────────────── */
.page-title {
    font-size: 1.5rem;   /* S-LUX-LOUD: bigger */
    font-weight: 700;
    letter-spacing: var(--tracking-tight);
    color: var(--text-primary);
    margin: 0 0 8px;
}

/* ── Photo thumbnail square ─────────────────────────────────── */
.photo-thumb {
    width: 80px;
    height: 80px;
    object-fit: cover;
    border-radius: var(--radius-md, 8px);
    border: 1px solid var(--border-color);
    background: var(--bg-surface-2);
}

/* ── Generic stepper step wrapper (non-FF flavour) ─────────── */
.step {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

/* ── Activity-log section header helper ─────────────────────── */
.activity-log-section {
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px solid var(--border-color);
}

/* ── Inline role form (settings/users inline role dropdown) ─── */
.inline-role-form {
    display: inline-block;
}
.inline-role-form select {
    min-width: 140px;
}

/* ── Email compose chips / rate explanation card ──────────── */
.rate-explanation-body {
    padding: 12px 16px;
    background: var(--bg-surface-2);
    border-radius: var(--radius-md, 8px);
    font-size: 0.8125rem;
    color: var(--text-secondary);
    line-height: 1.5;
}

/* ────────── UI-AUDIT-1 END ────────── */


/* ============================================================
   S-LEASE-UNITS — Apple-style segmented control tokens
   ────────────────────────────────────────────────────────────
   Convention: FleetForge defaults to dark mode at :root, with
   [data-theme="light"] overriding for light mode (opposite of
   the Apple spec literal text, but matches FleetForge's existing
   token system). Active pill is a grayscale "elevated" surface
   (white pill on light track / mid-gray pill on dark track) —
   no brand orange (D-D).
   ============================================================ */

:root {
    /* S-LUX-2.5: warm-neutral track (was cool rgba 118,118,128) to match the
       Atelier input-pill alphas; active pill stays grayscale-elevated (D-D,
       no brand orange) with the shadow re-tuned to the --shadow-sm language. */
    --segment-track-bg:        rgba(140, 130, 115, 0.20);
    --segment-active-bg:        #6B6660;
    --segment-active-shadow:    0 1px 4px rgba(0, 0, 0, 0.40),
                                0 1px 2px rgba(0, 0, 0, 0.25);
    --segment-active-text:      rgba(255, 255, 255, 1);
    --segment-inactive-text:    rgba(235, 230, 220, 0.62);
}

[data-theme="light"] {
    --segment-track-bg:        rgba(140, 130, 115, 0.14);
    --segment-active-bg:        #FFFFFF;
    --segment-active-shadow:    0 1px 3px rgba(0, 0, 0, 0.10),
                                0 1px 2px rgba(0, 0, 0, 0.05);
    --segment-active-text:      rgba(26, 24, 21, 1);
    --segment-inactive-text:    rgba(60, 60, 67, 0.62);
}

/* The control itself */
.ff-segment-control {
    position: relative;
    display: inline-flex;
    width: 320px;
    height: 44px;
    background: var(--segment-track-bg);
    border-radius: 9px;
    padding: 2px;
    user-select: none;
}

.ff-segment-control__pill {
    position: absolute;
    top: 2px;
    left: 2px;
    width: calc(50% - 2px);
    height: calc(100% - 4px);
    background: var(--segment-active-bg);
    border-radius: 7px;
    box-shadow: var(--segment-active-shadow);
    transition: transform 0.25s cubic-bezier(0.4, 0.0, 0.2, 1);
    z-index: 0;
    pointer-events: none;
}

.ff-segment-control__pill--right {
    transform: translateX(100%);
}

/* S-LEASE-MILEAGE-MODE: 3-position variant (Manual / Off / Samsara).
   The pill is one-third wide and slides one or two pill-widths right.
   Distinct modifiers (--mid/--end) so the 2-position --right rule is untouched. */
.ff-segment-control--3 .ff-segment-control__pill {
    width: calc(33.3333% - 2px);
}
.ff-segment-control__pill--mid {
    transform: translateX(100%);
}
.ff-segment-control__pill--end {
    transform: translateX(200%);
}

.ff-segment-control__option {
    position: relative;
    z-index: 1;
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: -0.01em;
    cursor: pointer;
    transition: color 0.15s ease;
    color: var(--segment-inactive-text);
    border-radius: 7px;
    outline: none;
}

.ff-segment-control__option--active {
    color: var(--segment-active-text);
}

.ff-segment-control__option:focus-visible {
    box-shadow: 0 0 0 2px var(--color-primary);
}

/* Secondary unit fields render slightly muted; full opacity on hover/focus */
.ff-field-secondary {
    opacity: 0.85;
    transition: opacity 0.15s ease;
}

.ff-field-secondary:hover,
.ff-field-secondary:focus-within {
    opacity: 1;
}

/* Lightweight collapsible (used for "Conversion factor" panel).
   x-show toggles display; we avoid the @alpinejs/collapse plugin. */
.ff-collapsible-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    color: var(--text-secondary);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    padding: 4px 0;
}

.ff-collapsible-toggle:hover {
    color: var(--text-primary);
}

.ff-collapsible-chevron {
    display: inline-block;
    font-size: 0.75rem;
    transition: transform 0.15s ease;
}

.ff-collapsible-chevron--open {
    transform: rotate(90deg);
}

.ff-collapsible-content {
    margin-top: 12px;
    padding: 16px;
    background: var(--bg-muted);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md, 8px);
}

.ff-link-button {
    background: none;
    border: none;
    color: var(--color-primary-text);
    font-size: 0.8125rem;
    cursor: pointer;
    padding: 0;
}

.ff-link-button:hover {
    text-decoration: underline;
}

/* Dual-unit "Per-unit rate" / "Allowance" wrapper labels */
.ff-dual-label {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--text-primary);
    margin-bottom: 8px;
    letter-spacing: -0.005em;
}

.ff-dual-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    margin-bottom: 24px;
}

@media (max-width: 540px) {
    .ff-segment-control { width: 100%; max-width: 320px; }
    .ff-dual-grid { grid-template-columns: 1fr; gap: 12px; }
}

/* show.php read-only variant — primary prominent, secondary muted */
.ff-show-primary {
    font-family: var(--font-mono);
    font-size: 1.125rem;
    font-weight: 400; /* S-LEASE-RATES-REDESIGN: emphasis from size + colour, not bold */
    color: var(--text-primary);
    letter-spacing: -0.01em;
}

.ff-show-secondary {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--text-muted);
    margin-top: 2px;
}

.ff-show-caption {
    font-size: 0.75rem;
    color: var(--text-muted);
    font-family: var(--font-mono);
    margin-top: 12px;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.ff-badge-custom {
    display: inline-block;
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--color-warning-text, #b45309);
    background: var(--color-warning-light, #fef3c7);
    padding: 2px 8px;
    border-radius: 4px;
    letter-spacing: 0.02em;
}
/* ────────── S-LEASE-UNITS END ────────── */

/* ────────── S-LEASE-RATES-REDESIGN ──────────
   Compact, multi-per-row Rates card (replaces the one-field-per-row table).
   Each field is a subtle tile; the VALUE is normal-weight mono in the primary
   text colour — emphasis comes from the tile surface + colour contrast against
   the muted uppercase label, NOT from bold. Auto-fit grids stack cleanly on
   narrow / mobile widths. */
.ff-rates-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 12px;
}
.ff-rate-cell {
    min-width: 0;
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-radius: 8px;
    padding: 10px 12px;
}
.ff-rate-label {
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
    margin-bottom: 5px;
}
.ff-rate-value {
    font-family: var(--font-mono);
    font-size: 0.9375rem;
    font-weight: 400;
    color: var(--text-primary);
    letter-spacing: -0.01em;
}
.ff-rate-value--sm { font-size: 0.8125rem; }
/* Responsive label/value pairs inside the mileage & precharge blocks
   (was a hard-coded 2-column grid; auto-fit stacks them on narrow widths). */
.ff-rate-pair {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 16px 24px;
    max-width: 520px;
}
.ff-rate-block {
    margin-top: 20px;
    padding-top: 18px;
    border-top: 1px solid var(--border-color);
}
/* Total-distance-driven callout (closed leases) — a left accent + tile gives it
   presence without bold text. */
.ff-rate-driven {
    margin-top: 18px;
    padding: 14px 16px;
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-left: 3px solid var(--color-primary);
    border-radius: 8px;
    display: flex;
    align-items: baseline;
    column-gap: 20px;
    row-gap: 4px;
    flex-wrap: wrap;
}
.ff-rate-driven .ff-show-primary { font-size: 1.375rem; }
.ff-rate-notes {
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--border-color);
    font-size: 0.875rem;
    color: var(--text-secondary);
}
@media (max-width: 600px) {
    /* Phones: 106px min keeps 2 tiles per row even on a ~230px-wide card column;
       long values (e.g. "Incl · $1.00/day") wrap inside the tile. */
    .ff-rates-grid { grid-template-columns: repeat(auto-fill, minmax(106px, 1fr)); gap: 8px; }
    .ff-rate-cell { padding: 8px 10px; }
    .ff-rate-value, .ff-rate-value--sm { font-size: 0.8125rem; }
    .ff-rate-pair { grid-template-columns: 1fr 1fr; gap: 12px 16px; }
    .ff-rate-driven .ff-show-primary { font-size: 1.25rem; }
    /* let the inline-nowrap conversion/precharge captions wrap instead of
       overflowing the card on narrow widths (overrides the inline white-space) */
    .ff-rate-block .ff-show-caption,
    .ff-rate-block .ff-show-caption span { white-space: normal !important; }
}
/* ────────── S-LEASE-RATES-REDESIGN END ────────── */

/* ────────── S-PERM-USERS-ACCESS-WALL ──────────
   Custom developer-only access wall, rendered inside the normal
   admin shell (sidebar + topbar) for the Users module when the
   current viewer is not super_admin. See app/admin/users/*.php
   gate blocks. */
.access-wall {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 80px 24px;
    max-width: 480px;
    margin: 0 auto;
}
.access-wall-icon {
    width: 80px;
    height: 80px;
    border-radius: 50%;
    background: var(--bg-surface-2, #1e293b);
    border: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 24px;
    color: var(--text-muted);
}
.access-wall-title {
    font-size: 1.25rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 12px;
}
.access-wall-message {
    font-size: 0.9375rem;
    color: var(--text-muted);
    line-height: 1.75;
    margin: 0 0 28px;
}
/* ────────── S-PERM-USERS-ACCESS-WALL END ────────── */

/* ════════════════════════════════════════════════════════════════════
   S-DASHBOARD-MOBILE-LAYOUT
   1. KPI tiles: 3-up tablet / 2-up mobile (dashboard only — scoped via
      #kpi-grid so other .stat-grid usages keep their auto-fit cadence).
   2. Active Leases card: horizontal scroll + height cap + only first
      4 rows visible on mobile so it doesn't dominate the page.
   ════════════════════════════════════════════════════════════════════ */

/* ── FIX 1: KPI tile grid at tablet (768-1023px) ── */
@media (max-width: 1023px) {
    .stat-grid#kpi-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 10px;
    }
}

/* ── FIX 1: KPI tile grid at mobile (≤767px) — and chrome tweaks
        to fit content cleanly in the 2-up footprint. Class names
        adapted from the spec to match the codebase (.stat-card not
        .kpi-card, .stat-value not .kpi-value, .stat-card__icon not
        .kpi-icon-wrap). ── */
@media (max-width: 767px) {
    .stat-grid#kpi-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 10px;
    }
    #kpi-grid .stat-card {
        padding: 14px;
    }
    #kpi-grid .stat-value {
        font-size: 1.125rem;
    }
    #kpi-grid .stat-label {
        font-size: 0.6875rem;
        /* S-LUX-2-FIX (no-crop): icon is in the flex header (in-flow), so no
           right reservation is needed — full width prevents label clipping at
           mobile too (this id rule outranks .stat-card__header .stat-label).
           Tighter tracking here so a long single word like "RESERVATIONS"
           fits the ~87px label box next to the icon in the 2-up footprint
           (the 0.08em micro-label tracking shaved its last letter). */
        padding-right: 0;
        letter-spacing: 0.02em;
        margin-bottom: 4px;
    }
    #kpi-grid .stat-card__icon {
        width: 30px;
        height: 30px;
    }
    #kpi-grid .stat-card__icon svg {
        width: 15px;
        height: 15px;
    }
    #kpi-grid .stat-delta {
        font-size: 0.6875rem;
    }
}

/* FIX 2 (mobile table scroll for .dashboard-leases-card) removed —
   S-DASHBOARD-MOBILE-CAROUSEL replaced the Active Leases table with a
   carousel, so the .dashboard-leases-card class is no longer present
   in the dashboard markup and these rules had no targets. */
/* ════════════════════════════════════════════════════════════════════
   S-DASHBOARD-MOBILE-LAYOUT END (KPI grid responsive rules above
   remain in force; only the leases-table-scroll block was dropped.)
   ════════════════════════════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════════════
   S-DASHBOARD-MOBILE-CAROUSEL
   Horizontal scroll carousel for Active Leases + Pending Activations.
   Each lease/contract becomes one .carousel-card with labelled rows.
   ════════════════════════════════════════════════════════════════════ */

/* ── Dashboard section header (lighter than .card.card-header) ── */
.dashboard-section {
    margin-bottom: 44px;
    /* WHY min-width:0: Pending Activations lives inside the
       .dashboard-grid--widgets 2-column grid on desktop. Without this,
       the carousel's wide flex children would push the grid cell wider
       than its 1fr allowance, defeating overflow-x:auto. */
    min-width: 0;
}
/* Section header: 2px left accent matches the card's 2px top accent —
   same visual language, horizontal on card / vertical on heading. */
.dashboard-section-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: 18px;
    padding: 0 0 14px 14px;
    gap: 16px;
    border-left: 2px solid var(--color-primary);
    border-bottom: 1px solid var(--border-color);
}
/* Title: uppercase, readable size, secondary color */
.dashboard-section-title {
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-secondary);
    margin: 0;
    flex: 1;
}
.dashboard-section-viewall {
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: color 150ms ease;
}
.dashboard-section-viewall:hover {
    color: var(--color-primary);
    text-decoration: none;
}

/* ── Carousel container ───────────────────────────────────────── */
.dashboard-carousel {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    /* WHY overflow-y:visible not hidden: the micro-tilt perspective transform
       on hover can push card edges fractionally outside the container bounds;
       visible lets them breathe without clipping. */
    overflow-y: visible;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;      /* Firefox */
    /* S-LUX-LOUD: no frosted track panel — the cards sit directly on the page
       background (operator ask). Scroll still works; the half-card "peek" on
       the right still signals swipe-ability. */
    background: transparent;
    border: none;
    box-shadow: none;
    border-radius: 0;
    padding: 6px 0;
    /* Right padding ≈ half a card — next card always peeking through */
    padding-right: calc(var(--carousel-card-w, 300px) / 2);
    cursor: grab;
}
.dashboard-carousel:active { cursor: grabbing; }
.dashboard-carousel::-webkit-scrollbar {
    display: none;              /* Chrome / Safari */
}

/* ══ CAROUSEL CARDS — Maison-level redesign ══════════════════════════
   Zero shadows. Zero sheens. Zero glows. Typography and white-space
   carry all the weight — Apple / Louis Vuitton / Chanel restraint.
   The only ornament: a 2px brand-color top border, like a silk ribbon.
   ═══════════════════════════════════════════════════════════════════ */
.carousel-card {
    flex: 0 0 300px;
    scroll-snap-align: start;
    background: var(--bg-surface);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 22px 22px 18px;
    display: flex;
    flex-direction: column;
    gap: 0;
    min-height: 270px;
    position: relative;
    transition:
        transform    200ms cubic-bezier(0.23, 1, 0.32, 1),
        border-color 200ms ease;
}
/* No ::before, no ::after — not a single pixel of shadow or glow */
.carousel-card--link {
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}
/* Hover: border highlights in theme color. JS micro-tilt (rotateX/rotateY
   ±5°) provides physical depth. No translateY, no scale, no movement. */
.carousel-card--link:hover {
    border-color: var(--color-primary);
    text-decoration: none;
}
.carousel-card--link:active {
    opacity: 0.85;
    transition-duration: 60ms;
}

/* ── Top row: ref code + status indicator ────────────────────── */
.cc-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 22px;
    gap: 10px;
}
/* Monospace ref — catalog / serial-number aesthetic */
.cc-id {
    font-family: var(--font-mono);
    font-size: 0.8125rem;              /* S-LUX-LOUD: 11px→13px, readable */
    color: var(--text-secondary);      /* was --text-muted (too faint) */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
    flex: 1;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
/* Status indicator: dot + uppercase label — no pill background.
   A 5px color dot is enough; a filled badge is visual noise. */
.cc-pill {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    white-space: nowrap;
}
.cc-pill::before {
    content: '';
    display: block;
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}
.cc-pill--active  { color: #4ade80; }
.cc-pill--danger  { color: #f87171; }
.cc-pill--warning { color: #fbbf24; }
.cc-pill--info    { color: #7dd3fc; }

/* ── Customer — dominant editorial text ──────────────────────── */
.cc-customer {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--text-primary);
    line-height: 1.3;
    margin-bottom: 4px;
    /* Allow up to 2 lines, then ellipsis */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* Remove white-space:nowrap — let it wrap */
    word-break: break-word;
}

/* ── Equipment — catalog reference line ──────────────────────── */
.cc-equipment {
    font-size: 0.8125rem;              /* S-LUX-LOUD: 11px→13px */
    color: var(--text-secondary);      /* was --text-muted — much more legible */
    margin-bottom: 24px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    letter-spacing: 0.02em;            /* was 0.08em — wide tracking hurt small caps */
    text-transform: uppercase;
}
.cc-dot { margin: 0 6px; opacity: 0.3; }

/* ── Divider — hairline rule ─────────────────────────────────── */
.cc-divider { height: 1px; background: var(--border-color); margin-bottom: 22px; }

/* ── Rate — the price-tag moment ─────────────────────────────── */
.cc-rate {
    flex: 1;
    display: flex;
    align-items: baseline;
    margin-bottom: 22px;
}
/* Light weight at large size = Louis Vuitton price-tag aesthetics */
.cc-rate-amount {
    font-size: 2.625rem;
    font-weight: 300;
    color: var(--text-primary);
    letter-spacing: -0.04em;
    line-height: 1;
}
/* Period unit: all-caps label like a spec sheet */
.cc-rate-period {
    font-size: 0.75rem;
    color: var(--text-secondary);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-left: 7px;
    align-self: flex-end;
    margin-bottom: 5px;
}

/* ── Footer ──────────────────────────────────────────────────── */
.cc-footer {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: 12px;
    margin-top: auto;
    padding-top: 18px;
    border-top: 1px solid var(--border-color);
}
.cc-footer-item { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cc-footer-item--right { text-align: right; align-items: flex-end; }
.cc-footer-label {
    font-size: 0.6875rem;              /* S-LUX-LOUD: 10px→11px */
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-tertiary);
}
.cc-footer-value {
    font-size: 0.9375rem;              /* S-LUX-LOUD: 14px→15px */
    font-weight: 500;
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    letter-spacing: -0.01em;
}

/* ── Empty / loading / error state ────────────────────────────── */
.carousel-empty {
    font-size: 0.875rem;
    color: var(--text-muted);
    padding: 24px 4px;
}

/* ── Responsive card widths + peek variable ─────────────────── */
@media (max-width: 767px) {
    .dashboard-carousel {
        --carousel-card-w: 78vw;
        scroll-padding-left: 14px;
    }
    .carousel-card {
        flex: 0 0 78vw;
        max-width: 300px;
        min-width: 220px;
    }

    /* ── Mobile type scale: bump all sub-heading text for thumb readability ── */
    .cc-id,
    .cc-equipment          { font-size: 0.8125rem; }
    .cc-pill               { font-size: 0.75rem; }
    .cc-rate-period        { font-size: 0.75rem; }
    .cc-footer-label       { font-size: 0.75rem; }
    .cc-footer-value       { font-size: 0.9375rem; }
}
@media (min-width: 768px) {
    .dashboard-carousel { --carousel-card-w: 340px; }
    .carousel-card { flex: 0 0 340px; }
}
@media (min-width: 1280px) {
    .dashboard-carousel { --carousel-card-w: 360px; }
    .carousel-card { flex: 0 0 360px; }
}
/* Light theme: flip to a darker tint so the track is a distinct sunken lane
   (white body → slightly gray track → white cards pop off it). */
[data-theme="light"] .dashboard-carousel {
    /* S-LUX-LOUD: track panel removed in both themes — cards on the page bg. */
    background: transparent;
    border-color: transparent;
    box-shadow: none;
}
/* ════════════════════════════════════════════════════════════════════
   S-DASHBOARD-MOBILE-CAROUSEL END
   ════════════════════════════════════════════════════════════════════ */

/* ── Notification dropdown — Flat / Grouped toggle ────────────────── */
.notif-view-toggle {
    display: flex;
    border: 1px solid var(--border-color, #2e2e2e);
    border-radius: 5px;
    overflow: hidden;
    flex-shrink: 0;
}
.notif-view-toggle button {
    background: none;
    border: none;
    color: var(--text-secondary, #9c9c96);
    font-size: 11px;
    padding: 3px 9px;
    cursor: pointer;
    line-height: 1.4;
    border-right: 1px solid var(--border-color, #2e2e2e);
}
.notif-view-toggle button:last-child { border-right: none; }
.notif-view-toggle button:hover { background: var(--bg-muted, #242424); color: var(--text-primary, #e8e8e4); }
.notif-view-toggle button.notif-view-toggle--active {
    background: var(--color-accent, #3b82f6);
    color: var(--color-on-primary);
}

/* ── Grouped view: category section ──────────────────────────────── */
.notif-group-block { border-bottom: 1px solid var(--border-color, #2e2e2e); }
.notif-group-block:last-child { border-bottom: none; }

.notif-group-row {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 9px 14px;
    cursor: pointer;
    background: var(--bg-surface-2, #161616);
    user-select: none;
}
.notif-group-row:hover { background: var(--bg-muted, #242424); }

.notif-icon--sm {
    width: 26px;
    height: 26px;
    border-radius: 6px;
    flex-shrink: 0;
}
.notif-icon--sm svg { width: 14px; height: 14px; }

.notif-group-row-label {
    flex: 1;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-primary, #e8e8e4);
}
.notif-group-row-badge {
    font-size: 10px;
    font-weight: 600;
    background: var(--color-warning, #f59e0b);
    color: #000;
    border-radius: 10px;
    padding: 1px 6px;
    flex-shrink: 0;
}
.notif-group-row-chevron {
    width: 14px;
    height: 14px;
    color: var(--text-muted, #6b6b66);
    transition: transform 0.15s ease;
    flex-shrink: 0;
}
.notif-group-row-chevron.is-collapsed { transform: rotate(-90deg); }

/* Items inside a group — no icon (category already shown in header) */
.notif-item--indented {
    padding-left: 20px;
    background: var(--bg-card, #1a1a1a);
}
.notif-item--indented:hover { background: var(--bg-muted, #242424); }

/* ============================================================
   HELP SYSTEM — In-app Help Center + Drawer
   S-HELP-SYSTEM-FOUNDATION / S-HELP-DRAWER-TUTORIAL-REWORK /
   S-HELP-DRAWER-PUSH-AND-PERSIST + styling rework
   ============================================================ */


/* ── Help Center index page ─────────────────────────────────── */
.help-guide-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 14px;
}
.help-guide-card {
    border: 1px solid var(--border-color, rgba(255,255,255,0.08));
    border-radius: var(--radius-md, 8px);
    transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.help-guide-card:hover {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 1px var(--color-primary), 0 4px 16px rgba(0,0,0,0.2);
    transform: translateY(-1px);
}
.help-guide-title {
    font-weight: 700;
    font-size: 0.9375rem;
    color: var(--text-primary);
    margin-bottom: 6px;
}
.help-guide-desc {
    font-size: 0.8125rem;
    line-height: 1.5;
    margin: 0 0 10px 0;
}
.help-guide-link {
    font-size: 0.8125rem;
    color: var(--color-primary);
    font-weight: 600;
}

/* ── Help button in page-header-actions ─────────────────────── */
.help-btn {
    color: var(--text-secondary);
    border-color: transparent;
}
.help-btn:hover {
    color: var(--color-primary);
    border-color: var(--color-primary);
    background: var(--color-primary-light, rgba(249,115,22,0.08));
}

/* ── Rendered guide content — full-page and drawer ──────────── */
.help-content {
    line-height: 1.72;
    font-size: 0.9375rem;
    max-width: 820px;
}

/* First paragraph (subtitle line) */
.help-content > p:first-child {
    font-size: 1rem;
    color: var(--text-secondary);
    margin-bottom: 1.25rem;
}

/* Section headings — left accent bar */
.help-content h2 {
    font-size: 1rem;
    font-weight: 700;
    margin: 2rem 0 0.75rem 0;
    padding: 7px 0 7px 12px;
    border-left: 3px solid var(--color-primary);
    background: none;              /* removed — primary-light varies by brand and wrecked contrast */
    color: var(--text-primary);
}
.help-content h2:first-of-type { margin-top: 0.5rem; }

.help-content h3 {
    font-size: 0.9375rem;
    font-weight: 700;
    margin: 1.5rem 0 0.5rem 0;
    color: var(--text-primary);
}
.help-content h4 {
    font-size: 0.8125rem;
    font-weight: 700;
    margin: 1.2rem 0 0.35rem 0;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.help-content p {
    margin: 0 0 0.85rem 0;
    color: var(--text-secondary);
}

/* Numbered steps — custom circles */
.help-content ol {
    list-style: none;
    counter-reset: help-step;
    padding-left: 0;
    margin: 0 0 1rem 0;
}
.help-content ol li {
    counter-increment: help-step;
    padding-left: 34px;
    position: relative;
    margin-bottom: 8px;
    line-height: 1.6;
    color: var(--text-secondary);
}
.help-content ol li::before {
    content: counter(help-step);
    position: absolute;
    left: 0;
    top: 2px;
    width: 20px;
    height: 20px;
    background: var(--color-primary);
    color: var(--color-on-primary);
    border-radius: 50%;
    font-size: 0.6875rem;
    font-weight: 700;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    flex-shrink: 0;
}

/* Bullet lists — dash style */
.help-content ul {
    padding-left: 0;
    margin: 0 0 0.85rem 0;
    list-style: none;
    color: var(--text-secondary);
}
.help-content ul li {
    padding-left: 18px;
    position: relative;
    margin-bottom: 6px;
    line-height: 1.6;
}
.help-content ul li::before {
    content: '\2013';
    position: absolute;
    left: 0;
    color: var(--color-primary);
    font-weight: 700;
}

/* Nested lists */
.help-content li > ul,
.help-content li > ol { margin-top: 6px; margin-bottom: 0; }

/* Bold / UI labels — subtle highlight pill */
.help-content strong {
    color: var(--text-primary);
    font-weight: 700;
    background: none;              /* removed — primary-light pill was unreadable across brand colors */
    padding: 0;
    margin: 0;
}
.help-content em { font-style: italic; }

/* Inline code */
.help-content code {
    background: var(--bg-surface-2, rgba(255,255,255,0.07));
    border: 1px solid var(--border-color, rgba(255,255,255,0.08));
    border-radius: 4px;
    padding: 1px 6px;
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    color: var(--color-primary-text, var(--color-primary));
}

/* Code blocks */
.help-content pre {
    background: var(--bg-surface-2);
    border-radius: 6px;
    padding: 14px 16px;
    overflow-x: auto;
    margin: 0 0 1rem 0;
    border: 1px solid var(--border-color, rgba(255,255,255,0.06));
}
.help-content pre code {
    background: none;
    border: none;
    padding: 0;
    font-size: 0.8125rem;
    color: var(--text-primary);
}

/* Links */
.help-content a {
    color: var(--color-primary);
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 500;
}
.help-content a:hover { opacity: 0.8; }

/* Horizontal rules — section dividers */
.help-content hr {
    border: none;
    border-top: 1px solid var(--border-color, rgba(255,255,255,0.08));
    margin: 1.5rem 0;
}

/* Blockquotes — tip / warning callouts */
.help-content blockquote {
    margin: 0.75rem 0 1rem 0;
    padding: 10px 14px;
    background: rgba(249,115,22,0.08);
    border-left: 3px solid var(--color-primary);
    border-radius: 0 6px 6px 0;
    color: var(--text-primary);       /* was text-secondary — too low contrast */
    font-size: 0.875rem;
    line-height: 1.6;
}
.help-content blockquote p { margin: 0; color: inherit; font-size: inherit; }
/* Bold labels inside callouts (e.g. "Tip:", "Note:") — orange, no pill background */
.help-content blockquote strong { background: none; padding: 0; margin: 0; color: var(--color-primary); font-weight: 700; }

/* Tables */
.help-content table {
    width: 100%;
    border-collapse: collapse;
    margin: 0 0 1rem 0;
    font-size: 0.8125rem;
    border-radius: 6px;
    overflow: hidden;
    border: 1px solid var(--border-color, rgba(255,255,255,0.08));
}
.help-content th {
    text-align: left;
    padding: 7px 10px;
    background: var(--bg-surface-2);
    color: var(--text-secondary);
    font-weight: 700;
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.help-content td {
    padding: 8px 10px;
    border-top: 1px solid var(--border-color, rgba(255,255,255,0.05));
    color: var(--text-secondary);
    vertical-align: top;
}
.help-content td strong { background: none; padding: 0; margin: 0; }

/* Details — Under the Hood card */
.help-content details {
    margin: 1.5rem 0 1rem 0;
    border: 1px solid var(--border-color, rgba(255,255,255,0.09));
    border-radius: 8px;
    overflow: hidden;
    background: var(--bg-surface-2);
}
.help-content summary {
    padding: 11px 16px;
    cursor: pointer;
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--text-secondary);
    background: transparent;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 8px;
    user-select: none;
    transition: color 0.15s;
}
.help-content summary:hover { color: var(--text-primary); }
.help-content summary::-webkit-details-marker { display: none; }
.help-content summary::before {
    content: '\25B6';
    font-size: 0.5625rem;
    transition: transform 200ms ease;
    color: var(--color-primary);
    flex-shrink: 0;
}
.help-content details[open] summary::before { transform: rotate(90deg); }
.help-content details[open] summary {
    border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.08));
    color: var(--text-primary);
}
.help-content details > *:not(summary) { padding: 12px 16px 4px 16px; }
/* Inside details: simpler bullet */
.help-content details ul li::before { content: '\00B7'; font-size: 1.2rem; top: -3px; }

/* ── Drawer panel ────────────────────────────────────────────── */
.help-drawer-backdrop {
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-modal) - 3);
    background: rgba(0, 0, 0, 0.5);
}

.help-drawer-panel {
    position: fixed;
    /* Sits below the topbar AND the sticky page-header. The page-header
       height is measured in JS and exposed as --ff-help-drawer-offset so
       the panel starts beneath the full-width header bar. */
    top: calc(var(--topbar-height) + var(--ff-help-drawer-offset, 0px) + 20px);
    right: 0;
    bottom: 0;
    z-index: calc(var(--z-topbar) - 2); /* 28 — below topbar (30) and page-header (29) */
    width: 500px;
    max-width: 100vw;
    background: var(--bg-surface);
    border-left: 1px solid var(--border-color, rgba(255,255,255,0.08));
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* Accent bar at the very top */
.help-drawer-panel::before {
    content: '';
    display: block;
    height: 3px;
    background: linear-gradient(90deg, var(--color-primary) 0%, var(--color-primary-hover, #c45f00) 100%);
    flex-shrink: 0;
}

.help-drawer-header {
    padding: 16px 20px 14px 20px;
    border-bottom: 1px solid var(--border-color, rgba(255,255,255,0.08));
    background: var(--bg-surface-2);
    flex-shrink: 0;
}

/* Top row: "HOW THIS WORKS" label + collapse button */
.help-drawer-header-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}

/* "How this works" eyebrow label */
.help-drawer-label {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--color-primary);
}

/* Module title — larger, prominent */
.help-drawer-title {
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--text-primary);
    letter-spacing: -0.02em;
    margin: 0;
    line-height: 1.3;
}

.help-drawer-close {
    flex-shrink: 0;
    opacity: 0.5;
    transition: opacity 0.15s;
    padding: 4px;
}
.help-drawer-close:hover { opacity: 1; }

.help-drawer-body {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 16px 20px 0 20px;
    scroll-behavior: smooth;
}
.help-drawer-body::-webkit-scrollbar { width: 4px; }
.help-drawer-body::-webkit-scrollbar-track { background: transparent; }
.help-drawer-body::-webkit-scrollbar-thumb {
    background: var(--border-color, rgba(255,255,255,0.12));
    border-radius: 2px;
}

/* Content inside drawer — tighter than full-page */
.help-drawer-content.help-content { font-size: 0.875rem; line-height: 1.68; }
.help-drawer-content.help-content > p:first-child { font-size: 0.875rem; margin-bottom: 1rem; }
.help-drawer-content.help-content h2 { font-size: 0.8125rem; padding: 6px 10px; margin: 1.4rem 0 0.6rem 0; }
.help-drawer-content.help-content h2:first-of-type { margin-top: 0; }
.help-drawer-content.help-content h3 { font-size: 0.8125rem; }
.help-drawer-content.help-content ol li { padding-left: 30px; margin-bottom: 6px; }
.help-drawer-content.help-content ol li::before { width: 18px; height: 18px; font-size: 0.625rem; top: 2px; }
.help-drawer-content.help-content blockquote { font-size: 0.8125rem; padding: 8px 12px; }
.help-drawer-content.help-content table { font-size: 0.75rem; }
.help-drawer-content.help-content th { padding: 5px 8px; }
.help-drawer-content.help-content td { padding: 6px 8px; }
.help-drawer-content.help-content details { margin: 1rem 0 0.75rem 0; }
.help-drawer-content.help-content summary { font-size: 0.75rem; padding: 9px 12px; }
.help-drawer-content { padding-bottom: 20px; }

.help-drawer-loading { padding: 4px 0; }

.help-drawer-error {
    padding: 12px 0;
    color: var(--text-secondary);
    font-size: 0.875rem;
}

.help-drawer-footer {
    flex-shrink: 0;
    padding: 10px 20px;
    border-top: 1px solid var(--border-color, rgba(255,255,255,0.08));
    background: var(--bg-surface-2);
}
.help-drawer-full-link {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    text-decoration: none;
    transition: color 0.15s;
}
.help-drawer-full-link:hover { color: var(--color-primary); }

/* ── Transitions ─────────────────────────────────────────────── */
.help-drawer-bd-enter  { transition: opacity 250ms ease; }
.help-drawer-bd-from   { opacity: 0; }
.help-drawer-bd-to     { opacity: 1; }
.help-drawer-bd-leave  { transition: opacity 180ms ease; }

.help-drawer-panel-enter { transition: transform 280ms cubic-bezier(0.4, 0, 0.2, 1); }
.help-drawer-panel-from  { transform: translateX(100%); }
.help-drawer-panel-to    { transform: translateX(0); }
.help-drawer-panel-leave { transition: transform 200ms cubic-bezier(0.4, 0, 1, 1); }

/* ── Desktop push (≥1024px) ─────────────────────────────────── */
@media (min-width: 1024px) {
    /* Push only .page-content and .app-footer — topbar is excluded so it stays full-width */
    body[data-help-drawer-open] .page-content,
    body[data-help-drawer-open] .app-footer {
        margin-right: 500px;
    }
    /* The sticky page-header spans the FULL width above the drawer (like the
       topbar) — counteract the page-content push so the title + action bar
       isn't squeezed. The drawer starts below it (see --ff-help-drawer-offset). */
    body[data-help-drawer-open] .page-header {
        margin-right: -500px;
    }
    body[data-help-drawer-open] .help-drawer-backdrop {
        display: none !important;
    }
    .help-drawer-panel {
        box-shadow: -2px 0 20px rgba(0, 0, 0, 0.2);
    }
}

/* ── Mobile: full-width overlay ─────────────────────────────── */
@media (max-width: 1023px) {
    /* Mobile is an OVERLAY (backdrop visible), so the panel MUST sit above the
       backdrop. The default panel z-index (calc(--z-topbar) - 2 = 28) is for the
       DESKTOP push layout where it stays below the topbar/page-header and the
       backdrop is display:none. On mobile that low value left the panel BENEATH
       its own backdrop (calc(--z-modal) - 3 = 57) — the panel rendered dimmed
       and every tap landed on the backdrop (closing the drawer) instead of the
       content. Lift the panel to one above the backdrop, still below real modals
       (60) and toasts (70). [S-FIX-HELP-DRAWER-MOBILE] */
    .help-drawer-panel {
        width: 100vw;
        border-left: none;
        z-index: calc(var(--z-modal) - 2); /* 58 — above backdrop (57) */
    }
}

/* ============================================================
   Permission UI — shared across permissions.php,
   role_permissions.php and the Users > Default Permissions tab.
   S-PERM-APPLE-REDESIGN / S-ROLE-PERM-OVERRIDE
   ============================================================ */

/* ── Page header ─────────────────────────────────────────── */
.perm-page-header { display:flex; align-items:center; gap:14px; margin-bottom:20px; }
.perm-back-btn { flex-shrink:0; display:inline-flex; align-items:center; gap:2px; }
.perm-user-identity { display:flex; align-items:center; gap:12px; flex:1; min-width:0; }
.perm-user-avatar {
    width:44px; height:44px; border-radius:50%;
    background:var(--color-primary-light); color:var(--color-primary-text);
    display:flex; align-items:center; justify-content:center;
    font-weight:700; font-size:1rem; flex-shrink:0; letter-spacing:.02em;
}
.perm-user-info { min-width:0; }
.perm-user-name { font-size:1.0625rem; font-weight:700; color:var(--text-primary); margin:0 0 4px; line-height:1.2; }
.perm-user-meta { display:flex; align-items:center; gap:8px; }

/* ── Shared Apple card sub-header ───────────────────────── */
.perm-apple-card-header {
    display:flex; align-items:center; gap:14px;
    padding:18px 20px 14px; border-bottom:1px solid var(--border-color);
}
.perm-apple-card-header--sm { padding:13px 16px 10px; gap:10px; }
.perm-apple-card-icon {
    width:38px; height:38px; border-radius:11px;
    display:flex; align-items:center; justify-content:center; flex-shrink:0;
}
.perm-apple-card-icon svg { width:20px; height:20px; }
.perm-apple-card-icon--sm { width:30px; height:30px; border-radius:9px; }
.perm-apple-card-icon--sm svg { width:16px; height:16px; }
.perm-apple-card-title { font-weight:600; font-size:0.9375rem; color:var(--text-primary); line-height:1.2; }
.perm-apple-card-title--sm { font-weight:600; font-size:0.875rem; color:var(--text-primary); flex:1; }
.perm-apple-card-subtitle { font-size:0.75rem; color:var(--text-secondary); margin-top:2px; line-height:1.4; }

/* ── Info banners ────────────────────────────────────────── */
.perm-info-banner {
    display:flex; align-items:flex-start; gap:10px;
    padding:12px 16px; border-radius:12px; font-size:0.875rem; margin-bottom:16px;
}
.perm-info-banner--warning {
    background:rgba(234,179,8,.10); border:1px solid rgba(234,179,8,.25); color:var(--color-warning,#eab308);
}

/* ── Role cards ──────────────────────────────────────────── */
.perm-roles-card { margin-bottom:18px; border-radius:16px; }
.perm-role-cards-row {
    display:grid; grid-template-columns:repeat(4,1fr); gap:12px; padding:16px 20px 20px;
}
@media (max-width:900px) { .perm-role-cards-row { grid-template-columns:repeat(2,1fr); } }
@media (max-width:520px)  { .perm-role-cards-row { grid-template-columns:1fr; } }

.perm-role-card {
    display:flex; flex-direction:column; gap:8px; padding:16px;
    background:linear-gradient(160deg,rgba(255,255,255,.03) 0%,transparent 55%),var(--bg-surface-2);
    border:1px solid rgba(255,255,255,.06); border-radius:14px; cursor:pointer;
    transition:all 200ms ease; position:relative; overflow:hidden;
}
.perm-role-card:hover:not(.is-active) { border-color:rgba(255,255,255,.14); transform:translateY(-1px); box-shadow:0 4px 16px rgba(0,0,0,.30); }
.perm-role-card.is-active { border-color:var(--color-primary); cursor:default; box-shadow:0 0 0 1px var(--color-primary),0 4px 16px rgba(249,115,22,.15); background:rgba(249,115,22,.06); }
[data-theme="light"] .perm-role-card { background:#f8fafc; border-color:rgba(0,0,0,.07); }
[data-theme="light"] .perm-role-card:hover:not(.is-active) { background:#f1f5f9; border-color:rgba(0,0,0,.12); box-shadow:0 2px 8px rgba(0,0,0,.08); }
[data-theme="light"] .perm-role-card.is-active { background:rgba(234,111,0,.05); border-color:var(--color-primary); }
.perm-role-card-icon { width:38px; height:38px; border-radius:11px; display:flex; align-items:center; justify-content:center; margin-bottom:2px; }
.perm-role-card-icon svg { width:20px; height:20px; }
.perm-role-card-name { font-weight:600; font-size:0.9375rem; color:var(--text-primary); }
.perm-role-card-desc { font-size:0.75rem; color:var(--text-secondary); line-height:1.45; flex:1; }
.perm-role-card-badge {
    display:inline-flex; align-items:center; font-size:0.6875rem; font-weight:600;
    color:var(--color-primary-text); background:var(--color-primary-light);
    padding:2px 8px; border-radius:999px; margin-top:2px; width:fit-content;
}
.perm-role-defaults-link {
    display:inline-flex; align-items:center; gap:3px; margin-top:6px;
    font-size:0.6875rem; font-weight:500; color:var(--text-tertiary);
    text-decoration:none; transition:color 140ms ease;
}
.perm-role-defaults-link:hover { color:var(--color-primary-text); }

/* ── Two-column layout ───────────────────────────────────── */
.perm-layout { display:grid; grid-template-columns:1fr 300px; gap:18px; align-items:start; }
@media (max-width:1050px) { .perm-layout { grid-template-columns:1fr; } }

/* ── Matrix card ─────────────────────────────────────────── */
.perm-matrix-card { border-radius:16px; overflow:hidden; margin-bottom:0; }
.perm-matrix-card-header {
    display:flex; align-items:center; justify-content:space-between;
    gap:12px; padding:18px 20px 14px; border-bottom:1px solid var(--border-color);
}
.perm-matrix-header-actions { display:flex; align-items:center; gap:10px; flex-shrink:0; }
.perm-override-pill {
    display:inline-flex; align-items:center; font-size:0.75rem; font-weight:600;
    color:var(--color-primary-text); background:var(--color-primary-light);
    padding:3px 10px; border-radius:999px;
}

/* ── Section headers ─────────────────────────────────────── */
.perm-section-header {
    display:flex; align-items:center; justify-content:space-between;
    gap:12px; padding:8px 20px 7px;
    background:var(--bg-surface-2); border-bottom:1px solid var(--border-color);
    border-top:1px solid var(--border-color); position:sticky; top:0; z-index:2;
}
.perm-section-title { display:flex; align-items:center; gap:7px; font-size:0.6875rem; font-weight:600; color:var(--text-secondary); text-transform:uppercase; letter-spacing:var(--tracking-label); }
.perm-section-dot { width:7px; height:7px; border-radius:50%; flex-shrink:0; }
.perm-section-bulk-actions { display:flex; align-items:center; gap:4px; flex-shrink:0; }
.perm-bulk-btn {
    font-size:0.6875rem; font-weight:500; padding:3px 9px; border-radius:999px;
    border:1px solid var(--border-color-strong); background:var(--bg-surface);
    color:var(--text-secondary); cursor:pointer; transition:all 140ms ease; white-space:nowrap;
}
.perm-bulk-btn:hover:not(:disabled) { background:var(--bg-surface-hover); color:var(--text-primary); border-color:var(--color-primary); }
.perm-bulk-btn--danger:hover:not(:disabled) { border-color:var(--color-danger); color:var(--color-danger-text,#f87171); background:rgba(239,68,68,.06); }
.perm-bulk-btn--muted:hover:not(:disabled) { border-color:var(--text-tertiary); }
.perm-bulk-btn:disabled { opacity:.45; cursor:not-allowed; }
@media (max-width:768px) { .perm-section-bulk-actions { display:none; } }

/* ── Matrix rows ─────────────────────────────────────────── */
.perm-matrix-row {
    display:flex; align-items:center; gap:20px; padding:12px 20px;
    border-bottom:1px solid var(--border-color); transition:background 120ms ease;
}
.perm-matrix-row:last-child { border-bottom:none; }
.perm-matrix-row:hover { background:rgba(255,255,255,.015); }
[data-theme="light"] .perm-matrix-row:hover { background:rgba(0,0,0,.015); }
@media (max-width:768px) { .perm-matrix-row { flex-wrap:wrap; } }
.perm-matrix-row-header { min-width:148px; max-width:148px; }
@media (max-width:768px) { .perm-matrix-row-header { min-width:100%; max-width:100%; } }
.perm-matrix-row-label { font-weight:500; font-size:0.875rem; color:var(--text-primary); }
.perm-matrix-row-cells { display:flex; flex-wrap:wrap; gap:12px 18px; flex:1; }
.perm-matrix-cell-col { display:flex; flex-direction:column; align-items:center; gap:6px; }
.perm-matrix-cell-header { font-size:0.6875rem; font-weight:500; color:var(--text-tertiary); text-transform:capitalize; text-align:center; cursor:help; white-space:nowrap; }

/* ── Apple iOS Toggle Switch ─────────────────────────────── */
.perm-ios-toggle {
    display:inline-block; position:relative;
    width:42px; height:25px; border-radius:12.5px;
    /* S-LUX-2.5: warm-neutral off-track (was cool slate rgba 100,116,139) to
       match the Atelier pills. The semantic ON states below (is-on green /
       ovr-allow brand / ovr-deny danger) are the permission language and are
       deliberately left as-is — this is a tri-state control, not a plain
       switch, so it is NOT flattened to a single brand colour. */
    background:rgba(140,130,115,.28); cursor:pointer; flex-shrink:0;
    transition:background 200ms ease, box-shadow 200ms ease;
    -webkit-user-select:none; user-select:none;
}
.perm-ios-toggle::after {
    content:''; position:absolute; top:2.5px; left:2.5px;
    width:20px; height:20px; border-radius:50%; background:#fff;
    box-shadow:0 1px 4px rgba(0,0,0,.35);
    transition:transform 200ms cubic-bezier(0.175,0.885,0.32,1.1);
}
.perm-ios-toggle.is-on { background:#22c55e; }
.perm-ios-toggle.is-on::after { transform:translateX(17px); }
.perm-ios-toggle.ovr-allow { background:var(--color-primary); box-shadow:0 0 0 2.5px rgba(249,115,22,.38); }
.perm-ios-toggle.ovr-allow::after { transform:translateX(17px); }
.perm-ios-toggle.ovr-deny { background:var(--color-danger); box-shadow:0 0 0 2.5px rgba(239,68,68,.38); }
.perm-ios-toggle.is-readonly { opacity:.52; cursor:default; pointer-events:none; }
.perm-ios-toggle--sm { width:36px; height:21px; border-radius:10.5px; }
.perm-ios-toggle--sm::after { width:17px; height:17px; top:2px; left:2px; }
.perm-ios-toggle--sm.is-on { background:#22c55e; }
.perm-ios-toggle--sm.is-on::after { transform:translateX(15px); }

/* ── Sidebar cards ───────────────────────────────────────── */
.perm-sidebar-col { display:flex; flex-direction:column; gap:14px; }
.perm-sidebar-card { border-radius:16px; overflow:hidden; }
.perm-badge-count { margin-left:auto; background:var(--color-primary-light); color:var(--color-primary-text); font-size:0.6875rem; font-weight:700; padding:1px 7px; border-radius:999px; }
.perm-empty-state { display:flex; flex-direction:column; align-items:center; gap:7px; padding:22px 16px; color:var(--text-tertiary); font-size:0.8125rem; text-align:center; }
.perm-override-list { list-style:none; margin:0; padding:0; }
.perm-override-item { padding:9px 14px; border-bottom:1px solid var(--border-color); font-size:0.8125rem; }
.perm-override-item:last-child { border-bottom:none; }
.perm-override-item-head { display:flex; align-items:center; gap:6px; }
.perm-override-badge { font-size:0.625rem; font-weight:700; padding:2px 6px; border-radius:999px; flex-shrink:0; text-transform:uppercase; letter-spacing:.04em; }
.perm-override-badge--allow { background:rgba(34,197,94,.15); color:var(--color-success-text,#4ade80); }
.perm-override-badge--deny  { background:rgba(239,68,68,.14); color:var(--color-danger-text,#f87171); }
.perm-override-target { font-weight:500; flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.perm-override-action { color:var(--text-tertiary); font-weight:400; }
.perm-override-clear-btn {
    flex-shrink:0; width:20px; height:20px; border-radius:50%;
    border:1px solid var(--border-color-strong); background:transparent;
    color:var(--text-tertiary); cursor:pointer; display:flex; align-items:center; justify-content:center; transition:all 140ms ease;
}
.perm-override-clear-btn:hover:not(:disabled) { background:rgba(239,68,68,.12); color:var(--color-danger,#ef4444); border-color:var(--color-danger,#ef4444); }
.perm-override-reason { font-size:.7rem; color:var(--text-tertiary); margin-top:3px; font-style:italic; padding-left:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* ── Notification toggle rows ────────────────────────────── */
.perm-notif-row { display:flex; align-items:center; gap:12px; padding:8px 16px; border-bottom:1px solid var(--border-color); cursor:pointer; transition:background 120ms ease; }
.perm-notif-row:last-child { border-bottom:none; }
.perm-notif-row:hover { background:rgba(255,255,255,.02); }
[data-theme="light"] .perm-notif-row:hover { background:rgba(0,0,0,.02); }
.perm-notif-text { flex:1; min-width:0; }
.perm-notif-label { display:block; font-size:.8125rem; font-weight:500; color:var(--text-primary); }
.perm-notif-desc  { display:block; font-size:.6875rem; color:var(--text-tertiary); margin-top:1px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.perm-notif-save-btn { margin-left:auto; font-size:.75rem; font-weight:600; padding:4px 12px; border-radius:999px; background:var(--color-primary); color:var(--text-on-primary); border:none; cursor:pointer; transition:background 140ms ease; flex-shrink:0; }
.perm-notif-save-btn:hover:not(:disabled) { background:var(--color-primary-hover); }
.perm-notif-save-btn:disabled { opacity:.6; cursor:not-allowed; }
.perm-notif-saved { margin:4px 16px 6px; padding:6px 12px; background:rgba(34,197,94,.12); color:var(--color-success-text,#4ade80); border-radius:8px; font-size:.75rem; font-weight:600; text-align:center; }
.perm-notif-error { margin:4px 16px 6px; padding:6px 12px; background:var(--color-danger-light); color:var(--color-danger-text,#f87171); border-radius:8px; font-size:.75rem; }

/* ── Role permissions page tabs ──────────────────────────── */
.rp-role-tabs { border-radius:16px; overflow:hidden; }
.rp-role-tabs-inner { display:flex; overflow-x:auto; padding:12px 16px; gap:8px; scrollbar-width:none; }
.rp-role-tabs-inner::-webkit-scrollbar { display:none; }
.rp-role-tab { display:flex; flex-direction:column; align-items:center; gap:4px; padding:12px 20px; border-radius:12px; border:1px solid rgba(255,255,255,.06); background:var(--bg-surface-2); text-decoration:none; color:var(--text-secondary); min-width:110px; transition:all 160ms ease; flex-shrink:0; }
.rp-role-tab:hover:not(.is-active) { border-color:rgba(255,255,255,.13); color:var(--text-primary); background:var(--bg-surface-hover); }
.rp-role-tab.is-active { border-color:var(--color-primary); background:rgba(249,115,22,.07); color:var(--text-primary); box-shadow:0 0 0 1px var(--color-primary); }
[data-theme="light"] .rp-role-tab { background:#f8fafc; border-color:rgba(0,0,0,.07); }
[data-theme="light"] .rp-role-tab.is-active { background:rgba(234,111,0,.05); }
.rp-role-tab-icon { width:22px; height:22px; display:flex; align-items:center; justify-content:center; }
.rp-role-tab-icon svg { width:20px; height:20px; }
.rp-role-tab-name { font-weight:600; font-size:0.875rem; white-space:nowrap; }
.rp-role-tab-count { font-size:0.6875rem; color:var(--text-tertiary); }
.rp-info-banner { display:flex; align-items:center; gap:8px; padding:10px 14px; background:rgba(59,130,246,.08); border:1px solid rgba(59,130,246,.20); border-radius:10px; font-size:0.8125rem; color:var(--text-secondary); margin-bottom:16px; line-height:1.5; }
.rp-config-badge { display:inline-block; font-size:0.5625rem; font-weight:600; text-transform:uppercase; letter-spacing:.05em; color:var(--text-tertiary); background:var(--bg-surface-2); border:1px solid var(--border-color); padding:1px 5px; border-radius:4px; margin-top:2px; }
.rp-config-dot { width:6px; height:6px; border-radius:50%; margin-top:1px; }
.rp-config-dot--on  { background:rgba(34,197,94,.55); }
.rp-config-dot--off { background:rgba(100,116,139,.4); }
.rp-legend-grid { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
.rp-legend-item { display:flex; align-items:center; gap:8px; font-size:0.75rem; color:var(--text-secondary); }
.rp-no-role-selected { display:flex; flex-direction:column; align-items:center; gap:10px; padding:60px 20px; color:var(--text-tertiary); font-size:0.875rem; text-align:center; }
.rp-loading { display:flex; align-items:center; justify-content:center; gap:10px; padding:60px 20px; color:var(--text-secondary); font-size:0.875rem; }
.rp-loading-spinner { width:20px; height:20px; border-radius:50%; border:2px solid var(--border-color-strong); border-top-color:var(--color-primary); animation:ff-spin .7s linear infinite; }
@keyframes ff-spin { to { transform:rotate(360deg); } }

/* ============================================================
   PORTAL-USERS MOBILE (S-FIX-PORTAL-USERS-MOBILE)
   Two portal-users containers were inline-styled multi-column
   grids that didn't stack at phone width → right-hand fields /
   the info column ran off-screen and cropped. New portal-users-
   SCOPED classes (not shared) carry the desktop grid + a ≤767px
   single-column stack. Desktop layout unchanged.
   ============================================================ */

/* BUG A — "Create Portal User" form (app/admin/users/index.php, Portal tab).
   Desktop: Customer | Name | Email | Create. Mobile: single column, each
   field + the Create button full-width. */
.portal-create-grid {
    display: grid;
    grid-template-columns: 240px 1fr 1fr auto;
    gap: 12px;
    align-items: end;
}
@media (max-width: 767px) {
    .portal-create-grid {
        grid-template-columns: 1fr;
        align-items: stretch;
    }
    .portal-create-grid > * { width: 100%; }
    .portal-create-grid .btn { width: 100%; }
}

/* BUG B — portal-user profile (app/admin/portal_users/show.php).
   Desktop: info column | 320px actions column. Mobile: stack vertically
   (info card on top, action cards below), each full-width. */
.portal-user-show-grid {
    display: grid;
    grid-template-columns: 1fr 320px;
    gap: 16px;
    align-items: start;
}
@media (max-width: 767px) {
    .portal-user-show-grid {
        grid-template-columns: 1fr;
    }
}


/* =============================================================================
   AI Summary Card  (S-AI-STREAM-SUMMARY v2)
   All var() refs use only real :root variables.
   ============================================================================= */

/* Orange top-accent distinguishes this card from plain data cards */
.ai-summary-card {
    border-top: 3px solid var(--color-primary);
}

.ai-summary-card .card-header {
    background: linear-gradient(135deg, var(--bg-surface-2) 0%, rgba(249,115,22,0.06) 100%);
    gap: 10px;
}

.ai-summary-card__header-left {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.ai-summary-card__header-right {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
    align-items: center;
}

.ai-summary-card__icon {
    width: 16px;
    height: 16px;
    color: var(--color-primary);
    flex-shrink: 0;
    filter: drop-shadow(0 0 4px rgba(249,115,22,0.45));
}

.ai-summary-card__title {
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--text-primary);
    white-space: nowrap;
}

.ai-summary-card__badge {
    font-size: 0.58rem;
    letter-spacing: 0.08em;
    padding: 1px 6px;
    background: rgba(249,115,22,0.15);
    color: var(--color-primary-text);
    border: 1px solid rgba(249,115,22,0.3);
    border-radius: var(--radius-full);
    font-weight: 700;
    text-transform: uppercase;
    flex-shrink: 0;
}

.ai-summary-card__btn { font-size: 0.75rem; white-space: nowrap; }

.ai-summary-card__btn-loading {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* Uses --border-color-strong (exists) not --border-default (doesn't) */
.ai-summary-card__spinner {
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 2px solid var(--border-color-strong);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
    flex-shrink: 0;
}

/* Empty state */
/* WHY: Compact single-line empty state — large centred layout wasted half the page */
.ai-summary-card__empty {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 2px 0;
    color: var(--text-tertiary);
    font-size: 0.8125rem;
}

/* Error state */
.ai-summary-card__error {
    padding: 10px 14px;
    background: var(--color-danger-light);
    border: 1px solid rgba(239,68,68,0.3);
    border-radius: var(--radius-md);
    font-size: 0.8125rem;
    color: var(--color-danger-text);
    line-height: 1.5;
}

/* Streaming cursor */
.ai-summary-card__cursor {
    display: inline-block;
    color: var(--color-primary);
    font-size: 1rem;
    line-height: 1;
    animation: ai-blink 0.85s steps(1) infinite;
    margin-left: 1px;
    vertical-align: text-bottom;
}

@keyframes ai-blink {
    0%, 49% { opacity: 1; }
    50%, 100% { opacity: 0; }
}

/* Footer meta strip */
.ai-summary-card__meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 16px;
    padding-top: 10px;
    border-top: 1px solid var(--border-color);
    font-size: 0.6875rem;
    color: var(--text-tertiary);
}

.ai-summary-card__meta-cached { color: var(--text-tertiary); }
.ai-summary-card__meta-fresh  { color: var(--color-success-text); }

/* =============================================================================
   .ai-response — prose rendering for Claude output
   Using only real CSS variables: --text-primary/secondary/tertiary,
   --border-color/strong, --bg-surface-2, --color-primary etc.
   ============================================================================= */
.ai-response {
    font-size: 0.8375rem;
    line-height: 1.6;
    color: var(--text-primary);
}

.ai-response > .ai-para:first-child { margin-top: 0; }
.ai-response > .ai-para:last-child  { margin-bottom: 0; }

.ai-response .ai-para {
    margin: 0 0 0.5em;
    color: var(--text-primary);
}

/* Section headers — thin bottom rule, compact label style */
.ai-response .ai-section-header {
    font-size: 0.625rem;
    font-weight: 700;
    color: var(--text-tertiary);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin: 1.1em 0 0.3em;
    padding: 0 0 3px;
    border-left: none;
    border-bottom: 1px solid var(--border-color);
    line-height: 1.3;
    display: block;
}

.ai-response .ai-section-header--h2 {
    font-size: 0.6875rem;
    color: var(--text-secondary);
    font-weight: 700;
    letter-spacing: 0.07em;
    border-bottom-width: 1px;
}

.ai-response .ai-section-header--h1 {
    font-size: 0.75rem;
    color: var(--text-secondary);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    border-bottom: 2px solid var(--border-color);
    padding-bottom: 4px;
}

.ai-response .ai-section-header:first-child { margin-top: 0; }

/* Bullet lists */
.ai-response .ai-list {
    margin: 0.2em 0 0.5em 0;
    padding-left: 1.1em;
    list-style: disc;
}

.ai-response .ai-list li {
    margin: 0.15em 0;
    line-height: 1.56;
    color: var(--text-primary);
}

.ai-response .ai-list li::marker {
    color: var(--color-primary);
}

/* Inline code */
.ai-response .ai-inline-code {
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    padding: 1px 5px;
    color: var(--text-primary);
}

/* Code blocks */
.ai-response .ai-code-block {
    background: var(--bg-surface-2);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: 10px 14px;
    margin: 0.35em 0 0.55em;
    overflow-x: auto;
    font-size: 0.8125rem;
    line-height: 1.6;
}

.ai-response .ai-code-block code {
    font-family: var(--font-mono);
    white-space: pre;
    color: var(--text-primary);
}

/* Horizontal rule */
.ai-response .ai-divider {
    border: none;
    border-top: 1px solid var(--border-color);
    margin: 0.7em 0;
}

.ai-response strong {
    font-weight: 600;
    color: var(--text-primary);
}

.ai-response em {
    font-style: italic;
    color: var(--text-secondary);
}

/* =============================================================================
   AI Analysis Panel  (S-AI-PANEL)
   Right-side drawer following the same pattern as .help-drawer-panel.
   Replaces the inline AI summary card — no more wasted page space.
   ============================================================================= */

/* Trigger button wrapper — sits in the normal flow where the include is placed */
.ai-panel-trigger-wrap {
    display: inline-block;
    margin-bottom: 0;
}

.ai-panel-trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.75rem;
}

.ai-panel-trigger__icon {
    width: 13px;
    height: 13px;
    color: var(--color-primary);
    flex-shrink: 0;
    filter: drop-shadow(0 0 3px rgba(249,115,22,0.5));
}

/* Backdrop — mobile only */
.ai-panel-backdrop {
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-modal) - 3);
    background: rgba(0,0,0,0.55);
}

/* Panel */
.ai-panel {
    position: fixed;
    top: var(--topbar-height);
    right: 0;
    bottom: 0;
    z-index: calc(var(--z-modal) - 2); /* 58 — above backdrop(57) and topbar(30); below modals(60) */
    width: 560px;
    max-width: 100vw;
    background: var(--bg-surface);
    border-left: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    /* Animation */
    animation: ai-panel-slide-in 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes ai-panel-slide-in {
    from { transform: translateX(100%); opacity: 0.6; }
    to   { transform: translateX(0);    opacity: 1; }
}

/* Orange accent bar at top */
.ai-panel::before {
    content: '';
    display: block;
    height: 3px;
    background: linear-gradient(90deg, var(--color-primary) 0%, var(--color-primary-hover, #dda720) 100%);
    flex-shrink: 0;
}

/* Push page content aside on desktop when panel is open */
@media (min-width: 1100px) {
    body[data-ai-panel-open] .page-content {
        margin-right: 560px;
        transition: margin-right 0.22s cubic-bezier(0.16, 1, 0.3, 1);
    }
    body[data-ai-panel-open] .ai-panel-backdrop {
        display: none !important;
    }
}

/* Header */
.ai-panel__header {
    padding: 13px 18px 12px;
    border-bottom: 1px solid var(--border-color);
    background: var(--bg-surface-2);
    flex-shrink: 0;
}

.ai-panel__header-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}

.ai-panel__eyebrow {
    display: flex;
    align-items: center;
    gap: 5px;
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--color-primary);
}

.ai-panel__title {
    font-size: 1.0625rem;
    font-weight: 700;
    color: var(--text-primary);
    letter-spacing: -0.02em;
    margin: 0 0 12px;
    line-height: 1.3;
}

.ai-panel__close {
    flex-shrink: 0;
    opacity: 0.45;
    transition: opacity 0.15s;
    padding: 4px;
    color: var(--text-primary);
}
.ai-panel__close:hover { opacity: 1; }

/* Toolbar row */
.ai-panel__toolbar {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}

.ai-panel__generating {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 0.75rem;
    color: var(--text-secondary);
}

.ai-panel__spinner {
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 2px solid var(--border-color-strong);
    border-top-color: var(--color-primary);
    border-radius: 50%;
    animation: spin 0.7s linear infinite;
    flex-shrink: 0;
}

/* Scrollable body */
.ai-panel__body {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 16px 20px 24px;
    scroll-behavior: smooth;
}

.ai-panel__body::-webkit-scrollbar { width: 4px; }
.ai-panel__body::-webkit-scrollbar-track { background: transparent; }
.ai-panel__body::-webkit-scrollbar-thumb {
    background: var(--border-color);
    border-radius: 2px;
}

/* Empty / prompt state */
.ai-panel__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 48px 24px 40px;
    color: var(--text-secondary);
}

.ai-panel__empty-graphic {
    color: var(--color-primary);
    margin-bottom: 18px;
}

.ai-panel__empty-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 8px;
}

.ai-panel__empty-desc {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    line-height: 1.6;
    max-width: 340px;
    margin: 0 0 20px;
}

/* Error state */
.ai-panel__error {
    padding: 12px 16px;
    background: var(--color-danger-light);
    border: 1px solid rgba(239,68,68,0.3);
    border-radius: var(--radius-md);
    font-size: 0.8125rem;
    color: var(--color-danger-text);
    line-height: 1.5;
}

/* Meta footer */
.ai-panel__meta {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 20px;
    padding-top: 12px;
    border-top: 1px solid var(--border-color);
    font-size: 0.6875rem;
    color: var(--text-tertiary);
}

.ai-panel__meta-cached { color: var(--text-tertiary); }
.ai-panel__meta-fresh  { color: var(--color-success-text); }

@media (max-width: 767px) {
    .ai-panel { width: 100vw; }
    .ai-panel__body { padding: 16px 16px 28px; }
}


/* ============================================================
   S-LUX-1 (2026-07-11) — Atelier finish layer
   ::selection tint + film grain. Lives at end-of-file so it wins
   source-order ties against everything above.
   ============================================================ */

/* Brand-tinted text selection. Background only — inheriting the
   element's own text color keeps selected text readable in both
   themes (the 30% orange tint reads over dark and light). */
::selection {
    background: rgba(249, 115, 22, 0.30);
}

/* Film grain — full-viewport SVG fractal noise at --grain-opacity
   (kill-switch: set --grain-opacity: 0). pointer-events:none so it
   never eats clicks; z-index 1 sits above static page content but
   below all positioned chrome (sidebar 40 / topbar 30 / modal 60 /
   toast 70). At 0.025 it reads as material texture, not dirt. */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: var(--grain-opacity);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-repeat: repeat;
}

/* Grain must never reach paper (PRINT-DARK-FIX companion) */
@media print {
    body::after { display: none; }
}
