:root {
  /* ------------------------------------------------------------------ */
  /* Design tokens — Phase B. Everything below this comment references  */
  /* these tokens, not hardcoded values. New CSS should follow suit.    */
  /* ------------------------------------------------------------------ */

  /* Spacing scale (4 → 32, base-2 friendly). */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  /* Type scale. Body is --text-sm; use --text-xs for chips/captions,
     --text-lg+ for headings. */
  --text-xs: 12px;
  --text-sm: 14px;
  --text-md: 16px;
  --text-lg: 18px;
  --text-xl: 22px;
  --text-2xl: 28px;

  /* Surfaces — 0 is page bg, 1 is cards/tables, 2 is hover, 3 is elevated. */
  --surface-0: #f7f8fa;
  --surface-1: #fff;
  --surface-2: #f3f6fb;
  --surface-3: #e8edf3;

  /* Borders. */
  --border: #e3e6eb;
  --border-strong: #c8cfd9;

  /* Text. */
  --text: #1d2330;
  --text-muted: #6a7280;
  --text-inverse: #fff;

  /* Semantic colors. Each has a "soft" variant for backgrounds/chips and
     a saturated variant for text/borders. */
  --accent:        #0064d2;
  --accent-hover:  #004fa6;
  --accent-soft:   #e6f0fb;
  --ok:            #0f7a3a;
  --ok-soft:       #e6f4ec;
  --warn:          #d18000;
  --warn-soft:     #fff5e6;
  --danger:        #c92a2a;
  --danger-soft:   #fcecec;
  --info:          #1a4ca8;
  --info-soft:     #e1ecff;
  --wc:            #6b1d8a;
  --wc-soft:       #f0e6f7;

  /* Radii. */
  --radius-sm: 4px;
  --radius:    8px;
  --radius-lg: 12px;

  /* Shadows. */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
  --shadow:    0 2px 8px rgba(0, 0, 0, 0.08);
  --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12);

  /* Backwards-compat aliases — kept until every reference migrates. */
  --bg:          var(--surface-0);
  --surface:     var(--surface-1);
  --muted:       var(--text-muted);
  --accent-text: var(--text-inverse);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
}

#app { min-height: 100vh; display: flex; flex-direction: column; }

/* Topbar — site brand, page nav, user menu. Stays at the top of viewport
   intentionally non-sticky so long table scrolls aren't choked. */
header.topbar {
  display: flex; align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  background: var(--surface-1);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}
/* Brand area — Altamedix mark + wordmark. Clickable; routes to /map.
   Acts as the visual anchor for the topbar; the right side of it sits
   against the nav with a subtle vertical separator. */
header.topbar .brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  text-decoration: none;
  color: var(--text);
  padding-right: var(--space-4);
  border-right: 1px solid var(--border);
  height: 36px;
}
header.topbar .brand:hover .brand-line { color: var(--accent); }
header.topbar .brand-logo {
  width: 28px; height: 28px;
  display: block;
  flex-shrink: 0;
}
/* Two-line lockup: "{Org} Dispatch" over "by route.care". */
header.topbar .brand-wordmark {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  line-height: 1.15;
  white-space: nowrap;
}
header.topbar .brand-line {
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: -0.01em;
  transition: color 0.12s ease;
}
header.topbar .brand-sub {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-muted);
  letter-spacing: 0.02em;
  opacity: 0.75;
}
header.topbar .brand-strong {
  font-weight: 700;
  color: var(--text);
}
/* Legacy h1 selector kept in case anything still references it. */
header.topbar h1 {
  margin: 0;
  font-size: var(--text-md);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}
@media (max-width: 720px) {
  header.topbar .brand { padding-right: var(--space-2); border-right: 0; }
  header.topbar .brand-wordmark { display: none; }
  header.topbar { gap: var(--space-2); padding: var(--space-2) var(--space-3); }
}

/* Hamburger toggle — only visible at narrow widths. */
.nav-toggle { display: none; }
@media (max-width: 720px) {
  .nav-toggle { display: inline-flex; }
}

/* At narrow widths the nav becomes a slide-in left drawer. The shell
   listens for Escape, link clicks, and backdrop clicks to close it. */
.nav-backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.4);
  z-index: 250;
  animation: nav-backdrop-in 0.15s ease-out;
}
.nav-backdrop[hidden] { display: none; }
@keyframes nav-backdrop-in {
  from { opacity: 0; } to { opacity: 1; }
}
@media (max-width: 720px) {
  header.topbar nav {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: 280px; max-width: 80vw;
    background: var(--surface-1);
    border-right: 1px solid var(--border-strong);
    box-shadow: var(--shadow-lg);
    flex-direction: column;
    align-items: stretch;
    padding: var(--space-5) var(--space-3) var(--space-3);
    gap: var(--space-1);
    transform: translateX(-100%);
    transition: transform 0.2s ease-out;
    z-index: 300;
    overflow-y: auto;
  }
  body.nav-open header.topbar nav { transform: translateX(0); }
  header.topbar nav a {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-md);
    border-radius: var(--radius);
  }
  /* Hide backdrop above breakpoint (desktop nav is inline). */
}
@media (min-width: 721px) {
  .nav-backdrop { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  header.topbar nav { transition: none; }
  .nav-backdrop { animation: none; }
}
header.topbar nav {
  display: flex; gap: var(--space-1);
  flex-wrap: wrap;
}
header.topbar nav a {
  text-decoration: none;
  color: var(--text-muted);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  font-weight: 500;
  font-size: var(--text-sm);
  transition: background-color 0.12s ease, color 0.12s ease;
}
header.topbar nav a:hover { background: var(--surface-2); color: var(--text); }
header.topbar nav a.active {
  background: var(--accent-soft);
  color: var(--accent);
}
header.topbar .spacer { flex: 1; }

/* User profile dropdown — replaces the formerly-separate user span and
   sign-out button. Holds Settings, Help, and Sign out behind a single
   trigger to keep the topbar clean. */
.user-menu { position: relative; }
.user-menu-trigger {
  display: inline-flex; align-items: center; gap: var(--space-2);
  background: transparent;
  border: 1px solid var(--border);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  cursor: pointer;
  font: inherit;
  font-size: var(--text-sm);
  color: var(--text);
  transition: background-color 0.12s ease, border-color 0.12s ease;
}
.user-menu-trigger:hover,
.user-menu-trigger[aria-expanded="true"] {
  background: var(--surface-2);
  border-color: var(--border-strong);
}
/* Avatar circle in the topbar trigger — replaces the previous full-email
   span so the right side of the topbar stays compact. The email itself
   moves to a header row inside the dropdown so account identity is one
   click away rather than missing. */
.user-menu-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}
/* Header row inside the dropdown — full email shown muted at the top so
   the user can confirm which account they're signed into. */
.user-menu-header {
  padding: var(--space-2) var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-sm);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.user-menu-chev { transition: transform 0.12s ease; }
.user-menu-trigger[aria-expanded="true"] .user-menu-chev { transform: rotate(180deg); }

.user-menu-dropdown {
  position: absolute;
  top: calc(100% + var(--space-1));
  right: 0;
  min-width: 220px;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-1);
  z-index: 200;
  display: flex; flex-direction: column;
  animation: user-menu-in 0.12s ease-out;
}
.user-menu-dropdown[hidden] { display: none; }
@keyframes user-menu-in {
  from { transform: translateY(-4px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .user-menu-dropdown { animation: none; }
  .user-menu-chev { transition: none; }
}

.user-menu-item {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  text-decoration: none;
  color: var(--text);
  font: inherit;
  font-size: var(--text-sm);
  background: transparent; border: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-align: left;
  width: 100%;
}
.user-menu-item .icon { color: var(--text-muted); }
.user-menu-item:hover { background: var(--surface-2); }
.user-menu-item.danger { color: var(--danger); }
.user-menu-item.danger:hover { background: var(--danger-soft); }
.user-menu-item.danger .icon { color: var(--danger); }
.user-menu-divider {
  height: 1px;
  background: var(--border);
  margin: var(--space-1) 0;
}

/* Nav-bar "Manage" dropdown — same open/close/outside-click behavior as
   .user-menu above, anchored left and inline with the other top nav links
   instead of at the far right of the topbar. */
.nav-dropdown { position: relative; display: inline-flex; }
.nav-dropdown-trigger {
  display: inline-flex; align-items: center; gap: 0.3rem;
  background: transparent; border: 0;
  padding: var(--space-2) var(--space-3);
  font: inherit; font-size: inherit;
  color: var(--text-muted);
  cursor: pointer;
}
.nav-dropdown-trigger:hover,
.nav-dropdown-trigger[aria-expanded="true"] { color: var(--text); }
.nav-dropdown-trigger.active { color: var(--accent); font-weight: 600; }
.nav-dropdown-chev { transition: transform 0.12s ease; }
.nav-dropdown-trigger[aria-expanded="true"] .nav-dropdown-chev { transform: rotate(180deg); }
.nav-dropdown-menu {
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 0;
  min-width: 180px;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-1);
  z-index: 200;
  display: flex; flex-direction: column;
}
.nav-dropdown-menu[hidden] { display: none; }
.nav-dropdown-item {
  display: block;
  padding: var(--space-2) var(--space-3);
  text-decoration: none;
  color: var(--text);
  font-size: var(--text-sm);
  border-radius: var(--radius-sm);
}
.nav-dropdown-item:hover, .nav-dropdown-item.active { background: var(--surface-2); }

/* Address-autocomplete suggestion list (participant/driver/office address fields) */
/* `label` in form.form is display:grid, which stretches a direct child to
   fill the column for free -- wrapping the address <input> in this div for
   the suggestion list broke that (the <input> itself isn't a grid item
   anymore), so it needs the same single-column grid to pass the stretch
   through to the input. */
.addr-suggest-wrap { position: relative; display: grid; }
.addr-suggest-list {
  position: absolute;
  top: calc(100% + var(--space-1));
  left: 0;
  right: 0;
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-1);
  z-index: 50;
  max-height: 240px;
  overflow-y: auto;
}
.addr-suggest-list[hidden] { display: none; }
.addr-suggest-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: var(--space-2) var(--space-3);
  background: none;
  border: none;
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--text);
  border-radius: var(--radius-sm);
}
.addr-suggest-item:hover, .addr-suggest-item.active { background: var(--surface-2); }

main {
  flex: 1;
  padding: var(--space-5);
  max-width: 1200px; width: 100%;
  margin: 0 auto;
}

/* Page header — title + right-aligned actions cluster. Wraps the actions
   to the next row on narrow viewports. The actions area can be a single
   button, a wrapping div of buttons + inputs, or an h2-sibling element
   with any layout — they all stay clustered to the right. */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
}
.page-header h2 {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--text);
}
.page-header h2 .page-count {
  /* "(N)" suffix on a page title, muted. */
  margin-left: var(--space-2);
  font-size: var(--text-md);
  font-weight: 500;
  color: var(--text-muted);
}
.page-header-title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
/* Optional second-row subtitle below the h2. Pages that want a description
   add an <p class="page-subtitle"> sibling. */
.page-subtitle {
  flex-basis: 100%;
  margin: 0;
  color: var(--text-muted);
  font-size: var(--text-sm);
  line-height: 1.45;
  order: 99; /* always last so it sits below the actions row when wrapped */
}
/* When the right-hand cluster is its own <div>, make it a flex row so
   per-button spacing is consistent regardless of what's inside (buttons,
   inputs, selects). The legacy ' ' text-node separators between buttons
   are harmless but redundant once gap kicks in. */
.page-header > div:not(.page-subtitle) {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}
@media (max-width: 600px) {
  .page-header { gap: var(--space-2); }
  .page-header h2 { font-size: var(--text-lg); }
}

/* Page sub-toolbar — sits below the page-header, holds day-level
   operations that don't belong in the title row (generate, reset, bulk
   actions). Visually distinct from the main page chrome. */
.page-toolbar {
  display: flex; align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* Button hierarchy:
   .primary   — page-level CTA (one per modal/page typically)
   .secondary — regular action (Edit, Cancel, etc.)
   .danger    — destructive (Delete, Reset)
   .ghost     — tertiary / icon-only; transparent until hover. */
button.primary,
button.secondary,
button.danger,
button.ghost {
  font: inherit; cursor: pointer;
  border-radius: var(--radius);
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
button.primary {
  background: var(--accent); color: var(--text-inverse);
  border: 1px solid var(--accent);
  padding: var(--space-2) var(--space-4);
  font-weight: 600;
}
button.primary:hover  { background: var(--accent-hover); border-color: var(--accent-hover); }
button.primary:disabled,
button.primary[aria-disabled="true"] {
  background: var(--surface-3); border-color: var(--surface-3);
  color: var(--text-muted); cursor: not-allowed;
}
button.secondary {
  background: var(--surface-1); color: var(--text);
  border: 1px solid var(--border);
  padding: calc(var(--space-2) - 1px) var(--space-3);
}
button.secondary:hover { background: var(--surface-2); border-color: var(--border-strong); }
button.danger {
  background: transparent; color: var(--danger);
  border: 1px solid var(--danger);
  padding: calc(var(--space-2) - 2px) var(--space-3);
}
button.danger:hover  { background: var(--danger-soft); }
button.ghost {
  background: transparent; color: var(--text-muted);
  border: 1px solid transparent;
  padding: calc(var(--space-2) - 1px) var(--space-3);
}
button.ghost:hover   { background: var(--surface-2); color: var(--text); }

/* Icons — drop-in SVG nodes that pick up the parent's color via
   stroke="currentColor". Inline-flex so they align with text in buttons. */
.icon {
  display: inline-block;
  flex-shrink: 0;
  vertical-align: -2px;
}
button .icon { margin-right: 0.35em; }
button.icon-only {
  padding: var(--space-2);
  line-height: 1;
}
button.icon-only .icon { margin-right: 0; }
button.icon-only[title]:hover::after {
  /* Optional CSS tooltip — relies on the title attribute, no JS. Hidden
     by default; shows on hover for a-11y backup to native tooltip. */
}

table.list {
  width: 100%; border-collapse: collapse;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius);
  /* No overflow: hidden here — it breaks `position: sticky` on <th> in Chrome
     and Safari. Corner-clipping now lives on .table-scroll (overflow:auto +
     border-radius), which clips the table's square corners cleanly. */
}
table.list th, table.list td {
  text-align: left; padding: 0.4rem 0.85rem;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
table.list th {
  background: var(--bg);
  font-weight: 600; font-size: 0.8rem; text-transform: uppercase; color: var(--muted);
  /* Sticky header — survives scroll inside any positioned parent and the page. */
  position: sticky; top: 0; z-index: 2;
}
table.list tr:last-child td { border-bottom: none; }
table.list tbody tr { transition: background-color 0.08s ease-out; }
table.list tbody tr:hover { background: #f3f6fb; }
table.list .actions { text-align: right; white-space: nowrap; }
table.list .actions button { margin-left: 0.4rem; }
table.list .actions-col { width: 1%; }
table.list .cell-name { font-weight: 600; }

/* Sortable column headers — small ↑/↓ glyph on the active column. */
table.list th.sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
table.list th.sortable:hover { color: var(--text); }
table.list th.sortable::after {
  content: ''; display: inline-block; width: 0; height: 0;
  margin-left: 0.4rem; vertical-align: middle;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  opacity: 0.25;
  border-top: 4px solid currentColor;
}
table.list th.sortable[data-sort=asc]::after  { border-top: none; border-bottom: 4px solid currentColor; opacity: 1; }
table.list th.sortable[data-sort=desc]::after { opacity: 1; }

/* Search bar above tables. */
.table-bar {
  display: flex; align-items: center; gap: 0.75rem;
  margin-bottom: 0.5rem;
}
.table-search {
  flex: 0 1 440px;
  min-width: 280px;
  padding: 0.55rem 0.85rem 0.55rem 2.1rem;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2393a0b3' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><line x1='21' y1='21' x2='16.65' y2='16.65'/></svg>") no-repeat 0.7rem center;
  background-size: 16px 16px;
  font: inherit;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.table-search:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.18);
}
.table-count { color: var(--muted); font-size: 0.85rem; }

/* Stats + search in a single header strip — stats on the left, search on
   the right. Wraps on narrow viewports so neither piece clips. */
.page-stats-row {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  margin: 0 0 var(--space-3);
}
.page-stats-row .table-bar { margin-bottom: 0; }
.page-stats-row .page-stats { margin: 0; }
/* Stats bar — operational counts above the table. Mirrors .table-bar's
   density so it reads as a header strip, not a body element. */
.page-stats {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem;
  margin: 0 0 var(--space-3);
  color: var(--text);
  font-size: 0.9rem;
}
.page-stats .sep { color: var(--border); user-select: none; }
.page-stats .muted { color: var(--text-muted); }
.page-stats .ok { color: var(--ok, #137333); }
.page-stats .warn { color: var(--warn, #b06000); }

/* Single-line address with ellipsis; full address in title tooltip. */
table.list td.cell-addr {
  max-width: 320px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Keep names on one line. The horizontal scroll wrapper handles overflow. */
table.list td.cell-name { white-space: nowrap; font-weight: 600; }

/* Sticky-left columns — pin to the left edge of the .table-scroll container
   so they stay visible during horizontal scroll. Background must be opaque
   so the underlying scrolled content doesn't bleed through. Both th and td
   need the rule; the th's existing sticky-top rule composes with this. */
table.list th.sticky-left, table.list td.sticky-left {
  position: sticky; left: 0;
  background: var(--surface);
  z-index: 1;
}
table.list th.sticky-left { z-index: 3; background: var(--bg); }
table.list tbody tr:hover td.sticky-left { background: #f3f6fb; }
table.list tbody tr.selected td.sticky-left { background: #e8f0fe; }

/* Day-of-week pills — five small badges, dimmed for days the participant
   doesn't ride. Single-line so the column stays compact. */
.day-pills { display: inline-flex; gap: 2px; }
.day-pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px;
  padding: 0 5px;
  border-radius: 4px;
  font-size: 0.72rem; font-weight: 600;
  line-height: 1;
}
.day-pill.preferred {
  background: rgba(26, 115, 232, 0.95);
  border: 1px solid rgba(26, 115, 232, 0.95);
  color: #fff;
}
.day-pill.authorized-only {
  background: #fff;
  border: 1px solid #c7ced8;
  color: #5a6575;
}
.day-pill.unauthorized {
  opacity: 0.25;
  border: 1px dashed #c7ced8;
  color: #8a93a3;
}

/* Columns-visibility toggle — small button + checkbox dropdown that lives
   inside the .table-bar next to the search input. Mirrors .row-menu styling. */
.columns-toggle-wrap { position: relative; display: inline-block; }
.columns-toggle {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.45rem 0.7rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font: inherit; cursor: pointer;
}
.columns-toggle:hover { border-color: var(--accent); color: var(--accent); }
.columns-toggle[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); }
.columns-menu {
  position: absolute; right: 0; top: calc(100% + 4px);
  z-index: 30;
  min-width: 180px;
  background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.12);
  padding: 0.3rem;
  display: flex; flex-direction: column;
}
.columns-menu[hidden] { display: none; }
.columns-menu-item {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.35rem 0.5rem;
  border-radius: 4px;
  font: inherit; cursor: pointer;
}
.columns-menu-item:hover { background: var(--bg); }
.columns-menu-item input { margin: 0; }

/* Bounded-height table. Caps width at 95% and height at (viewport − page
   chrome) so the table body scrolls inside its own pane instead of pushing
   the page-header off-screen. Tune the calc() if the chrome height changes:
   topbar ~56px + main padding 48px + page-header ~60px + stats-row ~40px
   + buffer for sel.bar/margins ~40px ≈ 240px. Sticky thead anchors to the
   top of this scroll container automatically. */
.table-scroll {
  max-height: calc(100vh - 240px);
  overflow: auto;
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05), 0 8px 20px rgba(15, 23, 42, 0.08);
}
.table-scroll > table.list { width: 100%; min-width: 0; }

/* Kebab/row-menu — trigger lives inline in the row; dropdown is portaled to
   document.body with position:fixed so it escapes any clipping ancestor
   (e.g. .table-scroll's overflow:auto). Coords are set in JS from
   trigger.getBoundingClientRect(). */
.row-menu { display: inline-block; }
.row-menu-trigger {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px;
  padding: 0; border: 1px solid transparent; border-radius: var(--radius);
  background: transparent; color: var(--text-muted);
  cursor: pointer;
}
.row-menu-trigger:hover { background: var(--surface); color: var(--text); }
.row-menu-trigger[aria-expanded="true"] {
  background: var(--surface); border-color: var(--border); color: var(--text);
}
.row-menu-dropdown {
  position: fixed;
  z-index: 1000;
  min-width: 160px;
  background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.18);
  padding: 0.25rem;
  display: flex; flex-direction: column;
}
.row-menu-item {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.45rem 0.6rem;
  background: transparent; border: 0; border-radius: 4px;
  color: var(--text);
  font: inherit; text-align: left; cursor: pointer;
}
.row-menu-item:hover { background: var(--bg); }
.row-menu-item.danger { color: var(--danger); }
.row-menu-item.danger:hover { background: var(--danger-soft); }

/* Multi-select column + selected row state. */
table.list .select-col {
  width: 36px;
  text-align: center;
  padding-left: var(--space-3);
  padding-right: var(--space-2);
}
table.list .select-col input[type=checkbox] {
  margin: 0; cursor: pointer; width: 16px; height: 16px;
}
table.list tbody tr.selected { background: var(--accent-soft); }
table.list tbody tr.selected:hover { background: #d8e7f8; }

/* Floating bulk action bar — appears bottom-center when ≥1 row selected. */
.bulk-bar {
  position: fixed;
  bottom: var(--space-5);
  left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--surface-1);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2) var(--space-3);
  z-index: 100;
  animation: bulk-bar-slide-up 0.16s ease-out;
}
.bulk-bar[hidden] { display: none; }
.bulk-bar .count {
  font-weight: 600;
  padding-right: var(--space-2);
  border-right: 1px solid var(--border);
}
@keyframes bulk-bar-slide-up {
  from { transform: translate(-50%, 8px); opacity: 0; }
  to   { transform: translate(-50%, 0);   opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .bulk-bar { animation: none; }
}

.empty { padding: 2rem; text-align: center; color: var(--muted); background: var(--surface); border: 1px dashed var(--border); border-radius: var(--radius); }
.empty p { margin: 0.3rem 0; }
.empty p:first-child { font-weight: 600; color: var(--text); }
.empty code { background: var(--bg); padding: 0 0.3em; border-radius: 3px; }

/* Global focus ring — :focus-visible only fires on keyboard nav, so mouse
   users don't see it. WCAG-compliant accent ring with 2px offset. */
*:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* Toast notifications — non-blocking feedback that stacks bottom-right. */
.toast-host {
  position: fixed;
  bottom: 1rem; right: 1rem;
  display: flex; flex-direction: column; gap: 0.5rem;
  z-index: 9999;
  pointer-events: none;
}
.toast {
  pointer-events: auto;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-left: 4px solid var(--muted);
  border-radius: var(--radius);
  box-shadow: 0 2px 12px rgba(0,0,0,0.12);
  padding: 0.6rem 0.75rem 0.6rem 0.9rem;
  min-width: 240px; max-width: 420px;
  display: flex; align-items: center; gap: 0.6rem;
  animation: toast-slide-in 0.18s ease-out;
}
.toast-msg { flex: 1; }
.toast-close {
  background: transparent; border: 0; cursor: pointer;
  color: var(--muted); font-size: 1.25rem; line-height: 1;
  padding: 0 0.2rem;
}
.toast-close:hover { color: var(--text); }
.toast.toast-ok     { border-left-color: var(--ok); }
.toast.toast-warn   { border-left-color: var(--warn); }
.toast.toast-danger { border-left-color: var(--danger); }
.toast.toast-info   { border-left-color: var(--accent); }
@keyframes toast-slide-in {
  from { transform: translateX(20px); opacity: 0; }
  to   { transform: translateX(0);    opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
  table.list tbody tr { transition: none; }
}

.modal-backdrop {
  position: fixed; inset: 0; background: rgba(0,0,0,0.4);
  display: flex; align-items: center; justify-content: center;
  z-index: 100;
}
.modal {
  background: var(--surface-1); border-radius: var(--radius);
  padding: var(--space-5);
  max-width: 540px; width: 100%;
  max-height: 90vh; overflow-y: auto;
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--border);
}
/* Wider variant for complex forms with many fields. */
.modal-lg { max-width: 760px; }
.modal h3 {
  margin: 0 0 var(--space-4);
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text);
}
.modal .actions {
  margin-top: var(--space-5);
  display: flex; justify-content: flex-end;
  gap: var(--space-2);
}
.modal .actions button[disabled] { cursor: wait; }
/* Two-column rows inside forms — wraps to one column at narrow widths. */
.modal .form .row {
  display: flex; gap: var(--space-3); align-items: flex-start;
}
.modal .form .row > label { flex: 1 1 0; min-width: 0; }
.modal .form .row > label > input,
.modal .form .row > label > select,
.modal .form .row > label > textarea { width: 100%; }
@media (max-width: 560px) {
  .modal .form .row { flex-direction: column; gap: var(--space-2); }
}
.modal .modal-text { white-space: pre-wrap; margin: 0; }
.modal .outlier-list { margin-top: 0.75rem; padding-left: 1.25rem; }
.modal .outlier-list li { margin-bottom: 0.4rem; }
.modal .progress-bar {
  margin: 0.75rem 0 0.5rem;
  height: 0.5rem;
  background: var(--surface, #e8edf3);
  border-radius: 999px;
  overflow: hidden;
}
.modal .progress-fill {
  height: 100%;
  background: var(--accent, #2563eb);
  transition: width 0.25s ease-out;
}

/* Map toolbar — the only chrome above the canvas. Was previously split
   across a page-header (h2 + date row) and a separate map-filters strip;
   now everything lives in one wrap-friendly row to give the map ~60px
   back. Sections are visually separated by a vertical rule rather than
   wide gaps, so the row reads as a single toolbar at a glance. */
.map-filters {
  display: flex; flex-wrap: wrap;
  gap: 0.4rem 1rem;
  align-items: center;
  font-size: 0.85rem;
}
.map-filters .filter-group {
  display: inline-flex; align-items: center; flex-wrap: wrap; gap: 0.35rem;
}
/* Vertical divider between toolbar sections — drawn on the left so the
   first group (date) has no rule to its left. */
.map-filters .filter-group + .filter-group {
  padding-left: 1rem;
  border-left: 1px solid var(--border);
}
@media (max-width: 720px) {
  /* When sections wrap to their own rows the vertical rules look orphaned;
     suppress them on narrow viewports. */
  .map-filters .filter-group + .filter-group { padding-left: 0; border-left: 0; }
}
.map-filters .filter-label { color: var(--muted); margin-right: 0.25rem; }
.map-filters .filter-empty { color: var(--muted); font-style: italic; }

/* Date input lives in the date group, styled to sit flush with the
   adjacent segmented Today/Tomorrow control. */
.map-filters .filter-date-input {
  font: inherit;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

/* Segmented controls (Today/Tomorrow, Direction, Shifts) — same pill
   strip the old design used, kept for direction/shifts/date toggles. */
.map-filters .filter-seg {
  display: inline-flex; gap: 0;
  border-radius: 999px; overflow: hidden;
  border: 1px solid var(--border);
}
.map-filters .filter-seg button {
  cursor: pointer; font: inherit;
  background: var(--surface); color: var(--text);
  border: 0;
  border-right: 1px solid var(--border);
  padding: 0.25rem 0.7rem;
  line-height: 1.2;
}
.map-filters .filter-seg button:last-child { border-right: none; }
.map-filters .filter-seg button:hover { background: var(--bg); }
.map-filters .filter-seg button.on {
  background: var(--accent); color: var(--accent-text);
}

/* Driver chips — lightweight (dot + name, no border). Off-state is
   muted opacity so the row stays quiet at rest; clicking a name lights
   it up to full color. */
.map-filters .filter-chip {
  cursor: pointer; font: inherit;
  background: transparent; color: var(--text);
  border: 0;
  padding: 0.2rem 0.4rem;
  border-radius: var(--radius);
  display: inline-flex; align-items: center; gap: 0.4rem;
  line-height: 1.2;
  opacity: 0.45;
  transition: opacity 0.12s ease, background-color 0.12s ease;
}
.map-filters .filter-chip:hover { background: var(--surface-2); opacity: 0.85; }
.map-filters .filter-chip.on { opacity: 1; font-weight: 500; }
.map-filters .filter-chip .chip-dot {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* "Show all / Hide all" — a quiet text-style link, not a button-shaped
   control. Lives beside the Drivers: label. */
.map-filters .filter-bulk {
  cursor: pointer; font: inherit;
  background: transparent;
  border: 0;
  padding: 0.2rem 0.3rem;
  font-size: 0.78rem;
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.map-filters .filter-bulk:hover { color: var(--text); }

/* Driver dropdown — single pill-shaped trigger ("Drivers (3/10) ▾") that
   opens an absolutely-positioned popover with a per-driver checklist plus a
   Show all / Hide all footer. Keeps the toolbar to one row on a busy day. */
.map-filters .filter-dropdown { position: relative; display: inline-block; }
.map-filters .filter-dropdown-trigger {
  cursor: pointer; font: inherit;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.25rem 0.7rem;
  display: inline-flex; align-items: center; gap: 0.4rem;
  line-height: 1.2;
}
.map-filters .filter-dropdown-trigger:hover { background: var(--bg); }
.map-filters .filter-dropdown.open .filter-dropdown-trigger {
  background: var(--bg);
  border-color: var(--accent);
}
.map-filters .filter-dropdown-caret { font-size: 0.7em; opacity: 0.7; }
.map-filters .filter-dropdown-label { font-weight: 500; }
.map-filters .filter-dropdown-panel {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  z-index: 10;
  min-width: 14rem;
  max-height: 22rem;
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 6px 20px rgba(0,0,0,0.12);
  overflow: hidden;
}
/* Author display:flex above outranks the user-agent [hidden] -> display:none
   rule, so the panel would stay visible even after setting .hidden = true.
   Restore the hide behaviour explicitly. */
.map-filters .filter-dropdown-panel[hidden] { display: none; }
.map-filters .filter-dropdown-list {
  overflow-y: auto;
  padding: 0.2rem 0;
}
.map-filters .filter-dropdown-row {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.3rem 0.7rem;
  cursor: pointer;
  font-size: 0.9rem;
  line-height: 1.2;
}
.map-filters .filter-dropdown-row:hover { background: var(--bg); }
.map-filters .filter-dropdown-row input[type="checkbox"] {
  margin: 0;
  cursor: pointer;
}
.map-filters .filter-dropdown-row .chip-dot {
  display: inline-block;
  width: 10px; height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}
.map-filters .filter-dropdown-row-name {
  flex: 1;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Per-row replay trigger (▶) — quiet until hovered, so the row still reads
   primarily as a show/hide checkbox at a glance. */
.map-filters .filter-dropdown-replay {
  cursor: pointer; font: inherit;
  background: transparent; color: var(--muted);
  border: 0;
  border-radius: var(--radius-sm, 4px);
  width: 1.4rem; height: 1.4rem;
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.7rem;
}
.map-filters .filter-dropdown-replay:hover {
  background: var(--surface-2);
  color: var(--accent);
}
.map-filters .filter-dropdown-footer {
  display: flex; gap: 0.4rem;
  padding: 0.35rem 0.6rem;
  border-top: 1px solid var(--border);
  background: var(--bg);
}
.map-filters .filter-dropdown-footer .filter-bulk { padding: 0.15rem 0.3rem; }

/* Combined "View" dropdown (direction / shifts / participants) — same
   panel shell as the Drivers dropdown, but stacked labeled sections instead
   of a checklist. Pushed to the toolbar's right edge since it's the last,
   most "settings-like" control. */
.map-filters .filter-view { margin-left: auto; }
.map-filters .filter-view-panel { min-width: 12rem; padding: 0.5rem 0.7rem; gap: 0.6rem; }
.map-filters .filter-dropdown-section-label {
  font-size: 0.75rem; color: var(--muted);
  margin-bottom: 0.3rem;
}

/* Map page fills the leftover vertical space below the topbar — no magic
   number for the canvas height. Chain: #app (column flex, min-height:100vh)
   → main (becomes column flex on the map page via :has) → .map-page
   (flex:1, column flex) → .map-canvas-wrap (flex:1). */
main:has(> .map-page) {
  display: flex;
  flex-direction: column;
}
.map-page {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.map-canvas-wrap {
  position: relative;
  margin-top: 0.75rem;
  flex: 1;
  min-height: 360px;
}
.map-canvas {
  /* The wrap is flex-sized (flex:1) which has a definite resolved height,
     but percentage heights on children of flex-sized parents don't resolve
     reliably (depends on the spec edition + browser). Using absolute fill
     against the relatively-positioned wrap is unambiguous and sidesteps it. */
  position: absolute;
  inset: 0;
  border-radius: var(--radius); border: 1px solid var(--border);
  overflow: hidden;
}

/* Map controls — keep them compact and unobtrusive. The attribution
   stays visible (OSM tile policy) but shrinks to a tiny ⓘ that
   expands on click. Zoom controls get a softer style that fits the
   token palette. */
.maplibregl-ctrl-attrib.maplibregl-compact {
  min-height: 22px; min-width: 22px;
  background: rgba(255, 255, 255, 0.75);
  margin: var(--space-2);
  border-radius: var(--radius-sm);
  font-size: 10px;
  box-shadow: var(--shadow-sm);
}
.maplibregl-ctrl-attrib.maplibregl-compact:not(.maplibregl-compact-show) {
  padding: 0;
  background-color: rgba(255, 255, 255, 0.85);
}
/* Force the inner attribution text to stay hidden until the (i) button is
   clicked. MapLibre's compact mode is supposed to do this on its own, but
   the rule doesn't always land at our canvas widths, leaving the full
   "© OpenStreetMap contributors" string visible. Click still reveals it
   (MapLibre adds .maplibregl-compact-show), so we remain OSM-policy
   compliant — attribution is one click away, not stripped. */
.maplibregl-ctrl-attrib.maplibregl-compact:not(.maplibregl-compact-show) .maplibregl-ctrl-attrib-inner {
  display: none;
}
.maplibregl-ctrl-attrib-inner { padding: 0 var(--space-2); }
.maplibregl-ctrl-attrib a { color: var(--accent); text-decoration: none; }
.maplibregl-ctrl-attrib a:hover { text-decoration: underline; }

.maplibregl-ctrl-group {
  border-radius: var(--radius) !important;
  box-shadow: var(--shadow-sm) !important;
  border: 1px solid var(--border);
  overflow: hidden;
}
.maplibregl-ctrl-group button {
  width: 28px !important; height: 28px !important;
}
.map-loading {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.6rem;
  background: rgba(255, 255, 255, 0.88);
  border-radius: var(--radius);
  z-index: 10;
  color: var(--muted, #555);
  font-size: 0.95rem;
}
.map-loading.error { color: #c0392b; }
.map-loading-spinner {
  width: 28px; height: 28px;
  border: 3px solid var(--border, #e1e6ee);
  border-top-color: var(--accent, #2563eb);
  border-radius: 50%;
  animation: map-loading-spin 0.85s linear infinite;
}
.map-loading.error .map-loading-spinner { display: none; }
@keyframes map-loading-spin {
  to { transform: rotate(360deg); }
}
.map-legend {
  margin-top: 0.5rem; display: flex; gap: 1rem; flex-wrap: wrap;
  font-size: 0.85rem; color: var(--muted);
}
.map-legend > span { display: inline-flex; align-items: center; gap: 0.35rem; }
/* Office (star) / driver-home (diamond) markers -- see makeOfficeMarker /
   makeHomeMarker in app.js. */
.marker-shape {
  display: block;
  cursor: pointer;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.35));
}
.map-dot {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,0.25);
  display: inline-block; cursor: pointer;
}
.map-dot.live {
  width: 18px; height: 18px;
  background: #d4001a !important;
  box-shadow: 0 0 0 1px rgba(0,0,0,0.25), 0 0 0 6px rgba(212, 0, 26, 0.18);
  animation: live-pulse 1.6s ease-in-out infinite;
}
@keyframes live-pulse {
  0%, 100% { box-shadow: 0 0 0 1px rgba(0,0,0,0.25), 0 0 0 6px rgba(212, 0, 26, 0.20); }
  50%      { box-shadow: 0 0 0 1px rgba(0,0,0,0.25), 0 0 0 14px rgba(212, 0, 26, 0); }
}
/* No updates for a while: stop pulsing, fade — hover shows "last seen". */
.map-dot.live.stale {
  animation: none;
  opacity: 0.4;
  filter: grayscale(0.7);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.25);
}
/* Day-replay marker — a ringed dot distinct from the pulsing live dot. */
.map-dot.replay-marker {
  width: 18px; height: 18px;
  box-shadow: 0 0 0 2px #fff, 0 0 0 5px rgba(0,0,0,0.35);
}
/* Day-replay toolbar controls — appears once a driver's breadcrumb is
   loaded via the ▶ icon in the Drivers checklist; hidden otherwise. */
.map-filters .filter-replay .replay-driver-label { font-weight: 500; }
.map-filters .filter-replay .replay-scrub { width: 12rem; max-width: 40vw; cursor: pointer; }
.map-filters .filter-replay .replay-play {
  min-width: 2rem; padding: 0.15rem 0.4rem;
  border: 1px solid var(--border); border-radius: 4px;
  background: var(--bg); color: var(--text); cursor: pointer;
}
.map-filters .filter-replay .replay-play:hover { color: var(--text); border-color: var(--muted); }
.map-filters .filter-replay .replay-time { font-variant-numeric: tabular-nums; min-width: 4.5rem; }
.map-filters .filter-replay .replay-note { font-size: 0.85em; }

.leg-swatch { width: 22px; height: 0; display: inline-block; vertical-align: middle; }
.leg-swatch.am { border-top: 3px solid #555; }
.leg-swatch.pm { border-top: 3px dashed #555; }

/* Hover tooltip for route lines + stop dots — follows the cursor, never
   intercepts clicks (stop dots still open their own popup on click). */
.map-hover-tip {
  position: absolute;
  z-index: 20;
  pointer-events: none;
  max-width: 220px;
  padding: 0.35rem 0.6rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 4px);
  box-shadow: var(--shadow-sm, 0 2px 8px rgba(0,0,0,0.15));
  font-size: 0.8rem;
  line-height: 1.35;
}
.map-hover-tip strong { display: block; }
.map-hover-tip .muted { color: var(--muted); }

/* Route-stop dots on the map (numbered, colored by status) */
.route-stop {
  width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,0.25);
  background: #888; color: #fff;
  font-size: 10px; font-weight: 700; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background 0.2s ease, opacity 0.2s ease;
}
.route-stop.status-pending        { background: #888; }
.route-stop.status-picked_up      { background: #0a7e2a; }
.route-stop.status-no_show        { background: #c43030; opacity: 0.55; text-decoration: line-through; }
.route-stop.status-self_transport { background: #b08400; opacity: 0.7; }
/* Multi-stop pile at one address: stretch into a pill so labels like
   "6–9" or "3,7" don't get clipped by the 20×20 dot. */
.route-stop.cluster {
  width: auto; min-width: 24px;
  padding: 0 6px;
  border-radius: 10px;
}

/* Modern teardrop pin for single (non-cluster) stops -- see makeStopPin in
   app.js. .pin neutralizes the plain dot's circle/background/box-shadow
   above (the SVG child supplies its own shape and driver-color ring
   instead); status still drives fill color, same as the plain dot, so the
   at-a-glance pending/picked-up/no-show read stays identical. */
.route-stop.pin {
  width: auto; height: auto; border-radius: 0;
  border: none; box-shadow: none; background: none;
  display: block;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.35));
}
.route-stop.pin .stop-fill        { fill: #888; }
.route-stop.pin.status-pending    .stop-fill { fill: #888; }
.route-stop.pin.status-picked_up  .stop-fill { fill: #0a7e2a; }
.route-stop.pin.status-no_show    .stop-fill { fill: #c43030; }
.route-stop.pin.status-self_transport .stop-fill { fill: #b08400; }
.route-stop.pin.status-no_show        { opacity: 0.55; }
.route-stop.pin.status-self_transport { opacity: 0.7; }
.route-stop.pin .stop-label {
  font: 700 12px -apple-system, BlinkMacSystemFont, sans-serif;
  fill: #fff; text-anchor: middle; pointer-events: none;
}
.route-stop.pin.status-no_show .stop-label { text-decoration: line-through; }

/* Daily Ops dashboard --------------------------------------------------- */

.today-grid th, .today-grid td { vertical-align: middle; }
.today-grid .today-cell { padding: 0.4rem 0.5rem; }
.today-grid .today-cell.empty { background: var(--bg); }
.today-grid .today-cell-link {
  display: inline-flex; align-items: center; gap: 0.35rem;
  text-decoration: none; color: inherit;
}
.today-grid .today-cell-link:hover { text-decoration: underline; }
.today-grid .cap { font-size: 0.75rem; color: var(--muted); }
.today-grid .cap.over { color: #c43030; font-weight: bold; }
.today-summary { margin-top: 0.75rem; font-size: 0.85rem; }

.today-suggestions {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 0.8rem;
  margin: 0.8rem 0;
}
.today-suggestions-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.8rem;
}
.today-suggestions-head h3 { margin: 0; }
.today-suggestion-filters {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 0.55rem;
}
.today-suggestion-list { display: grid; gap: 0.5rem; margin-top: 0.7rem; }
.today-suggestion-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.65rem;
  background: #fbfcff;
}
.today-suggestion-card p { margin: 0.2rem 0; }
.today-suggestion-actions { display: flex; gap: 0.4rem; margin-top: 0.45rem; flex-wrap: wrap; }

/* Daily Ops tab switcher (driver grid vs schedule view) */
.today-tabs {
  display: flex;
  gap: 0.25rem;
  border-bottom: 1px solid var(--border, #ddd);
  margin-bottom: 1rem;
}
.today-tab {
  background: transparent;
  border: none;
  padding: 0.55rem 1rem;
  cursor: pointer;
  font-size: 0.95rem;
  color: var(--muted, #666);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.today-tab:hover { color: var(--text, #222); }
.today-tab.on {
  color: var(--text, #222);
  font-weight: 600;
  border-bottom-color: var(--primary, #1a6cf2);
}

/* Reports page ---------------------------------------------------------- */
.reports-range {
  display: flex; align-items: center; gap: 0.5rem;
}
.reports-range label.inline {
  display: flex; align-items: center; gap: 0.4rem;
  margin: 0; font-weight: normal;
}
.reports-totals {
  display: flex; flex-wrap: wrap; gap: 1rem;
  margin: 0.75rem 0;
  padding: 0.6rem 0.8rem;
  background: var(--surface-2, #f7f7f9);
  border: 1px solid var(--border, #e4e4e8);
  border-radius: 4px;
  font-size: 0.9rem;
  align-items: center;
}
.reports-totals .ok   { color: #1f7a3b; font-weight: 600; }
.reports-totals .warn { color: #c43030; font-weight: 600; }
.reports-totals .cost-total { font-weight: 700; margin-left: auto; }
.reports-body { margin-top: 0.5rem; }
.reports-tabs { margin-top: 0.5rem; }
.reports-evv-filters { margin: 0.2rem 0 0.8rem; flex-wrap: wrap; }
.stack { display: grid; gap: 0.65rem; }

/* Reports — Calendar tab ------------------------------------------------ */

.calendar-month-label { font-weight: 600; min-width: 11ch; text-align: center; }
.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  margin-top: 0.75rem;
}
.calendar-weekday {
  background: var(--surface-2, #f4f4f5);
  padding: 0.4rem 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-muted);
  text-align: center;
}
.calendar-cell {
  background: var(--surface, #fff);
  min-height: 5.5rem;
  padding: 0.4rem 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  text-decoration: none;
  color: inherit;
}
.calendar-cell:not(.empty):hover { background: var(--surface-2, #f4f4f5); cursor: pointer; }
.calendar-cell.empty { background: var(--surface-2, #f4f4f5); }
.calendar-cell.today .calendar-cell-day {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.6rem; height: 1.6rem; border-radius: 50%;
  background: var(--accent); color: #fff;
}
.calendar-cell-day { font-size: 0.85rem; font-weight: 600; }
.calendar-cell-empty { font-size: 0.75rem; }
.calendar-cell-stats { display: flex; flex-direction: column; gap: 0.2rem; align-items: flex-start; }
.calendar-cell-detail { font-size: 0.7rem; color: var(--text-muted); }
@media (max-width: 640px) {
  .calendar-weekday { font-size: 0.65rem; padding: 0.3rem 0.2rem; }
  .calendar-cell { min-height: 4rem; padding: 0.3rem; font-size: 0.8rem; }
}
.calendar-day-panel { margin-top: 0.75rem; }
.calendar-cell-big {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  min-height: 8rem;
  padding: 1rem 1.25rem;
  max-width: 28rem;
}
.calendar-cell-big .calendar-cell-day { font-size: 1.1rem; }
/* The .today circular-badge treatment above assumes a bare day number (month/
   week grid) -- in big mode the same span holds a full date string ("Monday,
   August 24, 2026"), so undo the badge sizing or it gets clipped into a tiny
   circle (found via browser click-through, 2026-08-25). Higher specificity
   (4 classes vs. 3) wins over the rule above regardless of source order. */
.calendar-cell.calendar-cell-big.today .calendar-cell-day {
  display: block;
  width: auto; height: auto;
  border-radius: 0;
  background: transparent; color: inherit;
}
.calendar-cell-big .calendar-cell-stats .tag { font-size: 1.1rem; padding: 0.3rem 0.6rem; }
.calendar-cell-big .calendar-cell-detail { font-size: 0.85rem; }

/* Print view ----------------------------------------------------------- */

.print-page { padding: 1rem; max-width: 8.5in; margin: 0 auto; }
.print-actions { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
.print-doc {
  padding: 0.5in 0.5in 0.4in;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 1rem;
  color: #000;
}
.print-header {
  display: flex; gap: 1rem; align-items: baseline;
  padding-bottom: 0.4rem; border-bottom: 2px solid #000;
}
.print-org { font-size: 0.85rem; color: #555; text-transform: uppercase; letter-spacing: 0.05em; }
.print-driver { font-size: 1.4rem; font-weight: bold; }
.print-meta { font-size: 0.85rem; color: #555; margin-left: auto; text-align: right; }
.print-shift-heading { font-size: 1.15rem; margin: 0.8rem 0 0.2rem; }
.print-driver-phone { color: #555; margin: 0 0 0.5rem; font-size: 0.9rem; }
.print-stops { width: 100%; border-collapse: collapse; margin-top: 0.5rem; font-size: 0.9rem; }
.print-stops th, .print-stops td {
  padding: 0.4rem 0.3rem; text-align: left;
  border-bottom: 1px solid #ccc; vertical-align: top;
}
.print-stops th { border-bottom: 2px solid #000; font-size: 0.8rem; text-transform: uppercase; }
.print-stops .seq { font-weight: bold; width: 1.5rem; }
.print-stops .check { width: 1.5rem; text-align: center; font-size: 1.1rem; }
.print-stops .name .tag { margin-left: 0.4rem; }
.print-stops .wc-row { background: #faf5ff; }
.print-footer { margin-top: 0.8rem; font-size: 0.8rem; color: #555; font-style: italic; }
.print-qr {
  margin: 0.8rem 0 0; display: inline-flex; flex-direction: column;
  align-items: center; gap: 0.2rem;
}
.print-qr svg { border: 1px solid #ddd; }
.print-qr figcaption { font-size: 0.72rem; color: #555; text-transform: uppercase; letter-spacing: 0.03em; }

@media print {
  /* Strip everything except the print canvas */
  header.topbar,
  .modal-backdrop,
  .print-actions { display: none !important; }
  body { background: #fff; }
  main { padding: 0; }
  .print-page { padding: 0; max-width: none; margin: 0; }
  .print-doc {
    border: none; border-radius: 0;
    page-break-inside: avoid;
    padding: 0.4in 0.5in;
    margin: 0;
  }
  .print-doc.page-break { page-break-before: always; }
}

form.form { display: grid; gap: 0.85rem; }
form.form label { display: grid; gap: 0.25rem; font-size: 0.85rem; color: var(--muted); }
form.form input[type=text], form.form input[type=email], form.form input[type=tel],
form.form input[type=date], form.form input[type=time], form.form input[type=password],
form.form select, form.form textarea {
  font: inherit; padding: 0.55rem 0.7rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
}
form.form textarea { min-height: 60px; resize: vertical; }
form.form .row { display: grid; grid-template-columns: 1fr 1fr; gap: 0.85rem; }
form.form .checkbox { display: flex; gap: 0.4rem; align-items: center; }
form.form .checkbox input { width: auto; }

.login-page {
  max-width: 380px; margin: 8vh auto; background: var(--surface);
  padding: 2rem; border-radius: var(--radius); border: 1px solid var(--border);
}
.login-page h1 { margin-top: 0; font-size: 1.4rem; }
.login-page form { display: grid; gap: 0.85rem; }
.login-page input { font: inherit; padding: 0.6rem; border: 1px solid var(--border); border-radius: var(--radius); }
.login-page .err { color: var(--danger); font-size: 0.85rem; }
.login-page .hint { color: var(--muted); font-size: 0.8rem; }

/* Landing / split sign-in page (dispatcher + driver). Visible only when
   not signed in. Stacks on narrow screens; side-by-side cards on wide. */
.landing-page {
  max-width: 880px; margin: 6vh auto 4vh; padding: 0 1.25rem;
}
.landing-hero { text-align: center; margin-bottom: 2rem; }
.landing-logo {
  width: 64px; height: auto; margin: 0 auto 0.75rem; display: block;
}
.landing-hero h1 { margin: 0 0 0.35rem; font-size: 2rem; color: var(--text); }
.landing-tagline { margin: 0; color: var(--muted); font-size: 1rem; }
.landing-cards {
  display: grid; gap: 1.25rem;
  grid-template-columns: 1fr;
}
@media (min-width: 720px) {
  .landing-cards { grid-template-columns: 1fr 1fr; }
}
.landing-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.5rem 1.5rem 1.25rem;
  display: flex; flex-direction: column; gap: 0.85rem;
  box-shadow: 0 1px 3px rgba(0,0,0,0.03);
}
.landing-card-head h2 { margin: 0 0 0.35rem; font-size: 1.2rem; }
.landing-card-head .hint { font-size: 0.92rem; line-height: 1.4; }
.landing-card form { display: grid; gap: 0.7rem; }
.landing-card input { font: inherit; padding: 0.6rem; border: 1px solid var(--border); border-radius: var(--radius); }
.landing-card .err { color: var(--danger); font-size: 0.85rem; min-height: 1.1em; }
.landing-card .hint { color: var(--muted); font-size: 0.82rem; }
.landing-card .primary.as-btn {
  display: inline-flex; justify-content: center; align-items: center;
  text-decoration: none; padding: 0.65rem 1rem;
  background: var(--accent); color: #fff; border: 1px solid var(--accent);
  border-radius: var(--radius); font-weight: 600;
}
.landing-get-app {
  text-align: center; font-size: 0.9rem;
  color: var(--accent); text-decoration: none;
}
.landing-get-app:hover { text-decoration: underline; }
.landing-card .primary.as-btn:hover { filter: brightness(0.95); }
.landing-footer {
  text-align: center; margin-top: 2rem;
  color: var(--muted); font-size: 0.85rem;
}
.landing-footer a { color: var(--muted); }

.loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.85rem;
  padding: 3.5rem 1rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.85rem;
}
.loading::before {
  content: '';
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  animation: loading-spin 0.7s linear infinite;
}
@keyframes loading-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .loading::before { animation: none; opacity: 0.5; }
}
.error-banner {
  padding: 0.65rem 0.9rem; background: #fff3f3; border: 1px solid #f3c2c2;
  color: var(--danger); border-radius: var(--radius); margin-bottom: 1rem;
}

/* Distinguishes a bulk action's scope (all matching rows vs. just a
   selection) up front, since the two entry points otherwise open an
   identical-looking modal — see openBulkShiftAssign in app.js. */
.scope-banner {
  padding: 0.65rem 0.9rem; border-radius: var(--radius); margin-bottom: 1rem;
  font-weight: 500;
}
.scope-banner.all      { background: var(--warn-soft); color: var(--warn); }
.scope-banner.selected { background: var(--info-soft); color: var(--info); }

/* Persistent shell-level notice for a past_due/read_only org (plans/
   saas-selfserve.md §5) -- sits between the topbar and <main>, injected by
   renderBillingBanner() in app.js. Absent for the common trialing/active/
   no-org case. */
.billing-banner {
  display: flex; align-items: center; justify-content: center; gap: var(--space-3);
  padding: 0.65rem 1rem; font-weight: 500; text-align: center; flex-wrap: wrap;
}
.billing-banner.warn   { background: var(--warn-soft);   color: var(--warn); }
.billing-banner.danger { background: var(--danger-soft); color: var(--danger); }
.billing-banner a { color: inherit; text-decoration: underline; font-weight: 600; white-space: nowrap; }

/* Semantic chips. Each pairs a "soft" background with its saturated text
   color from the token palette. New variants should map to a semantic
   role, not a one-off color. */
.tag {
  display: inline-block;
  padding: 1px var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.5;
}
.tag.ok     { background: var(--ok-soft);     color: var(--ok); }
.tag.draft  { background: var(--surface-3);   color: var(--text-muted); }
.tag.warn   { background: var(--warn-soft);   color: var(--warn); }
.tag.danger { background: var(--danger-soft); color: var(--danger); }
.tag.info   { background: var(--accent-soft); color: var(--accent); }
.tag.wc     { background: var(--wc-soft);     color: var(--wc); }
.tag.lock   { background: var(--info-soft);   color: var(--info); }
/* A .tag rendered as a <button> (click-to-edit route/status pill, see
   .cell-edit-link below) needs the browser's button chrome reset, but
   should otherwise look identical to the plain-<span> tag. */
button.tag { border: none; font: inherit; cursor: pointer; }
button.tag:hover { filter: brightness(0.95); }

.muted { color: var(--muted); }

/* Click-to-edit table cells (Name in Drivers/Vans/Shifts/Route templates/
   Participants, Route number in Routes) -- opens the same edit form as the
   row's "⋮" > Edit action, just without the extra click. Plain text by
   default; combine with .tag when the cell already has a pill look. */
.cell-edit-link {
  background: none; border: none; padding: 0; margin: 0;
  font: inherit; font-weight: inherit;
  color: var(--accent); cursor: pointer; text-align: left;
}
.cell-edit-link:hover { text-decoration: underline; }
/* Higher-specificity override for the "no name yet" fallback (Routes'
   unnamed-route cell) -- stays visually muted at rest like the plain-text
   version did, but still reads as clickable on hover. */
.muted.cell-edit-link { color: var(--muted); }
.muted.cell-edit-link:hover { color: var(--accent); }
/* .cell-edit-link's padding:0 would otherwise flatten the pill look when
   combined with .tag (Routes' route-number column) -- restore it. */
.tag.cell-edit-link { padding: 1px var(--space-2); }

.route-builder { display: grid; grid-template-columns: 1fr 320px; gap: 1.25rem; }
.route-builder .stops-list, .route-builder .participants-bank {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 0.75rem;
}
.route-builder ol { list-style: decimal; padding-left: 1.5rem; margin: 0; }
.route-builder li { padding: 0.35rem 0; }
.route-builder .participant-pill {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.4rem 0.6rem; background: var(--bg);
  border-radius: var(--radius); margin-bottom: 0.3rem;
}
.route-builder .participant-pill button { font-size: 0.8rem; }

.checkbox-row { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.checkbox-col { display: flex; flex-direction: column; gap: 0.25rem; }
.checkbox.inline { display: inline-flex; align-items: center; gap: 0.25rem; margin: 0; }

.settings-layout { display: flex; align-items: flex-start; gap: var(--space-5); max-width: 900px; }
.settings-nav {
  display: flex; flex-direction: column; gap: 2px;
  flex: 0 0 180px;
}
.settings-nav-item {
  text-align: left; background: transparent; border: none; cursor: pointer;
  padding: var(--space-2) var(--space-3); border-radius: var(--radius);
  font-size: var(--text-sm); color: var(--text-muted);
}
.settings-nav-item:hover { background: var(--surface-2); color: var(--text); }
.settings-nav-item.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
@media (max-width: 640px) {
  .settings-layout { flex-direction: column; }
  .settings-nav { flex-direction: row; flex: 0 0 auto; flex-wrap: wrap; width: 100%; }
}
.settings-card {
  background: var(--surface-1); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  padding: var(--space-5);
  flex: 1 1 0; min-width: 0;
}
.settings-card-head { margin-bottom: var(--space-4); }
.settings-card-head h3 { margin: 0 0 var(--space-1); font-size: var(--text-md); }
.settings-card-desc { margin: 0; color: var(--text-muted); font-size: var(--text-sm); }

/* Route-assignment picker (Settings → Organization). Two option cards rather
   than the toolbar's compact .filter-seg pills -- each choice needs a line of
   explanation, and that styling is scoped to .map-filters anyway. */
.routing-choice {
  display: flex; gap: var(--space-3); flex-wrap: wrap;
  margin-top: var(--space-2);
}
.routing-choice button {
  flex: 1 1 14rem; min-width: 0;
  display: flex; flex-direction: column; gap: var(--space-1);
  padding: var(--space-3) var(--space-4); text-align: left;
  background: var(--surface-1); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius);
  cursor: pointer;
}
.routing-choice button:hover { border-color: var(--text-muted); }
.routing-choice button.on {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.routing-choice button .muted { font-size: var(--text-sm); color: var(--text-muted); }
.settings-subhead {
  margin: var(--space-4) 0 var(--space-2); padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: var(--text-sm); color: var(--text-muted);
}

.role-legend { display: flex; gap: var(--space-4); flex-wrap: wrap; margin-bottom: var(--space-2); }
.role-legend-item { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); color: var(--text-muted); }

.token-details summary {
  cursor: pointer; font-size: var(--text-sm); color: var(--accent); font-weight: 500;
  user-select: none;
}
.token-details summary:hover { color: var(--accent-hover); }
.token-details-body { margin-top: var(--space-3); }

.form.inline { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.75rem; }
.form.inline input[type="email"] { flex: 1; min-width: 16rem; }
.token-box {
  width: 100%; font-family: monospace; font-size: 0.78rem;
  resize: vertical; word-break: break-all;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 0.5rem;
}
.token-status { margin-left: 0.5rem; }
.code-snippet {
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 0.5rem 0.75rem;
  font-size: 0.85rem; overflow-x: auto;
}
.bootstrap-banner {
  background: #fff5e6; border: 1px solid #f5c887;
  border-radius: var(--radius); padding: 0.75rem 1rem; margin-bottom: 1rem;
}
.bootstrap-banner p { margin: 0 0 0.5rem; }

/* Maps deep links --------------------------------------------------------- */
.maps-links { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border); }
.maps-links-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.5rem; }
.maps-link {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.5rem 0.9rem; border-radius: var(--radius);
  border: 1px solid var(--border); background: var(--surface);
  text-decoration: none; color: inherit; font-size: 0.9rem;
}
.maps-link:hover { background: var(--bg); }
.maps-link.apple  { border-color: #555; }
.maps-link.google { border-color: #1a73e8; color: #1a73e8; }
.route-builder .stop-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 0.5rem 0.6rem; background: var(--bg);
  border-radius: var(--radius); margin-bottom: 0.3rem;
}
.route-builder .stop-row .controls button { font-size: 0.75rem; padding: 0.2rem 0.4rem; margin-left: 0.2rem; }

/* Help & support page */
.help-page { max-width: 640px; margin: 0 auto; }
.help-page .help-form { display: grid; gap: 0.85rem; margin: 1rem 0 1.5rem; }
.help-page .help-form label { display: grid; gap: 0.25rem; font-size: 0.85rem; color: var(--muted); }
.help-page .help-form input,
.help-page .help-form textarea {
  font: inherit; padding: 0.6rem 0.7rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
}
.help-page .help-form textarea { resize: vertical; min-height: 8rem; }
.help-page .help-form button.primary { justify-self: start; padding: 0.55rem 1.2rem; }
.help-page .form-status { font-size: 0.9rem; min-height: 1em; }
.help-page .form-status.success { color: #0a7e2a; font-weight: 600; }
.help-page .form-status.error { color: var(--danger); }
.help-page .help-resources { color: var(--muted); font-size: 0.9rem; }
.help-page .help-resources h3 { font-size: 1rem; color: var(--text); margin: 1rem 0 0.4rem; }
.help-page .help-resources ul { padding-left: 1.2rem; }

/* Embedded user guide */
.guide-page { max-width: 820px; }
.guide-body { color: var(--text); line-height: 1.6; }
.guide-body h1 { font-size: 1.6rem; margin: 1.6rem 0 0.8rem; }
.guide-body h2 { font-size: 1.3rem; margin: 1.8rem 0 0.6rem; border-bottom: 1px solid var(--border); padding-bottom: 0.3rem; }
.guide-body h3 { font-size: 1.08rem; margin: 1.2rem 0 0.4rem; }
.guide-body h4 { font-size: 0.98rem; margin: 1rem 0 0.3rem; color: var(--muted); }
.guide-body p { margin: 0.6rem 0; }
.guide-body ul, .guide-body ol { padding-left: 1.4rem; margin: 0.5rem 0; }
.guide-body li { margin: 0.25rem 0; }
.guide-body code { background: var(--surface-2, #f0f0f0); padding: 0.1em 0.35em; border-radius: 4px; font-size: 0.9em; }
.guide-body a { color: var(--accent, #1a73e8); }
.guide-body hr { border: 0; border-top: 1px solid var(--border); margin: 1.6rem 0; }
.guide-body table { border-collapse: collapse; width: 100%; margin: 0.8rem 0; font-size: 0.9rem; }
.guide-body th, .guide-body td { border: 1px solid var(--border); padding: 0.4rem 0.6rem; text-align: left; vertical-align: top; }
.guide-body th { background: var(--surface-2, #f6f6f6); font-weight: 600; }

/* ------------------------------------------------------------------ */
/* Demo onboarding tour (tour.js) — demo builds only (gated on IS_DEMO). */
/* ------------------------------------------------------------------ */
.tour-layer { position: fixed; inset: 0; z-index: 3000; }
.tour-guard { position: absolute; inset: 0; background: transparent; }
.tour-guard--dim { background: rgba(15, 20, 25, 0.55); }
.tour-spotlight {
  position: absolute; border-radius: var(--radius);
  box-shadow: 0 0 0 9999px rgba(15, 20, 25, 0.58);
  outline: 2px solid var(--accent); outline-offset: 2px;
  pointer-events: none;
  transition: top .25s ease, left .25s ease, width .25s ease, height .25s ease;
}
.tour-spotlight[hidden] { display: none; }
.tour-spotlight--click { pointer-events: auto; cursor: pointer; }
.tour-pop {
  position: absolute; width: min(360px, calc(100vw - 28px));
  background: var(--surface-1); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); padding: var(--space-4) var(--space-4) var(--space-3);
}
.tour-pop--center {
  top: 50% !important; left: 50% !important; transform: translate(-50%, -50%);
}
.tour-progress { display: flex; gap: 5px; margin-bottom: var(--space-3); }
.tour-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--border-strong); }
.tour-dot.on { background: var(--accent); }
.tour-title { margin: 0 0 var(--space-2); font-size: var(--text-lg); }
.tour-body { margin: 0 0 var(--space-4); font-size: var(--text-sm); color: var(--text-muted); line-height: 1.5; }
.tour-footnote { margin: calc(-1 * var(--space-3)) 0 var(--space-4); font-size: var(--text-xs); color: var(--text-muted); }
.tour-footnote-link { color: var(--text-muted); text-decoration: underline; }
.tour-footnote-link:hover { color: var(--text); }
.tour-foot { display: flex; align-items: center; gap: var(--space-2); }
.tour-spacer { flex: 1; }
.tour-btn {
  font: inherit; font-size: var(--text-sm); font-weight: 600;
  padding: var(--space-2) var(--space-4); border-radius: var(--radius);
  border: 1px solid transparent; cursor: pointer;
}
.tour-next { background: var(--accent); color: var(--text-inverse); }
.tour-next:hover { background: var(--accent-hover); }
.tour-back { background: var(--surface-2); color: var(--text); border-color: var(--border); }
.tour-back:hover { background: var(--surface-3); }
.tour-back[hidden] { display: none; }
.tour-skip { background: transparent; color: var(--text-muted); padding-left: 0; }
.tour-skip:hover { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .tour-spotlight { transition: none; } }
.tour-loading { display: flex; align-items: center; gap: var(--space-3); color: var(--text-muted); font-size: var(--text-sm); padding: var(--space-2) 0; }
.tour-spinner { width: 18px; height: 18px; border-radius: 50%; border: 2px solid var(--border); border-top-color: var(--accent); animation: tour-spin .7s linear infinite; }
@keyframes tour-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .tour-spinner { animation: none; } }

/* Front-desk EVV capture — signature pad + status feedback inside the
   standard modal() chrome (unlike the driver app, which has its own
   bespoke .evv-overlay/.evv-dialog since it predates modal()). Canvas/
   status/error rules mirror src/driver/app.css's .evv-* — keep in sync if
   restyled. */
.evv-modal-sub { margin: 0 0 0.5rem; color: var(--muted); }
.evv-canvas {
  width: 100%;
  height: 220px;
  border: 1px dashed #9aa5b1;
  border-radius: 8px;
  background: #fff;
  touch-action: none;
}
.evv-status {
  min-height: 1.2em;
  margin: 0.55rem 0 0;
  font-size: 0.92rem;
}
.evv-status[hidden] { display: none; }
.evv-status.saving {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--muted);
}
.evv-status.success { color: var(--ok); }
.evv-spinner {
  width: 0.95rem;
  height: 0.95rem;
  border: 2px solid rgba(26, 115, 232, 0.2);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: evv-spin 0.8s linear infinite;
}
.evv-error {
  min-height: 1.2em;
  margin: 0.45rem 0 0;
  color: var(--warn);
  font-size: 0.9rem;
}
@keyframes evv-spin { to { transform: rotate(360deg); } }

/* Participant avatar — photo edit field (openParticipantForm) and the
   front-desk cards page both use this circle: background-image when a
   photo is set, initials text otherwise (never a plain <img>, so there's
   no broken-image icon while empty). */
.avatar-preview {
  width: 64px; height: 64px;
  border-radius: 50%;
  background-color: var(--accent-soft);
  background-size: cover;
  background-position: center;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  color: var(--accent);
  font-weight: 600;
  font-size: 1.1rem;
}
.avatar-row { align-items: center; gap: 0.75rem; }
.avatar-row .file-label { display: block; font-size: 0.85rem; }
.avatar-row .secondary { margin-top: 0.3rem; font-size: 0.8rem; padding: 0.2rem 0.5rem; }

/* Front-desk cards page (#/front-desk) — larger touch targets for a shared
   tablet than the dense Today schedule table. */
.front-desk-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
  margin-top: 1rem;
}
.front-desk-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  padding: 1rem;
  display: flex; flex-direction: column; align-items: center;
  gap: 0.5rem;
  text-align: center;
}
.front-desk-card .avatar-preview { width: 88px; height: 88px; font-size: 1.6rem; }
.front-desk-card-name { font-weight: 600; }
.front-desk-card-actions { display: flex; flex-direction: column; gap: 0.4rem; width: 100%; margin-top: 0.4rem; }
.front-desk-card-actions button { width: 100%; }
.front-desk-card-signed { color: var(--ok); font-size: 0.85rem; }

/* Users → Edit access (per-product roles) */
.access-tags { display: inline-flex; flex-wrap: wrap; gap: 4px; }
.access-editor .checkbox { display: flex; gap: 6px; align-items: center; margin: 4px 0; }
.access-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 0 12px; }

/* Settings → Agreements (renderAgreementsSection / signAgreementModal). */
.agreement-row {
  display: flex; justify-content: space-between; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) 0; border-bottom: 1px solid var(--border);
}
.agreement-row p { margin: var(--space-1) 0 0; }
.agreement-actions { display: flex; gap: var(--space-2); flex-shrink: 0; }
.agreement-frame {
  width: 100%; height: min(50vh, 460px); border: 1px solid var(--border);
  border-radius: var(--radius); background: #fff;
}
.agreement-fields {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-2) var(--space-3); margin: var(--space-3) 0;
}
.agreement-fields label { display: flex; flex-direction: column; gap: var(--space-1); font-size: 0.9rem; }
.agreement-accept { display: flex; gap: var(--space-2); align-items: flex-start; font-size: 0.9rem; }
.agreement-accept input { margin-top: 0.2rem; }
@media (max-width: 600px) {
  .agreement-row { flex-direction: column; }
}
