/* ============================================================
   PBSI Driver Scheduler — style.css
   Desain: Clean operational / utilitarian dengan aksen merah PBSI
   Font: DM Sans (body) + DM Mono (timeline/jam)
   ============================================================ */

/* ── CSS Variables ── */
:root {
  /* v1.15.2 — Global mobile bottom safe-area clearance (single source of truth).
     Any scroll container that can sit behind the mobile FAB + bottom nav uses
     this for its padding-bottom, so the last actionable element (export, save,
     generate, restore, pagination, card action…) is always scrollable clear of
     both. Budget: bottom-nav 56px + FAB (50px height + 14px offset) + 16px
     comfort margin + the iOS home-indicator inset. Reusable across every
     workspace — fix is architectural, never per-page. */
  --mobile-safe-bottom: calc(136px + env(safe-area-inset-bottom, 0px));

  --red:        #D32F2F;
  --red-light:  #FFEBEE;
  --red-mid:    #EF5350;
  --dark:       #1A1A1A;
  --dark-2:     #2C2C2C;
  --dark-3:     #3D3D3D;
  --gray-1:     #F5F5F5;
  --gray-2:     #EEEEEE;
  --gray-3:     #BDBDBD;
  --gray-4:     var(--faint, #5E6861); /* audit C1: #757575 failed AA on every non-white surface (3.8-4.3:1) */
  --white:      #FFFFFF;
  --text:       #1A1A1A;
  --text-muted: var(--faint, #5E6861); /* audit C1: was #757575; --faint is >=4.8:1 on every light surface */
  --radius:     10px;
  --radius-sm:  6px;
  --shadow:     0 2px 12px rgba(0,0,0,0.10);
  --shadow-lg:  0 8px 32px rgba(0,0,0,0.18);

  /* Lebar setiap kolom 1 jam di timeline */
  --hour-width: 80px;
  /* Lebar kolom nama driver di kiri timeline */
  --driver-col: 110px;
  /* Tinggi setiap baris driver — increased for better card readability */
  --row-height: 66px;
  /* Tinggi header jam */
  --header-height: 36px;

  /* v1.20.8 — Motion System tokens (Native App Experience). Shared timing
     vocabulary for every animated surface added/retrofitted this sprint:
     drawer, dialogs, cards, bottom sheets, nav, toast, notification.

     Note (Phase 8.1): js/components/motion-tokens.js (MICRO_MS/STANDARD_MS/
     SCENE_MS/EASE_ARRIVE) and js/widgets/executive/motion-profiles.js
     (EASE/RESPONSIVE/MEASURED) are separate, deliberately un-bridged JS
     token layers this phase — not wired to these CSS custom properties.
     Their numbers don't line up 1:1 with the tokens below today (e.g.
     STANDARD_MS=240 vs --motion-base=200, SCENE_MS=550 vs
     --motion-slow=320) — bridging them is a real design decision, not a
     mechanical alias, so it's left for a future phase. */
  --motion-fast: 120ms;
  --motion-base: 200ms;
  --motion-slow: 320ms;
  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --ease-decelerate: cubic-bezier(0, 0, 0, 1);
  /* Phase 8.1 — replaces --ease-accelerate (verified zero consumers
     anywhere in the repo). Consolidates 3 near-identical hand-authored
     "overshoot" springs that differed only in the y1 control point
     (1.26 / 1.56 / 1.3): platform.css's .pbsi-toggle-input thumb, this
     file's .req-mode-card__tick and .req-sheet. Picked the middle
     amplitude (1.3), already in production use at .req-sheet, so that
     call site is a zero-diff swap; the other two shift imperceptibly.
     Deliberately does NOT also absorb cubic-bezier(.2,.8,.3,1) (ls-pop,
     platform.css:868, the login screen's own cold-load reveal) — that
     curve's only use site sits on the frozen login-entry surface. */
  --ease-overshoot: cubic-bezier(0.34, 1.3, 0.64, 1);
  /* Motion redesign (v1.31.23.0) — the curve for LEAVING motion (exits of
     modals, sheets, backdrops, toasts, dialogs). Accelerates: the element
     stays perceptible for the first part of the exit, then leaves. Measured
     reason: with --ease-standard (a DEcelerating curve) a 160 ms fade was
     already at opacity 0.12 by its midpoint — it read as instant. */
  --ease-exit: cubic-bezier(0.4, 0, 1, 1);
}

/* v1.20.8 — respect OS-level reduced-motion preference across every
   animation/transition in the app, not just the tokens above. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Phase 8.1 — app-wide manual reduced-motion override, [data-anim="off"],
   mirroring the OS-level block immediately above. Previously hand-patched
   into ~7 individual rules inside platform.css only; gudang.css,
   engineering.css, petty-cash.css, workspace-list-kit.css,
   sarpras-intelligence.css, nor-center.css and overtime.css had zero
   coverage of the app's own manual toggle before this rule (only the
   OS-level query above reached them). Purely additive: existing
   hand-patched [data-anim="off"] rules elsewhere are untouched — several
   also reset element-specific end state (e.g. stroke-dashoffset) that a
   duration override alone can't replicate — they just become harmless,
   redundant overlap for the duration piece. */
[data-anim="off"] *,
[data-anim="off"] *::before,
[data-anim="off"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}

/* ── Reset & Base ── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  /* NOTE: overflow-x:hidden on html/body blocks touch-pan events from reaching
     child scroll containers (timeline-body) on iOS Safari / Android Chrome.
     Page horizontal overflow is prevented by layout structure instead. */
  /* V1.31.1 "Agenda, Kalender & To-Do" §AB — the drawer/sidebar/bottom-
     sheet jitter root cause: every scroll-lock in this app (.sheet-scroll-
     lock, .sidebar-is-open, and document-viewer.js's own inline lock) works
     by toggling `overflow: hidden`, which removes the native scrollbar and
     lets the whole page reflow into the freed-up width for the split
     second the lock is active. `scrollbar-gutter: stable` reserves that
     gutter space PERMANENTLY, whether or not a scrollbar is actually
     showing — the exact, standards-based fix for "toggle overflow without
     layout shift" (this is its documented purpose). Browsers without
     support (older Safari) simply keep today's behavior — not a
     regression, just not yet improved there. */
  scrollbar-gutter: stable;
}

body {
  font-family: var(--font-sans);
  background: var(--gray-1);
  color: var(--text);
  min-height: 100vh;
  font-size: 14px;
  line-height: 1.5;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-x: none; /* prevents accidental horizontal overscroll bounce */
  overscroll-behavior-y: contain; /* v1.20.8 — kills rubber-band bounce on vertical scroll */
  touch-action: manipulation; /* v1.20.8 — removes double-tap-zoom and the ~300ms tap delay. PINCH-zoom stays enabled on purpose (an accessibility requirement): the viewport meta sets no user-scalable/maximum-scale — v1.20.9 had disabled it there, since reverted. iOS focus-zoom is prevented by ≥16px inputs instead. */
}

/* v1.20.8 — Native App Experience: every real button/role=button loses the
   browser's default tap highlight + text-selection + tap-delay, app-wide.
   Non-button clickable chrome (e.g. .pbsi-setting-row, a <label>) keeps its
   own explicit rule further down since this selector can't reach it. */
button, [role="button"] {
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}

/* ============================================================
   APP LAYOUT — Sidebar + Main Area
   ============================================================ */
.app-layout {
  display: flex;
  min-height: 100vh;
  /* Contain layout — sidebar is fixed, main-area is flex:1 min-width:0,
     so nothing overflows the viewport horizontally. */
  overflow-x: clip; /* clips overflow without creating a scroll container (no touch-event side-effects) */
}

/* ── Sidebar ── */
.sidebar {
  width: 240px;
  min-width: 240px;
  /* V1 Redesign Phase 3: canonical --side-bg instead of --dark. --dark is
     never remapped for [data-theme="dark"] (platform.css:206-221 — it's
     deliberately left as a legacy dual-use token), so the sidebar was
     already always-dark by coincidence, not by design. --side-bg is the
     purpose-built, theme-aware equivalent (#1D1D1B light / #191A1F dark —
     imperceptibly close to --dark's #1A1A1A either way). */
  background: var(--side-bg);
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  left: 0;
  height: 100vh;  /* fallback for engines without dynamic viewport units */
  /* UX audit #7: the VISIBLE viewport. 100vh is the largest viewport (URL bar
     collapsed), so on iOS Safari the drawer's footer sat under the toolbar. */
  height: 100dvh;
  z-index: 150;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
  padding-top: env(safe-area-inset-top, 0px); /* v1.20.8 — clears notch/status bar now that viewport-fit=cover is set */
}
.sidebar::-webkit-scrollbar { display: none; }

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px 12px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  flex-shrink: 0;
  gap: 8px;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
}

.sidebar-logo {
  width: 34px;
  height: 34px;
  object-fit: contain;
  flex-shrink: 0;
}

.sidebar-brand-text { flex: 1; min-width: 0; }

.sidebar-brand-title {
  color: var(--white);
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.35;
  display: block;
}

.sidebar-close {
  display: none;
  background: none;
  border: none;
  color: rgba(255,255,255,0.45);
  font-size: 22px;
  cursor: pointer;
  padding: 2px 6px;
  border-radius: 4px;
  line-height: 1;
  transition: color 0.15s;
  flex-shrink: 0;
}
.sidebar-close:hover { color: var(--white); }

/* ── Sidebar Nav ── */
.sidebar-nav {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 10px 10px 8px;
  gap: 2px;
  overflow-y: auto;
  scrollbar-width: none;
}
.sidebar-nav::-webkit-scrollbar { display: none; }

.sidebar-nav-group {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.sidebar-nav-group + .sidebar-nav-group {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(255,255,255,0.06);
}

.sidebar-nav-group-bottom {
  margin-top: auto !important;
  padding-top: 8px !important;
  border-top: 1px solid rgba(255,255,255,0.08) !important;
}

.sidebar-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  padding: 9px 12px;
  min-height: 48px; /* v1.20.8 — 48dp touch target */
  background: none;
  border: none;
  color: rgba(255,255,255,0.72);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  border-radius: 7px;
  transition: background 0.15s, color 0.15s;
  white-space: nowrap;
  position: relative;
}
.sidebar-nav-item:hover {
  background: rgba(255,255,255,0.09);
  color: var(--white);
}
.sidebar-nav-item:active {
  background: rgba(255,255,255,0.14);
}

.sidebar-nav-primary {
  /* V1 Redesign Phase 3: canonical --accent instead of --red (#D32F2F, a
     brighter Material-style red distinct from the app-wide crimson brand
     accent) — unifies the sidebar's primary CTA with the same brand color
     used everywhere else post-Phase-1b. */
  background: var(--accent);
  color: var(--white);
  font-weight: 600;
  font-size: 13px;
  margin-bottom: 2px;
}
.sidebar-nav-primary:hover {
  background: var(--accent-hover);
  color: var(--white);
}

.sidebar-nav-tertiary {
  color: rgba(255,255,255,0.52);
  font-size: 12px;
}
.sidebar-nav-tertiary:hover {
  color: rgba(255,255,255,0.85);
  background: rgba(255,255,255,0.07);
}

.sidebar-nav-logout {
  color: rgba(239,83,80,0.75);
  font-size: 12px;
}
.sidebar-nav-logout:hover {
  background: rgba(211,47,47,0.14);
  color: #EF5350;
}

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

/* Badges inside sidebar (notification dot, request count) */
.sidebar-badge {
  background: var(--accent);
  color: var(--white);
  font-size: 10px;
  font-weight: 700;
  border-radius: 999px;
  padding: 1px 6px;
  min-width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-left: auto;
}
/* Override single-class rules for notification-dot/request-count-badge in sidebar context */
.sidebar-badge.notification-dot,
.sidebar-badge.request-count-badge { margin-left: auto; }

/* Desktop-only sidebar NAV GROUPS — groups have no JS inline-display, safe to force */
.sidebar-nav-group.sidebar-nav-desktop-only { display: none !important; }
@media (min-width: 768px) {
  .sidebar-nav-group.sidebar-nav-desktop-only { display: flex !important; }
}

/* Hide the sidebar Profil button on mobile (it lives in the bottom nav there) */
@media (max-width: 767px) {
  #btnProfile.sidebar-nav-item { display: none !important; }
}

/* ── Sidebar Footer ── */
.sidebar-footer {
  padding: 12px 14px;
  border-top: 1px solid rgba(255,255,255,0.07);
  flex-shrink: 0;
}

.sidebar-version-block {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sidebar-app-name {
  font-size: 10px;
  color: rgba(255,255,255,0.28);
  line-height: 1.45;
}

.sidebar-version {
  font-size: 11px;
  font-weight: 600;
  color: rgba(255,255,255,0.38);
  font-family: var(--font-mono);
  letter-spacing: 0.2px;
}

/* ── Sidebar Overlay (mobile backdrop) ── */
/* V1 mobile UX update: dim only, no blur — app content behind the drawer
   must stay sharp. */
.sidebar-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.48);
  z-index: 140;
}
.sidebar-overlay.overlay-visible { display: block; }

/* ── Main Area ── */
.main-area {
  flex: 1;
  margin-left: 240px;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-width: 0;
}

/* ── Bottom Nav (mobile only) ── */
.bottom-nav {
  display: none;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: calc(56px + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: var(--surface);
  border-top: 1px solid var(--gray-2);
  z-index: 99;
  box-shadow: 0 -1px 8px rgba(0,0,0,0.07);
}

.bottom-nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  flex: 1;
  /* Phase 11 (Administration) — flex:1 alone still floors each
     item at its content's min-content width (the longest unbreakable
     label, e.g. "Pekerjaan"/"Notifikasi"), which silently overflowed the
     row once a 6th item was added (bottom-nav-notif-check.mjs caught it
     at 375/390/402px — the 5-item rows never hit this ceiling before).
     min-width:0 lets flex-shrink actually win; overflow-wrap covers any
     single word still wider than its shrunk share. */
  min-width: 0;
  height: 56px;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--gray-4);
  font-family: var(--font-sans);
  font-size: 10px;
  font-weight: 500;
  transition: color 0.15s;
  padding: 6px 4px;
  position: relative;
  -webkit-tap-highlight-color: transparent;
}
.bottom-nav-item span {
  /* align-items:center (above) means this label is never stretched to the
     button's width by the flex algorithm — without an explicit max-width
     it stays at its own unwrapped text width forever, which is what kept
     forcing the outer button (and the whole row) wide even after
     min-width:0. Capping it here is what actually lets overflow-wrap bite. */
  max-width: 100%;
  overflow-wrap: break-word;
  text-align: center;
  line-height: 1.15;
}
.bottom-nav-item:hover { color: var(--text); }
.bottom-nav-item:active { color: var(--accent-fg); }
.bottom-nav-primary { color: var(--accent-fg); }
.bottom-nav-primary:hover { color: var(--accent-hover); }

.bottom-nav-badge {
  position: absolute;
  top: 6px;
  left: calc(50% + 2px);
  background: var(--danger);
  color: var(--white);
  font-size: 9px;
  font-weight: 700;
  border-radius: 999px;
  padding: 1px 5px;
  min-width: 15px;
  height: 15px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

/* Small dot indicator (no number) for notification bell in bottom nav */
.bottom-nav-dot {
  position: absolute;
  top: 7px;
  left: calc(50% + 7px);
  width: 8px;
  height: 8px;
  background: var(--danger);
  border-radius: 50%;
  border: 2px solid var(--white);
}

/* ── FAB (Floating Action Button) — mobile primary action ── */
.fab-add {
  position: fixed;
  bottom: calc(56px + env(safe-area-inset-bottom, 0px) + 14px);
  right: 16px;
  height: 54px;
  width: auto;
  min-width: 128px;
  padding: 0 14px 0 10px;
  gap: 7px;
  background: var(--accent);
  color: var(--white);
  border: none;
  border-radius: 27px;
  cursor: pointer;
  box-shadow: 0 10px 26px rgba(20, 22, 28, 0.18), 0 2px 7px color-mix(in srgb, var(--accent) 22%, transparent);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 98;
  transition: transform 0.16s ease, box-shadow 0.16s ease, background 0.16s ease, opacity 0.16s ease;
  -webkit-tap-highlight-color: transparent;
}
.fab-add:hover  { transform: translateY(-1px); box-shadow: 0 12px 28px rgba(20, 22, 28, 0.2), 0 2px 8px color-mix(in srgb, var(--accent) 24%, transparent); }
.fab-add:active { transform: translateY(0); background: var(--accent-hover); opacity: 0.96; }
.fab-icon { flex-shrink: 0; }
.fab-label {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  letter-spacing: 0.1px;
}

@media (min-width: 768px) {
  .fab-add { display: none !important; }
}

/* ── Profile modal — logout button ── */
.btn-profile-logout {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 16px;
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--danger);
  background: var(--danger-bg);
  border: 1.5px solid #EF9A9A;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.btn-profile-logout:hover { background: #FFCDD2; border-color: var(--danger); }

/* ============================================================
   HEADER (redesigned — light background)
   ============================================================ */
.header {
  /* V1 Redesign Phase 3: NOT a live bug fix — platform.css:995-999 already
     redeclares .header's background/border-bottom onto these exact
     canonical tokens and, loading after this file, is what actually wins
     in production. Updated here anyway so style.css's own declared values
     are honest/consistent rather than a stale --white/--gray-2 fallback
     that would only matter if the load order ever changed. */
  background: var(--topbar-bg);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 100;
  box-shadow: 0 1px 4px rgba(0,0,0,0.05);
  flex-shrink: 0;
  padding-top: env(safe-area-inset-top, 0px); /* v1.20.8 — clears notch/status bar now that viewport-fit=cover is set */
}

.header-inner {
  padding: 0 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 56px;
  max-width: 100%;
}

/* Sidebar toggle (hamburger) — mobile only */
.sidebar-toggle {
  display: none;
  background: none;
  border: none;
  color: var(--gray-4);
  cursor: pointer;
  padding: 6px;
  border-radius: 6px;
  transition: color 0.15s, background 0.15s;
  flex-shrink: 0;
  line-height: 0;
}
.sidebar-toggle:hover { color: var(--text); background: var(--gray-1); }

/* Brand (mobile header only — desktop shows in sidebar) */
.header-brand {
  display: none;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.brand-logo {
  width: 32px;
  height: 32px;
  object-fit: contain;
  flex-shrink: 0;
  display: block;
}

.brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.brand-title {
  color: var(--text);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: 0.1px;
}

/* Module title (desktop) */
.header-module-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
  display: none;
}

/* Spacer pushes user area to the right */
.header-spacer { flex: 1; }

/* User area */
.header-user-area {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.header-display-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Header Actions (legacy fallback — no longer used for buttons) */
.header-actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

.role-badge {
  font-size: 11px;
  font-weight: 700;
  /* V1 Redesign Phase 3: NOT a live bug fix — platform.css:1153-1188 already
     fully overrides .role-badge (base + a complete admin/bidang/driver/
     viewer/guest variant set) with these exact canonical tokens, and wins
     in production since it loads after this file. Updated here anyway so
     style.css's own declared values are honest/consistent, not a stale
     --dark/--gray-2/--gray-3 fallback. The [data-role="..."] variants below
     (admin/bidang/viewer/driver) are left as their Phase-11A dark-mode
     overrides — also shadowed by platform.css, same reasoning. */
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  padding: 4px 10px;
  line-height: 1;
  white-space: nowrap;
}

.role-badge[data-role="admin"] {
  background: var(--danger-bg);
  border-color: #EF9A9A;
  color: var(--danger);
}

.role-badge[data-role="bidang"] {
  background: #E3F2FD;
  border-color: #90CAF9;
  color: #1565C0;
}

.role-badge[data-role="viewer"] {
  background: #E8F5E9;
  border-color: #A5D6A7;
  color: #2E7D32;
}

.role-badge[data-role="driver"] {
  background: #FFF3E0;
  border-color: #FFCC80;
  color: #E65100;
}

/* Phase 11A: dark-mode counterparts — these were hardcoded pastel hex with no
   dark variant anywhere, so they stayed as light "islands" inside dark mode.
   Mapped onto the same semantic families platform.css already uses for
   status colors in dark mode (danger/ok/info/warn), so meaning is preserved. */
[data-theme="dark"] .role-badge[data-role="admin"] {
  background: var(--danger-bg, #2E2120);
  border-color: var(--danger, #D6817C);
  color: var(--danger, #D6817C);
}
[data-theme="dark"] .role-badge[data-role="bidang"] {
  background: var(--info-bg, #242B39);
  border-color: var(--info, #8AA0CF);
  color: var(--info, #8AA0CF);
}
[data-theme="dark"] .role-badge[data-role="viewer"] {
  background: var(--ok-bg, #1F2C27);
  border-color: var(--ok, #72AF8F);
  color: var(--ok, #72AF8F);
}
[data-theme="dark"] .role-badge[data-role="driver"] {
  background: var(--warn-bg, #2C2719);
  border-color: var(--warn, #C7A05B);
  color: var(--warn, #C7A05B);
}

/* Legacy header button styles (kept for modals/forms that may still reference them) */
.btn-logout {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--gray-4);
  background: transparent;
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
  white-space: nowrap;
}
.btn-logout:hover {
  color: var(--text);
  border-color: var(--gray-3);
  background: var(--gray-1);
}

.btn-requests {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
  background: var(--gray-1);
  border: 1.5px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 6px;
  transition: background 0.15s, border-color 0.15s;
  white-space: nowrap;
}
.btn-requests:hover {
  background: var(--gray-2);
  border-color: var(--gray-3);
}

.request-count-badge {
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--danger);
  color: var(--white);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  line-height: 1;
}

/* Navigasi Tanggal */
.date-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 2px 6px;
}

.btn-icon {
  background: none;
  border: none;
  color: var(--gray-4);
  font-size: 16px;
  cursor: pointer;
  padding: 4px 6px;
  min-width: 44px; /* v1.20.8 — touch target */
  min-height: 44px;
  border-radius: 4px;
  line-height: 1;
  transition: color 0.15s, background 0.15s;
}
.btn-icon:hover { color: var(--text); background: var(--gray-2); }

.date-input {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text);
  background: transparent;
  border: none;
  outline: none;
  padding: 4px 2px;
  cursor: pointer;
  min-width: 110px;
  color-scheme: light;
}

/* M-04: Force consistent light picker across ALL date inputs (Flatpickr pending v1.4.0) */
input[type="date"] { color-scheme: light; }

.btn-today {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  color: var(--danger);
  background: var(--danger-bg);
  border: 1px solid #EF9A9A;
  border-radius: 4px;
  padding: 4px 9px;
  /* V1 Redesign Phase 6: touch-target fix (DESIGN_SYSTEM_SPEC "Touch
     Targets" finding, named explicitly — .btn-today shrank to ~30px on
     mobile via the ≤600px padding/font-size override below, which doesn't
     touch min-height, so this protects both tiers). */
  min-height: 44px;
  cursor: pointer;
  /* v1.31.4 R8 — only the properties :hover/:disabled/.is-today actually
     change (background, border-color, opacity), not every property. */
  transition: background-color 0.15s, border-color 0.15s, opacity 0.15s;
  white-space: nowrap;
}
.btn-today:hover:not(:disabled) { background: #FFCDD2; border-color: var(--danger); }
.btn-today:disabled, .btn-today.is-today {
  opacity: 0.4;
  cursor: default;
  pointer-events: none;
}

/* Tombol Primer */
.btn-primary {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  background: var(--accent);
  color: var(--white);
  border: none;
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  /* V1 Redesign Phase 6: touch-target fix (DESIGN_SYSTEM_SPEC "Touch
     Targets" finding — .btn-primary/secondary/danger/success had no
     min-height at all, effective ~34px). */
  min-height: 44px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 5px;
  transition: background 0.15s, transform 0.1s;
  white-space: nowrap;
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { transform: scale(0.97); }
.btn-primary:disabled {
  background: var(--gray-3, #d1d5db);
  color: var(--gray-5, #9ca3af);
  cursor: not-allowed;
  transform: none;
  pointer-events: none;
}

.btn-plus { font-size: 18px; line-height: 1; font-weight: 300; }

button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

button:disabled:hover {
  transform: none;
}

/* ============================================================
   MAIN CONTENT
   ============================================================ */
/* v1.20.2 — adaptive analytics search: highlight + ring for the matched
   KPI/section/card that the contextual search scrolls into view. */
.adaptive-search-hit {
  outline: 2px solid var(--red, #d32f2f);
  outline-offset: 3px;
  border-radius: 10px;
  animation: adaptiveSearchPulse 1.4s ease-out 1;
}
@keyframes adaptiveSearchPulse {
  0%   { box-shadow: 0 0 0 0 rgba(211, 47, 47, 0.28); }
  100% { box-shadow: 0 0 0 12px rgba(211, 47, 47, 0); }
}

.main-content {
  /* V1 desktop content-width fix: was a hardcoded 1400px, independent of
     platform.css's --workspace-max-width token (which happened to also be
     1400px) — two numbers via two mechanisms for the same visual cap. Now
     reads the same token so there's one source of truth; also means this
     stays correct if the token is ever retuned again. */
  max-width: var(--workspace-max-width, 1400px);
  margin: 0 auto;
  padding: 16px 12px 40px;
  /* width:100% is required when .main-content is a flex child of .main-area
     (column flex). Without it, margin:0 auto overrides align-self:stretch and
     the element sizes to its intrinsic width — collapsing narrower than the
     viewport and reducing the timeline-body clientWidth, cutting scroll range. */
  width: 100%;
}

/* Label tanggal di atas timeline */
.timeline-date-label {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 10px;
  letter-spacing: 0.2px;
}

/* ============================================================
   TIMELINE SCHEDULER
   ============================================================ */

/* Wrapper seluruh timeline — visual card container.
   overflow: hidden clips any rogue child overflow without creating a
   competing horizontal scroll container (timeline-body owns the scroll). */
.timeline-wrapper {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  border: 1px solid var(--gray-2);
  position: relative;
}

/* ── Header Row (Jam) ── */
.timeline-header-row {
  display: flex;
  border-bottom: 2px solid var(--gray-2);
  background: var(--gray-1);
  position: sticky;
  top: 0;
  z-index: 20;
}

/* Sudut kiri atas (label "Driver") */
.timeline-corner {
  width: var(--driver-col);
  min-width: var(--driver-col);
  height: var(--header-height);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.5px;
  text-transform: uppercase;
  flex-shrink: 0;
  background: var(--gray-1);
  position: sticky;
  left: 0;
  z-index: 25;
  box-shadow: 2px 0 8px rgba(0,0,0,0.07);
}

/* Area jam-jam di header */
.timeline-hours {
  display: flex;
  overflow-x: hidden;
  overflow-y: hidden;
  flex: 1;
  scrollbar-width: none;
  /* Motion polish (v1.31.22.0) — was `smooth`. The header never scrolls on
     its own: js/timeline.js copies #timelineBody.scrollLeft into it on every
     scroll event and every smoothScrollTimelineTo() frame. With `smooth`
     each of those writes started a NEW smooth scroll that the next frame
     retargeted, so the hour labels lagged the grid (measured: 13,760px
     behind 1s after a 650ms auto-focus / "Hari Ini" / save-reveal scroll)
     and a re-render mid-flight left them on the wrong hours. */
  scroll-behavior: auto;
-webkit-overflow-scrolling: touch;
   position: relative;
z-index: 1;
}
.timeline-hours::-webkit-scrollbar { display: none; }

/* Setiap kolom jam */
.hour-cell {
  width: var(--hour-width);
  min-width: var(--hour-width);
  height: var(--header-height);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
  border-right: 1px solid var(--gray-2);
  flex-shrink: 0;
  user-select: none;
}

.hour-cell.hour-shaded {
  background: rgba(0, 0, 0, 0.03);
}

.hour-cell.hour-current {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent-fg);
  font-weight: 700;
}

/* V1 Phase 3 — the continuous multi-day hour ruler. Every day boundary (the
   00:00 cell) carries a stronger divider + a compact date marker so the
   strip stays legible while scrolling across days. */
.hour-cell--daystart {
  border-left: 2px solid var(--gray-3);
  position: relative;
  overflow: visible;
}
.hour-cell--daystart::after {
  content: attr(data-day);
  position: absolute;
  left: 4px;
  top: 1px;
  font-family: var(--font-sans, inherit);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.2px;
  color: var(--accent-fg);
  white-space: nowrap;
  pointer-events: none;
}
.hour-cell--endcap {
  border-left: 2px solid var(--gray-3);
}

/* ── Timeline Body ── */
.timeline-body {
  overflow-x: auto;
  overflow-y: hidden;
  position: relative;
  -webkit-overflow-scrolling: touch;
}

/* Custom scrollbar untuk timeline body */
.timeline-body::-webkit-scrollbar { height: 6px; width: 6px; }
.timeline-body::-webkit-scrollbar-track { background: var(--gray-1); }
.timeline-body::-webkit-scrollbar-thumb { background: var(--gray-3); border-radius: 3px; }

/* Setiap baris driver.
   width:max-content — the row's box must span its full content (sticky
   .driver-label + the multi-day .driver-slots canvas), NOT collapse to the
   #timelineBody flex-container width. `position: sticky` pins .driver-label
   only within its containing block (this row); if the row stayed viewport-
   width the label would detach and scroll away once the canvas is scrolled
   past ~one screen (regression from the continuous multi-day timeline, which
   grew .driver-slots from 24h to up to 21 days). */
.driver-row {
  display: flex;
  width: max-content;
  border-bottom: 1px solid var(--gray-2);
  min-height: var(--row-height);
  position: relative;
}
.driver-row:last-child { border-bottom: none; }

/* Kolom nama driver (sticky kiri) */
.driver-label {
  width: var(--driver-col);
  min-width: var(--driver-col);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: 6px 10px;
  background: var(--surface);
  position: sticky;
  left: 0;
  z-index: 15;
  flex-shrink: 0;
  box-shadow: 2px 0 8px rgba(0,0,0,0.07);
}

.driver-name {
  font-weight: 600;
  font-size: 13px;
  color: var(--text);
}

.driver-phone {
  font-size: 10px;
  color: var(--text-muted);
  font-family: var(--font-mono);
  margin-top: 1px;
}

/* ── "Tanpa Driver" lane ─────────────────────────────────────────────
   The dedicated resource row for assignments with no driver (Self-Drive /
   not yet assigned). Structurally a normal driver lane — same sticky left
   label ↔ right slots, same 1:1 alignment — with only a warm tint so it
   reads as an operational exception, never a board redesign. The V2-shell
   refinement (avatar suppression + specificity to beat platform.css's
   `body.v2-shell-active .driver-label`) lives in platform.css. */
.driver-label--unassigned {
  background: var(--warn-bg, #F6EEDB);
}
.driver-label--unassigned .driver-name {
  color: var(--warn, #946420);
}
.driver-lane-count {
  font-family: var(--font-sans, inherit);
}

/* Area slot jam dalam satu baris driver.
   V1 Phase 3 — continuous multi-day canvas: total width = (--tl-days) days ×
   24h × lebar per jam. --tl-days is set on #timelineBody by js/timeline.js
   (renderDriverRows) and inherits down; defaults to 1 day pre-render. */
.driver-slots {
  position: relative;
  width: calc(var(--tl-days, 1) * 24 * var(--hour-width));
  min-width: calc(var(--tl-days, 1) * 24 * var(--hour-width));
  height: var(--row-height);
  flex-shrink: 0;
}

/* Grid lines: an hourly hairline (layer 2) + a stronger DAY SEAM every 24h
   (layer 1) so the midnight boundary of the continuous strip stays legible. */
.driver-slots::before {
  content: '';
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(
      to right,
      var(--gray-3) 0px,
      var(--gray-3) 1.5px,
      transparent 1.5px,
      transparent calc(24 * var(--hour-width))
    ),
    repeating-linear-gradient(
      to right,
      var(--gray-2) 0px,
      var(--gray-2) 1px,
      transparent 1px,
      transparent var(--hour-width)
    );
  pointer-events: none;
}

/* ── Blok Assignment ── */
.assignment-block {
  position: absolute;
  top: 5px;
  height: calc(var(--row-height) - 10px);
  border-radius: 8px;
  padding: 5px 8px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  overflow: hidden;
  font-size: 11px;
  color: var(--white);
  transition: filter 0.15s, box-shadow 0.15s;
  min-width: 24px;
  user-select: none;
  box-shadow: 0 2px 6px rgba(0,0,0,0.22);
}

.assignment-block:hover {
  filter: brightness(1.08);
  box-shadow: 0 3px 10px rgba(0,0,0,0.25);
  z-index: 3; /* always below .driver-label (z-index: 15) */
}

/* ── V1 Redesign Phase 3 (v1.30.9.15) — supplementary block markers ──
   All three are additive to the existing full-color fill + text badges
   above (never a replacement): the shape dot completes "never color alone"
   for colorblind users, the corner dot is a passive echo of the existing
   accessible text conflict badge, and the convoy mark is a passive,
   read-only visual grouping cue (see isConvoyAssignment() in timeline.js
   for why it's a time+location heuristic, not a new data field). */
.block-vehicle-shape {
  position: absolute;
  top: 5px;
  left: 6px;
  width: 8px;
  height: 8px;
  flex: none;
  background: var(--white);
  opacity: 0.85;
}
.block-conflict-dot {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--warn, #c77c11);
  border: 2px solid var(--surface);
  box-shadow: 0 1px 2px rgba(0,0,0,.25);
}
.block-convoy-mark {
  position: absolute;
  right: 6px;
  bottom: 5px;
  color: var(--white);
  opacity: 0.9;
  display: flex;
  align-items: center;
}
.block-convoy-mark svg { width: 14px; height: 8px; display: block; }

/* Assignment selesai — tampak redup */
.assignment-block.is-completed {
  opacity: 0.5;
  filter: grayscale(35%);
}

.assignment-block.is-completed:hover {
  opacity: 0.65;
  filter: grayscale(20%) brightness(1.05);
}

/* Teks di dalam blok — time on top for quick scanning */
.block-time {
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 500;
  opacity: 0.9;
  white-space: nowrap;
  line-height: 1.2;
}

.block-purpose {
  font-weight: 600;
  font-size: 11.5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.3;
}

.block-meta-row {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  line-height: 1;
}

.block-meta-separator {
  flex: 0 0 auto;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
  color: rgba(255,255,255,0.72);
}

/* Assignment sedang berlangsung — pulsing border */
.assignment-block.is-started {
  outline: 2px solid rgba(255,255,255,0.85);
  outline-offset: -2px;
}

/* V1 Phase 2/3 — the block is clipped by the WINDOW EDGE (not by a day
   boundary: inside the window a cross-midnight block is drawn continuously).
   Flatten the clipped edge so it reads as "runs off the loaded range" and
   add a chevron. Purely visual; the record is still ONE assignment, ONE id. */
.assignment-block.continues-next-day {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  border-right: 2px dashed rgba(255,255,255,0.7);
  padding-right: 14px;
}
.assignment-block.continues-next-day::after {
  content: "»";
  position: absolute;
  right: 2px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  color: var(--white);
  opacity: 0.92;
  pointer-events: none;
}
.assignment-block.continues-prev-day {
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  border-left: 2px dashed rgba(255,255,255,0.7);
  padding-left: 14px;
}
.assignment-block.continues-prev-day::before {
  content: "«";
  position: absolute;
  left: 2px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  color: var(--white);
  opacity: 0.92;
  pointer-events: none;
  z-index: 1;
}
/* A block that crosses midnight WITHIN the loaded window: one continuous
   card, with a faint vertical tick at each day seam it crosses (position fed
   from JS as --midnight-x, px from the block's left edge) so the crossing
   stays legible without splitting the card. */
.assignment-block.spans-midnight::before {
  content: "";
  position: absolute;
  left: var(--midnight-x, 50%);
  top: 3px;
  bottom: 3px;
  width: 2px;
  background: rgba(255,255,255,0.55);
  pointer-events: none;
}

/* Badge SELESAI / JALAN di dalam blok timeline */
.block-status-badge {
  display: inline-flex;
  align-items: center;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.4px;
  background: rgba(0,0,0,0.28);
  border-radius: 3px;
  padding: 1px 5px;
  line-height: 1.25;
  width: auto;
  max-width: 100%;
  margin-top: 0;
  white-space: nowrap;
}
.block-status-badge--started {
  background: rgba(255,255,255,0.25);
}
/* V1 Redesign Phase 4b: passive conflict indicator badge (timeline.js's
   hasConflict) — canonical --warn so it reads distinctly from the neutral
   status badges above, without touching conflict enforcement itself. */
.block-status-badge--conflict {
  background: var(--warn, #946420);
  color: #fff;
}
/* V1 Driver Timeline Resource Lanes — passive "no driver" indicator on the
   card itself, so a missing driver stays visible wherever the card appears
   (any lane, any view). Mirrors the --conflict badge's --warn treatment. */
.block-status-badge--unassigned {
  background: var(--warn, #946420);
  color: #fff;
}
/* A dashed inner ring marks the card as a provisional/exception state without
   affecting layout (outline is not clipped by the block's overflow:hidden). */
.assignment-block.is-unassigned {
  outline: 1.5px dashed rgba(255, 255, 255, 0.85);
  outline-offset: -3px;
}

/* Resize handle di kanan blok */
.resize-handle {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 8px;
  cursor: ew-resize;
  background: rgba(255,255,255,0.25);
  border-radius: 0 6px 6px 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.resize-handle::after {
  content: '⋮';
  font-size: 10px;
  color: rgba(255,255,255,0.7);
  line-height: 1;
}

/* ── Warna per Kendaraan ── */
[data-vehicle="Innova"]  { background: #1565C0; }
[data-vehicle="Luxio"]   { background: #2E7D32; }
[data-vehicle="Polytron"]{ background: #E65100; }
[data-vehicle="Hiace"]   { background: #6A1B9A; }

/* ── Legend Kendaraan ── */
.legend {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 12px;
  padding: 10px 14px;
  background: var(--surface);
  border-radius: var(--radius);
  border: 1px solid var(--gray-2);
}

.legend-title {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-right: 4px;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--text);
  padding: 3px 8px 3px 5px;
  border-radius: 20px;
  border: 1px solid var(--gray-2);
  background: var(--gray-1);
  font-weight: 500;
}

.legend-item::before {
  content: '';
  display: inline-block;
  width: 10px;
  height: 10px;
  /* V1 Redesign Phase 4: renderVehicleLegend() (js/drivers.js) sets this
     custom property per item from the vehicle's own stored color, so any
     vehicle — not just the original 4 below — gets a correct swatch. */
  background: var(--legend-swatch-color, currentColor);
  /* V1 Redesign Phase 3 (v1.30.9.15): shape half of vehicle identity — see
     js/utils/vehicle-identity.js#vehicleShapeParts(). Defaults reproduce the
     pre-Phase-3 plain rounded-square swatch exactly, so any legend item this
     JS hasn't touched yet renders unchanged. */
  border-radius: var(--legend-swatch-radius, 3px);
  clip-path: var(--legend-swatch-clip, none);
  transform: var(--legend-swatch-transform, none);
}
/* Legacy fallback for the 4 originally-seeded vehicle names — same
   specificity as the custom-property rule above, later in source order, so
   it still wins for these four (same resulting color either way); a 5th+
   vehicle has no matching selector here and falls through to the custom
   property instead. Left in place rather than removed since [data-vehicle]
   is also read by other, unaudited consumers (vehicle-prediction-dashboard,
   recommendation-card, explainability panels). */
[data-vehicle="Innova"]::before   { background: #1565C0; }
[data-vehicle="Luxio"]::before    { background: #2E7D32; }
[data-vehicle="Polytron"]::before { background: #E65100; }
[data-vehicle="Hiace"]::before    { background: #6A1B9A; }

/* ============================================================
   MODAL
   ============================================================ */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  backdrop-filter: blur(2px);
  animation: fadeIn var(--motion-fast, 0.15s) var(--ease-standard, ease);
}

/* ── Odometer Modal — Enterprise SaaS Style ──────────────────
   Inspired by Linear / Vercel / Stripe dashboard quality.
   Single-purpose modal: one input, focused UX.
   ─────────────────────────────────────────────────────────── */

.odo-modal {
  background: var(--surface);
  border-radius: 14px;
  width: 100%;
  max-width: 480px;
  box-shadow: 0 24px 64px rgba(0,0,0,0.18), 0 4px 16px rgba(0,0,0,0.06);
  overflow: hidden;
  animation: odoSlideIn 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes odoSlideIn {
  from { opacity: 0; transform: scale(0.97) translateY(8px); }
  to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* Header */
.odo-modal__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 20px 20px 16px;
  border-bottom: 1px solid var(--gray-2, #f0f0f0);
}

.odo-modal__header-text { flex: 1; min-width: 0; }

.odo-modal__title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  margin: 0 0 4px;
  line-height: 1.3;
}

.odo-modal__meta {
  font-size: 12px;
  color: var(--text-muted);
  margin: 0;
  line-height: 1.6;
  white-space: pre-line;
}

.odo-modal__close {
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  color: var(--text-muted);
  padding: 3px 6px;
  border-radius: 6px;
  transition: background 0.12s, color 0.12s;
}
.odo-modal__close:hover { background: var(--bg); color: var(--text); }

/* Body */
.odo-modal__body { padding: 20px; }

.odo-field__label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 8px;
}

.odo-field__input {
  display: block;
  width: 100%;
  font-size: 28px;
  font-weight: 600;
  color: var(--text);
  padding: 10px 14px;
  border: 1.5px solid var(--gray-2, #e4e4e4);
  border-radius: 10px;
  background: var(--surface);
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
  -moz-appearance: textfield;
  letter-spacing: -0.5px;
}
.odo-field__input::-webkit-outer-spin-button,
.odo-field__input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.odo-field__input::placeholder { color: var(--gray-2, #ccc); font-weight: 400; }
.odo-field__input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 8%, transparent);
}

.odo-field__error {
  display: block;
  font-size: 12px;
  color: var(--red, #c00);
  margin-top: 6px;
  min-height: 18px;
  line-height: 1.4;
}
/* v1.30.14.3 — Start Assignment: KM Awal locked to the authoritative vehicle
   odometer, with an explicit "Koreksi odometer" override that requires a
   reason. Prevents the free-typed wrong KM Awal that inflates vehicle mileage. */
.odo-field__inputwrap { position: relative; }
.odo-field__lock {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 15px;
  opacity: 0.55;
  pointer-events: none;
}
.odo-field__input[readonly] {
  background: var(--gray-1, #f5f5f5);
  color: var(--text-muted);
  cursor: not-allowed;
}
.odo-field__correct {
  margin-top: 8px;
  padding: 0;
  border: none;
  background: none;
  color: var(--accent-fg);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: underline;
}
.odo-field__correct[hidden] { display: none; }
.odo-correct { margin-top: 10px; }
.odo-correct[hidden] { display: none; }
.odo-field__reason {
  display: block;
  width: 100%;
  font-size: 13px;
  color: var(--text);
  padding: 8px 10px;
  border: 1.5px solid var(--gray-2, #e4e4e4);
  border-radius: 8px;
  background: var(--surface);
  outline: none;
  resize: vertical;
  font-family: inherit;
}
.odo-field__reason:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 8%, transparent);
}
/* Complete Assignment: explicit acknowledgement for a suspicious derived
   distance (jump beyond operations.odometerWarnJumpKm). */
.odo-sanity {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 14px;
  padding: 10px 12px;
  border: 1px solid var(--warn, #946420);
  border-radius: 8px;
  background: var(--warn-bg, #F6EEDB);
  font-size: 12.5px;
  color: var(--text);
  line-height: 1.45;
  cursor: pointer;
}
.odo-sanity[hidden] { display: none; }
.odo-sanity input { margin-top: 2px; flex-shrink: 0; }

/* Live preview (Complete mode when startOdometer is available) */
.odo-preview {
  margin-top: 16px;
  border: 1px solid var(--gray-2, #ebebeb);
  border-radius: 10px;
  overflow: hidden;
}

.odo-preview__row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 9px 14px;
  font-size: 13px;
}

.odo-preview__row + .odo-preview__row {
  border-top: 1px solid var(--gray-2, #f0f0f0);
}

.odo-preview__row--total {
  background: var(--bg, #fafafa);
}

.odo-preview__key { color: var(--text-muted); }
.odo-preview__val { font-weight: 500; color: var(--text); font-variant-numeric: tabular-nums; }

.odo-preview__row--total .odo-preview__key { font-weight: 600; color: var(--text); }
.odo-preview__row--total .odo-preview__val { font-weight: 700; color: var(--danger); font-size: 14px; }

/* Footer */
.odo-modal__footer {
  display: flex;
  gap: 8px;
  padding: 12px 20px 16px;
  border-top: 1px solid var(--gray-2, #f0f0f0);
}

.odo-btn {
  flex: 1;
  padding: 10px 16px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  font-family: inherit;
  cursor: pointer;
  border: none;
  transition: background 0.12s, opacity 0.12s;
  line-height: 1.4;
}

.odo-btn--secondary {
  background: var(--bg, #f2f2f2);
  color: var(--text);
}
.odo-btn--secondary:hover { background: var(--gray-2, #e6e6e6); }

.odo-btn--primary {
  background: var(--accent);
  color: #fff;
  flex: 2;
}
.odo-btn--primary:hover { opacity: 0.88; }

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.modal-box {
  background: var(--surface);
  border-radius: 14px;
  width: 100%;
  max-width: 560px;
  max-height: 90vh;
  overflow-y: auto;
  box-shadow: var(--shadow-lg);
  animation: slideUp 0.2s ease;
}

/* A small, single-purpose modal (e.g. Renew STNK) — narrower than the
   standard multi-section .modal-box form. */
.modal-box--compact { max-width: 420px; }

/* Vehicle Compliance & Financial History — the Renew STNK modal opens ON TOP
   of an already-open vehicle detail drawer (.drawer-overlay, the canonical
   drawer primitive — js/components/drawer.js, z-index 10000+) and must NOT
   close it, so its overlay needs to sit above the drawer rather than the
   default .modal-overlay z-index (200). */
.modal-overlay--above-drawer { z-index: 10050; }

.modal-detail-box {
  max-width: 480px;
}

.login-modal {
  z-index: 300;
}

.login-box {
  max-width: 360px;
}

.request-list-box {
  max-width: 620px;
}

@keyframes slideUp {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0); }
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px 0;
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 1;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--gray-2);
}

.modal-title {
  font-size: 17px;
  font-weight: 700;
  color: var(--text);
}

.modal-close {
  background: none;
  border: none;
  font-size: 22px;
  cursor: pointer;
  color: var(--gray-4);
  line-height: 1;
  padding: 2px 6px;
  border-radius: 4px;
  transition: color 0.15s;
}
.modal-close:hover { color: var(--text); }

.modal-body { padding: 16px 20px 20px; }

/* ── Form Grid ── */
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

.form-full { grid-column: 1 / -1; }

.form-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.form-group label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
}

.form-group input,
.form-group select,
.form-group textarea {
  font-family: var(--font-sans);
  font-size: 14px;
  color: var(--text);
  background: var(--gray-1);
  border: 1.5px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  outline: none;
  transition: border-color 0.15s, background 0.15s;
  width: 100%;
}

.time-pair-group .time-pair {
  display: flex;
  align-items: center;
  gap: 6px;
}

.time-pair-group .time-pair input {
  width: 100%;
  text-align: center;
  padding: 10px 8px;
  font-family: var(--font-mono);
  letter-spacing: 0.12em;
}

.time-separator {
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-size: 16px;
}

.notification-dot {
  display: inline-flex;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 999px;
  background: var(--danger);
  color: var(--white);
  align-items: center;
  justify-content: center;
  font-size: 11px;
  margin-left: 6px;
}

.users-list,
.notifications-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 12px;
}

.user-card,
.notification-item {
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 14px;
}

.user-card-main,
.notification-meta {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: center;
  margin-bottom: 8px;
}

.user-title {
  font-size: 14px;
  font-weight: 700;
}

.user-sub,
.notification-time,
.notification-target {
  font-size: 12px;
  color: var(--text-muted);
}

/* ── Operational Notification Cards ── */
.notif-card {
  background: var(--surface);
  border: 1px solid var(--gray-2);
  border-left: 4px solid var(--gray-3);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
}

/* Phase 11 (Administration) — audit finding Notifications D2: server-
   outbox cards with a real deep-link target (entityKind+entityId) are now
   clickable; this is the only visual signal distinguishing them from the
   plain, non-navigable cards (logs-derived entries, or a server entry
   with no entity yet). */
.notif-card--clickable { cursor: pointer; transition: background 0.12s, border-color 0.12s; }
.notif-card--clickable:hover { background: var(--hover, var(--surface-2, var(--gray-1))); border-color: var(--accent); }
.notif-card--clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.notif-priority-high   { border-left-color: var(--danger); }
.notif-priority-medium { border-left-color: #F57C00; }
.notif-priority-low    { border-left-color: var(--gray-3); }

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

.notif-card-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}

.notif-card-time {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  flex-shrink: 0;
}

.notif-card-desc {
  font-size: 13px;
  color: var(--text-muted);
}

/* v1.20.8 — per-item Mark Read / Archive actions */
.notif-card-actions {
  display: flex;
  gap: 8px;
  margin-top: 8px;
}
.notif-action-btn {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 5px 10px;
  min-height: 28px;
  cursor: pointer;
  transition: background var(--motion-fast, 120ms) var(--ease-standard, ease), color var(--motion-fast, 120ms) var(--ease-standard, ease);
}
.notif-action-btn:hover { background: var(--gray-2); color: var(--text); }

.notif-archive-toggle {
  display: block;
  width: 100%;
  text-align: center;
  font-size: 12px;
  font-weight: 600;
  color: var(--accent-fg);
  background: none;
  border: none;
  padding: 10px 0 4px;
  cursor: pointer;
}
.notif-archive-toggle:hover { text-decoration: underline; }

/* v1.20.8 — shared skeleton-loading utilities (Objective 9). Generalizes the
   Analytics module's existing (previously static, non-animated)
   .v2-analytics-kpi-skel block into a reusable pulse, matching the visual
   rhythm of the Home workspace's own pre-existing .wsp-skeleton pulse
   (js/workspace/workspace-styles.js) so both read as one system even though
   they remain two separately-tested implementations. Respects the global
   prefers-reduced-motion guard added in Phase 2 automatically. */
@keyframes skeleton-pulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }

.skel-line, .skel-block, .skel-avatar {
  background: var(--gray-2);
  border-radius: var(--radius-sm);
  animation: skeleton-pulse 1.3s ease-in-out infinite;
}
.skel-line   { height: 12px; width: 100%; }
.skel-block  { height: 60px; width: 100%; }
.skel-avatar { width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; }

.skel-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px;
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  background: var(--gray-1);
}

/* ── Notification section headers (Unread / Riwayat) ── */
.notif-section-header {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  padding: 4px 0 6px;
}

.notif-section-header--gap {
  margin-top: 12px;
}

/* ── Unread card state ── */
.notif-unread {
  background: #FFF8F0;
}

/* New-dot badge inline in title */
.notif-new-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--danger);
  margin-left: 6px;
  vertical-align: middle;
  flex-shrink: 0;
}

/* ── Notification detail rows ── */
.notif-card-details {
  margin-top: 8px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.notif-card-row {
  display: flex;
  gap: 8px;
  font-size: 12px;
  align-items: baseline;
}

.notif-card-label {
  color: var(--text-muted);
  min-width: 68px;
  flex-shrink: 0;
}

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

/* ── Activity Log ── */
.activity-log-subtitle {
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 12px;
}

.activity-log-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.activity-log-item {
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 10px 12px;
}

.activity-log-top {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 2px;
}

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

.activity-log-time {
  font-size: 11px;
  color: var(--text-muted);
  white-space: nowrap;
  flex-shrink: 0;
}

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

.user-status {
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 700;
}

.status-active { background: #E8F5E9; color: #2E7D32; border: 1px solid #A5D6A7; }
.status-inactive { background: #FFEBEE; color: #C62828; border: 1px solid #EF9A9A; }

.user-card-meta {
  display: grid;
  gap: 6px;
  font-size: 12px;
  color: var(--text-muted);
}

.user-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

/* ── User Management: Statistics Header ── */
.users-stats {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 14px;
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: 8px;
  margin-top: 12px;
  margin-bottom: 14px;
}

.users-stats-total {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}

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

.users-stats-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.users-stats-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--gray-2);
  background: var(--surface);
  font-size: 11px;
}

.users-stats-chip-label {
  color: var(--text-muted);
}

.users-stats-chip-count {
  font-weight: 700;
  color: var(--text);
}

/* ── User Management: Role Groups ── */
.users-list {
  gap: 10px;
}

.user-role-group {
  display: flex;
  flex-direction: column;
}

.user-role-header {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--gray-2);
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--text);
  box-shadow: 0 1px 3px rgba(0,0,0,0.05);
  transition: background 0.12s, box-shadow 0.12s;
}

.user-role-header:hover {
  background: var(--gray-1);
  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
}

.user-role-arrow {
  font-size: 10px;
  flex-shrink: 0;
  color: var(--text-muted);
}

.user-role-count-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 18px;
  padding: 0 6px;
  background: var(--gray-2);
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--gray-4);
  letter-spacing: 0;
}

.user-role-body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-top: 8px;
  padding-bottom: 2px;
}

.user-role-empty {
  padding: 14px;
  font-size: 12px;
  color: var(--text-muted);
  text-align: center;
  background: var(--gray-1);
  border: 1px dashed var(--gray-3);
  border-radius: 8px;
}

.toggle-switch {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--gray-1);
  border: 1.5px solid var(--gray-2);
  border-radius: 999px;
  padding: 8px 12px;
}

.toggle-switch input {
  display: none;
}

.switch-slider {
  width: 36px;
  height: 20px;
  border-radius: 999px;
  background: var(--gray-2);
  position: relative;
  flex-shrink: 0;
}

.switch-slider::after {
  content: '';
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  /* Not a --white-trap bug: the toggle knob is intentionally always white on
     top of a colored track, same convention as platform.css's own
     .p-switch::after (hardcoded #fff, same reasoning). */
  background: #fff;
  transition: transform 0.15s ease;
}

.toggle-switch input:checked + .switch-slider::after {
  transform: translateX(16px);
}

.toggle-switch input:checked + .switch-slider {
  background: #A5D6A7;
}

.toggle-switch .switch-label {
  font-size: 12px;
  color: var(--text-muted);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
  border-color: var(--accent);
  background: var(--surface);
}

.form-group input[readonly] {
  color: var(--text-muted);
  cursor: default;
}

.form-group textarea {
  resize: none;
  min-height: 56px;
}

/* C-02: Hide native number input increment/decrement arrows globally */
input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* Multi-day toggle */
.multi-day-section {
  padding-top: 2px;
  padding-bottom: 2px;
}

/* ── Multi-day / Full-day selectable cards ── */
.multi-day-checkbox-label {
  position: relative;
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 12px 14px;
  border: 1.5px solid var(--gray-3);
  border-radius: 10px;
  background: var(--surface);
  cursor: pointer;
  user-select: none;
  box-sizing: border-box;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}

/* Native checkbox hidden in DOM — kept for accessibility */
.multi-day-checkbox-label input[type="checkbox"] {
  position: absolute;
  opacity: 0;
  width: 0;
  height: 0;
  margin: 0;
  pointer-events: none;
}

.multi-day-checkbox-text {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-main);
}

.multi-day-hint {
  display: block;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 2px;
}

/* v1.30.4 — generic muted note under a form field (User Management Integration's
   "Custom Role belum aktif" hint; reusable anywhere a field needs a short caveat). */
.form-hint {
  display: block;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 4px;
}
.form-hint--warning {
  color: var(--danger, #A8292F);
  font-weight: 600;
}

.multi-day-checkbox-label:hover {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(211, 47, 47, 0.06);
}

.multi-day-checkbox-label:has(input[type="checkbox"]:focus-visible) {
  outline: 2px solid var(--danger);
  outline-offset: 2px;
}

.multi-day-checkbox-label:has(input[type="checkbox"]:checked) {
  border-color: var(--danger);
  background: var(--danger-bg);
}

.multi-day-checkbox-label:has(input[type="checkbox"]:checked)::after {
  content: '\2713';
  position: absolute;
  top: 9px;
  right: 10px;
  width: 18px;
  height: 18px;
  background: var(--danger);
  color: var(--white);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
}

/* End-date field — hidden by default.
   Desktop: visibility:hidden preserves the grid cell so the time fields
   below never shift when the field is toggled.
   Mobile: display:none collapses the space on a single-column stack. */
.end-date-wrapper {
  visibility: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.end-date-wrapper.visible {
  visibility: visible;
  pointer-events: auto;
  opacity: 1;
}

/* Full-day toggle — shares spacing/padding with multi-day row */
.full-day-section {
  padding-top: 2px;
  padding-bottom: 2px;
}

/* Time pair groups dimmed when Full Day is active.
   Inputs are kept visible but disabled so the user sees
   that time selection is not applicable for full-day assignments. */
.time-group-disabled {
  opacity: 0.38;
  pointer-events: none;
}
.time-group-disabled label::after {
  content: ' (Penuh Hari)';
  font-style: italic;
  font-weight: 400;
  opacity: 0.8;
}

/* Conflict Warning */
.conflict-warning {
  background: var(--danger-bg);
  border: 1.5px solid var(--danger);
  color: var(--danger);
  border-radius: var(--radius-sm);
  padding: 10px 14px;
  font-size: 13px;
  font-weight: 500;
  animation: shake 0.3s ease;
}

.conflict-warning-dates {
  margin-top: 4px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
}

.login-error {
  background: var(--danger-bg);
  border: 1px solid var(--danger);
  color: var(--danger);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  font-size: 13px;
  font-weight: 600;
  margin-top: 10px;
}

@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}

/* Form Action Buttons */
.form-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 16px;
  flex-wrap: wrap;
}

/* v1.30.9.3 — Secure Admin PIN Reset UX. Password-style field with an eye
   toggle: used both for a NEW PIN being typed (Create User) and for a
   just-generated PIN shown exactly once (Reset PIN result). Padding-right
   on the input reserves the toggle's space so revealing/hiding never
   shifts layout. */
.pin-reveal-field {
  position: relative;
  display: flex;
}
.pin-reveal-field input {
  flex: 1;
  padding-right: 40px !important;
  font-family: var(--font-mono, monospace);
  letter-spacing: 0.06em;
}
.pin-reveal-toggle {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--gray-4);
  cursor: pointer;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  transition: color 0.15s, background 0.15s;
}
.pin-reveal-toggle:hover { color: var(--text); background: var(--gray-2); }
.pin-reveal-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.pin-reveal-toggle svg { width: 18px; height: 18px; pointer-events: none; }

/* Edit User's "Kredensial" row — PIN is never shown/prefilled here, only a
   status label and the explicit Reset PIN action. */
.pin-credential-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  background: var(--gray-1);
  border: 1.5px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
}
.pin-credential-status {
  font-size: 14px;
  color: var(--text-muted);
}

/* v1.18.0 Vehicle Asset form — grouped section heading (Identitas/Registrasi/Legal/Asuransi) */
.form-section-label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted, #6b7280);
  margin: 18px 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--border, #e5e7eb);
}
.form-section-label:first-child { margin-top: 0; }

/* Vehicle Compliance & Financial History — explains why a field left the form */
.v2-vehicle-form-hint {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted, #6b7280);
  margin: 4px 0 0;
}

/* Renew STNK modal — collapsed-by-default "Detail Lainnya" disclosure, so the
   default view is only the three required fields. */
.v2-compliance-details { margin-top: 12px; border-top: 1px solid var(--gray-2, #e5e7eb); padding-top: 4px; }
.v2-compliance-details > .form-group { margin-top: 12px; }
.v2-compliance-summary {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted, #6b7280);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.v2-compliance-summary::-webkit-details-marker { display: none; }
.v2-compliance-summary:hover { color: var(--text); }
.v2-compliance-chevron { transition: transform 0.2s; font-size: 10px; }
.v2-compliance-details[open] .v2-compliance-chevron { transform: rotate(180deg); }

/* Assignment form: sticky footer with conflict notices stacked above buttons.
   Phase 9 mobile-first audit: the User form (#userForm) can run just as long
   — role summary + individual-permissions panel + engineering-level toggle
   all conditionally append — but never got this treatment, so Simpan/Batal
   scrolled away on a 375px screen. Same fix, same selector pattern. */
#assignmentForm .form-actions,
#userForm .form-actions {
  flex-direction: column;
  justify-content: stretch;
  margin-top: 12px;
  position: sticky;
  bottom: 0;
  background: var(--surface);
  padding-top: 10px;
  border-top: 1px solid var(--gray-2, #e5e7eb);
  z-index: 1;
}

/* SS19.1 — keyboard focus must never land UNDER the sticky footer above.
   Pinning it (position: sticky; bottom: 0) inside the drawer's scrolling body
   makes the footer paint over the bottom of the scrollport, but the browser's
   focus-scrolling only brings a control "just into" that scrollport — i.e. to
   the footer's top edge, directly behind it. Measured before this rule (real
   Tab key, geometry not class names): the focused Reset PIN button was 100%
   covered at 430x932 and 60% at 390x844, and the "Status Akun" toggle 41%.
   scroll-padding-bottom tells the scroll container its real visible area ends
   ABOVE the footer, so focus-scrolling stops there. The clearance must be >= the
   footer's height (two stacked 44px buttons + gap/padding ≈ 125px) plus a little
   breathing room; ss19-1-drawer-mobile-focus-check.mjs fails if it ever isn't.
   (Unsupported :has() simply leaves the old behaviour — no worse than before.) */
.drawer__body:has(#userForm .form-actions) {
  scroll-padding-bottom: var(--sticky-form-foot-clearance, 9rem);
}

.form-actions-btns {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

.form-text,
.small-instructions {
  color: var(--gray-4);
  font-size: 13px;
  line-height: 1.6;
  margin-top: 8px;
}

.small-instructions ol {
  list-style: decimal;
  padding-left: 18px;
  margin: 10px 0 0;
}

.small-instructions li {
  margin-bottom: 6px;
}

.btn-secondary {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  background: var(--gray-1);
  border: 1.5px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 8px 16px;
  min-height: 44px;
  cursor: pointer;
  /* v1.31.4 R8 — only background changes on :hover. */
  transition: background-color 0.15s;
}
.btn-secondary:hover { background: var(--gray-2); }

.btn-danger {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--white);
  background: var(--danger);
  border: none;
  border-radius: var(--radius-sm);
  padding: 8px 16px;
  min-height: 44px;
  cursor: pointer;
  transition: background 0.15s;
}
.btn-danger:hover { background: color-mix(in srgb, var(--danger) 85%, #000); }
.btn-danger:disabled { opacity: 0.45; cursor: default; }

.btn-success {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--white);
  background: #43a047;
  border: none;
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  min-height: 44px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: background 0.15s;
}
.btn-success:hover:not(:disabled) { background: #388e3c; }
.btn-success:disabled { opacity: 0.45; cursor: default; }

/* ── Modal Detail — Accordion Sections ── */

.accord-section {
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}
/* Hotfix: margin-bottom removed — every .accord-section is now a direct
   flex child of the canonical drawer's .drawer__body (gap:20px, see
   platform.css), so this pre-migration margin was stacking on top of that
   gap and made spacing around individual sections (e.g. reimbursement)
   visibly uneven versus the drawer's own uniform rhythm. */

/* Section 1 has a subtle tinted background to signal "always visible" */
#accordSummary {
  background: var(--gray-1);
  border-color: var(--gray-3);
}

.accord-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 11px 14px;
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
  min-height: 44px; /* touch-friendly tap target */
  gap: 8px;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s;
}

.accord-header:active {
  background: var(--gray-2);
}

@media (hover: hover) {
  .accord-header:hover {
    background: var(--gray-1);
  }
  #accordSummary .accord-header:hover {
    background: var(--gray-2);
  }
}

.accord-title {
  font-size: 12px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0.3px;
  text-transform: uppercase;
}

.accord-chevron {
  font-size: 9px;
  color: var(--text-muted);
  flex-shrink: 0;
  display: inline-block;
  transition: transform 0.2s ease;
  line-height: 1;
}

.accord-section--open > .accord-header > .accord-chevron {
  transform: rotate(90deg);
}

/* Open/close height animation for Assignment Detail's accordion sections.
   V1 HOTFIX: was a hardcoded `max-height: 0 → 700px` transition. The 700px
   cap + `overflow: hidden` SILENTLY CLIPPED the bottom of any taller section
   — Ringkasan Jadwal, Informasi Operasional and Ringkasan WhatsApp all
   exceed 700px on a narrow phone (long Tujuan/Keperluan/Catatan wrapping,
   full lifecycle audit, a multi-line WA preview) — with no scrollbar to
   recover the hidden content. `grid-template-rows: 0fr → 1fr` animates to
   the section's TRUE natural height instead: same collapsed state, same
   ~0.25s ease-out feel, ZERO clipping, and still no per-section scrollbar
   (the canonical drawer body stays the single vertical scroll container).
   `overflow: hidden` is kept — it's what clips the inner content to nothing
   while the row is collapsed / mid-transition. `.accord-body-inner` gets
   `min-height: 0` so the single grid row can actually collapse to 0
   (otherwise `minmax(auto, 0fr)` floors at the child's min-content). */
.accord-body {
  display: grid;
  grid-template-rows: minmax(0, 0fr); /* minmax(0,…) not bare 0fr: bare 0fr is
                                         minmax(auto,0fr) whose `auto` floor is
                                         the child's ~18px padding, leaving the
                                         collapsed section visibly non-zero. */
  overflow: hidden;
  transition: grid-template-rows 0.25s ease-out;
}

.accord-section--open > .accord-body {
  grid-template-rows: minmax(0, 1fr);
}

.accord-body-inner {
  min-height: 0;
  padding: 4px 14px 14px;
}

/* Odometer section is hidden entirely when no data is present */
.accord-section--hidden {
  display: none;
}

/* ── Primary Actions (Mulai / Selesaikan / Komentar) ── */
.detail-actions-primary {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 0 4px;
}

/* ── Detail Rows (inside accordion bodies) ── */

.detail-row {
  display: flex;
  gap: 8px;
  margin-bottom: 8px;
  align-items: flex-start;
}
.detail-row:last-child { margin-bottom: 0; }

.detail-label {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  min-width: 90px;
  padding-top: 1px;
  flex-shrink: 0;
}

.detail-value {
  font-size: 13px;
  color: var(--text);
  font-weight: 500;
  word-break: break-word;
}

/* Badge status assignment (Aktif / Selesai) */
.badge-aktif {
  display: inline-block;
  background: #e8f5e9;
  color: #2e7d32;
  border: 1px solid #a5d6a7;
  border-radius: 4px;
  padding: 2px 8px;
  font-size: 12px;
  font-weight: 600;
}

.badge-selesai {
  display: inline-block;
  background: #ede7f6;
  color: #6a1b9a;
  border: 1px solid #ce93d8;
  border-radius: 4px;
  padding: 2px 8px;
  font-size: 12px;
  font-weight: 600;
}

/* ── Lifecycle status badges (5-state model) ── */
.badge-status {
  display: inline-block;
  border-radius: 4px;
  padding: 2px 8px;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid transparent;
}
.badge-status--pending   { background: #fff9c4; color: #f57f17; border-color: #ffe082; }
.badge-status--approved  { background: #e3f2fd; color: #1565c0; border-color: #90caf9; }
.badge-status--assigned  { background: #e8f5e9; color: #2e7d32; border-color: #a5d6a7; }
.badge-status--started   { background: #fff3e0; color: #e65100; border-color: #ffcc80; }
.badge-status--completed { background: #ede7f6; color: #6a1b9a; border-color: #ce93d8; }

/* Phase 11A: dark-mode counterparts — see .role-badge dark block above for
   rationale. approved/assigned/started reuse the same info/ok/warn families
   platform.css already defines for dark mode; pending (yellow) and
   completed (purple) have no existing dark token pair, so a desaturated
   variant is added here following the same treatment as the existing ones
   (e.g. --warn dark: amber-on-umber). */
[data-theme="dark"] .badge-aktif   { background: var(--ok-bg, #1F2C27); color: var(--ok, #72AF8F); border-color: var(--ok, #72AF8F); }
[data-theme="dark"] .badge-selesai { background: #2A2030; color: #C9A0D9; border-color: #4A3752; }
[data-theme="dark"] .badge-status--pending   { background: #332D16; color: #D8C15C; border-color: #5C5024; }
[data-theme="dark"] .badge-status--approved  { background: var(--info-bg, #242B39); color: var(--info, #8AA0CF); border-color: var(--info, #8AA0CF); }
[data-theme="dark"] .badge-status--assigned  { background: var(--ok-bg, #1F2C27); color: var(--ok, #72AF8F); border-color: var(--ok, #72AF8F); }
[data-theme="dark"] .badge-status--started   { background: var(--warn-bg, #2C2719); color: var(--warn, #C7A05B); border-color: var(--warn, #C7A05B); }
[data-theme="dark"] .badge-status--completed { background: #2A2030; color: #C9A0D9; border-color: #4A3752; }

/* Timestamp shown inside lifecycle audit rows */
.detail-ts {
  font-size: 11px;
  color: var(--text-muted, #888);
  margin-left: 4px;
  font-weight: 400;
}

/* Badge kendaraan di detail */
.vehicle-badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
  color: var(--white);
}

/* ── WhatsApp Preview (inside accordion body) ── */
.wa-preview-box {
  background: #ECF8EE;
  border: 1.5px solid #A5D6A7;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 14px;
}

/* WA section accordion uses tinted header to match the old box feel */
#accordWA {
  border-color: #A5D6A7;
  background: #ECF8EE;
}

#accordWA .accord-title {
  color: #2E7D32;
}

#accordWA .accord-chevron {
  color: #2E7D32;
}

@media (hover: hover) {
  #accordWA .accord-header:hover {
    background: #d8f0dc;
  }
}

.wa-preview-label {
  font-size: 12px;
  font-weight: 700;
  color: #2E7D32;
  margin-bottom: 8px;
}

.wa-preview-text {
  font-family: var(--font-mono);
  font-size: 12px;
  white-space: pre-wrap;
  color: var(--text);
  line-height: 1.7;
  margin-bottom: 10px;
}

.btn-wa-copy {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--white);
  background: #2E7D32;
  border: none;
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  cursor: pointer;
  transition: background 0.15s;
}
.btn-wa-copy:hover { background: #388E3C; }

.btn-reimbursement {
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 600;
  color: var(--white);
  background: #1A1917;
  border: none;
  border-radius: var(--radius-sm);
  padding: 8px 14px;
  cursor: pointer;
  transition: background 0.15s;
  width: 100%;
  text-align: left;
}
.btn-reimbursement:hover { background: #333; }

.reimbursement-hint {
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 10px;
  line-height: 1.5;
}

.copy-feedback {
  font-size: 12px;
  font-weight: 600;
  color: #2E7D32;
  margin-left: 8px;
}

/* Empty state inside Informasi Operasional when no lifecycle events yet */
.detail-empty {
  font-size: 12px;
  color: var(--text-muted);
  font-style: italic;
  padding: 2px 0;
}

.detail-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  /* margin-top removed — same reasoning as .accord-section above; this is
     also a direct .drawer__body flex child now. */
}

/* Request Workflow */
.requests-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 14px;
}

.request-card {
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 12px;
}

.request-card[data-status="approved"] {
  border-color: #A5D6A7;
}

/* v1.20.8 — notification deep-link: briefly highlight the target card after
   scrollIntoView so the user can find it among a long list. */
.request-card--highlight {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  transition: outline-color var(--motion-slow, 320ms) var(--ease-standard, ease);
}

/* Design System Program Phase 8.3 — restrained entrance for a request card
   that's genuinely new to the list (never applied to updated/unaffected
   cards, and never applied on initial/bulk population). Both the OS-level
   prefers-reduced-motion and the app's own [data-anim="off"] blanket rules
   (top of this file) already collapse this to ~0.01ms — no extra gating. */
@keyframes requestCardEnter {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.request-card--enter {
  animation: requestCardEnter var(--motion-base, 200ms) var(--ease-decelerate, ease);
}

/* v1.20.8 — "Lainnya" bottom sheet (Administrator bottom-nav more menu).
   New reusable primitive: also the Bottom Sheet motion target Objective 10
   asks for generally. Slides up from the bottom edge, reuses the same
   overlay convention as .sidebar-overlay / .modal-overlay. */
.bottom-sheet-overlay {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.48);
  z-index: 160;
}
.bottom-sheet-overlay.bottom-sheet-visible { display: block; }

.bottom-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  background: var(--surface);
  border-radius: 16px 16px 0 0;
  z-index: 161;
  padding: 8px 0 calc(16px + env(safe-area-inset-bottom, 0px));
  transform: translateY(100%);
  transition: transform var(--motion-base, 200ms) var(--ease-decelerate, ease);
  box-shadow: var(--shadow-lg);
  max-height: 70vh;
  overflow-y: auto;
}
.bottom-sheet.bottom-sheet-open { transform: translateY(0); }

.bottom-sheet-handle {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: var(--gray-3);
  margin: 8px auto 12px;
}

.bottom-sheet-item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 48px;
  padding: 12px 20px;
  background: none;
  border: none;
  font-size: 14px;
  font-weight: 500;
  color: var(--text);
  cursor: pointer;
  text-align: left;
  transition: background var(--motion-fast, 120ms) var(--ease-standard, ease);
}
.bottom-sheet-item:hover { background: var(--gray-1); }

.request-card[data-status="rejected"] {
  border-color: #EF9A9A;
}

.request-card-header {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: flex-start;
}

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

.request-card-badges {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 5px;
  flex-shrink: 0;
}

.request-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--text);
}

.request-meta,
.request-notes {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 3px;
}

.request-status {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  background: var(--surface);
  border: 1px solid var(--gray-2);
  border-radius: 999px;
  padding: 3px 8px;
  color: var(--text);
}

.request-card[data-status="approved"] .request-status {
  background: #E8F5E9;
  border-color: #A5D6A7;
  color: #2E7D32;
}

.request-card[data-status="rejected"] .request-status {
  background: var(--danger-bg);
  border-color: #EF9A9A;
  color: var(--danger);
}

.request-duration {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 999px;
  background: #E3F2FD;
  color: #1565C0;
  border: 1px solid #BBDEFB;
  white-space: nowrap;
}

.request-details {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 9px;
  font-size: 13px;
  color: var(--text);
}

.request-card-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 12px;
  flex-wrap: wrap;
}

.empty-request-state {
  background: var(--gray-1);
  border: 1px dashed var(--gray-3);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: 13px;
  padding: 18px;
  text-align: center;
}

/* ============================================================
   DRIVER DASHBOARD
   ============================================================ */
.driver-dashboard {
  padding: 16px 20px 80px;
  max-width: 680px;
}

.dash-section {
  margin-bottom: 28px;
}

.dash-section--active .dash-section-title {
  color: #e65100;
}

.dash-section-title {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 12px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.dash-section-title::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border, #e0e0e0);
}

.dash-card {
  background: var(--surface);
  border: 1px solid var(--border, #e0e0e0);
  border-radius: var(--radius, 10px);
  padding: 14px 16px;
  margin-bottom: 10px;
  cursor: pointer;
  transition: box-shadow 0.15s, border-color 0.15s, transform 0.1s;
}

.dash-card:hover {
  box-shadow: 0 3px 12px rgba(0,0,0,0.09);
  border-color: #bdbdbd;
}

.dash-card:active { transform: scale(0.99); }

.dash-card--started {
  border-left: 3px solid #e65100;
  background: #fff9f5;
}

.dash-card--muted { opacity: 0.72; }

.dash-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}

.dash-card-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 4px;
  line-height: 1.4;
}

.dash-card-destination {
  font-size: 13px;
  color: var(--text);
  margin-bottom: 6px;
}

.dash-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.dash-meta-item {
  font-size: 12px;
  color: var(--text-muted);
}

.dash-card-pic {
  font-size: 12px;
  color: var(--text-muted);
  margin-top: 4px;
}

.dash-empty {
  color: var(--text-muted);
  font-size: 13px;
  text-align: center;
  padding: 20px 0;
  font-style: italic;
}

/* ============================================================
   COMMENT THREAD
   ============================================================ */
.modal-comment-box { max-width: 520px; }

/* ── Request context strip ── */
.comment-context {
  border: 1px solid var(--border, #e0e0e0);
  border-radius: var(--radius-sm, 6px);
  padding: 10px 14px;
  margin-bottom: 14px;
  background: var(--gray-1, #f5f5f5);
}

.comment-context-row {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
}

.comment-context-row + .comment-context-row { margin-top: 5px; }

.comment-context-footer { justify-content: space-between; }

.comment-context-driver {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

.comment-context-sep,
.comment-context-date  { font-size: 12px; color: var(--text-muted); }

.comment-context-requester { font-size: 11px; color: var(--text-muted); }

.comment-context-status-badge {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  border-radius: 4px;
  padding: 2px 7px;
}

.comment-context-status--pending  { background: #fff9c4; color: #f57f17; }
.comment-context-status--approved { background: #e8f5e9; color: #2e7d32; }
.comment-context-status--rejected { background: #fce4ec; color: #c62828; }

.comment-list {
  overflow-y: auto;
  max-height: 50vh;
  min-height: 100px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 4px 0 8px;
  margin-bottom: 4px;
}

.comment-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.comment-item--own { align-items: flex-end; }

.comment-header {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.comment-author {
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}

.comment-role-badge {
  font-size: 10px;
  font-weight: 600;
  background: #e8eaf6;
  color: #3949ab;
  border-radius: 3px;
  padding: 1px 5px;
}

.comment-time {
  font-size: 11px;
  color: var(--text-muted);
  margin-left: auto;
}

.comment-body {
  font-size: 13px;
  color: var(--text);
  background: var(--gray-1, #f5f5f5);
  border-radius: 8px;
  padding: 8px 12px;
  max-width: 92%;
  word-break: break-word;
  line-height: 1.5;
}

.comment-item--own .comment-body {
  background: #fde8e8;
}

.comment-input-area {
  display: flex;
  gap: 8px;
  align-items: flex-end;
  padding-top: 12px;
  border-top: 1px solid var(--border, #e0e0e0);
}

.comment-input-area textarea {
  flex: 1;
  border: 1px solid var(--border, #e0e0e0);
  border-radius: var(--radius-sm, 6px);
  padding: 8px 12px;
  font-family: var(--font-sans);
  font-size: 13px;
  resize: none;
  min-height: 40px;
  max-height: 100px;
  color: var(--text);
  background: var(--surface);
  line-height: 1.4;
}

.comment-input-area textarea:focus {
  outline: none;
  border-color: var(--red, #d32f2f);
}

.comment-empty {
  font-size: 13px;
  color: var(--text-muted);
  text-align: center;
  padding: 24px 0;
  font-style: italic;
}

/* ============================================================
   CONFLICT PREVIEW (real-time advisory warning)
   ============================================================ */
.conflict-preview {
  background: #fff8e1;
  border: 1.5px solid #ffca28;
  color: #5d4037;
  border-radius: var(--radius-sm, 6px);
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.7;
}

/* ============================================================
   TOAST NOTIFICATION
   ============================================================ */
.toast {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--dark);
  color: var(--white);
  padding: 10px 14px 10px 20px;
  border-radius: 20px;
  font-size: 13px;
  font-weight: 500;
  z-index: 999;
  box-shadow: var(--shadow-lg);
  animation: toastIn 0.2s ease;
  white-space: nowrap;
  max-width: 90vw;
  text-align: center;
  /* SS18 Phase E — flex row so the (optional) severity icon, message, and
     close button all align on one line regardless of which are present. */
  display: flex;
  align-items: center;
  gap: 8px;
}

/* SS18 Phase E — manual dismiss control (js/components/toast.js). A dim
   glyph by default so it doesn't compete with the message; full opacity on
   hover/focus. Padding (not just the 11px icon) sets the real hit target —
   7px clears the WCAG 2.5.8 24x24 CSS-pixel minimum (Phase G browser QA:
   the icon alone measured 19x19, confirmed via getBoundingClientRect at
   every QA viewport) without visually enlarging the glyph itself. */
.toast__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  margin-left: 2px;
  padding: 7px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: currentColor;
  opacity: 0.7;
  cursor: pointer;
}
.toast__close:hover,
.toast__close:focus-visible {
  opacity: 1;
  background: rgba(255, 255, 255, 0.16);
}
.toast__close:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 1px;
}

/* Motion polish (v1.31.22.0) — exit beat for an AUTO-dismissed toast
   (js/components/toast.js adds .toast--leaving, then display:none after
   180ms). Shorter and smaller than the entrance; a manual close skips it.
   Reduced motion never adds the class. */
.toast.toast--leaving { animation: toastOut 180ms var(--ease-exit, cubic-bezier(.4,0,1,1)) forwards; }
@keyframes toastOut {
  from { opacity: 1; transform: translateX(-50%) translateY(0); }
  to   { opacity: 0; transform: translateX(-50%) translateY(6px); }
}

@keyframes toastIn {
  from { opacity: 0; transform: translateX(-50%) translateY(10px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* ============================================================
   RESPONSIVE — Mobile First
   ============================================================ */
/* ============================================================
   RESPONSIVE — Desktop (sidebar always visible)
   ============================================================ */
@media (min-width: 768px) {
  .sidebar-toggle { display: none; }
  .sidebar-close  { display: none; }
  .sidebar-overlay { display: none !important; }
  .header-brand { display: none; }
  .bottom-nav { display: none !important; }
  .fab-add { display: none !important; }
  .header-module-title { display: block; }
  /* Desktop header: date-nav sits inline (reset mobile wrapping). */
  .date-nav {
    order: unset;
    width: auto;
    margin: 0;
    padding: 2px 6px;
    background: var(--gray-1);
    border: 1px solid var(--gray-2);
    border-radius: var(--radius-sm);
  }
  .header-inner { flex-wrap: nowrap; height: 56px; padding: 0 16px; }
  .header-spacer { flex: 1; }
}

/* ============================================================
   RESPONSIVE — Tablet + Mobile (sidebar as drawer)
   ============================================================ */
@media (max-width: 767px) {
  /* Sidebar: hidden by default, slides in on open. pointer-events is
     deliberately NOT part of the transition — the closed/exiting drawer
     must stop intercepting taps immediately when .sidebar-open is removed,
     not only once its ~260ms slide-out finishes (a real tap right after
     navigating could otherwise land on the still-visually-present, but
     logically closed, panel instead of the real content underneath). */
  .sidebar {
    transform: translateX(-240px);
    transition: transform var(--motion-slow, 0.26s) var(--ease-standard, ease);
    box-shadow: none;
    pointer-events: none;
  }
  .sidebar.sidebar-open {
    transform: translateX(0);
    box-shadow: var(--shadow-lg);
    pointer-events: auto;
  }
  .sidebar-close { display: flex; }
  .sidebar-toggle { display: flex; }
  .header-brand { display: flex; }
  .header-module-title { display: none; }

  /* Main area takes full width */
  .main-area { margin-left: 0; }

  /* Bottom nav visible */
  .bottom-nav { display: flex; }

  /* Global bottom clearance (v1.15.2): one reusable token applied to the single
     scroll container so NO actionable element is ever trapped behind the FAB or
     bottom nav, on any workspace (Driver Ops, Petty Cash, Analytics, Konfigurasi,
     forms, detail screens). */
  .main-content {
    padding-bottom: var(--mobile-safe-bottom);
  }

  /* Toast clears the FAB + bottom nav too. */
  .toast {
    bottom: var(--mobile-safe-bottom);
  }

  /* Prevent body scroll when sidebar is open */
  body.sidebar-is-open { overflow: hidden; }
}

/* Phase 11K — shared bottom-sheet scroll lock (js/ui/sheet-gesture.js).
   Same technique as .sidebar-is-open above, generically named since it's
   shared by every bottom sheet/modal (Lainnya, Notifications, Profile,
   request-mode confirm), not just the drawer. Unscoped by width — these
   sheets can also open on desktop. */
body.sheet-scroll-lock { overflow: hidden; }

@media (max-width: 600px) {

  /* ============================================================
     HEADER — 2-row compact layout
     Row 1: hamburger | brand | spacer | user info
     Row 2: date navigation (full-width centered)
     ============================================================ */
  .header-inner {
    flex-wrap: wrap;
    height: auto;
    padding: 8px 12px 0;
    gap: 6px;
    row-gap: 0;
    align-items: center;
  }

  /* Row 1 items */
  .sidebar-toggle { order: 1; flex-shrink: 0; }
  .header-brand   { order: 2; flex: 1; min-width: 0; gap: 6px; }
  .header-spacer  { order: 3; flex: 1; }
  .header-user-area { order: 4; flex-shrink: 0; }
  .header-module-title { display: none; }

  /* Row 2: date nav spans full width */
  .date-nav {
    order: 10;
    flex: 0 0 100%; /* full row, no extra width */
    width: 100%;
    margin-top: 6px;
    padding: 6px 0 8px;
    background: var(--gray-1);
    border: none;
    border-top: 1px solid var(--gray-2);
    border-radius: 0;
    justify-content: center;
    gap: 8px;
  }

  .brand-logo  { width: 26px; height: 26px; }
  .brand-title { font-size: 12px; }

  /* 16px minimum prevents iOS Safari auto-zoom on <input type="date"> focus */
  .date-input { min-width: 96px; font-size: 16px; color-scheme: light; }
  .btn-today  { font-size: 11px; padding: 3px 7px; }

  .header-display-name { max-width: 90px; font-size: 12px; }

  /* ============================================================
     MAIN CONTENT
     ============================================================ */
  /* Bottom clearance intentionally omitted here — the ≤767px rule above
     (line ~3010) already sets padding-bottom to the safe-area-aware
     --mobile-safe-bottom token; a shorthand `padding` here would clobber it
     back down to a flat value with no env(safe-area-inset-bottom) term. */
  .main-content        { padding-top: 10px; padding-left: 8px; padding-right: 8px; }
  .timeline-date-label { font-size: 13px; margin-bottom: 8px; }

  /* ============================================================
     TIMELINE — denser for more on-screen information
     ============================================================ */
  :root {
    --row-height:    44px;
    --driver-col:    72px;
    --hour-width:    58px;
    --header-height: 26px;
  }

  .driver-name  { font-size: 11px; }
  .driver-phone { display: none; }

  .assignment-block {
    top: 4px;
    height: calc(var(--row-height) - 8px);
  }
  .block-purpose { font-size: 10px; }
  .block-time    { font-size: 9px; }

  /* ============================================================
     FORMS — iOS Safari zoom prevention + tighter density
     ============================================================ */

  /* 16px minimum prevents iOS Safari auto-zoom on input focus */
  .form-group input,
  .form-group select,
  .form-group textarea {
    font-size: 16px;
    padding: 9px 10px;
  }

  .time-pair-group .time-pair input {
    font-size: 16px;
    padding: 9px 6px;
  }

  .comment-input-area textarea {
    font-size: 16px;
  }

  .form-grid {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .form-full  { grid-column: 1; }
  .form-group { gap: 3px; }
  .form-group label { font-size: 11px; }

  /* ============================================================
     MODALS — bottom sheet, safe-area aware
     ============================================================ */
  .modal-box {
    max-height: 95vh;
    border-radius: 16px 16px 0 0;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    max-width: 100%;
    margin: 0;
    animation: slideUpMobile 0.25s ease;
  }
  .modal-overlay { align-items: flex-end; padding: 0; }

  /* Odometer modal stays centered on mobile, not bottom-sheet */
  #modalOdometer { align-items: center; padding: 16px; }
  .odo-modal { border-radius: 14px; animation: odoSlideIn 0.22s cubic-bezier(0.16, 1, 0.3, 1); }
  .odo-field__input { font-size: 24px; }

  @keyframes slideUpMobile {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }

  .modal-header { padding: 14px 16px 10px; }
  .modal-body {
    padding: 10px 14px;
    /* Padding bottom covers iPhone home indicator */
    padding-bottom: max(20px, env(safe-area-inset-bottom, 20px));
  }

  /* Form action buttons: full-width, primary on top */
  .form-actions {
    flex-direction: column-reverse;
    margin-top: 14px;
  }
  #assignmentForm .form-actions { margin-top: 0; flex-direction: column; }
  .form-actions-btns { flex-direction: column-reverse; }
  .form-actions .btn-primary,
  .form-actions .btn-secondary,
  .form-actions-btns .btn-primary,
  .form-actions-btns .btn-secondary {
    width: 100%;
    text-align: center;
    justify-content: center;
  }

  .detail-actions { flex-wrap: wrap; }
  .detail-actions button { flex: 1; text-align: center; }

  /* ============================================================
     MULTI-DAY / FULL-DAY SECTION — compact
     ============================================================ */
  .multi-day-section      { padding-top: 0; padding-bottom: 0; }
  .full-day-section       { padding-top: 0; padding-bottom: 0; }
  .multi-day-checkbox-text { font-size: 13px; }
  .multi-day-hint          { font-size: 10px; margin-top: 1px; }

  /* On mobile collapse the end-date field instead of just hiding it */
  .end-date-wrapper         { display: none; }
  .end-date-wrapper.visible { display: flex; opacity: 1; visibility: visible; pointer-events: auto; }

  /* ============================================================
     REQUEST CARDS — compact
     ============================================================ */
  .request-card        { padding: 10px 12px; }
  .request-title       { font-size: 13px; }
  .request-card-header { flex-direction: column; gap: 5px; }
  .request-card-actions { margin-top: 8px; }
  .request-card-actions button { flex: 1; }

  /* ============================================================
     LEGEND — compact
     ============================================================ */
  .legend       { padding: 7px 10px; gap: 5px; margin-top: 8px; }
  .legend-item  { font-size: 10px; padding: 2px 6px 2px 4px; }
  .legend-title { font-size: 10px; }
}

/* ============================================================
   UTILITAS
   ============================================================ */

/* Status kosong jika tidak ada assignment */
.empty-slots-hint {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 11px;
  color: var(--gray-3);
  pointer-events: none;
  white-space: nowrap;
}

/* Highlight hari ini */
.today-line {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--accent);
  opacity: 0.5;
  pointer-events: none;
  z-index: 2;
}

/* ============================================================
   PROFILE MODAL
   ============================================================ */

.profile-modal-box {
  max-width: 460px;
}

/* ── User card ── */
.profile-user-card {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: var(--radius);
  margin-bottom: 20px;
}

.profile-avatar {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--white);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 19px;
  font-weight: 700;
  flex-shrink: 0;
  text-transform: uppercase;
  letter-spacing: 0;
}

.profile-user-info {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.profile-display-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  line-height: 1.2;
}

.profile-role-chip {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  width: fit-content;
  background: var(--gray-2);
  color: var(--text-muted);
}
.profile-role-chip[data-role="admin"]  { background: var(--danger-bg); color: var(--danger); }
.profile-role-chip[data-role="bidang"] { background: #E3F2FD; color: #1565C0; }
.profile-role-chip[data-role="viewer"] { background: #E8F5E9; color: #2E7D32; }
.profile-role-chip[data-role="driver"] { background: #FFF3E0; color: #E65100; }

/* ── Sections ── */
.profile-section {
  margin-bottom: 2px;
}

.profile-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
}

.profile-section-label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--text-muted);
  display: block;
  margin-bottom: 12px;
}
.profile-section-header .profile-section-label {
  margin-bottom: 0;
}

/* ── Telegram status badge ── */
.profile-tg-badge {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--gray-2);
  background: var(--gray-1);
  color: var(--text-muted);
  white-space: nowrap;
  flex-shrink: 0;
  transition: background 0.2s, color 0.2s, border-color 0.2s;
}
.profile-tg-badge[data-status="connected"] {
  background: #E8F5E9;
  color: #2E7D32;
  border-color: #A5D6A7;
}

/* ── Help text with inline action buttons ── */
.profile-help-text {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.7;
  margin-bottom: 14px;
}

.profile-inline-btn {
  background: none;
  border: none;
  color: var(--accent-fg);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  padding: 0;
  font-family: var(--font-sans);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.profile-inline-btn:hover { color: var(--accent-hover); }

.profile-code-btn {
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: 4px;
  padding: 1px 7px;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text);
  transition: background 0.15s;
  line-height: 1.6;
  vertical-align: baseline;
}
.profile-code-btn:hover { background: var(--gray-2); }
.profile-code-btn code { font-family: inherit; font-size: inherit; }

/* ── Input row with paste button ── */
.profile-input-row {
  display: flex;
  gap: 6px;
}
.profile-input-row input { flex: 1; min-width: 0; }

.btn-paste-small {
  font-family: var(--font-sans);
  font-size: 12px;
  font-weight: 600;
  padding: 0 13px;
  background: var(--gray-1);
  border: 1.5px solid var(--gray-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  color: var(--text);
  white-space: nowrap;
  transition: background 0.15s, border-color 0.15s;
  flex-shrink: 0;
}
.btn-paste-small:hover {
  background: var(--gray-2);
  border-color: var(--gray-3);
}

/* Status text below Chat ID input */
.profile-status-text {
  display: block;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: 4px;
  min-height: 14px;
  transition: color 0.2s;
}

/* Optional / hint tag */
.tag-optional {
  font-size: 10px;
  font-weight: 500;
  color: var(--gray-4);
  background: var(--gray-2);
  border-radius: 4px;
  padding: 1px 5px;
  text-transform: none;
  letter-spacing: 0;
  vertical-align: middle;
  margin-left: 4px;
}

/* ── Notifications toggle row ── */
.profile-toggle-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 11px 0;
  border-top: 1px solid var(--gray-2);
  border-bottom: 1px solid var(--gray-2);
  margin: 14px 0 12px;
}

.profile-toggle-text {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
}

/* Override toggle-switch pill style inside profile toggle row */
.profile-toggle-switch {
  background: none;
  border: none;
  padding: 0;
  gap: 0;
}

/* ── Appearance section — dark mode toggle ── */

/* Remove top border: section label already provides visual separation. */
.profile-theme-row {
  border-top: none;
  margin-top: 2px;
}

/* Stack label + subtext vertically inside the toggle row. */
.profile-theme-info {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* Muted subtext below the toggle label. */
.profile-theme-sub {
  font-size: 11px;
  color: var(--text-muted);
  line-height: 1.4;
}

/* ── Test button ── */
.profile-test-btn {
  width: 100%;
  justify-content: center;
  font-size: 12px;
  color: var(--text-muted);
}
.profile-test-btn:hover { color: var(--text); }

/* ── Section divider ── */
.profile-divider {
  height: 1px;
  background: var(--gray-2);
  margin: 20px 0;
}

/* ============================================================
   LOGIN MODAL — version footer
   ============================================================ */
.login-logo-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.login-logo {
  width: 34px;
  height: 34px;
  object-fit: contain;
  flex-shrink: 0;
}

.login-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  margin-top: 20px;
  padding-top: 14px;
  border-top: 1px solid var(--gray-2);
  text-align: center;
}

.login-footer-appname {
  font-size: 10px;
  color: var(--text-muted);
  line-height: 1.45;
}

.login-footer-version {
  font-size: 11px;
  font-weight: 600;
  color: var(--gray-4);
  font-family: var(--font-mono);
}

/* ============================================================
   PROFILE — Application Information section
   ============================================================ */
.app-info-block {
  background: var(--gray-1);
  border: 1px solid var(--gray-2);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.app-info-name {
  font-size: 12px;
  color: var(--text);
  font-weight: 500;
  line-height: 1.4;
}

.app-info-version {
  font-size: 11px;
  color: var(--text-muted);
  font-family: var(--font-mono);
}

/* ============================================================
   TIMELINE — scroll fade edge indicator (right side only)
   Left fade removed: sticky driver column eliminates the need for it,
   and the gradient was interfering with driver name readability.
   ============================================================ */
.timeline-scroll-fade-right {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  width: 36px;
  pointer-events: none;
  z-index: 8; /* above blocks (3) and body content, below driver-label (15) */
  transition: opacity 0.2s;
  background: linear-gradient(to left, rgba(255,255,255,0.85), transparent);
  opacity: 1;
}

/* ============================================================
   REQUEST MODE SELECTOR + CONFIRMATION SHEET (v1.17.4 — Part B)
   Premium Apple-style option cards that replace the legacy request
   checkboxes, plus a minimal Human-Interface confirmation sheet.
   Colors use platform.css tokens (--surface / --accent / --muted …)
   so the whole component is dark-mode safe with no overrides.
   ============================================================ */

/* Hidden source-of-truth checkboxes (read by requests.js). */
.req-mode-input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); clip-path: inset(50%);
  overflow: hidden; white-space: nowrap;
}

.request-mode-group { display: flex; flex-direction: column; gap: 9px; }
.request-mode-group__label {
  font-size: 12px; font-weight: 600; color: var(--muted, #5B5953);
  letter-spacing: .01em;
}

.req-mode-grid { display: flex; gap: 12px; }

/* ── Option card ── */
.req-mode-card {
  position: relative;
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: flex-start; gap: 12px;
  padding: 14px 15px;
  text-align: left;
  background: var(--surface, #fff);
  border: 1.5px solid var(--border-strong, #DAD7D2);
  border-radius: 14px;
  cursor: pointer;
  font: inherit; color: var(--text, #1A1917);
  transition: border-color .16s ease, box-shadow .16s ease, background .16s ease, transform .08s ease;
}
.req-mode-card:hover { border-color: var(--accent, #A8292F); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 7%, transparent); }
.req-mode-card:active { transform: scale(.985); }
.req-mode-card:focus-visible { outline: 2px solid var(--accent, #A8292F); outline-offset: 2px; }
.req-mode-card[hidden] { display: none; }

.req-mode-card.is-selected {
  border-color: var(--accent, #A8292F);
  background: var(--surface-2, #FBFAF8);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 12%, transparent);
}

.req-mode-card__icon {
  flex: none;
  width: 38px; height: 38px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 11px;
  background: var(--hover, #F2F0EC);
  color: var(--muted, #5B5953);
  transition: background .16s ease, color .16s ease;
}
.req-mode-card__icon svg { width: 21px; height: 21px; }
.req-mode-card.is-selected .req-mode-card__icon { background: var(--accent, #A8292F); color: var(--on-accent, #fff); }

.req-mode-card__main { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.req-mode-card__title { font-size: 13.5px; font-weight: 650; color: var(--text, #1A1917); }
.req-mode-card__desc { font-size: 11.5px; line-height: 1.45; color: var(--muted, #5B5953); }

.req-mode-card__tick {
  position: absolute; top: 11px; right: 11px;
  width: 20px; height: 20px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: var(--accent, #A8292F); color: var(--on-accent, #fff);
  opacity: 0; transform: scale(.4);
  transition: opacity .16s ease, transform .18s var(--ease-overshoot, cubic-bezier(.34,1.3,.64,1));
}
.req-mode-card__tick svg { width: 12px; height: 12px; }
.req-mode-card.is-selected .req-mode-card__tick { opacity: 1; transform: scale(1); }

/* ── Context hint (Feature 10) — subtle, secondary, no warning color ── */
.req-mode-hint {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px;
  border-radius: 10px;
  background: var(--surface-2, #FBFAF8);
  border: 1px solid var(--border, #E8E6E2);
  color: var(--muted, #5B5953);
  font-size: 11.5px; line-height: 1.4;
  animation: reqHintIn .2s ease;
}
.req-mode-hint[hidden] { display: none; }
.req-mode-hint svg { flex: none; width: 15px; height: 15px; color: var(--info, #3B5BA9); }
@keyframes reqHintIn { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: translateY(0); } }

/* ── Apple confirmation sheet (Feature 9) ── */
.req-sheet-overlay {
  position: fixed; inset: 0; z-index: 400;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: var(--scrim, rgba(28,26,23,.36));
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  opacity: 0; transition: opacity .2s ease;
}
.req-sheet-overlay[hidden] { display: none; }
.req-sheet-overlay.is-open { opacity: 1; }

.req-sheet {
  width: 100%; max-width: 340px;
  background: var(--surface, #fff);
  border: 1px solid var(--border, #E8E6E2);
  border-radius: 18px;
  box-shadow: var(--shadow-lg, 0 18px 50px -12px rgba(28,26,23,.28));
  padding: 22px 22px 16px;
  text-align: center;
  transform: scale(.92) translateY(8px);
  opacity: .6;
  transition: transform .24s var(--ease-overshoot, cubic-bezier(.34,1.3,.64,1)), opacity .2s ease;
}
.req-sheet-overlay.is-open .req-sheet { transform: scale(1) translateY(0); opacity: 1; }

.req-sheet__grab {
  display: none;
  width: 38px; height: 4px; margin: 0 auto 14px;
  border-radius: 999px; background: var(--border-strong, #DAD7D2);
}
.req-sheet__title { font-size: 16px; font-weight: 700; color: var(--text, #1A1917); margin: 0 0 6px; }
.req-sheet__body { font-size: 12.5px; line-height: 1.5; color: var(--muted, #5B5953); margin: 0 0 18px; }
.req-sheet__actions { display: flex; gap: 10px; }
.req-sheet__btn {
  flex: 1 1 0;
  padding: 11px 14px;
  border-radius: 11px;
  font: inherit; font-size: 13.5px; font-weight: 650;
  cursor: pointer;
  transition: background .14s ease, border-color .14s ease, transform .08s ease;
}
.req-sheet__btn:active { transform: scale(.97); }
.req-sheet__btn:focus-visible { outline: 2px solid var(--accent, #A8292F); outline-offset: 2px; }
.req-sheet__btn--cancel {
  background: var(--surface-2, #FBFAF8);
  border: 1px solid var(--border-strong, #DAD7D2);
  color: var(--text, #1A1917);
}
.req-sheet__btn--cancel:hover { background: var(--hover, #F2F0EC); }
.req-sheet__btn--confirm {
  background: var(--accent, #A8292F);
  border: 1px solid var(--accent, #A8292F);
  color: var(--on-accent, #fff);
}
.req-sheet__btn--confirm:hover { filter: brightness(1.06); }

/* Mobile: bottom sheet that slides up; swipe-to-dismiss handled in JS. */
@media (max-width: 560px) {
  .req-mode-grid { flex-direction: column; }
  .req-sheet-overlay { align-items: flex-end; padding: 0; }
  .req-sheet {
    max-width: 100%;
    border-radius: 20px 20px 0 0;
    padding: 12px 20px calc(18px + env(safe-area-inset-bottom, 0px));
    transform: translateY(100%);
    opacity: 1;
  }
  .req-sheet-overlay.is-open .req-sheet { transform: translateY(0); }
  .req-sheet__grab { display: block; }
}
