/* ============================================================
   platform.css — PBSI Operations Platform v2.0
   Design System Tokens · Phase 1

   PURPOSE
   -------
   Additive token layer loaded AFTER style.css. Does NOT
   modify existing selectors or class names — those remain
   owned by style.css throughout the migration. This file
   only adds new CSS custom properties and applies the font
   transition from DM Sans → Inter.

   Any selector defined here intentionally overrides a
   style.css equivalent is noted with "OVERRIDE:" inline.

   DARK MODE
   ---------
   Tokens are defined for both light and dark. The toggle
   mechanism will be wired in Phase 3; for now the
   [data-theme="dark"] block is inactive.
   ============================================================ */


/* ── Light mode tokens ── */
:root,
[data-theme="light"] {

  /* Typography — unified app-wide on the Executive Analytics font system.
     Manrope = base/body, Archivo = display headings, JetBrains Mono = numeric.
     All three are loaded in index.html. These tokens previously lived only
     inside .v2-analytics-claude; promoting them to :root makes the ENTIRE app
     inherit the same typeface as Executive Analytics. */
  /* Product redesign (v1.31.24.0) — one type family for the whole product:
     IBM Plex Sans (UI + headings) and its sibling IBM Plex Mono (codes,
     references, clock times). Replaces Manrope/Archivo/Inter/JetBrains Mono
     (4 families, ~19 font files) — see docs/DESIGN-SYSTEM.md §3. */
  --font-display: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-sans:    'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono:    'IBM Plex Mono', ui-monospace, 'Cascadia Mono', Consolas, monospace;
  /* Editorial voice (product reimagining) — brand and narrative moments only
     (login, the Today briefing headline, empty states); never for data. */
  --font-editorial: 'Instrument Serif', 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;

  /* ── Unified WORKSPACE system (v1.16.2.5) — Analytics Executive is the
     design authority. ONE source for every page's content geometry. The values
     are exactly Executive's current geometry, so Executive is visually identical.
     Effective left inset = main-content(16) + workspace(16) + pad-inline(24) = 56px.
     Pages that lack the intermediate 16px workspace layer use --workspace-edge
     (=workspace 16 + pad-inline 24 = 40) so the TOTAL still lands at 56px. */
  /* V1 desktop content-width fix: bumped 1400→1600px. .main-content (style.css)
     used to independently hardcode its own max-width:1400px — same number,
     different mechanism — which was harmless only by coincidence. It now reads
     this same token (see style.css .main-content), so this is the single place
     that controls desktop content width for every page keyed off it. */
  --workspace-max-width:   1600px;
  --workspace-pad-inline:  24px;   /* gutter when already inside .v2-workspace */
  --workspace-edge:        40px;   /* gutter when a page sits directly in .main-content */
  --workspace-pad-top:     8px;
  --workspace-pad-bottom:  44px;
  --workspace-section-gap: 32px;
  --workspace-card-gap:    16px;

  /* Vertical rhythm — one spacing scale for every page (Executive values). */
  --space-hero:       26px;  /* hero → first section */
  --space-section:    26px;  /* section → section */
  --space-subsection: 22px;  /* sub-group within a section */
  --space-card:       16px;  /* card → card */

  /* ── Unified TYPOGRAPHY scale (v1.16.2.5) — mirrors the Executive shell, so
     there are no standalone font-sizes; every level references a token. */
  --type-display-xl: clamp(30px, 4.4vw, 52px);  /* keynote hero (Exec hero-title) */
  --type-display-lg: clamp(22px, 2.6vw, 28px);  /* secondary hero / .an-display */
  --type-heading-xl: 19px;   /* section title (eyebrow h2) */
  --type-heading-lg: 18px;   /* KPI / figure value */
  --type-heading-md: 15px;   /* card title */
  --type-body:       14px;   /* body / narrative */
  --type-caption:    11px;   /* caption under a figure */
  --type-label:      11px;   /* micro label / table header / tag base */

  /* ── Product redesign (v1.31.24.0) — colour foundation ──────────────────
     Cool, quiet neutrals; ONE interactive accent ("court green", the colour
     of a badminton court — PBSI's own sport); PBSI crimson kept as the
     BRAND mark only (logo tile, login), never as the primary-action colour,
     because the old accent was byte-identical to --danger and every primary
     button read as destructive. Full rationale + contrast table:
     docs/DESIGN-SYSTEM.md §2. Every value below meets WCAG AA where it is
     used as text (≥4.5:1 on --surface and --canvas). */

  /* ── Product reimagining ("Lapangan", v1.32 branch) — the identity now
     comes from the REAL PBSI shield (assets/Logo-PBSI.png: green field,
     yellow crown, white shuttlecock, red ribbon), not from the crimson the
     1.31.24 redesign assumed. Neutrals carry a faint green cast so the
     canvas reads as "court" without colouring any data. Contrast for every
     pair below is guarded by scripts/design-token-contrast-check.mjs and
     documented in docs/DESIGN-SYSTEM.md §2. */

  /* ── Reimagining v2 ("Lapangan Terang", 2026-10-14) — light mode is BRIGHT:
     a warm ivory canvas (sunlit hall floor, not a grey-green office), white
     working surfaces, warm hairlines. The previous green-cast neutrals read
     as cool and, with the near-black deck, made light mode feel dark.
     Ratios are guarded by scripts/design-token-contrast-check.mjs. */

  /* Canvas & surfaces */
  --canvas:         #FAF8F2;
  --surface:        #FFFFFF;
  --surface-2:      #F6F4EC;
  --surface-3:      #EDEAE0;
  --border:         #E6E2D5;
  --border-strong:  #CFC9B8;
  --grid:           #EFECE3;

  /* Text hierarchy */
  --text:           #17201B;   /* 16.5:1 on surface */
  --muted:          #4F5A53;   /* 7.1:1 on surface */
  --faint:          #5E6861;   /* 5.7:1 on surface — meta text, placeholders */

  /* Interactive states */
  --hover:          #F2F0E7;
  --input-bg:       #FFFFFF;
  --input-border:   #CBC5B4;

  /* Accent — PBSI green (the shield's field, deepened for UI). 6.6:1 with
     white text and 6.6:1 as text on surface. --accent-fg is the accent used
     AS TEXT; in dark it is a lighter step (see dark block). */
  --accent:         #0A6B3C;
  --accent-fg:      #0A6B3C;
  --on-accent:      #FFFFFF;
  --focus-ring:     0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent);

  /* Brand — PBSI shield colours, identity only (deck, login, splash).
     --shuttle is the crown/shuttle yellow: decorative marks and the active
     marker on the dark deck ONLY — never text or fills on light surfaces. */
  --brand:          #0E8A45;
  --brand-subtle:   #E6F3EB;
  --shuttle:        #F2D80B;

  /* Chrome regions — the topbar sits ON the canvas (one hairline), so the
     white deck and white work surfaces are what step forward. */
  --topbar-bg:      #FAF8F2;
  --tl-head-bg:     #F6F4EC;

  /* The deck — navigation frame. Reimagining v2: in LIGHT it is a white
     panel (labelled, court-line accents in PBSI green); the deep court-green
     deck stays as the DARK theme's frame and as the login court. Text pairs
     (guarded): deck-text, deck-muted, deck-strong on deck; deck-active-fg
     on deck-active. --deck-marker = active line/dot, --deck-focus = focus
     outline (≥3:1 on deck). */
  --deck:           #FFFFFF;
  --deck-2:         #F8F6EF;
  --deck-line:      #ECE8DC;
  --deck-text:      #2C3530;
  --deck-muted:     #56615A;
  --deck-strong:    #17201B;
  --deck-hover:     #F3F1E8;
  --deck-active:    #E3F1E8;
  --deck-active-fg: #0A5F35;
  --deck-marker:    #0A6B3C;
  --deck-focus:     #0A6B3C;

  /* The court — PBSI's deep court green, the BRAND surface: login court,
     splash, logout veil, admin identity, toasts. Deliberately a green, not
     the near-black of the old deck. court-text 10.4:1, court-muted 5.9:1. */
  --court:          #0B4A2B;
  --court-2:        #0A6B3C;
  --court-text:     #E3EEE6;
  --court-muted:    #A8C4B2;

  /* Sidebar / rail (legacy drawer surfaces) — the deck */
  --side-bg:        var(--deck);
  --side-border:    #E6E2D5;
  --side-text:      #3A443E;
  --side-strong:    #17201B;
  --side-faint:     #5E6861;
  --side-active-bg: var(--deck-active);
  --side-active-tx: #0A5F35;

  /* Avatars */
  --avatar-bg:      #EAEDE4;
  --avatar-tx:      #3F4B44;

  /* Semantic status palette. ok shares the accent's family (success IS the
     product's colour here) — it is always carried by a label + check icon in a
     tinted chip, never a solid fill, so it cannot read as a primary button. */
  --info:           #2455B0;
  --info-bg:        #EAF0FB;
  --ok:             #17733A;
  --ok-bg:          #E6F3EA;
  --warn:           #935607;
  --warn-bg:        #FBF1E1;
  --danger:         #B4271E;
  --danger-bg:      #FCEDEB;

  /* Elevation — cards are flat (border only); shadows are reserved for
     things that float (menus, popovers, dialogs, sheets). Tinted with the
     deck green instead of neutral black so shadows sit IN the palette. */
  --shadow-xs:      0 1px 1px rgba(12,25,18,.05);
  --shadow-sm:      0 1px 2px rgba(12,25,18,.07), 0 1px 1px rgba(12,25,18,.03);
  --shadow-md:      0 8px 20px -6px rgba(12,25,18,.14), 0 2px 4px -2px rgba(12,25,18,.06);
  --shadow-lg:      0 24px 52px -14px rgba(12,25,18,.26), 0 4px 10px -4px rgba(12,25,18,.07);

  /* Overlay / scrim */
  --scrim:          rgba(8,17,12,.46);

  /* Radius scale (product redesign) — the --radius-sm/-lg names keep their
     legacy owners in style.css; these are the new canonical names. */
  --r-xs:  4px;
  --r-sm:  6px;
  --r-md:  8px;
  --r-lg:  12px;
  --r-xl:  16px;
  --r-full: 999px;

  /* Control sizing */
  --control-h:    36px;
  --control-h-sm: 30px;
  --touch-min:    44px;

  /* Vehicle color palette — matches existing per-vehicle vars in style.css.
     Provides canonical names for V2.0 components going forward. */
  --v-innova:       #3B5BA9;
  --v-luxio:        #2F7D62;
  --v-poly:         #A9742A;
  --v-hiace:        #6B4E9E;

  /* Compact density defaults */
  --row-h:          62px;
  --bar-top:        9px;
  --bar-h:          44px;

  /* ── V1 Redesign Phase 1a: token gap-fill (additive only) ──────────────
     Extends the existing type scale, adds a real 8px spacing scale, motion
     durations, breakpoint custom properties, and accent interaction states
     per docs/DESIGN_SYSTEM_SPEC_v1.30.9.12.md + RESPONSIVE_BEHAVIOR_SPEC.
     Every name below was grep-verified to have zero existing consumers, so
     this block cannot change anything currently rendered. The radius scale
     (--radius-sm/md/lg) is deliberately NOT added here: --radius-sm/-lg
     already have 30+ consumers elsewhere (style.css, platform.css,
     js/components/vehicle-*.js) with inconsistent/no fallbacks, so defining
     them at :root now would be a real visual change, not an addition —
     deferred to the phase that deliberately migrates those call sites. */

  /* Type scale gap-fill */
  --type-heading-sm: 14px;
  --type-body-lg:    15px;
  --type-body-sm:    12px;

  /* Real 8px spacing scale (kept separate from the still-live
     --space-hero/section/subsection/card set above) */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 28px;
  --space-8: 32px;

  /* Motion durations — --motion-pop and --motion-sheet are real, consumed
     tokens (rail-item hover, mobile sheet slide-in respectively). Phase
     8.1 removed --motion-view and --motion-theme: both had zero var()
     consumers anywhere, and --motion-theme's comment was stale (claimed
     to mirror the .theme-anim crossfade, but that crossfade is a
     hardcoded .32s/.26s, never var-driven — see line ~366). */
  --motion-pop:   160ms;
  --motion-sheet: 260ms;

  /* Breakpoint tokens — documentation/JS (matchMedia) use only; CSS custom
     properties cannot appear inside @media conditions, so existing @media
     literals are unaffected until each file migrates deliberately. */
  --bp-xs: 380px;
  --bp-sm: 600px;
  --bp-md: 768px;
  --bp-lg: 1024px;
  --bp-xl: 1280px;

  /* Accent interaction states — formalizes the two most common ad hoc
     patterns already used repeatedly in this file (color-mix 85%/#000 for
     hover at line ~838; color-mix ~12%/transparent for subtle tints at
     e.g. lines ~8792, ~12446), deriving both light/dark automatically from
     --accent with no per-theme duplication. */
  --accent-hover:  color-mix(in srgb, var(--accent) 85%, #000);
  --accent-subtle: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* ── Comfortable density (future Tweaks toggle) ── */
[data-density="comfortable"] {
  --row-h:          74px;
  --bar-top:        11px;
  --bar-h:          52px;
}


/* ── Dark mode tokens ── */
/* Inactive until the theme toggle is wired in Phase 3.
   Defined now so dark-mode-aware components can reference
   them without a later refactor. */
[data-theme="dark"] {
  /* Product redesign (v1.31.24.0) — dark is a designed theme, not an
     inversion: surfaces step UP in lightness with elevation, borders carry
     the structure, and the accent is lightened just enough to stay ≥4.5:1
     both as text on --surface and under white button text. */
  --canvas:         #0B100D;
  --surface:        #121915;
  --surface-2:      #172019;
  --surface-3:      #1E2821;
  --border:         #253029;
  --border-strong:  #36433B;
  --grid:           #1A231D;

  --text:           #E5ECE7;
  --muted:          #A4B2A9;   /* 8.1:1 on surface */
  --faint:          #88968D;   /* 5.8:1 on surface */

  --hover:          #1A241E;
  --input-bg:       #0E1411;
  --input-border:   #34413A;

  /* One value cannot serve both roles in dark (white-on-fill needs a darker
     green than accent-as-text on #121915 allows), so the pair is split:
     --accent for fills under white text (4.8:1), --accent-fg for accent-
     coloured text (8.8:1). Legacy rules that still use --accent as a text
     colour get 3.7:1 (≥3:1 UI floor); new rules use --accent-fg. */
  --accent:         #17834A;
  --accent-fg:      #5CCB8D;
  --on-accent:      #FFFFFF;
  --focus-ring:     0 0 0 3px color-mix(in srgb, #5CCB8D 38%, transparent);

  --brand:          #2FA862;
  --brand-subtle:   #15281D;
  --shuttle:        #F2D80B;

  --topbar-bg:      #0F1512;
  --tl-head-bg:     #161E19;

  /* The deck sits BELOW the canvas in dark (darker), so it still reads as
     the frame around content rather than a raised panel. */
  --deck:           #070C09;
  --deck-2:         #0F1912;
  --deck-line:      rgba(220,231,224,.08);
  --deck-text:      #DCE7E0;
  --deck-muted:     #8DA295;
  --deck-strong:    #FFFFFF;
  --deck-hover:     #112018;
  --deck-active:    #17301F;
  --deck-active-fg: #FFFFFF;
  --deck-marker:    #F2D80B;
  --deck-focus:     #F2D80B;

  --court:          #0A1A11;
  --court-2:        #0F3B23;
  --court-text:     #DCE7E0;
  --court-muted:    #8DA295;

  --side-bg:        var(--deck);
  --side-border:    #18261D;
  --side-text:      #B9C9BF;
  --side-strong:    #F1F6F2;
  --side-faint:     #7F9587;
  --side-active-bg: var(--deck-active);
  --side-active-tx: #F1F6F2;

  --avatar-bg:      #1F2A23;
  --avatar-tx:      #B9C9BF;

  --info:           #83A9EE;
  --info-bg:        #19233A;
  --ok:             #61C98A;
  --ok-bg:          #14271C;
  --warn:           #E2A84C;
  --warn-bg:        #2D2314;
  --danger:         #F17F75;
  --danger-bg:      #331918;

  --shadow-xs:      0 1px 1px rgba(0,0,0,.30);
  --shadow-sm:      0 1px 2px rgba(0,0,0,.40);
  --shadow-md:      0 10px 24px -8px rgba(0,0,0,.62), 0 0 0 1px rgba(220,231,224,.04);
  --shadow-lg:      0 26px 60px -16px rgba(0,0,0,.72), 0 0 0 1px rgba(220,231,224,.05);

  --scrim:          rgba(2,5,3,.64);

  --v-innova:       #5E76B0;
  --v-luxio:        #479B79;
  --v-poly:         #BE9350;
  --v-hiace:        #8470B5;

  /* ── Phase 11A: legacy V1 token bridge (the "white-trap" fix) ──────────
     style.css's original tokens (--gray-1..4, --text-muted) were never
     remapped for dark mode anywhere in this file — every V1-era card,
     badge, input, and border still rendered with its light-mode value in
     dark mode (previously patched ad hoc per-selector; see the
     #v2TimelineSurface scoped fix a few hundred lines down as the prior
     art this generalizes). Bridging them here fixes every consumer at the
     source instead of one selector at a time.
     --dark / --dark-2 / --dark-3 / --white are intentionally NOT bridged
     here: they're dual-use in style.css (e.g. --dark is both `.sidebar`'s
     always-dark background AND ~28 places' primary text color; --white is
     both "card surface" background AND "text on a colored badge/button").
     A blind remap of either would fix one usage and break the other into
     an actual invisible-text regression, worse than today's visual
     inconsistency. Left for a follow-up that first splits the token by
     usage (see Phase 11 report, Remaining Findings). */
  --gray-1:     var(--surface-2);
  --gray-2:     var(--border);
  --gray-3:     var(--border-strong);
  --gray-4:     var(--muted);
  --text-muted: var(--muted);
}


/* ============================================================
   BASE OVERRIDES
   ============================================================ */

/* Font: Inter replaces DM Sans.
   Both are grotesque sans-serifs with near-identical metrics;
   no layout shift expected. */
body {
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.011em;
  /* OVERRIDE: background and color aligned to V2.0 tokens.
     #F5F5F3 (--canvas) vs #F5F5F5 (--gray-1) — imperceptible. */
  background: var(--canvas);
  color: var(--text);
}

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

/* Mono elements: JetBrains Mono replaces DM Mono */
code,
kbd,
pre,
.wa-preview-text,
#filterDate,
.date-input {
  font-family: var(--font-mono);
}

/* Text selection accent */
::selection {
  background: color-mix(in srgb, var(--accent) 22%, transparent);
}


/* ============================================================
   SMOOTH THEME CROSSFADE
   Armed by JS adding .theme-anim to <html> for ~650ms during
   a theme switch. Prevents flash-on-load by being opt-in.
   ============================================================ */
/* Phase 7G.3 — durations trimmed from .55s/.55s/.40s/.55s to .32s/.32s/
   .26s/.32s (kept in 7G.4). MEASURED via a real Chrome trace of an actual
   toggle click (page.tracing, not inferred): style recalculation was the
   dominant cost — layout, paint, compositing, and JS were all negligible
   by comparison. See docs/EXECUTIVE_COMMAND_CENTER_7G3_TYPOGRAPHY_PERFORMANCE_REPORT.md
   for the full before numbers.

   Phase 7G.4 — TRIED and MEASURED, then reverted: narrowing this selector
   from a bare `*` to `*:not(path, circle, rect, ... option, link, meta,
   title, script, style)` — element types that are structurally incapable
   of needing this transition (SVG shape internals never render
   background-color/border-color/color/box-shadow at all; icon recoloring
   already works via fill="currentColor" tracking an ancestor's `color`
   live, no transition of its own required; <option>/<link>/<meta>/<title>/
   <script>/<style> never have a visible box). That excluded ~16% of the
   real app's matched elements (316 of 1978, measured via
   document.querySelectorAll('*') on the live page) with zero visual risk.
   A controlled before/after — SAME session, SAME machine, 3 real Chrome
   traces per side via page.tracing on an actual toggle click, narrowed vs.
   reverted-to-plain-`*` — showed NO reproducible difference: style
   recalculation stayed at ~290-308ms across 13-15 events either way, and
   long-task count/duration were statistically indistinguishable between
   the two. Chrome's recalculation cost during this transition is
   apparently driven by walking the invalidation scope from the
   `.theme-anim` class toggle on <html> (an ancestor of literally
   everything), not by how many elements the transition-property selector
   itself matches — so trimming the selector doesn't reduce the work
   Chrome actually does. Reverted to the simpler, unmodified `*` rather
   than keep unjustified complexity for a measured non-improvement — see
   docs/EXECUTIVE_COMMAND_CENTER_PHASE_7G4_REPORT.md for the full A/B
   numbers. This finding is recorded so the same hypothesis isn't
   re-attempted without new evidence. */
/* v1.31.4 R5 — the ONE shared timing contract every theme-toggle crossfade
   rule in this file draws from (this universal rule AND the curated "Part
   D" override further down, ~line 5700) — previously two independently-
   tuned duration sets (this rule's .32s/.26s vs Part D's 280ms/200ms) that
   had drifted apart, so a curated container (rail, topbar, surface cards)
   visibly settled ~40ms before/after everything else, and even WITHIN one
   curated element its background/color (280ms) and box-shadow (still this
   rule's .32s, since Part D never overrode shadow) settled at different
   moments. Both rules now reference these tokens, so they can never drift
   again. Border intentionally settles faster than fill/shadow (a crisper
   edge reads better than a slow-fading outline) — that is a deliberate
   difference, kept, not the accidental one this fixes. */
:root {
  --theme-fade-duration: 280ms;
  --theme-border-duration: 200ms;
}
html.theme-anim,
html.theme-anim *,
html.theme-anim *::before,
html.theme-anim *::after {
  transition:
    background-color var(--theme-fade-duration) cubic-bezier(.4,0,.2,1),
    border-color     var(--theme-border-duration) cubic-bezier(.4,0,.2,1),
    color            var(--theme-fade-duration) ease,
    box-shadow       var(--theme-fade-duration) ease !important;
}
/* Phase 7G.2 — this whole crossfade mechanism (this block and Part D
   further down) had no reduced-motion guard at all. [data-anim="off"] is
   the same site-level opt-out js/app.js's prefersReducedMotion() already
   checks (see its own comment: "respect prefers-reduced-motion /
   [data-anim=\"off\"]"); prefers-reduced-motion is the OS-level signal.
   Either one disables the crossfade — data-theme still switches instantly
   to the correct final colors, nothing is left mid-transition. */
@media (prefers-reduced-motion: reduce) {
  html.theme-anim,
  html.theme-anim *,
  html.theme-anim *::before, html.theme-anim *::after {
    transition: none !important;
  }
}
html[data-anim="off"].theme-anim,
html[data-anim="off"].theme-anim *,
html[data-anim="off"].theme-anim *::before,
html[data-anim="off"].theme-anim *::after {
  transition: none !important;
}

/* ============================================================
   Phase 7G.5 — WHOLE-VIEW THEME TRANSITION (View Transitions API)

   Primary mechanism now, when the browser supports it (js/app.js's
   applyTheme() feature-detects document.startViewTransition and falls
   back to the .theme-anim mechanism above when unsupported OR when
   motion is disabled — see that function's own comment). Everything the
   API needs is this one small block: startViewTransition() automatically
   captures the whole viewport before/after the theme mutation and cross-
   fades the two as a single compositor animation — by design there is no
   way for one region to visibly lag another, which is the actual thing
   every prior phase's per-element tuning (7G.1-7G.4) could not fix,
   because that model was fundamentally N independent transitions, not
   one. Deliberately minimal: the UA default (a plain cross-fade) is
   already correct for a same-layout theme swap; only duration is tuned
   into the requested ~250-350ms band (default is 250ms, already in
   range, but explicit here rather than relying on an unstated default).
   ============================================================ */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 300ms;
  animation-timing-function: ease;
}

/* SS14.5 — the ::view-transition pseudo-element tree renders as a
   position:fixed, viewport-covering top-layer overlay (the View
   Transitions spec's own UA stylesheet) for the ~300-390ms (occasionally
   longer — a heavier workspace render measured ~590ms) a navigation
   transition runs. Confirmed by direct instrumentation, mouse held
   genuinely stationary and hovering the whole time: document.
   elementFromPoint() at the cursor's own screen position reads back <html>
   instead of the live rail button underneath for the ENTIRE transition
   window, and real :hover on .domshell-rail genuinely goes false as a
   result — not a visual artifact, an actual pseudo-class state change,
   which the rail's own base .domshell-rail{transition:width 200ms ease}
   rule then genuinely, visibly collapses to 72px in response to, before
   re-expanding once the overlay tears down and hit-testing reaches the
   live DOM again. This is what SS14.1-14.4 were chasing as "the sidebar
   collapses during navigation" and could never reproduce with a
   programmatic .click() (no real hover in flight to lose) or in the
   isolated domain-shell.js harness (whose mock `land` never called
   startViewTransition() at all).
   Tried first, disproven by direct testing (a minimal, app-independent
   repro page): giving the transition pseudo-elements pointer-events:none,
   the standard web-platform recommendation for "let input reach the live
   page under a purely decorative overlay." getComputedStyle confirmed the
   rule applied (pointer-events read "none"), but elementFromPoint() still
   returned <html> throughout — this Chromium's top-layer hit-testing for
   View Transitions does not fall through to the live document underneath
   regardless of pointer-events. Not a viable fix here.
   Actual fix: the SAME "brief interruption shouldn't visibly move the
   rail" technique this file already uses for the opposite direction (the
   :hover/:focus-within rule's own 100ms expand dwell, "so a pointer graze
   across the 72px boundary never triggers a visible width change") is
   applied to the COLLAPSE direction, but only for the width of an actual
   navigation transition: a collapse trigger scoped to
   html.domshell-nav-transition gets a delay comfortably longer than any
   observed transition duration. Real hover reliably returns before that
   delay elapses (confirmed: ~250-300ms after the transition starts, the
   overlay tears down and :hover flips true again) — CSS transitions
   cancel a still-pending (not yet started) transition outright when the
   property's target value reverts before the delay elapses, so the width
   never actually moves. A genuine pointer-leave DURING a navigation is
   delayed by the same margin before it visibly collapses — an accepted,
   narrow trade-off, and back to instant the moment the class clears. */
html.domshell-nav-transition .domshell-rail:not(:hover):not(:focus-within) {
  transition: width var(--motion-normal, 200ms) var(--motion-ease, ease) 700ms;
}

/* SS14.6 — the width fix above didn't cover the whole picture: the exact
   same real-hover-genuinely-lapses mechanism also drives
   .domshell-rail-label/-brandtext/-usertext, each independently opacity:0
   with its OWN no-delay 120ms fade-out (".domshell-rail:hover
   .domshell-rail-label{opacity:1}" simply stops matching the instant
   :hover is lost). Confirmed by direct measurement, mouse genuinely
   stationary and hovering throughout a real navigation: label/brandtext
   opacity actually ran 1.00 -> 0.72 -> 0.30 -> 0.03 -> 0.00 over the first
   100ms, sat at 0 through the rest of the transition, then 0.00 -> 0.33 ->
   1.00 back up once real hover returned — a real, visible disappear/
   reappear despite the rail's own WIDTH never moving (SS14.5). Same fix,
   same reasoning, same 700ms margin: a fade-out transition scoped to
   html.domshell-nav-transition + :not(:hover):not(:focus-within) on the
   ancestor rail never gets the chance to start before real hover returns,
   so the browser cancels it outright and opacity never visibly moves. */
html.domshell-nav-transition .domshell-rail:not(:hover):not(:focus-within) .domshell-rail-label,
html.domshell-nav-transition .domshell-rail:not(:hover):not(:focus-within) .domshell-rail-brandtext,
html.domshell-nav-transition .domshell-rail:not(:hover):not(:focus-within) .domshell-rail-usertext {
  transition: opacity var(--motion-fast, 120ms) linear 700ms;
}

/* SS14.4 — the persistent domain-shell sidebar (js/shell/domain-shell.js,
   .domshell-rail above) is given its own view-transition-name so Phase 8.5's
   navigation transition (setWorkspace(), js/app.js) can't sweep it into the
   root cross-fade alongside the outgoing/incoming CONTENT, which is what
   produced the reported "sidebar ghosts/disappears during navigation" —
   confirmed via document.startViewTransition() introspection: the rail
   participated in a ~300-360ms root transition on every module click, with
   getComputedStyle(rail).viewTransitionName reading "none" (i.e. not
   excluded). Its OWN group still gets old/new snapshots (the active-item
   highlight really does recolor between them), so still cross-fading those
   would trade one visible artifact for a smaller one — a hard, instant snap
   reads as "stayed put" the way nothing else in this rail visibly does. The
   CONTENT area keeps its full animated cross-fade untouched; only this one
   named group is affected.
   Scoped to html.domshell-nav-transition (set only for the lifetime of a
   setWorkspace() navigation transition) — applyTheme()'s OWN View Transition
   (Phase 7G.5, light/dark toggle) also targets root and deliberately WANTS
   the rail's colors cross-fading smoothly together with the rest of the
   page, exactly like every other region; an unscoped rule here would have
   made the sidebar hard-snap on every theme toggle too, a real regression
   to a working, intentionally-designed feature this fix has nothing to do
   with. */
/* SS14.10: this rule never actually matched anything, since SS14.4 — the
   space before each `::view-transition-*(domshell-rail)` made it a
   descendant-combinator selector, but view-transition pseudo-elements are
   only selectable via a compound selector attached directly to their
   originating root element (no space): `html.foo::view-transition-old(x)`,
   never `html.foo ::view-transition-old(x)`. Proven with a controlled A/B
   stylesheet injection: the descendant-combinator form left old/new
   running the browser's UA default cross-fade (animationName
   "-ua-view-transition-fade-out"/"-ua-view-transition-fade-in", opacity
   genuinely interpolating over the full 250ms) regardless of !important;
   the identical rule rewritten as a compound selector suppressed it
   completely (animationName "none", opacity pinned at 1 throughout). That
   250ms crossfade of the rail's OWN old/new snapshots is what let the
   PREVIOUS domain's active-highlight stay visibly on top for the ~1.1s a
   real screen recording caught, before snapping to the new state. Fixed
   selector shape below; `!important` kept as defense-in-depth, matching
   the identical precedent already used below for
   prefers-reduced-motion/data-anim="off".
   ::view-transition-image-pair(domshell-rail) is deliberately NOT included
   here: measured directly, it already computes animation:none with no
   authored rule targeting it at all — Chrome never assigns image-pair a
   default animation (only old/new get one), so there is nothing to
   suppress and no reason to add a selector for it. */
html.domshell-nav-transition::view-transition-group(domshell-rail),
html.domshell-nav-transition::view-transition-old(domshell-rail),
html.domshell-nav-transition::view-transition-new(domshell-rail) {
  animation: none !important;
}

/* Defense in depth: js/app.js already never calls startViewTransition()
   when prefers-reduced-motion/data-anim="off" is active (the transition
   simply never starts, applyThemeState() runs and the new theme appears
   immediately). This CSS guard means even a future/alternate call site
   that starts one anyway degrades to an instant, non-animated state
   change rather than a broken or partially-respected transition. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}
html[data-anim="off"] ::view-transition-group(*),
html[data-anim="off"] ::view-transition-old(*),
html[data-anim="off"] ::view-transition-new(*) {
  animation: none !important;
}


/* ============================================================
   V2.0 UTILITY CLASSES
   Defined now so Phase 2 components can reference them.
   These class names do NOT exist in style.css — no collision.
   ============================================================ */

/* ── Status pills ── */
.p-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 20px;
  white-space: nowrap;
  line-height: 1;
}
.p-pill .p-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}
.p-pill.info    { background: var(--info-bg);    color: var(--info);   }
.p-pill.ok      { background: var(--ok-bg);      color: var(--ok);     }
.p-pill.warn    { background: var(--warn-bg);    color: var(--warn);   }
.p-pill.danger  { background: var(--danger-bg);  color: var(--danger); }
.p-pill.neutral { background: var(--hover);      color: var(--muted);  }
.p-pill.info   .p-dot  { background: var(--info);   }
.p-pill.ok     .p-dot  { background: var(--ok);     }
.p-pill.warn   .p-dot  { background: var(--warn);   }
.p-pill.danger .p-dot  { background: var(--danger); }
.p-pill.neutral .p-dot { background: var(--faint);  }

/* ── V2.0 buttons ── */
.p-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border-radius: 8px;
  font-size: 12.5px;
  font-weight: 600;
  padding: 8px 14px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: filter .12s, background .12s;
  white-space: nowrap;
  font-family: inherit;
}
.p-btn svg { width: 14px; height: 14px; flex-shrink: 0; }

.p-btn-primary {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
}
.p-btn-primary:hover { filter: brightness(.92); }

.p-btn-ghost {
  background: var(--input-bg);
  color: var(--text);
  border-color: var(--input-border);
}
.p-btn-ghost:hover { background: var(--hover); }

.p-btn-ok      { background: var(--ok-bg);     color: var(--ok);     }
.p-btn-danger  { background: var(--danger-bg); color: var(--danger); }
.p-btn-sm      { padding: 6px 11px; font-size: 12px; }

/* ── V2.0 form inputs ── */
.p-inp,
.p-sel,
.p-ta {
  width: 100%;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 8px;
  padding: 9px 11px;
  font-size: 13px;
  color: var(--text);
  outline: none;
  font-family: inherit;
  transition: border-color .12s, box-shadow .12s;
}
.p-inp::placeholder,
.p-ta::placeholder  { color: var(--faint); }
.p-inp:focus,
.p-sel:focus,
.p-ta:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}
.p-ta     { resize: none; min-height: 62px; line-height: 1.5; }

/* ── Toggle switch ── */
.p-switch {
  width: 40px;
  height: 23px;
  border-radius: 12px;
  background: var(--input-border);
  position: relative;
  cursor: pointer;
  transition: background .15s;
  flex-shrink: 0;
  border: none;
  padding: 0;
}
.p-switch.on  { background: var(--ok); }
.p-switch::after {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  width: 19px; height: 19px;
  border-radius: 50%;
  background: #fff;
  transition: transform .15s;
  box-shadow: 0 1px 2px rgba(0,0,0,.28);
}
.p-switch.on::after { transform: translateX(17px); }

/* ── Toast (V2.0 style, complements existing #toast) ── */
.p-toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--text);
  color: var(--canvas);
  font-size: 12.5px;
  font-weight: 600;
  padding: 11px 18px;
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  z-index: 500;
  display: flex;
  align-items: center;
  gap: 9px;
  animation: p-toastin .2s ease;
  pointer-events: none;
}
.p-toast svg { width: 15px; height: 15px; flex-shrink: 0; }
@keyframes p-toastin {
  from { opacity: 0; transform: translate(-50%, 10px); }
  to   { opacity: 1; transform: translate(-50%, 0);    }
}

/* ── SVG size normalization for V2.0 icons ── */
.p-icon     { width: 16px; height: 16px; flex-shrink: 0; }
.p-icon-sm  { width: 13px; height: 13px; flex-shrink: 0; }
.p-icon-lg  { width: 20px; height: 20px; flex-shrink: 0; }

/* ── Scrollbar styling (V2.0 standard) ── */
.p-scroll        { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
.p-scroll::-webkit-scrollbar              { width: 10px; height: 10px; }
.p-scroll::-webkit-scrollbar-thumb       { background: var(--border-strong); border-radius: 6px; border: 3px solid transparent; background-clip: content-box; }
.p-scroll::-webkit-scrollbar-track       { background: transparent; }

/* ── Section divider ── */
.p-shead {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 4px 2px 2px;
}
.p-shead h2 {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--faint);
  margin: 0;
  white-space: nowrap;
}
.p-shead .p-line {
  flex: 1;
  height: 1px;
  background: var(--border);
}

/* ── Empty state ── */
.p-empty {
  text-align: center;
  padding: 48px 20px;
  color: var(--faint);
}
.p-empty .p-empty-icon {
  width: 46px; height: 46px;
  border-radius: 13px;
  background: var(--hover);
  display: grid; place-items: center;
  margin: 0 auto 12px;
  color: var(--faint);
}
.p-empty .p-empty-icon svg { width: 22px; height: 22px; }
.p-empty .p-empty-title { font-size: 13.5px; font-weight: 600; color: var(--muted); }
.p-empty .p-empty-desc  { font-size: 12px; margin-top: 3px; }


/* ============================================================
   V1 REDESIGN PHASE 2 — SHARED COMPONENT LIBRARY (additions)
   Extends the .p-* V2.0 utility classes above with the remaining
   primitives from docs/DESIGN_SYSTEM_SPEC_v1.30.9.12.md §3 (the
   "Sarpras Component Library" design map). These are NEW class names,
   grep-verified to collide with nothing app-wide — nothing existing is
   rewired to consume them yet; that's module-by-module adoption in a
   later phase, done one module at a time so each migration stays
   isolated and independently shippable (per the guardrails doc). The
   one exception is .p-btn's min-height, a real accessibility gap this
   fixes directly (DESIGN_SYSTEM_SPEC's Touch Targets finding: every
   interactive element should be ≥44×44px on touch surfaces; .p-btn had
   no min-height at all). .p-btn-sm intentionally still inherits it —
   the spec is explicit that there's no "small button" exception.
   ============================================================ */

.p-btn { min-height: 44px; }

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

.p-btn-tertiary {
  background: transparent;
  color: var(--accent-fg);
  border-color: transparent;
}
.p-btn-tertiary:hover { background: var(--accent-subtle); }

.p-btn-icon {
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  background: transparent;
  color: var(--muted);
  border-color: transparent;
}
.p-btn-icon:hover { background: var(--hover); color: var(--text); }

/* ── Removable chip (extends .p-pill) ── */
.p-pill--removable { padding-right: 6px; }
.p-pill-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: transparent;
  border: none;
  cursor: pointer;
  color: currentColor;
  opacity: .65;
  flex-shrink: 0;
}
.p-pill-remove:hover { opacity: 1; background: rgba(0,0,0,.08); }

/* ── Card — de-boxed by default per the redesign's visual language
   (DESIGN_SYSTEM_SPEC §2.7); .p-card--boxed is the explicit legacy
   escape hatch, not the default. ── */
.p-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: var(--space-6);
  box-shadow: none;
}
.p-card--boxed { box-shadow: var(--shadow-sm); }
.p-card--boxed:hover { box-shadow: var(--shadow-md); }
.p-card-title { font-size: var(--type-heading-md); font-weight: 600; color: var(--text); margin: 0 0 4px; }
.p-card-sub   { font-size: var(--type-body-sm); color: var(--muted); margin: 0; }

/* ── Table shell — horizontal-scroll wrapper, the pattern
   RESPONSIVE_BEHAVIOR_SPEC §5 confirmed is already correct and
   consistent app-wide; this just gives it reusable shared classes. ── */
.p-table-wrap { overflow-x: auto; }
.p-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.p-table th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.p-table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
  white-space: nowrap;
}
.p-table tbody tr:hover td { background: var(--hover); }

/* ── Generic nav rail item — mirrors the already-proven
   .v2-panel-nav-item shape (line ~12480) but built on canonical,
   app-wide tokens instead of the Executive-UI-kit-scoped ones. ── */
.p-navitem {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 44px;
  padding: 10px 14px;
  border-radius: 12px;
  font-size: 13.5px;
  font-weight: 520;
  color: var(--muted);
  cursor: pointer;
  transition: background-color var(--motion-pop) ease, color var(--motion-pop) ease;
}
.p-navitem:hover, .p-navitem:focus-visible {
  background: var(--accent-subtle);
  color: var(--text);
}
.p-navitem--active {
  background: var(--accent-subtle);
  color: var(--accent-fg);
  font-weight: 600;
}

/* ── Tabs — underline style, used by every module workspace. ── */
.p-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
}
.p-tab {
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  border: none;
  border-bottom: 2px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  background: none;
  font-family: inherit;
  min-height: 44px;
}
.p-tab:hover { color: var(--text); }
.p-tab--active { color: var(--accent-fg); border-bottom-color: var(--accent); }

/* ── KPI strip — capped at 3–5, never a KPI wall. ── */
.p-kpi-strip {
  display: flex;
  gap: var(--space-8);
  flex-wrap: wrap;
}
.p-kpi { display: flex; flex-direction: column; gap: 2px; }
.p-kpi-label { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--faint); }
.p-kpi-value { font-size: var(--type-heading-lg); font-weight: 700; color: var(--text); font-family: var(--font-mono); }
.p-kpi-delta { font-size: 11px; font-weight: 600; }
.p-kpi-delta--up   { color: var(--ok); }
.p-kpi-delta--down { color: var(--danger); }

/* ── Error state — mirrors .p-empty's shape (line ~522) with a danger
   tone, part of the design map's "15 states" state-component set. ── */
.p-error {
  text-align: center;
  padding: 48px 20px;
  color: var(--danger);
}
.p-error .p-error-icon {
  width: 46px; height: 46px;
  border-radius: 13px;
  background: var(--danger-bg);
  display: grid; place-items: center;
  margin: 0 auto 12px;
  color: var(--danger);
}
.p-error .p-error-icon svg { width: 22px; height: 22px; }
.p-error .p-error-title { font-size: 13.5px; font-weight: 600; color: var(--text); }
.p-error .p-error-desc  { font-size: 12px; margin-top: 3px; color: var(--muted); }


/* ============================================================
   LOGIN — retired here by the product reimagining. The login is a new
   composition (court + form) styled in design-system.css §8; the old
   glass-card rules were only used by the markup it replaced.
   ============================================================ */

/* ============================================================
   BRAND MARK — Design System Program Phase 6: Entry Transition
   One shared badge recipe for the PBSI crest, used at boot splash
   and login (pre-auth contexts). Neutral surface + border + soft
   shadow — deliberately NOT accent-filled, so the crest reads as
   one consistent object as it scales between contexts, including
   into js/shell/domain-shell.js's .domshell-rail-logo (32px,
   already neutral-badge styled, unchanged by this phase) at the
   end of the login→shell transition.
   ============================================================ */
.brand-mark {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  background: #fff;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.brand-mark img { width: 100%; height: 100%; object-fit: contain; padding: 8px; }
.brand-mark--login  { width: 56px; height: 56px; border-radius: 15px; }
.brand-mark--splash { width: 76px; height: 76px; border-radius: 18px; margin-bottom: 14px; }
[data-theme="dark"] .brand-mark { background: var(--surface); }

/* Restrained brand reveal — replaces an instant/static splash logo with a
   single settle-in beat. Uses the app's Phase 3 motion tokens (SCENE_MS /
   EASE_ARRIVE) rather than inventing new timing. */
@keyframes brand-mark-reveal {
  from { opacity: 0; transform: scale(.92); }
  to   { opacity: 1; transform: none; }
}
.app-splash-inner .brand-mark--splash { animation: brand-mark-reveal 550ms cubic-bezier(.16,1,.3,1) both; }
@media (prefers-reduced-motion: reduce) {
  .app-splash-inner .brand-mark--splash { animation: none; }
}

/* Brand row */
/* login-card-body fades as a single unit during the login→shell success
   transition (js/components/entry-transition.js) — the wrapper itself
   carries no visual styling, purely a fade target. */
.login-card-body { }

/* Shell reveal — applied briefly to .domshell-rail / .main-area by
   entry-transition.js once the login overlay is hidden, so the shell
   settles in rather than appearing as a hard cut. Reused for the
   session-restore boot path too (same restrained beat either way). */
@keyframes entry-reveal {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.entry-reveal { animation: entry-reveal 240ms cubic-bezier(.16,1,.3,1) both; }
@media (prefers-reduced-motion: reduce) {
  .entry-reveal { animation: none; }
}


/* ============================================================
   EXISTING COMPONENT REFINEMENTS
   Minor visual upgrades to current components using the new
   token values. All selector names unchanged from style.css.
   ============================================================ */

/* Header: soften shadow, use surface token */
.header {
  background: var(--topbar-bg);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

/* Modal overlay: use scrim token */
.modal-overlay {
  background: var(--scrim);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

/* Modal box: use surface token + V2.0 shadow */
.modal-box {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  border-radius: 14px;
}

/* Modal header: sticky with surface bg */
.modal-header {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

/* Form inputs: use input tokens */
.form-group input:not([type="checkbox"]):not([type="radio"]),
.form-group select,
.form-group textarea {
  background: var(--input-bg);
  border-color: var(--input-border);
  color: var(--text);
  transition: border-color .12s, box-shadow .12s;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
  outline: none;
}

/* Timeline header: tl-head-bg */
.timeline-header-row {
  background: var(--tl-head-bg);
}

/* Timeline vehicle bars: use canonical --v-* vars.
   These override any hardcoded hex in style.css bar rules. */
.bar-vehicle-innova { background: var(--v-innova) !important; }
.bar-vehicle-luxio  { background: var(--v-luxio)  !important; }
.bar-vehicle-poly   { background: var(--v-poly)   !important; }
.bar-vehicle-hiace  { background: var(--v-hiace)  !important; }

/* Legend swatches */
.legend-item[data-vehicle="Innova"]  { --swatch: var(--v-innova); }
.legend-item[data-vehicle="Luxio"]   { --swatch: var(--v-luxio);  }
.legend-item[data-vehicle="Polytron"]{ --swatch: var(--v-poly);   }
.legend-item[data-vehicle="Hiace"]   { --swatch: var(--v-hiace);  }

/* Toast: use text/canvas tokens */
.toast {
  background: var(--text);
  color: var(--canvas);
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  font-family: inherit;
}
/* Design System Program Phase 5 — severity variants, canonical toast
   (js/components/toast.js). Reuses the existing semantic status tokens
   (same palette already used app-wide by .p-pill.ok/info/warn/danger etc.)
   rather than inventing new colors. Solid background + white text keeps the
   same high-contrast pill look the base .toast already has. */
.toast--success { background: var(--ok);      color: #fff; }
.toast--error   { background: var(--danger);   color: #fff; }
.toast--warning { background: var(--warn);     color: #fff; }
.toast--info    { background: var(--info);     color: #fff; }
.toast__ico { display: inline-flex; vertical-align: -2px; margin-right: 6px; }
@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }
[data-anim="off"] .toast { animation: none; }

/* Btn-primary: use accent token */
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  font-family: inherit;
}
.btn-primary:hover  { background: color-mix(in srgb, var(--accent) 85%, #000); }
.btn-primary:active { background: color-mix(in srgb, var(--accent) 78%, #000); }


/* ============================================================
   PHASE 1 — DESIGN SYSTEM CONSOLIDATION
   Applying V2.0 tokens to V1 production components.

   Every rule below is an intentional OVERRIDE of a style.css
   declaration. style.css selectors are unchanged — these rules
   win by load-order (platform.css loads after style.css) and
   specificity matching.

   Scope: Header · Navigation · Notifications
   Does NOT activate any feature flag or V2 shell.
   ============================================================ */


/* ── 2. Header ───────────────────────────────────────────────
   OVERRIDE: header colours already partially set above.
   Below: date nav, buttons, role badge, module title.
   ──────────────────────────────────────────────────────────── */

/* Date navigation pill */
.date-nav {
  background: var(--surface-2);
  border-color: var(--border);
}

/* "Hari Ini" button */
.btn-today {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--accent-fg);
  border-color: color-mix(in srgb, var(--accent) 22%, transparent);
  font-family: inherit;
  font-weight: 600;
}
.btn-today:hover:not(:disabled) {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border-color: var(--accent);
}

/* Arrow buttons */
.btn-icon {
  color: var(--muted);
}
.btn-icon:hover {
  color: var(--text);
  background: var(--hover);
}

/* Date input */
.date-input {
  color: var(--text);
}

/* Module title (desktop) */
.header-module-title {
  color: var(--muted);
  font-size: 13px;
  font-weight: 500;
}

/* Display name */
.header-display-name {
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
}

/* Role badge — base */
.role-badge {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--muted);
  font-family: inherit;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
}

/* Role badge — per-role semantic colours */
.role-badge[data-role="admin"] {
  background: var(--danger-bg);
  border-color: color-mix(in srgb, var(--accent) 24%, transparent);
  color: var(--danger);
}
.role-badge[data-role="bidang"] {
  background: var(--info-bg);
  border-color: rgba(59, 91, 169, 0.24);
  color: var(--info);
}
.role-badge[data-role="driver"] {
  background: var(--warn-bg);
  border-color: rgba(148, 100, 32, 0.24);
  color: var(--warn);
}
.role-badge[data-role="viewer"] {
  background: var(--ok-bg);
  border-color: rgba(47, 125, 98, 0.24);
  color: var(--ok);
}
.role-badge[data-role="guest"] {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--faint);
}


/* ── 3. Sidebar Navigation ───────────────────────────────────
   OVERRIDE: all sidebar hardcoded rgba/hex values → --side-* tokens.
   The sidebar background was already correct (#1D1D1B ≈ --side-bg).
   ──────────────────────────────────────────────────────────── */

/* Sidebar shell */
.sidebar {
  background: var(--side-bg);
  border-right: 1px solid var(--side-border);
}

/* Brand header rule */
.sidebar-header {
  border-bottom-color: var(--side-border);
}

/* Brand title */
.sidebar-brand-title {
  color: var(--side-strong);
  font-family: inherit;
}

/* Close button (mobile drawer) */
.sidebar-close {
  color: var(--side-faint);
}
.sidebar-close:hover {
  color: var(--side-strong);
}

/* Nav item base */
.sidebar-nav-item {
  color: var(--side-text);
  font-family: inherit;
  border-radius: 8px;
  transition: background 0.13s, color 0.13s;
}
.sidebar-nav-item:hover {
  background: rgba(255, 255, 255, 0.07);
  color: var(--side-strong);
}
.sidebar-nav-item:active {
  background: rgba(255, 255, 255, 0.11);
}

/* Primary CTA (Tambah Jadwal) */
.sidebar-nav-primary {
  background: var(--accent);
  color: var(--on-accent);
  font-family: inherit;
}
.sidebar-nav-primary:hover {
  background: color-mix(in srgb, var(--accent) 86%, #000);
  color: var(--on-accent);
}

/* Active nav item — left-edge accent bar mirroring the planned V2 rail indicator.
   Tokens --side-active-bg and --side-active-tx are already defined in :root. */
.sidebar-nav-item--active {
  background: var(--side-active-bg);
  color: var(--side-active-tx);
  box-shadow: inset 3px 0 0 var(--accent);
}
.sidebar-nav-item--active:hover {
  background: var(--side-active-bg);
  color: var(--side-active-tx);
}

/* Tertiary items (Profil, Admin Panel) */
.sidebar-nav-tertiary {
  color: var(--side-faint);
}
.sidebar-nav-tertiary:hover {
  color: var(--side-text);
  background: rgba(255, 255, 255, 0.05);
}

/* Logout item */
.sidebar-nav-logout {
  color: rgba(207, 100, 105, 0.72);
}
.sidebar-nav-logout:hover {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--danger);
}

/* Nav group separators */
.sidebar-nav-group + .sidebar-nav-group {
  border-top-color: var(--side-border);
}
.sidebar-nav-group-bottom {
  border-top-color: var(--side-border) !important;
}

/* Request count badge inside sidebar */
.sidebar-badge {
  background: var(--accent);
  color: var(--on-accent);
}

/* Sidebar footer */
.sidebar-footer {
  border-top-color: var(--side-border);
}
.sidebar-app-name {
  color: var(--side-faint);
  font-size: 9.5px;
  letter-spacing: 0.01em;
}
.sidebar-version {
  color: var(--side-faint);
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: 0.1px;
}

/* Sidebar backdrop overlay */
.sidebar-overlay {
  background: var(--scrim);
}


/* ── 4. Bottom Navigation (mobile) ───────────────────────────
   OVERRIDE: bg/border/text → V2 surface tokens.
   ──────────────────────────────────────────────────────────── */

.bottom-nav {
  background: var(--surface);
  border-top-color: var(--border);
  box-shadow: 0 -1px 0 var(--border), 0 -4px 12px rgba(28, 26, 23, 0.06);
}

.bottom-nav-item {
  color: var(--faint);
  font-family: inherit;
  font-size: 10px;
  font-weight: 500;
  transition: color 0.13s;
}
.bottom-nav-item:hover  { color: var(--text); }
.bottom-nav-item:active { color: var(--accent-fg); }

/* Active tab */
.bottom-nav-active,
.bottom-nav-primary {
  color: var(--accent-fg);
}
.bottom-nav-active:hover,
.bottom-nav-primary:hover {
  color: color-mix(in srgb, var(--accent) 80%, #000);
}

/* Badges */
.bottom-nav-badge { background: var(--accent); }
.bottom-nav-dot   { background: var(--accent); border-color: var(--surface); }


/* ── 5. Notification Cards ────────────────────────────────────
   OVERRIDE: notif-card colours → V2 surface/text tokens.
   Priority accent colours use semantic tokens instead of hardcoded hex.
   ──────────────────────────────────────────────────────────── */

/* List container */
.notifications-list {
  gap: 8px;
  margin-top: 0;
}

/* Card base */
.notif-card {
  background: var(--surface);
  border-color: var(--border);
  border-left-width: 3px;
  border-radius: 8px;
  padding: 11px 14px;
}

/* Priority left-edge accent */
.notif-priority-high   { border-left-color: var(--danger); }
.notif-priority-medium { border-left-color: var(--warn);   }
.notif-priority-low    { border-left-color: var(--border-strong); }

/* Card title */
.notif-card-title {
  color: var(--text);
  font-size: 12.5px;
  font-weight: 600;
}

/* Timestamp */
.notif-card-time {
  color: var(--faint);
  font-family: var(--font-mono);
  font-size: 10.5px;
}

/* Description */
.notif-card-desc {
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
}

/* ── Activity Log ────────────────────────────────────────────
   OVERRIDE: log item colours → V2 surface tokens.
   ──────────────────────────────────────────────────────────── */

.activity-log-subtitle {
  color: var(--faint);
  font-size: 12px;
}

.activity-log-item {
  background: var(--surface-2);
  border-color: var(--border);
  border-radius: 7px;
  padding: 9px 12px;
}

.activity-log-action {
  color: var(--text);
  font-family: var(--font-mono);
  font-size: 11.5px;
  font-weight: 600;
}

.activity-log-time {
  color: var(--faint);
  font-family: var(--font-mono);
  font-size: 10.5px;
}

.activity-log-user {
  color: var(--muted);
  font-size: 12px;
  margin-top: 2px;
}


/* ── 6. Header Notification Bell ─────────────────────────────
   New element: #btnHeaderNotif in .header-user-area.
   Visible to all authenticated users on desktop.
   Hidden on mobile (≤768px) — bottom nav bell covers that surface.
   ──────────────────────────────────────────────────────────── */

.header-notif-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.13s, color 0.13s;
  padding: 0;
}
.header-notif-btn:hover  { background: var(--hover); color: var(--text); }
.header-notif-btn:active { background: var(--border); }

/* Header install affordance — same visual language as the notification
   button. Unlike the bell (hidden on mobile in favour of the bottom nav),
   this stays available on every viewport so Android Chrome and iPhone
   Safari can install from the header too. Visibility is JS-controlled. */
.header-install-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background 0.13s, color 0.13s;
  padding: 0;
}
.header-install-btn:hover  { background: var(--hover); color: var(--text); }
.header-install-btn:active { background: var(--border); }
.header-install-btn:disabled { opacity: 0.55; cursor: default; }

/* Unread indicator dot */
.header-notif-dot {
  position: absolute;
  top: 5px;
  right: 5px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
  /* Border matches the topbar so the dot reads as floating above the icon */
  border: 2px solid var(--topbar-bg);
  pointer-events: none;
}

/* Hide header bell on mobile — bottom nav handles notification access */
@media (max-width: 767px) {
  .header-notif-btn { display: none !important; }
}


/* ── 7. Sidebar notification button: retired on desktop ──────
   OVERRIDE: #btnNotifications hidden on desktop; the header bell
   takes its place. The element stays in DOM — mobile bottom nav
   still proxies its click() event successfully.
   ──────────────────────────────────────────────────────────── */
@media (min-width: 768px) {
  #btnNotifications { display: none !important; }
}


/* ── 8. Display name: always visible, never hidden ───────────
   OVERRIDE: guarantee truncation at any viewport width instead
   of relying on fixed max-width alone.
   ──────────────────────────────────────────────────────────── */
.header-display-name {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tighter max-width on compact screens so user-area doesn't overflow */
@media (max-width: 600px) {
  .header-display-name { max-width: 80px; }
  .role-badge           { flex-shrink: 0; white-space: nowrap; }
}


/* ── Admin access button inside Profile modal (P2.2) ─────────
   Compact single-row button. Intentionally not styled as danger
   (that is for Logout). Uses ghost token palette.
   ──────────────────────────────────────────────────────────── */
.btn-profile-admin {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  transition: background 0.13s, border-color 0.13s;
}
.btn-profile-admin:hover {
  background: var(--hover);
  border-color: var(--border-strong);
}
.btn-profile-admin svg { flex-shrink: 0; color: var(--muted); }


/* ── 9. Mobile header overflow fix ───────────────────────────
   OVERRIDE: .header-user-area is flex-shrink:0 in style.css
   (line ~2567). On very narrow screens the area can push the
   row wider than the viewport. Allow it to compress here;
   its children have explicit max-widths and won't collapse.
   ──────────────────────────────────────────────────────────── */
@media (max-width: 600px) {
  .header-user-area {
    flex-shrink: 1;
    min-width: 0;
    gap: 5px;
  }
}


/* ============================================================
   VSM-1 — Navigation Rail

   The .v2-rail element is injected into the DOM by initV2Rail()
   in app.js ONLY when the visualShellV2 feature flag is true.
   When the flag is false: this element does not exist in the DOM
   and these rules have zero effect on the application.

   body.v2-shell-active is added by initV2Rail() at the same time.
   Sidebar-hide and margin overrides are scoped to @media(min-width:768px)
   so V1 mobile behavior (bottom nav + sidebar drawer) is fully preserved
   when the flag is on.

   Z-index budget: rail 120, tooltip 130 — both below modals (200+).
   ============================================================ */

/* ── Desktop sidebar + main-area overrides ── */
@media (min-width: 768px) {
  body.v2-shell-active #sidebar {
    display: none !important;
  }
  body.v2-shell-active .main-area {
    margin-left: 282px; /* 64px rail + 218px panel */
  }
}

/* ── VSM-3: Hide V1 header at all breakpoints when V2 shell is active.
   The .header element stays in the DOM as an empty shell (elements migrated
   to .v2-topbar) so rollback (page reload with flag off) restores full V1. ── */
body.v2-shell-active .header {
  display: none !important;
}

/* ── Rail container ──
   display:none by default; becomes display:flex on desktop via
   the media query below. On mobile (≤767px) the element stays hidden
   so the V1 sidebar drawer and bottom nav handle all navigation.       */
.v2-rail {
  position: fixed;
  top: 0;
  left: 0;
  width: 64px;
  height: 100vh;
  height: 100dvh; /* dynamic viewport on mobile Safari — fallback above */
  background: var(--side-bg);
  border-right: 1px solid rgba(255, 255, 255, 0.07);
  z-index: 120;
  display: none;
  flex-direction: column;
  align-items: center;
  padding: 10px 0 14px;
  box-sizing: border-box;
  overflow: hidden;
}

@media (min-width: 768px) {
  .v2-rail {
    display: flex;
  }
}

/* ── Platform crest (logo at top of rail) ── */
.v2-rail-crest {
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  cursor: pointer;
  margin-bottom: 16px;
  flex-shrink: 0;
  transition: background 0.15s;
  outline: none;
}
.v2-rail-crest:hover,
.v2-rail-crest:focus-visible {
  background: rgba(255, 255, 255, 0.08);
}
.v2-rail-crest-img {
  width: 28px;
  height: 28px;
  object-fit: contain;
  border-radius: 6px;
  display: block;
}

/* ── Module icon list ── */
.v2-rail-modules {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  flex: 1;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* ── Individual module icon ── */
.v2-rail-item {
  position: relative;
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  cursor: pointer;
  color: rgba(255, 255, 255, 0.38);
  transition: background 0.15s, color 0.15s;
  flex-shrink: 0;
  outline: none;
}
.v2-rail-item:hover,
.v2-rail-item:focus-visible {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.72);
}

/* Active state: crimson tint + left-edge indicator */
.v2-rail-item--active {
  color: #ffffff;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}
.v2-rail-item--active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 22px;
  background: var(--accent, #A8292F);
  border-radius: 0 2px 2px 0;
}

.v2-rail-icon {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  display: block;
}

/* ── Hover tooltip ── */
.v2-rail-tooltip {
  position: absolute;
  left: calc(100% + 10px);
  top: 50%;
  transform: translateY(-50%) translateX(-6px);
  background: var(--surface);
  color: var(--text);
  font-size: 12px;
  font-weight: 600;
  font-family: var(--font-sans, 'Inter', sans-serif);
  white-space: nowrap;
  padding: 5px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s, transform 0.12s;
  z-index: 130;
}
.v2-rail-item:hover .v2-rail-tooltip,
.v2-rail-item:focus-visible .v2-rail-tooltip {
  opacity: 1;
  transform: translateY(-50%) translateX(0);
}

/* ── User avatar ── */
.v2-rail-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent, #A8292F);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  margin-top: auto;
  transition: opacity 0.15s;
  outline: none;
}
.v2-rail-avatar:hover,
.v2-rail-avatar:focus-visible {
  opacity: 0.82;
}
.v2-rail-avatar-initials {
  font-size: 11px;
  font-weight: 700;
  color: #ffffff;
  letter-spacing: 0.03em;
  font-family: var(--font-sans, 'Inter', sans-serif);
  text-transform: uppercase;
  user-select: none;
  line-height: 1;
}


/* ============================================================
   VSM-2 — Context Panel (Section Panel)

   The .v2-panel element is injected into the DOM by initV2Panel()
   in app.js ONLY when the visualShellV2 feature flag is true.
   When the flag is false: this element does not exist in the DOM
   and these rules have zero effect on the application.

   Layout: position:fixed at left:64px (right of the rail).
   Desktop only (≥768px): same breakpoint as the rail.
   Mobile (≤767px): panel stays hidden; V1 sidebar drawer handles
   mobile navigation — no change to V1 mobile behavior.

   Z-index budget: panel 115 — below rail (120), below modals (200+).
   ============================================================ */

/* ── Panel container ── */
.v2-panel {
  position: fixed;
  top: 0;
  left: 64px; /* flush right of rail */
  width: 218px;
  height: 100vh;
  height: 100dvh;
  background: var(--surface);
  border-right: 1px solid var(--border);
  z-index: 115;
  display: none; /* hidden by default; shown at ≥768px below */
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
}

@media (min-width: 768px) {
  .v2-panel {
    display: flex;
  }
}

/* ── Module title header ── */
.v2-panel-header {
  padding: 18px 16px 10px;
  flex-shrink: 0;
}

.v2-panel-title {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.01em;
  font-family: var(--font-sans, 'Inter', sans-serif);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Horizontal divider ── */
.v2-panel-divider {
  height: 1px;
  background: var(--border);
  flex-shrink: 0;
  margin: 0;
}

/* ── CTA button area ── */
.v2-panel-cta {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 10px 6px;
  flex-shrink: 0;
}

.v2-panel-btn {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 7px 11px;
  border-radius: 6px;
  font-size: 12.5px;
  font-weight: 500;
  font-family: var(--font-sans, 'Inter', sans-serif);
  cursor: pointer;
  width: 100%;
  text-align: left;
  border: none;
  outline: none;
  transition: background 0.13s, color 0.13s, opacity 0.13s;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.v2-panel-btn--primary {
  background: var(--accent);
  color: var(--on-accent);
}
.v2-panel-btn--primary:hover,
.v2-panel-btn--primary:focus-visible {
  background: #8f2226;
}

.v2-panel-btn--ghost {
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
}
.v2-panel-btn--ghost:hover,
.v2-panel-btn--ghost:focus-visible {
  background: var(--hover);
}

/* ── Navigation list ── */
.v2-panel-nav {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 8px 0;
  flex-shrink: 0;
  list-style: none;
  margin: 0;
}

.v2-panel-nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 500;
  font-family: var(--font-sans, 'Inter', sans-serif);
  color: var(--muted);
  cursor: pointer;
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  transition: background 0.13s, color 0.13s;
  outline: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v2-panel-nav-item:hover,
.v2-panel-nav-item:focus-visible {
  background: var(--hover);
  color: var(--text);
}

/* Active state — crimson tint + left-edge indicator */
.v2-panel-nav-item--active {
  background: var(--side-active-bg);
  color: var(--accent-fg);
  font-weight: 600;
}
.v2-panel-nav-item--active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 16px;
  background: var(--accent);
  border-radius: 0 2px 2px 0;
}
.v2-panel-nav-item--active .v2-panel-nav-icon {
  opacity: 1;
}

/* Nav item icon */
.v2-panel-nav-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  opacity: 0.55;
}

/* Pending count badge on nav item */
.v2-panel-badge {
  margin-left: auto;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  font-family: var(--font-sans, 'Inter', sans-serif);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* ── Spacer pushes footer to bottom ── */
.v2-panel-spacer {
  flex: 1;
  min-height: 8px;
}

/* ── Footer (Profil + Keluar) ── */
.v2-panel-footer {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 8px 14px;
  flex-shrink: 0;
}

.v2-panel-footer-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 500;
  font-family: var(--font-sans, 'Inter', sans-serif);
  color: var(--muted);
  cursor: pointer;
  width: 100%;
  text-align: left;
  border: none;
  background: transparent;
  transition: background 0.13s, color 0.13s;
  outline: none;
}
.v2-panel-footer-btn:hover,
.v2-panel-footer-btn:focus-visible {
  background: var(--hover);
  color: var(--text);
}

.v2-panel-footer-btn--logout {
  color: var(--danger);
}
.v2-panel-footer-btn--logout:hover,
.v2-panel-footer-btn--logout:focus-visible {
  background: var(--danger-bg);
  color: var(--danger);
}

/* ── Version watermark — pinned below footer buttons ── */
.v2-panel-version {
  padding: 6px 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex-shrink: 0;
  border-top: 1px solid var(--border);
  margin-top: 4px;
}

.v2-panel-version-name,
.v2-panel-version-num {
  font-size: 11px;
  line-height: 1.4;
  color: var(--faint);
  display: block;
}

.v2-panel-version-name {
  white-space: normal;
  overflow-wrap: break-word;
}

.v2-panel-version-num {
  font-family: var(--font-mono);
  font-size: 10.5px;
  white-space: nowrap;
}


/* ============================================================
   VSM-3 — Header Migration (V2 Topbar)

   .v2-topbar is injected into .main-area (as first child, before
   the now-empty V1 .header shell) by initV2Topbar() in app.js
   ONLY when visualShellV2 === true.

   DOM migration strategy: existing elements (#sidebarToggle,
   .date-nav, .header-user-area) are physically moved from .header
   into .v2-topbar. Their IDs and all future event listeners remain
   intact — no rebinding required because initV2Topbar() runs before
   initDateControls(), initNotificationUI(), and the sidebarToggle
   click handler.

   When visualShellV2 === false: .v2-topbar does not exist in DOM;
   .header is fully functional with all its elements in place.
   Rollback (page reload with flag off) restores V1 without any
   un-migration logic.

   Breakpoints:
     ≥768px  — full row: crumb | date-nav | spacer | user-area
     ≤767px  — hamburger visible; crumb hidden
     ≤600px  — 2-row: (hamburger + spacer + user-area) / (date-nav)

   Z-index: 50 — above timeline content; below panel(115)/rail(120)/
   modals(200+)/sidebar-drawer(300+).
   ============================================================ */

/* ── Topbar container ── */
.v2-topbar {
  display: none; /* hidden by default; shown via body.v2-shell-active below */
  height: calc(56px + env(safe-area-inset-top, 0px)); /* v1.20.8 — clears notch/status bar now that viewport-fit=cover is set */
  position: sticky;
  top: 0;
  background: var(--topbar-bg, #FFFFFF);
  border-bottom: 1px solid var(--border);
  z-index: 50;
  align-items: center;
  gap: 10px;
  padding: env(safe-area-inset-top, 0px) 16px 0;
  flex-shrink: 0;
  box-sizing: border-box;
  width: 100%;
}

body.v2-shell-active .v2-topbar {
  display: flex;
}

/* ── Hamburger (#sidebarToggle) migrated into topbar ──
   V1 .sidebar-toggle CSS already hides it on desktop (min-width:768px).
   These rules are an explicit safety net for when parent-scoped V1 rules
   no longer apply after migration. ── */
.v2-topbar #sidebarToggle {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  order: 1;
}
@media (min-width: 768px) {
  .v2-topbar #sidebarToggle {
    display: none !important;
  }
}

/* ── Breadcrumb — desktop only, decorative ── */
#v2TopbarCrumb {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  font-family: var(--font-sans, 'Inter', sans-serif);
  white-space: nowrap;
  flex-shrink: 0;
  display: none; /* hidden on mobile */
  order: 2;
}
@media (min-width: 768px) {
  #v2TopbarCrumb {
    display: block;
  }
}

/* ── Date nav migrated into topbar ── */
.v2-topbar .date-nav {
  flex-shrink: 0;
  order: 3;
}

/* ── Spacer ── */
.v2-topbar-spacer {
  flex: 1;
  min-width: 0;
  order: 4;
}

/* ── User area migrated into topbar ── */
.v2-topbar .header-user-area {
  flex-shrink: 0;
  order: 5;
  display: flex;
  align-items: center;
}

/* ── Notification bell: ensure hidden on mobile inside topbar ──
   Re-states V1 BP-P2 rule in the topbar context in case parent-scoped
   V1 rules no longer apply after DOM migration. ── */
@media (max-width: 767px) {
  .v2-topbar #btnHeaderNotif {
    display: none !important;
  }
}

/* ── ≤600px: two-row compact layout (mirrors V1 BP-S6 behavior) ──
   Row 1: hamburger | spacer | user-area
   Row 2: date-nav full-width (centered)                          ── */
@media (max-width: 600px) {
  .v2-topbar {
    flex-wrap: wrap;
    height: auto;
    min-height: 44px;
    padding: 6px 12px 2px;
    gap: 4px;
    align-items: center;
  }

  /* Row 1 */
  .v2-topbar #sidebarToggle { order: 1; }
  .v2-topbar-spacer          { order: 2; }
  .v2-topbar .header-user-area { order: 3; }

  /* Row 2: date nav pushed to its own line, full width, centered */
  .v2-topbar .date-nav {
    order: 10;
    width: 100%;
    display: flex;
    justify-content: center;
    padding: 2px 0 6px;
  }

  /* Breadcrumb hidden at ≤600px — too narrow */
  #v2TopbarCrumb {
    display: none !important;
  }
}


/* ============================================================
   VSM-4 — KPI Cards

   #v2KpiStrip is injected as the first child of .main-content
   (above .timeline-date-label) by initV2KpiStrip() in app.js
   ONLY when visualShellV2 === true.

   renderKPIStrip() controls visibility (none | grid) and
   updates all four values on every data/auth change.

   Shown for: Admin, Bidang, Viewer.
   Hidden for: Driver (personal dashboard is their KPI surface),
               unauthenticated state.

   All values come from in-memory arrays (assignments, requests).
   No Firebase reads. No Firebase writes. No historical aggregation.
   No charts. No trend calculations. No utilization calculations.
   No analytics. No AI.

   VSM-4 intentionally uses operational KPIs only.
   Historical, trend, utilization, forecasting, analytics and AI
   metrics belong to v1.5+ Analytics and v1.6+ AI Assistant.

   Breakpoints:
     ≥768px   4-column grid
     ≤767px   2×2 grid
     ≤400px   single column, horizontal card layout

   When visualShellV2 === false: #v2KpiStrip is absent from DOM.
   V1 has no KPI strip. No CSS rules apply. No visual change.
   ============================================================ */

/* ── Strip container ──
   display is toggled by renderKPIStrip() via inline style.
   CSS sets grid-template-columns and gap only — not display.     */
#v2KpiStrip {
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  padding: 16px 16px 0;
  box-sizing: border-box;
}

/* ── 2×2 grid on phone ── */
@media (max-width: 767px) {
  #v2KpiStrip {
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    padding: 12px 12px 0;
  }
}

/* ── Single column on very small screens ── */
@media (max-width: 400px) {
  #v2KpiStrip {
    grid-template-columns: 1fr;
    gap: 6px;
    padding: 10px 10px 0;
  }
}

/* ── Individual KPI card ── */
.v2-kpi-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;       /* --radius-md: not in current :root — 8px hardcoded */
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  box-shadow: var(--shadow-sm);
  min-width: 0;             /* prevent flex/grid child overflow */
  overflow: hidden;
}

/* ── Horizontal layout on very small screens ── */
@media (max-width: 400px) {
  .v2-kpi-card {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    gap: 8px;
  }
}

/* ── KPI label (uppercase caption) ── */
.v2-kpi-label {
  font-size: 11px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  font-family: var(--font-sans, 'Inter', sans-serif);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
}

/* ── KPI value (large number) ──
   v1.16.2.6: converged to the app/Executive sans face (was JetBrains Mono) so
   the Driver Operations KPI strip reads as the same design generation as the
   Analytics KPI cards. tabular-nums keeps the figures column-aligned. */
.v2-kpi-value {
  font-size: 24px;
  font-weight: 700;
  line-height: 1;
  color: var(--text);
  font-family: var(--font-sans);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}

/* ── Compact value on very small screens ── */
@media (max-width: 400px) {
  .v2-kpi-value {
    font-size: 20px;
  }
}

/* ============================================================
   VSM-5 — Timeline Container

   #v2TimelineSurface wraps .timeline-date-label, .timeline-wrapper,
   and .legend as a single elevated card inside .main-content.

   Injected by initV2TimelineContainer() in app.js ONLY when
   visualShellV2 === true. When flag is false: element does not
   exist in the DOM and every rule here has zero effect.

   timeline.js is NOT modified. Document-root querySelector and
   getElementById calls work unchanged after the reparent.

   Pointer-down drag guard and all event listeners on moved
   elements are preserved — listeners bind to the element
   object, not to DOM position.

   Specificity: all rules scoped under body.v2-shell-active
   (≥ 0-2-0) beats style.css base rules (0-1-0 to 0-1-1).
   padding-bottom on .main-content is never overridden so the
   BP-S5 mobile nav clearance calc() remains active.
   ============================================================ */

/* ── .main-content padding — top/sides only ──
   KPI strip owns top spacing (padding: 16px 16px 0 from VSM-4).
   padding-bottom left untouched → BP-S5 mobile calc preserved. */
body.v2-shell-active .main-content {
  padding-top: 0;
  padding-left: 16px;
  padding-right: 16px;
}

/* ── Surface card — single elevated timeline container ── */
body.v2-shell-active #v2TimelineSurface {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-md);
  overflow: hidden;           /* clips border-radius; does NOT create scroll ctx */
  margin-top: 16px;           /* gap between KPI strip and timeline */
  min-height: 420px;          /* timeline remains dominant even with sparse data */
  display: flex;
  flex-direction: column;
}

/* ── Date label — V2 card header ── */
body.v2-shell-active #v2TimelineSurface .timeline-date-label {
  padding: 14px 16px 12px;
  margin-bottom: 0;           /* override style.css margin-bottom: 10px */
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.01em;
  line-height: 1.3;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  flex-shrink: 0;
}

/* ── Timeline wrapper — surface owns elevation, wrapper owns scroll ── */
body.v2-shell-active #v2TimelineSurface .timeline-wrapper {
  background: var(--surface);
  border-radius: 0;           /* surface owns border-radius */
  box-shadow: none;           /* surface owns elevation */
  overflow-x: auto;           /* horizontal scroll preserved — unchanged */
  flex: 1;                    /* grows to fill remaining height in surface */
  min-height: 0;              /* flex shrink without overflow */
}

/* ── Legend — V2 footer strip ── */
body.v2-shell-active #v2TimelineSurface .legend {
  background: var(--surface-2, #FBFAF8);
  border: none;
  border-top: 1px solid var(--border);
  border-radius: 0;
  padding: 8px 16px;
  margin-top: 0;              /* override any style.css margin-top */
  gap: 8px;
  flex-shrink: 0;
}

/* ── Legend items — pill / chip style ── */
body.v2-shell-active #v2TimelineSurface .legend-item {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 3px 10px 3px 8px;
  font-size: 12px;
  font-weight: 500;
  color: var(--text);
  gap: 5px;
}

/* Circular swatch — inherits --swatch from .legend-item[data-vehicle="*"] rules */
body.v2-shell-active #v2TimelineSurface .legend-item::before {
  content: '';                 /* explicit — guarantees pseudo-element renders */
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--swatch);  /* overrides hardcoded V1 hex from style.css */
}

/* ── Mobile ≤767px ── */
@media (max-width: 767px) {
  body.v2-shell-active .main-content {
    padding-left: 8px;
    padding-right: 8px;
  }

  body.v2-shell-active #v2TimelineSurface {
    border-radius: 8px;
    margin-top: 12px;
    min-height: 320px;
  }

  body.v2-shell-active #v2TimelineSurface .timeline-date-label {
    padding: 10px 12px 8px;
    font-size: 14px;
  }

  body.v2-shell-active #v2TimelineSurface .legend {
    padding: 6px 12px;
    gap: 5px;
  }

  body.v2-shell-active #v2TimelineSurface .legend-item {
    font-size: 11px;
    padding: 2px 8px 2px 6px;
  }
}

/* ── Small phone ≤600px ── */
@media (max-width: 600px) {
  body.v2-shell-active #v2TimelineSurface {
    border-radius: 6px;
    margin-top: 10px;
    min-height: 280px;
  }

  body.v2-shell-active #v2TimelineSurface .timeline-date-label {
    font-size: 13px;
    padding: 8px 10px 6px;
  }
}

/* ============================================================
   VSM-5B — Dashboard Modernization

   All rules here are purely additive. Every selector is either:
     (a) scoped under body.v2-shell-active so it has zero effect
         when visualShellV2 === false, or
     (b) an attribute selector on an element that only exists in
         the DOM when the flag is on.

   No existing rules in this file are modified here — Gap 5
   (legend swatch) was patched inline in the VSM-5 block above.

   Gaps implemented:
     1  KPI card semantic accent bars (data-kpi selectors)
     2  .v2-tl-header card header for timeline surface
     3  #v2DashHeader section label above KPI strip
     4  Driver dashboard V2 token overrides
     7  KPI 2×2 grid at 768–900px tablet breakpoint
   ============================================================ */


/* ── Gap 3: Section label above KPI strip ──────────────────────
   #v2DashHeader is injected by initV2KpiStrip() only when the
   flag is on. .p-shead (already defined above) provides flex
   layout, gap, and child typography. This rule adds vertical
   positioning within .main-content.                            */
#v2DashHeader {
  padding: 14px 16px 0;
  box-sizing: border-box;
}

@media (max-width: 767px) {
  #v2DashHeader { padding: 12px 8px 0; }
}


/* ── Gap 1: KPI card semantic left-edge accent bars ────────────
   .v2-kpi-card[data-kpi="*"] specificity (0-2-0) beats
   .v2-kpi-card (0-1-0) so border-left overrides correctly.
   Left-edge bar is consistent with .notif-priority-* and the
   .v2-panel-nav-item--active::before patterns already in use.  */
.v2-kpi-card[data-kpi="aktif"]   { border-left: 3px solid var(--info);          }
.v2-kpi-card[data-kpi="menunggu"]{ border-left: 3px solid var(--warn);          }
.v2-kpi-card[data-kpi="selesai"] { border-left: 3px solid var(--ok);            }
.v2-kpi-card[data-kpi="bertugas"]{ border-left: 3px solid var(--border-strong); }

/* Value colour — semantic accent when metric is meaningful */
.v2-kpi-card[data-kpi="aktif"]    .v2-kpi-value { color: var(--info);  }
.v2-kpi-card[data-kpi="menunggu"] .v2-kpi-value { color: var(--warn);  }
.v2-kpi-card[data-kpi="selesai"]  .v2-kpi-value { color: var(--ok);    }
/* bertugas stays --text (neutral) */


/* ── Gap 7: KPI 2×2 grid at 768–900px tablet ───────────────────
   At 768px content width = 486px; 4-column cards are ~101px wide.
   "Selesai Hari Ini" label wraps at that width. Switch to 2×2 at
   the 768–900px window for readable, breathable cards.
   #v2KpiStrip only exists in DOM when flag is on → no V1 impact. */
@media (min-width: 768px) and (max-width: 900px) {
  #v2KpiStrip {
    grid-template-columns: repeat(2, 1fr);
    gap: 10px;
  }
}


/* ── Gap 2: .v2-tl-header — timeline card header row ───────────
   Replaces the single .timeline-date-label card header with a
   two-part row: "Timeline Board" title (left) + date badge (right).

   IMPORTANT: #timelineDateLabel keeps its ID and is moved (not
   cloned) inside .v2-tl-header. All timeline.js textContent
   updates targeting getElementById('timelineDateLabel') continue
   to work — DOM position does not affect getElementById.

   The old VSM-5 rule for .timeline-date-label set padding,
   border-bottom and background directly on the date element.
   Those properties now belong to .v2-tl-header (the container).
   The overrides below use higher specificity (1-3-2 > 1-2-2)
   to reset the old date-label styles.                           */

/* Header row — owns the border-bottom and padding that the
   old .timeline-date-label rule previously carried.            */
body.v2-shell-active #v2TimelineSurface .v2-tl-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 16px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

/* "Timeline Board" — section label, left side */
body.v2-shell-active #v2TimelineSurface .v2-tl-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  flex-shrink: 0;
}

/* Date badge — right side; resets old full-width header styling */
body.v2-shell-active #v2TimelineSurface .v2-tl-header .timeline-date-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
  line-height: 1.3;
  /* Reset VSM-5 full-width header properties */
  padding: 0;
  margin-bottom: 0;
  border-bottom: none;
  background: transparent;
  flex: 1;
  text-align: right;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
  min-width: 0;
}

/* Mobile ≤767px: tighter header padding */
@media (max-width: 767px) {
  body.v2-shell-active #v2TimelineSurface .v2-tl-header {
    padding: 10px 12px;
  }
  body.v2-shell-active #v2TimelineSurface .v2-tl-title {
    font-size: 11px;
  }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header .timeline-date-label {
    font-size: 12px;
    padding: 0;
  }
}

/* Small phone ≤600px */
@media (max-width: 600px) {
  body.v2-shell-active #v2TimelineSurface .v2-tl-header {
    padding: 8px 10px;
  }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header .timeline-date-label {
    font-size: 11px;
  }
}


/* ── Gap 4: Driver dashboard V2 token overrides ────────────────
   All rules scoped to body.v2-shell-active #driverDashboard so
   they are inert when visualShellV2 === false.

   driver-dashboard.js is NOT modified. CSS targets the V1 class
   names it generates. Specificity (body 0-1-0 + #driverDashboard
   1-0-0 + .dash-card 0-1-0 = 1-2-0) beats style.css base rules
   (0-1-0) on every property.

   Audit of driver-dashboard.js class output:
     .dash-section, .dash-section--active
     .dash-section-title
     .dash-card, .dash-card--{assigned|started|completed|muted}
     .dash-card-header, .dash-card-title, .dash-card-destination
     .dash-card-meta, .dash-meta-item, .dash-card-pic
     .badge-status, .badge-status--{pending|approved|assigned|started|completed}
     .vehicle-badge  (background from inline style — not overridable via token)
     .dash-empty                                                */

/* Dashboard wrapper */
body.v2-shell-active #driverDashboard {
  padding: 16px 0 80px;
  max-width: none;
}

/* Section wrapper */
body.v2-shell-active #driverDashboard .dash-section {
  margin-bottom: 24px;
}

/* Section title — aligns with .p-shead aesthetic */
body.v2-shell-active #driverDashboard .dash-section-title {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
  margin-bottom: 10px;
}
/* Active section (Berlangsung Sekarang) title accent */
body.v2-shell-active #driverDashboard .dash-section--active .dash-section-title {
  color: var(--warn);
}
/* Horizontal rule after section title — inherits V1 ::after, just update colour */
body.v2-shell-active #driverDashboard .dash-section-title::after {
  background: var(--border);
}

/* Card base */
body.v2-shell-active #driverDashboard .dash-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow-sm);
  padding: 12px 14px;
  margin-bottom: 8px;
  transition: box-shadow 0.14s, border-color 0.14s, transform 0.1s;
}
body.v2-shell-active #driverDashboard .dash-card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--border-strong);
}

/* Active assignment card — warm left-edge accent */
body.v2-shell-active #driverDashboard .dash-card--started {
  border-left: 3px solid var(--warn);
  background: var(--warn-bg);
}

/* Muted history cards */
body.v2-shell-active #driverDashboard .dash-card--muted {
  opacity: 0.78;
}

/* Card inner typography */
body.v2-shell-active #driverDashboard .dash-card-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 4px;
}
body.v2-shell-active #driverDashboard .dash-card-destination {
  font-size: 12.5px;
  color: var(--muted);
  margin-bottom: 5px;
}
body.v2-shell-active #driverDashboard .dash-meta-item {
  font-size: 11.5px;
  color: var(--faint);
}
body.v2-shell-active #driverDashboard .dash-card-pic {
  font-size: 11.5px;
  color: var(--faint);
  margin-top: 4px;
}

/* Empty state */
body.v2-shell-active #driverDashboard .dash-empty {
  color: var(--faint);
  font-size: 12.5px;
  font-style: normal;
  padding: 16px 0;
}

/* Status badges — replace V1 hardcoded hex with V2 semantic tokens */
body.v2-shell-active #driverDashboard .badge-status {
  font-size: 10.5px;
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 20px;
  line-height: 1;
  border: 1px solid transparent;
}
body.v2-shell-active #driverDashboard .badge-status--pending {
  background: var(--warn-bg);
  color: var(--warn);
  border-color: rgba(148,100,32,.22);
}
body.v2-shell-active #driverDashboard .badge-status--approved {
  background: var(--info-bg);
  color: var(--info);
  border-color: rgba(59,91,169,.22);
}
body.v2-shell-active #driverDashboard .badge-status--assigned {
  background: var(--ok-bg);
  color: var(--ok);
  border-color: rgba(47,125,98,.22);
}
body.v2-shell-active #driverDashboard .badge-status--started {
  background: var(--warn-bg);
  color: var(--warn);
  border-color: rgba(148,100,32,.22);
}
body.v2-shell-active #driverDashboard .badge-status--completed {
  background: var(--hover);
  color: var(--muted);
  border-color: var(--border-strong);
}
body.v2-shell-active #driverDashboard .badge-status--cancelled {
  background: var(--danger-bg);
  color: var(--danger);
  border-color: color-mix(in srgb, var(--accent) 22%, transparent);
}

/* Vehicle badge — background is inline style (JS-controlled).
   Override only shape/typography; leave background untouched.  */
body.v2-shell-active #driverDashboard .vehicle-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 20px;
  color: #ffffff;
  letter-spacing: 0.01em;
}


/* ============================================================
   VSM-5C — Claude Design Component Parity

   Objective: close the visual gap between current V2 shell and
   the approved Claude Design reference to ≤ 10%.

   All rules are either:
     (a) scoped under body.v2-shell-active so they are inert
         when visualShellV2 === false, or
     (b) attribute/class selectors on elements that only exist
         in the V2 DOM.

   Parts implemented:
     1  Rail — light surface theme
     2  Context Panel — wider + more breathing room
     3  KPI cards — icon + subtitle + larger value
     4  Topbar — visual-only search field
     5  Topbar — user avatar circle
     6  Timeline header — Papan Jadwal + view toggle
     7  Driver row — avatar initials via ::before
     8  Assignment cards — tighter, cleaner visual polish
   ============================================================ */


/* ── Part 1: Rail — light surface theme ────────────────────────
   Overrides the VSM-1 dark charcoal background. Rail now reads
   as a premium surface panel instead of a dark sidebar remnant.
   All selectors beat their VSM-1 counterparts by load order
   (later in file = wins on equal specificity).               */

.v2-rail {
  background: var(--surface);
  border-right: 1px solid var(--border);
}

/* Crest (logo button) — hover on light bg */
.v2-rail-crest:hover,
.v2-rail-crest:focus-visible {
  background: var(--hover);
}

/* Module icon — inactive: muted dark, active: accent crimson */
.v2-rail-item {
  color: var(--muted);
}
.v2-rail-item:hover,
.v2-rail-item:focus-visible {
  background: var(--hover);
  color: var(--text);
}

/* Active module — light crimson tint + icon in accent */
.v2-rail-item--active {
  color: var(--accent-fg);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.v2-rail-item--active:hover,
.v2-rail-item--active:focus-visible {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent-fg);
}

/* Active indicator bar stays crimson */
.v2-rail-item--active::before {
  background: var(--accent);
}

/* Tooltip on light rail — already uses --surface/--border, unchanged */

/* Avatar circle — crimson reads well on both light and dark bgs */
.v2-rail-avatar {
  background: var(--accent);
}


/* ── Part 2: Context Panel — wider + more breathing room ───────
   Width 218px → 240px. margin-left on main-area updates in step.
   Internal spacing increases for visual breathing room.        */

.v2-panel {
  width: 240px;
}

@media (min-width: 768px) {
  body.v2-shell-active .main-area {
    margin-left: 304px; /* 64px rail + 240px panel */
  }
}

/* More generous header padding */
.v2-panel-header {
  padding: 20px 16px 12px;
}

/* CTA area: more vertical gap between buttons */
.v2-panel-cta {
  padding: 10px 12px 8px;
  gap: 8px;
}

/* Nav list: consistent padding */
.v2-panel-nav {
  padding: 8px 10px 0;
  gap: 2px;
}

/* Footer: more top breathing room */
.v2-panel-footer {
  padding: 8px 10px 16px;
}


/* ── Part 3: KPI cards — icon + subtitle + larger value ────────
   Adds .v2-kpi-icon and .v2-kpi-sub inside each card.
   Card layout: icon → label → value → subtitle (column).      */

/* Card: slightly more padding, tighter internal gap */
.v2-kpi-card {
  padding: 14px 16px;
  gap: 2px;
}

/* Icon container — small rounded square tinted by card type */
.v2-kpi-icon {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-bottom: 6px;
}
.v2-kpi-card[data-kpi="aktif"]    .v2-kpi-icon { background: var(--info-bg);   color: var(--info);   }
.v2-kpi-card[data-kpi="bertugas"] .v2-kpi-icon { background: var(--hover);     color: var(--muted);  }
.v2-kpi-card[data-kpi="menunggu"] .v2-kpi-icon { background: var(--warn-bg);   color: var(--warn);   }
.v2-kpi-card[data-kpi="selesai"]  .v2-kpi-icon { background: var(--ok-bg);     color: var(--ok);     }

/* Label stays small + uppercase (unchanged) */

/* Value: significantly larger for impact */
.v2-kpi-value {
  font-size: 32px;
  line-height: 1;
  margin-top: 2px;
}

/* Subtitle — muted context line below value */
.v2-kpi-sub {
  font-size: 11px;
  color: var(--faint);
  font-weight: 400;
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Horizontal layout on very small screens: subtitle hidden */
@media (max-width: 400px) {
  .v2-kpi-icon  { width: 24px; height: 24px; margin-bottom: 4px; }
  .v2-kpi-value { font-size: 24px; }
  .v2-kpi-sub   { display: none; }
}


/* ── Part 4: Topbar — visual-only search field ─────────────────
   Purely decorative. pointer-events: none. No handlers.
   Visible ≥ 1024px only to preserve topbar space on tablet.   */

.v2-topbar-search {
  display: none;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 8px;
  color: var(--faint);
  font-size: 12.5px;
  flex-shrink: 0;
  min-width: 160px;
  pointer-events: none;
  user-select: none;
  order: 30; /* between crumb (20) and date-nav (40) — see order renumbering below */
  cursor: default;
}
/* Phase 11N: was ≥1024px — but #v2TopbarCrumb, .date-nav, and
   .header-user-area (the profile avatar/name card) are ALL flex-shrink:0
   with no wrap on .v2-topbar, so the only thing that could absorb space
   was the spacer. At 1024-1279px ("laptop") their combined natural width
   already exceeds what's left after the rail nav, so turning on this
   field — decorative only, pointer-events:none, no handlers, the single
   most expendable element in the row — right at 1024px pushed the
   avatar/notif/theme controls off the right edge, clipped by an ancestor's
   overflow. Deferring it to ≥1280px (where Part 9's own comment already
   states "all elements visible" was the original design assumption)
   directly frees that space without touching anything load-bearing. */
@media (min-width: 1280px) {
  .v2-topbar-search { display: flex; }
}
.v2-topbar-search-placeholder {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v2-topbar-search svg { flex-shrink: 0; }

/* Renumber topbar flex orders to accommodate search field (order 30) */
#v2TopbarCrumb                        { order: 20; }
body.v2-shell-active .v2-topbar .date-nav    { order: 40; }
body.v2-shell-active .v2-topbar-spacer       { order: 50; }
body.v2-shell-active .v2-topbar .header-user-area { order: 60; }

/* Mobile ≤600px: search always hidden */
@media (max-width: 600px) {
  .v2-topbar-search { display: none !important; }
}


/* ── Part 5: Topbar — user avatar circle ───────────────────────
   #v2TopbarAvatar is injected into .header-user-area by
   initV2Topbar(). Initials are stamped by updatePermissionUI(). */

.v2-topbar-avatar {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--accent);
  color: #ffffff;
  font-size: 10px;
  font-weight: 700;
  font-family: var(--font-sans, 'Inter', sans-serif);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  user-select: none;
  margin-right: 4px;
}


/* ── Part 6: Timeline header — Papan Jadwal + view toggle ──────
   Replaces the VSM-5B two-element row with a semantic left/right
   group structure. VSM-5B date label resets still apply via
   cascade; additional overrides here handle the new nesting.   */

/* Header container: slightly tighter */
body.v2-shell-active #v2TimelineSurface .v2-tl-header {
  padding: 10px 16px;
  align-items: center;
}

/* Left group: title + subtitle stacked */
body.v2-shell-active #v2TimelineSurface .v2-tl-header-left {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex-shrink: 0;
}

/* Title: prominent */
body.v2-shell-active #v2TimelineSurface .v2-tl-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.01em;
  text-transform: none; /* override VSM-5B uppercase */
}

/* Subtitle under title */
body.v2-shell-active #v2TimelineSurface .v2-tl-sub {
  font-size: 11px;
  color: var(--faint);
  font-weight: 400;
  white-space: nowrap;
}

/* Right group: view toggle + date label inline */
body.v2-shell-active #v2TimelineSurface .v2-tl-header-right {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
}

/* Hotfix: board-native create-assignment CTA — reuses .v2-panel-btn's
   colors/hover/press states verbatim for visual consistency with the
   panel's own CTAs, but that class is written for a vertical, full-width
   panel list (width:100%), which would be wrong inline in this row. */
body.v2-shell-active #v2TimelineSurface .v2-tl-header-right .v2-tl-header-cta {
  width: auto;
  flex-shrink: 0;
}
@media (max-width: 767px) {
  /* Mobile FAB already covers this action — avoid a duplicate control. */
  .v2-tl-header-cta { display: none !important; }
}

/* View toggle pill container */
body.v2-shell-active #v2TimelineSurface .v2-tl-view-toggle {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 2px;
  flex-shrink: 0;
}

/* View toggle buttons */
body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn {
  padding: 4px 10px;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 500;
  font-family: inherit;
  cursor: default;
  border: none;
  background: transparent;
  color: var(--muted);
  line-height: 1.4;
  white-space: nowrap;
}
body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn--active {
  background: var(--surface);
  color: var(--text);
  font-weight: 600;
  box-shadow: var(--shadow-sm);
  cursor: default;
}
body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn:disabled {
  opacity: 0.42;
}

/* Date label inside .v2-tl-header-right — reset VSM-5B flex:1 */
body.v2-shell-active #v2TimelineSurface .v2-tl-header-right .timeline-date-label {
  flex: none;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  text-align: right;
  padding: 0;
  border-bottom: none;
  background: transparent;
  white-space: nowrap;
}

/* Mobile ≤767px: hide subtitle and view toggle to save space */
@media (max-width: 767px) {
  body.v2-shell-active #v2TimelineSurface .v2-tl-sub          { display: none; }
  /* V1 Redesign 4c (P0, code-confirmed gap): this used to be display:none,
     which hid the ONLY control that reaches List view on mobile — List view
     itself was already fully built and is more touch-appropriate than a
     horizontally-scrolling hour grid, it just had no way to be reached.
     Made visible and more compact instead of hidden. */
  body.v2-shell-active #v2TimelineSurface .v2-tl-view-toggle  {
    display: flex;
  }
  body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn {
    padding: 4px 8px;
    font-size: 11px;
  }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header       { padding: 10px 12px; }
  body.v2-shell-active #v2TimelineSurface .v2-tl-title        { font-size: 13px; }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header-right .timeline-date-label {
    font-size: 11px;
  }
}

/* Smallest phones: title + toggle + date don't fit on one row even at
   their most compact (measured overflow at 320px viewport) — wrap onto a
   second row instead of continuing to shrink text past legibility. */
@media (max-width: 380px) {
  body.v2-shell-active #v2TimelineSurface .v2-tl-title        { font-size: 12px; }
  body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn      { padding: 4px 6px; font-size: 10.5px; }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header-right .timeline-date-label {
    font-size: 10px;
  }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header {
    flex-wrap: wrap;
    row-gap: 6px;
  }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header-right {
    flex-basis: 100%;
    justify-content: space-between;
    gap: 6px;
  }
}


/* ── Part 7: Driver row — avatar initials ──────────────────────
   initV2DriverAvatars() stamps data-initials on .driver-label
   via MutationObserver after every renderDriverRows() call.
   CSS ::before renders the avatar; no timeline.js changes.

   Layout: CSS Grid — avatar spans both rows, name + phone stack
   on the right. Fits within the existing --driver-col: 110px.  */

body.v2-shell-active .driver-label {
  display: grid;
  grid-template-columns: 26px 1fr;
  column-gap: 6px;
  align-content: center;
  padding: 6px 8px;
  background: var(--surface);
  border-right: 1px solid var(--border);
}

/* Avatar circle rendered from data-initials attribute */
body.v2-shell-active .driver-label::before {
  content: attr(data-initials);
  grid-column: 1;
  grid-row: 1 / 3;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent);
  color: #ffffff;
  font-size: 9px;
  font-weight: 700;
  font-family: var(--font-sans, 'Inter', sans-serif);
  letter-spacing: 0.04em;
  display: grid;
  place-items: center;
  align-self: center;
  flex-shrink: 0;
  user-select: none;
}

/* Name + phone: explicitly placed in grid column 2 */
body.v2-shell-active .driver-label .driver-name {
  grid-column: 2;
  grid-row: 1;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body.v2-shell-active .driver-label .driver-phone {
  grid-column: 2;
  grid-row: 2;
  font-size: 9.5px;
  color: var(--faint);
  font-family: var(--font-mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Mobile ≤600px: avatar hidden, label returns to single column */
@media (max-width: 600px) {
  body.v2-shell-active .driver-label {
    grid-template-columns: 1fr;
    padding: 4px 6px;
  }
  body.v2-shell-active .driver-label::before {
    display: none;
  }
  body.v2-shell-active .driver-label .driver-name {
    grid-column: 1;
    grid-row: 1;
  }
  body.v2-shell-active .driver-label .driver-phone {
    grid-column: 1;
    grid-row: 2;
  }
}


/* ── Part 8: Assignment cards — visual polish ──────────────────
   DO NOT touch: left, width, background, top, height, z-index.
   Those are engine-generated values — they remain unchanged.
   Only: radius, shadow, padding, typography.                  */

body.v2-shell-active .assignment-block {
  border-radius: 6px;
  padding: 4px 8px;
  box-shadow:
    0 1px 3px rgba(0, 0, 0, 0.20),
    inset 0 1px 0 rgba(255, 255, 255, 0.18);
}

body.v2-shell-active .assignment-block:hover {
  box-shadow:
    0 3px 10px rgba(0, 0, 0, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.22);
}

/* Time label: slightly smaller, higher opacity */
body.v2-shell-active .block-time {
  font-family: var(--font-mono);
  font-size: 9px;
  font-weight: 500;
  opacity: 0.82;
  letter-spacing: 0.01em;
}

/* Purpose: stronger hierarchy */
body.v2-shell-active .block-purpose {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* Status badge: tighter */
body.v2-shell-active .block-status-badge {
  font-size: 8.5px;
  padding: 1px 4px;
  border-radius: 3px;
  font-weight: 700;
}


/* ============================================================
   VSM-6 — Motion & Micro Interactions

   Scope: visual-only, all platform behaviour unchanged.

   Guard strategy:
   - Motion TOKENS (Part 1) are defined in :root — universal,
     zero side-effects when no element references them.
   - Transitions on .v2-* elements are inert until those
     elements exist in the DOM (flag on only).
   - Modal animations are scoped under body.v2-shell-active,
     so V1 modals receive no animation when flag is off.
   - prefers-reduced-motion (Part 9) zeroes all durations,
     disabling every animation in one block.

   DO NOT touch: timeline positioning, Firebase, routing,
   business logic, modal open/close logic, role gates.
   ============================================================ */


/* ── Part 1: Motion tokens ─────────────────────────────────────
   Enterprise-grade easing: fast start, gentle deceleration.
   No bounce, no overshoot. Safe to add to :root — unused
   tokens have zero rendering cost.                            */

:root {
  /* Phase 8.1 — --motion-fast/--motion-slow removed here: they were
     byte-identical duplicates of style.css's :root tokens of the same
     name (style.css loads first and is now the sole source for both).
     --motion-normal is aliased rather than removed, since ~30 call
     sites in this file reference it by this name — value is unchanged
     (200ms), only its origin is now a reference instead of an
     independent literal. --motion-ease is untouched: it's a genuinely
     different curve from style.css's --ease-standard/--ease-decelerate,
     not a duplicate. */
  --motion-normal: var(--motion-base, 200ms);
  /* Ease-out expo variant — snappy entry, gentle rest.
     Used across all VSM-6 transitions and animations.        */
  --motion-ease:   cubic-bezier(0.16, 1, 0.3, 1);
}


/* ── VSM-6 keyframes ───────────────────────────────────────────
   Named with vsm6- prefix to avoid collision with existing
   ls-fade, ls-pop keyframes.                                 */

/* Modal overlay: simple opacity fade */
@keyframes vsm6-overlay-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Modal box: subtle upward slide + scale — enterprise, not theatrical */
@keyframes vsm6-box-in {
  from {
    opacity:   0;
    transform: scale(0.98) translateY(6px);
  }
  to {
    opacity:   1;
    transform: scale(1)    translateY(0);
  }
}

/* Notification dot: single gentle opacity pulse on appearance */
@keyframes vsm6-dot-pulse {
  0%, 60%, 100% { opacity: 1;   }
  30%           { opacity: 0.4; }
}


/* ── Part 2: Rail interactions ─────────────────────────────────
   Enhances existing VSM-5C hover rules with smooth transitions.
   No layout shifts, no size changes.                          */

/* Base transitions on all rail interactive elements */
.v2-rail-crest {
  transition:
    background var(--motion-fast) var(--motion-ease);
}

.v2-rail-item {
  transition:
    background var(--motion-fast) var(--motion-ease),
    color      var(--motion-fast) var(--motion-ease);
}

.v2-rail-avatar {
  transition: opacity var(--motion-fast) var(--motion-ease);
}

/* Active indicator bar: animate height from 0 on base state
   so the bar "grows" when module becomes active              */
.v2-rail-item::before {
  /* Pseudo-element present on all items; only visible when active.
     Transition plays when height changes via --active class.  */
  transition: height var(--motion-normal) var(--motion-ease);
}


/* ── Part 3: Panel interactions ────────────────────────────────
   Nav items, CTAs, footer buttons — all get smooth state      */

.v2-panel-nav-item {
  transition:
    background var(--motion-fast) var(--motion-ease),
    color      var(--motion-fast) var(--motion-ease);
}

.v2-panel-btn {
  transition:
    background var(--motion-fast) var(--motion-ease),
    color      var(--motion-fast) var(--motion-ease),
    filter     var(--motion-fast) var(--motion-ease),
    transform  var(--motion-fast) var(--motion-ease);
}

.v2-panel-footer-btn {
  transition:
    background var(--motion-fast) var(--motion-ease),
    color      var(--motion-fast) var(--motion-ease);
}

/* Primary CTA press state — tactile feedback without glow */
body.v2-shell-active .v2-panel-btn--primary:active {
  filter: brightness(0.86);
  transform: scale(0.99);
}

/* Ghost CTA press */
body.v2-shell-active .v2-panel-btn--ghost:active {
  filter: brightness(0.96);
}


/* ── Part 4: KPI card hover lift ───────────────────────────────
   Scoped to pointer devices — no sticky-hover on touch screens.
   translateY(-2px) is visual-only; layout is unaffected.      */

body.v2-shell-active .v2-kpi-card {
  transition:
    transform    var(--motion-normal) var(--motion-ease),
    box-shadow   var(--motion-normal) var(--motion-ease),
    border-color var(--motion-fast)   var(--motion-ease);
}

@media (hover: hover) {
  body.v2-shell-active .v2-kpi-card:hover {
    transform:    translateY(-2px);
    box-shadow:   var(--shadow-md);
    border-color: var(--border-strong);
  }
}


/* ── Part 5: Assignment card hover ─────────────────────────────
   Extends VSM-5C box-shadow change with a smooth transition
   and a 1px visual lift. left/width/top untouched.            */

body.v2-shell-active .assignment-block {
  transition:
    filter     var(--motion-fast) var(--motion-ease),
    box-shadow var(--motion-fast) var(--motion-ease),
    transform  var(--motion-fast) var(--motion-ease);
}

@media (hover: hover) {
  body.v2-shell-active .assignment-block:hover {
    transform: translateY(-1px);
  }
}


/* ── Parts 6 + 7: Modal entry animation ───────────────────────
   Fires automatically when display changes none → flex.
   Only active under body.v2-shell-active — V1 modals unchanged.

   Close remains instant (no JS changes). Entry polish is the
   primary gain.

   Excluded intentionally:
   - .login-screen  — has its own ls-fade animation             */

body.v2-shell-active .modal-overlay {
  animation: vsm6-overlay-in var(--motion-normal) var(--motion-ease);
}

body.v2-shell-active .modal-box {
  animation: vsm6-box-in var(--motion-normal) var(--motion-ease);
}

/* Motion redesign (v1.31.23.0) — audit F-07. ≤600px every .modal-box is a
   bottom sheet (style.css "MODALS — bottom sheet"), which declares its own
   slide-up entrance — but the rule above (0,2,1) out-specified it, so on
   phones the sheet popped in place (scale .98) instead of rising from the
   edge it is anchored to. Restore the sheet's own motion here, at the same
   specificity and later in the cascade. */
@media (max-width: 600px) {
  body.v2-shell-active .modal-box {
    animation: vsm6-sheet-in var(--motion-sheet, 260ms) var(--motion-ease);
  }
}
@keyframes vsm6-sheet-in {
  from { transform: translateY(100%); }
  to   { transform: translateY(0); }
}

/* Motion redesign (v1.31.23.0) — audit F-08: legacy modals now animate OUT
   too. js/ui/modal-motion.js hides the real overlay synchronously (so every
   "closed" check stays exact) and plays the exit on an inert, aria-hidden
   copy carrying .modal-exit-ghost, removed on animationend. Exit is ~80% of
   the entrance and accelerates away (--ease-exit), the usual rule for
   leaving motion. `html body` prefix: must beat the (0,2,1) entrance rules
   above regardless of order. */
html body .modal-overlay.modal-exit-ghost {
  pointer-events: none;
  animation: vsm6-overlay-out 160ms var(--ease-exit, cubic-bezier(.4,0,1,1)) forwards;
}
html body .modal-exit-ghost .modal-box,
html body .modal-exit-ghost .odo-modal {
  animation: vsm6-box-out 160ms var(--ease-exit, cubic-bezier(.4,0,1,1)) forwards;
}
@media (max-width: 600px) {
  /* the scrim must outlast the sheet's slide, or the sheet (a child)
     fades with it before it has left the screen */
  html body .modal-overlay.modal-exit-ghost { animation-duration: 200ms; }
  html body .modal-exit-ghost .modal-box {
    animation: vsm6-sheet-out 200ms var(--ease-exit, cubic-bezier(.4,0,1,1)) forwards;
  }
}
/* Motion redesign (v1.31.23.0) — audit F-10: mobile drawer + "Lainnya"
   sheet backdrops fade with their panels instead of popping. Entrance: a
   keyframe on the visible class (display:none → block restarts it). Exit:
   js/ui/modal-motion.js fadeOutGhost() — the real backdrop is removed at
   once, an inert copy fades. */
.sidebar-overlay.overlay-visible,
.bottom-sheet-overlay.bottom-sheet-visible {
  animation: vsm6-overlay-in var(--motion-sheet, 260ms) var(--ease-decelerate, ease-out);
}
/* doubled class: the copy still carries .overlay-visible / .bottom-sheet-visible,
   whose (0,2,0) entrance rule must lose to this exit */
html body .overlay-exit-ghost.overlay-exit-ghost {
  display: block !important;
  pointer-events: none !important;
  animation: vsm6-overlay-out var(--motion-base, 200ms) var(--ease-exit, cubic-bezier(.4,0,1,1)) forwards;
}
@media (prefers-reduced-motion: reduce) {
  .sidebar-overlay.overlay-visible,
  .bottom-sheet-overlay.bottom-sheet-visible { animation: none; }
}
[data-anim="off"] .sidebar-overlay.overlay-visible,
[data-anim="off"] .bottom-sheet-overlay.bottom-sheet-visible { animation: none; }

/* Motion redesign (v1.31.23.0) — module dialogs rendered by innerHTML
   (Gudang, Engineering, Overtime; Petty Cash exit only). js/ui/modal-motion.js
   trackModuleDialogs() adds .dlg-enter ONLY on the render where a dialog
   first appears (realtime re-renders never replay it) and re-inserts the last
   snapshot as an inert .dlg-exit copy when it disappears. Same vocabulary as
   the legacy modals: scrim fade + box rise in, shorter fade + settle out.
   !important on the exit: Petty Cash sets its entrance animation inline. */
.dlg-enter { animation: vsm6-overlay-in var(--motion-base, 200ms) var(--motion-ease, ease) both; }
.dlg-enter > * { animation: vsm6-box-in var(--motion-base, 200ms) var(--motion-ease, ease) both; }
html body .dlg-exit {
  pointer-events: none !important;
  animation: vsm6-overlay-out 160ms var(--ease-exit, cubic-bezier(.4,0,1,1)) forwards !important;
}
html body .dlg-exit > * { animation: vsm6-box-out 160ms var(--ease-exit, cubic-bezier(.4,0,1,1)) forwards !important; }
@media (prefers-reduced-motion: reduce) {
  .dlg-enter, .dlg-enter > * { animation: none; }
}
[data-anim="off"] .dlg-enter, [data-anim="off"] .dlg-enter > * { animation: none; }

@keyframes vsm6-overlay-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}
@keyframes vsm6-box-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: scale(0.985) translateY(4px); }
}
@keyframes vsm6-sheet-out {
  from { transform: translateY(0); }
  to   { transform: translateY(100%); }
}

/* Odometer modal uses .odo-modal, not .modal-box. Give it the
   same entry feel via the overlay animation (overlay is enough). */


/* ── Part 8: Topbar polish ─────────────────────────────────────
   Hover states and badge animation. No layout changes.        */

/* Date navigation buttons */
body.v2-shell-active .v2-topbar .btn-today,
body.v2-shell-active .v2-topbar .btn-icon {
  transition:
    background   var(--motion-fast) var(--motion-ease),
    color        var(--motion-fast) var(--motion-ease),
    border-color var(--motion-fast) var(--motion-ease);
}

/* Notification bell */
.header-notif-btn {
  transition:
    background var(--motion-fast) var(--motion-ease),
    color      var(--motion-fast) var(--motion-ease);
}

/* Unread dot: single gentle pulse when it becomes visible.
   Plays once on display:none → visible transition.           */
body.v2-shell-active .header-notif-dot {
  animation: vsm6-dot-pulse 1.8s var(--motion-ease) 1;
}

body.v2-shell-active .bottom-nav-dot {
  animation: vsm6-dot-pulse 1.8s var(--motion-ease) 1;
}

/* Topbar avatar — universal profile entry point (Phase 11B) */
body.v2-shell-active .v2-topbar-avatar {
  transition: opacity var(--motion-fast) var(--motion-ease);
  cursor: pointer;
}
@media (hover: hover) {
  body.v2-shell-active .v2-topbar-avatar:hover { opacity: 0.78; }
}
body.v2-shell-active .v2-topbar-avatar:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* View toggle buttons in timeline header */
body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn {
  transition:
    background var(--motion-fast) var(--motion-ease),
    color      var(--motion-fast) var(--motion-ease),
    box-shadow var(--motion-fast) var(--motion-ease);
}


/* ── Part 9: Reduced motion support ───────────────────────────
   When user requests reduced motion, zero out all VSM-6
   durations via token override. Transforms on hover are
   explicitly disabled. Opacity transitions collapse to 0ms
   (element appears/disappears instantly — accessible).       */

@media (prefers-reduced-motion: reduce) {
  :root {
    --motion-fast:   0.01ms;
    --motion-normal: 0.01ms;
    --motion-slow:   0.01ms;
  }

  /* Disable all animations and transforms explicitly */
  body.v2-shell-active .modal-overlay,
  body.v2-shell-active .modal-box,
  body.v2-shell-active .header-notif-dot,
  body.v2-shell-active .bottom-nav-dot {
    animation: none;
  }

  body.v2-shell-active .v2-kpi-card:hover {
    transform: none;
  }

  body.v2-shell-active .assignment-block:hover {
    transform: none;
  }

  body.v2-shell-active .v2-panel-btn--primary:active,
  body.v2-shell-active .v2-panel-btn--ghost:active {
    transform: none;
  }
}


/* ============================================================
   VSM-7 — Topbar Final Parity

   Objective: bring the V2 topbar to ~95% visual parity with
   the approved Claude Design reference.

   All rules are either:
     (a) scoped under body.v2-shell-active, or
     (b) on .v2-topbar-* selectors that only exist when the
         flag is on.
   V1 topbar (.header) is completely unaffected.

   Parts implemented:
     1  Two-line breadcrumb hierarchy (label + title)
     2  Search bar — full-width, polished
     3  Date navigation — unified sizing
     4  Role chip — full role name, pill style
     5  Theme toggle — visual-only moon button
     6  Notification button — icon-button treatment
     7  User profile card — avatar + name + role column
     8  Spacing normalization — single grid baseline
     9  Responsive — 4-breakpoint verified rules
   ============================================================ */


/* ── Part 1: Two-line breadcrumb ───────────────────────────────
   #v2TopbarCrumb is now a <div> with .v2-topbar-label (top)
   and .v2-topbar-title (bottom). Dynamic section name updates
   target .v2-topbar-title only — label stays "DRIVER OPS".  */

#v2TopbarCrumb {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0;
  flex-shrink: 0;
  line-height: 1;
}

.v2-topbar-label {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  white-space: nowrap;
  line-height: 1.2;
}

.v2-topbar-title {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  letter-spacing: -0.01em;
  line-height: 1.3;
  white-space: nowrap;
}

/* Phase 11N (adaptive topbar): #v2TopbarCrumb is purely decorative
   (aria-hidden="true" — set in initV2Topbar(); its content duplicates the
   page's own <h1> title shown right below the topbar) and was the last
   flex-shrink:0 element in the row with no size cap at all. On a
   viewport-width media-query strategy alone, every new "resolution where
   it still breaks" needs its own new breakpoint chasing whatever content
   happens to overflow that specific width — not truly adaptive. Making
   crumb (and only crumb — .date-nav and .header-user-area, the profile
   avatar, stay flex-shrink:0 and fully protected) shrink and truncate
   continuously means the browser's own flex algorithm absorbs whatever
   space is missing at ANY width, not just the handful of pixel values
   that happened to get tested. Same ID specificity as the rule above,
   later in source, so it wins the flex-shrink tug-of-war. */
#v2TopbarCrumb {
  flex-shrink: 1;
  min-width: 0;
}
.v2-topbar-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}


/* ── Part 2: Search bar parity ─────────────────────────────────
   Upgraded from VSM-5C placeholder — wider, more structured.
   Visible ≥1024px; hidden on tablet/phone.                   */

.v2-topbar-search {
  min-width: 240px;
  max-width: 320px;
  padding: 6px 12px;
  gap: 8px;
  border-radius: 8px;
  font-size: 12.5px;
  height: 32px;
  box-sizing: border-box;
}

.v2-topbar-search svg { opacity: 0.6; }

@media (min-width: 768px) and (max-width: 1279px) {
  .v2-topbar-search { min-width: 180px; max-width: 240px; }
}


/* ── Part 3: Date navigation parity ───────────────────────────
   Unified 32px height for all date controls — today button,
   arrow buttons, and date input sit on the same baseline.
   No logic changes; only visual sizing.                       */

/* Hotfix: the "unified 32px height" here never actually applied to
   .btn-today/.btn-icon — those carry an unconditional min-height:44px
   (style.css, WCAG touch target) which always wins over a smaller explicit
   height, so the buttons were rendering at 44px inside this 32px-tall pill
   with no `overflow` set — visually breaking out of their own rounded
   container. Touch targets must stay 44px (hotfix requirement), so the fix
   is to stop fighting that height rather than shrink the buttons: `height`
   removed here so the pill sizes itself from its tallest child instead of
   clipping/overflowing at a fixed 32px. padding/gap bumped in the later,
   higher-priority "Date nav: consistent height, rounder" block below
   (same selector, wins by source order) — not here, to avoid two
   conflicting values for the same property in the cascade. */
body.v2-shell-active .v2-topbar .date-nav {
  gap: 3px;
  align-items: center;
  border-radius: 8px;
}

body.v2-shell-active .v2-topbar .btn-today {
  height: 26px;
  padding: 0 10px;
  font-size: 12px;
  border-radius: 6px;
  display: flex;
  align-items: center;
}

body.v2-shell-active .v2-topbar .btn-icon {
  width: 26px;
  height: 26px;
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 14px;
}

body.v2-shell-active .v2-topbar .date-input {
  height: 26px;
  padding: 0 6px;
  font-size: 12px;
  border-radius: 6px;
  border-color: var(--border);
  background: var(--input-bg);
  color: var(--text);
  box-sizing: border-box;
}


/* ── Part 4: Role chip in profile card ─────────────────────────
   #v2TopbarRoleLabel shows full role name (Administrator etc).
   #roleBadge (V1 element) is hidden inside the V2 topbar —
   the label supersedes it. V1 roleBadge still works in flag-off
   mode since the hide rule is scoped to body.v2-shell-active.  */

.v2-topbar-user-role {
  font-size: 10px;
  font-weight: 500;
  color: var(--faint);
  white-space: nowrap;
  line-height: 1.2;
}

/* Hide V1 abbreviated badge inside the V2 profile card */
body.v2-shell-active .v2-topbar .header-user-area .role-badge {
  display: none !important;
}


/* ── Part 5: Theme toggle ──────────────────────────────────────
   Visual-only moon icon button. No dark mode wired.
   Uses shared .v2-topbar-icon-btn base (defined below).       */

.v2-topbar-theme-btn {
  order: 55;
}

/* V1 mobile UX update: theme toggle is now part of the compact single-row
   mobile header (task 3) — previously unconditionally hidden ≤767px here.
   Mobile visibility/placement is owned by the consolidated mobile topbar
   grid rules further down this file (search "single-row compact mobile
   topbar"). Desktop (≥768px) is unaffected — this button was already
   visible there before this change. */


/* ── Parts 5 + 6: Shared topbar icon button base ───────────────
   Theme toggle and notification bell share this base style.
   Ensures equal height, border style, and hover treatment with
   the date navigation and search controls.                    */

.v2-topbar-icon-btn {
  width: 32px;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
  font-family: inherit;
  transition:
    background var(--motion-fast, 120ms) var(--motion-ease, ease-out),
    color      var(--motion-fast, 120ms) var(--motion-ease, ease-out),
    border-color var(--motion-fast, 120ms) var(--motion-ease, ease-out);
}
@media (hover: hover) {
  .v2-topbar-icon-btn:hover {
    background: var(--hover);
    color: var(--text);
    border-color: var(--border-strong);
  }
}


/* ── Part 6: Notification bell — icon-button treatment ────────
   The extracted #btnHeaderNotif becomes a .v2-topbar-icon-btn
   sibling. Notification logic and unread badge are unchanged.
   Only visual sizing and border are applied here.             */

body.v2-shell-active .v2-topbar #btnHeaderNotif {
  width: 32px;
  height: 32px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  order: 57;
}

/* Ensure inner SVG stays centred at the new size */
body.v2-shell-active .v2-topbar #btnHeaderNotif svg {
  width: 16px;
  height: 16px;
}


/* ── Part 7: User profile card ─────────────────────────────────
   .header-user-area is restructured by initV2Topbar() into:
     [.v2-topbar-avatar] [.v2-topbar-user-info(name + role)]
   The card has a subtle border and rounded surface, matching
   the Claude Design profile card pattern.                     */

body.v2-shell-active .v2-topbar .header-user-area {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px 4px 6px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  cursor: default;
  min-width: 0;
  flex-shrink: 0;
  transition:
    border-color var(--motion-fast, 120ms) var(--motion-ease, ease-out),
    background   var(--motion-fast, 120ms) var(--motion-ease, ease-out);
}
@media (hover: hover) {
  body.v2-shell-active .v2-topbar .header-user-area:hover {
    border-color: var(--border-strong);
    background: var(--surface-2);
  }
}

/* Name inside profile card */
body.v2-shell-active .v2-topbar .header-display-name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100px;
  line-height: 1.3;
  display: block;
}

/* User info column: name + role stacked */
.v2-topbar-user-info {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
}


/* ── Part 8: Spacing normalization ─────────────────────────────
   All topbar interactive elements sit on a 32px height baseline.
   Gap between elements is 6px; padding is 0 12px.             */

body.v2-shell-active .v2-topbar {
  gap: 6px;
  padding: 0 12px;
  height: 52px; /* slightly taller than 56px V1 to match Claude Design */
}

/* Order assignments for new elements */
body.v2-shell-active .v2-topbar .v2-topbar-theme-btn { order: 55; }
body.v2-shell-active .v2-topbar #btnHeaderNotif { order: 57; }


/* ── Part 9: Responsive review ─────────────────────────────────
   Breakpoints: ≥1280 / 1024–1279 / 768–1023 / ≤767           */

/* ≥1280px (full desktop): all elements visible */
/* (no extra rules — the defaults above are for ≥1280px)       */

/* 1024–1279px (laptop): search narrower, crumb visible */
@media (min-width: 1024px) and (max-width: 1279px) {
  .v2-topbar-search {
    min-width: 160px;
    max-width: 200px;
  }
  body.v2-shell-active .v2-topbar .header-display-name {
    max-width: 80px;
  }
}

/* 768–1023px (tablet): hide search, keep crumb and date-nav */
@media (min-width: 768px) and (max-width: 1023px) {
  .v2-topbar-search { display: none !important; }
  body.v2-shell-active .v2-topbar .header-display-name {
    max-width: 70px;
  }
}

/* ≤767px (phone): single-row header ── these rules mirror the existing
   VSM-3 mobile structure. Notif bell hidden (existing ≤767px rule);
   theme toggle now shown (V1 mobile UX update, task 3) — layout/placement
   owned by the consolidated mobile topbar grid further down this file. */
@media (max-width: 767px) {
  body.v2-shell-active .v2-topbar .header-user-area {
    border: none;
    background: transparent;
    padding: 0;
    gap: 5px;
  }
  .v2-topbar-user-info { display: none; } /* compact: avatar only on phone */
  body.v2-shell-active .v2-topbar { height: auto; }
}

/* ≤600px (small phone): search input stays hidden (unrelated to the Quick
   Search trigger, which is now shown icon-only — see the consolidated
   mobile topbar grid further down); crumb stays hidden. */
@media (max-width: 600px) {
  .v2-topbar-search   { display: none !important; }
  #v2TopbarCrumb       { display: none !important; }
}


/* ============================================================
   VSM-8 — Timeline / List View Architecture

   #v2TimelineView — wraps .timeline-wrapper + .legend
   #v2ListView     — assignment list cards for the viewed date

   Both containers are always mounted inside #v2TimelineSurface.
   .v2-view-active manages display:flex / display:none.
   setDashboardView() swaps the class; no layout reflow for the
   outgoing view, entry animation for the incoming view.

   All selectors scoped under body.v2-shell-active or on IDs/
   classes that only exist when the flag is on — zero V1 impact.
   ============================================================ */


/* ── View container base ───────────────────────────────────────
   display:none by default; .v2-view-active makes it visible
   with a fade-in entry animation using VSM-6 motion tokens.  */

.v2-view-container {
  display: none;
  flex-direction: column;
  flex: 1;
  min-height: 0;           /* allows flex children to shrink below content */
}

.v2-view-active {
  display: flex;
  animation: vsm8-view-in var(--motion-normal, 200ms) var(--motion-ease, ease-out);
}

@keyframes vsm8-view-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .v2-view-active { animation: none; }
}

/* ── #v2TimelineView — timeline content wrapper ────────────────
   Contains .timeline-wrapper + .legend (moved inside by VSM-8).
   flex:1 in .v2-view-active gives the timeline its full height
   since #v2TimelineSurface is already a flex column.          */

body.v2-shell-active #v2TimelineSurface #v2TimelineView {
  flex: 1;
  min-height: 0;
}


/* ── #v2ListView — list view container ─────────────────────────
   overflow-y: auto so tall lists scroll independently without
   moving the timeline header or topbar.                       */

body.v2-shell-active #v2ListView {
  overflow-y: auto;
  padding: 12px 16px 16px;
  gap: 8px;
  display: none;           /* overridden by .v2-view-active when list is shown */
  flex-direction: column;
}

body.v2-shell-active #v2ListView.v2-view-active {
  display: flex;
}


/* ── List card base ────────────────────────────────────────────
   Three-column grid: time (auto) | body (1fr) | status (auto).
   Left-edge accent bar distinguishes assignment status.       */

.v2-list-card {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 10px 14px;
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: 8px;
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition:
    box-shadow  var(--motion-fast, 120ms) var(--motion-ease, ease-out),
    border-color var(--motion-fast, 120ms) var(--motion-ease, ease-out);
  outline: none;
}
.v2-list-card:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
@media (hover: hover) {
  .v2-list-card:hover {
    box-shadow:   var(--shadow-md);
    border-color: var(--border-strong);
  }
}

/* Status accent bars */
.v2-list-card--started   { border-left-color: var(--warn);          }
.v2-list-card--completed { border-left-color: var(--ok);            }
.v2-list-card--assigned  { border-left-color: var(--info);          }
.v2-list-card--pending   { border-left-color: var(--border-strong); }
.v2-list-card--approved  { border-left-color: var(--info);          }


/* ── Time column ───────────────────────────────────────────────
   Monospace so hh:mm–hh:mm aligns across cards.              */

.v2-list-card-time {
  font-family: var(--font-mono);
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  white-space: nowrap;
  padding-top: 1px;
  line-height: 1.4;
}


/* ── Body column — title + meta ────────────────────────────────*/

.v2-list-card-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;            /* allows text-overflow: ellipsis to work in flex child */
}

.v2-list-card-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.3;
}

.v2-list-card-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.4;
}


/* ── Vehicle chip inside meta ──────────────────────────────────
   Uses data-vehicle attribute; colour matched to --v-* tokens. */

.v2-list-vehicle-chip {
  font-size: 11px;
  font-weight: 600;
  padding: 1px 7px;
  border-radius: 20px;
  border: 1px solid transparent;
  white-space: nowrap;
}
.v2-list-vehicle-chip[data-vehicle="Innova"]   { color: var(--v-innova); border-color: var(--v-innova); }
.v2-list-vehicle-chip[data-vehicle="Luxio"]    { color: var(--v-luxio);  border-color: var(--v-luxio);  }
.v2-list-vehicle-chip[data-vehicle="Polytron"] { color: var(--v-poly);   border-color: var(--v-poly);   }
.v2-list-vehicle-chip[data-vehicle="Hiace"]    { color: var(--v-hiace);  border-color: var(--v-hiace);  }
/* v1.30.9.15 — buildListCard() now sets color/border-color inline (any
   fleet size, not just these 4 names); inline always wins the cascade over
   the attribute-selector rules above, so those are harmless dead weight
   for the 4 legacy names rather than something to delete right now. */
.v2-list-vehicle-dot {
  width: 8px;
  height: 8px;
  flex: none;
  display: inline-block;
}
.v2-list-convoy-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--accent-fg);
}


/* ── Status column ─────────────────────────────────────────────*/

.v2-list-card-status {
  display: flex;
  align-items: flex-start;
  flex-shrink: 0;
}

.v2-list-status-pill {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 20px;
  white-space: nowrap;
  line-height: 1.4;
}
.v2-list-status-pill--started   { background: var(--warn-bg);  color: var(--warn);  }
.v2-list-status-pill--completed { background: var(--ok-bg);    color: var(--ok);    }
.v2-list-status-pill--assigned  { background: var(--info-bg);  color: var(--info);  }
.v2-list-status-pill--pending   { background: var(--hover);    color: var(--muted); }
.v2-list-status-pill--approved  { background: var(--info-bg);  color: var(--info);  }


/* ── Empty state ───────────────────────────────────────────────*/

.v2-list-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  padding: 48px 24px;
  text-align: center;
  gap: 12px;
  color: var(--faint);
}

.v2-list-empty-icon {
  width: 48px;
  height: 48px;
  border-radius: 14px;
  background: var(--hover);
  display: grid;
  place-items: center;
  color: var(--faint);
}

.v2-list-empty p {
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  margin: 0;
  line-height: 1.5;
}


/* ── Responsive ────────────────────────────────────────────────
   Tablet ≤1023px: compressed card spacing.
   Phone ≤767px: single-column stacked layout.
   No horizontal scrolling on any breakpoint.                  */

@media (max-width: 1023px) {
  body.v2-shell-active #v2ListView { padding: 10px 12px 14px; }
  .v2-list-card { padding: 12px 14px; gap: 8px 12px; }
}

@media (max-width: 767px) {
  body.v2-shell-active #v2ListView { padding: 10px 8px 14px; gap: 6px; }

  /* Single-column: time + body + status stack vertically */
  .v2-list-card {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto auto;
    gap: 6px;
    padding: 12px 14px;
  }

  .v2-list-card-time {
    font-size: 11px;
    color: var(--faint);
  }

  .v2-list-card-status {
    align-items: flex-start;
  }
}

@media (max-width: 480px) {
  .v2-list-card { padding: 10px 12px; }
  .v2-list-card-title { font-size: 13px; }
}


/* ============================================================
   VSM-9 — Visual Parity Cleanup

   CSS-only refinements. Appended last so these rules beat all
   prior VSM phases by cascade order.

   Scope: every selector is either a V2 class (.v2-*) that only
   exists when the flag is on, or scoped under
   body.v2-shell-active — zero V1 impact in either case.

   Areas covered:
     1  Rail
     2  Section Panel
     3  Topbar
     4  KPI Cards
     5  Timeline Header
     6  Driver Column
     7  Vehicle Legend
     8  Empty States
     9  Responsive Polish
   ============================================================ */


/* ── 1. Rail — lighter, more premium ──────────────────────────
   Reduces hit-area, icon size, active indicator weight and
   avatar size for a more refined, enterprise-grade rail.     */

/* Tighter top/bottom padding */
.v2-rail {
  padding: 8px 0 12px;
}

/* Crest: smaller gap, tighter radius */
.v2-rail-crest {
  margin-bottom: 10px;
  border-radius: 8px;
  width: 38px;
  height: 38px;
}

/* Crest logo: slightly smaller */
.v2-rail-crest-img {
  width: 26px;
  height: 26px;
}

/* Module icon hit area: more compact */
.v2-rail-item {
  width: 38px;
  height: 38px;
  border-radius: 8px;
}

/* Icon SVG */
.v2-rail-icon {
  width: 17px;
  height: 17px;
}

/* Active indicator: thinner and shorter — lighter visual weight */
.v2-rail-item--active::before {
  width: 2px;
  height: 18px;
}

/* Avatar: slightly smaller with subtle ring */
.v2-rail-avatar {
  width: 28px;
  height: 28px;
  box-shadow: 0 0 0 2px var(--border);
}

/* Avatar initials */
.v2-rail-avatar-initials {
  font-size: 10px;
}


/* ── 2. Section Panel — better proportions ────────────────────
   Adds a "MENU" section label above nav items, tightens the
   module title, and improves footer visual separation.       */

/* Module title: slightly more muted — "Driver Operations" is
   decorative context, not the primary action area.          */
.v2-panel-title {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--muted);
}

/* "MENU" section label — rendered purely via CSS ::before.
   Sits inside the flex column before the nav item buttons.  */
.v2-panel-nav::before {
  content: 'MENU';
  display: block;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  padding: 0 2px 6px;
  font-family: var(--font-sans, 'Inter', sans-serif);
  flex-shrink: 0;
}

/* Nav items: slightly more generous horizontal padding */
.v2-panel-nav-item {
  padding: 8px 12px;
  border-radius: 7px;
  font-size: 12.5px;
}

/* Active left bar: match icon hit width change */
.v2-panel-nav-item--active::before {
  height: 14px;
}

/* Pending badge: refined pill */
.v2-panel-badge {
  font-size: 10px;
  height: 16px;
  min-width: 16px;
  padding: 0 5px;
  border-radius: 8px;
}

/* CTA buttons: slightly more padding */
.v2-panel-btn {
  padding: 8px 12px;
  font-size: 12.5px;
  font-weight: 600;
  border-radius: 7px;
}

/* Footer: cleaner separation */
.v2-panel-footer {
  padding: 6px 10px 16px;
  gap: 1px;
}

.v2-panel-footer-btn {
  padding: 8px 12px;
  font-size: 12.5px;
  border-radius: 7px;
}

/* Dividers: ensure consistent appearance */
.v2-panel-divider {
  margin: 1px 0;
}

/* Panel header: tighter */
.v2-panel-header {
  padding: 16px 14px 10px;
}


/* ── 3. Topbar — refined spacing and typography ───────────────
   Targets consistent 32px height baseline for all controls,
   tighter gaps, and cleaner breadcrumb hierarchy.           */

body.v2-shell-active .v2-topbar {
  gap: 8px;
  padding: 0 14px;
}

/* Breadcrumb container: slight gap between label and title */
#v2TopbarCrumb {
  gap: 1px;
}

/* Module label: refined */
.v2-topbar-label {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--faint);
  opacity: 0.8;
}

/* Section title: slightly larger */
.v2-topbar-title {
  font-size: 13.5px;
  font-weight: 700;
  letter-spacing: -0.015em;
}

/* Search: rounder, cleaner */
.v2-topbar-search {
  border-radius: 9px;
  gap: 7px;
  font-size: 12px;
  border-color: var(--border);
}

/* Date nav: consistent height, rounder.
   Hotfix: padding/gap bumped (2px 3px / 2px → 4px 6px / 6px) — see the
   longer comment on this same selector above (~line 3779) for why: the
   44px-touch-target buttons this pill holds were overflowing its old
   tight sizing. */
body.v2-shell-active .v2-topbar .date-nav {
  border-radius: 9px;
  gap: 6px;
  padding: 4px 6px;
}

body.v2-shell-active .v2-topbar .btn-today {
  font-size: 11.5px;
  font-weight: 600;
  padding: 0 10px;
  border-radius: 7px;
}

body.v2-shell-active .v2-topbar .btn-icon {
  border-radius: 7px;
  font-size: 13px;
}

body.v2-shell-active .v2-topbar .date-input {
  border-radius: 7px;
  font-size: 11.5px;
}

/* Icon buttons: rounder */
.v2-topbar-icon-btn {
  border-radius: 9px;
}

/* Notification bell: consistent */
body.v2-shell-active .v2-topbar #btnHeaderNotif {
  border-radius: 9px;
}

/* User profile card: slightly rounder */
body.v2-shell-active .v2-topbar .header-user-area {
  border-radius: 9px;
  padding: 4px 10px 4px 7px;
}

/* Avatar in topbar: tighter */
.v2-topbar-avatar {
  width: 26px;
  height: 26px;
  font-size: 9.5px;
  font-weight: 700;
}

/* Name in card: cleaner */
body.v2-shell-active .v2-topbar .header-display-name {
  font-size: 12.5px;
  font-weight: 600;
}

/* Role label: refined */
.v2-topbar-user-role {
  font-size: 9.5px;
  letter-spacing: 0.01em;
  color: var(--faint);
}


/* ── 4. KPI Cards — refined density and hierarchy ─────────────
   Improves icon, label, value and subtitle proportions for
   the target V2 KPI card readability.                       */

/* Card: generous padding, cleaner radius */
.v2-kpi-card {
  padding: 16px;
  border-radius: 10px;
  gap: 2px;
}

/* Icon container: slightly larger, refined radius */
.v2-kpi-icon {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  margin-bottom: 8px;
}

/* Label: tighter letter-spacing, slightly bolder */
.v2-kpi-label {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  margin-top: 0;
}

/* Value: strong hierarchy — keep 28px for 768-1279, 32px at 1280+ */
.v2-kpi-value {
  font-size: 28px;
  letter-spacing: -0.02em;
  margin-top: 2px;
}

@media (min-width: 1280px) {
  .v2-kpi-value { font-size: 32px; }
}

/* Subtitle: subtle secondary context */
.v2-kpi-sub {
  font-size: 10.5px;
  color: var(--faint);
  margin-top: 3px;
  font-weight: 400;
}

/* Strip gap: consistent with content padding */
#v2KpiStrip {
  gap: 10px;
  padding: 14px 16px 0;
}

/* Dashboard section header: tighter bottom padding */
#v2DashHeader {
  padding: 12px 16px 0;
}


/* ── 5. Timeline Header — clearer hierarchy ───────────────────
   "Papan Jadwal" title should dominate; subtitle should read
   as secondary context without competing.                   */

body.v2-shell-active #v2TimelineSurface .v2-tl-header {
  padding: 11px 16px;
}

/* Title: more prominent */
body.v2-shell-active #v2TimelineSurface .v2-tl-title {
  font-size: 14px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}

/* Subtitle: clearly secondary */
body.v2-shell-active #v2TimelineSurface .v2-tl-sub {
  font-size: 10.5px;
  color: var(--faint);
  font-weight: 400;
}

/* View toggle: slightly more refined */
body.v2-shell-active #v2TimelineSurface .v2-tl-view-toggle {
  border-radius: 8px;
  padding: 2px;
  gap: 1px;
}

body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn {
  padding: 4px 12px;
  font-size: 12px;
  border-radius: 6px;
  letter-spacing: 0;
}

body.v2-shell-active #v2TimelineSurface .v2-tl-view-btn--active {
  font-weight: 600;
}

/* Date badge in header: cleaner */
body.v2-shell-active #v2TimelineSurface .v2-tl-header-right .timeline-date-label {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}

/* ── V1 Redesign 4c: Assignment Board filter chips ──────────────────────
   Sits between the header and the timeline/list views (shared by both,
   since both read getFilteredAssignments()). Native <select> chrome kept
   deliberately — styled to read as a chip without building a bespoke
   custom-dropdown component for something this codebase already handles
   fine natively. */
#v2FilterBar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.v2-filter-chip {
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 10px;
  min-height: 32px;
  max-width: 150px;
  cursor: pointer;
  transition: background-color var(--motion-pop) ease, border-color var(--motion-pop) ease;
}
.v2-filter-chip:hover { background: var(--hover); }
.v2-filter-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* A chip with a real selection reads as "active" — accent-tinted like the
   rest of the app's active/selected states. */
.v2-filter-chip:not([data-empty="true"]) {
  background: var(--accent-subtle);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
  color: var(--accent-fg);
}
.v2-filter-clear {
  font-family: inherit;
  font-size: 12px;
  font-weight: 700;
  color: var(--accent-fg);
  background: none;
  border: none;
  cursor: pointer;
  padding: 6px 4px;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
}
.v2-filter-clear:hover { text-decoration: underline; }

@media (max-width: 767px) {
  #v2FilterBar { padding: 8px 12px; gap: 6px; }
  .v2-filter-chip { font-size: 11.5px; padding: 5px 9px; min-height: 32px; max-width: 108px; }
}
@media (max-width: 380px) {
  .v2-filter-chip { max-width: 92px; font-size: 11px; }
}


/* ── 6. Driver Column — refined avatar ────────────────────────
   Increases avatar from 26px to 28px and adds a subtle ring
   to anchor the avatar visually against the surface.        */

body.v2-shell-active .driver-label {
  grid-template-columns: 28px 1fr;
  column-gap: 7px;
  padding: 5px 10px;
  background: var(--surface);
  border-right: 1px solid var(--border);
}

/* Avatar: 28px with accent ring */
body.v2-shell-active .driver-label::before {
  width: 28px;
  height: 28px;
  font-size: 9.5px;
  letter-spacing: 0.02em;
  box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--accent) 22%, transparent);
}

/* Driver name: clearer */
body.v2-shell-active .driver-label .driver-name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
}

/* Phone: refined */
body.v2-shell-active .driver-label .driver-phone {
  font-size: 9px;
  color: var(--faint);
}

/* Mobile: maintain correct columns for 28px avatar */
@media (max-width: 600px) {
  body.v2-shell-active .driver-label {
    grid-template-columns: 1fr;
    padding: 4px 6px;
  }
  body.v2-shell-active .driver-label::before { display: none; }
  body.v2-shell-active .driver-label .driver-name { grid-column: 1; grid-row: 1; }
  body.v2-shell-active .driver-label .driver-phone { grid-column: 1; grid-row: 2; }
}

/* ── "Tanpa Driver" (unassigned) lane — V2 shell refinement ───────────
   V1 Driver Timeline Resource Lanes. The lane is built by js/timeline.js
   #renderDriverRows with class .driver-label--unassigned. It is a state,
   not a person: no avatar, and a warm tint + amber label so a missing
   driver reads as an operational exception. Selectors carry the same
   `body.v2-shell-active .driver-label…` prefix so they win here (defined
   after the base driver-label rules above). */
body.v2-shell-active .driver-label--unassigned {
  grid-template-columns: 1fr;
  background: var(--warn-bg, #F6EEDB);
}
body.v2-shell-active .driver-label--unassigned::before { display: none; }
body.v2-shell-active .driver-label--unassigned .driver-name {
  grid-column: 1;
  color: var(--warn, #946420);
}
body.v2-shell-active .driver-label--unassigned .driver-lane-count {
  grid-column: 1;
  color: var(--warn, #946420);
  opacity: 0.85;
  font-family: var(--font-sans, 'Inter', sans-serif);
}


/* ── 7. Vehicle Legend — refined chips ────────────────────────
   Tighter padding, slightly smaller text, better swatch size. */

body.v2-shell-active #v2TimelineSurface .legend {
  padding: 8px 16px;
  gap: 5px;
}

body.v2-shell-active #v2TimelineSurface .legend-item {
  padding: 3px 9px 3px 7px;
  font-size: 11.5px;
  font-weight: 500;
  gap: 5px;
  border-radius: 20px;
}

/* Swatch: slightly smaller circle */
body.v2-shell-active #v2TimelineSurface .legend-item::before {
  width: 7px;
  height: 7px;
  border-radius: 50%;
}


/* ── 8. Empty States — unified visual language ────────────────
   All empty states (timeline rows, list view, driver dashboard)
   use the same typographic and colour treatment.             */

/* Timeline row "Belum ada jadwal" hint text */
body.v2-shell-active .empty-slots-hint {
  font-size: 11.5px;
  color: var(--faint);
  font-style: normal;
  font-family: var(--font-sans, 'Inter', sans-serif);
  font-weight: 400;
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  left: 8px;
  pointer-events: none;
  user-select: none;
}

/* List empty state: already defined in VSM-8 — refine icon */
.v2-list-empty-icon {
  border-radius: 16px;
}

/* Driver dashboard empty sections: consistent */
body.v2-shell-active #driverDashboard .dash-empty {
  padding: 20px 0;
  font-size: 12.5px;
  color: var(--faint);
  font-style: normal;
  text-align: center;
}


/* ── 9. Responsive Polish ─────────────────────────────────────
   Fine-tune density at each major breakpoint.               */

/* ≥1440px: wide desktop — more comfortable spacing */
@media (min-width: 1440px) {
  body.v2-shell-active .main-content {
    padding-left: 20px;
    padding-right: 20px;
  }
  #v2KpiStrip  { gap: 14px; padding: 16px 20px 0; }
  .v2-kpi-card { padding: 18px 20px; }
  body.v2-shell-active #v2TimelineSurface .v2-tl-header { padding: 13px 20px; }
  body.v2-shell-active #v2TimelineSurface .legend { padding: 9px 20px; }
  body.v2-shell-active #v2ListView { padding: 14px 20px 16px; }
}

/* 1024–1279px: laptop — moderate density */
@media (min-width: 1024px) and (max-width: 1279px) {
  .v2-kpi-value { font-size: 26px; }
  .v2-kpi-icon  { width: 30px; height: 30px; margin-bottom: 6px; }
  body.v2-shell-active .v2-topbar { padding: 0 10px; gap: 6px; }
  .v2-topbar-search { min-width: 160px; max-width: 200px; }
  /* Phase 11N: .v2-topbar-search is display:none in this range now (see
     ~line 2724), so its own min/max-width above are moot — kept as-is,
     harmless. Safety margin: crumb text length varies per workspace
     (module titles aren't all as short as "Home") and had no cap at all,
     so a longer title could still starve the avatar/notif/theme controls
     the same way search did. Capping + truncating it here means the
     avatar (the one control that must never disappear) is never at the
     mercy of how long a given workspace's title happens to be. */
  .v2-topbar-title {
    max-width: 150px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* 768–1023px: tablet — compact */
@media (min-width: 768px) and (max-width: 1023px) {
  .v2-kpi-value { font-size: 24px; }
  .v2-kpi-icon  { width: 28px; height: 28px; margin-bottom: 5px; }
  .v2-kpi-sub   { display: none; }  /* recover height on tablet */
  body.v2-shell-active .v2-topbar { padding: 0 10px; gap: 5px; }
}

/* ≤767px phone — existing rules remain; just these refinements */
@media (max-width: 767px) {
  /* Driver column: tighter on phone */
  body.v2-shell-active .driver-label { padding: 4px 8px; }
  /* KPI strip: tighter */
  #v2KpiStrip { gap: 8px; }
  #v2DashHeader { padding: 10px 8px 0; }
}

@media (max-width: 480px) {
  .v2-list-card { padding: 10px 12px; }
  .v2-list-card-title { font-size: 13px; }
}

/* ============================================================
   VSM-9 — Workspace System, Search, Dark Mode, Admin
   ============================================================ */

/* ── Shared workspace base ── */
.v2-workspace {
  padding: 16px;
  box-sizing: border-box;
}

.v2-workspace-header {
  margin-bottom: 16px;
  position: relative;
}

/* Phase 9 mobile-first audit — Pending's only search mechanism
   (#v2SearchInput) is display:none below 1024px with no alternative. This
   toggle reveals the same input as a fixed overlay bar (see
   "pending-mobile-search-open" below) rather than adding a second search
   implementation. Desktop already has the real search bar visible in the
   topbar, so this stays hidden there. */
.v2-pending-search-toggle {
  display: none;
  position: absolute;
  top: 0;
  right: 0;
  width: 44px;
  height: 44px;
  align-items: center;
  justify-content: center;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
  cursor: pointer;
}
@media (max-width: 1023px) {
  .v2-pending-search-toggle { display: inline-flex; }
}
body.pending-mobile-search-open .v2-topbar-search {
  display: flex !important;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 250;
  height: 52px;
  padding: 0 14px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-md, 0 4px 14px rgba(0,0,0,.08));
  padding-top: env(safe-area-inset-top, 0px);
}
body.pending-mobile-search-open .v2-topbar-search-input {
  font-size: 16px; /* iOS zoom guard, matches the rest of the app's mobile inputs */
}

.v2-workspace-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.02em;
  margin: 0 0 4px;
}

.v2-workspace-subtitle {
  font-size: 12.5px;
  color: var(--muted);
  margin: 0;
}

/* ── Pending workspace cards ── */
.v2-pending-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.v2-pending-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  box-shadow: var(--shadow-sm);
}

/* Design System Program Phase 8.4 — restrained entrance for a pending card
   that's genuinely new to the list (realtime arrival from another device),
   never applied to updated/unaffected cards or on initial/bulk population.
   Same motion tokens + keyframe shape as .request-card--enter (style.css,
   Phase 8.3); both the OS-level prefers-reduced-motion and the app's own
   [data-anim="off"] blanket rules already collapse this — no extra gating. */
@keyframes v2PendingCardEnter {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.v2-pending-card--enter {
  animation: v2PendingCardEnter var(--motion-base, 200ms) var(--ease-decelerate, ease);
}

.v2-pending-card-header {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}

.v2-pending-status-pill {
  font-size: 10.5px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 20px;
  background: var(--warn-bg, #fef3cd);
  color: var(--warn, #92610a);
  border: 1px solid rgba(148,100,32,.2);
  line-height: 1.4;
}

.v2-pending-age {
  font-size: 11px;
  color: var(--faint);
  font-family: var(--font-mono);
}

.v2-pending-card-body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 12px;
  margin-bottom: 12px;
}

.v2-pending-field {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.v2-pending-field--full {
  grid-column: 1 / -1;
}

.v2-pending-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--faint);
}

.v2-pending-value {
  font-size: 12.5px;
  color: var(--text);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Phase 9 mobile-first audit: the ellipsis above silently clipped free-text
   fields (Rekomendasi Dispatch/Ketersediaan/Keperluan/Catatan) with no way
   to reveal them on touch — a `title` attribute was added as a desktop-hover
   bonus, but mobile browsers don't reliably surface `title` on tap, so the
   actual mobile fix is to let these specific fields wrap instead of clip.
   The short fields (Bidang/Penumpang/Tanggal/Waktu) keep the ellipsis —
   they're bounded values, not user-entered free text. */
.v2-pending-field--full .v2-pending-value {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  word-break: break-word;
}

.v2-pending-card-actions {
  display: flex;
  gap: 8px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.v2-pending-btn {
  flex: 1;
  padding: 7px 12px;
  border-radius: 7px;
  font-size: 12.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  border: 1px solid transparent;
  transition: filter var(--motion-fast) var(--motion-ease);
  /* Phase 9 mobile-first audit: no min-height meant ~30-32px effective
     height on the primary approve/reject task; align items so the taller
     box doesn't just push the label to one edge. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* Phase 9 mobile-first audit: 3 buttons (incl. "Setujui Sesuai Rekomendasi")
   sharing one row at flex:1 have no room for their Indonesian labels below
   ~480px and wrap mid-word with no dedicated layout for it. Stack instead. */
@media (max-width: 480px) {
  .v2-pending-card-actions { flex-direction: column; }
  .v2-pending-btn { width: 100%; }
}

.v2-pending-btn--approve {
  background: var(--ok, #1a7a4a);
  color: #fff;
  border-color: var(--ok, #1a7a4a);
}
.v2-pending-btn--approve:hover { filter: brightness(0.88); }

.v2-pending-btn--reject {
  background: var(--danger-bg, #fdecea);
  color: var(--danger, #a8292f);
  border-color: color-mix(in srgb, var(--accent) 20%, transparent);
}
.v2-pending-btn--reject:hover {
  background: var(--danger, #a8292f);
  color: #fff;
}

.v2-pending-empty {
  text-align: center;
  padding: 40px 20px;
  color: var(--faint);
  font-size: 13px;
}

/* ── Administration workspace ── */
.v2-admin-sections {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.v2-admin-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  box-shadow: var(--shadow-sm);
}

.v2-admin-section--disabled {
  opacity: 0.55;
  pointer-events: none;
}

.v2-admin-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 5px;
}

.v2-admin-section-title {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
}

.v2-admin-section-action {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-fg);
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  padding: 0;
  transition: opacity var(--motion-fast) var(--motion-ease);
}
.v2-admin-section-action:hover { opacity: 0.7; }

.v2-admin-section-badge {
  font-size: 10px;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: 20px;
  background: var(--hover);
  color: var(--muted);
  border: 1px solid var(--border);
}

.v2-admin-section-desc {
  font-size: 12px;
  color: var(--muted);
  margin: 0;
  line-height: 1.5;
}

.v2-admin-shell {
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  gap: 24px;
  align-items: start;
  padding: 0 20px 20px;
}

.v2-admin-workspace-layout {
  display: flex;
  flex-direction: column;
  gap: 32px;
  padding: 0 20px 40px;
}

/* v1.16.2.3 — Unified analytics container (ONE width language for Driver /
   Petty Cash / Executive). Left-aligned so content hugs the sidebar; capped so
   the editorial hero + 3-column grids stay dense and premium on ultra-wide
   monitors instead of spreading into sparse gutters. Below ~1680px viewport the
   cap is never reached, so every breakpoint renders identically across the three
   modules — the cap only tames 1920+. Governs WIDTH/alignment only; each view
   keeps its own vertical rhythm + padding. */
/* v1.16.2.5 — the analytics shell is now one instance of the unified workspace
   system; geometry comes entirely from the global tokens (Executive authority).
   Never margin:auto (that re-creates the old "too far from sidebar" centering). */
.v2-analytics-shell {
  max-width: var(--workspace-max-width);
  margin-left: 0;
  margin-right: 0;
  padding: var(--workspace-pad-top) var(--workspace-pad-inline) var(--workspace-pad-bottom);
}
/* Responsive gutter is tuned ONCE at the token level so every workspace
   consumer (analytics, petty cash center, driver operations) shrinks together. */
@media (max-width: 1024px) { :root { --workspace-pad-inline: 18px; --workspace-edge: 34px; --workspace-pad-bottom: 40px; } }
@media (max-width: 600px)  { :root { --workspace-pad-inline: 12px; --workspace-edge: 28px; --workspace-pad-top: 6px; --workspace-pad-bottom: 34px; } }

/* Analytics Driver sits one level deeper than Executive (inside
   .v2-admin-workspace-layout, padding:0 20px). The shell can't override that
   ancestor, so cancel it with a matching negative margin → identical 56px left
   edge as Executive. The negative value tracks the admin-layout padding at each
   breakpoint (20px desktop → 16px ≤960). */
#v2AdminSectionAnalytics { margin-left: -20px; margin-right: -20px; }
@media (max-width: 960px) { #v2AdminSectionAnalytics { margin-left: -16px; margin-right: -16px; } }

/* Driver Operations has NO .v2-workspace layer — its blocks are direct children
   of .main-content. Use --workspace-edge (= workspace 16 + pad-inline 24 = 40)
   so the TOTAL left inset still equals Executive's 56px, and cap at the same
   max-width. The timeline grid keeps overflow-x:auto, so nothing is hidden — it
   scrolls within the capped surface. */
body.v2-shell-active .main-content > #v2KpiStrip,
body.v2-shell-active .main-content > #v2TimelineSurface,
body.v2-shell-active .main-content > .legend,
body.v2-shell-active .main-content > #driverDashboard {
  max-width: var(--workspace-max-width);
  margin-left: var(--workspace-edge);
  margin-right: auto;
  box-sizing: border-box;
}
/* The KPI strip carried its own horizontal padding; drop it so its cards align
   to the same left edge the strip element now establishes via margin. */
body.v2-shell-active .main-content > #v2KpiStrip { padding-left: 0; padding-right: 0; }

/* V1 mobile UX fix — Operations horizontal balance. The block above's
   margin-left:var(--workspace-edge) / margin-right:auto pairing has no
   margin-left:auto counterpart, so the auto on the right resolves to 0 (per
   CSS auto-margin rules) instead of sharing space — the whole surface hugs
   .main-content's right edge with a permanent dead gap on the left. That's
   an intentional desktop alignment (matches Executive's 56px inset), but
   nothing neutralizes it below desktop, so Operations alone (every sibling
   module mounts via .v2-workspace, which only ever uses symmetric margins)
   reads as pushed-right on mobile. Zero it out at the same 767px split every
   other mobile-shell rule in this file already uses. */
@media (max-width: 767px) {
  body.v2-shell-active .main-content > #v2KpiStrip,
  body.v2-shell-active .main-content > #v2TimelineSurface,
  body.v2-shell-active .main-content > .legend,
  body.v2-shell-active .main-content > #driverDashboard {
    margin-left: 0;
    margin-right: 0;
  }
}

/* ── Page header layer ──────────────────────────────────────────────── */
.v2-admin-page-header {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

.v2-admin-page-title {
  margin: 0;
  font-size: 28px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text);
}

.v2-admin-page-subtitle {
  margin: 0;
  font-size: 14px;
  font-weight: 400;
  color: var(--muted);
}

/* ── Overview layer ────────────────────────────────────────────────── */
.v2-admin-overview-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}

.v2-admin-overview-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  max-width: 100%;
}

.v2-admin-overview-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0;
  background: transparent;
  border: none;
  box-shadow: none;
}

.v2-admin-overview-label {
  font-size: 12px;
  font-weight: 500;
  text-transform: capitalize;
  letter-spacing: 0;
  color: var(--muted);
}

.v2-admin-overview-desc {
  font-size: 11px;
  color: var(--faint);
  margin: 0;
  line-height: 1.3;
}

.v2-admin-overview-value {
  font-size: 42px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
  line-height: 1.1;
}

/* ── Navigation layer ──────────────────────────────────────────────── */
.v2-admin-nav {
  display: flex;
  gap: 4px;
  align-items: center;
  padding: 0;
  border: none;
  overflow-x: auto;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  margin-top: 8px;
}

.v2-admin-nav::-webkit-scrollbar {
  height: 4px;
}

.v2-admin-nav::-webkit-scrollbar-track {
  background: transparent;
}

.v2-admin-nav::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 2px;
}

.v2-admin-nav-tab {
  position: relative;
  padding: 10px 14px;
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
  transition: color .15s, padding .15s;
  font-family: inherit;
  outline: none;
}

.v2-admin-nav-tab::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 2px;
  background: transparent;
  transition: background .15s;
}

.v2-admin-nav-tab:hover {
  color: var(--text);
}

.v2-admin-nav-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.v2-admin-nav-tab--active {
  color: var(--text);
  font-weight: 600;
  padding-bottom: 8px;
}

.v2-admin-nav-tab--active::after {
  background: var(--accent);
}

/* ── Content layer ────────────────────────────────────────────────── */
.v2-admin-content {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
  margin-top: 12px;
}


.v2-admin-shell-content {
  min-width: 0;
}

.v2-admin-placeholder-card {
  display: none;
}

@media (max-width: 960px) {
  .v2-admin-shell {
    grid-template-columns: 1fr;
    gap: 16px;
    padding: 0 16px 16px;
  }

  .v2-admin-workspace-layout {
    padding: 0 16px 16px;
  }

  .v2-admin-overview-cards {
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  }
}

@media (max-width: 720px) {
  .v2-admin-shell-nav {
    flex-direction: row;
    overflow-x: auto;
    padding-bottom: 8px;
  }

  .v2-admin-section-tab {
    flex: 1 0 auto;
    min-width: 170px;
    white-space: nowrap;
  }

  .v2-admin-header {
    gap: 12px;
    margin-bottom: 16px;
  }

  .v2-admin-nav {
    gap: 0;
  }

  .v2-admin-nav-tab {
    padding: 10px 14px;
    font-size: 12px;
  }

  .v2-admin-overview-cards {
    grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
  }
}

/* ── Footer user menu — Part H ── */
.v2-panel-footer-user {
  padding: 8px 10px;
  position: relative;
  flex-shrink: 0;
}

.v2-footer-avatar {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: 8px;
  cursor: pointer;
  transition: background var(--motion-fast) var(--motion-ease);
  border: none;
  background: transparent;
  width: 100%;
  text-align: left;
  font-family: inherit;
}
.v2-footer-avatar:hover { background: var(--hover); }

.v2-footer-avatar-circle {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-family: inherit;
  user-select: none;
  letter-spacing: 0.03em;
}

.v2-footer-user-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.v2-footer-display-name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  display: block;
}

.v2-footer-role-label {
  font-size: 10.5px;
  color: var(--faint);
  white-space: nowrap;
  display: block;
}

.v2-footer-chevron {
  color: var(--faint);
  flex-shrink: 0;
  transition: transform var(--motion-fast) var(--motion-ease);
}

.v2-footer-avatar[aria-expanded="true"] .v2-footer-chevron {
  transform: rotate(180deg);
}

.v2-footer-menu {
  position: absolute;
  bottom: calc(100% - 8px);
  left: 10px;
  right: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  z-index: 50;
  animation: vsm6-box-in var(--motion-normal) var(--motion-ease) both;
}

.v2-footer-menu-item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  color: var(--text);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  transition: background var(--motion-fast) var(--motion-ease);
}
.v2-footer-menu-item:hover { background: var(--hover); }

.v2-footer-menu-item--danger { color: var(--danger, #a8292f); }
.v2-footer-menu-item--danger:hover { background: var(--danger-bg, #fdecea); }

/* ── Interactive search — Part C ── */
.v2-topbar-search--active {
  pointer-events: auto !important;
  user-select: auto !important;
  cursor: text !important;
}

.v2-topbar-search-input {
  flex: 1;
  background: none;
  border: none;
  outline: none;
  font-size: 12.5px;
  color: var(--text);
  font-family: inherit;
  min-width: 0;
  padding: 0;
}

.v2-topbar-search-input::placeholder { color: var(--faint); }
.v2-topbar-search-input::-webkit-search-cancel-button { display: none; }

/* ── Mobile: 16px font prevents iOS Safari zoom on focus ──── */
@media (max-width: 600px) {
  .v2-topbar-search-input { font-size: 16px; }
}

.v2-topbar-search-clear {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: rgba(0,0,0,.18);
  color: var(--text);
  border: none;
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 0;
  font-family: inherit;
  transition: background var(--motion-fast) var(--motion-ease);
}
.v2-topbar-search-clear:hover { background: rgba(0,0,0,.32); }

[data-theme="dark"] .v2-topbar-search-clear {
  background: rgba(255,255,255,.14);
  color: var(--text);
}
[data-theme="dark"] .v2-topbar-search-clear:hover {
  background: rgba(255,255,255,.24);
}

.v2-topbar-search--active:focus-within {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 10%, transparent);
}

/* ── Rail avatar — hidden when panel footer owns user identity ── */
body.v2-shell-active .v2-rail-avatar {
  display: none;
}

/* ── Dark mode transition — Part D ── */
/* Targeted containers only — avoid transitioning every DOM element */
.theme-anim body,
.theme-anim .app-layout,
.theme-anim .v2-topbar,
.theme-anim .v2-rail,
.theme-anim .v2-panel,
.theme-anim .main-area,
.theme-anim .main-content,
.theme-anim .v2-workspace,
.theme-anim .v2-surface-card,
.theme-anim .v2-list-card,
.theme-anim .v2-pending-card,
.theme-anim .v2-admin-section,
.theme-anim .v2-footer-menu,
.theme-anim .v2-topbar-search,
.theme-anim .modal-container,
.theme-anim .modal-backdrop,
/* Phase 7G.2 — .v2-rail above is the PRE-domain-shell rail class; the
   live nav chrome under the current domainShellV1 default is
   .domshell-rail (js/shell/domain-shell.js), which this curated list
   never got updated for. Its background — and its own logo chip's,
   .domshell-rail-logo, now dark-mode-aware for the first time this pass —
   was falling back to the generic 550ms selector further up this file
   instead of this list's 280ms, so the rail visibly kept crossfading for
   ~270ms after the topbar (.v2-topbar, already in this list) had already
   settled. Measured live via a real theme-toggle click, sampling
   getComputedStyle().backgroundColor every animation frame: before this
   line, .v2-topbar reached its final color by ~210ms while .domshell-rail
   and .domshell-rail-logo were still visibly interpolating past 500ms. */
.theme-anim .domshell-rail,
.theme-anim .domshell-rail-logo {
  transition:
    background-color var(--theme-fade-duration) ease,
    color var(--theme-fade-duration) ease,
    border-color var(--theme-border-duration) ease !important;
}
/* Reduced-motion guard for Part D specifically: this block's own
   `!important` transition comes AFTER the early-file universal
   reduced-motion override (same file, ~line 340) at equal-or-higher
   specificity for these class selectors, so without a guard here too it
   would silently win and re-enable the 280ms crossfade even with
   prefers-reduced-motion/data-anim="off" active. */
@media (prefers-reduced-motion: reduce) {
  .theme-anim body, .theme-anim .app-layout, .theme-anim .v2-topbar, .theme-anim .v2-rail,
  .theme-anim .v2-panel, .theme-anim .main-area, .theme-anim .main-content, .theme-anim .v2-workspace,
  .theme-anim .v2-surface-card, .theme-anim .v2-list-card, .theme-anim .v2-pending-card,
  .theme-anim .v2-admin-section, .theme-anim .v2-footer-menu, .theme-anim .v2-topbar-search,
  .theme-anim .modal-container, .theme-anim .modal-backdrop,
  .theme-anim .domshell-rail, .theme-anim .domshell-rail-logo {
    transition: none !important;
  }
}
html[data-anim="off"].theme-anim body, html[data-anim="off"].theme-anim .app-layout,
html[data-anim="off"].theme-anim .v2-topbar, html[data-anim="off"].theme-anim .v2-rail,
html[data-anim="off"].theme-anim .v2-panel, html[data-anim="off"].theme-anim .main-area,
html[data-anim="off"].theme-anim .main-content, html[data-anim="off"].theme-anim .v2-workspace,
html[data-anim="off"].theme-anim .v2-surface-card, html[data-anim="off"].theme-anim .v2-list-card,
html[data-anim="off"].theme-anim .v2-pending-card, html[data-anim="off"].theme-anim .v2-admin-section,
html[data-anim="off"].theme-anim .v2-footer-menu, html[data-anim="off"].theme-anim .v2-topbar-search,
html[data-anim="off"].theme-anim .modal-container, html[data-anim="off"].theme-anim .modal-backdrop,
html[data-anim="off"].theme-anim .domshell-rail, html[data-anim="off"].theme-anim .domshell-rail-logo {
  transition: none !important;
}

/* ═══════════════════════════════════════════════════════════════
   VSM-9 Navigation Architecture Cleanup
   ═══════════════════════════════════════════════════════════════ */

/* ── Part 1: Rail Administration module ── */
/* Administration module visible as a peer to Driver Operations */
body.v2-shell-active #v2RailAdmin { display: none; } /* shown by updatePermissionUI() */

/* ── Part 2: Pending badge — admin + bidang ── */
/* Badge is already styled as .v2-panel-badge; no extra CSS needed */

/* ── Part 3: Timeline dark mode — neutralise V1 CSS variable artifacts ── */
/* V1 style.css uses --white / --gray-1-3 which are undefined in dark mode.
   Scoped to #v2TimelineSurface so V1 modals and V1 flow are unaffected. */
[data-theme="dark"] #v2TimelineSurface {
  --white:  var(--surface);
  --gray-1: var(--tl-head-bg, #202229);
  --gray-2: var(--border);
  --gray-3: var(--border-strong);
  --shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
}

/* ── Part 5: Compact KPI strip (~30% space reduction) ── */
body.v2-shell-active #v2KpiStrip {
  padding: 10px 16px 0;
  gap: 8px;
}
body.v2-shell-active .v2-kpi-card {
  padding: 10px 14px;
  gap: 2px;
}
body.v2-shell-active .v2-kpi-value { font-size: 22px; }

/* ── Part 6: Rail footer — theme button ── */
.v2-rail-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding-bottom: 4px;
  margin-top: auto;
  flex-shrink: 0;
}

.v2-rail-footer-btn {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: transparent;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--side-faint);
  transition:
    background var(--motion-fast, 150ms) ease,
    color      var(--motion-fast, 150ms) ease;
  flex-shrink: 0;
}
.v2-rail-footer-btn:hover,
.v2-rail-footer-btn:focus-visible {
  background: var(--side-active-bg);
  color: var(--side-strong);
  outline: none;
}

/* ── Part 1 (continued): Admin panel nav section label ── */
/* Override the generic "MENU" pseudo-element for the admin nav section */
.v2-panel-nav--admin::before {
  content: 'ADMINISTRASI' !important;
}

/* Disabled nav items (coming-soon) */
.v2-panel-nav-item--disabled {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
}

/* "Soon" badge on disabled nav items */
.v2-panel-nav-badge-soon {
  margin-left: auto;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 5px;
  border-radius: 4px;
  background: var(--hover);
  color: var(--faint);
  border: 1px solid var(--border);
  text-transform: uppercase;
  flex-shrink: 0;
}

/* ════════════════════════════════════════════════════════════════
   V1 MOBILE UX UPDATE — single-row compact mobile topbar (≤767px)
   ════════════════════════════════════════════════════════════════
   Supersedes the old "Part 7 flex row (601-767px) + Phase 11I/11L two-row
   grid (≤600px) + V1 post-QA hotfix three-row grid (≤430px)" cascade —
   three different layouts fighting for the same ≤767px range, documented
   at length in the git history this block replaces (search this repo's
   history for "Phase 11L" / "V1 post-QA hotfix (Issue B)" for the prior
   incidents). ONE CSS Grid template now covers the full 320-767px mobile
   range: hamburger | date-nav (Hari Ini + prev + date + next) | quick
   search (icon-only) | theme toggle | avatar — always a single row, never
   a second row, per the V1 mobile UX update spec (points 1 + 3). Below
   431px, a second block (further down) only tightens gaps/padding/date
   text on the SAME template — it never changes row count. */
@media (max-width: 767px) {
  body.v2-shell-active .v2-topbar {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto auto;
    grid-template-areas: "ham dnav search theme avatar";
    align-items: center;
    column-gap: 6px;
    row-gap: 0;
    height: auto;
    min-height: 44px;
    padding: 6px 10px;
  }
  body.v2-shell-active .v2-topbar #sidebarToggle {
    grid-area: ham;
  }
  body.v2-shell-active .v2-topbar .date-nav {
    grid-area: dnav;
    min-width: 0;
    justify-self: stretch;
  }
  body.v2-shell-active .v2-topbar .domshell-palette-trigger {
    grid-area: search;
    justify-self: center;
  }
  body.v2-shell-active .v2-topbar .v2-topbar-theme-btn {
    grid-area: theme;
    display: flex;
  }
  body.v2-shell-active .v2-topbar .header-user-area {
    grid-area: avatar;
  }
  /* Not part of the mobile toolbar row — remove from the grid entirely so
     it can't consume an implicit extra row via auto-placement. */
  body.v2-shell-active .v2-topbar .v2-topbar-spacer {
    display: none;
  }
  /* Pre-existing gap, surfaced by unifying 601-767px into this same grid:
     #v2TopbarCrumb's base rule (display:none, display:block ≥768px) was
     already being defeated at ALL widths by an unconditional, later
     "display:flex" from the two-line-breadcrumb rework (search this file
     for "Part 1: Two-line breadcrumb") — only ≤600px ever explicitly
     force-hid it again, so 601-767px was showing the decorative desktop
     breadcrumb unintentionally. Not part of the required mobile header
     controls (task 1) and would eat into the compact row's width budget. */
  #v2TopbarCrumb {
    display: none !important;
  }

  /* Quick Search (task 1C): icon-only trigger on mobile — reuses the
     existing command-palette trigger/overlay untouched (same click
     handler, same focus/Escape behaviour); only the "Cari Cepat" label
     text is visually hidden here, so it stays in the accessible name via
     the button's own aria-label. */
  body.v2-shell-active .v2-topbar .domshell-palette-trigger span {
    display: none;
  }
  body.v2-shell-active .v2-topbar .domshell-palette-trigger {
    padding: 0;
    gap: 0;
    justify-content: center;
  }

  /* Consistent control height across the whole row (tasks 1D/1E). The base
     .btn-icon/.btn-today (style.css) carry an app-wide min-height:44px WCAG
     touch-target rule that fights any smaller pill height in this specific
     compact toolbar — scoped override to a uniform, still-tappable 32px
     band for ONLY this mobile topbar context (every control here stays
     comfortably above the 24px WCAG 2.5.5 AA minimum; the same buttons
     keep their normal 44px sizing everywhere else in the app, e.g. any
     other .date-nav instance). */
  body.v2-shell-active .v2-topbar #sidebarToggle,
  body.v2-shell-active .v2-topbar .domshell-palette-trigger,
  body.v2-shell-active .v2-topbar .v2-topbar-theme-btn,
  body.v2-shell-active .v2-topbar .date-nav .btn-today,
  body.v2-shell-active .v2-topbar .date-nav .btn-icon,
  body.v2-shell-active .v2-topbar .date-nav .pbsi-datepicker-trigger {
    height: 32px;
    min-height: 32px;
    box-sizing: border-box;
  }
  body.v2-shell-active .v2-topbar #sidebarToggle,
  body.v2-shell-active .v2-topbar .domshell-palette-trigger,
  body.v2-shell-active .v2-topbar .v2-topbar-theme-btn {
    width: 32px;
    min-width: 32px;
  }
  body.v2-shell-active .v2-topbar .date-nav .btn-icon {
    width: 26px;
    min-width: 26px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  body.v2-shell-active .v2-topbar .date-nav .btn-today {
    padding: 0 8px;
    display: flex;
    align-items: center;
  }
  body.v2-shell-active .v2-topbar .date-nav .pbsi-datepicker-trigger {
    min-width: 0;
    width: auto;
    padding: 0 6px;
    gap: 4px;
    font-size: 11px;
  }
  body.v2-shell-active .v2-topbar .date-nav {
    height: 32px;
    box-sizing: border-box;
    padding: 0 4px;
    gap: 2px;
    /* Pin margin/border explicitly: an unrelated legacy V1 rule
       (style.css, plain ".date-nav" 2-row-header layout, ≤600px) sets
       margin-top:6px + border:none/border-top — it isn't scoped to
       ".v2-topbar" so it still matches this same live node and was
       silently offsetting the pill ~6px below the row's centerline
       (confirmed via getBoundingClientRect — every other 32px-tall
       control shared one center-Y, only date-nav didn't). */
    margin: 0;
    border: 1px solid var(--border);
  }
  /* Avatar grown from its 26px "tighter" desktop-adjacent size (Phase 8
     token pass, elsewhere in this file) up to the same 32px band as every
     other control in this row — width may differ (it's a circle), height
     must not (task 1E). */
  body.v2-shell-active .v2-topbar .header-user-area .v2-topbar-avatar {
    width: 32px;
    height: 32px;
    font-size: 11px;
  }
  /* .header-user-area itself: an unrelated later "rounder" token-refinement
     rule (same selector, no media query, further up this file — Phase 8)
     re-applies padding:4px 10px 4px 7px at equal specificity and wins by
     source order, silently padding the wrapper out to 40px tall even
     though the ≤767px block above already sets padding:0. Re-asserted
     here (this block is later in the file, so it wins the tie) so the
     avatar's wrapper matches every other control's 32px band exactly. */
  body.v2-shell-active .v2-topbar .header-user-area {
    padding: 0;
    min-height: 32px;
  }
}

/* Below 431px: tighter padding/gaps + a shorter date string so the exact
   same single-row template above also fits the narrowest supported phones
   (320-430px) without wrapping (task 1F: "compact at narrow widths,
   roomier above 430"). This never changes row count or which controls are
   present — only their density. 431-767px keeps the roomier spacing from
   the block above. */
@media (max-width: 430px) {
  body.v2-shell-active .v2-topbar {
    column-gap: 4px;
    padding: 6px 6px;
  }
  body.v2-shell-active .v2-topbar .date-nav {
    gap: 1px;
    padding: 0 2px;
  }
  body.v2-shell-active .v2-topbar .date-nav .btn-today {
    padding: 0 6px;
    font-size: 10.5px;
  }
  body.v2-shell-active .v2-topbar .date-nav .pbsi-datepicker-trigger {
    padding: 0 4px;
    gap: 3px;
    font-size: 10.5px;
  }
  /* Shorter date text ("30 Agu" instead of "Min, 30 Agu 2026") — see
     pbsi-datepicker.js's _updateTrigger()/_formatDisplay(); both spans
     always exist, only visibility flips here, scoped to this topbar. */
  body.v2-shell-active .v2-topbar .pbsi-datepicker-display-full {
    display: none;
  }
  body.v2-shell-active .v2-topbar .pbsi-datepicker-display-compact {
    display: inline;
  }
}


/* ════════════════════════════════════════════════════════════════
   VSM-10 — Dark Mode Cleanup + Bidang UX Fix
   ════════════════════════════════════════════════════════════════ */

/* ── Part 1: Timeline scroll-fade edge overlays ─────────────────
   .timeline-scroll-fade-* use literal rgba(255,255,255,0.85) that
   render as opaque white bars at the left/right edges in dark mode.
   Remap gradient source to the surface color so the fade blends
   into the dark canvas without visible white artifacts.          */
[data-theme="dark"] .timeline-scroll-fade-right {
  background: linear-gradient(to left, var(--surface), transparent);
}

/* ── Part 1: Assignment card white inset highlight ──────────────
   The inset top-edge shadow (rgba 255,255,255,0.18) is a
   light-mode "top-surface sheen" trick. In dark mode it produces
   a bright white horizontal line on every assignment card.
   Replace with a deeper drop shadow + subtle transparent ring to
   define card edges without any white glow.                      */
[data-theme="dark"] body.v2-shell-active .assignment-block {
  box-shadow:
    0 1px 4px rgba(0, 0, 0, 0.45),
    0 0 0 1px rgba(255, 255, 255, 0.08);
}
[data-theme="dark"] body.v2-shell-active .assignment-block:hover {
  box-shadow:
    0 3px 12px rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(255, 255, 255, 0.14);
}

/* ── Part 1: Timeline scrollbar — explicit dark override ────────
   CSS custom property cascade to ::-webkit-scrollbar pseudo-
   elements is unreliable across browsers. Explicit rule wins.   */
[data-theme="dark"] #v2TimelineSurface .timeline-body::-webkit-scrollbar-track {
  background: var(--tl-head-bg, #202229);
}
[data-theme="dark"] #v2TimelineSurface .timeline-body::-webkit-scrollbar-thumb {
  background: var(--border-strong, #3E414C);
}

/* ── Part 2: Low-contrast faint text upgrade ────────────────────
   --faint (#71706B) on --surface (#1F2025) is ~3.6:1 contrast —
   below WCAG AA for small text. Upgrade to --muted in dark mode. */
[data-theme="dark"] .v2-kpi-sub {
  color: var(--muted);
}
[data-theme="dark"] body.v2-shell-active .driver-label .driver-phone {
  color: var(--muted);
}

/* ── Part 4: Navigation icon baseline visibility ────────────────
   Inactive nav icons at opacity:0.55 are too faint on the dark
   panel surface. Raise to 0.70 for readable without hover.      */
[data-theme="dark"] .v2-panel-nav-icon {
  opacity: 0.70;
}

/* ── Part 2: Timeline theme-switch exemption ────────────────────
   html.theme-anim * (platform.css line 214) applies a 550ms
   background-color transition to EVERY element. For the timeline
   this causes gradient overlays and assignment card shadows to
   flash through intermediate light states before settling dark.
   Explicitly zero the transition for all timeline descendants so
   the switch is instant and clean. The !important beats the
   html.theme-anim * !important specificity tie by being later.  */
html.theme-anim #v2TimelineSurface,
html.theme-anim #v2TimelineSurface *,
html.theme-anim #v2TimelineSurface *::before,
html.theme-anim #v2TimelineSurface *::after,
html.theme-anim .timeline-scroll-fade-right {
  transition: none !important;
}

/* ── Part 3: Panel module subtitle ─────────────────────────────
   .v2-panel-subtitle sits below .v2-panel-title. It describes
   the active module — text is updated by setRailModule().       */
.v2-panel-subtitle {
  display: block;
  font-size: 10px;
  font-weight: 600;
  color: var(--faint);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-family: var(--font-sans, 'Inter', sans-serif);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 3px;
}

/* ── Part 3: Panel header — tighten bottom padding ─────────────
   Original 18px / 10px padding assumed a single title line.
   With a subtitle the visual weight now fills the header.       */
.v2-panel-header {
  padding: 16px 16px 8px;
}

/* ── Part 4: Footer version label ──────────────────────────────
   Third line inside .v2-footer-user-info — version watermark
   placed directly below the role label. aria-hidden="true" on
   the element so screen readers skip it.                        */
.v2-footer-version-label {
  display: block;
  font-size: 10px;
  font-family: var(--font-mono);
  color: var(--faint);
  white-space: nowrap;
  margin-top: 2px;
  line-height: 1.3;
}

/* ── Part 6: Footer role label contrast ────────────────────────
   --faint (#71706B) on --surface (#1F2025) is ~3.6:1 in dark
   mode. Upgrade role label and version label to --muted.        */
[data-theme="dark"] .v2-footer-role-label,
[data-theme="dark"] .v2-footer-version-label {
  color: var(--muted);
}

/* ── Toolbar layer (search, filter, action) ────────────────────────── */
.v2-admin-toolbar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 1rem 0;
  flex-wrap: wrap;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}

/* Phase 11 (Administration) — Decision 1: view-only-access banner, shown
   on Users/Settings when the session lacks real admin write access.
   Same shape as .ipm-readonly-notice (kept separate: that one lives inside
   a form panel and needs a tighter margin, this sits above a toolbar). */
.v2-admin-readonly-banner {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--surface-2, var(--hover));
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px 14px;
  margin-bottom: 1rem;
}

/* Toolbar sections — search left, filters center, actions right */
.v2-admin-toolbar__search {
  flex: 1 1 280px;
  min-width: 0;
}

.v2-admin-toolbar__filters {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
  min-width: 0;
}

.v2-admin-toolbar__actions {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  align-items: center;
  margin-left: auto;
}

.v2-admin-search {
  flex: 1 1 220px;
  min-width: 0;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 8px;
  padding: 0.65rem 0.85rem;
  font-size: 0.76rem;
  color: var(--text);
  font-family: inherit;
  outline: none;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.v2-admin-search::placeholder {
  color: var(--faint);
}

.v2-admin-search:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}

.v2-admin-filter {
  flex: 0 0 auto;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 8px;
  padding: 0.65rem 0.75rem;
  font-size: 0.74rem;
  color: var(--text);
  font-family: inherit;
  outline: none;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.v2-admin-filter:focus {
  border-color: var(--accent);
}

.v2-admin-filter:hover {
  background: var(--surface-2);
}

.v2-admin-add-btn {
  flex: 0 0 auto;
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  border-radius: 8px;
  padding: 0.65rem 1rem;
  font-size: 0.74rem;
  font-weight: 700;
  font-family: inherit;
  cursor: pointer;
  outline: none;
  transition: filter 0.15s ease;
}

.v2-admin-add-btn:hover {
  filter: brightness(1.05);
}

.v2-admin-add-btn:active {
  filter: brightness(0.95);
}

/* ── Stats layer ───────────────────────────────────────────────────── */
.v2-admin-stats {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 0 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
}

.v2-admin-stats-total {
  flex-shrink: 0;
  color: var(--muted);
  font-weight: 500;
}

.v2-admin-stats-total strong {
  color: var(--text);
  font-weight: 700;
  margin-left: 4px;
}

.v2-admin-stats-chips {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}

.v2-admin-stats-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 0;
}

.v2-admin-stats-chip-label {
  font-size: 12px;
  color: var(--muted);
  text-transform: capitalize;
}

.v2-admin-stats-chip-count {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent-fg);
}

/* ── Placeholder/Roadmap module layer ──────────────────────────────── */
.v2-admin-placeholder-module {
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 680px;
}

.v2-admin-placeholder-header {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

.v2-admin-placeholder-module-title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}

.v2-admin-placeholder-badge {
  display: inline-block;
  padding: 4px 8px;
  background: var(--hover);
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.v2-admin-placeholder-features {
  display: grid;
  gap: 12px;
  list-style: none;
  padding: 0;
  margin: 0;
}

.v2-admin-placeholder-features li {
  padding-left: 24px;
  position: relative;
  color: var(--text);
  font-size: 14px;
  line-height: 1.6;
  font-weight: 400;
}

.v2-admin-placeholder-features li::before {
  content: '✓';
  position: absolute;
  left: 0;
  top: 2px;
  color: var(--accent-fg);
  font-weight: 700;
  font-size: 14px;
}

.v2-admin-user-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0;
  margin-top: 12px;
}

.v2-admin-empty {
  padding: 60px 0;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
}

/* User card */
.v2-user-card {
  display: flex;
  align-items: center;
  gap: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 0.95rem 1.1rem;
  box-shadow: var(--shadow-sm);
  transition: box-shadow .15s ease, border-color .15s ease;
}

.v2-user-card:hover {
  border-color: var(--border-strong, color-mix(in srgb, var(--border) 85%, black));
  box-shadow: var(--shadow-md);
}

.v2-user-card--inactive {
  opacity: 0.6;
}

/* Avatar ring — role-coloured border */
.v2-user-avatar-ring {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  border: 2px solid transparent;
}
.v2-user-avatar--admin   { background: var(--danger-bg);  border-color: var(--danger);  color: var(--danger);  }
.v2-user-avatar--bidang  { background: var(--info-bg);    border-color: var(--info);    color: var(--info);    }
.v2-user-avatar--driver  { background: var(--warn-bg);    border-color: var(--warn);    color: var(--warn);    }
.v2-user-avatar--viewer  { background: var(--ok-bg);      border-color: var(--ok);      color: var(--ok);      }

.v2-user-avatar-initials {
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: .03em;
}

/* Name + username */
.v2-user-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1 1 0;
  min-width: 0;
}
.v2-user-display-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v2-user-username {
  font-size: 11px;
  color: var(--muted);
  font-family: var(--font-mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Pill badges */
.v2-user-meta {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.v2-user-role-pill {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 20px;
}
.v2-role-pill--admin   { background: var(--danger-bg);  color: var(--danger);  }
.v2-role-pill--bidang  { background: var(--info-bg);    color: var(--info);    }
.v2-role-pill--driver  { background: var(--warn-bg);    color: var(--warn);    }
.v2-role-pill--viewer  { background: var(--ok-bg);      color: var(--ok);      }

.v2-user-status-pill {
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  padding: 0.25rem 0.55rem;
  border-radius: 999px;
  background: var(--ok-bg);
  color: var(--ok);
  transition: background 0.15s ease;
}
.v2-user-status-pill.v2-status-pill--inactive {
  background: var(--surface-2);
  color: var(--muted);
  border: 1px solid var(--border);
}
/* Leave statuses (Cuti/Sakit/Izin) — amber, theme-aware via --warn tokens. v1.16.4.4 */
.v2-user-status-pill.v2-status-pill--leave {
  background: var(--warn-bg);
  color: var(--warn);
}

/* Action buttons */
.v2-user-card-actions {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
}
.v2-user-btn {
  font-size: 0.74rem;
  font-weight: 700;
  font-family: inherit;
  border-radius: 9px;
  padding: 0.4rem 0.75rem;
  cursor: pointer;
  border: 1px solid var(--border);
  transition: filter 0.12s ease, background 0.12s ease;
  white-space: nowrap;
  background: var(--surface-2);
  color: var(--text);
  /* Phase 9 mobile-first audit: narrower breakpoints below shrink padding/
     font to fit 3 buttons per row, which had also been shrinking the tap
     target itself to ~23-26px on driver/user cards' Edit/Nonaktifkan/
     Arsipkan actions. min-height floors it at the 44px guideline regardless
     of how tight the padding gets; centering keeps the label from riding
     to the top of the taller box. */
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.v2-user-btn:hover {
  filter: brightness(0.96);
}
.v2-user-btn--edit {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text);
}
.v2-user-btn--edit:hover {
  filter: brightness(0.94);
}

.v2-user-btn--toggle {
  background: var(--warn-bg);
  color: var(--warn);
  border-color: var(--warn);
}
.v2-user-btn--toggle:hover {
  filter: brightness(0.92);
}
/* .v2-user-btn--archive/--delete/--restore: styled further down by the
   "V1.5.3 — Archive & Safe Deletion Framework" block, which — same
   specificity, later in source order — is what actually wins the cascade;
   a same-selector duplicate here was pure dead weight (Phase 11
   Administration audit, P3). */
.v2-user-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Responsive: collapse meta + actions on narrow widths */
@media (max-width: 800px) {
  .v2-user-card {
    gap: 0.85rem;
    padding: 0.85rem 0.95rem;
  }
  .v2-user-btn {
    padding: 0.35rem 0.6rem;
    font-size: 0.7rem;
  }
}

@media (max-width: 560px) {
  .v2-user-card {
    flex-wrap: wrap;
    gap: 0.7rem;
    padding: 0.8rem 0.9rem;
  }
  .v2-user-info {
    flex: 1 1 100%;
    order: 1;
  }
  .v2-user-meta {
    order: 2;
    width: 100%;
    gap: 0.5rem;
  }
  .v2-user-card-actions {
    order: 3;
    width: 100%;
    gap: 0.4rem;
  }
  .v2-user-btn {
    flex: 1 1 auto;
    padding: 0.35rem 0.5rem;
    font-size: 0.68rem;
    border-radius: 8px;
  }
}

/* ── V1.5.0 Phase 3.1: Driver linked-account pill ───────────── */
.v2-driver-linked-pill {
  background: var(--info-bg);
  color: var(--info);
  font-family: var(--font-mono);
  font-size: 10px;
}

/* ── V1.5.2: Vehicle Management ─────────────────────────────── */

/* Vehicle avatar — colored ring using the vehicle's timeline color */
.v2-vehicle-avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border: 2px solid transparent;
  background-size: cover;
  background-position: center;
}

.v2-vehicle-color-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  display: block;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}

/* Capacity chip — lighter, more breathing room */
.v2-vehicle-cap-chip {
  display: inline-flex;
  align-items: center;
  padding: 0.35rem 0.65rem;
  border-radius: 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--text);
  white-space: nowrap;
  transition: background 0.15s ease;
}

/* Color picker row in vehicle form */
.v2-vehicle-color-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.v2-vehicle-color-input {
  width: 42px;
  height: 34px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--input-bg);
  cursor: pointer;
  flex-shrink: 0;
}

.v2-vehicle-color-preview {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: block;
  flex-shrink: 0;
  border: 1px solid rgba(0,0,0,.1);
}

.v2-vehicle-color-hex {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

/* ── VSM-12 Part 4: Dark mode audit ────────────────────────────
   Pending workspace labels that use --faint (3.42:1 on dark
   surface) — upgrade to --muted for WCAG AA.                     */
[data-theme="dark"] .v2-pending-label,
[data-theme="dark"] .v2-pending-age {
  color: var(--muted);
}

/* Dark mode: inset card shadows already removed for assignment
   blocks in VSM-10; admin user card hover gets darker tint */
[data-theme="dark"] .v2-user-card {
  box-shadow: 0 1px 4px rgba(0,0,0,.35);
}
[data-theme="dark"] .v2-user-card:hover {
  box-shadow: 0 3px 12px rgba(0,0,0,.50);
}


/* ════════════════════════════════════════════════════════════════
   VEHICLE INVENTORY (v1.18.2) — Executive Asset presentation

   The inventory is the HERO of Vehicle Management. These styles inherit the
   Dispatch Analytics design language (token-driven, daa-sec geometry: 18px
   radius, --shadow-sm, surface/border tokens, same type rhythm) but define a
   purpose-built ASSET card — a vehicle is an asset, never a person, so it does
   NOT reuse .v2-user-card. Cards are pure navigation objects (whole card opens
   the drawer; no inline action buttons). Tokens only — dark-mode automatic.
   ════════════════════════════════════════════════════════════════ */

/* v1.18.4 — the section header migrated to the Executive UI Kit (.exec-head);
   the old .vm-inv__* header rules were removed (see js/app.js inventory markup). */

/* Responsive asset grid: desktop auto-fill, tablet 2-col, mobile 1-col */
.vm-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
  gap: var(--workspace-card-gap, 16px);
  min-width: 0;
}
.vm-grid .v2-admin-empty { grid-column: 1 / -1; }

/* Executive asset card — daa-sec geometry, clickable surface */
.vm-asset {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  border: 1px solid var(--border);
  border-radius: 18px;
  background: var(--surface);
  box-shadow: var(--shadow-sm);
  padding: 1rem 1.05rem;
  cursor: pointer;
  min-width: 0;
  transition: box-shadow .16s ease, border-color .16s ease, transform .16s ease;
  text-align: left;
}
.vm-asset:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}
.vm-asset:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.vm-asset--inactive { opacity: 0.72; }
.vm-asset--archived { background: var(--surface-2); }

/* Identity row */
.vm-asset__top {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-width: 0;
}
.vm-asset__ico {
  flex: 0 0 auto;
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text);
}
.vm-asset__id {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
  flex: 1 1 auto;
}
.vm-asset__name {
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vm-asset__plate {
  font-size: 0.74rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.vm-asset__health {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  line-height: 1.02;
}
.vm-asset__health b {
  font-size: 1.4rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}
.vm-asset__health b[data-tone="ok"] { color: var(--ok); }
.vm-asset__health b[data-tone="warn"] { color: var(--warn); }
.vm-asset__health b[data-tone="danger"] { color: var(--danger); }
.vm-asset__health i {
  font-style: normal;
  font-size: 0.56rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  font-weight: 700;
}

/* Secondary metadata line (type · year · transmission · fuel) */
.vm-asset__meta {
  font-size: 0.72rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* Status strip — now uses the Executive Badge System (exec-pill, one grammar).
   v1.18.4: the bespoke pill family was removed; cards render
   ExecutiveStatusPill (see buildVehicleCard in js/app.js). */
.vm-asset__strip {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}

/* Footer — last activity */
.vm-asset__foot {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--border);
  font-size: 0.7rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}
.vm-asset__foot b { color: var(--text); font-weight: 700; }

@media (max-width: 720px) {
  .vm-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 520px) {
  .vm-grid { grid-template-columns: 1fr; }
}
/* Phase 11J — defensive pass: .vm-grid/.vm-asset already have min-width:0
   and collapse to 1 column at 520px, so no reproducible overflow bug was
   found; --workspace-card-gap has no narrow-width tier at all, so this adds
   one for the smallest real phones, mirroring the same tightening already
   proven for Analytics' .hm-stats. */
@media (max-width: 380px) {
  .vm-grid { gap: 12px; }
  .vm-asset { padding: 0.85rem 0.9rem; }
}


/* ════════════════════════════════════════════════════════════════
   VSM-13 — Accessibility, Contrast & Dark Mode Polish

   Part 1+2 handled via [data-theme="dark"] token block edits:
     --faint upgraded to #908C86 (4.78:1 WCAG AA)
     --accent upgraded to #B8454A (5.33:1 with white)
     --on-accent set to #FFFFFF

   Parts 3–6 below.
   ════════════════════════════════════════════════════════════════ */


/* ── Part 3: Global scrollbar system ────────────────────────────
   Uniform thin scrollbars. Sizes set globally; colors per theme.
   Existing high-specificity rules (timeline, sidebar) still win. */

/* Size — universal, lowest specificity so class-level overrides win */
::-webkit-scrollbar              { width: 8px; height: 8px; }
::-webkit-scrollbar-corner       { background: transparent; }

/* Light theme */
::-webkit-scrollbar-track        { background: #F3F4F6; }
::-webkit-scrollbar-thumb        { background: #CBD5E1; border-radius: 4px; }
::-webkit-scrollbar-thumb:hover  { background: #94A3B8; }
:root,
[data-theme="light"] { scrollbar-width: thin; scrollbar-color: #CBD5E1 #F3F4F6; }

/* Dark theme — scoped under html[data-theme="dark"] so descendant
   pseudo-element rules beat the bare light-mode rules above      */
html[data-theme="dark"] ::-webkit-scrollbar-track       { background: #161A22; }
html[data-theme="dark"] ::-webkit-scrollbar-thumb       { background: #3A4152; border-radius: 4px; }
html[data-theme="dark"] ::-webkit-scrollbar-thumb:hover { background: #4C556A; }
[data-theme="dark"] { scrollbar-color: #3A4152 #161A22; }

/* Preserve explicitly hidden scrollbars (sidebar, timeline hours) */
.sidebar::-webkit-scrollbar,
.sidebar-nav::-webkit-scrollbar,
.timeline-hours::-webkit-scrollbar { display: none !important; }


/* ── Part 4: Notification card V2 redesign ──────────────────────
   Overrides the Phase-1 / VSM-5 .notif-card block by cascade.
   No HTML changes — only CSS.                                     */

.notifications-list { gap: 10px; }

.notif-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--border-strong);
  border-radius: 10px;
  padding: 13px 15px;
  box-shadow: var(--shadow-sm);
  transition: background .13s, box-shadow .13s;
  cursor: default;
}
@media (hover: hover) {
  .notif-card:hover {
    background: var(--hover);
    box-shadow: var(--shadow-md);
  }
}
.notif-priority-high   { border-left-color: var(--danger); }
.notif-priority-medium { border-left-color: var(--warn);   }
.notif-priority-low    { border-left-color: var(--border-strong); }

.notif-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 5px;
}

.notif-card-title {
  flex: 1 1 0;
  min-width: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.3;
}

.notif-card-time {
  flex-shrink: 0;
  font-size: 10px;
  font-family: var(--font-mono);
  color: var(--muted);
  margin-top: 2px;
  white-space: nowrap;
}

.notif-card-desc {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.55;
}

/* Activity log: same hierarchy uplift */
.activity-log-subtitle { color: var(--muted); }
.activity-log-time     { color: var(--muted); }

/* ── Notification: section headers + unread state + detail rows ─
   Uses semantic tokens → works in light & dark automatically.    */

.notif-section-header {
  color: var(--faint);
  font-size: 10.5px;
}

.notif-unread {
  background: var(--surface-2);
}

.notif-new-dot {
  background: var(--danger);
}

.notif-card-details {
  margin-top: 7px;
  gap: 4px;
}

.notif-card-row {
  font-size: 12px;
}

.notif-card-label {
  color: var(--faint);
  min-width: 72px;
}

.notif-card-value {
  color: var(--text);
}

/* Dark mode: tint unread cards slightly against surface */
[data-theme="dark"] .notif-unread {
  background: var(--hover);
}


/* ── Part 5: Mobile quality audit ───────────────────────────────
   Polish for V2 workspaces on 375–600px viewports.              */

/* Admin workspace: tighter horizontal padding on phone */
@media (max-width: 960px) {
  .v2-admin-workspace-layout {
    gap: 24px;
    padding: 0 16px 32px;
  }

  .v2-admin-page-header {
    padding-bottom: 16px;
  }

  .v2-admin-page-title {
    font-size: 24px;
  }

  .v2-admin-overview-cards {
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  }

  .v2-admin-toolbar {
    padding: 16px 0;
  }
}

@media (max-width: 720px) {
  .v2-admin-workspace-layout {
    gap: 20px;
    padding: 0 12px 24px;
  }

  .v2-admin-page-title {
    font-size: 20px;
  }

  .v2-admin-page-subtitle {
    font-size: 13px;
  }

  .v2-admin-overview-cards {
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 12px;
  }

  .v2-admin-overview-value {
    font-size: 32px;
  }

  .v2-admin-overview-label {
    font-size: 11px;
  }

  .v2-admin-nav {
    margin-top: 4px;
    gap: 2px;
  }

  .v2-admin-nav-tab {
    padding: 8px 12px;
    font-size: 12px;
  }

  .v2-admin-toolbar {
    padding: 12px 0;
    gap: 8px;
  }

  .v2-admin-search {
    flex-basis: 100%;
  }

  .v2-admin-filter {
    flex: 1 1 0;
  }

  .v2-admin-user-list {
    gap: 8px;
  }

  .v2-user-card {
    padding: 12px;
    gap: 10px;
  }

  .v2-user-card-actions {
    gap: 4px;
  }

  .v2-user-btn {
    padding: 6px 10px;
    font-size: 12px;
  }
}

/* Old media query for backward compatibility - kept but not used */

/* Workspace panels: compact padding on small phone */
@media (max-width: 480px) {
  .v2-workspace               { padding: 10px 0 0; }
  .v2-workspace-header        { padding: 0 12px; margin-bottom: 12px; }
  .v2-workspace-title         { font-size: 16px; }
  .v2-workspace-subtitle      { font-size: 12px; }
  .v2-pending-card            { padding: 11px 12px; }
  .v2-pending-card-body       { grid-template-columns: 1fr; gap: 6px 0; }
}

/* User card: ultra-compact on very narrow screens */
@media (max-width: 400px) {
  .v2-user-card               { padding: 10px; gap: 8px; }
  .v2-user-meta               { gap: 4px; }
  .v2-user-card-actions       { gap: 4px; }
  .v2-user-btn                { padding: 4px 8px; font-size: 11px; }
}

/* Notification list: scrollable in tight modal on phone */
@media (max-width: 600px) {
  .notifications-list { max-height: 55vh; overflow-y: auto; }
  .activity-log-list  { max-height: 35vh; overflow-y: auto; }
}


/* ── Part 6: Accessibility — focus rings & keyboard nav ─────────
   All interactive V2 elements get a visible 2px accent ring
   on :focus-visible (keyboard focus only, not mouse).           */

/* Admin workspace controls */
.v2-admin-search:focus-visible,
.v2-admin-filter:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.v2-admin-add-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.v2-user-btn:focus-visible      { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Pending workspace buttons */
.v2-pending-btn:focus-visible   { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Topbar icon buttons */
.v2-topbar-icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Login screen */
.login-submit:focus-visible     { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Placeholder contrast — Firefox normalisation (opacity:1 default in Chrome) */
::placeholder { opacity: 1; }

/* Disabled inputs: keep label readable at reduced opacity */
input:disabled,
select:disabled,
textarea:disabled { opacity: 0.55; }

/* KPI/metadata contrast: --faint token upgraded globally in dark token
   block. Explicit belt-and-suspenders for notification timestamps.    */
[data-theme="dark"] .notif-card-time,
[data-theme="dark"] .activity-log-time,
[data-theme="dark"] .activity-log-subtitle,
[data-theme="dark"] .v2-topbar-user-role { color: var(--muted); }


/* ════════════════════════════════════════════════════════════════
   VSM-14 — Final Polish & Complete Application Audit

   Scope: every V1 component that has no dark-mode override.
   Strategy: append high-specificity [data-theme="dark"] rules
   so style.css originals stay untouched — rollback = delete block.

   Sections:
     A  Global token fixes (both themes)
     B  Modal chrome (header, title, body, close)
     C  Form system (labels, inputs, focus, readonly, toggles)
     D  Button system (secondary, success, danger, WA, reimburse)
     E  Profile modal (all sub-components)
     F  Detail modal accordion
     G  Status & role badges
     H  User / notification cards (V1)
     I  Typography & hierarchy cleanup
     J  Additional V2 consistency polish
   ════════════════════════════════════════════════════════════════ */


/* ── A: Global token fixes (both light & dark themes) ───────────
   Replace hardcoded colours with semantic tokens so dark mode
   token redefinitions propagate automatically.                   */

/* btn-success: hardcoded #43a047 → semantic --ok token */
.btn-success {
  background: var(--ok);
  color: #FFFFFF;
}
.btn-success:hover:not(:disabled) { background: color-mix(in srgb, var(--ok) 85%, #000); }

/* Toggle switch checked state: hardcoded #A5D6A7 → --ok */
.toggle-switch input:checked + .switch-slider { background: var(--ok); }

/* Role chips: hardcoded hex → semantic bg/fg tokens */
.profile-role-chip[data-role="admin"]  {
  background: var(--danger-bg); color: var(--danger);
}
.profile-role-chip[data-role="bidang"] {
  background: var(--info-bg);   color: var(--info);
}
.profile-role-chip[data-role="viewer"] {
  background: var(--ok-bg);     color: var(--ok);
}
.profile-role-chip[data-role="driver"] {
  background: var(--warn-bg);   color: var(--warn);
}

/* Telegram connected badge */
.profile-tg-badge[data-status="connected"] {
  background: var(--ok-bg); color: var(--ok); border-color: var(--ok);
}

/* Status badges in detail modal — semantic tokens */
.badge-aktif  { background: var(--ok-bg);   color: var(--ok);     border-color: var(--ok);     }
.badge-selesai { background: var(--info-bg); color: var(--info);   border-color: var(--info);   }

.badge-status--pending   { background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn);   }
.badge-status--approved  { background: var(--info-bg);   color: var(--info);   border-color: var(--info);   }
.badge-status--assigned  { background: var(--ok-bg);     color: var(--ok);     border-color: var(--ok);     }
.badge-status--started   { background: var(--warn-bg);   color: var(--warn);   border-color: var(--warn);   }
.badge-status--completed { background: var(--info-bg);   color: var(--info);   border-color: var(--info);   }
.badge-status--cancelled { background: var(--danger-bg); color: var(--danger); border-color: var(--danger); }

/* ── v1.10.7 Assignment Cancellation Workflow ── */
.detail-row--cancelled .detail-label,
.detail-row--cancelled .detail-value { color: var(--danger); }

.cancel-warning {
  margin: 0 0 14px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--danger-bg);
  color: var(--danger);
  font-size: 13px;
  line-height: 1.45;
  border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent);
}
.cancel-reason-counter {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
}
.cancel-reason-counter--ok { color: var(--ok); }

/* WA accordion: semantic green tokens */
#accordWA { border-color: var(--ok); background: var(--ok-bg); }
#accordWA .accord-title,
#accordWA .accord-chevron { color: var(--ok); }
@media (hover: hover) {
  #accordWA .accord-header:hover { background: color-mix(in srgb, var(--ok-bg) 90%, var(--ok)); }
}
.wa-preview-box { background: var(--ok-bg); border-color: var(--ok); }
.wa-preview-label { color: var(--ok); }
.copy-feedback  { color: var(--ok); }
.btn-wa-copy    { background: var(--ok); }
.btn-wa-copy:hover { background: color-mix(in srgb, var(--ok) 85%, #000); }


/* ── B: Modal chrome — dark mode ────────────────────────────────
   .modal-box, .modal-header, .modal-title use hardcoded V1 values
   (--white, --gray-2, --dark). Override for dark theme.          */

[data-theme="dark"] .modal-box {
  background: var(--surface);
  border-color: var(--border);
}

/* Phase 11I: same white-trap this block already fixes for .modal-box —
   .bottom-sheet (the "Lainnya" more-menu) used var(--white) directly and
   was missed in this pass, so its text (already correctly dark-remapped)
   rendered near-invisible on a white background. */
[data-theme="dark"] .bottom-sheet {
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}

[data-theme="dark"] .modal-header {
  background: var(--surface);
  border-bottom-color: var(--border);
}

[data-theme="dark"] .modal-title { color: var(--text); }

[data-theme="dark"] .modal-close {
  color: var(--muted);
}
[data-theme="dark"] .modal-close:hover { color: var(--text); }

[data-theme="dark"] .modal-body { color: var(--text); }


/* ── C: Form system — dark mode ──────────────────────────────────
   All inputs, selects, textareas in .form-group use V1 --gray-*
   tokens. Override everything for dark.                           */

[data-theme="dark"] .form-group label { color: var(--muted); }

[data-theme="dark"] .form-group input,
[data-theme="dark"] .form-group select,
[data-theme="dark"] .form-group textarea {
  background: var(--input-bg);
  border-color: var(--input-border);
  color: var(--text);
  font-family: inherit;
}

[data-theme="dark"] .form-group input::placeholder,
[data-theme="dark"] .form-group textarea::placeholder { color: var(--faint); }

[data-theme="dark"] .form-group input:focus,
[data-theme="dark"] .form-group select:focus,
[data-theme="dark"] .form-group textarea:focus {
  border-color: var(--accent);
  background: var(--input-bg);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent);
}

[data-theme="dark"] .form-group input[readonly] { color: var(--muted); }

[data-theme="dark"] .time-separator { color: var(--muted); }

[data-theme="dark"] .multi-day-checkbox-text { color: var(--text); }
[data-theme="dark"] .multi-day-hint          { color: var(--muted); }
[data-theme="dark"] .multi-day-checkbox-label {
  background: var(--surface-2);
  border-color: var(--border);
}
[data-theme="dark"] .multi-day-checkbox-label:hover {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(184, 69, 74, 0.12);
}
[data-theme="dark"] .multi-day-checkbox-label:has(input[type="checkbox"]:focus-visible) {
  outline-color: var(--accent);
}
[data-theme="dark"] .multi-day-checkbox-label:has(input[type="checkbox"]:checked) {
  border-color: var(--accent);
  background: rgba(184, 69, 74, 0.12);
}
[data-theme="dark"] .multi-day-checkbox-label:has(input[type="checkbox"]:checked)::after {
  background: var(--accent);
}

/* Toggle switch — dark background surface */
[data-theme="dark"] .toggle-switch {
  background: var(--input-bg);
  border-color: var(--input-border);
}
[data-theme="dark"] .switch-slider { background: var(--border-strong); }

/* form-group input[readonly] in dark */
[data-theme="dark"] .form-group input[type="checkbox"] {
  accent-color: var(--accent);
}

/* Form actions row */
[data-theme="dark"] .form-actions { border-top-color: var(--border); }

/* Tag optional */
[data-theme="dark"] .tag-optional {
  background: var(--input-bg);
  color: var(--muted);
}


/* ── D: Button system — dark mode ────────────────────────────────
   .btn-secondary, .btn-danger, .btn-reimbursement               */

[data-theme="dark"] .btn-secondary {
  background: var(--input-bg);
  border-color: var(--input-border);
  color: var(--text);
}
[data-theme="dark"] .btn-secondary:hover {
  background: var(--hover);
  border-color: var(--border-strong);
}

[data-theme="dark"] .btn-danger {
  background: var(--danger);
  color: #FFFFFF;
}
[data-theme="dark"] .btn-danger:hover {
  background: color-mix(in srgb, var(--danger) 85%, #000);
}

/* Reimbursement button: inverts from dark (#1A1917) to surface */
[data-theme="dark"] .btn-reimbursement {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border);
}
[data-theme="dark"] .btn-reimbursement:hover {
  background: var(--hover);
}

/* Paste small button */
[data-theme="dark"] .btn-paste-small {
  background: var(--input-bg);
  border-color: var(--input-border);
  color: var(--text);
}
[data-theme="dark"] .btn-paste-small:hover {
  background: var(--hover);
  border-color: var(--border-strong);
}

/* Profile test button */
[data-theme="dark"] .profile-test-btn { color: var(--muted); }
[data-theme="dark"] .profile-test-btn:hover { color: var(--text); }


/* ── E: Profile modal — dark mode ───────────────────────────────
   Complete override of every profile sub-component.             */

[data-theme="dark"] .profile-user-card {
  background: var(--surface-2);
  border-color: var(--border);
}

[data-theme="dark"] .profile-display-name { color: var(--text); }

[data-theme="dark"] .profile-section-label { color: var(--muted); }

[data-theme="dark"] .profile-tg-badge {
  background: var(--input-bg);
  border-color: var(--border);
  color: var(--muted);
}

[data-theme="dark"] .profile-help-text { color: var(--muted); }

[data-theme="dark"] .profile-code-btn {
  background: var(--input-bg);
  border-color: var(--border);
  color: var(--text);
}
[data-theme="dark"] .profile-code-btn:hover { background: var(--hover); }

[data-theme="dark"] .profile-status-text { color: var(--muted); }

[data-theme="dark"] .profile-toggle-row {
  border-top-color:    var(--border);
  border-bottom-color: var(--border);
}
[data-theme="dark"] .profile-toggle-text { color: var(--text); }

[data-theme="dark"] .profile-divider { background: var(--border); }

/* Informasi Aplikasi block — built on legacy V1 tokens (--gray-1/--gray-2/
   --dark/--text-muted) that the dark token block does NOT remap, so it
   rendered with a near-white (#F5F5F5) background in dark mode. Re-point it
   to the dark surface tokens at higher specificity (the documented
   "--white trap" for legacy-token surfaces). */
[data-theme="dark"] .app-info-block {
  background: var(--surface-2);
  border-color: var(--border);
}
[data-theme="dark"] .app-info-name    { color: var(--text); }
[data-theme="dark"] .app-info-version { color: var(--muted); }

[data-theme="dark"] .profile-inline-btn { color: var(--accent-fg); }

/* Toggle switch: checked state dark */
[data-theme="dark"] .toggle-switch input:checked + .switch-slider {
  background: var(--ok);
}

/* Profile section header inline btn */
[data-theme="dark"] .profile-section-header .profile-inline-btn { color: var(--accent-fg); }

/* Reimbursement hint */
[data-theme="dark"] .reimbursement-hint { color: var(--muted); }

/* Detail empty state */
[data-theme="dark"] .detail-empty { color: var(--muted); }


/* ── F: Detail modal accordion — dark mode ───────────────────────
   .accord-section, .accord-header, .accord-title, .accord-body  */

[data-theme="dark"] .accord-section {
  background: var(--surface);
  border-color: var(--border);
}

[data-theme="dark"] #accordSummary {
  background: var(--surface-2);
  border-color: var(--border-strong);
}

[data-theme="dark"] .accord-header:active { background: var(--hover); }
@media (hover: hover) {
  [data-theme="dark"] .accord-header:hover { background: var(--hover); }
  [data-theme="dark"] #accordSummary .accord-header:hover { background: var(--input-bg); }
}

[data-theme="dark"] .accord-title   { color: var(--text); }
[data-theme="dark"] .accord-chevron { color: var(--muted); }

[data-theme="dark"] .detail-label   { color: var(--muted); }
[data-theme="dark"] .detail-value   { color: var(--text); }
[data-theme="dark"] .detail-ts      { color: var(--faint); }

/* WA accordion in dark: subtle green on dark surface */
[data-theme="dark"] #accordWA {
  background: var(--ok-bg);
  border-color: var(--ok);
}

/* WA preview box in dark */
[data-theme="dark"] .wa-preview-box {
  background: var(--ok-bg);
  border-color: var(--ok);
}
[data-theme="dark"] .wa-preview-text { color: var(--text); }


/* ── G: Status & role badges — dark mode ─────────────────────────
   All badges already use semantic tokens via Section A, so the
   dark-mode token redefinitions handle them automatically.
   Only extra: vehicle badge uses inline color (JS-injected).     */

[data-theme="dark"] .vehicle-badge { opacity: 0.90; }

/* Notification dot in sidebar */
[data-theme="dark"] .notification-dot {
  background: var(--danger);
  border: 2px solid var(--surface);
}


/* ── H: User / notification cards (V1) — dark mode ──────────────
   .user-card and .notification-item use --gray-1 background.    */

[data-theme="dark"] .user-card,
[data-theme="dark"] .notification-item {
  background: var(--surface-2);
  border-color: var(--border);
}

[data-theme="dark"] .user-title { color: var(--text); }
[data-theme="dark"] .user-sub,
[data-theme="dark"] .notification-time,
[data-theme="dark"] .notification-target { color: var(--muted); }

/* ── H2: User Management — stats + role groups — dark mode ── */
[data-theme="dark"] .users-stats {
  background: var(--surface-2);
  border-color: var(--border);
}

[data-theme="dark"] .users-stats-total { color: var(--muted); }
[data-theme="dark"] .users-stats-total strong { color: var(--text); }

[data-theme="dark"] .users-stats-chip {
  background: var(--surface);
  border-color: var(--border);
}

[data-theme="dark"] .users-stats-chip-label { color: var(--muted); }
[data-theme="dark"] .users-stats-chip-count { color: var(--text); }

[data-theme="dark"] .user-role-header {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--text);
  box-shadow: 0 1px 4px rgba(0,0,0,.30);
}

[data-theme="dark"] .user-role-header:hover {
  background: var(--grid);
  box-shadow: 0 2px 8px rgba(0,0,0,.45);
}

[data-theme="dark"] .user-role-count-badge {
  background: var(--grid);
  color: var(--muted);
}

[data-theme="dark"] .user-role-empty {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--muted);
}


/* ── I: Typography & hierarchy — dark mode consistency ───────────
   Elements where V1 --dark / --text-muted cause readability gaps.*/

/* Form actions — text labels near buttons */
[data-theme="dark"] .form-section-title { color: var(--text); }

/* Detail hint text */
[data-theme="dark"] .detail-hint { color: var(--muted); }

/* Section caption/subtitle patterns across all modals */
[data-theme="dark"] .section-caption,
[data-theme="dark"] .field-hint,
[data-theme="dark"] .helper-text { color: var(--muted); }

/* Reimbursement section */
[data-theme="dark"] .accord-body-inner { color: var(--text); }


/* ── J: Additional V2 consistency polish ─────────────────────────
   Visual consistency pass: radius, spacing, shadow alignment.   */

/* Standardise .btn-secondary radius to match V2 (8px) */
.btn-secondary { border-radius: 8px; }
.btn-danger    { border-radius: 8px; }
.btn-success   { border-radius: 8px; }

/* Modal close button: rounder, clearer hover */
.modal-close {
  border-radius: 6px;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font-size: 20px;
  transition: background .12s, color .12s;
}
.modal-close:hover { background: var(--hover); }
[data-theme="dark"] .modal-close:hover { background: var(--hover); }

/* Modal title: consistent weight with V2 workspace titles */
.modal-title { font-size: 16px; font-weight: 700; letter-spacing: -0.02em; }

/* Accordion section: slightly rounder to match V2 cards */
.accord-section { border-radius: 8px; }

/* user-card in admin modal: align with V2 card radius */
.user-card { border-radius: 8px; }

/* Form label: consistent with V2 pending/admin labels */
.form-group label {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* Scrollbar for modal-box (inherits global from VSM-13 Part 3) */
.modal-box { scrollbar-width: thin; }

/* Focus ring on modal close button */
.modal-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Form actions: consistent bottom spacing */
.form-actions {
  padding-top: 14px;
  border-top: 1px solid var(--border);
  margin-top: 4px;
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* Accordion body inner: consistent padding */
.accord-body-inner { padding: 6px 14px 14px; }

/* ── Mobile: profile & form modal on phone ───────────────────────
   Ensure profile modal is usable on 390px screens.             */
@media (max-width: 480px) {
  .profile-modal-box { margin: 8px; width: calc(100vw - 16px); max-width: none; }
  .profile-user-card { padding: 12px 13px; gap: 10px; }
  .profile-input-row { flex-direction: column; gap: 6px; }
  .profile-input-row input { width: 100%; }
  .btn-paste-small { width: 100%; justify-content: center; padding: 9px; }
  .form-grid { grid-template-columns: 1fr; }
}

/* ═══════════════════════════════════════════════════════════════
   VSM-15 — Dark Mode Final Polish
   ───────────────────────────────────────────────────────────────
   Parts 1–6: Timeline assignment visibility, grid contrast,
   scrollbar hover, sidebar footer hierarchy, bidang label
   (app.js), and full-app consistency audit.
   Append-only. style.css is never modified.
   ═══════════════════════════════════════════════════════════════ */

/* ── Part 1A: Assignment text — restore white ───────────────────
   VSM-9 Part 3 sets --white: var(--surface) inside #v2TimelineSurface,
   so .assignment-block's color: var(--white) renders dark text on
   coloured block backgrounds. Explicit #fff breaks the cascade.   */
[data-theme="dark"] #v2TimelineSurface .assignment-block {
  color: #FFFFFF;
}

/* ── Part 1B: Vehicle block colours — dark-mode-appropriate ─────
   style.css uses vivid Material Design hex values. In dark mode we
   override with the already-defined muted --v-* tokens so blocks
   sit clearly on the dark grid without harsh saturation.
   Specificity [1,1,1] beats style.css's bare attr selector [0,1,0]. */
[data-theme="dark"] #v2TimelineSurface [data-vehicle="Innova"]   { background: var(--v-innova); }
[data-theme="dark"] #v2TimelineSurface [data-vehicle="Luxio"]    { background: var(--v-luxio);  }
[data-theme="dark"] #v2TimelineSurface [data-vehicle="Polytron"] { background: var(--v-poly);   }
[data-theme="dark"] #v2TimelineSurface [data-vehicle="Hiace"]    { background: var(--v-hiace);  }

/* ── Part 1C: Block elevation — stronger shadow in dark ─────────
   Assignment blocks are the primary element; they must read as
   clearly elevated over the grid. VSM-10's 1px shadow is too light.
   Remove the inset top-highlight (visible white line in VSM-9) and
   replace with deeper drop shadow + more opaque ring.              */
[data-theme="dark"] body.v2-shell-active .assignment-block {
  box-shadow:
    0 2px 8px rgba(0,0,0,.60),
    0 0 0 1px rgba(255,255,255,.12);
}
[data-theme="dark"] body.v2-shell-active .assignment-block:hover {
  box-shadow:
    0 4px 14px rgba(0,0,0,.70),
    0 0 0 1px rgba(255,255,255,.20);
  filter: brightness(1.10);
}

/* ── Part 1D: Completed blocks — reduce dark-mode fading ────────
   style.css applies opacity: 0.5 + grayscale(35%). On dark-muted
   vehicle colours the result is nearly invisible. Ease both values
   so completed slots remain identifiable without appearing active.  */
[data-theme="dark"] .assignment-block.is-completed {
  opacity: 0.65;
  filter: grayscale(20%);
}
[data-theme="dark"] .assignment-block.is-completed:hover {
  opacity: 0.78;
  filter: grayscale(10%) brightness(1.08);
}

/* ── Part 2: Grid lines — halve visual weight ───────────────────
   --gray-2 = --border = #31333C draws attention away from blocks.
   Opacity 0.5 on the ::before pseudo-element that draws vertical
   hour lines softens the grid noise without touching .driver-row
   border-bottom or .driver-label border-right (which remain full). */
[data-theme="dark"] #v2TimelineSurface .driver-slots::before {
  opacity: 0.5;
}

/* ── Part 3: Timeline scrollbar — thumb hover state ────────────
   VSM-10 defined track + thumb colours but no hover state.
   Thumb brightens on hover consistent with the global scrollbar
   pattern added in VSM-13.                                         */
[data-theme="dark"] #v2TimelineSurface .timeline-body::-webkit-scrollbar-thumb:hover {
  background: #4C556A;
}

/* ── Part 4A: Footer role label — reduce visual weight ──────────
   12.5px name / 10.5px role / 10px version is nearly two distinct
   sizes instead of three. Shrinking role to 10px creates a clear
   primary→secondary→tertiary stack.                                */
.v2-footer-role-label { font-size: 10px; }

/* ── Part 4B: Footer version label — truly tertiary in dark ─────
   VSM-11 promoted both role + version to --muted for WCAG AA.
   VSM-13 upgraded --faint to #908C86 (4.78:1, WCAG AA-compliant).
   Version is informational, not actionable — it should whisper.
   Revert to var(--faint) now that the token itself passes AA.      */
[data-theme="dark"] .v2-footer-version-label { color: var(--faint); }

/* ── Part 6: Consistency audit — minor sweeps ───────────────────
   Items surfaced during final review across all views and roles.

   a. Footer user-info: tighten vertical gap so name/role/version
      group feels like a single unit, not three separate labels.
   b. Nav icons: 0.55 opacity on --side-text renders almost
      invisible in dark mode. 0.70 maintains hierarchy while
      keeping icons readable against the dark sidebar.             */
.v2-footer-user-info { gap: 0; }

[data-theme="dark"] .v2-panel-nav-icon { opacity: 0.70; }


/* ════════════════════════════════════════════════════════════════
   STARTUP SPLASH — FOUC prevention
   Prevents V1 shell from flashing before V2 shell is initialized.

   Strategy:
     • body:not(.app-ready) { visibility: hidden } is declared in
       a <style> block in <head> — active at first paint, before
       any JavaScript runs.
     • body:not(.app-ready) #app-splash { visibility: visible }
       overrides the inheritance so only the splash is painted.
     • When app.js finishes initialization it adds .app-ready to
       body → splash fades out, V2 shell becomes visible.
   ════════════════════════════════════════════════════════════════ */

/* ── Splash container ───────────────────────────────────────────
   Full-viewport overlay.  Background matches the page canvas so
   there is no color seam between splash and first V2 frame.      */
#app-splash {
  position: fixed;
  inset: 0;
  z-index: 99999;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--page-bg, #FBFAF8); /* v1.20.8 — matches manifest.json background_color so the OS-drawn splash (Android) doesn't flash a mismatched color before this splash paints */
  transition: opacity 0.18s ease;
}

/* Fade to invisible once body.app-ready is set.                  */
.app-ready #app-splash {
  opacity: 0;
  pointer-events: none;
}

/* ── Splash content ─────────────────────────────────────────────
   Minimal: wordmark + platform label + spinner.
   Uses Inter from the already-preconnected Google Fonts request.  */
.app-splash-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  user-select: none;
}

/* Sarpras Operations logo above the wordmark. Explicit box (matches the
   width/height attributes on the <img>) so there is no layout shift while
   the asset decodes. Responsive size handled below. */
/* Sizing/chrome now lives on the .brand-mark--splash wrapper (Phase 6);
   this class just marks the <img> itself for the mobile query below. */
.app-splash-logo { display: block; }

.app-splash-name {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 22px;
  font-weight: 600;
  letter-spacing: 0.12em;
  color: var(--text, #1A1917);
}

.app-splash-sub {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--muted, #8C8981);
}

/* ── Spinner ────────────────────────────────────────────────────
   CSS-only rotating ring.  Visible only during init (≈50–300ms
   on fast networks; up to 3s on slow networks / Firebase timeout).*/
.app-splash-spinner {
  display: block;
  width: 18px;
  height: 18px;
  margin-top: 20px;
  border-radius: 50%;
  border: 2px solid var(--border, rgba(0, 0, 0, 0.12));
  border-top-color: var(--accent, #C62828);
  opacity: 0;
  /* Delayed fade-in: a fast boot never shows this at all — the ring only
     becomes visible if still on screen after ~380ms, so it reads as "loading
     is taking a moment" rather than a spinner dominating every launch. */
  animation:
    startup-spin 0.75s linear infinite,
    startup-spinner-in 160ms ease 380ms forwards;
}

@keyframes startup-spin {
  to { transform: rotate(360deg); }
}
@keyframes startup-spinner-in {
  to { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .app-splash-spinner { animation: none; opacity: .8; }
}

/* ── Dark-mode overrides ────────────────────────────────────────
   CSS custom properties already flip automatically via
   [data-theme="dark"] rules higher in this file.  The explicit
   overrides below are only needed for properties that use literal
   fallback values (the second arg of var()) as a safety net.     */
[data-theme="dark"] #app-splash {
  background: var(--page-bg, #17181C);
}
[data-theme="dark"] .app-splash-spinner {
  border-color: var(--border, rgba(255, 255, 255, 0.10));
  border-top-color: var(--accent, #B8454A);
}

/* Mobile: slightly smaller splash logo (applied pre-paint → no layout shift). */
@media (max-width: 480px) {
  .brand-mark--splash { width: 64px; height: 64px; margin-bottom: 12px; }
}


/* ════════════════════════════════════════════════════════════════
   PBSI TOGGLE COMPONENT — Reusable premium setting row + toggle
   ────────────────────────────────────────────────────────────────
   Usage (HTML):
     <label class="pbsi-setting-row">
       <div class="pbsi-setting-content">
         <span class="pbsi-setting-title">Title</span>
         <span class="pbsi-setting-desc">Description text.</span>
         <span class="pbsi-setting-status" id="...">Nonaktif</span>
       </div>
       <input type="checkbox" id="..." class="pbsi-toggle-input" role="switch" />
     </label>

   Usage (JS — sync status text):
     syncSettingStatus(id, isOn)  → defined in admin.js
     applyTheme() also syncs #statusDarkModeToggle automatically.

   Design references: iOS Settings · Linear · Notion · Slack Preferences
   ════════════════════════════════════════════════════════════════ */

/* ── Setting Row — full-row clickable container ──────────────────
   Using <label> as outer element gives full-row click-to-toggle
   semantics without any JS. -8px margin bleeds the hover bg
   slightly beyond the section's content box for a cleaner feel.   */
.pbsi-setting-row {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 12px 10px;
  margin: 0 -10px;
  border-radius: 10px;
  cursor: pointer;
  transition: background 0.14s ease;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

.pbsi-setting-row:hover  { background: var(--hover, rgba(0,0,0,.04)); }
.pbsi-setting-row:active { background: color-mix(in srgb, var(--hover, rgba(0,0,0,.04)) 150%, transparent); }

/* ── Setting content — left text column ─────────────────────────
   flex: 1 + min-width: 0 prevents long description text from
   pushing the toggle pill off-screen on narrow viewports.         */
.pbsi-setting-content {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: 2px;   /* optical alignment: centre-line of title ≈ centre of toggle pill */
}

.pbsi-setting-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--text, #1A1917);
  line-height: 1.35;
}

.pbsi-setting-desc {
  font-size: 12px;
  color: var(--muted, #5B5953);
  line-height: 1.55;
  margin-top: 1px;
}

.pbsi-setting-status {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--muted, #5B5953);
  margin-top: 5px;
  transition: color 0.18s ease;
}

/* Green when toggle is ON */
.pbsi-setting-status.is-active { color: var(--ok, #2F7D62); }

/* ── Toggle pill — custom checkbox, no native appearance ─────────
   Dimensions:
     Outer (border-box): 52 × 30 px  — comfortable touch target
     Border: 1.5 px each side        — inner: 49 × 27 px
     Thumb: ⌀22 px, gap 3 px each side
     translateX(21px) = (49 − 22 − 3) − 3 = 21 px          */
.pbsi-toggle-input {
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  flex-shrink: 0;
  width: 52px;
  height: 30px;
  border-radius: 999px;
  background: #D1D5DB;
  border: 1.5px solid #C4C7CE;
  cursor: pointer;
  position: relative;
  margin-top: 1px;
  transition:
    background  0.22s ease,
    border-color 0.22s ease;
  outline: none;
}

/* Thumb */
.pbsi-toggle-input::after {
  content: '';
  position: absolute;
  top: 3px;
  left: 3px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: #FFFFFF;
  /* Subtle double shadow: lift + edge definition */
  box-shadow:
    0 1px 4px rgba(0,0,0,.22),
    0 0 0 0.5px rgba(0,0,0,.05);
  /* Spring easing for premium tactile feel */
  transition: transform 0.22s var(--ease-overshoot, cubic-bezier(0.34, 1.3, 0.64, 1));
}

/* ON state */
.pbsi-toggle-input:checked {
  background: var(--ok, #2F7D62);
  border-color: var(--ok, #2F7D62);
}
.pbsi-toggle-input:checked::after {
  transform: translateX(21px);
}

/* Keyboard focus ring (WCAG AA) */
.pbsi-toggle-input:focus-visible {
  outline: 2px solid var(--accent, #A8292F);
  outline-offset: 2px;
}

/* ── Dark mode ───────────────────────────────────────────────────
   OFF: --border-strong for the pill bg (visible but muted).
   ON:  --ok resolves to the dark-palette green (#72AF8F).
   Thumb: slightly warm off-white, shadow amplified.              */
[data-theme="dark"] .pbsi-setting-row:hover {
  background: var(--hover, rgba(255,255,255,.05));
}
[data-theme="dark"] .pbsi-setting-row:active {
  background: color-mix(in srgb, var(--hover, rgba(255,255,255,.05)) 160%, transparent);
}
[data-theme="dark"] .pbsi-toggle-input {
  background: var(--border-strong, #3E414C);
  border-color: var(--border-strong, #3E414C);
}
[data-theme="dark"] .pbsi-toggle-input::after {
  background: #E8E6E2;
  box-shadow: 0 1px 5px rgba(0,0,0,.45), 0 0 0 0.5px rgba(0,0,0,.18);
}
[data-theme="dark"] .pbsi-toggle-input:checked {
  background: var(--ok, #72AF8F);
  border-color: var(--ok, #72AF8F);
}


/* ════════════════════════════════════════════════════════════════
   REQUEST CARD — DARK MODE & MOBILE OPTIMIZATION

   Root cause: .request-card and siblings in style.css use
   hardcoded light-mode variables (--gray-1, --white, --dark,
   --red-light, literal hex values) that never update in dark
   mode. Zero dark-mode overrides existed.

   This block is pure CSS — no HTML or JS changes.
   Mobile density improvements are theme-agnostic (both modes).
   Desktop layout is unchanged.
   ════════════════════════════════════════════════════════════════ */

/* ── 1  Card surface ─────────────────────────────────────────── */

[data-theme="dark"] .request-card {
  background: var(--surface);
  border-color: var(--border);
  box-shadow: var(--shadow-sm);
}

/* ── 2  Status-specific card treatment ───────────────────────── */

/* Approved: faint success tint on the card itself */
[data-theme="dark"] .request-card[data-status="approved"] {
  background: color-mix(in srgb, var(--surface) 94%, var(--ok) 6%);
  border-color: color-mix(in srgb, var(--ok) 35%, transparent);
}

/* Rejected: danger-tinted surface, no harsh solid-red border     */
[data-theme="dark"] .request-card[data-status="rejected"] {
  background: var(--danger-bg);
  border-color: color-mix(in srgb, var(--danger) 35%, transparent);
}

/* ── 3  Typography hierarchy ─────────────────────────────────── */

/* Title — high emphasis */
[data-theme="dark"] .request-title {
  color: var(--text);
}

/* Requester · date · time, and inline notes — medium emphasis    */
[data-theme="dark"] .request-meta,
[data-theme="dark"] .request-notes {
  color: var(--muted);
}

/* Driver row, vehicle badge context — medium emphasis            */
[data-theme="dark"] .request-details {
  color: var(--muted);
}

/* ── 4  Status pill ──────────────────────────────────────────── */

/* Default / pending — muted surface chip                         */
[data-theme="dark"] .request-status {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--muted);
}

/* Approved — semantic green */
[data-theme="dark"] .request-card[data-status="approved"] .request-status {
  background: var(--ok-bg);
  border-color: color-mix(in srgb, var(--ok) 45%, transparent);
  color: var(--ok);
}

/* Rejected — semantic danger, softer than light-mode             */
[data-theme="dark"] .request-card[data-status="rejected"] .request-status {
  background: color-mix(in srgb, var(--danger-bg) 80%, var(--surface) 20%);
  border-color: color-mix(in srgb, var(--danger) 40%, transparent);
  color: var(--danger);
}

/* ── 5  Duration / multi-day badge ──────────────────────────── */
/* Base: hardcoded #E3F2FD / #1565C0 (light blue) — not dark-aware */
[data-theme="dark"] .request-duration {
  background: var(--info-bg);
  border-color: color-mix(in srgb, var(--info) 40%, transparent);
  color: var(--info);
}

/* ── 6  Vehicle badge — request card context ────────────────── */
/* inline style="background:..." can't be overridden via color;
   filter desaturates + dims the vivid vehicle palette so it sits
   comfortably on dark surfaces without eye-strain.               */
[data-theme="dark"] .request-card .vehicle-badge {
  filter: saturate(0.55) brightness(0.78);
  opacity: 1;
}

/* ── 7  Card actions separator ──────────────────────────────── */
[data-theme="dark"] .request-card .request-card-actions {
  margin-top: 0;
  padding-top: 11px;
  border-top: 1px solid var(--border);
}

/* ── 8  Comment button — elevated chip style ────────────────── */
/* Visually distinct from action buttons: surface-2 background,
   muted text, smaller padding — integrates into card vs floating */
[data-theme="dark"] .request-card-actions
  .btn-secondary[data-request-action="comment"] {
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--muted);
  font-size: 12px;
  padding: 5px 10px;
}
[data-theme="dark"] .request-card-actions
  .btn-secondary[data-request-action="comment"]:hover {
  background: var(--hover);
  border-color: var(--border-strong);
  color: var(--text);
}

/* ── 9  Empty state ──────────────────────────────────────────── */
[data-theme="dark"] .empty-request-state {
  background: var(--surface);
  border-color: var(--border);
  color: var(--muted);
}

/* ════════════════════════════════════════════════════════════════
   REQUEST CARD — MOBILE DENSITY IMPROVEMENTS
   Applies in both light and dark mode.
   Desktop layout (≥768px) unchanged.
   ════════════════════════════════════════════════════════════════ */

@media (max-width: 767px) {

  /* Tighter card gap — more cards visible without scrolling      */
  .requests-list { gap: 8px; }

  /* Comment button: compact emoji chip, does NOT flex-grow       */
  .request-card-actions
    .btn-secondary[data-request-action="comment"] {
    flex: 0 0 auto;
    padding: 6px 10px;
    font-size: 12px;
    line-height: 1;
  }

  /* Admin action buttons (Edit / Tolak / Setujui) expand evenly  */
  .request-card-actions
    button:not([data-request-action="comment"]) {
    flex: 1;
  }

  /* Right-align the action row so comment stays rightmost        */
  .request-card-actions {
    justify-content: flex-end;
    align-items: center;
  }

  /* Tighter details row on mobile                                */
  .request-details { margin-top: 7px; gap: 6px; font-size: 12px; }
}

/* ── Mobile + dark mode combined ────────────────────────────── */
@media (max-width: 767px) {
  /* Comment chip: deeper surface on mobile dark for contrast      */
  [data-theme="dark"] .request-card-actions
    .btn-secondary[data-request-action="comment"] {
    background: var(--input-bg);
    border-color: var(--input-border);
    color: var(--faint);
  }
  [data-theme="dark"] .request-card-actions
    .btn-secondary[data-request-action="comment"]:hover {
    background: var(--hover);
    color: var(--muted);
  }
}


/* ================================================================
   Notification Center — sticky footer layout + action tray polish

   Layout chain:
     .modal-box          → flex column, overflow hidden
     .modal-header       → flex-shrink 0 (top, never moves)
     .modal-body         → flex 1, no padding (tray bleeds edge-to-edge)
     #notificationsContent → flex 1, scrolls, own inset padding
     .detail-actions     → action tray, surface-2, flex-shrink 0
   ================================================================ */

#modalNotifications .modal-box {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* position:sticky on .modal-header is a no-op inside overflow:hidden;
   flex order keeps it at top naturally */
#modalNotifications .modal-header {
  flex-shrink: 0;
}

/* Zero padding so the tray background bleeds to the modal edges */
#modalNotifications .modal-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
  padding: 0;
}

/* Scrollable list: own inset restores the removed body padding */
#modalNotifications #notificationsContent {
  flex: 1;
  overflow-y: auto;
  min-height: 0;
  padding: 16px 20px 12px;
}

/* ── Action tray ──
   surface-2 is #FBFAF8 (light) / #262830 (dark) — one step above
   the modal floor, creating natural separation from the card list.
   --border separator and 14px vertical padding keep it uncluttered. */
#modalNotifications .detail-actions {
  flex-shrink: 0;
  background: var(--surface-2);
  border-top: 1px solid var(--border);
  padding: 14px 20px;
  margin-top: 0;
}

/* No explicit dark-mode override needed:
   --surface-2 and --border switch automatically via :root[data-theme="dark"] */

/* ── Mobile: match modal-body horizontal inset (14px),
   safe-area padding on the tray covers iPhone home indicator ── */
@media (max-width: 767px) {
  #modalNotifications .modal-body {
    padding: 0;
  }
  #modalNotifications #notificationsContent {
    padding: 12px 14px 10px;
  }
  #modalNotifications .detail-actions {
    padding: 12px 14px;
    padding-bottom: max(12px, env(safe-area-inset-bottom, 12px));
  }
}


/* ================================================================
   Form-Guard Confirmation Dialog
   z-index 400 — above modal overlays (200) and sidebar (150)
   ================================================================ */

#modalFormGuardDialog {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 400;
  padding: 20px;
}

.form-guard-box {
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  box-shadow: var(--shadow-lg);
  padding: 24px;
  max-width: 360px;
  width: 100%;
  animation: vsm6-box-in var(--motion-normal, 0.18s) var(--motion-ease, ease);
}

.form-guard-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 8px;
  line-height: 1.3;
}

.form-guard-msg {
  font-size: 14px;
  color: var(--muted);
  line-height: 1.6;
  margin: 0 0 20px;
}

.form-guard-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

@media (max-width: 480px) {
  .form-guard-actions {
    flex-direction: column-reverse;
  }
  .form-guard-btn {
    width: 100%;
    text-align: center;
  }
}

/* ============================================================
   V1.3.2 — V2 UI Consistency Sprint
   Fixes: C-01 select arrow · M-03 calendar icon · N-04 placeholder
   Placed at end to win cascade over all earlier background shorthands.
   ============================================================ */

/* C-01: Custom select chevron — removes native OS dropdown arrow.
   background longhand properties are re-declared here because earlier
   platform.css rules use the background: shorthand which resets
   background-image; placing these rules last wins the cascade. */
.form-group select,
.v2-admin-filter {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7'%3E%3Cpath d='M1 1l4.5 4.5L10 1' stroke='%23757575' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 34px;
}
[data-theme="dark"] .form-group select,
[data-theme="dark"] .v2-admin-filter {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7'%3E%3Cpath d='M1 1l4.5 4.5L10 1' stroke='%23A6A39B' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}

/* M-03: Hide native calendar picker indicator icon.
   Clicking anywhere on the input still opens the date picker. */
input[type="date"]::-webkit-calendar-picker-indicator {
  opacity: 0;
  cursor: pointer;
}

/* N-04: Placeholder contrast — WCAG 3:1 on --input-bg (#FBFAF8).
   --gray-4 (#757575) gives ~4.4:1; dark mode already uses --faint at 5:1. */
.form-group input:not([type="checkbox"]):not([type="radio"])::placeholder,
.form-group textarea::placeholder {
  color: var(--gray-4);
}

/* ============================================================
   v1.3.3 — Native Component Replacement Sprint I
   ============================================================ */

/* ── T1: PBSI Stepper ────────────────────────────────────────
   Replaces <input type="number"> for bounded integer fields.
   Uses a hidden input as the form value source; JS manages it.
   Layout: [−] value-display [+] */
.pbsi-stepper {
  display: inline-flex;
  align-items: center;
  gap: 0;
  height: 38px;
  border: 1.5px solid var(--input-border);
  border-radius: 8px;
  background: var(--input-bg);
  overflow: hidden;
}

.pbsi-stepper-btn {
  flex-shrink: 0;
  width: 36px;
  height: 100%;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 18px;
  font-weight: 400;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--motion-fast, .12s) var(--motion-ease, ease);
  user-select: none;
  -webkit-user-select: none;
}
.pbsi-stepper-btn:hover:not(:disabled) {
  background: var(--hover, rgba(0,0,0,.06));
}
.pbsi-stepper-btn:active:not(:disabled) {
  background: color-mix(in srgb, var(--hover, rgba(0,0,0,.06)) 200%, transparent);
}
.pbsi-stepper-btn:disabled {
  color: var(--faint);
  cursor: not-allowed;
}
.pbsi-stepper-btn:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: 6px;
}

.pbsi-stepper-display {
  flex: 1;
  min-width: 36px;
  text-align: center;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  border-left: 1px solid var(--input-border);
  border-right: 1px solid var(--input-border);
  padding: 0 6px;
  pointer-events: none;
  line-height: 36px;
}

/* v1.18.3.3 — editable numeric variant of the stepper display.
   Same footprint/typography as the static span; only the middle becomes
   tappable + typeable. Neutralises default <input> chrome (top/bottom border,
   background, spinners, default outline) so the control looks unchanged. */
input.pbsi-stepper-display {
  height: 100%;
  border-top: 0;
  border-bottom: 0;
  background: transparent;
  pointer-events: auto;
  -webkit-appearance: none;
  -moz-appearance: textfield;
  appearance: none;
  outline: none;
}
input.pbsi-stepper-display:focus { outline: none; }
input.pbsi-stepper-display:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
input.pbsi-stepper-display::-webkit-outer-spin-button,
input.pbsi-stepper-display::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

[data-theme="dark"] .pbsi-stepper {
  border-color: var(--input-border);
  background: var(--input-bg);
}
[data-theme="dark"] .pbsi-stepper-btn:hover:not(:disabled) {
  background: var(--hover, rgba(255,255,255,.06));
}
[data-theme="dark"] .pbsi-stepper-display {
  border-color: var(--input-border);
}

/* ── T2: Admin search icon ───────────────────────────────────
   Background SVG mirrors the Timeline topbar search glass.
   Cascade placement here ensures it wins over earlier
   background: shorthands in the admin toolbar rules.         */
.v2-admin-search {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23757575' width='14' height='14'%3E%3Cpath fill-rule='evenodd' d='M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z' clip-rule='evenodd'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: left 10px center;
  padding-left: 32px;
}
[data-theme="dark"] .v2-admin-search {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23A6A39B' width='14' height='14'%3E%3Cpath fill-rule='evenodd' d='M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z' clip-rule='evenodd'/%3E%3C/svg%3E");
}

/* ── T3: Form-context toggle row ────────────────────────────
   Wraps .pbsi-toggle-input with a label inside a .form-group.
   Simpler than .pbsi-setting-row — no hover card, no desc.   */
.pbsi-form-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0;
  cursor: pointer;
  user-select: none;
}
.pbsi-form-toggle-label {
  font-size: 13px;
  color: var(--text-secondary, var(--text-muted));
}

/* Fix: .form-group (column flex, align-items:stretch) would stretch the toggle
   to full container width. align-self:flex-start shrinks it to content width.
   .form-group input has specificity 0,1,1 which overrides .pbsi-toggle-input
   (0,1,0); the two-class selector below (0,2,0) restores the fixed 52px width. */
.form-group .pbsi-form-toggle {
  align-self: flex-start;
}
.form-group .pbsi-toggle-input {
  width: 52px;
  flex-shrink: 0;
}

/* ── T4: Comment character counter ──────────────────────────
   .comment-input-wrap takes the flex: 1 slot that the bare
   textarea previously held; counter sits directly below it.  */
.comment-input-wrap {
  flex: 1;
  min-width: 0;
}
.comment-input-wrap textarea {
  width: 100%;
}

.comment-char-counter {
  display: block;
  text-align: right;
  font-size: 11px;
  color: var(--faint);
  margin-top: 3px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  transition: color .15s ease;
}
.comment-char-counter--warn { color: var(--warn, #B45309); }
.comment-char-counter--full { color: var(--error, var(--accent)); }

/* ============================================================
   v1.4.0 — PBSI Select Component
   ============================================================ */

/* ── Wrapper: fills layout slot of the hidden native select ── */
.pbsi-select {
  position: relative;
  width: 100%;
  display: block;
}

/* ── Trigger button: visually identical to .form-group select ─
   Resets all browser button chrome, then applies PBSI tokens.  */
.pbsi-select-trigger {
  /* Reset button defaults */
  box-sizing: border-box;
  -webkit-appearance: none;
  appearance: none;
  border: none;
  outline: none;
  margin: 0;
  padding: 0;

  /* Layout */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  width: 100%;
  padding: 8px 10px;

  /* Typography — matches .form-group input */
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.4;
  text-align: left;

  /* Appearance — matches .form-group select token overrides */
  background: var(--input-bg);
  color: var(--text);
  border: 1.5px solid var(--input-border);
  border-radius: var(--radius-sm, 6px);

  cursor: pointer;
  transition: border-color .12s, box-shadow .12s, background .12s;
}

.pbsi-select-trigger:hover:not(:disabled) {
  background: var(--hover, rgba(0,0,0,.03));
  border-color: color-mix(in srgb, var(--input-border) 60%, var(--text));
}

.pbsi-select-trigger:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
  outline: none;
}

/* Open state: accent border (same as native :focus) */
.pbsi-select-trigger--open {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}

.pbsi-select-trigger:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── Value span ─────────────────────────────────────────────── */
.pbsi-select-value {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pbsi-select-value--placeholder {
  color: var(--faint);
}

/* ── Chevron ────────────────────────────────────────────────── */
.pbsi-select-chevron {
  flex-shrink: 0;
  color: var(--text-secondary, #757575);
  transition: transform .2s cubic-bezier(.4,0,.2,1);
  pointer-events: none;
}

.pbsi-select-trigger--open .pbsi-select-chevron {
  transform: rotate(180deg);
}

/* ── Portal panel ────────────────────────────────────────────
   Portal-rendered: position:fixed, lives on <body>.
   z-index 350 — above modals (200) and backdrop (140),
   below form-guard confirmation dialog (400).              */
.pbsi-select-panel {
  position: fixed;
  z-index: 350;

  background: var(--surface-card, var(--white, #fff));
  border: 1.5px solid var(--input-border);
  border-radius: var(--radius-sm, 6px);
  box-shadow:
    0 4px 6px -1px rgba(0,0,0,.08),
    0 10px 20px -4px rgba(0,0,0,.10);

  max-height: 240px;
  overflow-y: auto;
  overflow-x: hidden;

  /* Smooth open animation */
  opacity: 0;
  transform: translateY(-4px);
  transition:
    opacity  .13s ease,
    transform .13s ease;

  /* Scrollbar styling (matches platform.css pattern) */
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.pbsi-select-panel::-webkit-scrollbar       { width: 4px; }
.pbsi-select-panel::-webkit-scrollbar-track { background: transparent; }
.pbsi-select-panel::-webkit-scrollbar-thumb { background: var(--border); border-radius: 2px; }

.pbsi-select-panel--open {
  opacity: 1;
  transform: translateY(0);
}

/* Flip-up variant: animate upward instead */
.pbsi-select-panel--above {
  transform: translateY(4px);
}
.pbsi-select-panel--above.pbsi-select-panel--open {
  transform: translateY(0);
}

/* ── Option items ─────────────────────────────────────────── */
.pbsi-select-option {
  padding: 9px 12px;
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text);
  cursor: pointer;
  transition: background .08s ease;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Minimum touch target */
  min-height: 38px;
  display: flex;
  align-items: center;
}

/* Hover state */
.pbsi-select-option:hover,
.pbsi-select-option--active {
  background: var(--hover, rgba(0,0,0,.05));
}

/* Selected state — accent tint left border */
.pbsi-select-option--selected {
  color: var(--accent-fg);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
  font-weight: 500;
}

.pbsi-select-option--selected::before {
  content: '';
  display: inline-block;
  width: 3px;
  height: 14px;
  background: var(--accent);
  border-radius: 2px;
  margin-right: 8px;
  flex-shrink: 0;
}

/* Keyboard-highlighted overrides selected bg */
.pbsi-select-option--active.pbsi-select-option--selected {
  background: color-mix(in srgb, var(--hover, rgba(0,0,0,.05)) 200%, color-mix(in srgb, var(--accent) 5%, transparent));
}

/* Placeholder option ("-- Pilih ...") */
.pbsi-select-option--placeholder {
  color: var(--faint);
  font-weight: 400;
}
.pbsi-select-option--placeholder::before {
  display: none;
}

/* ── Dark mode ────────────────────────────────────────────── */
[data-theme="dark"] .pbsi-select-trigger {
  background: var(--input-bg);
  border-color: var(--input-border);
  color: var(--text);
}
[data-theme="dark"] .pbsi-select-trigger:hover:not(:disabled) {
  background: var(--hover, rgba(255,255,255,.05));
  border-color: color-mix(in srgb, var(--input-border) 60%, var(--text));
}
[data-theme="dark"] .pbsi-select-chevron {
  color: var(--text-secondary, #A6A39B);
}
[data-theme="dark"] .pbsi-select-panel {
  background: var(--surface-card, #2A2A2A);
  border-color: var(--input-border);
  box-shadow:
    0 4px 6px -1px rgba(0,0,0,.30),
    0 10px 20px -4px rgba(0,0,0,.40);
}
[data-theme="dark"] .pbsi-select-option {
  color: var(--text);
}
[data-theme="dark"] .pbsi-select-option:hover,
[data-theme="dark"] .pbsi-select-option--active {
  background: var(--hover, rgba(255,255,255,.06));
}
[data-theme="dark"] .pbsi-select-option--selected {
  color: var(--accent-fg);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
[data-theme="dark"] .pbsi-select-option--active.pbsi-select-option--selected {
  background: color-mix(in srgb, var(--hover, rgba(255,255,255,.06)) 200%, color-mix(in srgb, var(--accent) 12%, transparent));
}

/* ── Mobile: 16px font prevents iOS Safari zoom on focus ──── */
@media (max-width: 600px) {
  .pbsi-select-trigger { font-size: 16px; padding: 9px 10px; }
  .pbsi-select-option  { font-size: 16px; }
}

/* ── v2-admin-filter variant (outside .form-group) ──────────
   The admin role filter sits in .v2-admin-toolbar, not in a
   .form-group. Its trigger inherits toolbar flex sizing.     */
.v2-admin-toolbar .pbsi-select {
  flex: 0 0 auto;
}
.v2-admin-toolbar .pbsi-select-trigger {
  border-radius: 8px;
  font-size: 13px;
  padding: 8px 12px;
}

/* ============================================================
   PBSI DATEPICKER COMPONENT  (v1.4.1)
   ============================================================ */

/* ── Wrapper ──────────────────────────────────────────────── */
.pbsi-datepicker {
  position: relative;
  display: block;
  width: 100%;
}

/* ── Preset strip ─────────────────────────────────────────── */
.pbsi-datepicker-presets {
  display: flex;
  gap: 4px;
  margin-bottom: 6px;
  flex-wrap: nowrap;
}

.pbsi-datepicker-preset {
  flex: 1;
  padding: 5px 6px;
  font-family: var(--font-sans);
  font-size: 11.5px;
  font-weight: 500;
  background: transparent;
  border: 1.5px solid var(--input-border);
  border-radius: 20px;
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  transition: border-color .12s, background .12s, color .12s;
}
.pbsi-datepicker-preset:hover {
  background: var(--hover);
  border-color: var(--accent);
  color: var(--accent-fg);
}
.pbsi-datepicker-preset--active {
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  border-color: var(--accent);
  color: var(--accent-fg);
  font-weight: 600;
}

/* ── Trigger button ───────────────────────────────────────── */
.pbsi-datepicker-trigger {
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 7px;
  width: 100%;
  padding: 8px 10px;
  font-family: var(--font-sans);
  font-size: 14px;
  background: var(--input-bg);
  color: var(--text);
  border: 1.5px solid var(--input-border);
  border-radius: var(--radius-sm, 6px);
  cursor: pointer;
  text-align: left;
  transition: border-color .12s;
}
.pbsi-datepicker-trigger:hover {
  border-color: var(--accent);
}
.pbsi-datepicker-trigger:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}

/* ── Display span ─────────────────────────────────────────── */
.pbsi-datepicker-display {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pbsi-datepicker-display--empty {
  color: #757575;
}
/* V1 mobile UX update: _updateTrigger() always renders BOTH a full and a
   compact span (see pbsi-datepicker.js); default everywhere is full-only.
   The V1 mobile topbar (≤430px) flips this — see the compact mobile
   header rules further down this file. */
.pbsi-datepicker-display-compact { display: none; }

/* ── Calendar icon ────────────────────────────────────────── */
.pbsi-datepicker-icon {
  flex-shrink: 0;
  color: var(--faint);
  opacity: .7;
}

/* ── Date-nav header variant (filterDate) ─────────────────── */
.date-nav .pbsi-datepicker {
  width: auto;
}
.date-nav .pbsi-datepicker-trigger {
  font-family: var(--font-mono);
  font-size: 12px;
  background: transparent;
  border: none;
  border-radius: 4px;
  padding: 4px 6px;
  gap: 5px;
  min-width: 148px;
  color: var(--text);
}
.date-nav .pbsi-datepicker-trigger:hover {
  background: var(--hover);
  border-color: transparent;
}
.date-nav .pbsi-datepicker-trigger:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  box-shadow: none;
}
.date-nav .pbsi-datepicker-display--empty {
  color: var(--faint);
}
@media (max-width: 600px) {
  .date-nav .pbsi-datepicker-trigger {
    font-size: 11px;
    min-width: 126px;
    padding: 3px 5px;
  }
}

/* ── Mobile: prevent iOS Safari zoom on focus ─────────────── */
@media (max-width: 600px) {
  .pbsi-datepicker-trigger { font-size: 16px; }
  .pbsi-datepicker-preset  { font-size: 13px; }
}

/* ============================================================
   FLATPICKR — PBSI V2 THEME OVERRIDE
   Replaces Flatpickr's default blue with PBSI accent tokens.
   z-index 360 — above PBSI Select panel (350) and modals (200).
   ============================================================ */

.flatpickr-calendar {
  font-family: var(--font-sans) !important;
  font-size: 13px !important;
  background: var(--surface-card, #ffffff) !important;
  border: 1.5px solid var(--input-border) !important;
  border-radius: 8px !important;
  box-shadow: 0 4px 6px -2px rgba(0,0,0,.08),
              0 12px 24px -6px rgba(0,0,0,.10) !important;
  z-index: 360 !important;
  color: var(--text) !important;
}
.flatpickr-calendar.open { display: inline-block; }

/* Month / year header */
.flatpickr-months {
  background: var(--surface-card, #ffffff) !important;
  border-radius: 6px 6px 0 0;
}
.flatpickr-month {
  background: transparent !important;
  color: var(--text) !important;
  fill: var(--text) !important;
}
.flatpickr-current-month {
  color: var(--text) !important;
}
.flatpickr-current-month select.cur-month,
.flatpickr-current-month .numInputWrapper input.cur-year {
  color: var(--text) !important;
  font-family: var(--font-sans) !important;
  font-weight: 600 !important;
}
.flatpickr-prev-month,
.flatpickr-next-month {
  fill: var(--text) !important;
  color: var(--text) !important;
}
.flatpickr-prev-month:hover svg,
.flatpickr-next-month:hover svg {
  fill: var(--accent) !important;
}

/* Weekday header row */
.flatpickr-weekdays,
.flatpickr-weekdaycontainer {
  background: var(--surface-card, #ffffff) !important;
}
span.flatpickr-weekday {
  background: transparent !important;
  color: var(--faint) !important;
  font-weight: 600 !important;
  font-size: 11px !important;
}

/* Day cells */
.flatpickr-days, .dayContainer {
  background: var(--surface-card, #ffffff) !important;
}
.flatpickr-day {
  color: var(--text) !important;
  border-radius: 6px !important;
  font-family: var(--font-sans) !important;
}
.flatpickr-day:hover,
.flatpickr-day.prevMonthDay:hover,
.flatpickr-day.nextMonthDay:hover {
  background: var(--hover) !important;
  border-color: transparent !important;
}
.flatpickr-day.today {
  border-color: var(--accent) !important;
}
.flatpickr-day.today:hover {
  background: var(--hover) !important;
  color: var(--text) !important;
  border-color: var(--accent) !important;
}
.flatpickr-day.selected,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange,
.flatpickr-day.endRange {
  background: var(--accent) !important;
  border-color: var(--accent) !important;
  color: #ffffff !important;
}
.flatpickr-day.prevMonthDay,
.flatpickr-day.nextMonthDay {
  color: var(--faint) !important;
}
.flatpickr-day.flatpickr-disabled {
  color: var(--faint) !important;
  opacity: .4 !important;
}

/* Number input (year) arrows */
.numInputWrapper:hover,
.numInputWrapper span {
  background: var(--hover) !important;
}

/* ── Dark mode ────────────────────────────────────────────── */
[data-theme="dark"] .pbsi-datepicker-display--empty {
  color: var(--faint);
}
[data-theme="dark"] .flatpickr-calendar {
  background: var(--surface-2, #262830) !important;
  border-color: var(--input-border) !important;
  box-shadow: 0 4px 8px -2px rgba(0,0,0,.40),
              0 16px 32px -8px rgba(0,0,0,.48) !important;
}
[data-theme="dark"] .flatpickr-months,
[data-theme="dark"] .flatpickr-weekdays,
[data-theme="dark"] .flatpickr-weekdaycontainer,
[data-theme="dark"] .flatpickr-days,
[data-theme="dark"] .dayContainer {
  background: var(--surface-2, #262830) !important;
}
[data-theme="dark"] .flatpickr-day:hover {
  background: var(--hover) !important;
}
[data-theme="dark"] .flatpickr-day.selected,
[data-theme="dark"] .flatpickr-day.selected:hover {
  background: var(--accent) !important;
  border-color: var(--accent) !important;
  color: #ffffff !important;
}

/* ============================================================
   V1.5.3 — Archive & Safe Deletion Framework
   ============================================================ */

/* Archived card — visually distinct from inactive (opacity) */
.v2-user-card--archived {
  opacity: 1;
  border-color: var(--border);
  background: repeating-linear-gradient(
    135deg,
    transparent,
    transparent 6px,
    var(--hover) 6px,
    var(--hover) 7px
  );
}

/* v1.30.4 — Role Diarsipkan / Role Tidak Dikenal badge (User Management
   Integration): a user's stored role reference no longer resolves cleanly. */
.v2-entity-badge--role-warning {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .03em;
  background: var(--danger-bg);
  color: var(--danger);
  text-transform: uppercase;
}

/* Archived badge — shows in meta column */
.v2-entity-badge--archived {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .03em;
  background: var(--border);
  color: var(--muted);
  text-transform: uppercase;
}

/* Archive button — muted amber style */
.v2-user-btn--archive {
  background: color-mix(in srgb, var(--warning, #F59E0B) 12%, transparent);
  color: color-mix(in srgb, var(--warning, #F59E0B) 80%, var(--text));
  border-color: color-mix(in srgb, var(--warning, #F59E0B) 30%, transparent);
}
.v2-user-btn--archive:hover { filter: brightness(.94); }

/* Restore button — muted green style */
.v2-user-btn--restore {
  background: color-mix(in srgb, var(--ok, #22C55E) 12%, transparent);
  color: color-mix(in srgb, var(--ok, #22C55E) 80%, var(--text));
  border-color: color-mix(in srgb, var(--ok, #22C55E) 30%, transparent);
}
.v2-user-btn--restore:hover { filter: brightness(.94); }

/* Delete button — strong danger */
.v2-user-btn--delete {
  background: var(--danger-bg);
  color: var(--danger);
  border-color: transparent;
  font-weight: 700;
}
.v2-user-btn--delete:hover { filter: brightness(.92); }

/* Reference-blocked hint (shown instead of delete button) */
.v2-delete-blocked-hint {
  font-size: 11px;
  color: var(--muted);
  font-style: italic;
  padding: 2px 4px;
}

/* Archived chip in stats bar */
.v2-stats-chip--archived .v2-admin-stats-chip-label { color: var(--muted); }
.v2-stats-chip--archived .v2-admin-stats-chip-count { color: var(--muted); }
.v2-stats-chip--archived { border-color: var(--border); }

/* Archived overview card */
.v2-admin-overview-card--archived {
  border-color: var(--border);
  opacity: 0.7;
}
.v2-admin-overview-card--archived .v2-admin-overview-value { color: var(--muted); }
.v2-admin-overview-card--archived .v2-admin-overview-label { color: var(--faint); }

/* Delete confirmation modal — narrower than default */
.modal-box--narrow {
  max-width: 440px;
  width: 100%;
}

/* Delete confirm description text */
.v2-delete-confirm-desc {
  font-size: 13.5px;
  color: var(--text-secondary, var(--muted));
  margin: 0 0 12px;
  line-height: 1.5;
}

/* Reference count display */
.v2-delete-confirm-refs {
  margin-bottom: 16px;
  border-radius: 8px;
  padding: 10px 14px;
  font-size: 13px;
  line-height: 1.4;
}
.v2-delete-refs-warning {
  background: var(--danger-bg);
  color: var(--danger);
  border-radius: 8px;
  padding: 10px 14px;
  font-weight: 600;
}
.v2-delete-refs-ok {
  background: color-mix(in srgb, var(--ok, #22C55E) 10%, transparent);
  color: color-mix(in srgb, var(--ok, #22C55E) 80%, var(--text));
  border-radius: 8px;
  padding: 10px 14px;
  font-weight: 500;
}

/* Dark mode overrides */
[data-theme="dark"] .v2-user-card--archived {
  background: repeating-linear-gradient(
    135deg,
    transparent,
    transparent 6px,
    rgba(255,255,255,.03) 6px,
    rgba(255,255,255,.03) 7px
  );
}
[data-theme="dark"] .v2-entity-badge--archived {
  background: rgba(255,255,255,.08);
  color: var(--muted);
}

/* ================================================================
   V1.6.0 — Audit Center
   ================================================================ */

/* ── Date filter input ─────────────────────────────────────── */
.v2-audit-date-input {
  padding: 0 10px;
  height: 36px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 13px;
  color: var(--text);
  background: var(--surface);
  cursor: pointer;
  min-width: 140px;
}
.v2-audit-date-input:focus {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* ── Audit list container ──────────────────────────────────── */
.v2-audit-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
  margin-top: 8px;
}

/* ── Header row ────────────────────────────────────────────── */
.v2-audit-header-row {
  display: grid;
  grid-template-columns: 160px 120px 1fr 130px 1fr;
  gap: 0;
  padding: 9px 14px;
  background: var(--surface-alt, var(--surface));
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* ── Log row ───────────────────────────────────────────────── */
.v2-audit-row {
  display: grid;
  grid-template-columns: 160px 120px 1fr 130px 1fr;
  gap: 0;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  align-items: center;
  cursor: pointer;
  transition: background 0.1s;
}
.v2-audit-row:last-child {
  border-bottom: none;
}
.v2-audit-row:hover {
  background: var(--surface-alt, color-mix(in srgb, var(--accent) 5%, transparent));
}
.v2-audit-row:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* ── Columns ───────────────────────────────────────────────── */
.v2-audit-col {
  font-size: 13px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-right: 12px;
}
.v2-audit-col--time {
  color: var(--muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.v2-audit-col--actor {
  font-weight: 500;
}
.v2-audit-col--action {
  color: var(--text);
}
.v2-audit-col--category {
  padding-right: 8px;
}
.v2-audit-col--summary {
  color: var(--muted);
  font-size: 12.5px;
}

/* ── Category pills ────────────────────────────────────────── */
.v2-audit-cat-pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 20px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.v2-audit-cat--users        { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent-fg); }
.v2-audit-cat--drivers      { background: color-mix(in srgb, #22C55E 15%, transparent);       color: #16A34A; }
.v2-audit-cat--vehicles     { background: color-mix(in srgb, #F97316 15%, transparent);       color: #EA580C; }
.v2-audit-cat--assignments  { background: color-mix(in srgb, #A855F7 15%, transparent);       color: #9333EA; }
.v2-audit-cat--requests     { background: color-mix(in srgb, #EAB308 15%, transparent);       color: #CA8A04; }
.v2-audit-cat--authentication{ background: color-mix(in srgb, #EF4444 15%, transparent);      color: #DC2626; }
.v2-audit-cat--system       { background: color-mix(in srgb, var(--muted) 15%, transparent);  color: var(--muted); }
.v2-audit-cat--archiverestore{ background: color-mix(in srgb, #0D9488 15%, transparent);      color: #0F766E; }

/* ── Detail modal container ────────────────────────────────── */
.v2-audit-detail-box {
  max-width: 600px;
  width: 100%;
}
.v2-audit-detail {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* ── Sections ──────────────────────────────────────────────── */
.v2-audit-section {
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
}
.v2-audit-section:last-of-type {
  border-bottom: none;
}
.v2-audit-section--warn {
  border-left: 3px solid var(--danger, #EF4444);
  padding-left: 12px;
}
.v2-audit-section-title {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
  margin-bottom: 10px;
}
.v2-audit-section-title--warn {
  color: var(--danger, #EF4444);
}

/* ── Section 1: Activity Summary ───────────────────────────── */
.v2-audit-section--summary {
  background: color-mix(in srgb, var(--accent) 4%, transparent);
  border-radius: 8px;
  padding: 14px;
  margin-bottom: 0;
  border-bottom: none;
}
.v2-audit-summary-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 16px;
}
.v2-audit-summary-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.v2-audit-summary-label {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--muted);
}
.v2-audit-summary-value {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--text);
  line-height: 1.4;
  word-break: break-word;
}
.v2-audit-summary-value--action {
  font-weight: 700;
  color: var(--text);
}
.v2-audit-summary-sub {
  font-size: 11.5px;
  color: var(--muted);
  font-weight: 400;
}
.v2-audit-summary-mono {
  font-family: 'Menlo', 'Consolas', monospace;
  font-size: 12px;
  color: var(--muted);
}

/* ── Section 2: Human-Readable Details ─────────────────────── */
.v2-audit-human-grid {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.v2-audit-human-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.v2-audit-human-label {
  flex: 0 0 150px;
  font-size: 12.5px;
  color: var(--muted);
  text-align: right;
}
.v2-audit-human-value {
  flex: 1;
  font-size: 13.5px;
  color: var(--text);
  word-break: break-word;
}
.v2-audit-human-value--bold {
  font-weight: 700;
}
.v2-audit-detail-warn {
  color: var(--danger, #EF4444);
  font-weight: 600;
}

/* ── Section 3: Change Detection ───────────────────────────── */
.v2-audit-changes {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.v2-audit-change-item {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.v2-audit-change-label {
  flex: 0 0 150px;
  font-size: 12.5px;
  color: var(--muted);
  text-align: right;
  padding-top: 4px;
}
.v2-audit-change-flow {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
}
.v2-audit-change-old {
  font-size: 13px;
  color: var(--muted);
  text-decoration: line-through;
  padding: 2px 8px;
  background: color-mix(in srgb, var(--muted) 10%, transparent);
  border-radius: 4px;
}
.v2-audit-change-arrow {
  font-size: 13px;
  color: var(--muted);
  padding-left: 4px;
  line-height: 1;
}
.v2-audit-change-new {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent-fg);
  padding: 2px 8px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-radius: 4px;
}

/* ── Section 4: Technical Details ──────────────────────────── */
.v2-audit-tech-details {
  margin-top: 4px;
  border-top: 1px solid var(--border);
  padding-top: 4px;
}
.v2-audit-tech-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  list-style: none;
  user-select: none;
  border-radius: 4px;
}
.v2-audit-tech-summary::-webkit-details-marker { display: none; }
.v2-audit-tech-summary:hover { color: var(--text); }
.v2-audit-tech-chevron {
  transition: transform 0.2s;
  flex-shrink: 0;
}
.v2-audit-tech-details[open] .v2-audit-tech-chevron {
  transform: rotate(180deg);
}
.v2-audit-meta-json {
  margin: 0;
  padding: 12px 14px;
  background: color-mix(in srgb, var(--border) 30%, transparent);
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 12px;
  font-family: 'Menlo', 'Consolas', monospace;
  color: var(--text);
  overflow-x: auto;
  white-space: pre;
  line-height: 1.6;
  max-height: 240px;
  overflow-y: auto;
  margin-bottom: 8px;
}

/* ── Dark mode adjustments ─────────────────────────────────── */
[data-theme="dark"] .v2-audit-cat--drivers      { color: #4ADE80; }
[data-theme="dark"] .v2-audit-cat--vehicles     { color: #FB923C; }
[data-theme="dark"] .v2-audit-cat--assignments  { color: #C084FC; }
[data-theme="dark"] .v2-audit-cat--requests     { color: #FACC15; }
[data-theme="dark"] .v2-audit-cat--authentication{ color: #F87171; }
[data-theme="dark"] .v2-audit-cat--archiverestore{ color: #2DD4BF; }

/* ── Mobile responsive ─────────────────────────────────────── */
@media (max-width: 900px) {
  .v2-audit-header-row,
  .v2-audit-row {
    grid-template-columns: 130px 100px 1fr 110px;
  }
  .v2-audit-col--summary {
    display: none;
  }
}
@media (max-width: 600px) {
  .v2-audit-header-row,
  .v2-audit-row {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto;
    padding: 10px 12px;
    gap: 4px;
  }
  .v2-audit-col--time     { grid-column: 1; font-size: 11px; }
  .v2-audit-col--category { grid-column: 2; text-align: right; padding-right: 0; }
  .v2-audit-col--actor    { grid-column: 1; }
  .v2-audit-col--action   { grid-column: 2; text-align: right; font-size: 12px; }
  .v2-audit-col--summary  { display: none; }
  .v2-audit-header-row    { display: none; }
  .v2-audit-date-input      { min-width: 132px; font-size: 16px; }
  .v2-audit-summary-grid    { grid-template-columns: 1fr; }
  .v2-audit-human-label     { flex: 0 0 110px; font-size: 12px; }
  .v2-audit-change-label    { flex: 0 0 110px; font-size: 12px; }
  .v2-audit-detail-box      { max-width: 100%; }
}

/* ================================================================
   V1.7.1 — Configuration Center
   ================================================================ */

.v2-admin-config-groups {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 20px;
}

.v2-admin-config-group {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.v2-admin-config-group-title {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}

.v2-admin-config-fields {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.v2-admin-config-field {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.v2-admin-config-label {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

.v2-admin-config-input {
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 6px;
  padding: 9px 11px;
  font-size: 13px;
  color: var(--text);
  font-family: inherit;
  outline: none;
  width: 100%;
  box-sizing: border-box;
  transition: border-color .12s, box-shadow .12s;
  -webkit-appearance: none;
  appearance: none;
}

input[type="time"].v2-admin-config-input {
  min-height: 38px;
}

.v2-admin-config-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}

.v2-admin-config-input-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.v2-admin-config-input-row .v2-admin-config-input {
  flex: 1;
  min-width: 0;
  width: auto;
}

.v2-admin-config-unit {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
  flex-shrink: 0;
}

.v2-admin-config-footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
  padding-top: 4px;
}

/* Token display */
.v2-admin-config-token-display {
  font-size: 13px;
  font-family: 'SF Mono', 'Fira Code', ui-monospace, monospace;
  color: var(--text);
  background: var(--hover);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 9px 12px;
  letter-spacing: 0.04em;
  min-height: 38px;
  display: flex;
  align-items: center;
  word-break: break-all;
}

.v2-admin-config-token-actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 4px;
}

.v2-admin-config-ghost-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 7px 12px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
  font-family: inherit;
  transition: background .12s, border-color .12s;
  outline: none;
}

.v2-admin-config-ghost-btn:hover {
  background: var(--hover);
  border-color: var(--border-strong);
}

.v2-admin-config-ghost-btn:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}

.v2-admin-config-ghost-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Mobile: stack to single column */
@media (max-width: 720px) {
  .v2-admin-config-groups {
    grid-template-columns: 1fr;
  }
}
/* Phase 11J — defensive pass: already collapses to 1fr at 720px; no narrow
   tier existed for the smallest real phones. */
@media (max-width: 380px) {
  .v2-admin-config-groups { gap: 14px; }
  .v2-admin-config-group { padding: 16px 16px; }
}

/* Dark mode overrides */
@media (prefers-color-scheme: dark) {
  .v2-admin-config-input:focus {
    box-shadow: 0 0 0 3px rgba(184, 69, 74, 0.2);
  }
  .v2-admin-config-ghost-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(184, 69, 74, 0.25);
  }
}

[data-theme="dark"] .v2-admin-config-input:focus {
  box-shadow: 0 0 0 3px rgba(184, 69, 74, 0.2);
}
[data-theme="dark"] .v2-admin-config-ghost-btn:focus-visible {
  box-shadow: 0 0 0 3px rgba(184, 69, 74, 0.25);
}

/* ── v1.7.1.1 UX additions ─────────────────────────────────────── */

.v2-admin-config-hint {
  font-size: 11px;
  color: var(--faint);
  margin: 0;
  line-height: 1.4;
}

.v2-admin-config-status {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--muted);
  padding: 8px 0;
}

.v2-admin-config-status-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--faint);
  flex-shrink: 0;
}

.v2-admin-config-status-dot--online {
  background: #22a84a;
}

/* ================================================================
   V1.8.0 — Analytics Foundation
   ================================================================ */

/* Grid layout — mirrors config groups, responsive to single column on mobile */
.v2-analytics-groups {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  gap: 20px;
}

@media (max-width: 720px) {
  .v2-analytics-groups {
    grid-template-columns: 1fr;
  }
}
/* Phase 11J — defensive pass: already collapses to 1fr at 720px; no narrow
   tier existed for the smallest real phones (mirrors the ≤380px trim
   already added for .v2-admin-config-groups above). */
@media (max-width: 380px) {
  .v2-analytics-groups { gap: 14px; }
}

/* KPI stat list — vertical stack of label/value rows */
.v2-analytics-kpi-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Single KPI row: label left, value right */
.v2-analytics-kpi-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 2px 0;
  border-bottom: 1px solid var(--border);
}
.v2-analytics-kpi-row:last-child {
  border-bottom: none;
}

.v2-analytics-kpi-label {
  font-size: 13px;
  color: var(--muted);
  flex: 1;
  line-height: 1.4;
}

.v2-analytics-kpi-val {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  text-align: right;
  flex-shrink: 0;
  max-width: 55%;
  word-break: break-word;
}

.v2-analytics-kpi-val--ok   { color: var(--ok);   }
.v2-analytics-kpi-val--info { color: var(--info);  }
.v2-analytics-kpi-val--warn { color: var(--warn);  }

/* Section subtitle divider within an analytics card */
.v2-analytics-subtitle {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding-top: 4px;
  border-top: 1px solid var(--border);
  margin-top: 2px;
}

/* Breakdown list — per-driver / per-vehicle / per-bidang rows */
.v2-analytics-breakdown {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* One breakdown row */
.v2-analytics-breakdown-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0;
  border-bottom: 1px solid var(--border);
}
.v2-analytics-breakdown-row:last-child {
  border-bottom: none;
}

/* Header row: dimmed labels, no background on chips */
.v2-analytics-breakdown-row--header {
  padding-bottom: 6px;
}
.v2-analytics-breakdown-row--header .v2-analytics-breakdown-name,
.v2-analytics-breakdown-row--header .v2-analytics-breakdown-count {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  background: none;
  padding: 0;
  border-radius: 0;
}

/* Rank number (#1, #2 …) */
.v2-analytics-breakdown-rank {
  font-size: 11px;
  font-weight: 700;
  color: var(--faint);
  width: 18px;
  flex-shrink: 0;
  text-align: right;
}

/* Entity name — takes remaining space */
.v2-analytics-breakdown-name {
  flex: 1;
  font-size: 13px;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Count chip — compact badge on the right */
.v2-analytics-breakdown-count {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
  background: var(--hover);
  padding: 2px 8px;
  border-radius: 10px;
  flex-shrink: 0;
  min-width: 44px;
  text-align: center;
}

/* Empty state message (inline, inside a card breakdown) */
.v2-analytics-empty {
  font-size: 12px;
  color: var(--faint);
  margin: 0;
  text-align: center;
  padding: 10px 0;
}

/* Global empty state — shown in place of all 5 module cards */
.v2-analytics-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 64px 24px;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.v2-analytics-empty-state-msg {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  margin: 0;
}

.v2-analytics-empty-state-hint {
  font-size: 12px;
  color: var(--faint);
  margin: 0;
}

/* ================================================================
   V1.8.2 — Analytics Deep Metrics
   ================================================================ */

/* Workload classification badge */
.v2-analytics-wl-badge {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 2px 7px;
  border-radius: 6px;
  flex-shrink: 0;
  white-space: nowrap;
}

.v2-analytics-wl-badge--balanced {
  background: var(--ok-bg);
  color: var(--ok);
}

.v2-analytics-wl-badge--over {
  background: var(--warn-bg);
  color: var(--warn);
}

.v2-analytics-wl-badge--under {
  background: var(--hover);
  color: var(--muted);
}

.v2-analytics-wl-badge--idle {
  background: var(--surface);
  color: var(--faint);
  border: 1px solid var(--border);
}

/* Percentage column in breakdown rows */
.v2-analytics-breakdown-pct {
  font-size: 11px;
  font-weight: 600;
  color: var(--faint);
  flex-shrink: 0;
  width: 36px;
  text-align: right;
}

/* Spacer for workload badge column in header rows */
.v2-analytics-breakdown-wl {
  flex-shrink: 0;
  width: 60px;
}

/* Inherit header row dimming for new breakdown column types */
.v2-analytics-breakdown-row--header .v2-analytics-breakdown-pct,
.v2-analytics-breakdown-row--header .v2-analytics-breakdown-wl {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Extra top margin for second subtitle within same card (Inactive Resources) */
.v2-analytics-subtitle--mt {
  margin-top: 14px;
}

/* ================================================================
   V1.8.3 — Analytics UX Polish
   ================================================================ */

/* Active filter summary bar — shown below the toolbar */
.v2-analytics-filter-summary {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 6px 0 2px;
}
.v2-analytics-filter-summary:empty { display: none; }

/* Individual active filter chip */
.v2-analytics-filter-chip {
  display: inline-flex;
  align-items: center;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent-fg);
  background: var(--danger-bg);
  border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
  border-radius: 20px;
  padding: 3px 10px;
  white-space: nowrap;
}

/* Reset all filters button — sits in v2-admin-toolbar */
.v2-analytics-reset-btn {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 12px;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.v2-analytics-reset-btn:hover {
  color: var(--text);
  border-color: var(--border-strong);
  background: var(--hover);
}

/* Secondary toolbar utility action — sits inline beside Reset Filter.
   Content-sized (no width:100%/flex-grow), token-based so light & dark
   adapt automatically. Replaces the misused full-width .btn-reimbursement. */
.v2-analytics-export-btn {
  flex: 0 0 auto;
  width: auto;
  max-width: max-content;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 9px 14px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.v2-analytics-export-btn:hover {
  border-color: var(--border-strong);
  background: var(--hover);
}
.v2-analytics-export-btn[disabled] {
  opacity: 0.6;
  cursor: default;
}

/* Analytics Export dropdown (4 server-rendered reports). Token-based so
   light & dark adapt automatically; click-toggle so it works on touch. */
.v2-analytics-export {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
}
.v2-analytics-export-caret {
  margin-left: 1px;
  opacity: 0.7;
}
.v2-analytics-export-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 60;
  min-width: 200px;
  display: flex;
  flex-direction: column;
  padding: 5px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.08);
}
.v2-analytics-export-menu[hidden] {
  display: none;
}
.v2-analytics-export-item {
  display: block;
  width: 100%;
  text-align: left;
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  background: transparent;
  border: none;
  border-radius: 6px;
  padding: 9px 12px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.12s, color 0.12s;
}
.v2-analytics-export-item:hover {
  background: var(--hover);
}
.v2-analytics-export-item[disabled] {
  opacity: 0.6;
  cursor: default;
}
/* On narrow screens the toolbar wraps; let the menu align to the left edge. */
@media (max-width: 600px) {
  .v2-analytics-export {
    width: 100%;
  }
  .v2-analytics-export-btn {
    width: 100%;
    max-width: none;
    justify-content: center;
  }
  .v2-analytics-export-menu {
    left: 0;
    right: 0;
    min-width: 0;
  }
}

/* Insight highlights bar — at top of analytics content */
.v2-analytics-insights {
  display: flex;
  flex-direction: column;
  gap: 7px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px 16px;
}
.v2-analytics-insight-item {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.5;
}
.v2-analytics-insight-item strong {
  color: var(--text);
  font-weight: 600;
}

/* Section grouping — vertical stack of named sections */
.v2-analytics-sections {
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.v2-analytics-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-analytics-section-header {
  font-size: 11px;
  font-weight: 700;
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border);
}

/* ── Executive Summary V2 — KPI cards / grid / highlights (v1.10.0) ──────── */
.v2-analytics-kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
  margin-bottom: 4px;
}
.v2-analytics-kpi-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 16px 18px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  min-width: 0;
}
.v2-analytics-kpi-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.v2-analytics-kpi-card-icon {
  font-size: 14px;
  line-height: 1;
  flex-shrink: 0;
}
.v2-analytics-kpi-card-title {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  /* v1.15.3: wrap to a second line on tight cards rather than truncating with
     an ellipsis (A2: "no ellipsis unless absolutely required"). */
  min-width: 0;
  overflow-wrap: anywhere;
}
.v2-analytics-kpi-card-value {
  /* v1.15.2: responsive value typography. A fixed 30px currency value
     (e.g. "Rp 12.345.678") overflowed its 1fr grid column on narrow desktop /
     split-screen / DevTools-open widths and visually overlapped the next card
     ("Saldo Aktif" bug). clamp() scales the value to the available width and
     overflow-wrap lets an unbreakable long number wrap inside the card instead
     of spilling out. The card already has min-width:0 so the grid track shrinks. */
  font-size: clamp(22px, 2.4vw, 30px);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  min-width: 0;
  overflow-wrap: anywhere;
}
.v2-analytics-kpi-card-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 16px;
}

/* v1.15.3 — Container-aware currency (renderResponsiveCurrency +
   bindResponsiveCurrency). A single cell whose text is swapped by a
   ResizeObserver to the widest form that fits the cell's ACTUAL container width
   (not the viewport), so it never overflows/clips/fragments under DevTools,
   split-screen, foldables or tablet landscape. Always one unbreakable line. */
.an-cur { white-space: nowrap; }
.an-cur--hero { font-size: 0.62em; letter-spacing: -0.01em; }

/* v1.15.3 — Executive filter bar (Phase B): period segment + scope selects.
   Wraps fluidly; on mobile the scope selects go full-width 1-per-row. */
.v2-analytics-claude .exec-filter-bar {
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* v1.15.4: relocated OUT of the (display:none) .v2-admin-page-header to sit
     directly below the hero. The workspace layout supplies the vertical rhythm
     via its flex `gap`, so no extra top margin is needed here. */
  margin-top: 0;
}
.v2-analytics-claude .exec-period-seg { margin-bottom: 0; }
.v2-analytics-claude .exec-scope-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr));
  gap: 8px;
}
.v2-analytics-claude .exec-scope-row .v2-admin-filter { width: 100%; min-width: 0; }
@media (max-width: 600px) {
  .v2-analytics-claude .exec-scope-row { grid-template-columns: 1fr; }
}
.v2-analytics-kpi-card-comp {
  font-size: 11px;
  color: var(--faint);
}
.v2-analytics-kpi-card-sub {
  font-size: 11.5px;
  color: var(--faint);
  line-height: 1.35;
}
/* status accents (left edge) */
.v2-analytics-kpi-card--ok     { box-shadow: inset 3px 0 0 var(--ok); }
.v2-analytics-kpi-card--warn   { box-shadow: inset 3px 0 0 var(--warn); }
.v2-analytics-kpi-card--info   { box-shadow: inset 3px 0 0 var(--info); }
.v2-analytics-kpi-card--danger { box-shadow: inset 3px 0 0 var(--danger); }
.v2-analytics-kpi-card--ok     .v2-analytics-kpi-card-value { color: var(--ok); }
.v2-analytics-kpi-card--warn   .v2-analytics-kpi-card-value { color: var(--warn); }
.v2-analytics-kpi-card--danger .v2-analytics-kpi-card-value { color: var(--danger); }

/* trend chip */
.v2-analytics-kpi-trend {
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.v2-analytics-kpi-trend--up      { color: var(--ok); }
.v2-analytics-kpi-trend--down    { color: var(--warn); }
.v2-analytics-kpi-trend--neutral { color: var(--ghost, var(--faint)); font-weight: 500; }

/* loading skeleton */
.v2-analytics-kpi-card--loading { gap: 10px; }
.v2-analytics-kpi-skel {
  background: var(--hover);
  border-radius: 6px;
  /* v1.20.8 — was a static block; now pulses via the shared skeleton-pulse
     keyframe (style.css) so this reads as the same loading language as the
     rest of the app instead of a dead gray placeholder. */
  animation: skeleton-pulse 1.3s ease-in-out infinite;
}
.v2-analytics-kpi-skel--line  { height: 12px; width: 55%; }
.v2-analytics-kpi-skel--value { height: 28px; width: 70%; }

/* Operational Highlights */
.v2-analytics-highlights {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}
.v2-analytics-highlight {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 14px 16px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 10px;
  min-width: 0;
}
.v2-analytics-highlight-eyebrow {
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--faint);
}
.v2-analytics-highlight-value {
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.25;
  word-break: break-word;
}
.v2-analytics-highlight-context {
  font-size: 11.5px;
  color: var(--muted);
}

/* Executive Summary sub-headers + quick summary */
.v2-analytics-subhead {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 10px;
}
.v2-analytics-quick-summary-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  font-size: 13px;
  color: var(--muted);
}
.v2-analytics-quick-summary-stats strong {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1024px) {
  .v2-analytics-kpi-grid,
  .v2-analytics-highlights { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
  .v2-analytics-kpi-grid,
  .v2-analytics-highlights { grid-template-columns: 1fr; gap: 12px; }
  .v2-analytics-kpi-card-value { font-size: 26px; }
}

/* ── Executive Analytics KPI grid (v1.16.4.5 — KPI Rationalization) ──────────
   Dedicated layout for the rationalized Executive KPI strip: six indicators in
   ONE desktop row, 3+3 on tablet, stacked on mobile. Scoped to .v2-exec-kpi-grid
   (an extra class alongside .v2-analytics-kpi-grid) so NO other analytics screen
   is affected. These rules intentionally sit AFTER the generic .v2-analytics-kpi-
   grid rules above so that, at equal specificity, source order makes the
   Executive overrides win at every breakpoint. */
.v2-exec-kpi-grid { grid-template-columns: repeat(6, 1fr); gap: 12px; }
/* Six columns are tighter than the default four — trim the title size ONLY here
   (no global typography change) so longer labels like "Persentase Pemakaian RAB
   Petty Cash" wrap cleanly instead of overlapping a neighbour. */
.v2-exec-kpi-grid .v2-analytics-kpi-card { padding: 14px 14px; }
.v2-exec-kpi-grid .v2-analytics-kpi-card-title { font-size: 10.5px; letter-spacing: 0.03em; }
.v2-exec-kpi-grid .v2-analytics-kpi-card-value { font-size: clamp(18px, 1.5vw, 24px); }
@media (max-width: 1024px) {
  .v2-exec-kpi-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 600px) {
  .v2-exec-kpi-grid { grid-template-columns: 1fr; }
}

/* Top performer badge — #1 Driver / #1 Kendaraan / #1 Bidang */
.v2-analytics-top-badge {
  display: inline-block;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--warn);
  background: var(--warn-bg);
  border-radius: 5px;
  padding: 1px 6px;
  margin-left: 4px;
  vertical-align: middle;
  white-space: nowrap;
  flex-shrink: 0;
}

@media (max-width: 600px) {
  .v2-analytics-reset-btn,
  .v2-analytics-export-btn {
    width: 100%;
    justify-content: center;
    text-align: center;
  }
  .v2-analytics-insights {
    padding: 10px 12px;
  }
}

/* ── Analytics Charts (v1.8.4) ── */
.v2-analytics-chart-wrap {
  margin-top: 14px;
  padding: 14px 16px 16px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}

.v2-analytics-chart-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  margin-bottom: 10px;
}

.v2-analytics-chart-box {
  position: relative;
  width: 100%;
}

.v2-analytics-chart-box--donut {
  max-width: 300px;
  margin: 0 auto;
}

/* Unified chart container slots (v1.10.0 Sprint 3) — title/subtitle/actions/
   footer/state. Charts without these render exactly as before. */
.v2-analytics-chart-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}
.v2-analytics-chart-head .v2-analytics-chart-label { margin-bottom: 0; }
.v2-analytics-chart-titles {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.v2-analytics-chart-subtitle {
  font-size: 11px;
  color: var(--faint);
  line-height: 1.35;
}
.v2-analytics-chart-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.v2-analytics-chart-footer {
  margin-top: 10px;
  font-size: 11px;
  color: var(--faint);
}
.v2-analytics-chart-state { padding: 6px 0; }

/* ── Insight cards (v1.10.0 Sprint 4) ───────────────────────────────────── */
.v2-analytics-insight-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-analytics-insight-card {
  display: flex;
  gap: 12px;
  padding: 14px 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.v2-analytics-insight-card--info    { box-shadow: inset 3px 0 0 var(--info); }
.v2-analytics-insight-card--success { box-shadow: inset 3px 0 0 var(--ok); }
.v2-analytics-insight-card--warning { box-shadow: inset 3px 0 0 var(--warn); }
.v2-analytics-insight-icon {
  font-size: 15px;
  line-height: 1.4;
  flex-shrink: 0;
}
.v2-analytics-insight-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.v2-analytics-insight-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.35;
}
.v2-analytics-insight-desc {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.v2-analytics-insight-source {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--faint);
  margin-top: 2px;
}

/* ================================================================
   V1.8.4 — Analytics Refinement & Data Quality
   ================================================================ */

/* Filter chip modifiers — date chip is neutral, entity chips are accented */
.v2-analytics-filter-chip--date {
  color: var(--muted);
  background: var(--surface);
  border-color: var(--border);
  font-weight: 500;
}
.v2-analytics-filter-chip--active {
  color: var(--accent-fg);
  background: var(--danger-bg);
  border-color: color-mix(in srgb, var(--accent) 22%, transparent);
  font-weight: 600;
}

/* Data quality warnings — collapsible details block */
.v2-dq-details {
  background: var(--surface);
  border: 1px solid var(--warn-border, rgba(180, 120, 20, 0.25));
  border-radius: 8px;
  padding: 0;
  overflow: hidden;
}
.v2-dq-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--warn-text, #7a5b10);
  list-style: none;
  user-select: none;
}
.v2-dq-summary::-webkit-details-marker { display: none; }
.v2-dq-summary::marker { display: none; }
.v2-dq-summary:hover { background: var(--hover); }
.v2-dq-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  font-size: 11px;
  font-weight: 700;
  background: var(--warn-bg);
  color: var(--warn);
  border-radius: 10px;
  flex-shrink: 0;
}
.v2-dq-hint {
  font-size: 11.5px;
  font-weight: 400;
  color: var(--muted);
}
.v2-dq-list {
  display: flex;
  flex-direction: column;
  gap: 0;
  border-top: 1px solid var(--border);
  padding: 6px 0;
}
.v2-dq-item {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 14px;
  font-size: 12.5px;
  border-bottom: 1px solid var(--border);
}
.v2-dq-item:last-child { border-bottom: none; }
.v2-dq-type {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 6px;
  min-width: 56px;
  text-align: center;
}
.v2-dq-pair {
  color: var(--text);
  line-height: 1.4;
}
.v2-dq-note {
  font-size: 11.5px;
  color: var(--faint);
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  margin: 0;
}

/* ── Data Quality Resolution Center (v1.8.4.1) ── */

/* Container */
.v2-dq-center { padding: 16px; }
.v2-dq-alias-table-title { margin-top: 20px; }

/* Detected pair rows */
.v2-dq-pair-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}
.v2-dq-pair-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  flex-wrap: wrap;
}
.v2-dq-pair-row--resolved {
  border-color: var(--ok);
  background: var(--ok-bg);
}
.v2-dq-type-badge {
  flex-shrink: 0;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 2px 7px;
  min-width: 54px;
  text-align: center;
}
.v2-dq-pair-names {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  flex-wrap: wrap;
}
.v2-dq-name {
  font-size: 13px;
  color: var(--text);
}
.v2-dq-name--canonical {
  font-weight: 600;
  color: var(--ok);
}
.v2-dq-sep, .v2-dq-arrow {
  font-size: 12px;
  color: var(--faint);
  flex-shrink: 0;
}
.v2-dq-resolved-badge {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 700;
  color: var(--ok);
  background: transparent;
  margin-left: auto;
}
.v2-dq-merge-btn {
  flex-shrink: 0;
  margin-left: auto;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-fg);
  background: var(--danger-bg);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  border-radius: 6px;
  padding: 5px 12px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s;
}
.v2-dq-merge-btn:hover {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

/* Kelola Alias table */
.v2-dq-alias-table-wrap {
  overflow-x: auto;
  margin-top: 8px;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.v2-dq-alias-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12.5px;
}
.v2-dq-alias-table thead th {
  padding: 8px 12px;
  text-align: left;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.v2-dq-alias-table tbody tr {
  border-bottom: 1px solid var(--border);
}
.v2-dq-alias-table tbody tr:last-child { border-bottom: none; }
.v2-dq-alias-table tbody tr:hover { background: var(--hover); }
.v2-dq-alias-table td { padding: 9px 12px; vertical-align: middle; }
.v2-dq-alias-key {
  font-family: monospace;
  font-size: 11.5px;
  color: var(--muted);
  max-width: 200px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.v2-dq-alias-canonical {
  font-weight: 600;
  color: var(--text);
}
.v2-dq-alias-actions { text-align: right; }
.v2-dq-delete-btn {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 4px 10px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.v2-dq-delete-btn:hover {
  color: var(--accent-fg);
  border-color: var(--accent);
  background: var(--danger-bg);
}

/* Alias Resolution Modal */
.v2-alias-modal-box {
  max-width: 480px;
  width: 94%;
}
.v2-alias-detected-values {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 16px;
}
.v2-alias-detected-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted);
  display: block;
  margin-bottom: 8px;
}
.v2-alias-detected-pair {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.v2-alias-detected-value {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}
.v2-alias-detected-sep {
  font-size: 12px;
  color: var(--faint);
  flex-shrink: 0;
}
.v2-alias-form { display: flex; flex-direction: column; gap: 4px; }
.v2-alias-form-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
}
.v2-alias-radio-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  cursor: pointer;
  font-size: 13px;
  color: var(--text);
  transition: background 0.12s, border-color 0.12s;
}
.v2-alias-radio-row:has(input:checked) {
  background: var(--danger-bg);
  border-color: var(--accent);
}
.v2-alias-radio-row input[type="radio"] { flex-shrink: 0; }
.v2-alias-custom-input {
  margin-top: 4px;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--surface);
  color: var(--text);
  font-size: 13px;
  width: 100%;
  box-sizing: border-box;
  outline: none;
  transition: border-color 0.15s;
}
.v2-alias-custom-input:focus { border-color: var(--accent); }
.v2-alias-modal-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  background: var(--surface);
}

/* v1.8.4.2 — Data Quality Center Enhancements */

/* Stats bar */
.v2-dq-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  margin-bottom: 18px;
}
.v2-dq-stat-card {
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  border-radius: 9px;
  padding: 12px 10px;
  text-align: center;
}
.v2-dq-stat-card--warn { border-color: #c97900; background: #fffbf0; }
.v2-dq-stat-card--ok  { border-color: #2F7D62; background: #f0faf6; }
[data-theme="dark"] .v2-dq-stat-card--warn { background: #2a2200; border-color: #b38200; }
[data-theme="dark"] .v2-dq-stat-card--ok   { background: #0d2a20; border-color: #2F7D62; }
.v2-dq-stat-value {
  display: block;
  font-size: 1.6rem;
  font-weight: 700;
  color: var(--text-primary);
  line-height: 1.2;
}
.v2-dq-stat-label {
  display: block;
  font-size: 0.7rem;
  color: var(--text-muted);
  margin-top: 2px;
}

/* v1.30.1 — Role Management (read-only). Everything else (toolbar, search,
   filter, stats cards, module-group disclosure) reuses .v2-admin-toolbar /
   .v2-admin-search / .v2-admin-filter / .v2-dq-stats / .user-role-group
   verbatim — only the two-pane layout and the permission-row itself are new. */
.rm-layout {
  display: grid;
  grid-template-columns: 220px 1fr;
  gap: 20px;
  align-items: start;
}
.rm-sidebar__title {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 10px;
}
.rm-role-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.rm-role-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  padding: 8px 10px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  font: inherit;
  font-size: 0.8rem;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.rm-role-item:hover { background: var(--surface-2, var(--hover)); }
.rm-role-item--active {
  background: var(--surface-2, var(--hover));
  border-color: var(--accent);
  font-weight: 600;
}
.rm-role-item__label { overflow-wrap: anywhere; }
.rm-header {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.rm-header__title {
  font-size: 1.1rem;
  font-weight: 700;
  margin: 0;
}
.rm-stats { margin-top: 14px; }
.rm-tree {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 16px;
}
.rm-category { padding: 8px 0 4px 14px; }
.rm-category__title {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 6px;
}
.rm-permission-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 6px 4px;
  cursor: default;
}
.rm-permission-row input[type="checkbox"] {
  margin-top: 3px;
  accent-color: var(--accent);
  width: 16px;
  height: 16px;
  flex: none;
}
.rm-permission-row__text {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.rm-permission-row__title { font-size: 0.8rem; font-weight: 600; }
.rm-permission-row__desc { font-size: 0.72rem; color: var(--text-muted); }

/* Role-Level Permission Assignment, Phase 4 (v1.30.9.9) — System Role
   checkbox states. Mirrors .ipm-picker-row__note's language (same tokens:
   italic, muted, 0.7rem) for the same "why is this checkbox the way it
   is" note role. */
.rm-permission-row__note { font-size: 0.7rem; color: var(--text-muted); font-style: italic; margin-top: 1px; }
.rm-permission-row--base { opacity: 0.82; }
.rm-permission-row--protected { opacity: 0.6; }
.rm-permission-row--role-additional .rm-permission-row__title { color: var(--accent-fg); }
.rm-ra-status {
  font-size: 0.74rem;
  color: var(--text-muted);
  margin-top: 8px;
}
.rm-ra-status--error { color: var(--danger); font-weight: 600; }

/* v1.30.2 — Role Management (Editable). Header now shows the selected
   role's identity + actions; everything below (toolbar/stats/tree) is
   untouched from v1.30.1. */
.rm-header__top {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.rm-header__role {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-top: 8px;
}
.rm-header__role-name {
  font-size: 0.95rem;
  font-weight: 600;
}
.rm-header__actions { display: flex; gap: 8px; }

.rm-name-input {
  flex: 1 1 260px;
  min-width: 0;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 8px;
  padding: 0.5rem 0.7rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  font-family: inherit;
  outline: none;
}
.rm-name-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent); }

.rm-role-type-badge {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--border-faint, var(--hover));
  color: var(--text-dim, var(--text-muted));
}

.rm-action-btn {
  padding: 0.45rem 0.85rem;
  font-size: 0.76rem;
  font-weight: 600;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.rm-action-btn:hover { background: var(--surface-2, var(--hover)); }
.rm-action-btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.rm-action-btn--primary:hover { filter: brightness(1.05); }
.rm-action-btn--danger {
  background: var(--danger-bg, #fdecea);
  border-color: var(--danger);
  color: var(--danger);
}

.rm-error {
  background: var(--danger-bg, #fdecea);
  color: var(--danger);
  border: 1px solid var(--danger);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 0.78rem;
  font-weight: 600;
  margin-bottom: 12px;
}

.rm-save-bar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 16px;
  padding: 10px 14px;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-lg, 0 4px 16px rgba(0,0,0,.12));
}
.rm-save-bar__msg { font-size: 0.78rem; color: var(--text-muted); }
.rm-save-bar__actions { display: flex; gap: 8px; }

/* Clone prompt + Review modal — Phase 11 (Administration): migrated onto
   the canonical drawer (js/components/drawer.js); .rm-modal-box/
   .rm-review-box (the old .modal-box-derived wrapper sizing) are gone,
   the drawer shell now owns panel width/scroll/padding. These are still
   the real body-content classes, unchanged. */
.rm-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding-top: 14px;
  margin-top: 14px;
  border-top: 1px solid var(--border);
}
.rm-review-col { margin-bottom: 12px; }
.rm-review-col h4 { margin: 0 0 6px; font-size: 0.76rem; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); }
.rm-review-list { margin: 0; padding-left: 18px; font-size: 0.8rem; }
.rm-review-list li { margin-bottom: 3px; }
.rm-review-empty { font-size: 0.8rem; color: var(--text-muted); margin: 0; }

/* v1.30.3 — Role Assignment & Dependency. Collapsed-by-default disclosure
   panel inserted between the header and the toolbar/stats/tree — reuses
   .wsp-pill/.wsp-empty (js/widgets/_widget-base.js) for every status pill
   and zero-value row rather than inventing new tone/color rules. */
.rm-detail-panel { margin-bottom: 14px; }
.rm-detail-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  font: inherit;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text);
  text-align: left;
  cursor: pointer;
}
.rm-detail-toggle:hover { background: var(--surface-2, var(--hover)); }
.rm-detail-toggle__label { flex: 1; }
.rm-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 12px;
  margin-top: 10px;
}
.rm-detail-card {
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.rm-detail-card--wide { grid-column: 1 / -1; }
.rm-detail-card__title {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 10px;
}
.rm-detail-dl { display: flex; flex-direction: column; gap: 8px; margin: 0; }
.rm-detail-dl > div { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.rm-detail-dl dt { font-size: 0.74rem; color: var(--text-muted); }
.rm-detail-dl dd { margin: 0; font-size: 0.8rem; font-weight: 600; color: var(--text); text-align: right; }
.rm-detail-note { margin: 10px 0 0; font-size: 0.7rem; color: var(--text-muted); }
.rm-detail-stale { color: var(--warn, #b45309); font-weight: 600; font-size: 0.78rem; }
.rm-detail-list { margin: 0; padding-left: 16px; font-size: 0.78rem; }
.rm-detail-list li { margin-bottom: 2px; }
.rm-detail-breakdown {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 6px 16px;
}
.rm-detail-breakdown-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.78rem;
}
.rm-detail-breakdown-row__module { color: var(--text-muted); }
.rm-detail-breakdown-row__count { font-weight: 600; }

@media (max-width: 720px) {
  .rm-layout { grid-template-columns: 1fr; }
  .rm-role-list { flex-direction: row; flex-wrap: wrap; }
  .rm-role-item { width: auto; }
  .rm-header__role { flex-direction: column; align-items: stretch; }
  .rm-header__actions { justify-content: flex-end; }
  .rm-save-bar { flex-direction: column; align-items: stretch; }
  .rm-save-bar__actions { justify-content: flex-end; }
  .rm-detail-grid { grid-template-columns: 1fr; }
  .rm-detail-dl dd { text-align: left; }
}

/* Phase 11 (Administration) — Permissions Matrix: Per-Role/Matrix segmented
   toggle (shared header) + the matrix table itself (audit §5's biggest
   single UX gap: no view answered "who can do X" across every role and
   every user in one place). */
.rm-view-toggle {
  display: inline-flex;
  margin-left: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 2px;
  gap: 2px;
  background: var(--surface-2, var(--hover));
}
.rm-view-toggle__btn {
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 5px 12px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
}
.rm-view-toggle__btn:hover { color: var(--text); }
.rm-view-toggle__btn--active { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.06)); }

.rm-layout--matrix { display: block; }
.rm-main--matrix { max-width: none; }
.rm-matrix-intro { margin: 8px 0 0; font-size: 0.8rem; color: var(--text-muted); max-width: 72ch; }

.rm-matrix-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin: 10px 0 14px;
  font-size: 0.74rem;
  color: var(--text-muted);
}
.rm-matrix-legend__item { display: inline-flex; align-items: center; gap: 6px; }
.rm-matrix-dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: transparent;
  flex: none;
}
.rm-matrix-dot--base { background: var(--text-muted); border-color: var(--text-muted); }
.rm-matrix-dot--additional { background: var(--accent); border-color: var(--accent); }
.rm-matrix-dot--custom { background: var(--info, #2563eb); border-color: var(--info, #2563eb); }
.rm-matrix-dot--none { background: transparent; }

.rm-matrix-scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  -webkit-overflow-scrolling: touch;
}
.rm-matrix-table {
  border-collapse: collapse;
  width: 100%;
  min-width: max-content;
  font-size: 0.78rem;
}
.rm-matrix-th--perm, .rm-matrix-td--perm {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  text-align: left;
  min-width: 220px;
  max-width: 320px;
}
.rm-matrix-th--perm { z-index: 2; }
.rm-matrix-table th, .rm-matrix-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.rm-matrix-td--perm { white-space: normal; font-weight: 600; }
.rm-matrix-th--role { text-align: center; font-weight: 700; font-size: 0.72rem; color: var(--text-muted); }
.rm-matrix-role-tag { font-weight: 500; color: var(--info, #2563eb); font-size: 0.65rem; }
.rm-matrix-td { text-align: center; }
.rm-matrix-td--zero { color: var(--faint, var(--text-muted)); }
.rm-matrix-module-row td {
  background: var(--surface-2, var(--hover));
  font-weight: 700;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--text-muted);
  position: sticky;
  left: 0;
}
.rm-matrix-individual-btn {
  font: inherit;
  font-size: 0.76rem;
  font-weight: 700;
  min-width: 26px;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--accent);
  background: var(--danger-bg, #faebeb);
  color: var(--accent-fg);
  cursor: pointer;
}
.rm-matrix-individual-btn:hover { filter: brightness(0.97); }

.rm-drillin-list { list-style: none; padding: 0; margin: 0 0 12px; }
.rm-drillin-list li { padding: 6px 0; border-bottom: 1px solid var(--border-faint, var(--border)); font-size: 0.82rem; }
.rm-drillin-list li:last-child { border-bottom: none; }
.rm-drillin-username { color: var(--text-muted); font-size: 0.76rem; }

@media (max-width: 720px) {
  .rm-header__top { flex-wrap: wrap; }
  .rm-view-toggle { margin-left: 0; }
}

/* Actions row */
.v2-dq-actions-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}
.v2-dq-review-btn {
  padding: 7px 14px;
  font-size: 0.8rem;
  font-weight: 600;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  border-radius: 7px;
  color: var(--accent-fg);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.v2-dq-review-btn:hover { background: var(--accent-tint, #eef2fc); border-color: var(--accent); }

/* Pair actions (merge + dismiss) */
.v2-dq-pair-actions {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-shrink: 0;
}
.v2-dq-dismiss-btn {
  padding: 5px 11px;
  font-size: 0.75rem;
  font-weight: 500;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
  white-space: nowrap;
}
.v2-dq-dismiss-btn:hover { background: var(--surface-2, #f4f3f0); color: var(--text-primary); }

/* Dismissed warnings list */
.v2-dq-section-divider { margin-top: 24px; }
.v2-dq-dismissed-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 8px;
}
.v2-dq-dismissed-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  border-radius: 8px;
  opacity: 0.75;
  flex-wrap: wrap;
}
.v2-dq-dismissed-meta {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
  min-width: 0;
}
.v2-dq-dismissed-by { font-size: 0.72rem; color: var(--text-muted); }
.v2-dq-dismissed-at { font-size: 0.68rem; color: var(--text-muted); }
.v2-dq-restore-btn {
  padding: 4px 10px;
  font-size: 0.72rem;
  font-weight: 500;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--accent-fg);
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s;
}
.v2-dq-restore-btn:hover { background: var(--accent-tint, #eef2fc); }

/* Alias table new columns */
.v2-dq-alias-usage  { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; }
.v2-dq-alias-created { font-size: 0.78rem; color: var(--text-muted); white-space: nowrap; cursor: default; }

/* Impact preview inside alias modal */
.v2-alias-impact-preview {
  margin-top: 14px;
  padding: 10px 14px;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  border-radius: 8px;
}
.v2-alias-impact-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text-primary);
}
.v2-alias-impact-item  { }
.v2-alias-impact-sep   { color: var(--text-muted); font-weight: 600; }
.v2-alias-impact-total { font-weight: 600; color: var(--accent-fg); }

/* Destination Review Modal */
.v2-dest-review-modal-box {
  max-width: 560px;
  width: 96%;
  max-height: 80vh;
  display: flex;
  flex-direction: column;
}
.v2-dest-review-modal-box .modal-body {
  overflow-y: auto;
  flex: 1;
  padding: 16px 20px;
}
.v2-dest-review-search {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 0.85rem;
  background: var(--surface);
  color: var(--text-primary);
  box-sizing: border-box;
  margin-bottom: 8px;
  outline: none;
  transition: border-color 0.15s;
}
.v2-dest-review-search:focus { border-color: var(--accent); }
.v2-dest-review-hint {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin: 0 0 10px;
}
.v2-dest-review-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.v2-dest-review-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border: 1px solid transparent;
  border-radius: 7px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.v2-dest-review-row:hover { background: var(--surface-2, #f4f3f0); }
.v2-dest-review-row--selected { background: var(--accent-tint, #eef2fc); border-color: var(--accent); }
.v2-dest-review-check { flex-shrink: 0; }
.v2-dest-review-name { flex: 1; font-size: 0.85rem; color: var(--text-primary); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-dest-review-count { font-size: 0.75rem; color: var(--text-muted); white-space: nowrap; flex-shrink: 0; }

/* ── Assignment Review Modal — Analytics Governance Layer (Sprint 7D) ─────── */
.v2-asg-review-modal-box {
  max-width: 1000px;
  width: 97%;
  max-height: 86vh;
  display: flex;
  flex-direction: column;
}
.v2-asg-review-modal-box .modal-body {
  overflow-y: auto;
  flex: 1;
  padding: 16px 20px;
}
.v2-asg-review-intro {
  font-size: 0.8rem;
  line-height: 1.55;
  color: var(--text-muted);
  margin: 0 0 14px;
}
.v2-asg-review-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 12px;
}
.v2-asg-review-filters .v2-admin-filter {
  height: 34px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  font-size: 0.8rem;
  font-weight: 600;
  padding: 0 10px;
}
.v2-asg-review-search {
  flex: 1 1 160px;
  min-width: 140px;
  height: 34px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 0.8rem;
  background: var(--surface);
  color: var(--text-primary);
  box-sizing: border-box;
  outline: none;
  transition: border-color 0.15s;
}
.v2-asg-review-search:focus { border-color: var(--accent); }
.v2-asg-review-summary {
  font-size: 0.74rem;
  color: var(--text-muted);
  margin: 0 0 10px;
  font-weight: 600;
}

/* Compact governance table — subtle hairlines, no zebra noise */
.v2-asg-review-table-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: 10px; }
.v2-asg-review-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; }
.v2-asg-review-table thead th {
  position: sticky;
  top: 0;
  background: var(--surface-2, var(--surface));
  color: var(--text-muted);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: left;
  padding: 9px 10px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.v2-asg-review-table tbody td {
  padding: 8px 10px;
  border-top: 1px solid var(--border);
  color: var(--text-primary);
  vertical-align: middle;
  white-space: nowrap;
}
.v2-asg-review-table tbody tr:first-child td { border-top: none; }
.v2-asg-review-row--excluded td { background: var(--surface-2, var(--surface)); color: var(--text-muted); }
.v2-asg-id {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--text-muted);
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.v2-asg-date { color: var(--text-muted); }
.v2-asg-dest { max-width: 160px; overflow: hidden; text-overflow: ellipsis; }

/* Semantic status chip (operational lifecycle) */
.v2-asg-status-chip {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: 999px;
  white-space: nowrap;
}
.v2-asg-status-chip--ok      { background: var(--ok-bg);     color: var(--ok);     }
.v2-asg-status-chip--info    { background: var(--info-bg);   color: var(--info);   }
.v2-asg-status-chip--danger  { background: var(--danger-bg); color: var(--danger); }
.v2-asg-status-chip--neutral { background: var(--surface-2); color: var(--text-muted); border: 1px solid var(--border); }

/* Classification chip (analytics eligibility) */
.v2-asg-class-chip {
  display: inline-block;
  font-size: 0.68rem;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: 999px;
  white-space: nowrap;
}
.v2-asg-class-chip--included { background: var(--ok-bg);   color: var(--ok);   }
.v2-asg-class-chip--excluded { background: var(--warn-bg); color: var(--warn); }
.v2-asg-class-sub {
  display: block;
  font-size: 0.62rem;
  font-weight: 600;
  color: var(--text-muted);
  margin-top: 3px;
}

/* Per-row governance actions */
.v2-asg-review-actions-th { text-align: right; }
.v2-asg-review-actions { text-align: right; }
.v2-asg-review-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  justify-content: flex-end;
}
.v2-asg-act-btn {
  font-size: 0.68rem;
  font-weight: 600;
  padding: 4px 9px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  white-space: nowrap;
}
.v2-asg-act-btn:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-tint, var(--surface-2)); }
.v2-asg-act-btn:disabled { opacity: 0.4; cursor: default; }
.v2-asg-act-btn--produksi:hover:not(:disabled)  { color: var(--ok);     border-color: var(--ok);     background: var(--ok-bg);     }
.v2-asg-act-btn--pulihkan:hover:not(:disabled)  { color: var(--ok);     border-color: var(--ok);     background: var(--ok-bg);     }
.v2-asg-act-btn--uji:hover:not(:disabled)       { color: var(--warn);   border-color: var(--warn);   background: var(--warn-bg);   }
.v2-asg-act-btn--keluarkan:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); background: var(--danger-bg); }
.v2-asg-review-empty { padding: 24px 12px; text-align: center; color: var(--text-muted); font-size: 0.82rem; }

/* ── Request Review — compact, expandable, no horizontal scroll (Sprint 7D) ──
   Only the primary fields (Tanggal · Bidang · Tujuan · Status · Klasifikasi ·
   Aksi) are columns; the secondary fields (Pemohon · Driver · Kendaraan + ID)
   live in an expandable detail row. Operational review, not a database viewer. */
.v2-req-review-modal-box { max-width: 880px; }
.v2-req-review-table { table-layout: auto; }
.v2-req-review-table thead th,
.v2-req-review-table tbody td { padding-left: 8px; padding-right: 8px; }
.v2-req-review-table .v2-req-exp-th,
.v2-req-review-table .v2-req-exp-cell { width: 28px; padding-left: 8px; padding-right: 0; }
.v2-req-review-table .v2-asg-date { white-space: nowrap; }
.v2-req-review-table .v2-req-bidang { max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
.v2-req-review-table .v2-asg-dest { max-width: 130px; }
.v2-req-exp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text-muted);
  cursor: pointer;
  transition: transform 0.15s, color 0.12s, border-color 0.12s, background 0.12s;
}
.v2-req-exp-btn:hover { color: var(--text-primary); border-color: var(--border-strong); background: var(--surface-2); }
.v2-req-exp-btn.is-open { transform: rotate(90deg); color: var(--accent-fg); border-color: var(--accent); }
.v2-req-detail-row > td { padding: 0 10px 12px; border-top: none; background: var(--surface-2, var(--surface)); }
.v2-req-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px 18px;
  padding: 10px 12px 11px 38px;
}
.v2-req-detail-item { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.v2-req-detail-label { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-muted); }
.v2-req-detail-value { font-size: 0.8rem; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; }
.v2-req-review-table .v2-asg-review-actions { gap: 4px; max-width: 168px; }
.v2-req-review-table .v2-asg-act-btn { padding: 4px 7px; }

@media (max-width: 720px) {
  .v2-asg-review-modal-box { max-width: 100%; }
  .v2-asg-review-filters .v2-admin-filter,
  .v2-asg-review-search { flex: 1 1 100%; }
}

@media (max-width: 600px) {
  .v2-dq-stats { grid-template-columns: repeat(2, 1fr); }
  .v2-dq-pair-row { flex-wrap: wrap; }
  .v2-dq-pair-actions { width: 100%; justify-content: flex-end; }
  .v2-dq-merge-btn, .v2-dq-resolved-badge { margin-left: 0; }
  .v2-alias-modal-box, .v2-dest-review-modal-box { max-width: 100%; }
  .v2-dq-alias-table th:nth-child(4),
  .v2-dq-alias-table th:nth-child(5),
  .v2-dq-alias-table td:nth-child(4),
  .v2-dq-alias-table td:nth-child(5) { display: none; }
}

/* ── v1.9.0 — PWA Foundation ────────────────────────────────────────────── */

/* PWA Config Center status grid */
.v2-pwa-status-grid {
  display: flex;
  flex-direction: column;
  gap: 0;
  border: 1px solid var(--border);
  border-radius: 9px;
  overflow: hidden;
  margin-bottom: 2px;
}
.v2-pwa-stat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  gap: 12px;
}
.v2-pwa-stat-row:last-child { border-bottom: none; }
.v2-pwa-stat-label {
  font-size: 0.8rem;
  color: var(--text-muted);
  min-width: 110px;
}
.v2-pwa-stat-value {
  font-size: 0.82rem;
  font-weight: 500;
  color: var(--text-primary);
  text-align: right;
}
.v2-pwa-stat--ok { color: #2F7D62; }

/* Action footer */
.v2-pwa-action-footer {
  padding-top: 14px;
  justify-content: flex-start;
  border-top: none;
}
.v2-pwa-installed-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  background: #f0faf6;
  border: 1px solid #2F7D62;
  border-radius: 7px;
  font-size: 0.82rem;
  font-weight: 600;
  color: #2F7D62;
}
[data-theme="dark"] .v2-pwa-installed-badge {
  background: #0d2a20;
  border-color: #2F7D62;
  color: #4CAF82;
}

/* iOS install modal */
.v2-pwa-ios-modal-box {
  max-width: 440px;
  width: 96%;
}
.v2-pwa-ios-body {
  padding: 20px;
}
.v2-pwa-ios-intro {
  font-size: 0.88rem;
  color: var(--text-secondary, var(--text-muted));
  margin-bottom: 18px;
}
.v2-pwa-ios-steps {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: 18px;
}
.v2-pwa-ios-step {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}
.v2-pwa-ios-num {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  min-width: 26px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.75rem;
  font-weight: 700;
}
.v2-pwa-ios-step-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-top: 3px;
}
.v2-pwa-ios-step-text strong {
  font-size: 0.88rem;
  color: var(--text-primary);
}
.v2-pwa-ios-hint {
  font-size: 0.75rem;
  color: var(--text-muted);
}
.v2-pwa-ios-note {
  font-size: 0.78rem;
  color: var(--text-muted);
  padding: 10px 12px;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  border-radius: 7px;
  line-height: 1.5;
}

/* Divider row before SW diagnostics section */
.v2-pwa-stat-divider {
  border-top: 1px solid var(--border);
  margin-top: 6px;
  padding-top: 6px;
}

/* Warn state — used for "Update Tersedia: Ya" diagnostic in the config panel.
   (The old persistent update banner/button was removed in v1.15.5.2 — updates
   now apply silently on startup; see js/pwa.js.) */
.v2-pwa-stat--warn { color: #B45309; }
[data-theme="dark"] .v2-pwa-stat--warn { color: #F59E0B; }

@media (max-width: 600px) {
  .v2-pwa-ios-modal-box { max-width: 100%; }
  .v2-pwa-stat-label { min-width: 90px; }
}

/* ── Install onboarding banner (Android — all roles) ─────────
   Auto-shown 3 s after page load when beforeinstallprompt fires.
   Dismissed → localStorage for 7 days.
   Auto-hidden on appinstalled / standalone mode change.
   ──────────────────────────────────────────────────────────── */
.v2-pwa-install-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 9998;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  /* Push above iPhone home-bar */
  padding-bottom: max(10px, env(safe-area-inset-bottom));
  background: var(--surface, #fff);
  border-top: 1px solid var(--border, #E0DDD8);
  box-shadow: 0 -2px 14px rgba(0, 0, 0, 0.1);
}

.v2-pwa-install-icon {
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  background: var(--brand);
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 19px;
  font-weight: 600;
  font-family: inherit;
  letter-spacing: -0.5px;
  user-select: none;
}

.v2-pwa-install-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.v2-pwa-install-name {
  font-size: 0.87rem;
  font-weight: 700;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.v2-pwa-install-hint {
  font-size: 0.74rem;
  color: var(--text-muted, #888);
}

.v2-pwa-install-cta {
  flex-shrink: 0;
  padding: 7px 15px;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 7px;
  font-size: 0.82rem;
  font-weight: 700;
  cursor: pointer;
  font-family: inherit;
  transition: background 0.15s;
  white-space: nowrap;
}

.v2-pwa-install-cta:hover    { background: #8E1E23; }
.v2-pwa-install-cta:active   { background: #751A1E; }
.v2-pwa-install-cta:disabled { opacity: 0.6; cursor: default; }

.v2-pwa-install-close {
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-muted, #888);
  font-size: 20px;
  border-radius: 6px;
  padding: 0;
  line-height: 1;
}

.v2-pwa-install-close:hover { background: var(--surface-2, #F5F4F2); }

[data-theme="dark"] .v2-pwa-install-close:hover { background: rgba(255,255,255,0.06); }

@media (max-width: 400px) {
  /* Very narrow: hide hint text to keep banner single-line */
  .v2-pwa-install-hint { display: none; }
}

/* ================================================================
   SPRINT 7 — Analytics Claude Design Experience (v1.10.x)
   Presentation-only: 6-section IA wrapped in elevated section cards,
   segmented Resource tabs, prioritized Operational Health, Export
   Center. Reuses existing design tokens; no values/logic touched.
   ================================================================ */

/* ── Section cards (the six IA groups) ─────────────────────────── */
.v2-analytics-sections--cards { gap: 28px; }

.v2-analytics-card {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow-sm);
  padding: 24px 26px;
  transition: box-shadow 0.18s ease, border-color 0.18s ease;
}
.v2-analytics-card:hover { box-shadow: var(--shadow-md); }

.v2-analytics-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}
.v2-analytics-card-titles { min-width: 0; }
.v2-analytics-card-eyebrow {
  display: block;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent-fg);
  margin-bottom: 6px;
}
.v2-analytics-card-title {
  font-size: 19px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--text);
  margin: 0;
}
.v2-analytics-card-desc {
  margin: 5px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  max-width: 64ch;
}
.v2-analytics-card-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.v2-analytics-card-body { min-width: 0; }

/* The exec card gets a subtle accent rail to anchor the page top. */
.v2-analytics-card--exec { box-shadow: var(--shadow-sm), inset 3px 0 0 var(--accent); }

/* ── Resource Analytics — segmented tabs ───────────────────────── */
.v2-analytics-tabs {
  display: flex;
  flex-wrap: wrap;            /* wrap, never horizontal-scroll */
  gap: 4px;
  padding: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 13px;
  margin-bottom: 18px;
}
.v2-analytics-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: 1 1 auto;
  justify-content: center;
  padding: 8px 14px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  background: transparent;
  border: none;
  border-radius: 9px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
.v2-analytics-tab:hover { color: var(--text); }
.v2-analytics-tab.is-active {
  color: var(--text);
  background: var(--surface);
  box-shadow: var(--shadow-sm);
}
.v2-analytics-tab.is-active .v2-analytics-tab-ic { filter: none; }
.v2-analytics-tab-ic { font-size: 13px; line-height: 1; }
.v2-analytics-tab-panel[hidden] { display: none; }

/* ── Operational Health — prioritized insight + recommendation buckets ── */
.v2-analytics-health {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.v2-analytics-health-group {
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px 16px 16px;
  background: var(--surface-2);
  border-left: 3px solid var(--faint);
}
.v2-analytics-health-group--p1 { border-left-color: var(--danger); }
.v2-analytics-health-group--p2 { border-left-color: var(--warn); }
.v2-analytics-health-group--p3 { border-left-color: var(--info); }

.v2-analytics-health-group-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
.v2-analytics-health-dot {
  width: 8px; height: 8px;
  border-radius: 999px;
  background: var(--faint);
  flex-shrink: 0;
}
.v2-analytics-health-group--p1 .v2-analytics-health-dot { background: var(--danger); }
.v2-analytics-health-group--p2 .v2-analytics-health-dot { background: var(--warn); }
.v2-analytics-health-group--p3 .v2-analytics-health-dot { background: var(--info); }
.v2-analytics-health-group-label {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text);
}
.v2-analytics-health-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px; height: 20px;
  padding: 0 7px;
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  margin-left: auto;
}
.v2-analytics-health-group-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.v2-analytics-health-item {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.v2-analytics-health-eyebrow {
  align-self: flex-start;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  padding: 1px 7px;
  border-radius: 999px;
}
.v2-analytics-health-eyebrow--insight {
  color: var(--info);
  background: var(--info-bg);
}
.v2-analytics-health-eyebrow--rec {
  color: var(--accent-fg);
  background: var(--danger-bg);
}

/* ── Export Center ─────────────────────────────────────────────── */
.v2-analytics-export-center { display: flex; flex-direction: column; gap: 14px; }
.v2-analytics-export-desc {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
}
.v2-analytics-export-row {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}
.v2-analytics-export-btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 11px 18px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 11px;
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
}
.v2-analytics-export-btn:hover {
  border-color: var(--accent);
  background: var(--danger-bg);
  box-shadow: var(--shadow-sm);
}
.v2-analytics-export-btn .v2-analytics-export-ic { font-size: 16px; line-height: 1; }
.v2-analytics-export-btn--soon {
  cursor: default;
  color: var(--faint);
  background: var(--surface-2);
  border-style: dashed;
  border-color: var(--border);
}
.v2-analytics-export-btn--soon:hover {
  border-color: var(--border);
  background: var(--surface-2);
  box-shadow: none;
}
.v2-analytics-export-soon {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--faint);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 7px;
}

/* ── Mobile: keep everything single-column, no horizontal scroll ── */
@media (max-width: 600px) {
  .v2-analytics-sections--cards { gap: 18px; }
  .v2-analytics-card { padding: 18px 16px; border-radius: 14px; }
  .v2-analytics-card-head { flex-direction: column; gap: 10px; margin-bottom: 14px; }
  .v2-analytics-card-actions { width: 100%; }
  .v2-analytics-card-title { font-size: 17px; }
  .v2-analytics-tab { flex: 1 1 40%; }   /* two-per-row, wraps cleanly */
  .v2-analytics-export-btn { flex: 1 1 100%; justify-content: center; }
}

/* ================================================================
   SPRINT 7B — CLAUDE DESIGN VISUAL LANGUAGE (v1.10.2)

   Faithful port of the approved Analytics prototype (Analytics-V2/
   styles.css + analytics.css), SCOPED under .v2-analytics-claude so
   the rest of the production app is untouched. Typography-first,
   de-boxed, Apple-style. Presentation only — no values change.
   ================================================================ */

/* ── Scoped design tokens — light (prototype values, verbatim) ──────────── */
.v2-analytics-claude {
  /* Product redesign (v1.31.24.0) — this scope no longer carries its own
     palette: Analytics, Executive and Home now render on the SAME canonical
     tokens as every other module (they used to be a cooler, Apple-like
     second palette, which is why those screens read as a different app).
     The scope-only NAMES (--text-dim, --border-faint …) stay, aliased. */
  --border-faint:  color-mix(in srgb, var(--border) 60%, transparent);

  --text-dim:   var(--muted);
  --text-faint: var(--faint);
  --text-ghost: color-mix(in srgb, var(--faint) 60%, transparent);

  /* V1 Redesign Phase 1b: accent/crit/status colors + shadow scale retired
     onto canonical :root tokens (one brand accent, one shadow scale, one
     semantic-status palette app-wide). --c-violet/--c-teal removed —
     grep-confirmed zero consumers anywhere in this file (dead, unlike
     engineering.css where the same two are live category tones). --c-neutral
     is KEPT (unlike violet/teal) even though it has zero consumers nested
     under .v2-analytics-claude directly: js/workspace/workspace-styles.js's
     `.wsp-root` is deliberately mounted *inside* this scope (its own header
     comment says so) and reads var(--c-neutral) for 3 live elements (status
     dot, severity-row bar, an icon+tint) — removing it would silently break
     those on the Home workspace. --radius/--radius-sm/--radius-lg are
     deliberately UNCHANGED: several selectors in this scope
     (.an-export-list, .ec-stat, .ec-card, .ec-history, and .wsp-card via the
     same cross-scope mounting) consume var(--radius-sm) with no fallback,
     and no canonical :root --radius-* exists yet (see platform.css:140-150)
     — removing this block would leave them with no radius at all. */
  --accent-2:    color-mix(in srgb, var(--accent) 88%, white);
  --accent-weak: var(--accent-subtle);
  --accent-line: color-mix(in srgb, var(--accent) 22%, transparent);

  --crit:      var(--danger);
  --crit-weak: var(--danger-bg);
  --crit-line: color-mix(in srgb, var(--danger) 22%, transparent);

  --c-green:  var(--ok);
  --c-blue:   var(--info);
  --c-amber:  var(--warn);
  --c-neutral: var(--faint);

  /* Product redesign: radii and rhythm on the product scale (were 16/11/22
     and a 56px section gap — the roomy "keynote" spacing). */
  --radius:    var(--r-lg);
  --radius-sm: var(--r-md);
  --radius-lg: var(--r-xl);
  --gap: 16px;
  --pad: 20px;
  --sec-gap: 40px;

  font-family: var(--font-sans);
  color: var(--text);
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* ── Scoped design tokens — dark (proper dark surfaces, not inverted) ────── */
[data-theme="dark"] .v2-analytics-claude {
  /* Product redesign: palette inherited from the canonical dark tokens. */
  --accent-line: color-mix(in srgb, var(--accent) 32%, transparent);
  --crit-line: color-mix(in srgb, var(--danger) 32%, transparent);

}

/* ── Section scaffold (de-boxed) ────────────────────────────────────────── */
.v2-analytics-claude .an-sections { display: block; }
.v2-analytics-claude .an-sections > .level { margin-top: var(--sec-gap); }
.v2-analytics-claude .an-sections > .hero,
.v2-analytics-claude .an-sections > .level:first-child { margin-top: 6px; }

.v2-analytics-claude .eyebrow { display: flex; align-items: center; gap: 12px; margin-bottom: 18px; flex-wrap: wrap; }
.v2-analytics-claude .eyebrow .tag { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: 0.04em; color: var(--accent-fg); background: var(--accent-weak); border: 1px solid var(--accent-line); padding: 3px 8px; border-radius: 7px; font-weight: 600; }
.v2-analytics-claude .eyebrow h2 { font-family: var(--font-display); font-weight: 700; font-size: 19px; margin: 0; letter-spacing: -0.018em; }
.v2-analytics-claude .eyebrow .sub { color: var(--text-faint); font-size: 13px; }
.v2-analytics-claude .eyebrow .line { flex: 1; height: 1px; background: var(--border-faint); min-width: 24px; }

.v2-analytics-claude .an-note { margin: 0 0 14px; font-size: 13px; line-height: 1.5; color: var(--text-dim); }
.v2-analytics-claude .an-note span { color: var(--text-faint); }

/* ── Analytics typography scale (v1.16.2.4) ───────────────────────────────────
   ONE reusable type language for Driver / Executive / Petty Cash. Values MIRROR
   the keynote shell (hero / eyebrow / stat), so a given hierarchy level is
   byte-identical everywhere — no module may invent its own weight/size/family.
   Reuse these instead of hardcoding inline typography. Presentation only — no
   numbers/formulas change.
     .an-display  Secondary hero / display headline (font-display 800)
     .an-tag      Eyebrow / kicker tag (mono, accent)
     .an-label    Micro label for stats, table headers, captions (sans uppercase)
     .an-figure   Inline numeric figure / KPI value (font-display, tabular)
     .an-body     Body / narrative paragraph
     .an-caption  Small caption beneath a figure                                */
.v2-analytics-claude .an-display { font-family: var(--font-display); font-weight: 600; font-size: var(--type-display-lg); line-height: 1.12; letter-spacing: -0.028em; color: var(--text); margin: 0; }
.v2-analytics-claude .an-tag { display: inline-block; font-family: var(--font-mono); font-size: var(--type-label); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--accent-fg); }
.v2-analytics-claude .an-label { font-family: var(--font-sans); font-size: var(--type-label); font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-faint); }
.v2-analytics-claude .an-figure { font-family: var(--font-display); font-weight: 700; font-size: var(--type-heading-lg); line-height: 1.15; letter-spacing: -0.012em; color: var(--text); font-variant-numeric: tabular-nums; }
.v2-analytics-claude .an-body { font-family: var(--font-sans); font-size: var(--type-body); line-height: 1.6; color: var(--text-dim); margin: 0; }
.v2-analytics-claude .an-caption { font-family: var(--font-sans); font-size: var(--type-caption); line-height: 1.5; color: var(--text-dim); }

/* ── Keynote hero ───────────────────────────────────────────────────────── */
.v2-analytics-claude .hero { margin-top: 8px; }
.v2-analytics-claude .hero-head { max-width: 860px; }
.v2-analytics-claude .hero-title { font-family: var(--font-display); font-weight: 600; font-size: clamp(22px, 2.4vw, 28px); line-height: 1.2; letter-spacing: -0.015em; margin: 0; color: var(--text); text-wrap: balance; }
.v2-analytics-claude .an-tone-green .hero-title .hl { color: var(--c-green); }
.v2-analytics-claude .an-tone-amber .hero-title .hl { color: var(--c-amber); }
.v2-analytics-claude .an-tone-crit  .hero-title .hl { color: var(--crit); }
.v2-analytics-claude .hero-sub { font-family: var(--font-sans); font-weight: 400; font-size: 15px; line-height: 1.5; letter-spacing: 0; color: var(--text-dim); margin: 8px 0 0; text-wrap: balance; }
.v2-analytics-claude .hero-sub .up { color: var(--c-green); font-weight: 600; }
.v2-analytics-claude .hero-attn { margin-top: 22px; display: inline-flex; align-items: center; gap: 9px; font-family: var(--font-sans); font-size: 13.5px; font-weight: 700; color: var(--crit); background: var(--crit-weak); border: 1px solid var(--crit-line); padding: 8px 15px; border-radius: 999px; cursor: pointer; transition: background .14s; }
/* UX/motion audit remove #2: ONE pulse when the verdict appears (was infinite —
   continuous decorative repaint for as long as the screen stayed open). */
.v2-analytics-claude .hero-attn .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--crit); animation: anAttnPulse 1.6s ease-out .4s 1 both; }
.v2-analytics-claude .hero-attn:hover { background: color-mix(in srgb, var(--crit-weak) 55%, var(--crit)); color: #fff; }
.v2-analytics-claude .hero-attn:hover .dot { background: #fff; }

.v2-analytics-claude .hero-metrics { display: grid; grid-template-columns: auto 1fr; align-items: center; margin-top: 44px; padding-top: 38px; border-top: 1px solid var(--border-faint); }
.v2-analytics-claude .hm-health { display: flex; align-items: center; gap: 24px; padding-right: 42px; border-right: 1px solid var(--border-faint); }
.v2-analytics-claude .hm-health .gwrap { position: relative; flex: 0 0 auto; }
.v2-analytics-claude .hm-health .score { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.v2-analytics-claude .hm-health .score .v { font-family: var(--font-display); font-weight: 600; font-size: 36px; line-height: 1; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.v2-analytics-claude .hm-health .score .s { font-family: var(--font-mono); font-size: 12px; color: var(--text-faint); font-weight: 500; margin-top: 3px; }
.v2-analytics-claude .hm-health .meta .lbl { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint); font-weight: 700; max-width: 132px; line-height: 1.4; }
.v2-analytics-claude .hm-health .meta .grade { display: inline-flex; align-items: center; gap: 6px; margin-top: 12px; font-size: 13px; font-weight: 700; padding: 5px 13px; border-radius: 999px; }
.v2-analytics-claude .an-tone-green .hm-health .meta .grade { background: rgba(47,158,107,0.12); color: var(--c-green); }
.v2-analytics-claude .an-tone-amber .hm-health .meta .grade { background: rgba(196,137,44,0.13); color: var(--c-amber); }
.v2-analytics-claude .an-tone-crit  .hm-health .meta .grade { background: var(--crit-weak); color: var(--crit); }

.v2-analytics-claude .an-ring-val { transition: stroke-dasharray .9s cubic-bezier(.2,.7,.2,1); }

.v2-analytics-claude .hm-stats { display: grid; grid-template-columns: repeat(3, 1fr); }
.v2-analytics-claude .hm-stat { padding: 2px 28px; border-right: 1px solid var(--border-faint); display: flex; flex-direction: column; min-width: 0; }
.v2-analytics-claude .hm-stat:last-child { border-right: none; padding-right: 0; }
.v2-analytics-claude .hm-stat .lbl { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-faint); font-weight: 700; }
.v2-analytics-claude .hm-stat .big { font-family: var(--font-display); font-weight: 600; font-size: clamp(22px, 2vw, 28px); line-height: 1.1; letter-spacing: -0.015em; margin-top: 10px; font-variant-numeric: tabular-nums; }
.v2-analytics-claude .hm-stat .big .u { font-size: 0.44em; color: var(--text-faint); font-weight: 700; margin-left: 2px; }
.v2-analytics-claude .hm-stat .delta { margin-top: 12px; display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 700; color: var(--text-dim); }
.v2-analytics-claude .hm-stat .delta.up { color: var(--c-green); }
.v2-analytics-claude .hm-stat .delta.down { color: var(--crit); }
.v2-analytics-claude .hm-stat .delta.delta--none { color: var(--text-ghost); }
.v2-analytics-claude .hm-stat .delta span { color: var(--text-faint); font-weight: 600; }
.v2-analytics-claude .hm-stat.alert .big { color: var(--crit); }
.v2-analytics-claude .hm-stat .alertbtn { margin-top: 12px; align-self: flex-start; font-size: 12.5px; font-weight: 700; color: var(--crit); background: transparent; border: none; padding: 0; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; transition: gap .14s; }
.v2-analytics-claude .hm-stat .alertbtn:hover { gap: 9px; }

/* ── Editorial highlights ───────────────────────────────────────────────── */
.v2-analytics-claude .highlights { display: grid; grid-template-columns: repeat(3, 1fr); }
.v2-analytics-claude .hl-item { text-align: left; background: none; border: none; font-family: inherit; cursor: pointer; padding: 4px 36px; display: flex; flex-direction: column; align-items: flex-start; border-left: 1px solid var(--border-faint); min-height: 170px; transition: transform .16s; color: var(--text); }
.v2-analytics-claude .hl-item:first-child { padding-left: 0; border-left: none; }
.v2-analytics-claude .hl-item:hover { transform: translateY(-2px); }
.v2-analytics-claude .hl-eye { font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); font-weight: 700; }
.v2-analytics-claude .hl-val { display: flex; align-items: center; gap: 14px; margin-top: 18px; }
.v2-analytics-claude .hl-ava { width: 48px; height: 48px; border-radius: 15px; background: linear-gradient(150deg, var(--accent-2), var(--accent)); color: #fff; display: grid; place-items: center; font-family: var(--font-display); font-weight: 600; font-size: 21px; flex: 0 0 48px; box-shadow: var(--shadow-sm); }
.v2-analytics-claude .hl-name { font-family: var(--font-display); font-weight: 600; font-size: clamp(18px, 1.8vw, 22px); letter-spacing: -0.01em; line-height: 1.2; transition: color .14s; }
.v2-analytics-claude .hl-num { font-family: var(--font-display); font-weight: 600; font-size: clamp(24px, 2.2vw, 30px); line-height: 1.1; letter-spacing: -0.015em; margin-top: 12px; font-variant-numeric: tabular-nums; transition: color .14s; }
.v2-analytics-claude .hl-num.up { color: var(--c-green); }
.v2-analytics-claude .hl-num .u { font-size: 0.42em; color: var(--text-faint); font-weight: 700; margin-left: 2px; }
.v2-analytics-claude .hl-ctx { font-size: 13px; color: var(--text-faint); margin-top: 12px; }
.v2-analytics-claude .hl-tag { margin-top: auto; padding-top: 16px; font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; }
.v2-analytics-claude .hl-tag::before { content: ''; width: 6px; height: 6px; border-radius: 50%; margin-right: 8px; }
.v2-analytics-claude .hl-tag.up { color: var(--c-green); }
.v2-analytics-claude .hl-tag.up::before { background: var(--c-green); }
.v2-analytics-claude .hl-tag.crit { color: var(--crit); }
.v2-analytics-claude .hl-tag.crit::before { background: var(--crit); }
.v2-analytics-claude .hl-item:hover .hl-name, .v2-analytics-claude .hl-item:hover .hl-num:not(.up) { color: var(--accent-fg); }

/* ── Premium segmented control + panels ─────────────────────────────────── */
.v2-analytics-claude .seg { display: inline-flex; flex-wrap: wrap; gap: 3px; padding: 4px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 13px; max-width: 100%; margin-bottom: 16px; }
.v2-analytics-claude .seg button { appearance: none; border: none; background: transparent; cursor: pointer; font-family: var(--font-sans); font-weight: 600; font-size: 13px; color: var(--text-dim); padding: 8px 15px; border-radius: 9px; white-space: nowrap; display: inline-flex; align-items: center; gap: 7px; transition: color .14s, background .14s, box-shadow .14s; }
.v2-analytics-claude .seg button .ic { display: inline-flex; opacity: .7; }
.v2-analytics-claude .seg button:hover { color: var(--text); }
.v2-analytics-claude .seg button.on { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }
.v2-analytics-claude .seg button.on .ic { opacity: 1; color: var(--accent-fg); }
.v2-analytics-claude .deep-panel { animation: anDeepIn .42s cubic-bezier(.2,.7,.2,1) both; }
.v2-analytics-claude .v2-analytics-tab-panel[hidden] { display: none; }

/* ── Card (only for detailed panels / charts) ───────────────────────────── */
.v2-analytics-claude .card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--pad); box-shadow: var(--shadow-sm); transition: border-color .16s, box-shadow .16s; }
.v2-analytics-claude .card + .card { margin-top: 14px; }
.v2-analytics-claude .card:hover { border-color: var(--border-strong); box-shadow: var(--shadow-md); }

/* ── Stat row (Operational Trends, de-boxed) ────────────────────────────── */
.v2-analytics-claude .statrow { display: grid; grid-template-columns: repeat(4, 1fr); gap: 26px; padding: 6px 0 22px; border-bottom: 1px solid var(--border-faint); margin-bottom: 22px; }
.v2-analytics-claude .statrow .st { display: flex; flex-direction: column; min-width: 0; }
.v2-analytics-claude .statrow .st .l { font-size: 10.5px; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
.v2-analytics-claude .statrow .st .v { font-family: var(--font-display); font-size: 24px; font-weight: 600; margin-top: 6px; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; }
.v2-analytics-claude .statrow .st .delta { margin-top: 8px; font-size: 12px; font-weight: 700; display: inline-flex; align-items: center; gap: 6px; color: var(--text-dim); }
.v2-analytics-claude .statrow .st .delta.up { color: var(--c-green); }
.v2-analytics-claude .statrow .st .delta.down { color: var(--crit); }
.v2-analytics-claude .statrow .st .delta.delta--none { color: var(--text-ghost); }
/* Cancellation status cell (v1.10.8) — visible but clearly separated/muted. */
.v2-analytics-claude .statrow .st--cancelled .l { color: var(--crit); }
.v2-analytics-claude .statrow .st--cancelled .v { color: var(--crit); }

/* ── Operational Health — divider insight list ──────────────────────────── */
.v2-analytics-claude .insights { display: flex; flex-direction: column; }
.v2-analytics-claude .insight { display: flex; gap: 14px; padding: 16px 10px; border-top: 1px solid var(--border-faint); background: transparent; align-items: flex-start; border-radius: 11px; transition: background .14s; animation: anFadeUp .5s cubic-bezier(.2,.7,.2,1) both; }
.v2-analytics-claude .insight:first-child { border-top: none; }
.v2-analytics-claude .insight:hover { background: var(--hover); }
.v2-analytics-claude .insight:nth-child(1) { animation-delay: .02s; }
.v2-analytics-claude .insight:nth-child(2) { animation-delay: .06s; }
.v2-analytics-claude .insight:nth-child(3) { animation-delay: .10s; }
.v2-analytics-claude .insight:nth-child(4) { animation-delay: .14s; }
.v2-analytics-claude .insight:nth-child(5) { animation-delay: .18s; }
.v2-analytics-claude .insight:nth-child(n+6) { animation-delay: .22s; }
.v2-analytics-claude .insight .ib { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; flex: 0 0 32px; }
.v2-analytics-claude .insight .an-insight-main { min-width: 0; }
.v2-analytics-claude .insight .it { font-weight: 700; font-size: 13.5px; color: var(--text); display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.v2-analytics-claude .insight .id { font-size: 12.5px; color: var(--text-faint); margin-top: 4px; line-height: 1.5; }
.v2-analytics-claude .insight .sev { font-size: 9.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; padding: 2px 7px; border-radius: 6px; }
.v2-analytics-claude .sev-warn { color: var(--c-amber); background: rgba(196,137,44,0.13); }
.v2-analytics-claude .sev-crit { color: var(--crit); background: var(--crit-weak); }
.v2-analytics-claude .sev-info { color: var(--c-blue); background: rgba(63,114,214,0.12); }
.v2-analytics-claude .sev-good { color: var(--c-green); background: rgba(47,158,107,0.12); }
.v2-analytics-claude .ib-warn { background: rgba(196,137,44,0.13); color: var(--c-amber); }
.v2-analytics-claude .ib-crit { background: var(--crit-weak); color: var(--crit); }
.v2-analytics-claude .ib-info { background: rgba(63,114,214,0.12); color: var(--c-blue); }
.v2-analytics-claude .ib-good { background: rgba(47,158,107,0.12); color: var(--c-green); }
.v2-analytics-claude .an-kind { font-size: 9.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; padding: 2px 7px; border-radius: 6px; border: 1px solid var(--border); color: var(--text-faint); }
.v2-analytics-claude .an-kind--rec { color: var(--accent-fg); border-color: var(--accent-line); background: var(--accent-weak); }

/* ── Score Breakdown bars (v1.16.3) — Executive Petty Cash explainability ─────
   Compact weighted sub-score list under the hero. Quiet, editorial: a label, a
   tabular score, and a thin tone-colored bar. No boxes — matches the de-boxed
   insight rows above. */
.v2-analytics-claude .an-scorebar { display: flex; flex-direction: column; gap: 15px; }
.v2-analytics-claude .an-sb-row { animation: anFadeUp .5s cubic-bezier(.2,.7,.2,1) both; }
.v2-analytics-claude .an-sb-row:nth-child(1) { animation-delay: .02s; }
.v2-analytics-claude .an-sb-row:nth-child(2) { animation-delay: .06s; }
.v2-analytics-claude .an-sb-row:nth-child(3) { animation-delay: .10s; }
.v2-analytics-claude .an-sb-row:nth-child(4) { animation-delay: .14s; }
.v2-analytics-claude .an-sb-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 7px; }
.v2-analytics-claude .an-sb-lbl { font-weight: 700; font-size: 13px; color: var(--text); display: inline-flex; align-items: baseline; gap: 8px; }
.v2-analytics-claude .an-sb-wt { font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; color: var(--text-faint); }
.v2-analytics-claude .an-sb-val { font-family: var(--font-mono); font-size: 14px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.v2-analytics-claude .an-sb-track { height: 7px; border-radius: 99px; background: var(--border-faint); overflow: hidden; }
.v2-analytics-claude .an-sb-fill { display: block; height: 100%; border-radius: 99px; transition: width .8s cubic-bezier(.2,.7,.2,1); }
.v2-analytics-claude .an-sb-fill.an-tone-green { background: var(--c-green); }
.v2-analytics-claude .an-sb-fill.an-tone-amber { background: var(--c-amber); }
.v2-analytics-claude .an-sb-fill.an-tone-crit  { background: var(--crit); }
/* v1.16.4.6.1 Phase B — per-component analysis-horizon subtitle. */
.v2-analytics-claude .an-sb-scope { font-size: 11px; color: var(--muted); margin: -3px 0 7px; letter-spacing: .01em; }

/* ── Executive Trust Layer (v1.16.4.6.1) — confidence badge / transparency /
   null-state. Presentation only; no score, weight, or formula meaning. ──────── */
.v2-analytics-exec .exec-confidence {
  display: flex; width: fit-content; align-items: center; gap: 8px;
  margin: -6px auto 0; padding: 6px 14px; border-radius: 99px;
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
}
.v2-analytics-exec .exec-confidence-dot { width: 8px; height: 8px; border-radius: 99px; background: var(--muted); }
.v2-analytics-exec .exec-confidence--high   { border-color: var(--c-green); color: var(--c-green); }
.v2-analytics-exec .exec-confidence--high   .exec-confidence-dot { background: var(--c-green); }
.v2-analytics-exec .exec-confidence--medium { border-color: var(--c-green); color: var(--text); }
.v2-analytics-exec .exec-confidence--medium .exec-confidence-dot { background: var(--c-green); }
.v2-analytics-exec .exec-confidence--low    { border-color: var(--c-amber); color: var(--c-amber); }
.v2-analytics-exec .exec-confidence--low    .exec-confidence-dot { background: var(--c-amber); }
.v2-analytics-exec .exec-confidence--insufficient { border-color: var(--border); color: var(--muted); }
.v2-analytics-exec .exec-confidence--insufficient .exec-confidence-dot { background: var(--muted); }

/* Phase D — null-state clarification note (idle-cash / partial-data case). */
.v2-analytics-exec .exec-nullstate {
  margin-top: 12px; padding: 11px 14px; border-radius: 8px;
  background: var(--warn-bg, var(--surface)); border: 1px solid var(--c-amber);
  font-size: 12px; line-height: 1.5; color: var(--text);
}

/* Phase C — transparency panel facts. */
.v2-analytics-exec .exec-transparency {
  padding: 14px 16px; border-radius: 10px;
  background: var(--surface); border: 1px solid var(--border);
}
.v2-analytics-exec .exec-transparency-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; }
.v2-analytics-exec .exec-transparency-list li { font-size: 12.5px; line-height: 1.45; color: var(--text); }
.v2-analytics-exec .exec-transparency-empty { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--muted); }

/* ── Export Center — future-ready reporting hub (Sprint 7D restore) ───────────
   A calm, secondary format LIST. Intentionally quiet: no gradients, no large
   surfaces, no banner — it must not compete with the keynote hero. */
.v2-analytics-claude .an-export { display: flex; flex-direction: column; gap: 14px; }
.v2-analytics-claude .an-export-desc { margin: 0; font-size: 13px; line-height: 1.55; color: var(--text-dim); max-width: 62ch; }
.v2-analytics-claude .an-export-list { display: flex; flex-direction: column; border: 1px solid var(--border-faint); border-radius: var(--radius-sm); overflow: hidden; }
.v2-analytics-claude .an-export-item { display: flex; align-items: center; gap: 13px; padding: 13px 15px; border-top: 1px solid var(--border-faint); background: var(--surface); }
.v2-analytics-claude .an-export-item:first-child { border-top: none; }
.v2-analytics-claude .an-export-item--soon { background: transparent; }
.v2-analytics-claude .an-export-ic { display: inline-flex; width: 34px; height: 34px; flex: 0 0 34px; border-radius: 9px; background: var(--surface-2); color: var(--text-dim); align-items: center; justify-content: center; }
.v2-analytics-claude .an-export-item--soon .an-export-ic { color: var(--text-faint); }
.v2-analytics-claude .an-export-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.v2-analytics-claude .an-export-name { font-size: 13.5px; font-weight: 700; color: var(--text); letter-spacing: -0.005em; }
.v2-analytics-claude .an-export-item--soon .an-export-name { color: var(--text-dim); }
.v2-analytics-claude .an-export-sub { font-size: 12px; color: var(--text-faint); line-height: 1.45; }
.v2-analytics-claude .an-export-go { flex: 0 0 auto; height: 32px; padding: 0 13px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); color: var(--text-dim); font-family: var(--font-sans); font-weight: 600; font-size: 12.5px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; box-shadow: var(--shadow-sm); transition: color .12s, border-color .12s, background .12s; }
.v2-analytics-claude .an-export-go:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-2); }
.v2-analytics-claude .an-export-chip { flex: 0 0 auto; font-size: 10px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-faint); border: 1px dashed var(--border); padding: 4px 9px; border-radius: 999px; }

/* ── Restyle RETAINED markup inside the scope (de-box, prototype type) ───── */
/* flatten the old nested group boxes so only .card boxes */
.v2-analytics-claude .v2-analytics-groups { display: block; }
.v2-analytics-claude .v2-admin-config-group { background: transparent; border: none; padding: 0; box-shadow: none; border-radius: 0; }
.v2-analytics-claude .v2-admin-config-group + .v2-admin-config-group { margin-top: 14px; }
.v2-analytics-claude .v2-admin-config-group-title { font-family: var(--font-display); font-weight: 700; font-size: 15.5px; letter-spacing: -0.012em; color: var(--text); margin: 0 0 10px; }
.v2-analytics-claude .v2-analytics-subtitle { font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); font-weight: 700; margin: 14px 0 7px; }
.v2-analytics-claude .v2-analytics-subtitle--mt { margin-top: 16px; }
/* KPI summary rows → clean hairline rows, mono values */
.v2-analytics-claude .v2-analytics-kpi-list { display: flex; flex-direction: column; }
.v2-analytics-claude .v2-analytics-kpi-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; padding: 7px 0; border-top: 1px solid var(--border-faint); }
.v2-analytics-claude .v2-analytics-kpi-row:first-child { border-top: none; }
.v2-analytics-claude .v2-analytics-kpi-label { font-size: 13px; color: var(--text-dim); font-weight: 600; }
.v2-analytics-claude .v2-analytics-kpi-val { font-family: var(--font-mono); font-size: 13px; color: var(--text); font-variant-numeric: tabular-nums; }
.v2-analytics-claude .v2-analytics-kpi-val--warn { color: var(--c-amber); }
/* breakdown rows → bar-list aesthetic */
.v2-analytics-claude .v2-analytics-breakdown { display: flex; flex-direction: column; }
.v2-analytics-claude .v2-analytics-breakdown-row { display: flex; align-items: center; gap: 12px; padding: 7px 0; border-top: 1px solid var(--border-faint); font-size: 13px; color: var(--text-dim); }
.v2-analytics-claude .v2-analytics-breakdown-row:first-child { border-top: none; }
.v2-analytics-claude .v2-analytics-breakdown-row--header { color: var(--text-ghost); font-size: 10.5px; letter-spacing: 0.07em; text-transform: uppercase; font-weight: 700; padding-bottom: 8px; }
.v2-analytics-claude .v2-analytics-breakdown-rank { width: 19px; height: 19px; border-radius: 6px; background: var(--surface-2); color: var(--text-faint); font-family: var(--font-mono); font-size: 10px; font-weight: 600; display: grid; place-items: center; flex: 0 0 19px; }
.v2-analytics-claude .v2-analytics-breakdown-name { flex: 1; min-width: 0; color: var(--text); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-analytics-claude .v2-analytics-breakdown-count,
.v2-analytics-claude .v2-analytics-breakdown-pct { font-family: var(--font-mono); font-size: 12.5px; color: var(--text-dim); font-variant-numeric: tabular-nums; text-align: right; min-width: 44px; }
.v2-analytics-claude .v2-analytics-breakdown-count--primary { color: var(--text); font-weight: 600; min-width: 64px; }
.v2-analytics-claude .v2-analytics-breakdown-count--muted { color: var(--text-faint); }
.v2-analytics-claude .v2-analytics-breakdown-row--header .v2-analytics-breakdown-count--primary,
.v2-analytics-claude .v2-analytics-breakdown-row--header .v2-analytics-breakdown-count--muted { color: var(--text-ghost); font-weight: 700; }
.v2-analytics-claude .v2-analytics-top-badge { font-family: var(--font-sans); font-size: 9.5px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--accent-fg); background: var(--accent-weak); border: 1px solid var(--accent-line); border-radius: 5px; padding: 1px 6px; margin-left: 6px; }
.v2-analytics-claude .v2-analytics-wl-badge { font-size: 10px; font-weight: 700; padding: 2px 8px; border-radius: 999px; }
.v2-analytics-claude .v2-analytics-wl-badge--over { color: var(--c-amber); background: rgba(196,137,44,0.13); }
.v2-analytics-claude .v2-analytics-wl-badge--under { color: var(--c-blue); background: rgba(63,114,214,0.12); }
.v2-analytics-claude .v2-analytics-wl-badge--balanced { color: var(--c-green); background: rgba(47,158,107,0.12); }
.v2-analytics-claude .v2-analytics-wl-badge--idle { color: var(--text-faint); background: var(--surface-2); }
.v2-analytics-claude .v2-analytics-empty { font-size: 12.5px; color: var(--text-faint); padding: 10px 0; }
/* charts → de-box (now live inside .card) */
.v2-analytics-claude .v2-analytics-chart-wrap { background: transparent; border: none; padding: 0; margin-top: 12px; }
.v2-analytics-claude .v2-analytics-chart-label { font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-faint); font-weight: 700; margin-bottom: 12px; }
/* empty state */
.v2-analytics-claude .v2-analytics-empty-state { min-height: 140px; display: grid; place-items: center; text-align: center; }
.v2-analytics-claude .v2-analytics-empty-state-msg { color: var(--text-ghost); font-size: 13px; font-weight: 600; margin: 0; }
.v2-analytics-claude .v2-analytics-empty-state-hint { color: var(--text-faint); font-size: 12px; margin: 4px 0 0; }
.v2-analytics-claude .an-state-ico { display: inline-flex; vertical-align: -2px; color: var(--c-amber); }
/* filter summary chips */
.v2-analytics-claude .v2-analytics-filter-chip { display: inline-flex; align-items: center; height: 28px; padding: 0 11px; border-radius: 999px; font-size: 12px; font-weight: 600; border: 1px solid var(--border); }
.v2-analytics-claude .v2-analytics-filter-chip--date { color: var(--text-dim); background: var(--surface); }
.v2-analytics-claude .v2-analytics-filter-chip--active { color: var(--accent-fg); background: var(--accent-weak); border-color: var(--accent-line); }
/* header / toolbar → page-head + filter-bar feel */
.v2-analytics-claude .v2-analytics-header-title { font-family: var(--font-display) !important; font-weight: 600 !important; font-size: 26px !important; letter-spacing: -0.022em; color: var(--text); }
.v2-analytics-claude .v2-analytics-header-sub { color: var(--text-dim) !important; }
.v2-analytics-claude .v2-admin-filter { height: 36px; border-radius: 10px; border: 1px solid var(--border); background: var(--surface); color: var(--text); font-family: var(--font-sans); font-size: 13px; font-weight: 600; box-shadow: var(--shadow-sm); padding: 0 10px; }
.v2-analytics-claude .v2-admin-filter:hover { border-color: var(--border-strong); }
.v2-analytics-claude .v2-analytics-reset-btn { height: 36px; border-radius: 10px; border: 1px solid transparent; background: transparent; color: var(--text-faint); font-weight: 600; font-size: 13px; padding: 0 12px; cursor: pointer; }
.v2-analytics-claude .v2-analytics-reset-btn:hover { color: var(--text); background: var(--hover); }
/* Header Export PDF — quiet, theme-aware utility action (Sprint 7C) */
/* Compact, content-width Export PDF utility — uses .v2-analytics-export-btn
   (never the full-width .btn-reimbursement). Scoped tweaks for the Claude tokens. */
.v2-analytics-claude .v2-analytics-export-btn { box-shadow: var(--shadow-sm); color: var(--text-dim); font-size: 12.5px; padding: 8px 13px; border-radius: 9px; }
.v2-analytics-claude .v2-analytics-export-btn:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-2); }

/* ── Keyframes (calm; gated by reduced-motion / data-anim) ──────────────── */
@keyframes anFadeUp { from { transform: translateY(9px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes anDeepIn { from { transform: translateY(8px); } to { transform: none; } }
@keyframes anAttnPulse { 0% { box-shadow: 0 0 0 0 var(--crit-line); } 70% { box-shadow: 0 0 0 6px rgba(207,74,67,0); } 100% { box-shadow: 0 0 0 0 rgba(207,74,67,0); } }
.v2-analytics-claude .fade-up { animation: anFadeUp .5s cubic-bezier(.2,.7,.2,1) both; }

:root[data-anim="off"] .v2-analytics-claude .fade-up,
:root[data-anim="off"] .v2-analytics-claude .deep-panel,
:root[data-anim="off"] .v2-analytics-claude .insight { animation: none; }
:root[data-anim="off"] .v2-analytics-claude .hero-attn .dot { animation: none; }
:root[data-anim="off"] .v2-analytics-claude .an-ring-val { transition: none; }
@media (prefers-reduced-motion: reduce) {
  .v2-analytics-claude .fade-up,
  .v2-analytics-claude .deep-panel,
  .v2-analytics-claude .insight { animation: none; }
  .v2-analytics-claude .hero-attn .dot { animation: none; }
  .v2-analytics-claude .an-ring-val { transition: none; }
}

/* ── Responsive (mobile-first intent; no horizontal scroll) ─────────────── */
@media (max-width: 920px) {
  .v2-analytics-claude .hero-metrics { grid-template-columns: 1fr; }
  .v2-analytics-claude .hm-health { border-right: none; border-bottom: 1px solid var(--border-faint); padding-right: 0; padding-bottom: 30px; margin-bottom: 30px; }
}
@media (max-width: 760px) {
  .v2-analytics-claude .statrow { grid-template-columns: 1fr 1fr; gap: 20px; }
  /* v1.15.2: keep the three hero KPIs HORIZONTAL on mobile (all visible above
     the fold) instead of stacking full-width — saves ~2 rows of vertical space.
     Compact dividers + viewport-scaled values; the currency stat ("Dana
     Terpakai") wraps within its cell rather than overflowing. */
  .v2-analytics-claude .hm-stats { grid-template-columns: repeat(3, 1fr); gap: 10px; }
  .v2-analytics-claude .hm-stat { border-right: 1px solid var(--border-faint); border-bottom: none; padding: 0 9px; min-width: 0; }
  .v2-analytics-claude .hm-stat:first-child { padding-left: 0; }
  .v2-analytics-claude .hm-stat:last-child { border-right: none; padding-right: 0; }
  .v2-analytics-claude .hm-stat .lbl { font-size: 9.5px; letter-spacing: 0.03em; }
  .v2-analytics-claude .hm-stat .big { font-size: clamp(16px, 5.2vw, 24px); margin-top: 8px; overflow-wrap: anywhere; }
  .v2-analytics-claude .hm-stat .delta,
  .v2-analytics-claude .hm-stat .alertbtn { font-size: 11px; margin-top: 8px; }
  .v2-analytics-claude .highlights { grid-template-columns: 1fr; }
  .v2-analytics-claude .hl-item { border-left: none; border-top: 1px solid var(--border-faint); padding: 22px 0; min-height: 0; }
  .v2-analytics-claude .hl-item:first-child { border-top: none; padding-top: 4px; }
  .v2-analytics-claude .hl-tag { margin-top: 14px; }
  .v2-analytics-claude .seg { width: 100%; }
  .v2-analytics-claude .seg button { flex: 1 1 auto; justify-content: center; }
  .v2-analytics-claude .an-sections > .level { margin-top: 46px; }
}
/* v1.15.4 — Executive period chips: horizontal scroll on narrow viewports.
   Overrides the shared `.seg` mobile WRAP (which would stack the 5 chips into
   2–3 rows on 320–430px) with a single scrollable row. Scoped to the Executive
   filter bar via .exec-filter-bar .exec-period-seg (specificity 0,3,1 > the
   shared .seg button rule) so Driver Analytics segments are untouched. */
@media (max-width: 600px) {
  .v2-analytics-claude .exec-filter-bar .exec-period-seg {
    width: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;            /* Firefox */
  }
  .v2-analytics-claude .exec-filter-bar .exec-period-seg::-webkit-scrollbar { display: none; }
  .v2-analytics-claude .exec-filter-bar .exec-period-seg button {
    flex: 0 0 auto;                   /* natural width; scroll instead of squish/wrap */
    justify-content: center;
  }
}
@media (max-width: 520px) {
  .v2-analytics-claude .statrow { grid-template-columns: 1fr 1fr; }
  .v2-analytics-claude .hm-health { flex-direction: column; text-align: center; align-items: center; gap: 16px; }
  .v2-analytics-claude .hm-health .meta .lbl { max-width: none; }
  /* v1.15.2: shrink the health ring + score on small phones so the 3 hero KPIs
     below it stay above the fold (the SVG width/height attrs are overridden). */
  .v2-analytics-claude .hm-health .an-ring { width: 132px; height: 132px; }
  .v2-analytics-claude .hm-health .score .v { font-size: 30px; }
  .v2-analytics-claude .an-export-item { flex-wrap: wrap; }
  .v2-analytics-claude .an-export-go,
  .v2-analytics-claude .an-export-chip { margin-left: 47px; }
}
/* Tightest phones: trim 3-up gutters so wide currency never forces overflow. */
@media (max-width: 380px) {
  .v2-analytics-claude .hm-stats { gap: 7px; }
  .v2-analytics-claude .hm-stat { padding: 0 6px; }
}

/* ── Export Center — modern hub (v1.12.1C) ───────────────────────────────
   Built on the Registry (catalog) + Metadata (history/summary) foundations.
   Summary stat row + a two-column Catalog / History grid (stacked on mobile). */
.v2-analytics-claude .ec { display: flex; flex-direction: column; gap: 22px; }

/* Summary — four headline figures derived from the metadata cache */
.v2-analytics-claude .ec-summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.v2-analytics-claude .ec-stat { display: flex; flex-direction: column; gap: 4px; padding: 14px 16px; border: 1px solid var(--border-faint); border-radius: var(--radius-sm); background: var(--surface); min-width: 0; }
.v2-analytics-claude .ec-stat-num { font-family: var(--font-display); font-weight: 600; font-size: 24px; letter-spacing: -0.02em; color: var(--text); line-height: 1.1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-analytics-claude .ec-stat--wide .ec-stat-num { font-size: 16px; font-weight: 700; }
.v2-analytics-claude .ec-stat--ok .ec-stat-num { color: var(--c-green); }
.v2-analytics-claude .ec-stat--fail .ec-stat-num { color: var(--crit); }
.v2-analytics-claude .ec-stat-label { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; font-weight: 700; color: var(--text-faint); }

/* Two-column layout */
.v2-analytics-claude .ec-grid { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); gap: 22px; align-items: start; }
.v2-analytics-claude .ec-col { min-width: 0; display: flex; flex-direction: column; gap: 12px; }
.v2-analytics-claude .ec-col-title { font-family: var(--font-display); font-weight: 700; font-size: 14px; letter-spacing: -0.01em; color: var(--text); margin: 0; }

/* Catalog cards (from listExportReports) */
.v2-analytics-claude .ec-catalog { display: flex; flex-direction: column; gap: 10px; }
.v2-analytics-claude .ec-card { display: flex; align-items: center; gap: 13px; padding: 13px 15px; border: 1px solid var(--border-faint); border-radius: var(--radius-sm); background: var(--surface); }
.v2-analytics-claude .ec-card-ic { display: inline-flex; width: 36px; height: 36px; flex: 0 0 36px; border-radius: 9px; background: var(--surface-2); color: var(--text-dim); align-items: center; justify-content: center; }
.v2-analytics-claude .ec-card-ic svg { width: 18px; height: 18px; }
.v2-analytics-claude .ec-card-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.v2-analytics-claude .ec-card-title { font-size: 13.5px; font-weight: 700; color: var(--text); letter-spacing: -0.005em; }
.v2-analytics-claude .ec-card-desc { font-size: 12px; color: var(--text-faint); line-height: 1.45; }
.v2-analytics-claude .ec-card-gen { flex: 0 0 auto; height: 32px; padding: 0 14px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface); color: var(--text-dim); font-family: var(--font-sans); font-weight: 600; font-size: 12.5px; cursor: pointer; box-shadow: var(--shadow-sm); transition: color .12s, border-color .12s, background .12s; }
.v2-analytics-claude .ec-card-gen:hover { color: var(--text); border-color: var(--border-strong); background: var(--surface-2); }
.v2-analytics-claude .ec-card-gen:disabled { opacity: 0.6; cursor: default; }

/* History list (from the metadata cache) */
.v2-analytics-claude .ec-history { display: flex; flex-direction: column; border: 1px solid var(--border-faint); border-radius: var(--radius-sm); overflow: hidden; }
.v2-analytics-claude .ec-history--empty { padding: 0; }
.v2-analytics-claude .ec-empty { margin: 0; padding: 18px 15px; font-size: 12.5px; color: var(--text-faint); }
.v2-analytics-claude .ec-hist { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 15px; border-top: 1px solid var(--border-faint); background: var(--surface); }
.v2-analytics-claude .ec-hist:first-child { border-top: none; }
.v2-analytics-claude .ec-hist-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.v2-analytics-claude .ec-hist-title { font-size: 13px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-analytics-claude .ec-hist-meta { font-size: 11.5px; color: var(--text-faint); }
.v2-analytics-claude .ec-hist-aside { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.v2-analytics-claude .ec-hist-stat { font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* Status badges */
.v2-analytics-claude .ec-badge { font-size: 10px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; padding: 2px 9px; border-radius: 999px; }
.v2-analytics-claude .ec-badge--success { color: var(--c-green); background: rgba(47,158,107,0.12); }
.v2-analytics-claude .ec-badge--failed { color: var(--crit); background: var(--crit-weak); }

/* Responsive — stack catalog over history, 2-up summary */
@media (max-width: 920px) {
  .v2-analytics-claude .ec-grid { grid-template-columns: 1fr; gap: 26px; }
}
@media (max-width: 560px) {
  .v2-analytics-claude .ec-summary { grid-template-columns: 1fr 1fr; }
  .v2-analytics-claude .ec-card { flex-wrap: wrap; }
  .v2-analytics-claude .ec-card-gen { margin-left: 49px; }
}

/* ═══════════════════════════════════════════════════════════════
   v1.12.2 — Task 1: Dark Mode footer bleed (iPhone "Tambah Jadwal")
   ───────────────────────────────────────────────────────────────
   ROOT CAUSE: #assignmentForm .form-actions is a sticky footer that
   style.css hardcodes to `background: var(--white)` (id+class
   specificity 1,1,0). The generic dark override
   `[data-theme="dark"] .form-actions` is class-only (0,2,0) and only
   recoloured the border-top — so the footer background stayed pure
   white in dark mode, showing a white strip behind the Simpan/Batal
   buttons. Most visible on the iPhone bottom-sheet, where the footer
   sits over the home-indicator safe-area.

   FIX: make the footer background follow the theme. The modal box +
   body are already dark (var(--surface), see the dark .modal-box /
   .modal-header rules above), so the bottom safe-area inset region —
   which is .modal-body's padding-bottom: max(20px, env(safe-area-
   inset-bottom)) over the dark modal-box — already reads dark once
   the footer matches. Buttons then merge seamlessly with the modal.

   Scoped to [data-theme="dark"] only: light mode (Android + iPhone +
   desktop) is byte-for-byte unchanged; this purely repairs the dark
   theme on every platform. style.css remains untouched (append-only).
   ═══════════════════════════════════════════════════════════════ */
[data-theme="dark"] #assignmentForm .form-actions {
  background: var(--surface);
}

/* ═══════════════════════════════════════════════════════════════════
   v1.14.0 — Navigation Architecture Foundation

   Unifies Driver Operations, Petty Cash Center, Analytics and Konfigurasi
   into one platform shell. The rail = MODUL, the panel = MENU. The shared
   Administration workspace is reused headless (its title hidden; its tab strip
   becomes the mobile-only sub-nav). Petty Cash is embedded (no overlay).
   ═══════════════════════════════════════════════════════════════════ */

/* Panel section sub-headers (Operasional / Master Data / Audit). */
.v2-panel-section {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim, #8a8a93);
  padding: 14px 12px 6px;
  user-select: none;
}
.v2-panel-nav > .v2-panel-section:first-child { padding-top: 4px; }

/* The Administration workspace is now headless — the platform panel (desktop)
   and the tab strip (mobile) provide navigation + context, so the retired
   "Administration" page header is hidden at every breakpoint. */
.v2-admin-page-header { display: none !important; }

/* The admin tab strip is the mobile sub-nav only; on desktop the panel menu
   drives navigation (no redundant in-content tabs). */
@media (min-width: 768px) {
  .v2-admin-nav { display: none !important; }
}

/* ── Mobile Analytics sub-nav (v1.15.2) ───────────────────────────────────
   A module-level segmented strip giving mobile users parity access to the
   three Analytics screens (Driver / Petty Cash / Executive). Reuses the
   .v2-admin-nav-tab look but renders as an equal-width segmented control with
   ≥44px touch targets. JS toggles display:flex when the Analytics module is
   active; the media query below keeps it mobile-only (the panel drives desktop).
   The inline display set by JS is non-important, so the desktop !important wins. */
#v2AnalyticsMobileNav.v2-an-mnav {
  display: none;
  gap: 3px;
  margin: 0 0 16px;
  padding: 4px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 13px;
  overflow: visible;
}
.v2-an-mnav .v2-admin-nav-tab {
  flex: 1 1 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 44px;
  padding: 8px 10px;
  border-radius: 9px;
  font-weight: 600;
  white-space: nowrap;
}
.v2-an-mnav .v2-admin-nav-tab::after { display: none; }
.v2-an-mnav .v2-admin-nav-tab--active {
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow-sm);
  padding-bottom: 8px;
}
@media (min-width: 768px) {
  #v2AnalyticsMobileNav.v2-an-mnav { display: none !important; }
}

/* Module "coming soon" placeholder (Analytics Petty Cash / Analytics Gabungan). */
.v2-module-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 50vh;
  padding: 40px 20px;
}
.v2-module-placeholder-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
  max-width: 440px;
  color: var(--text-dim, #8a8a93);
}
.v2-module-placeholder-card svg { color: var(--text-dim, #b0b0b8); opacity: 0.8; }
.v2-module-placeholder-title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--text);
}
.v2-module-placeholder-text { margin: 0; font-size: 14px; line-height: 1.6; }

/* ============================================================
   v1.16.4.7 — Driver Actual Working Time & Overtime Detection
   Append-only. Amber LEMBUR badge on overtime timeline blocks +
   Normal/Lembur indicators in Driver Analytics. Theme-aware via
   the existing --warn / --warn-bg tokens.
   ============================================================ */

/* Timeline: amber "Lembur" badge + subtle amber outline on OT blocks. */
body.v2-shell-active .block-status-badge--overtime {
  background: var(--warn-bg);
  color: var(--warn);
}
.assignment-block.is-overtime {
  box-shadow: inset 0 0 0 1.5px var(--warn);
}

/* Driver Analytics: Normal / Lembur per-driver indicator. */
.v2-analytics-ot-badge {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  padding: 3px 8px;
  border-radius: 6px;
  flex-shrink: 0;
  white-space: nowrap;
  width: 60px;
  justify-content: center;
}
.v2-analytics-ot-badge--lembur {
  background: var(--warn-bg);
  color: var(--warn);
}
.v2-analytics-ot-badge--normal {
  background: var(--ok-bg);
  color: var(--ok);
}

/* Explanatory note under the working-time KPI group. */
.v2-analytics-note {
  margin: 10px 0 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--muted);
}

/* ── Driver Workload Intelligence (v1.16.4.8) — explainable per-driver score ── */
.v2-wl-list { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
.v2-wl-item {
  border: 1px solid var(--border-faint);
  border-radius: 10px;
  padding: 11px 13px;
  background: var(--surface-2, transparent);
}
.v2-wl-item--top { border-color: var(--accent, var(--ok)); }
.v2-wl-head { display: flex; align-items: center; gap: 9px; }
.v2-wl-rank {
  font-family: var(--font-mono); font-size: 11px; font-weight: 700;
  color: var(--text-ghost); min-width: 24px;
}
.v2-wl-name { flex: 1; min-width: 0; font-weight: 700; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-wl-score {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: 17px; font-weight: 600; color: var(--text); line-height: 1;
}
.v2-wl-score-unit { font-size: 10px; font-weight: 600; color: var(--text-ghost); margin-left: 2px; }
.v2-wl-metrics {
  margin-top: 5px; font-family: var(--font-mono); font-size: 11.5px;
  color: var(--text-dim); font-variant-numeric: tabular-nums;
}
.v2-wl-bars {
  display: flex; height: 7px; border-radius: 4px; overflow: hidden;
  margin-top: 9px; background: var(--border-faint);
}
.v2-wl-seg { height: 100%; }
.v2-wl-seg--hours { background: var(--accent, #4f8cff); }
.v2-wl-seg--dist  { background: var(--ok, #36b37e); }
.v2-wl-seg--asg   { background: var(--warn, #f5a623); }
.v2-wl-legend {
  display: flex; flex-wrap: wrap; gap: 12px; margin-top: 7px;
  font-size: 10.5px; color: var(--muted);
}
.v2-wl-legend-item { display: inline-flex; align-items: center; gap: 5px; }
.v2-wl-dot { width: 8px; height: 8px; border-radius: 2px; flex-shrink: 0; }
.v2-wl-dot--hours { background: var(--accent, #4f8cff); }
.v2-wl-dot--dist  { background: var(--ok, #36b37e); }
.v2-wl-dot--asg   { background: var(--warn, #f5a623); }

/* ============================================================================
   EXECUTIVE UI KIT — v1.18.3 Foundation
   ----------------------------------------------------------------------------
   ADDITIVE. Every selector below is a NEW `.exec-*` class consumed by NOTHING
   in production yet (Sprint 1 prepares the platform; migration starts Sprint 2).
   Therefore this block changes the appearance of ZERO existing pages.

   Token strategy: the kit reuses the canonical `.v2-analytics-claude` token
   authority. Components that render INSIDE an analytics module inherit those
   tokens from the ancestor; the drawer, which attaches to <body>, carries
   `class="exec-ui v2-analytics-claude"` so it inherits the SAME set (incl. the
   dark-mode variant) for free. `.exec-ui` adds ONLY the genuinely-new tokens
   (z-index scale, motion, focus ring). Literal fallbacks keep every component
   robust if rendered standalone. Pills use the globally dark-safe semantic
   tokens (--ok/--info/--warn/--danger) — the same ones the legacy dashboards
   already prove dark-safe.
   ============================================================================ */

/* ── Net-new kit tokens (motion · elevation order · focus) ────────────────── */
.exec-ui {
  --exec-z-overlay:  10000;
  --exec-z-drawer:   10001;
  --exec-dur:        .2s;
  --exec-dur-fast:   .14s;
  /* Phase 8.1 removed --exec-ease here — zero var() consumers anywhere;
     --exec-dur/--exec-dur-fast are real and kept. */
  --exec-focus-ring: 0 0 0 3px var(--accent-line, color-mix(in srgb, var(--accent) 32%, transparent));
}
.exec-table-wrap * { box-sizing: border-box; }

/* ── ExecutiveHeader ──────────────────────────────────────────────────────── */
.exec-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 14px; }
.exec-head__l { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.exec-head__title { display: flex; align-items: center; gap: 10px; margin: 0; font-family: var(--font-display, system-ui); font-weight: 600; font-size: var(--type-display-lg, clamp(22px,2.6vw,28px)); letter-spacing: -0.02em; color: var(--text, #1a1917); }
.exec-head__ico { display: inline-flex; color: var(--accent-fg, #a8292f); }
.exec-head__sub { margin: 0; font-size: var(--type-body, 14px); color: var(--text-dim, var(--muted, #5b5953)); max-width: 60ch; }
.exec-head__meta { font-family: var(--font-mono, ui-monospace); font-size: var(--type-caption, 11px); color: var(--text-faint, var(--faint, #94918b)); white-space: nowrap; }

/* ── ExecutiveToolbar / FilterBar / Search / Reset ────────────────────────── */
.exec-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 8px 0 14px; }
.exec-toolbar__l, .exec-toolbar__r { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.exec-filterbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.exec-filterbar__scope { display: flex; gap: 8px; flex-wrap: wrap; }

.exec-search { display: inline-flex; align-items: center; gap: 7px; padding: 0 10px; background: var(--input-bg, var(--surface-2, #fbfaf8)); border: 1px solid var(--input-border, var(--border, #e8e6e2)); border-radius: var(--radius-sm, 11px); transition: border-color var(--exec-dur-fast, .14s), box-shadow var(--exec-dur-fast, .14s); }
.exec-search__ico { color: var(--text-faint, var(--faint, #94918b)); display: inline-flex; }
.exec-search__input { border: 0; background: transparent; padding: 8px 2px; font: inherit; font-size: var(--type-body, 14px); color: var(--text, #1a1917); min-width: 9rem; outline: none; }
.exec-search:focus-within { border-color: var(--accent, #a8292f); box-shadow: var(--exec-focus-ring); }

/* ── Mobile: 16px font prevents iOS Safari zoom on focus ──── */
@media (max-width: 600px) {
  .exec-search__input { font-size: 16px; }
}

.exec-reset { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; font: inherit; font-size: var(--type-body, 14px); font-weight: 600; color: var(--text-dim, var(--muted, #5b5953)); background: transparent; border: 1px solid var(--border, #e8e6e2); border-radius: var(--radius-sm, 11px); padding: 7px 12px; transition: background var(--exec-dur-fast, .14s), border-color var(--exec-dur-fast, .14s); }
.exec-reset:hover { background: var(--hover, #f2f0ec); border-color: var(--border-strong, #dad7d2); }
.exec-reset:focus-visible { outline: none; box-shadow: var(--exec-focus-ring); }

/* ── ExecutiveBadge ───────────────────────────────────────────────────────── */
.exec-badge { display: inline-block; font-family: var(--font-mono, ui-monospace); font-size: 10.5px; letter-spacing: 0.04em; font-weight: 600; padding: 3px 8px; border-radius: 7px; border: 1px solid var(--accent-line, color-mix(in srgb, var(--accent) 22%, transparent)); color: var(--accent-fg, #a8292f); background: var(--accent-weak, color-mix(in srgb, var(--accent) 8%, transparent)); }
.exec-badge--ok     { color: var(--ok, #2f7d62);    border-color: var(--ok, #2f7d62);    background: var(--ok-bg, #e7f2ec); }
.exec-badge--info   { color: var(--info, #3b5ba9);  border-color: var(--info, #3b5ba9);  background: var(--info-bg, #edf1fb); }
.exec-badge--warn   { color: var(--warn, #946420);  border-color: var(--warn, #946420);  background: var(--warn-bg, #f6eedb); }
.exec-badge--danger { color: var(--danger, #a8292f);border-color: var(--danger, #a8292f);background: var(--danger-bg, #faebeb); }

/* ── ExecutiveStatusPill (the ONE pill) ───────────────────────────────────── */
.exec-pill { display: inline-block; font-size: 11px; font-weight: 700; line-height: 1.4; border-radius: 999px; padding: 2px 9px; border: 1px solid var(--border, #e8e6e2); color: var(--muted, #5b5953); background: var(--surface-2, #fbfaf8); white-space: nowrap; }
.exec-pill--ok     { color: var(--ok, #2f7d62);     background: var(--ok-bg, #e7f2ec);    border-color: var(--ok, #2f7d62); }
.exec-pill--info   { color: var(--info, #3b5ba9);   background: var(--info-bg, #edf1fb);  border-color: var(--info, #3b5ba9); }
.exec-pill--warn   { color: var(--warn, #946420);   background: var(--warn-bg, #f6eedb);  border-color: var(--warn, #946420); }
.exec-pill--danger { color: var(--danger, #a8292f); background: var(--danger-bg, #faebeb);border-color: var(--danger, #a8292f); }

/* ── ExecutiveMetric / ExecutiveCard ──────────────────────────────────────── */
.exec-metric { display: flex; flex-direction: column; gap: 5px; padding: 14px 16px; background: var(--surface-2, #fbfaf8); border: 1px solid var(--border, #e8e6e2); border-radius: var(--radius-sm, 11px); min-width: 0; }
.exec-metric__l { font-size: var(--type-label, 11px); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--text-faint, var(--faint, #94918b)); }
.exec-metric__v { font-family: var(--font-display, system-ui); font-size: var(--type-heading-lg, 18px); font-weight: 700; color: var(--text, #1a1917); letter-spacing: -0.01em; }
.exec-metric__v[data-tone="ok"]     { color: var(--c-green, var(--ok, #2f7d62)); }
.exec-metric__v[data-tone="warn"]   { color: var(--c-amber, var(--warn, #946420)); }
.exec-metric__v[data-tone="danger"] { color: var(--crit, var(--danger, #a8292f)); }
.exec-metric__c { font-size: var(--type-caption, 11px); color: var(--text-dim, var(--muted, #5b5953)); }
.exec-card { background: var(--surface, #fff); border: 1px solid var(--border, #e8e6e2); border-radius: var(--radius, 16px); padding: var(--pad, 22px); box-shadow: var(--shadow-sm, 0 1px 2px rgba(28,26,23,.06)); }
.exec-card--flush { padding: 0; }

/* ── ExecutiveSparkline ───────────────────────────────────────────────────── */
.exec-spark { display: block; color: var(--info, #3b5ba9); }
.exec-spark--ok     { color: var(--ok, #2f7d62); }
.exec-spark--warn   { color: var(--warn, #946420); }
.exec-spark--danger { color: var(--danger, #a8292f); }
.exec-spark--empty  { height: 36px; border-radius: 6px; background: var(--surface-2, #fbfaf8); }
.exec-kpi-spark { margin-top: 8px; }

/* ── ExecutiveTable (the ONE table) ───────────────────────────────────────── */
.exec-table-cap { font-size: var(--type-label, 11px); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--text-faint, var(--faint, #94918b)); margin-bottom: 8px; }
.exec-table-empty { padding: 24px 12px; text-align: center; font-size: var(--type-body, 14px); color: var(--text-dim, var(--muted, #5b5953)); background: var(--surface-2, #fbfaf8); border: 1px solid var(--border-faint, var(--border, #e8e6e2)); border-radius: var(--radius-sm, 11px); }
.exec-table-wrap { width: 100%; min-width: 0; max-width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--border-faint, var(--border, #e8e6e2)); border-radius: var(--radius-sm, 11px); }
.exec-table { width: 100%; border-collapse: collapse; font-size: 13px; color: var(--text, #1a1917); min-width: 0; }
.exec-table--dense { font-size: 12px; }
.exec-th, .exec-td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border-faint, var(--border, #e8e6e2)); white-space: nowrap; vertical-align: middle; }
.exec-table--dense .exec-th, .exec-table--dense .exec-td { padding: 7px 10px; }
.exec-th { font-size: var(--type-label, 11px); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--text-faint, var(--faint, #94918b)); background: var(--surface-2, #fbfaf8); }
.exec-table--sticky thead th { position: sticky; top: 0; z-index: 1; }
.exec-th--r, .exec-td--r { text-align: right; font-variant-numeric: tabular-nums; }
.exec-th--c, .exec-td--c { text-align: center; }
.exec-td--name { font-weight: 700; color: var(--text, #1a1917); white-space: normal; max-width: 16rem; }
.exec-th-btn { display: inline-flex; align-items: center; gap: 4px; font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit; font-weight: inherit; background: transparent; border: 0; padding: 0; cursor: pointer; }
.exec-th--r .exec-th-btn { flex-direction: row-reverse; }
.exec-th-ico { opacity: .4; display: inline-flex; transition: opacity var(--exec-dur-fast, .14s); }
.exec-th--sort:hover .exec-th-ico { opacity: .8; }
.exec-th[aria-sort="ascending"] .exec-th-ico, .exec-th[aria-sort="descending"] .exec-th-ico { opacity: 1; color: var(--accent-fg, #a8292f); }
.exec-th-btn:focus-visible { outline: none; box-shadow: var(--exec-focus-ring); border-radius: 5px; }
.exec-tbody .exec-tr:last-child .exec-td, .exec-table tbody tr:last-child .exec-td { border-bottom: 0; }
.exec-tr--click { cursor: pointer; transition: background var(--exec-dur-fast, .14s); }
.exec-tr--click:hover { background: var(--hover, var(--surface-2, #f2f0ec)); }
.exec-tr--click:focus-visible { outline: none; box-shadow: inset var(--exec-focus-ring); }

/* ── The canonical drawer (Design System Program Phase 2) ────────────────────
   Relocated + extended from .exec-drawer (js/analytics/executive-drawer.js ->
   js/components/drawer.js) — one implementation, app-wide, not scoped to any
   one module. Self-contained token block below (not dependent on .exec-ui,
   which stays as-is for the OTHER Executive UI Kit components on this page
   unrelated to the drawer). ── */
.drawer-overlay {
  --drawer-z-overlay: 10000; --drawer-z-panel: 10001;
  --drawer-dur: .2s; --drawer-dur-fast: .14s;
  --drawer-ease: cubic-bezier(.4, 0, .2, 1);
  --drawer-focus-ring: 0 0 0 3px var(--accent-line, color-mix(in srgb, var(--accent) 32%, transparent));
  position: fixed; inset: 0; z-index: var(--drawer-z-overlay); display: flex; justify-content: flex-end;
  background: rgba(16, 17, 26, 0); transition: background var(--drawer-dur) var(--drawer-ease);
}
.drawer-overlay * { box-sizing: border-box; }
.drawer-overlay.is-open { background: rgba(16, 17, 26, .42); }
/* V1 fix (intermittent "whole screen frozen / unclickable" on mobile): this
   overlay is position:fixed;inset:0;z-index:10000. Only while it is
   .is-open may it intercept pointer events — a closing/closed/stale one
   (its JS removal is deferred ~260ms and can be delayed further by a
   backgrounded tab, or left behind by a rapid open→close→open race) must
   NEVER become an invisible full-viewport click-blocker over the rest of
   the app, the sidebar included. */
.drawer-overlay:not(.is-open) { pointer-events: none; }
.drawer-overlay:not(.is-open) .drawer { pointer-events: none; }
.drawer { position: relative; z-index: var(--drawer-z-panel); display: flex; flex-direction: column; width: min(440px, 92vw); max-width: 92vw; height: 100%; background: var(--surface, #fff); border-left: 1px solid var(--border, #e8e6e2); box-shadow: var(--shadow-lg, 0 18px 50px -12px rgba(28,26,23,.28)); transform: translateX(100%); transition: transform var(--drawer-dur) var(--drawer-ease); }
.drawer-overlay.is-open .drawer { transform: translateX(0); }
/* Desktop: no grabber (mobile-only drag affordance). */
.drawer__grabber { display: none; }
.drawer__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--border-faint, var(--border, #e8e6e2)); flex: 0 0 auto; }
.drawer__titles { min-width: 0; }
.drawer__title { display: flex; align-items: center; gap: 9px; font-family: var(--font-display, system-ui); font-weight: 600; font-size: var(--type-heading-xl, 19px); letter-spacing: -0.018em; color: var(--text, #1a1917); }
.drawer__ico { color: var(--accent-fg, #a8292f); display: inline-flex; }
.drawer__sub { margin-top: 3px; font-size: var(--type-caption, 12px); color: var(--text-dim, var(--muted, #5b5953)); }
.drawer__close { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 9px; border: 1px solid var(--border, #e8e6e2); background: var(--surface, #fff); color: var(--text-dim, var(--muted, #5b5953)); cursor: pointer; transition: background var(--drawer-dur-fast); }
.drawer__close:hover { background: var(--hover, #f2f0ec); }
.drawer__close:focus-visible { outline: none; box-shadow: var(--drawer-focus-ring); }
.drawer__error { margin: 14px 20px 0; padding: 10px 13px; border-radius: var(--radius-sm, 11px); background: var(--danger-bg, #faebeb); color: var(--danger, #a8292f); font-size: 13px; font-weight: 600; flex: 0 0 auto; }
.drawer__body { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 18px 20px; display: flex; flex-direction: column; gap: 20px; }
/* V1 HOTFIX — the body is a column flex container purely for `gap`, but that
   let a tall child with `overflow` other than `visible` (e.g. Assignment
   Detail's `.accord-section`, which is `overflow:hidden` for its border
   radius → flex auto-min-size 0) be SHRUNK by the flex algorithm instead of
   overflowing, so it silently clipped its own content and no scrollbar
   appeared. Children must keep their natural block size so the body's own
   `overflow-y:auto` stays the single vertical scroll owner. Harmless for
   every other consumer (their body children have visible overflow and
   already refuse to shrink below content). */
.drawer__body > * { flex-shrink: 0; }
.drawer__foot { flex: 0 0 auto; display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; padding: 14px 20px; padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--border-faint, var(--border, #e8e6e2)); }
.drawer button:disabled, .drawer input:disabled, .drawer select:disabled, .drawer textarea:disabled { opacity: .55; cursor: not-allowed; }
.drawer-btn { font: inherit; font-size: var(--type-body, 14px); font-weight: 700; cursor: pointer; padding: 9px 15px; border-radius: var(--radius-sm, 11px); border: 1px solid var(--border, #e8e6e2); background: var(--surface, #fff); color: var(--text, #1a1917); transition: background var(--drawer-dur-fast), filter var(--drawer-dur-fast); }
.drawer-btn:hover { background: var(--hover, #f2f0ec); }
.drawer-btn--primary { background: var(--accent, #a8292f); border-color: var(--accent, #a8292f); color: var(--on-accent, #fff); }
.drawer-btn--primary:hover { filter: brightness(1.06); background: var(--accent, #a8292f); }
.drawer-btn--danger { color: var(--danger, #a8292f); border-color: var(--danger, #a8292f); background: var(--danger-bg, #faebeb); }
.drawer-btn:focus-visible { outline: none; box-shadow: var(--drawer-focus-ring); }

/* Drawer slots: section · metric grid · timeline */
.drawer-sec { display: flex; flex-direction: column; gap: 9px; }
.drawer-sec__h { font-size: var(--type-label, 11px); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--text-faint, var(--faint, #94918b)); }
.drawer-metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); gap: 10px; }
.drawer-metric { display: flex; flex-direction: column; gap: 3px; padding: 11px 13px; background: var(--surface-2, #fbfaf8); border: 1px solid var(--border-faint, var(--border, #e8e6e2)); border-radius: var(--radius-sm, 11px); }
.drawer-metric__l { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; color: var(--text-faint, var(--faint, #94918b)); }
.drawer-metric__v { font-family: var(--font-display, system-ui); font-size: var(--type-heading-lg, 18px); font-weight: 700; color: var(--text, #1a1917); }
.drawer-metric__v[data-tone="ok"]     { color: var(--c-green, var(--ok, #2f7d62)); }
.drawer-metric__v[data-tone="warn"]   { color: var(--c-amber, var(--warn, #946420)); }
.drawer-metric__v[data-tone="danger"] { color: var(--crit, var(--danger, #a8292f)); }
.drawer-metric__s { font-size: 11px; color: var(--text-dim, var(--muted, #5b5953)); }
.drawer-tl { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.drawer-tl__li { display: flex; gap: 11px; padding-bottom: 14px; }
.drawer-tl__li:last-child { padding-bottom: 0; }
.drawer-tl__rail { flex: 0 0 12px; display: flex; flex-direction: column; align-items: center; }
.drawer-tl__dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 3px; flex: 0 0 auto; }
.drawer-tl__li:not(:last-child) .drawer-tl__rail::after { content: ""; flex: 1 1 auto; width: 2px; background: var(--border, #e8e6e2); margin-top: 3px; }
.drawer-tl__dot--ok { background: var(--ok, #2f7d62); }
.drawer-tl__dot--info { background: var(--info, #3b5ba9); }
.drawer-tl__dot--warn { background: var(--warn, #946420); }
.drawer-tl__dot--danger { background: var(--danger, #a8292f); }
.drawer-tl__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.drawer-tl__top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.drawer-tl__when { font-size: 11px; color: var(--text-faint, var(--faint, #94918b)); font-variant-numeric: tabular-nums; }
.drawer-tl__title { font-size: 13px; font-weight: 700; color: var(--text, #1a1917); }
.drawer-tl__d { font-size: 12px; color: var(--text-dim, var(--muted, #5b5953)); }

/* NEW — loading skeleton (shares the Home workspace's .skel-line/.skel-block visual language) */
.drawer-skeleton { display: flex; flex-direction: column; gap: 12px; }

/* NEW — source-row highlight while its drawer is open */
.drawer-source-highlight { background: var(--accent-weak, color-mix(in srgb, var(--accent) 8%, transparent)) !important; transition: background var(--drawer-dur, .2s) ease; }

/* Drawer — mobile bottom-sheet (≤640px) */
@media (max-width: 640px) {
  /* V1 post-QA hotfix (Issue D): pin the overlay to the *visible* (dynamic)
     viewport, not the large viewport a `position:fixed; inset:0` element
     resolves against while the mobile URL/nav bar is showing. Without this
     the sheet's `align-items:flex-end` anchor is the large-viewport bottom,
     which sits behind the browser chrome — so the bottom of the sheet (its
     last section + action row: reimbursement, WhatsApp copy, Edit/Delete)
     renders off-screen and can't be scrolled into view. `100vh` stays as
     the fallback for engines without `dvh`; this mirrors the 100vh→100dvh
     pattern already used across this file (app shell, login, etc.). */
  .drawer-overlay { justify-content: stretch; align-items: flex-end; height: 100vh; height: 100dvh; }
  /* Height in `dvh` for the same reason: `86vh` measures 86% of the LARGE
     viewport, which can equal or exceed the visible area with the URL bar
     shown, pushing the footer past the screen edge. `min(…, 100%)` keeps the
     original clamp (100% = the now-100dvh overlay). */
  .drawer { width: 100%; max-width: 100%; height: 86vh; height: min(86dvh, 100%); border-left: 0; border-top: 1px solid var(--border, #e8e6e2); border-radius: var(--radius-lg, 22px) var(--radius-lg, 22px) 0 0; transform: translateY(100%); }
  /* Safe-area inset belongs to whichever element is actually last: .drawer__foot
     already carries it (padding: 12872) when a footer exists — applying it here
     too would double-count the inset. Only add it here for the no-footer case,
     where .drawer__body is the last child and has none of its own. */
  .drawer:not(:has(.drawer__foot)) { padding-bottom: env(safe-area-inset-bottom, 0px); }
  .drawer-overlay.is-open .drawer { transform: translateY(0); }
  /* NEW — drag-handle affordance, touch target padded well past the visible bar */
  .drawer__grabber { display: flex; align-items: center; justify-content: center; flex: 0 0 auto; padding: 10px 0 6px; touch-action: none; }
  .drawer__grabber::before { content: ""; width: 36px; height: 4px; border-radius: 999px; background: var(--border-strong, #dad7d2); }
}

/* Motion-reduction respect */
@media (prefers-reduced-motion: reduce) {
  .drawer, .drawer-overlay, .drawer-source-highlight { transition: none; }
}
/* ── end canonical drawer ─────────────────────────────────────────────────── */

/* ── Save-Feedback (Design System Program Phase 3) ────────────────────────────
   js/components/save-feedback.js. Applied to a button while runSaveFeedback()
   is driving it — .sf-btn + [data-sf-state] both cleared on return to idle,
   so an idle button carries neither class nor attribute (no lingering
   layout impact once a save finishes). ── */
.sf-btn[data-sf-state] { display: inline-flex; align-items: center; justify-content: center; gap: 7px; }
.sf-btn[data-sf-state] .sf-btn__label { line-height: 1; }
.sf-icon { display: inline-flex; flex: 0 0 auto; }
.sf-btn[data-sf-state="saving"] .sf-icon--spin { animation: sf-spin .8s linear infinite; transform-origin: 50% 50%; }
[data-sf-state="success"] .sf-icon--check { color: var(--ok, #2f7d62); }
.sf-icon--check { stroke-dasharray: 20; stroke-dashoffset: 20; animation: sf-check-draw .24s ease forwards; }
@keyframes sf-spin { to { transform: rotate(360deg); } }
@keyframes sf-check-draw { to { stroke-dashoffset: 0; } }

/* Reduced motion — both the OS preference and this app's own data-anim="off"
   override switch (same dual-guard convention already used elsewhere, e.g.
   the domain-shell tab-strip disclosure) freeze rotation/draw-in; the state
   itself (icon shape, label, aria-busy) still communicates fully without it. */
@media (prefers-reduced-motion: reduce) {
  .sf-icon--spin, .sf-icon--check { animation: none; }
  .sf-icon--check { stroke-dashoffset: 0; }
}
[data-anim="off"] .sf-icon--spin, [data-anim="off"] .sf-icon--check { animation: none; }
[data-anim="off"] .sf-icon--check { stroke-dashoffset: 0; }

/* One-shot success pulse on the record a save landed on (a timeline block,
   list card, or expense row — resolved by the caller's pulseTarget()).
   Additive: only a background-color animation, never layout-affecting. */
/* A box-shadow ring, not background-color — stays correct regardless of
   the target's OWN background (a solid-color badge pill, a transparent
   table row, a card — all pulse the same way without fighting the
   element's resting appearance). */
@keyframes sf-pulse {
  0%   { box-shadow: 0 0 0 0 var(--accent-line, color-mix(in srgb, var(--accent) 40%, transparent)); }
  100% { box-shadow: 0 0 0 7px rgba(168,41,47,0); }
}
/* Motion polish (v1.31.22.0) — two beats instead of one: a single 0.55s ring
   was over before the eye found a record that had just been scrolled into
   view (save-feedback.js reveals it first). Still a ring only — no layout. */
.sf-pulse { animation: sf-pulse .6s ease-out 2; }
@media (prefers-reduced-motion: reduce) { .sf-pulse { animation: none; } }
[data-anim="off"] .sf-pulse { animation: none; }

/* Inline, contextual error — same danger-token shape as the existing
   .conflict-warning, deliberately without its shake (this state is meant to
   read as calm and actionable, not alarming). */
.sf-inline-error {
  background: var(--danger-bg, #faebeb); border: 1.5px solid var(--danger, #a8292f); color: var(--danger, #a8292f);
  border-radius: var(--radius-sm, 11px); padding: 10px 14px; font-size: 13px; font-weight: 500; margin-top: 8px;
}
.sf-inline-error[hidden] { display: none; }

/* Motion polish (v1.31.22.0) — inline and per-field errors settle in rather
   than snapping into place. Plays only on the hidden→shown edge (a CSS
   animation restarts when display goes none→block), never when the message
   text of an already-visible error changes, so re-validation doesn't flicker.
   No shake, same "calm and actionable" rule as above. */
@keyframes sf-msg-in {
  from { opacity: 0; transform: translateY(-3px); }
  to   { opacity: 1; transform: none; }
}
.sf-inline-error:not([hidden]),
.sf-field-error:not([hidden]) { animation: sf-msg-in var(--motion-base, 200ms) var(--ease-decelerate, ease-out); }
@media (prefers-reduced-motion: reduce) {
  .sf-inline-error:not([hidden]), .sf-field-error:not([hidden]) { animation: none; }
}
[data-anim="off"] .sf-inline-error:not([hidden]),
[data-anim="off"] .sf-field-error:not([hidden]) { animation: none; }

/* Motion polish (v1.31.22.0) — shared press feedback. Before this, only
   .btn-primary (and the FAB / Executive .wsp-btn, each with its own rule)
   answered a press; the ~15 other action-button families had hover but no
   press state at all. One rule for all of them instead of per-family patches.
   Uses the independent `scale` property, not `transform`, so it composes with
   any transform a family already sets (e.g. .eng-btn.-primary:hover's
   translateY) instead of replacing it. Instant on press, like a native
   touch-down highlight; nothing is added to the families' own transition
   lists. Disabled / aria-disabled / busy (save-feedback) buttons excluded.
   Deliberately NOT here: navigation (rail, tabs, bottom nav — they have
   their own active indicators), cards/rows, and the families listed above
   that already own a press state. Gated positively on motion being allowed
   (OS preference + the app's own data-anim switch) rather than undone
   afterwards, so no override has to win a specificity contest. */
@media (prefers-reduced-motion: no-preference) {
  :root:not([data-anim="off"]) :is(.btn-secondary, .btn-danger, .btn-success, .btn-today, .btn-icon,
      .gud-btn, .gud-icon-btn, .eng-btn, .cal-btn, .p-btn, .odo-btn,
      .pc-add-btn, .notif-action-btn, .form-guard-btn, .rm-action-btn,
      .sim-btn, .docv-btn):active:not(:disabled):not([aria-disabled="true"]):not([aria-busy="true"]) {
    scale: .97;
  }
}

/* Design System Program Phase 5 — per-field inline validation. Same
   danger-token language as .sf-inline-error above (deliberately without
   shake, same binding precedent), scaled down to sit under a single form
   field rather than a whole section. */
.sf-field-error {
  display: flex; align-items: center; gap: 5px; margin-top: 5px;
  color: var(--danger, #a8292f); font-size: 12px; font-weight: 500; line-height: 1.4;
}
.sf-field-error[hidden] { display: none; }

/* V1 (Overnight) — informational cue under Jam Selesai when the assignment
   window crosses midnight (end earlier than start). Advisory only: neutral
   tone, not an error. */
.overnight-cue {
  display: flex; align-items: center; gap: 5px; margin-top: 5px;
  color: var(--accent-fg, #2f6bd8); font-size: 12px; font-weight: 600; line-height: 1.4;
}
.overnight-cue::before {
  content: "🌙"; font-size: 11px; line-height: 1;
}
.overnight-cue[hidden] { display: none; }
/* Reinforces the field-error text with a border on the input(s) it
   describes — color is never the ONLY indicator (text + icon already
   carry the meaning), this is additive emphasis. */
.form-group input[aria-invalid="true"],
.form-group select[aria-invalid="true"] {
  border-color: var(--danger, #a8292f);
}

/* ============================================================
   v1.18.3 HOTFIX — Executive Navigation Refinement
   Single V2 renderer reused for desktop shell + mobile drawer.
   ============================================================ */

/* Desktop panel: lighter hierarchy, more breathing room, premium grouping. */
.v2-panel-header {
  padding: 24px 18px 12px;
}

.v2-panel-title {
  font-size: 14px;
  font-weight: 650;
  letter-spacing: 0;
}

.v2-panel-subtitle {
  margin-top: 2px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--faint);
}

.v2-panel-nav {
  gap: 10px;
  padding: 10px 10px 0;
}

.v2-panel-section {
  margin: 4px 6px 2px;
  padding: 0;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--faint);
  text-transform: uppercase;
}

.v2-panel-nav-item {
  min-height: 42px;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 12px;
  font-size: 13.5px;
  font-weight: 520;
  color: var(--text-dim);
  transition: background-color 160ms ease, color 160ms ease, transform 160ms ease;
}

.v2-panel-nav-item:hover,
.v2-panel-nav-item:focus-visible {
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  color: var(--text);
  transform: translateX(2px);
}

.v2-panel-nav-item--active {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent-fg);
  font-weight: 600;
}

.v2-panel-nav-item--active::before {
  left: 7px;
  width: 4px;
  height: 18px;
  border-radius: 999px;
  opacity: 0.95;
}

.v2-panel-nav-icon {
  width: 18px;
  height: 18px;
  opacity: 0.66;
  flex-shrink: 0;
}

.v2-panel-nav-item--active .v2-panel-nav-icon {
  opacity: 1;
}

/* Rail polish: calmer modules, consistent icon sizing and spacing. */
.v2-rail-modules {
  gap: 8px;
}

.v2-rail-item {
  width: 46px;
  height: 46px;
  border-radius: 12px;
  transition: background-color 160ms ease, color 160ms ease, transform 160ms ease;
}

.v2-rail-item:hover,
.v2-rail-item:focus-visible {
  transform: translateX(2px);
}

.v2-rail-icon {
  width: 18px;
  height: 18px;
}

.v2-rail-item--active {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}

.v2-rail-item--active::before {
  width: 4px;
  height: 20px;
  border-radius: 999px;
}

/* User card refinement (macOS-like sidebar footer tone). */
.v2-panel-footer-user {
  margin: 6px 8px 10px;
  padding-top: 8px;
}

.v2-footer-avatar {
  min-height: 52px;
  border-radius: 14px;
  border: 1px solid color-mix(in srgb, var(--border) 85%, transparent);
  background: color-mix(in srgb, var(--surface-2) 72%, transparent);
  transition: background-color 160ms ease, border-color 160ms ease;
}

.v2-footer-avatar:hover {
  background: color-mix(in srgb, var(--accent) 8%, var(--surface-2));
}

.v2-footer-avatar-circle {
  width: 30px;
  height: 30px;
  font-size: 11px;
}

.v2-footer-display-name {
  font-size: 12px;
  font-weight: 600;
}

.v2-footer-role-label {
  font-size: 10px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.v2-footer-version-label {
  font-size: 9px;
  letter-spacing: 0.05em;
}

.v2-panel-footer-logout-direct {
  display: none;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  width: 100%;
  min-height: 40px;
  padding: 10px 12px;
  border: 1px solid color-mix(in srgb, var(--danger) 32%, transparent);
  border-radius: 12px;
  background: color-mix(in srgb, var(--danger) 7%, transparent);
  color: var(--danger);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 160ms ease;
}

.v2-panel-footer-logout-direct:hover,
.v2-panel-footer-logout-direct:focus-visible {
  background: color-mix(in srgb, var(--danger) 11%, transparent);
}

/* ════════════════════════════════════════════════════════════════════════
   MOBILE NAVIGATION DRAWER — v1.18.3.2
   Single renderer: the LIVE #v2Rail + #v2Panel are physically relocated into
   #sidebar > .sidebar-nav > #v2MobileNavHost by syncV2ResponsiveNavReuse().
   No duplicate menu, no second renderer. These rules are presentation-only
   and apply solely while the JS-added .v2-*--mobile-drawer classes are
   present (≤767px). Desktop is hidden via display:none on #sidebar (≥768px).

   Drawer layout (Apple HIG inspired):
     ┌ Header (brand)
     │ Active module menu (expanded panel — sections + items)
     │ Bottom Modules (the rail, minus the active module)
     │ Logout
     └ Footer (app name + version — legacy .sidebar-footer)
   ════════════════════════════════════════════════════════════════════════ */
@media (max-width: 767px) {
  /* ── Drawer container ── */
  body.v2-shell-active #sidebar {
    width: 84vw;
    max-width: 360px;
    min-width: 0;
    transform: translateX(-100%);
    background: var(--surface);
    color: var(--text);
    /* v1.31.4 R2 — pointer-events is intentionally not transitioned: the
       drawer must stop intercepting taps the instant .sidebar-open is
       removed, not only after its slide-out transition (inherited from
       style.css's base .sidebar rule) finishes. Otherwise a tap that
       lands right after a navigation-triggered close can hit the
       still-visually-present panel instead of the real content beneath. */
    pointer-events: none;
  }
  body.v2-shell-active #sidebar.sidebar-open {
    transform: translateX(0);
    box-shadow: 16px 0 44px rgba(0, 0, 0, 0.28);
    pointer-events: auto;
  }
  body.v2-shell-active #sidebar .sidebar-header {
    flex: 0 0 auto;
    padding: 18px 18px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
  }
  body.v2-shell-active #sidebar .sidebar-brand-title,
  body.v2-shell-active #sidebar .sidebar-close {
    color: var(--text);
    font-size: 13px;
    line-height: 1.35;
    letter-spacing: 0;
  }
  body.v2-shell-active #sidebar .sidebar-nav {
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    padding: 0;
  }
  body.v2-shell-active #sidebar .sidebar-footer {
    flex: 0 0 auto;
    display: block;
    padding: 12px 18px calc(14px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--border);
    background: var(--surface);
  }
  /* V1 mobile UX update: the app content behind the drawer must stay sharp —
     dim only, no blur. (backdrop-filter: blur(6px) previously blurred the
     whole app behind the overlay; removed, background dim kept as-is.) */
  body.v2-shell-active .sidebar-overlay {
    background: rgba(0, 0, 0, 0.4);
  }

  #v2MobileNavHost {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    padding: 0;
    background: var(--surface);
    color: var(--text);
  }

  /* ── Active module menu (panel) ── */
  .v2-panel--mobile-drawer {
    position: static;
    left: auto;
    top: auto;
    width: 100%;
    height: auto;
    min-height: 0;
    flex: 1 1 auto;
    display: flex !important;
    flex-direction: column;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    background: var(--surface);
    color: var(--text);
    border-right: 0;
    z-index: auto;
  }

  /* Desktop-only panel chrome is suppressed in the drawer. */
  .v2-panel--mobile-drawer .v2-panel-header,
  .v2-panel--mobile-drawer .v2-panel-divider,
  .v2-panel--mobile-drawer .v2-panel-cta,
  .v2-panel--mobile-drawer .v2-panel-spacer,
  .v2-panel--mobile-drawer .v2-panel-footer-user,
  .v2-panel--mobile-drawer .v2-panel-nav--admin {
    display: none !important;
  }

  /* Show ONLY the active module's nav — driven by the inline display that
     setRailModule() already toggles (single source of active state). */
  .v2-panel--mobile-drawer .v2-panel-nav {
    display: none !important;
    flex-direction: column;
    gap: 2px;
    padding: 12px 12px 4px;
  }
  .v2-panel--mobile-drawer .v2-panel-nav:not([style*="display: none"]):not([style*="display:none"]) {
    display: flex !important;
  }

  .v2-panel--mobile-drawer .v2-panel-nav-item {
    min-height: 52px;
    margin: 0;
    padding: 0 14px;
    border-radius: 14px;
    gap: 14px;
    align-items: center;
    font-size: 15px;
    font-weight: 500;
    color: var(--text);
    background: transparent;
    box-shadow: none;
    transition: background-color 140ms ease, color 140ms ease;
  }
  .v2-panel--mobile-drawer .v2-panel-nav-item:hover,
  .v2-panel--mobile-drawer .v2-panel-nav-item:focus-visible {
    background: var(--hover);
    color: var(--text);
  }
  .v2-panel--mobile-drawer .v2-panel-nav-item--active {
    color: var(--accent-fg);
    background: var(--accent-soft);
    box-shadow: none;
  }
  .v2-panel--mobile-drawer .v2-panel-nav-item--active::before {
    left: 6px;
    width: 3px;
    height: 18px;
    border-radius: 999px;
    background: var(--accent);
  }
  .v2-panel--mobile-drawer .v2-panel-nav-icon {
    width: 20px;
    height: 20px;
    opacity: 0.85;
  }

  .v2-panel--mobile-drawer .v2-panel-section {
    margin: 14px 8px 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .v2-panel--mobile-drawer .v2-panel-nav > .v2-panel-section:first-child {
    margin-top: 2px;
  }

  /* ── Bottom Modules (the relocated rail) ── */
  .v2-rail--mobile-drawer {
    position: static;
    left: auto;
    top: auto;
    width: 100%;
    height: auto;
    flex: 0 0 auto;
    display: flex !important;
    flex-direction: column;
    background: var(--surface);
    border-right: 0;
    border-top: 1px solid var(--border);
    margin-top: 8px;
    padding: 12px 12px 0;
    overflow: visible;
  }
  /* Hidden when the current role has no other module to switch to. */
  .v2-rail--mobile-empty {
    display: none !important;
  }
  .v2-rail--mobile-drawer .v2-rail-crest,
  .v2-rail--mobile-drawer .v2-rail-footer,
  .v2-rail--mobile-drawer .v2-rail-tooltip,
  .v2-rail--mobile-drawer .v2-rail-item--active {
    display: none !important;
  }
  .v2-rail--mobile-drawer .v2-rail-modules {
    width: 100%;
    align-items: stretch;
    gap: 2px;
  }
  .v2-rail--mobile-drawer .v2-rail-modules::before {
    content: 'Bottom Modules';
    display: block;
    margin: 0 8px 6px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
  }
  .v2-rail--mobile-drawer .v2-rail-item {
    width: 100%;
    height: 52px;
    justify-content: flex-start;
    align-items: center;
    margin: 0;
    padding: 0 14px;
    border-radius: 14px;
    color: var(--text);
    background: transparent;
    box-shadow: none;
    transition: background-color 140ms ease, color 140ms ease;
  }
  .v2-rail--mobile-drawer .v2-rail-item:hover,
  .v2-rail--mobile-drawer .v2-rail-item:focus-visible {
    background: var(--hover);
    color: var(--text);
  }
  .v2-rail--mobile-drawer .v2-rail-item::after {
    content: attr(aria-label);
    margin-left: 14px;
    font-size: 15px;
    font-weight: 500;
    letter-spacing: 0;
  }
  .v2-rail--mobile-drawer .v2-rail-icon {
    width: 20px;
    height: 20px;
    opacity: 0.85;
  }

  /* ── Logout (relocated into the host, after Bottom Modules) ── */
  #v2MobileNavHost .v2-panel-footer-logout-direct {
    display: inline-flex !important;
    flex: 0 0 auto;
    margin: 12px 12px 14px;
    min-height: 50px;
    padding: 0 14px;
    border-radius: 14px;
  }
}

/* v1.18.3 Mobile Navigation Refinement: final cascade layer.
   Mobile reuses the live desktop V2 renderer; these rules only tune layout. */
.v2-panel-nav-item,
.v2-rail-item {
  border-radius: 8px;
  transition: background-color 140ms ease, color 140ms ease, border-color 140ms ease;
}

.v2-panel-nav-item:hover,
.v2-panel-nav-item:focus-visible,
.v2-rail-item:hover,
.v2-rail-item:focus-visible {
  transform: none;
}

.v2-panel-nav-item--active::before,
.v2-rail-item--active::before {
  width: 3px;
  border-radius: 999px;
}


body.v2-shell-active .block-meta-row {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  line-height: 1;
}

body.v2-shell-active .block-meta-separator {
  flex: 0 0 auto;
  font-size: 8.5px;
  font-weight: 700;
  line-height: 1;
  color: rgba(255, 255, 255, 0.72);
}

body.v2-shell-active .block-status-badge {
  display: inline-flex;
  align-items: center;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  margin-top: 0;
  line-height: 1.2;
  white-space: nowrap;
}

/* Mobile KPI density and shortened label treatment. */
@media (max-width: 767px) {
  #v2KpiStrip {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    padding: 10px 12px 0;
  }

  .v2-kpi-card {
    min-height: 98px;
    padding: 10px 10px;
    border-radius: 12px;
    align-items: flex-start;
    gap: 3px;
  }

  .v2-kpi-icon {
    width: 20px;
    height: 20px;
    margin-bottom: 2px;
  }

  .v2-kpi-label {
    font-size: 10px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--faint);
  }

  .v2-kpi-label::after {
    content: attr(data-short);
  }

  .v2-kpi-label {
    font-size: 0;
  }

  .v2-kpi-label::after {
    font-size: 10px;
  }

  .v2-kpi-value {
    font-size: 22px;
    line-height: 1.05;
  }

  .v2-kpi-sub {
    display: none;
  }
}

@media (max-width: 560px) {
  #v2KpiStrip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Vehicle edit modal presentation (grouped sections, Apple-like form rhythm). */
#modalVehicleForm .modal-box {
  max-width: 920px;
  border-radius: 18px;
}

#modalVehicleForm .modal-header {
  padding: 18px 24px 12px;
}

#modalVehicleForm .modal-title {
  font-size: 20px;
  letter-spacing: -0.01em;
}

#modalVehicleForm .modal-body {
  padding: 10px 24px 24px;
}

#modalVehicleForm .form-section-label {
  margin-top: 32px;
  margin-bottom: 12px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--faint);
}

#modalVehicleForm .form-grid {
  gap: 12px 14px;
}

#modalVehicleForm .form-group label {
  font-size: 13px;
  font-weight: 600;
}

#modalVehicleForm .form-group input,
#modalVehicleForm .form-group select {
  min-height: 48px;
  border-radius: 14px;
}

#modalVehicleForm .form-actions {
  margin-top: 32px;
  gap: 10px;
}


body.v2-shell-active .v2-panel-nav::before {
  content: none;
  display: none;
}

body.v2-shell-active .assignment-block {
  top: 3px;
  height: calc(var(--row-height) - 6px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  overflow: hidden;
}

body.v2-shell-active .block-time,
body.v2-shell-active .block-purpose,
body.v2-shell-active .block-meta-row {
  flex: 0 0 auto;
  min-height: 0;
}

body.v2-shell-active .block-purpose {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

body.v2-shell-active .block-meta-row {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 4px;
  overflow: hidden;
  white-space: nowrap;
}

body.v2-shell-active .block-status-badge {
  flex: 0 1 auto;
  white-space: nowrap;
}

/* ============================================================
   v1.30.9.6 — Individual Permission Assignment, Phase 3A (User
   Management UX). Lives inside #modalUserForm (js/admin.js). Visually
   mirrors Role Management's .rm-permission-row/.rm-category language
   (same tokens, same row shape) without literally reusing those classes
   or mounting role-management-center.js — a distinct "ipm-" prefix keeps
   the two features decoupled even though they look related.
   ============================================================ */
.ipm-panel { margin-top: 4px; }
.ipm-header {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.ipm-title {
  font-size: 0.82rem;
  font-weight: 700;
  color: var(--text);
}
.ipm-count {
  font-size: 0.72rem;
  color: var(--text-muted);
}
.ipm-add-btn { margin-left: auto; }

.ipm-status {
  font-size: 0.78rem;
  color: var(--text-muted);
  padding: 10px 0;
}
.ipm-status--error { color: var(--danger); }

.ipm-readonly-notice {
  font-size: 0.76rem;
  color: var(--text-muted);
  background: var(--surface-2, var(--hover));
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 8px 10px;
  margin-bottom: 8px;
}

.ipm-empty {
  font-size: 0.78rem;
  color: var(--text-muted);
  padding: 8px 0;
}

/* v1.30.9.7 — Phase 3B, B3: Base Role + Individual = Effective, computed
   locally per edited user (never via role-summary-model.js). */
.ipm-effective-line {
  font-size: 0.74rem;
  color: var(--text-muted);
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
}

.ipm-grant-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ipm-grant-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  background: var(--surface-2, var(--surface));
  border: 1px solid var(--border);
  border-radius: 8px;
}
.ipm-grant-row__check {
  color: var(--accent-fg);
  font-weight: 700;
  flex: none;
}
.ipm-grant-row__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}
.ipm-grant-row__title { font-size: 0.8rem; font-weight: 600; }
.ipm-grant-row__badge {
  align-self: flex-start;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--border-faint, var(--hover));
  color: var(--text-dim, var(--text-muted));
}
.ipm-revoke-btn { flex: none; }

.ipm-picker {
  margin-top: 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 10px;
  background: var(--surface);
}
.ipm-picker-search-row {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
}
.ipm-picker-search {
  flex: 1;
  min-width: 0;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: 8px;
  padding: 0.45rem 0.7rem;
  font-size: 0.8rem;
  color: var(--text);
  font-family: inherit;
  outline: none;
}
.ipm-picker-search:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent); }

.ipm-picker-body {
  max-height: 320px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.ipm-picker-empty {
  font-size: 0.78rem;
  color: var(--text-muted);
  padding: 8px 0;
}
.ipm-picker-group { padding: 4px 0; }
.ipm-picker-group__title {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text);
  margin: 0 0 4px;
}
.ipm-picker-category { padding: 2px 0 2px 10px; }
.ipm-picker-category__title {
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin: 0 0 4px;
}
.ipm-picker-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 5px 4px;
  cursor: pointer;
}
.ipm-picker-row--disabled { cursor: default; opacity: 0.72; }
.ipm-picker-row input[type="checkbox"] {
  margin-top: 3px;
  accent-color: var(--accent);
  width: 16px;
  height: 16px;
  flex: none;
}
.ipm-picker-row__text {
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.ipm-picker-row__title { font-size: 0.8rem; font-weight: 600; }
.ipm-picker-row__desc { font-size: 0.72rem; color: var(--text-muted); }
.ipm-picker-row__note { font-size: 0.7rem; color: var(--text-muted); font-style: italic; }

@media (max-width: 640px) {
  .ipm-header { align-items: flex-start; }
  .ipm-add-btn { margin-left: 0; }
  .ipm-grant-row { flex-wrap: wrap; }
  .ipm-picker-body { max-height: 260px; }
}

/* ============================================================
   v1.30.9.13 — Phase 1 App Shell Rebuild (Claude Design mockup)

   Restyles the EXISTING .v2-rail/.v2-panel/.v2-topbar/.bottom-nav shell
   (initV2Rail()/initV2Panel() in js/app.js, live since v1.14.0) to match
   the actual Claude Design App Shell mockup. Deliberately positioned as
   the LAST cascade layer in this file so it wins over every earlier
   .v2-* rule above without needing !important — same technique this file
   already uses (see the v1.16.4.7 / v1.18.3 HOTFIX blocks earlier).

   No mechanism change: setRailModule(), canAccessModule(), the mobile
   DOM-relocation in syncV2ResponsiveNavReuse(), and bottom-nav-registry.js
   are all untouched. This block is presentation-only, plus the CSS half
   of the net-new tablet tap-to-reveal panel overlay (JS half lives next
   to setRailModule() in js/app.js).
   ============================================================ */

/* ── Rail: theme-aware surface (was permanently-dark var(--side-bg)) ── */
.v2-rail {
  background: var(--surface);
  border-right: 1px solid var(--border);
}

.v2-rail-crest:hover,
.v2-rail-crest:focus-visible {
  background: var(--hover);
}

.v2-rail-item {
  width: 40px;
  height: 40px;
  border-radius: 11px;
  color: var(--muted);
}
.v2-rail-item:hover,
.v2-rail-item:focus-visible {
  background: var(--hover);
  color: var(--text);
}

/* Active state = accent-subtle fill + accent icon (mockup) — replaces the
   translucent-dark-tint + left-edge-bar language the rail used while it
   was permanently dark. */
.v2-rail-item--active,
.v2-rail-item--active:hover,
.v2-rail-item--active:focus-visible {
  color: var(--accent-fg);
  background: var(--accent-subtle);
}
.v2-rail-item--active::before {
  display: none;
}

.v2-rail-tooltip,
.v2-rail-avatar-initials {
  font-family: var(--font-sans);
}

/* Pin the last visible module (Konfigurasi/gear) to the rail's bottom —
   matches the mockup's rail composition without reordering the DOM. */
#v2RailKonfigurasi {
  margin-top: auto;
}

.v2-rail-footer-btn {
  color: var(--muted);
}
.v2-rail-footer-btn:hover,
.v2-rail-footer-btn:focus-visible {
  background: var(--hover);
  color: var(--text);
}

/* ── Panel: surface-2 (recessed vs. rail/content surface), accent-subtle
   active pill instead of the translucent left-bar treatment ── */
.v2-panel {
  background: var(--surface-2);
  border-right: 1px solid var(--border);
}

.v2-panel-title {
  font-family: var(--font-sans);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--faint);
}

.v2-panel-subtitle,
.v2-panel-section,
.v2-panel-btn,
.v2-panel-footer-btn {
  font-family: var(--font-sans);
}

.v2-panel-nav-item {
  border-radius: 10px;
  padding: 10px 12px;
  font-family: var(--font-sans);
  font-weight: 500;
  color: var(--text);
}
.v2-panel-nav-item:hover,
.v2-panel-nav-item:focus-visible {
  background: var(--hover);
  color: var(--text);
}

.v2-panel-nav-item--active {
  background: var(--accent-subtle);
  color: var(--accent-fg);
  font-weight: 700;
}
.v2-panel-nav-item--active::before {
  display: none;
}

.v2-panel-nav-icon {
  opacity: 0.7;
}
.v2-panel-nav-item--active .v2-panel-nav-icon {
  opacity: 1;
}

/* ── Tablet 768–1024px: section panel becomes a tap-to-reveal overlay ──
   Net-new — the panel used to be permanently visible from 768px up with
   no tablet-specific state at all. Desktop (≥1024px) is explicitly
   cancelled below so nothing here affects the persistent desktop panel. */
@media (min-width: 768px) and (max-width: 1023px) {
  .v2-panel {
    transform: translateX(-100%);
    transition: transform var(--motion-sheet, 260ms) cubic-bezier(.16,1,.3,1);
    box-shadow: var(--shadow-lg);
  }
  .v2-panel--tablet-open {
    transform: translateX(0);
  }
  .v2-panel-tablet-backdrop {
    position: fixed;
    inset: 0;
    left: 64px;
    background: var(--scrim);
    z-index: 114; /* below the rail (120) and panel (115 owns its own layer) */
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--motion-sheet, 260ms) ease;
  }
  .v2-panel--tablet-open + .v2-panel-tablet-backdrop,
  .v2-panel--tablet-open ~ .v2-panel-tablet-backdrop {
    opacity: 1;
    pointer-events: auto;
  }
}
@media (min-width: 1024px) {
  .v2-panel-tablet-backdrop {
    display: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .v2-panel {
    transition: none;
  }
}

/* ── Topbar: token/font alignment inside the existing flex-order skeleton —
   no structural change to any of the media queries already tuning it ── */
.v2-topbar {
  background: var(--surface);
}
#v2TopbarCrumb,
.v2-topbar-title {
  font-family: var(--font-display);
}
.v2-topbar-label {
  font-family: var(--font-sans);
  color: var(--accent-fg);
}
.v2-topbar-search,
.v2-topbar-search-input,
.v2-topbar-user-role {
  font-family: var(--font-sans);
}

/* ── Mobile drawer: align to the same rebuilt token language. The relocation
   mechanism (syncV2ResponsiveNavReuse) and the drawer's own composition
   (panel-expanded → rail-as-"Bottom Modules" → Logout) are unchanged. ── */
.v2-panel--mobile-drawer .v2-panel-nav-item--active {
  background: var(--accent-subtle);
}
.v2-rail--mobile-drawer .v2-rail-modules::before,
.v2-panel--mobile-drawer .v2-panel-section {
  font-family: var(--font-sans);
}

/* ============================================================
   DOMAIN SHELL (domainShellV1) — Redesign Phase 1
   ============================================================
   Additive alternative to .v2-rail/.v2-panel — mutually exclusive with
   them at the JS level (see initDomainShellV1() in app.js), so no rule
   here needs to fight the old shell's CSS for space. body.domain-shell-
   active is always paired with body.v2-shell-active (domainShellV1
   requires visualShellV2), so the existing v2-shell-active rules that
   hide #sidebar/.header and show .v2-topbar keep working unmodified;
   this block only needs to override .main-area's margin (the old rule
   reserves space for rail+panel combined — this shell has no separate
   panel) and add the rail + tab-bar chrome itself. Cascade-final by
   position in the file, same convention as the blocks above it.
   Z-index budget: matches the old rail (120); tab bar sits in normal
   flow, well under any modal/drawer (200+).
   ============================================================ */

.domshell-rail {
  position: fixed;
  top: 0;
  left: 0;
  width: 72px;
  height: 100vh;
  height: 100dvh;
  background: var(--surface);
  border-right: 1px solid var(--border);
  z-index: 120;
  display: none;
  flex-direction: column;
  padding: 8px;
  box-sizing: border-box;
  overflow: hidden;
  transition: width var(--motion-normal, 200ms) var(--motion-ease, ease);
  box-shadow: none;
  /* SS14.4 — js/app.js's setWorkspace() wraps every module navigation in
     document.startViewTransition(), same whole-viewport root snapshot Phase
     7G.5 uses for the theme toggle (see the ::view-transition-old/new(root)
     rule below). That's correct for a theme swap (the whole page legitimately
     changes together) but wrong here: without its own name the rail has none
     — getComputedStyle(...).viewTransitionName reads "none" — so it gets
     baked into the SAME flat old/new screenshot pair as the completely
     different outgoing/incoming CONTENT, and the ~300ms root cross-fade
     visibly ghosts straight across the persistent sidebar even though the
     rail's own DOM/CSS state never actually changes underneath (confirmed:
     width stays 220px, :hover stays true, no childList mutation — see
     scripts/ss14-sidebar-navigation-close-check.mjs). Naming it pulls it
     into its own ::view-transition-group(domshell-rail), separate from
     root — the suppression of THAT group's own cross-fade is just below. */
  view-transition-name: domshell-rail;
}
/* Brand header — logo + short name + real app version (never the
   prototype's internal "v2 · redesign" build label). */
.domshell-rail-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 3px 18px;
  flex-shrink: 0;
  overflow: hidden;
}
.domshell-rail-logo {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  background: #fff;
  border: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  padding: 4px;
  overflow: hidden;
}
/* Phase 7G.2 — the logo asset itself (assets/Logo-PBSI.png) is already a
   transparent-background crest; this hardcoded #fff was the actual white
   rectangle reported in dark mode. Same fix .brand-mark already got
   (search this file for "[data-theme=\"dark\"] .brand-mark") — a real
   dark surface, not the logo asset. Solid background-color, so it
   inherits the site's existing html.theme-anim background-color
   crossfade automatically; no extra transition rule needed here. This
   node is the SAME live element on mobile too (see the "Mobile: rail +
   tab bar relocated into the legacy sidebar drawer" comment further
   down) — one fix covers both surfaces. */
[data-theme="dark"] .domshell-rail-logo { background: var(--surface); }
.domshell-rail-logo img { width: 100%; height: 100%; object-fit: contain; }
.domshell-rail-brandtext { min-width: 0; white-space: nowrap; opacity: 0; transition: opacity var(--motion-fast, 120ms) linear; }
.domshell-rail:hover .domshell-rail-brandtext,
.domshell-rail:focus-within .domshell-rail-brandtext { opacity: 1; transition: opacity var(--motion-normal, 200ms) linear 100ms; }
.domshell-rail-brandname { font-size: 13px; font-weight: 600; letter-spacing: -0.01em; color: var(--text); font-family: var(--font-display); }
.domshell-rail-brandver { font-size: 10px; color: var(--muted); font-family: var(--font-mono); }
.domshell-rail-list {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow: hidden;
}
/* Product reimagining — deck structure (presentation lives in design-system.css
   §3). Each domain is an ENTRY (rail button + its screen list); the screen
   list is hidden unless the deck is expanded/in the phone drawer AND the entry
   is active, so the collapsed 72px rail is unchanged. Entries never shrink —
   an overflowing rail scrolls instead of squeezing buttons over each other. */
.domshell-deck-entry { display: flex; flex-direction: column; flex-shrink: 0; }
.domshell-deck-screens { display: none; }
.domshell-deck-sep { flex-shrink: 0; }
.domshell-deck-toggle { display: none; }
/* Footer — real signed-in user, same identity data as the old rail's
   footer/topbar avatar (updatePermissionUI() in app.js). */
.domshell-rail-footer {
  border-top: 1px solid var(--border);
  flex-shrink: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 8px 3px 4px;
}
.domshell-rail-avatar {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  flex-shrink: 0;
}
.domshell-rail-usertext { min-width: 0; white-space: nowrap; opacity: 0; transition: opacity var(--motion-fast, 120ms) linear; }
.domshell-rail:hover .domshell-rail-usertext,
.domshell-rail:focus-within .domshell-rail-usertext { opacity: 1; transition: opacity var(--motion-normal, 200ms) linear 100ms; }
.domshell-rail-username { font-size: 12px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; }
.domshell-rail-userrole { font-size: 10px; color: var(--muted); text-transform: capitalize; overflow: hidden; text-overflow: ellipsis; }
@media (min-width: 768px) {
  .domshell-rail { display: flex; }
  /* Motion redesign (v1.31.23.0) — audit F-06: the rail now expands OVER the
     content instead of pushing it. Before, `.domshell-rail:hover ~
     .main-area { margin-left: 220px }` reflowed the entire workspace on
     every hover in and out (measured: 26 layouts / 53 ms per hover cycle on
     the Board, ~0.2 cumulative layout shift per session), slid the timeline's
     sticky driver column 148 px whenever the pointer grazed the rail, moved
     the content under the pointer, and was the mechanism behind the SS14.7
     fix (and the 700 ms nav-transition delay that rule needed). The rail is
     position:fixed at z-index 120 (above the 50 of the topbar) and already
     gains --shadow-lg when expanded, so it reads as a flyout; the content
     column is now fixed at 72 px and never moves. */
  body.domain-shell-active .main-area {
    margin-left: 72px;
  }
  body.domain-shell-active #sidebar { display: none !important; }
}
.domshell-rail:hover,
.domshell-rail:focus-within {
  width: 220px;
  box-shadow: var(--shadow-lg);
  /* Phase 8.1 (spec §12) — 100ms dwell before the expand transition even
     starts, so a pointer graze across the 72px boundary never triggers a
     visible width change. Collapse (base rule, line ~14009) is
     deliberately left at 0 delay — leaving should feel immediate. */
  transition: width var(--motion-normal, 200ms) var(--motion-ease, ease) 100ms;
}

/* Phase 9 mobile-first audit — 768-1023px touch tablets have no hover, so
   the rail's collapsed-with-hover-to-reveal-labels pattern above never
   reveals anything: there is no pointer to hover with, and :focus-within
   only fires at the same moment a tap already navigates away. Below 768px
   this is moot (the rail is reparented into the always-expanded mobile
   drawer, see ".domshell-rail--mobile-drawer" further down); this closes
   the same gap for touch devices at/above that width instead of gating it
   on width alone. */
@media (min-width: 768px) and (hover: none) {
  .domshell-rail { width: 220px; }
  .domshell-rail-label,
  .domshell-rail-brandtext,
  .domshell-rail-usertext { opacity: 1; }
  body.domain-shell-active .main-area { margin-left: 220px; }
}

.domshell-rail-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  height: 44px;
  padding: 0 11px;
  border: none;
  border-radius: 11px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-family: var(--font-sans);
  text-align: left;
  flex-shrink: 0;
  transition: background-color var(--motion-fast, 120ms) ease, color var(--motion-fast, 120ms) ease;
}
.domshell-rail-item:hover,
.domshell-rail-item:focus-visible {
  background: var(--hover, rgba(0,0,0,0.04));
  color: var(--text);
}
.domshell-rail-item--active {
  background: var(--accent-subtle);
  color: var(--accent-fg);
}
.domshell-rail-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex-shrink: 0;
}
.domshell-rail-icon svg { stroke: currentColor; }
.domshell-rail-label {
  font-size: 13.5px;
  font-weight: 700;
  white-space: nowrap;
  opacity: 0;
  /* Collapse (un-hover) keeps this quick --motion-fast fade — leaving the
     rail should feel immediate. The reveal is overridden below. */
  transition: opacity var(--motion-fast, 120ms) linear;
}
.domshell-rail:hover .domshell-rail-label,
.domshell-rail:focus-within .domshell-rail-label {
  opacity: 1;
  /* V1 post-QA hotfix (Issue F): was the base --motion-fast/0-delay fade, so
     the label hit full opacity (~120ms) while the 72→220px width expansion
     had not visibly started (it has its own 100ms dwell) — the text read as
     a sudden flash detached from the panel opening. Match the width
     transition's timing exactly (--motion-normal + the same 100ms dwell)
     so the label materialises *with* the rail as one continuous motion.
     prefers-reduced-motion / [data-anim="off"] zero the duration globally
     (style.css), leaving only the 100ms dwell the width expansion already
     carries under those modes — labels stay usable, no new motion system. */
  transition: opacity var(--motion-normal, 200ms) linear 100ms;
}

/* ── Tab bar (domain screen-tabs + nested-module sub-tabs) ── */
.domshell-tabbar {
  display: none;
  flex-direction: column;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 0 24px;
  flex-shrink: 0;
}
.domshell-tabrow {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  padding: 10px 0;
}
.domshell-tabrow--sub {
  border-top: 1px solid var(--border);
  padding: 7px 0;
}
/* Segmented-control pill container — reuses the EXACT established pattern
   from the existing Timeline/Daftar toggle (.v2-tl-view-toggle,
   platform.css ~line 3060: surface-2 fill + border + padding:2px wrapping
   transparent buttons, active = surface + shadow-sm), rather than inventing
   a second tab-pill language for this shell. */
.domshell-tabrow {
  display: flex;
  gap: 2px;
  overflow-x: auto;
  padding: 2px;
  margin: 10px 0;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 9px;
  width: fit-content;
  max-width: 100%;
  animation: v2FadeInScale var(--motion-fast, 160ms) var(--motion-ease, ease);
}
.domshell-tabrow--sub {
  background: transparent;
  border: none;
  border-top: 1px solid var(--border);
  border-radius: 0;
  padding: 7px 0 0;
  margin: 0 0 8px;
}
.domshell-tab {
  flex-shrink: 0;
  border: none;
  background: transparent;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  padding: 7px 13px;
  border-radius: 7px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color var(--motion-fast, 120ms) ease, color var(--motion-fast, 120ms) ease;
}
.domshell-tab:hover,
.domshell-tab:focus-visible {
  color: var(--text);
}
.domshell-tab--active {
  background: var(--surface);
  color: var(--text);
  font-weight: 700;
  box-shadow: var(--shadow-sm);
}
.domshell-tabrow--sub .domshell-tab--active {
  background: var(--accent-subtle);
  color: var(--accent-fg);
  box-shadow: none;
}
.domshell-tab--sub {
  font-size: 12px;
  padding: 6px 12px;
}

/* ── Command Palette trigger + overlay ──
   Click-triggered only in this phase — see command-palette.js's doc
   comment for why no keyboard shortcut is bound yet. */
/* .v2-topbar lays out its children by explicit flex `order` (crumb=20,
   search=default, date-nav=40, spacer=50, theme=55, notif=57, user=60 —
   see the VSM-8/Part-9 rules above); this reuses that same convention so
   the trigger settles right after the crumb+search, before date-nav. */
body.v2-shell-active .v2-topbar .domshell-palette-trigger { order: 35; }
.domshell-palette-trigger {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 9px;
  background: transparent;
  color: var(--muted);
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
}
.domshell-palette-trigger:hover,
.domshell-palette-trigger:focus-visible {
  background: var(--hover, rgba(0,0,0,0.04));
  color: var(--text);
}
.domshell-palette-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 14, 10, 0.42);
  z-index: 150;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 14vh;
}
.domshell-palette-box {
  width: min(560px, 90vw);
  max-height: 60vh;
  background: var(--surface);
  border-radius: 16px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  /* No entrance animation — UX/motion audit remove #1 (command-palette.js). */
}
.domshell-palette-inputwrap {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.domshell-palette-input {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-sans);
  font-size: 15px;
  color: var(--text);
}
.domshell-palette-results {
  overflow-y: auto;
  padding: 8px;
}
.domshell-palette-empty {
  padding: 26px;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
}
.domshell-palette-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  border: none;
  background: transparent;
  padding: 10px 10px;
  border-radius: 10px;
  cursor: pointer;
  text-align: left;
}
.domshell-palette-item:hover,
.domshell-palette-item:focus-visible {
  background: var(--hover, rgba(0,0,0,0.05));
}
.domshell-palette-item-type {
  flex-shrink: 0;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--muted);
}
.domshell-palette-item-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.domshell-palette-item-label {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.domshell-palette-item-sub {
  font-size: 11.5px;
  color: var(--muted);
}
@keyframes v2FadeInScale {
  from { opacity: 0; transform: scale(0.97); }
  to   { opacity: 1; transform: scale(1); }
}

/* ── Mobile: rail + tab bar relocated into the legacy sidebar drawer ──
   Same physical-reparenting technique as the old shell's
   syncV2ResponsiveNavReuse() (see domain-shell.js's syncResponsive()) —
   below 768px these are the SAME live nodes, just moved and restyled from
   a fixed hover-expand rail into a normal vertical list inside the
   existing hamburger drawer. */
.domshell-rail--mobile-drawer {
  position: static;
  display: flex !important;
  width: 100%;
  height: auto;
  border-right: none;
  border-bottom: 1px solid var(--border);
  padding: 6px 4px 10px;
}
.domshell-rail--mobile-drawer:hover,
.domshell-rail--mobile-drawer:focus-within {
  width: 100%;
  box-shadow: none;
}
.domshell-rail--mobile-drawer .domshell-rail-label,
.domshell-rail--mobile-drawer .domshell-rail-brandtext,
.domshell-rail--mobile-drawer .domshell-rail-usertext {
  opacity: 1; /* always-expanded in the drawer — no hover affordance on touch */
}
/* V1 mobile UX update: the drawer already has its own real header (PBSI
   logo + "Bidang Sarana dan Prasarana" + close button — .sidebar-header,
   untouched) directly above this rail. This redundant "Sarpras Ops +
   version" identity block only makes sense on the desktop hover-rail
   (collapsed icon rail, expands identity on hover); scoped hide only for
   the mobile-drawer instance of the same live node — desktop untouched. */
.domshell-rail--mobile-drawer .domshell-rail-brand {
  display: none;
}
.domshell-tabbar--mobile-drawer {
  border-bottom: 1px solid var(--border);
  padding: 0 4px;
}
.domshell-tabbar--mobile-drawer .domshell-tabrow {
  flex-wrap: wrap;
}

@media (prefers-reduced-motion: reduce) {
  .domshell-rail { transition: none; }
  .domshell-rail-label { transition: none; }
  .domshell-rail-item { transition: none; }
  .domshell-rail-brandtext { transition: none; }
  .domshell-rail-usertext { transition: none; }
  body.domain-shell-active .main-area { transition: none; }
  .domshell-tab { transition: none; }
  .domshell-tabrow { animation: none; }
  .domshell-palette-box { animation: none; }
}

