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

:root {
  --black: #1a1a1a;
  --gold: #ee6c4d;        /* brand coral (from ABP logo) */
  --gold-dk: #d4552f;     /* darker coral for active states */
  --white: #ffffff;
  --navy: #3d5a80;        /* brand navy (from ABP logo) */
  --gray-50: #f9f9f8;
  --gray-100: #f1f0ec;
  --gray-200: #e2e1db;
  --gray-300: #d8d6cf;
  --gray-400: #6f6e6a;
  --gray-500: #6f6e6a;
  --gray-600: #5f5e5a;
  --gray-700: #3a3a36;
  --gray-800: #2c2c2a;
  --gray-900: #1a1a1a;
  --green: #22c55e;
  --green-bg: #dcfce7;
  --green-txt: #166534;
  --amber-bg: #fef3c7;
  --amber-txt: #92400e;
  --red: #ef4444;
  --red-bg: #fee2e2;
  --red-txt: #991b1b;
  --blue-bg: #dbeafe;
  --blue-txt: #1e40af;
  --purple-bg: #ede9fe;
  --purple-txt: #4c1d95;
  --r: 8px;
  --rl: 12px;
  --shadow: 0 1px 4px rgba(0,0,0,0.08);

  /* ─── "Fresh Asphalt & Hi-Vis Steel" design tokens (additive) ───────
     New token ramp introduced by the premium design pass. Defined here
     so every selector that references them resolves in both themes; the
     dark-theme retune at the end of this file is where they come alive.
     Brand --gold is repurposed below to the hi-vis amber (was coral). */
  /* substrate — asphalt */
  --asphalt-900:#0E0F12; --asphalt-850:#16181D; --asphalt-800:#1C1F26;
  --asphalt-700:#262A33; --asphalt-600:#333845; --asphalt-500:#4A5161;
  /* ink (text on dark) */
  --ink-hi:#F4F6FA; --ink-mid:#AEB6C4; --ink-low:#6E7686;
  /* accent — hi-vis safety amber (the one loud color) */
  --hivis:#FFB200; --hivis-bright:#FFC83D; --hivis-deep:#E08A00;
  --hivis-glow:rgba(255,178,0,.22);
  /* warm metal (premium secondary, not a second loud color) */
  --brass:#C8A567; --brass-dim:#7C6B45;
  /* tiered radii */
  --r-sm:10px; --r-md:14px; --r-lg:18px; --r-xl:24px;
  /* dark-aware elevation (inner top highlight + dark drop) */
  --elev-1:0 1px 0 rgba(255,255,255,.04) inset,0 1px 2px rgba(0,0,0,.4);
  --elev-2:0 1px 0 rgba(255,255,255,.05) inset,0 6px 16px -4px rgba(0,0,0,.55);
  --elev-3:0 1px 0 rgba(255,255,255,.06) inset,0 14px 34px -8px rgba(0,0,0,.6);
  --glow-hivis:0 0 0 1px rgba(255,178,0,.35),0 8px 24px -6px rgba(255,178,0,.25);
}

body {
  font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 14px;
  background: var(--gray-100);
  color: var(--gray-800);
  min-height: 100vh;
  -webkit-text-size-adjust: 100%;
}

/* Kill horizontal scroll/jiggle. The page is meant to scroll vertically only;
   any element a few px too wide used to let iOS pan + rubber-band sideways
   ("the page moves left and right"). Clamp width and disable the horizontal
   overscroll bounce. overflow-x:hidden on the scroll root keeps position:sticky
   (topbar / nav) working; intentional horizontal scrollers (wide tables) keep
   their own overflow-x:auto containers, which are unaffected. */
html, body {
  max-width: 100%;
  overflow-x: hidden;
  overscroll-behavior-x: none;
}

/* SCREENS */
.screen { display: none; }
.screen.active { display: block; }
.hidden { display: none !important; }

/* LOGIN */
.login-wrap {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  background: var(--navy);
  background: linear-gradient(160deg, #3d5a80 0%, #28415f 100%);
}
.login-logo { text-align: center; margin-bottom: 24px; }
.login-logo-img {
  width: auto;
  height: 90px;
  max-width: 280px;
  object-fit: contain;
  filter: brightness(0) invert(1);
}
/* ── TENANT (full-color) LOGOS ───────────────────────────────────
   The default ABP mark is a single-color SVG rendered as a white
   silhouette on the dark navy login/topbar. A tenant's uploaded logo
   is full-color, so these classes (added by UI.applyTheme only when a
   tenant logoUrl is set — ABP is unaffected) cancel that filter and
   sit the logo on a clean white badge so its reds/blacks read on the
   dark background. */
.login-logo .tenant-logo-img {
  display: inline-block;
  filter: none;
  background: #fff;
  padding: 14px 20px;
  border-radius: 18px;
  max-height: 96px;
  max-width: 260px;
  object-fit: contain;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}
.topbar-logo.has-tenant-logo {
  filter: none;
  background: #fff;
  padding: 3px 7px;
  border-radius: 8px;
  height: 40px;
}
/* PaveHub product lockup — already white+gold on transparent, so no invert. */
.login-logo-lockup {
  width: auto;
  height: 84px;
  max-width: 320px;
  object-fit: contain;
}
.login-card {
  background: var(--white);
  border-radius: var(--rl);
  padding: 24px 20px;
  width: 100%; max-width: 340px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.4);
  border-top: 4px solid var(--gold);
}
.field-label { font-size: 11px; font-weight: 600; color: var(--gray-600); letter-spacing: 0.5px; display: block; margin-bottom: 6px; }
.field-input {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--gray-200);
  border-radius: var(--r);
  font-size: 14px; color: var(--gray-800);
  background: var(--white);
  outline: none; appearance: none;
  transition: border-color 0.15s;
}
.field-input:focus { border-color: var(--gold); }
/* Required-field validation marking (UI.requireFields) */
.field-input.field-invalid { border-color: var(--red) !important; box-shadow: 0 0 0 2px rgba(220, 38, 38, .15); }
.field-error-msg { color: var(--red); font-size: 12px; margin-top: 4px; }
.pin-user-name { font-size: 15px; font-weight: 600; text-align: center; margin-bottom: 14px; color: var(--gray-800); }
.pin-dots { display: flex; gap: 12px; justify-content: center; margin-bottom: 20px; }
.pin-dots span {
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid var(--gray-200);
  transition: background 0.1s, border-color 0.1s;
}
.pin-dots span.filled { background: var(--gold); border-color: var(--gold); }
.pin-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.pin-btn {
  height: 52px; border-radius: var(--r);
  border: 1px solid var(--gray-200);
  background: var(--gray-50);
  font-size: 20px; font-weight: 500;
  color: var(--gray-800); cursor: pointer;
  transition: background 0.1s;
}
.pin-btn:active { background: var(--gray-200); }
.pin-clear { font-size: 18px; }
.pin-back { font-size: 13px; color: var(--gray-400); }
.pin-error { color: var(--red-txt); font-size: 12px; text-align: center; margin-top: 10px; }

/* BUTTONS */
.btn-primary {
  background: var(--gold); color: var(--black);
  border: none; border-radius: var(--r);
  padding: 11px 20px; font-size: 14px; font-weight: 600;
  cursor: pointer; transition: background 0.1s;
}
.btn-primary:active { background: var(--gold-dk); }
.btn-secondary {
  background: var(--white); color: var(--gray-800);
  border: 1px solid var(--gray-200); border-radius: var(--r);
  padding: 10px 18px; font-size: 14px;
  cursor: pointer; transition: background 0.1s;
}
.btn-secondary:active { background: var(--gray-100); }
.btn-xs {
  background: rgba(255,255,255,0.15); color: var(--white);
  border: 1px solid rgba(255,255,255,0.2); border-radius: 6px;
  padding: 4px 10px; font-size: 11px; font-weight: 500;
  cursor: pointer;
}
.btn-danger {
  background: var(--red-bg); color: var(--red-txt);
  border: 1px solid #fca5a5; border-radius: var(--r);
  padding: 10px 18px; font-size: 14px;
  cursor: pointer;
}
.full-width { width: 100%; }
.mt16 { margin-top: 16px; }
.icon-btn {
  width: 32px; height: 32px; border-radius: 50%;
  border: none; background: rgba(255,255,255,0.1);
  color: var(--white); font-size: 14px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}

/* TOPBAR */
.topbar {
  background: var(--navy);
  padding: 8px 14px;
  display: flex; align-items: center; justify-content: space-between;
  position: sticky; top: 0; z-index: 50;
  min-height: 52px;
}
/* Left side may shrink/truncate so the right-side action buttons (notifications,
   change-PIN, log-out) are never pushed off-screen on narrow phones. */
.topbar-left { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 1 1 auto; overflow: hidden; }
.topbar-logo {
  height: 36px;
  width: auto;
  max-width: 180px;
  object-fit: contain;
  flex-shrink: 0;
  filter: brightness(0) invert(1);
}
.topbar-role {
  font-size: 10px; color: var(--gold);
  letter-spacing: 0.5px; text-transform: uppercase;
  align-self: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
}
.topbar-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.topbar-time { font-size: 11px; color: var(--gray-400); }
/* iPhone: the status bar already shows the time; the clock was squeezing the
   role label down to "O…" on 390px screens. */
@media (max-width: 480px) { .topbar-time { display: none; } }

/* NOTIFICATION BELL + BADGE */
.notif-btn { position: relative; }
.notif-badge {
  position: absolute; top: -4px; right: -4px;
  min-width: 16px; height: 16px; padding: 0 4px; box-sizing: border-box;
  border-radius: 9px; background: var(--gold); color: var(--black);
  font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
  box-shadow: 0 0 0 2px var(--navy); pointer-events: none;
}
.notif-badge.hidden { display: none; }

/* NOTIFICATION CENTER (modal list) */
.notif-list { max-height: 60vh; overflow-y: auto; margin: 0 -6px; }
.notif-item {
  display: flex; gap: 10px; padding: 12px 8px;
  border-bottom: 1px solid var(--gray-100); cursor: pointer; align-items: flex-start;
}
.notif-item:active { background: rgba(0,0,0,0.04); }
.notif-item.unread { background: rgba(238,108,77,0.08); }
.notif-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gold); margin-top: 5px; flex-shrink: 0; }
.notif-dot.read { background: transparent; }
.notif-main { flex: 1; min-width: 0; }
.notif-title { font-weight: 600; font-size: 14px; }
.notif-body { font-size: 13px; color: var(--gray-700); white-space: pre-wrap; margin-top: 2px; word-break: break-word; }
.notif-meta { font-size: 11px; color: var(--gray-400); margin-top: 4px; }

/* WEATHER BAR */
.weather-bar {
  background: #3d5a80;
  padding: 7px 14px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  font-size: 12px; color: #c8d8e8;
}
.weather-status { font-weight: 600; color: var(--green); }
.weather-status.warn { color: var(--gold); }
.weather-status.danger { color: var(--red); font-weight: 700; }
/* No forecast (no yard address): neutral, never the green go-signal. */
.weather-status.neutral { color: #c8d8e8; font-weight: 600; }
/* Legal: the advisory is a suggestion off the forecast, not a guarantee.
   Kept visible but unobtrusive (muted, small, italic). flex-basis:100% drops
   it onto its own line within the already-wrapping bar. */
.wx-note { font-size: 10px; font-style: italic; opacity: 0.7; flex-basis: 100%; order: 99; }
.wx-sep { flex: 1; }
.weather-bar-actions { margin-left: auto; }

/* NAV TABS */
.nav-tabs {
  display: flex; overflow-x: auto;
  background: var(--white);
  border-bottom: 1px solid var(--gray-200);
  scrollbar-width: none;
  position: sticky; top: 50px; z-index: 40;
}
.nav-tabs::-webkit-scrollbar { display: none; }
.nav-tab {
  padding: 10px 14px; white-space: nowrap;
  font-size: 12px; font-weight: 500;
  color: var(--gray-600); cursor: pointer;
  border: none; background: none;
  border-bottom: 2px solid transparent;
  transition: all 0.15s;
}
.nav-tab.active { color: var(--gold); border-bottom-color: var(--gold); }

/* PANELS */
#panels-container { padding: 14px; }
.panel { display: none; }
.panel.active { display: block; }

/* CARDS */
.card {
  background: var(--white);
  border-radius: var(--rl);
  border: 1px solid var(--gray-200);
  padding: 14px;
  margin-bottom: 12px;
}
.card-title { font-size: 11px; font-weight: 600; color: var(--gray-600); letter-spacing: 0.5px; text-transform: uppercase; margin-bottom: 10px; }
.metric-num { font-size: 28px; font-weight: 600; color: var(--gray-800); }
.metric-sub { font-size: 12px; color: var(--gray-400); margin-top: 2px; }

/* STAT GRID */
.stat-grid { display: grid; gap: 10px; margin-bottom: 14px; }
.stat-grid-2 { grid-template-columns: 1fr 1fr; }
.stat-grid-3 { grid-template-columns: 1fr 1fr 1fr; }
.stat-card {
  background: var(--gray-50);
  border-radius: var(--r);
  padding: 12px;
  border: 1px solid var(--gray-200);
}

/* JOB ROWS */
.job-row {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 12px;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--r);
  margin-bottom: 8px;
  cursor: pointer;
  transition: border-color 0.1s;
}
.job-row:active { border-color: var(--gold); }

/* SWIPE-TO-DELETE (see js/swipe.js) — wraps a .job-row so it can slide left
   over a red Delete action. The wrapper carries the row's bottom margin so
   the slide stays gap-free. */
.swipe-row { position: relative; overflow: hidden; border-radius: var(--r); margin-bottom: 8px; }
.swipe-row .job-row { margin-bottom: 0; position: relative; z-index: 1; will-change: transform; touch-action: pan-y; }
.swipe-action {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: 84px; border: none; cursor: pointer; z-index: 0;
  background: var(--red); color: #fff;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; font-size: 11px; font-weight: 700; letter-spacing: 0.3px;
}
.swipe-action .swipe-action-ico { font-size: 18px; line-height: 1; }
.swipe-action:active { filter: brightness(0.92); }
.status-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dot-green { background: var(--green); }
.dot-amber { background: var(--gold); }
.dot-blue { background: #3b82f6; }
.dot-gray { background: var(--gray-400); }
.dot-red { background: var(--red); }
.dot-purple { background: #8b5cf6; }
.job-body { flex: 1; min-width: 0; }
.job-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.job-sub { font-size: 11px; color: var(--gray-400); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.job-right { text-align: right; flex-shrink: 0; }
.job-val { font-size: 13px; font-weight: 600; }

/* Tappable address → Google Maps (rendered by UI.mapLink). Inherits the
   surrounding text size/color so it fits list sub-lines and detail rows alike;
   the pin + underline are the "this is tappable" affordance. */
.addr-link { color: inherit; text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--gray-400); }
.addr-link:active { opacity: 0.7; }

/* BADGES */
.badge {
  display: inline-block; padding: 2px 8px;
  border-radius: 20px; font-size: 10px; font-weight: 600;
  letter-spacing: 0.3px;
}
.badge-green { background: var(--green-bg); color: var(--green-txt); }
.badge-amber { background: var(--amber-bg); color: var(--amber-txt); }
.badge-red { background: var(--red-bg); color: var(--red-txt); }
.badge-blue { background: var(--blue-bg); color: var(--blue-txt); }
.badge-purple { background: var(--purple-bg); color: var(--purple-txt); }
.badge-gray { background: var(--gray-100); color: var(--gray-600); }

/* FORMS */
.form-group { margin-bottom: 12px; }
.form-group label { font-size: 11px; font-weight: 600; color: var(--gray-600); display: block; margin-bottom: 5px; letter-spacing: 0.4px; }
.form-group input,
.form-group select,
.form-group textarea {
  width: 100%; padding: 9px 11px;
  border: 1px solid var(--gray-200); border-radius: var(--r);
  font-size: 13px; color: var(--gray-800); background: var(--white);
  outline: none; font-family: inherit;
  transition: border-color 0.15s;
  appearance: none;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus { border-color: var(--gold); }
/* Checkboxes/radios must keep their native rendering: the generic
   appearance:none above strips the checkmark, so the box still toggles but
   never draws its check — taps look like they do nothing. */
.form-group input[type="checkbox"],
.form-group input[type="radio"] {
  -webkit-appearance: checkbox;
  appearance: auto;
  width: auto; padding: 0;
  accent-color: var(--gold);
  cursor: pointer;
}
.form-group input[type="radio"] { -webkit-appearance: radio; }
.form-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.form-actions { display: flex; gap: 8px; margin-top: 16px; }
.form-actions .btn-primary,
.form-actions .btn-secondary { flex: 1; }

/* WORKER CARD */
.worker-card {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--r);
  padding: 12px;
  margin-bottom: 8px;
}
.worker-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--black); color: var(--gold);
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; flex-shrink: 0;
}
.worker-info { flex: 1; }
.worker-name { font-size: 13px; font-weight: 600; }
.worker-role { font-size: 11px; color: var(--gray-400); }
.duty-chip {
  display: inline-block; background: var(--gray-100);
  border: 1px solid var(--gray-200); border-radius: 4px;
  padding: 2px 7px; font-size: 11px; margin: 2px;
}

/* CLOCK */
.clock-row {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 12px;
  background: var(--white); border: 1px solid var(--gray-200);
  border-radius: var(--r); margin-bottom: 8px;
}
.clock-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gray-400); flex-shrink: 0; }
.clock-dot.in { background: var(--green); }
.clock-info { flex: 1; }
.clock-name { font-size: 13px; font-weight: 600; }
.clock-time { font-size: 11px; color: var(--gray-400); }
.punch-btn {
  padding: 6px 12px; border-radius: var(--r); font-size: 12px; font-weight: 600;
  border: 1px solid var(--gray-200); cursor: pointer; background: var(--white);
}
.punch-btn.punch-in { background: var(--green-bg); color: var(--green-txt); border-color: #86efac; }
.punch-btn.punch-out { background: var(--red-bg); color: var(--red-txt); border-color: #fca5a5; }

/* MORNING BRIEF */
.brief-card {
  background: var(--navy); border-radius: var(--rl);
  padding: 16px; margin-bottom: 14px; color: var(--white);
}
.brief-label { font-size: 10px; font-weight: 600; color: var(--gold); letter-spacing: 1px; text-transform: uppercase; margin-bottom: 4px; }
.brief-date { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
.brief-line { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 7px; font-size: 12px; color: #dde5ef; }
.brief-bullet { color: var(--gold); flex-shrink: 0; }

/* AI BRIEF BOX */
.ai-brief {
  background: linear-gradient(135deg, #3d5a80 0%, #28415f 100%);
  border: 1px solid #333;
  border-radius: var(--rl);
  padding: 16px;
  margin-bottom: 14px;
  color: var(--white);
}
.ai-brief-label {
  display: flex; align-items: center; gap: 6px;
  font-size: 10px; font-weight: 600; color: var(--gold);
  letter-spacing: 1px; text-transform: uppercase; margin-bottom: 10px;
}
.ai-brief-label::before {
  content: '';
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--gold);
  animation: pulse-dot 2s infinite;
}
@keyframes pulse-dot { 0%,100%{opacity:1} 50%{opacity:0.4} }
.ai-brief-body { font-size: 13px; line-height: 1.7; color: #ddd; }
.ai-brief-body strong { color: var(--gold); font-weight: 600; }
.ai-brief-loading { font-size: 12px; color: var(--gray-400); font-style: italic; }

/* WARRANTY PROGRESS */
.w-bar { background: var(--gray-200); border-radius: 4px; height: 4px; margin-top: 4px; overflow: hidden; }
.w-fill { height: 100%; border-radius: 4px; }

/* LEAD SOURCE BARS */
.source-bar-wrap { margin-bottom: 10px; }
.source-bar-label { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 3px; }
.source-bar { background: var(--gray-200); border-radius: 4px; height: 8px; overflow: hidden; }
.source-fill { height: 100%; border-radius: 4px; }

/* STAT ROW */
.stat-row {
  display: flex; justify-content: space-between; align-items: center;
  padding: 8px 0; border-bottom: 1px solid var(--gray-100);
  font-size: 13px;
}
.stat-row:last-child { border-bottom: none; }
.stat-label { color: var(--gray-600); }

/* MONEY LEAK */
.money-leak-item {
  display: flex; justify-content: space-between; align-items: center;
  padding: 7px 0; border-bottom: 1px solid var(--gray-100);
  font-size: 12px;
}
.money-leak-item:last-child { border-bottom: none; }

/* SEVERITY SELECTOR */
.sev-group { display: flex; gap: 6px; margin-top: 5px; }
.sev-opt {
  flex: 1; padding: 7px; text-align: center;
  border: 1px solid var(--gray-200); border-radius: var(--r);
  font-size: 12px; font-weight: 600; cursor: pointer; background: var(--white);
  transition: all 0.1s;
}
.sev-opt.sel-low { background: var(--green-bg); color: var(--green-txt); border-color: #86efac; }
.sev-opt.sel-med { background: var(--amber-bg); color: var(--amber-txt); border-color: #fcd34d; }
.sev-opt.sel-high { background: var(--red-bg); color: var(--red-txt); border-color: #fca5a5; }

/* MODAL */
.modal-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.55);
  display: flex; align-items: flex-end;
  z-index: 200;
}
.modal-box {
  background: var(--white);
  border-radius: 16px 16px 0 0;
  padding: 20px 16px 32px;
  width: 100%; max-height: 85vh;
  overflow-y: auto;
}
.modal-title { font-size: 16px; font-weight: 700; margin-bottom: 16px; color: var(--gray-800); display: flex; align-items: center; gap: 10px; }
.modal-title-text { flex: 1 1 auto; min-width: 0; }
/* Back arrow shown on every modal (UI.openModal). Circular tap target that
   closes the modal — same action as the OS back button / swipe-back. */
.modal-back {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; margin: -4px 0;
  padding: 0; border: none; border-radius: 50%;
  background: var(--gray-100); color: var(--gray-800);
  font-size: 20px; line-height: 1; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.12s ease;
}
.modal-back:hover { background: var(--gray-200); }
.modal-back:active { background: var(--gray-300); transform: scale(0.94); }
@media (max-width: 480px) { .modal-back { width: 40px; height: 40px; } }

/* TOAST */
.toast {
  position: fixed;
  /* Respect iOS home-indicator safe area; never overlap it */
  bottom: calc(80px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%);
  background: var(--gray-800); color: var(--white);
  padding: 12px 22px; border-radius: 22px;
  font-size: 14px; font-weight: 600;
  z-index: 300;
  max-width: calc(100vw - 40px);
  text-align: center;
  /* Wrap on phones when message is long instead of cutting off */
  white-space: normal;
  line-height: 1.35;
  box-shadow: 0 4px 16px rgba(0,0,0,0.25);
  animation: toast-in 0.2s ease;
}
@keyframes toast-in { from { opacity:0; transform: translateX(-50%) translateY(10px); } }

/* SECTION TITLE */
.section-title {
  font-size: 13px; font-weight: 600;
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 10px;
}
.section-title-sm { font-size: 11px; font-weight: 600; color: var(--gray-600); letter-spacing: 0.4px; text-transform: uppercase; margin-bottom: 8px; }

/* RANGE SLIDER */
input[type="range"] {
  -webkit-appearance: none; width: 100%; height: 4px;
  background: var(--gray-200); border-radius: 2px; outline: none;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 18px; height: 18px;
  border-radius: 50%; background: var(--gold); cursor: pointer;
}

/* RESPONSIVE */
@media (min-width: 600px) {
  .modal-overlay { align-items: center; }
  .modal-box { border-radius: 16px; max-width: 500px; }
  #panels-container { max-width: 700px; margin: 0 auto; }
}

/* ─── PHONE-FRIENDLY AUDIT FIXES ─────────────────────────────
   Issues found:
   1. Inline action buttons (📱, ✓, +) below 36px tap height → finger misses
   2. Nav tab strip scrolls horizontally but no visual hint
   3. Form rows collapse weirdly on narrow screens
   4. Modals at 85vh cut off content on small phones (iPhone SE = 667px)
   5. Tables in detail modals overflow horizontally without scroll wrapper
   6. Bulk-mode action bars stack awkwardly under 380px wide
*/

/* Tap targets — Apple HIG says 44px min. Bump all inline action buttons. */
.job-row button,
.job-right button,
.worker-head button,
button.btn-xs {
  min-height: 32px;
  min-width: 32px;
  /* Phones still need touch generosity; only force 44px on the smallest sizes */
}
@media (max-width: 480px) {
  .job-row button,
  .job-right button {
    min-height: 36px;
    padding: 6px 10px !important;
  }
  /* Stack the "+ Schedule" button below price on narrow phones so it
     doesn't get squeezed into nothing */
  .job-row .job-right {
    flex-shrink: 0;
  }
  /* Form rows stack cleanly */
  .form-row-2 {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .form-row-2 .form-group {
    margin-bottom: 12px;
  }
  /* Buttons in form-actions wrap with full width when stacked */
  .form-actions {
    flex-wrap: wrap;
    gap: 8px;
  }
  .form-actions button {
    flex: 1 1 auto;
    min-width: 100px;
    min-height: 44px;
  }
}

/* Modal sizing — phones get more vertical room + bottom padding for thumb */
@media (max-width: 599px) {
  .modal-box {
    max-height: 92vh;
    padding: 18px 14px 28px;
  }
}

/* Nav tab horizontal scroll hint — fading edge so the user sees there's more */
.nav-tabs {
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
  position: relative;
}
.nav-tabs::-webkit-scrollbar {
  height: 3px;
}
.nav-tabs::-webkit-scrollbar-thumb {
  background: var(--gray-300);
  border-radius: 2px;
}

/* Detail-modal line item tables can overflow on phones — wrap in a scroll
   container. Targets the table style used by EstimatesView/InvoicesView
   detail modals. */
.modal-box table {
  display: block;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  max-width: 100%;
}
@media (min-width: 600px) {
  .modal-box table {
    display: table;
    overflow-x: visible;
  }
}

/* Bulk-mode bars (#rc-bulk-bar, #ld-bulk-bar, #job-bulk-bar, #dm-bulk-bar):
   the All / None / status / Apply row gets cramped under 380px. Wrap and
   give each child a sane min-width. */
@media (max-width: 400px) {
  #rc-bulk-bar, #ld-bulk-bar, #job-bulk-bar, #dm-bulk-bar {
    padding: 8px !important;
    font-size: 11px;
  }
  #rc-bulk-bar button, #ld-bulk-bar button, #job-bulk-bar button, #dm-bulk-bar button,
  #rc-bulk-bar select, #ld-bulk-bar select, #job-bulk-bar select, #job-bulk-bar select {
    min-height: 36px;
  }
}

/* Cards (stat-card, worker-card, brief-card) get a little more breathing
   room on phones — easier to tap and scan. */
@media (max-width: 480px) {
  .stat-card, .worker-card {
    padding: 12px;
  }
}

/* Inline icon-only buttons (🔑 reset PIN, ✕ remove, etc.) get touch-padding */
button[title*="Reset"],
button[title*="Remove"],
button.li-row-remove,
.worker-card button {
  min-height: 36px;
  min-width: 36px;
}

/* The brief-card (Owner / Manager / Worker AI brief) is dense — give phones
   slightly bigger text so the AI advice stays readable. */
@media (max-width: 480px) {
  .ai-brief-body { font-size: 14px; line-height: 1.6; }
  .brief-line { font-size: 13px; line-height: 1.5; }
}

/* Sticky modal header so the modal title stays visible while scrolling
   long forms on phones. */
@media (max-width: 599px) {
  .modal-box .modal-title {
    position: sticky;
    top: -18px;
    background: var(--white);
    margin: -18px -14px 14px;
    padding: 18px 14px 10px;
    z-index: 2;
    border-bottom: 1px solid var(--gray-100);
  }
}
html.dark .modal-box .modal-title { background: #1f1e1a; }

/* Sticky map-panel header (Job Map / Measure) so the ✕ Close button doesn't
   scroll away behind a long shape/job list — same treatment as the modal
   title above, scoped to just these two panels. */
@media (max-width: 599px) {
  #panel-jobmap .map-panel-title,
  #panel-measure .map-panel-title {
    position: sticky;
    top: -14px;
    background: var(--white);
    margin: -14px -14px 14px;
    padding: 14px 14px 10px;
    z-index: 2;
    border-bottom: 1px solid var(--gray-100);
  }
}
html.dark #panel-jobmap .map-panel-title,
html.dark #panel-measure .map-panel-title { background: #1f1e1a; }

/* PWA install bar / iOS notch padding */
body {
  padding-top: env(safe-area-inset-top, 0);
  padding-bottom: env(safe-area-inset-bottom, 0);
}

/* Touch feedback: every button shows a subtle press state on tap. iOS
   defaults aren't visible enough on our dark/light themes. */
button:active,
.job-row:active,
.worker-card:active,
.clock-row:active,
.stat-card:active {
  transform: scale(0.98);
  transition: transform 0.05s ease;
  opacity: 0.92;
}
button {
  /* Remove iOS Safari tap highlight rectangle (we have our own :active) */
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0.05);
  /* Snap touches don't double-tap-zoom */
  touch-action: manipulation;
}

/* Customer info (phone numbers, emails, addresses) on detail modals
   should be long-press-selectable so the user can copy/share. By default
   on iOS, list rows have user-select:none from the modal styles. */
.stat-row,
.stat-row span,
.cust .info,
.cust .name,
[data-selectable="true"] {
  -webkit-user-select: text;
  user-select: text;
}

/* Larger focus outline on form inputs for accessibility on small screens */
@media (max-width: 480px) {
  .field-input:focus {
    outline: 3px solid var(--gold);
    outline-offset: -1px;
    border-color: var(--gold);
  }
}

/* Inputs sized for phone: prevent zoom-on-focus by ensuring >= 16px font.
   Below 16px, iOS Safari auto-zooms into the input on tap which is
   disorienting. */
input,
textarea,
select,
.form-group input,
.form-group select,
.form-group textarea {
  font-size: 16px;
}
@media (min-width: 600px) {
  /* Desktops can use smaller fonts comfortably */
  input,
  textarea,
  select,
  .form-group input,
  .form-group select,
  .form-group textarea {
    font-size: 14px;
  }
}

/* Sticky form-actions on phone so the Save button is always reachable
   without scrolling through 50 line items or 20 fields. */
@media (max-width: 599px) {
  .modal-box .form-actions {
    position: sticky;
    bottom: calc(-12px - env(safe-area-inset-bottom, 0px));
    background: var(--white);
    margin: 12px -14px -16px;
    padding: 12px 14px calc(12px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--gray-200);
    z-index: 3;
    box-shadow: 0 -3px 8px rgba(0,0,0,0.04);
  }
}
html.dark .modal-box .form-actions { background: #1f1e1a; border-top-color: #2c2b27; }

/* Job tools grid (job edit modal): secondary actions live in the scrolling
   body, not the sticky footer — the footer stays one row (Cancel / Save). */
.job-tools-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
.job-tools-grid button { min-height: 44px; margin: 0; }
@media (min-width: 480px) { .job-tools-grid { grid-template-columns: repeat(3, 1fr); } }

/* Forms get bottom padding so the sticky action bar never overlaps the
   last field. */
@media (max-width: 599px) {
  .modal-box {
    padding-bottom: 80px;
  }
}

/* Line item editor (UI.lineItemEditorHtml) has a 4-col grid for rate / qty /
   unit / amount. Cramped on narrow phones — stack to 2 cols. */
@media (max-width: 420px) {
  .li-row > div[style*="grid-template-columns:1fr 1fr 1fr 1fr"],
  .li-row > div[style*="grid-template-columns: 1fr 1fr 1fr 1fr"] {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
  }
}

/* CALENDAR (Jobs view) phone tuning. Event labels at 9px are unreadable
   on phones; cell height too cramped at 7 columns wide. */
@media (max-width: 480px) {
  #jobs-cal > div[style*="grid-template-columns:repeat(7, 1fr)"] > div[onclick] {
    font-size: 11px;
    padding: 3px 2px !important;
  }
  /* Bump event-label font + tighten padding */
  #jobs-cal div[style*="background:#22c55e"],
  #jobs-cal div[style*="background:#3b82f6"],
  #jobs-cal div[style*="background:#ef4444"] {
    font-size: 10px !important;
    padding: 2px 4px !important;
  }
  /* Sticky month nav so you don't have to scroll up to change months */
  #jobs-cal > div:first-child {
    position: sticky;
    top: 0;
    background: var(--white);
    z-index: 5;
    padding: 6px 0;
  }
}

/* Notes/long-text columns on stat-row max-width 180px overflow on narrow
   phones. Allow them to wrap onto a second line. */
@media (max-width: 420px) {
  .stat-row {
    flex-wrap: wrap;
  }
  .stat-row > span[style*="max-width"] {
    max-width: 100% !important;
    text-align: left !important;
    margin-top: 4px;
  }
}

/* Horizontally-scrolling containers inside a modal get a subtle right-edge
   scroll hint. IMPORTANT: only match *horizontal* scroll (overflow-x). The
   earlier version also matched `overflow:auto`/`overflow: auto`, which is what
   wraps most modal bodies (vertical scroll) — painting the fade gradient over
   the whole form. With `background-attachment: local` those gradient layers
   scroll with the content, so they appeared as opaque blocks sliding over the
   fields (very visible in dark mode, where the hard-coded `white` clashed with
   the dark surface). The `--modal-surface` var keeps the fade matching the
   theme instead of forcing white. */
:root { --modal-surface: #ffffff; }
html.dark { --modal-surface: #1f1e1a; }
@media (max-width: 480px) {
  .modal-box > div[style*="overflow-x:auto"],
  .modal-box > div[style*="overflow-x: auto"],
  .modal-box > div[style*="overflow-x:scroll"],
  .modal-box > div[style*="overflow-x: scroll"] {
    background:
      linear-gradient(to right, var(--modal-surface) 30%, rgba(255,255,255,0) 70%),
      linear-gradient(to right, rgba(0,0,0,0.08), var(--modal-surface) 30%) 0 100%,
      radial-gradient(farthest-side at 100% 50%, rgba(0,0,0,0.1), transparent) 100% 0,
      radial-gradient(farthest-side at 0 50%, rgba(0,0,0,0.1), transparent) 0 0;
    background-repeat: no-repeat;
    background-size: 40px 100%, 40px 100%, 14px 100%, 14px 100%;
    background-attachment: local, local, scroll, scroll;
  }
}

/* PRODUCTION FORM */
.prod-form { background: var(--gray-50); border-radius: var(--r); padding: 14px; border: 1px solid var(--gray-200); }

/* EMPTY STATE */
.empty-state { text-align: center; padding: 32px 16px; color: var(--gray-400); font-size: 13px; }

/* SCROLL FIX */
.scroll-x { overflow-x: auto; scrollbar-width: none; }
.scroll-x::-webkit-scrollbar { display: none; }

/* ── Address autocomplete dropdown ───────────────────────── */
.addr-ac-dropdown {
  z-index: 99999;
  background: #2a2a2a;
  border: 1px solid #444;
  border-radius: 8px;
  max-height: 230px;
  overflow-y: auto;
  box-shadow: 0 8px 24px rgba(0,0,0,.45);
}
.addr-ac-item {
  padding: 10px 12px;
  cursor: pointer;
  color: #eee;
  font-size: 14px;
  line-height: 1.3;
  border-bottom: 1px solid #333;
}
.addr-ac-item:last-child { border-bottom: none; }
.addr-ac-item:hover,
.addr-ac-item.active { background: #f5a623; color: #1a1a1a; }

/* ─── DARK MODE (audit #20) ──────────────────────────────────────
   Inverts the gray scale + body backgrounds. Brand colors (--gold,
   --gold-dk, --navy) stay -- they're brand identity, not chrome.
   Triggered by adding class="dark" on <html>; UI.applyDarkMode reads
   localStorage('abp-dark') at boot to persist the choice. */
html.dark {
  --gray-50:  #1f1f1d;
  --gray-100: #262624;
  --gray-200: #34332f;
  --gray-300: #45433d;
  --gray-400: #888880;
  --gray-500: #a8a8a0;
  --gray-600: #c8c8c0;
  --gray-700: #e0e0d8;
  --gray-800: #f0f0e8;
  --black: #f5f5ef;        /* "black" becomes light in dark mode */
  --white: #1a1a18;
  --shadow: 0 1px 4px rgba(0,0,0,0.4);
  background: #14130f;
  color: var(--gray-800);
}
html.dark body { background: #14130f; color: var(--gray-800); }
html.dark .card,
html.dark .stat-card,
html.dark .worker-card,
html.dark .job-row,
html.dark .clock-row,
html.dark .modal-box,
html.dark .login-card,
html.dark .brief-card { background: #1f1e1a; color: var(--gray-700); }
html.dark .field-input,
html.dark .form-control,
html.dark input,
html.dark select,
html.dark textarea {
  background: #262522;
  color: var(--gray-700);
  border-color: #3a3934;
}
html.dark .field-input::placeholder,
html.dark input::placeholder,
html.dark textarea::placeholder { color: #5f5e5a; }
html.dark .btn-secondary {
  background: #2c2b27;
  color: var(--gray-700);
  border-color: #3a3934;
}
html.dark .empty-state { background: #1a1916; border-color: #2c2b27; color: var(--gray-500); }
html.dark .topbar { background: #1a1916; border-bottom-color: #2c2b27; }
html.dark .nav-tabs { background: #1a1916; border-bottom-color: #2c2b27; }
html.dark .nav-tab { color: var(--gray-500); }
html.dark .nav-tab.active { color: var(--gold); }
html.dark .weather-bar { background: #1a1916; border-bottom-color: #2c2b27; color: var(--gray-600); }
html.dark .modal-overlay { background: rgba(0, 0, 0, 0.8); }
html.dark details summary { color: var(--gray-700); }
html.dark .badge-gray { background: #2c2b27; color: var(--gray-600); }
html.dark a { color: #f5a623; }

/* ============================================================
   BOTTOM TAB BAR (native-style nav) — replaces the top scroll strip
   ============================================================ */
.nav-tabs { display: none; }            /* old 24-tab top strip retired */

#bottom-nav {
  position: fixed; left: 0; right: 0; bottom: 0;
  display: none;                         /* shown by UI.renderBottomNav */
  background: var(--white);
  border-top: 1px solid var(--gray-200);
  z-index: 60;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  box-shadow: 0 -2px 12px rgba(0,0,0,0.07);
}
.bnav-btn {
  flex: 1 1 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 3px;
  padding: 8px 2px 6px; min-height: 54px;
  border: none; background: none; cursor: pointer;
  color: var(--gray-500); -webkit-tap-highlight-color: transparent;
}
.bnav-btn:active { background: var(--gray-100); }
.bnav-btn.active { color: var(--gold); }
.bnav-icon { font-size: 21px; line-height: 1; }
.bnav-label { font-size: 10px; font-weight: 600; letter-spacing: .2px; }

/* keep content clear of the fixed bottom bar */
#panels-container { padding-bottom: calc(74px + env(safe-area-inset-bottom, 0px)); }

/* "More" overflow sheet — grouped 2-column tap grid */
.more-list { display: flex; flex-direction: column; }
.more-group {
  font-size: 10px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase;
  color: var(--gray-400); padding: 16px 2px 6px;
}
.more-group:first-child { padding-top: 2px; }
.more-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.more-item {
  display: flex; align-items: center; text-align: left;
  padding: 14px 12px; min-height: 50px;
  border: 1px solid var(--gray-200); background: var(--gray-50);
  border-radius: 12px;
  font-size: 14px; font-weight: 600; line-height: 1.25; color: var(--gray-800);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.more-item:active { background: var(--gray-100); transform: scale(.98); }

html.dark #bottom-nav { background: #1a1916; border-top-color: #2c2b27; }
/* NOTE: the dark-theme retune below remaps --gray-200 to a DARK slate, so the
   old `color: var(--gray-200)` rendered dark text on a dark tile (invisible).
   Use the high-contrast ink token + asphalt surfaces to match the rest of dark. */
html.dark .more-item { color: var(--ink-hi); background: var(--asphalt-800); border-color: var(--asphalt-600); }
html.dark .more-item:active { background: var(--asphalt-700); }
html.dark .more-group { color: var(--ink-mid); }

/* Schedule tab — agenda rows + section labels */
.sched-section-label {
  font-size: 11px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase;
  color: var(--gray-500); margin: 4px 0 8px;
}
.sched-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--white); border: 1px solid var(--gray-200);
  border-radius: 10px; padding: 10px 12px; margin-bottom: 8px;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.sched-row:active { background: var(--gray-50); }
.sched-when {
  flex-shrink: 0; width: 92px; font-size: 11px; font-weight: 700;
  color: var(--gray-600); line-height: 1.25;
}
.sched-main { flex: 1; min-width: 0; }
.sched-name {
  font-size: 14px; font-weight: 700; color: var(--gray-900);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sched-sub {
  font-size: 12px; color: var(--gray-500); margin-top: 2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
html.dark .sched-row { background: #232220; border-color: #2c2b27; }
html.dark .sched-row:active { background: #2c2b27; }
/* retune remapped --gray-100 to a DARK slate, so this rendered dark-on-dark
   on the asphalt sched-row (same trap fixed for .more-item above). */
html.dark .sched-name { color: var(--ink-hi); }

/* ============================================================
   UX + APP STORE POLISH — readability, tap targets, iOS zoom, notch
   (override block: later rules win, so these tune the base styles
   above without rewriting them)
   ============================================================ */

/* Bigger base text — field crews read this outdoors, in gloves, older eyes */
body { font-size: 15px; padding-top: 0; }   /* notch handling moves to .topbar */
.field-label { font-size: 12px; }

/* 16px input font stops iOS from auto-zooming the page on focus (the jarring
   zoom-in when you tap a field). Taller padding = easier to hit. */
.field-input { font-size: 16px; padding: 12px 12px; }

/* Tap targets on the real action buttons toward Apple's 44pt minimum */
.btn-primary { padding: 13px 20px; }
.btn-secondary, .btn-danger { padding: 12px 18px; }
.btn-xs { padding: 7px 12px; font-size: 12px; }

/* Notch fill: the navy top bar now extends up through the status-bar safe
   area (was leaving an off-color band on notched iPhones). Login screen
   already has a full-bleed navy gradient, so it's unaffected. */
.topbar { padding-top: calc(8px + env(safe-area-inset-top, 0px)); }
.login-wrap { padding-top: calc(24px + env(safe-area-inset-top, 0px)); }

/* Comfortable list rows — a touch more height so jobs/workers are easy to tap */
.job-row, .worker-card { min-height: 56px; }

/* ── Chip picker (production report: equipment + services) ───────── */
.chip-picker { margin-top: 2px; }
.chip-row { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  font: inherit; font-size: 13px; line-height: 1;
  padding: 8px 12px; border-radius: 999px; cursor: pointer;
  background: var(--gray-50, #f7f7f5); color: var(--gray-700, #444);
  border: 1px solid var(--gray-300, #d8d6cf);
  transition: background .12s, border-color .12s, color .12s;
  min-height: 34px;
}
.chip:hover { border-color: var(--navy); }
.chip-on {
  background: var(--navy); color: #fff; border-color: var(--navy);
  font-weight: 600;
}
.chip-on::before { content: "✓ "; }

/* ── Accessibility: keyboard focus indicator (WCAG 2.4.7) ──────────────────
   Several resets above set `outline: none`, which leaves keyboard-only users
   with no visible focus. `:focus-visible` restores a strong, high-contrast
   ring ONLY for keyboard / assistive-tech navigation — mouse clicks don't
   trigger it, so the visual design is unchanged for pointer users. The dark
   ring + light offset stays visible on both the dark login screen and the
   light app surfaces. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible,
.pin-btn:focus-visible,
.btn-primary:focus-visible {
  outline: 3px solid var(--gold, #f5a623);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.55);
  border-radius: 6px;
}

/* Respect the OS "reduce motion" setting app-wide (WCAG 2.3.3 / vestibular). */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ════════════════════════════════════════════════════════════════════
   "FRESH ASPHALT & HI-VIS STEEL" — premium design pass
   (paving-loop/DESIGN-DIRECTION.md, owner-approved)

   CSS-only. No markup/JS changes. Strategy per the spec: the new look IS
   the dark theme — so light mode is left untouched and cannot break. This
   block is appended last, so its rules win over the base styles without
   rewriting them, and the whole block is independently revertible.

   Items map 1:1 to §7 of the spec (#1–#9). #10 (the Google-Fonts <link>)
   is intentionally NOT done here because it requires editing index.html;
   the type rules below use graceful fallback stacks ('Arial Narrow',
   Impact for the squared display face) so they look right with or without
   a webfont. Add the <link> in a follow-up to get true Archivo/Inter.
   ════════════════════════════════════════════════════════════════════ */

/* ── #3 — Fix the accent: --gold becomes the hi-vis amber (was coral) ──
   One swap recolors active nav, focus rings, badges, dots, sliders and
   KPI accents app-wide to "safety hi-vis". Applies in BOTH themes so the
   brand accent is consistent; the gradient primary button is the CTA. */
:root{ --gold:#FFB200; --gold-dk:#E08A00; }
.btn-primary{
  background:linear-gradient(180deg,var(--hivis-bright),var(--hivis));
  color:#1A1305; font-weight:700;
  box-shadow:0 0 0 1px rgba(255,178,0,.35),0 8px 20px -8px rgba(255,178,0,.45);
}
.btn-primary:active{ background:var(--hivis-deep); }
/* A <label class="btn-primary"> (camera "📷 Take Photo" pickers) inside a
   .form-group picked up `.form-group label`'s gray ink (0,1,1 beats 0,1,0):
   ~1.3:1 on the gold. Give it the button's ink. */
.form-group label.btn-primary, label.btn-primary { color:#1A1305; }

/* ── #4 — Make the money KPIs the hero (squared, heavy, tabular) ──
   .metric-num is the dashboard number; tabular figures line money up.
   These are theme-agnostic (type only); color is left to existing rules
   / JS inline styles (profit goes green/red at runtime — preserved). */
.metric-num{
  font-family:Archivo,'Arial Narrow',Impact,sans-serif;
  font-weight:800; letter-spacing:-.01em; font-variant-numeric:tabular-nums;
}

/* ─────────────────────────────────────────────────────────────────────
   The rest is scoped to html.dark — the "Asphalt & Hi-Vis" surface.
   ───────────────────────────────────────────────────────────────────── */

/* ── #1 — Re-tune the dark palette to the asphalt + ink ramp ──
   Remap the gray scale / black / white the whole app reads, so every
   card, row, modal and chrome element inherits the blacked-out look. */
html.dark{
  --gray-50:#16181D; --gray-100:#1C1F26; --gray-200:#262A33;
  --gray-300:#333845; --gray-400:#8A92A2; --gray-500:#8A92A2;
  --gray-600:#AEB6C4; --gray-700:#D7DCE5; --gray-800:#F4F6FA;
  --gray-900:#F4F6FA;
  --white:#1C1F26;            /* raised card surface */
  --black:#F4F6FA;            /* "black" reads light on dark */
  --rl:18px;                  /* softer card corners (#2) */
  --shadow:var(--elev-2);
  background:#0E0F12;
  color:var(--ink-hi);
}
html.dark body{ background:#0E0F12; color:var(--ink-hi); }

/* ── #2 — Real elevation on cards (kills the "spreadsheet" feel) ──
   Layered dark shadow + 1px top inner highlight (the "metal edge"),
   tiered radius. Cards lift off the substrate instead of sitting flat. */
html.dark .card,
html.dark .stat-card,
html.dark .worker-card,
html.dark .job-row,
html.dark .clock-row,
html.dark .sched-row{
  background:linear-gradient(180deg,#1F232B,#191C23);
  border:1px solid var(--asphalt-600);
  border-radius:var(--r-lg);
  box-shadow:var(--elev-2);
}
html.dark .modal-box,
html.dark .login-card{
  background:#1C1F26;
  border:1px solid var(--asphalt-600);
  box-shadow:var(--elev-3);
}

/* ── #4 (cont.) — Hero the four money KPIs; profit tile = the money shot ──
   stat-card already restyled above; the profit tile gets the amber halo.
   NOTE: owner.js sets an inline border-left (green/red) on #d-profit-card
   at runtime, which wins over border-color — so the "money shot" is the
   amber box-shadow halo here, which inline styles do NOT override. */
html.dark #d-profit-card{
  box-shadow:0 1px 0 rgba(255,255,255,.05) inset,
             0 8px 24px -8px rgba(255,178,0,.25);
  border-color:rgba(255,178,0,.35);
}

/* ── #5 — Weather bar: from flat text to an instrument readout ──
   Gauge-style gradient strip; the GOOD/STOP status becomes a pill. The
   danger pill reuses the existing pulse-dot animation via .danger only. */
html.dark .weather-bar{
  background:linear-gradient(180deg,#16181D,#101216);
  border-bottom:1px solid var(--asphalt-600);
  color:var(--ink-mid); font-weight:600;
}
html.dark .weather-status{
  padding:3px 10px; border-radius:999px; letter-spacing:.4px;
  background:rgba(52,211,153,.14); color:#34D399; font-weight:700;
}
html.dark .weather-status.warn{
  background:rgba(255,178,0,.14); color:var(--hivis);
}
html.dark .weather-status.danger{
  background:rgba(251,107,91,.14); color:#FB6B5B; font-weight:700;
}
html.dark .weather-status.neutral{
  background:rgba(255,255,255,.08); color:var(--ink-mid); font-weight:600;
}

/* ── #6 — Bottom nav: floating glass + amber active pill ── */
html.dark #bottom-nav{
  background:rgba(22,24,29,.92); backdrop-filter:blur(12px);
  -webkit-backdrop-filter:blur(12px);
  border-top:1px solid var(--asphalt-600);
}
html.dark .bnav-btn.active{ color:var(--hivis); }
html.dark .bnav-btn.active .bnav-icon{
  filter:drop-shadow(0 0 8px rgba(255,178,0,.5));
}

/* ── #7 — Inputs / focus state premium-up ──
   Sunken well fill, soft radius, amber focus ring (the hi-vis glow). */
html.dark .field-input,
html.dark .form-group input,
html.dark .form-group select,
html.dark .form-group textarea,
html.dark input,
html.dark select,
html.dark textarea{
  background:#14161B; border:1px solid var(--asphalt-600);
  border-radius:12px;
}
html.dark .field-input:focus,
html.dark .form-group input:focus,
html.dark .form-group select:focus,
html.dark .form-group textarea:focus{
  border-color:var(--hivis);
  box-shadow:0 0 0 3px var(--hivis-glow); outline:none;
}

/* ── #8 — Topbar glass + AI brief re-skin with amber edge stripe ──
   Topbar gets the asphalt glass treatment; the AI briefing card drops the
   navy gradient for asphalt with a single hi-vis left-edge "stripe". */
html.dark .topbar{
  background:rgba(14,15,18,.85); backdrop-filter:blur(12px);
  -webkit-backdrop-filter:blur(12px);
  border-bottom:1px solid var(--asphalt-600);
}
/* Chrome buttons ride the dark glass bars in both themes, but base
   .icon-btn / .btn-xs use color:var(--white), which this palette remaps to
   #1C1F26 (a DARK surface) -> the topbar lock/log-out icons and the weather
   "Update" button rendered invisible (~1.3:1). Use light ink. Scoped to the
   bars so the many reused .btn-xs (gold/navy/white inline bg in cards) keep
   their own colors. */
html.dark .icon-btn,
html.dark .weather-bar .btn-xs{ color:var(--ink-hi); }
html.dark .ai-brief{
  background:linear-gradient(180deg,#1B1F27,#15181E);
  border:1px solid var(--asphalt-600);
  border-left:3px solid var(--hivis);
  border-radius:var(--r-lg);
}
html.dark .ai-brief-label,
html.dark .ai-brief-body strong,
html.dark .brief-bullet,
html.dark .brief-label{ color:var(--hivis); }
html.dark .ai-brief-body{ color:var(--ink-mid); }
html.dark .ai-brief-label::before{ background:var(--hivis); }
/* brief-card (Owner/Manager/Worker brief) shares the asphalt treatment */
html.dark .brief-card{
  background:linear-gradient(180deg,#1B1F27,#15181E);
  border:1px solid var(--asphalt-600);
  border-left:3px solid var(--hivis);
}

/* ── #9 — Skeleton shimmer utility (replaces "Loading…" / "—" placeholders) ──
   Utility only — JS owns the placeholder text today, so this is opt-in:
   apply class="skeleton" (optionally with an inline width/height) where a
   value is loading. Honors prefers-reduced-motion via the global rule. */
.skeleton{
  display:inline-block; border-radius:6px; color:transparent !important;
  background:linear-gradient(90deg,
    rgba(255,255,255,.04) 25%, rgba(255,255,255,.10) 37%,
    rgba(255,255,255,.04) 63%);
  background-size:400% 100%;
  animation:skeleton-shimmer 1.4s ease infinite;
}
@keyframes skeleton-shimmer{ 0%{background-position:100% 0} 100%{background-position:-100% 0} }

/* Status-bar / address autocomplete already dark; nudge the address
   dropdown onto the new amber accent for consistency. */
html.dark .addr-ac-item:hover,
html.dark .addr-ac-item.active{ background:var(--hivis); color:#1A1305; }

/* ── TINTED SURFACES (theme-aware callouts / highlighted rows) ──────────
   Views used to paint "needs attention" rows and callouts with a hard-coded
   pastel (#fffbeb, #f0fdf4, #eff6ff, #fef2f2, #f9fafb) inline. In dark mode
   the text flips to light while the inline pastel stays, so the Bids list
   showed near-white names on a cream card. These classes give each tint a
   light-mode look identical to the old inline hex and a dark-mode variant
   that keeps the same hue as a dim wash + colored edge. Text colour
   inherits unless the -txt variant is added. Rules sit after the html.dark
   card/row rules above so they win on specificity + order. */
.tint-amber        { background:#fffbeb; border-color:#fde68a; }
.tint-amber-strong { background:#fef3c7; border-color:#fcd34d; }
.tint-green        { background:#f0fdf4; border-color:#bbf7d0; }
.tint-blue         { background:#eff6ff; border-color:#bfdbfe; }
.tint-red          { background:#fef2f2; border-color:#fca5a5; }
.tint-gray         { background:#f9fafb; border-color:var(--gray-200); }
.tint-amber-txt    { color:#92400e !important; }
.tint-green-txt    { color:#15803d !important; }
.tint-blue-txt     { color:#1e40af !important; }
.tint-red-txt      { color:#b91c1c !important; }
/* !important so the -txt colour beats the html.dark .card.tint-* colour
   rule below (higher specificity); no inline colour is ever paired with
   these classes, so nothing else needs to win. */
.tint-amber-txt a  { color:inherit; }

html.dark .tint-amber,
html.dark .tint-amber-strong,
html.dark .job-row.tint-amber,
html.dark .card.tint-amber,
html.dark .brief-card.tint-amber { background:#2A2412; border-color:#6B5414; color:var(--gray-700); }
html.dark .tint-green,
html.dark .job-row.tint-green,
html.dark .card.tint-green   { background:#122619; border-color:#1F5A33; color:var(--gray-700); }
html.dark .tint-blue,
html.dark .card.tint-blue    { background:#141E2E; border-color:#27436B; color:var(--gray-700); }
html.dark .tint-red,
html.dark .card.tint-red     { background:#2A1516; border-color:#6B2224; color:var(--gray-700); }
html.dark .tint-gray,
html.dark .card.tint-gray    { background:#1C1F26; border-color:var(--asphalt-600); color:var(--gray-700); }
html.dark .tint-amber-txt    { color:#FCD34D !important; }
html.dark .tint-green-txt    { color:#6EE7A0 !important; }
html.dark .tint-blue-txt     { color:#93C5FD !important; }
html.dark .tint-red-txt      { color:#FCA5A5 !important; }
html.dark mark.tint-amber-strong { color:var(--gray-800); }

/* Outline buttons on list rows: navy/gray on a dark row was ~2:1 contrast
   (the "+ Schedule" / "Take Off" buttons in the Bids list). */
.btn-outline-navy { background:none; border:1px solid var(--navy); color:var(--navy); }
.btn-outline-gray { background:none; border:1px solid var(--gray-300); color:var(--gray-500); }
html.dark .btn-outline-navy { border-color:#8FB3E0; color:#8FB3E0; }
/* Home "Team Profit-to-Pay" card: the inline pastel bg is light-mode only.
   Neutral text uses the gray tokens inline (owner.js); the mood accents
   (header, %, crew pay, profit) are light-mode hexes from UI's margin bands
   (~2–3.4:1 on this surface) — lift them to ≥6:1 here. */
html.dark .ptp-card { background:#1f1e1a !important; }
html.dark .ptp-card [style*="rgba(255,255,255,0.6)"] { background:rgba(255,255,255,0.05) !important; }
html.dark .ptp-card [style*="color:#15803d"] { color:#4ADE80 !important; }
html.dark .ptp-card [style*="color:#b45309"] { color:#FBBF24 !important; }
html.dark .ptp-card [style*="color:#dc2626"] { color:#F87171 !important; }
html.dark .ptp-card [style*="color:#991b1b"] { color:#FCA5A5 !important; }
html.dark .ptp-card [style*="color:#4b5563"] { color:var(--gray-600) !important; }
html.dark .btn-outline-gray { border-color:var(--gray-400); color:var(--gray-600); }

/* ── Native shell (Capacitor) — the app must not feel like a web page ── */
html.is-native, html.is-native body {
  -webkit-user-select: none; user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}
html.is-native input, html.is-native textarea, html.is-native select,
html.is-native [contenteditable], html.is-native [contenteditable] * {
  -webkit-user-select: text; user-select: text; -webkit-touch-callout: default;
}
html.is-native img, html.is-native a { -webkit-user-drag: none; }
/* contentInset is 'never': the web view runs edge to edge, so the notch strip
   is painted by the sticky navy .topbar (safe-area padded), never a white bar. */
html.is-native .login-wrap { padding-top: calc(24px + env(safe-area-inset-top, 0px)); }
#native-page-sheet { position: fixed; inset: 0; z-index: 10000; background: #fff; display: flex; flex-direction: column; }
#native-page-sheet .nps-bar { background: var(--navy); padding: calc(8px + env(safe-area-inset-top, 0px)) 14px 8px; display: flex; justify-content: flex-end; }
#native-page-sheet .nps-close { background: none; border: 0; color: #fff; font-size: 17px; font-weight: 600; padding: 6px 4px; }
#native-page-sheet .nps-frame { flex: 1; border: 0; width: 100%; padding-bottom: env(safe-area-inset-bottom, 0px); }

/* ── Native navigation (js/tabbar.js): tab bar, push transitions, pull-to-refresh ── */
html.is-native #bottom-nav {
  background: rgba(255,255,255,0.92);
  -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px);
  box-shadow: none; border-top: 0.5px solid rgba(0,0,0,0.18);
}
html.is-native.dark #bottom-nav { background: rgba(26,25,22,0.92); border-top-color: rgba(255,255,255,0.12); }
html.is-native .bnav-btn { min-height: 49px; padding: 6px 2px 4px; }
html.is-native .bnav-btn:active { background: none; opacity: .6; }
html.is-native .bnav-label { font-size: 10px; font-weight: 500; }
html.is-native #panels-container { padding-bottom: calc(66px + env(safe-area-inset-bottom, 0px)); }
html.is-native body, html.has-ptr body { overscroll-behavior-y: none; }
@keyframes tabFade { from { opacity: .35; } to { opacity: 1; } }
html.is-native #panels-container.tab-fade { animation: tabFade .18s ease-out; }
@keyframes nativePush { from { transform: translateX(100%); } to { transform: translateX(0); } }
html.is-native #modal-box.native-push { animation: nativePush .28s cubic-bezier(.2,.8,.2,1); }
#ptr-indicator {
  position: fixed; left: 50%; top: calc(env(safe-area-inset-top, 0px) + 56px); z-index: 70;
  width: 34px; height: 34px; border-radius: 50%; background: var(--white);
  box-shadow: 0 2px 8px rgba(0,0,0,.18); display: flex; align-items: center; justify-content: center;
  transform: translate(-50%,-44px); opacity: 0; pointer-events: none; transition: opacity .15s;
}
#ptr-indicator .ptr-spin { width: 16px; height: 16px; border: 2px solid var(--gray-300); border-top-color: var(--navy); border-radius: 50%; }
#ptr-indicator.ready .ptr-spin { border-top-color: var(--gold); }
#ptr-indicator.spinning { transition: transform .2s; }
#ptr-indicator.spinning .ptr-spin { animation: ptrSpin .7s linear infinite; }
@keyframes ptrSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  html.is-native #modal-box.native-push, html.is-native #panels-container.tab-fade { animation: none; }
}

/* ── Native device features (js/native-device.js) ── */
#outbox-pill{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(env(safe-area-inset-bottom,0px) + 74px);z-index:900;
  background:#1f2937;color:#fff;border:0;border-radius:999px;padding:8px 14px;font-size:13px;font-weight:600;
  box-shadow:0 4px 14px rgba(0,0,0,.25);max-width:calc(100vw - 32px);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
#nd-bio-lock{position:fixed;inset:0;z-index:10000;background:#1a1a1a;color:#fff;display:flex;align-items:center;justify-content:center;
  padding:env(safe-area-inset-top,0) 16px env(safe-area-inset-bottom,0)}
#nd-bio-lock .nd-lock-inner{display:flex;flex-direction:column;align-items:center;gap:14px;width:100%;max-width:320px}
#nd-bio-lock .nd-lock-title{font-size:18px;font-weight:700;margin-bottom:6px}
#nd-bio-lock button{width:100%}
.nd-contact-btn{min-height:44px;display:flex;align-items:center;justify-content:center}

/* ── Owner nav redesign (5-tab bar, segmented Jobs / Crew, SealCycle pill) ── */
/* Header SealCycle pill — rides in the weather bar on every owner screen. */
.seal-pill {
  order: -1; display: inline-flex; align-items: center; gap: 4px;
  min-height: 44px; padding: 0 14px; border-radius: 999px;
  background: var(--gold); color: #1a1a1a; border: none;
  font-size: 13px; font-weight: 700; white-space: nowrap; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.seal-pill:active { background: var(--gold-dk); }
.seal-pill.has-due { box-shadow: 0 0 0 2px #fff inset; }
html.dark .seal-pill { color: #1a1a1a; }
/* Segment control at the top of Jobs (List | Map | Calendar) and Crew
   (Time Clock | Morning Brief | Team). */
.seg-ctl {
  display: flex; gap: 4px; background: var(--gray-100);
  border-radius: 10px; padding: 4px; margin-bottom: 12px;
}
.seg-btn {
  flex: 1 1 0; min-height: 44px; border: none; border-radius: 7px;
  background: transparent; color: var(--gray-600);
  font-size: 13px; font-weight: 600; cursor: pointer; white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
}
.seg-btn.active { background: var(--white); color: var(--gray-800); box-shadow: 0 1px 3px rgba(0,0,0,.08); }
/* Count badge on a bottom-bar tab (UI.setNavBadge). */
.bnav-icon { position: relative; }
.bnav-badge {
  position: absolute; top: -6px; left: 60%;
  min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;
  border-radius: 9px; background: var(--red); color: #fff;
  font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
  box-shadow: 0 0 0 2px var(--white); pointer-events: none;
}
/* HIG 44px tap targets for the small inline buttons on phones. */
@media (max-width: 600px) {
  button.btn-xs, .btn-xs { min-height: 44px; }
  /* Header action buttons ("+ Assign", "+ New", "📊 Export Summary",
     "📸 Instant Measure", "⬇ CSV") are btn-primary/btn-secondary with an
     inline padding:0 12px, which collapsed them to their 15-17px line
     height — the btn-xs rule above never matched them. */
  button.btn-primary, button.btn-secondary { min-height: 44px; }
  /* Topbar icon buttons (bell / switch account / log out): 44×44 for every
     role on phones — the pointer:coarse rule below didn't reach the crew's. */
  .topbar-right .icon-btn { width: 44px; height: 44px; min-width: 44px; min-height: 44px; }
  /* "✎ Adjust hours" on the Hours worker cards was 21px tall. */
  .adj-hours-btn { min-height: 44px; }
}

/* Touch targets: small buttons/chips get a 44px hit area on touch screens
   (Apple HIG). Desktop mouse layouts keep their compact size. */
@media (pointer:coarse){
  button.btn-xs, label.btn-xs, .icon-btn, .sev-opt, .chip { min-height:44px; min-width:44px }
}

/* App Store 3.1.1: no web-pricing links inside the native app. native.js
   puts .is-native on <html> before first paint. */
.is-native .web-only { display:none !important; }

/* ── First-run tour + Help & FAQ (js/tour.js) ─────────────────────────
   Sits above the bottom nav (60), modals (200) and the outbox pill (900),
   below the Terms consent gate (100000). The spotlight is a hole made with
   a huge box-shadow, so the real button shows through untouched. */
#tour-root { position: fixed; inset: 0; z-index: 9000; }
#tour-root .tour-block { position: absolute; inset: 0; background: transparent; }
#tour-root.tour-centered .tour-block { background: rgba(0,0,0,.6); }
#tour-root .tour-spot {
  position: fixed; display: none; pointer-events: none; border-radius: 12px;
  box-shadow: 0 0 0 9999px rgba(0,0,0,.6), 0 0 0 3px var(--gold) inset;
  transition: left .2s ease, top .2s ease, width .2s ease, height .2s ease;
}
#tour-root .tour-card {
  position: fixed; width: min(360px, calc(100vw - 24px));
  max-height: calc(100vh - 24px - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  overflow-y: auto; padding: 16px; border-radius: 14px;
  background: var(--white); color: var(--gray-800); border: 1px solid var(--gray-200);
  box-shadow: 0 12px 40px rgba(0,0,0,.35);
}
#tour-root.tour-centered .tour-card {
  left: 50% !important; top: 50% !important; bottom: auto !important; transform: translate(-50%, -50%);
}
#tour-root .tour-wave { font-size: 34px; line-height: 1; margin-bottom: 6px; }
#tour-root .tour-title { font-size: 19px; font-weight: 800; margin-bottom: 6px; }
#tour-root .tour-body { font-size: 15px; line-height: 1.5; margin-bottom: 14px; }
#tour-root .tour-count { font-size: 11px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase; color: var(--gray-500); margin-bottom: 6px; }
#tour-root .tour-foot { font-size: 12px; color: var(--gray-500); text-align: center; margin-top: 10px; line-height: 1.4; }
#tour-root .tour-skip-welcome { margin-top: 8px; }
#tour-root .tour-actions { display: flex; align-items: center; gap: 8px; }
#tour-root .tour-actions .btn-primary,
#tour-root .tour-actions .btn-secondary { width: auto; min-height: 44px; padding: 0 16px; }
#tour-root .tour-spacer { flex: 1; }
.tour-link {
  background: none; border: none; padding: 0 4px; min-height: 44px; cursor: pointer;
  color: var(--gray-600); font-size: 14px; text-decoration: underline;
}
.tour-link.tour-inline { min-height: 0; padding: 0; color: var(--gold-dk, #b45309); font-size: inherit; }
html.dark .tour-link.tour-inline { color: var(--gold); }

.tour-replay-card { margin-bottom: 16px; border-left: 3px solid var(--gold); }
.tour-faq-item { border: 1px solid var(--gray-200); border-radius: var(--r); margin-bottom: 8px; background: var(--white); overflow: hidden; }
.tour-faq-item > summary {
  list-style: none; cursor: pointer; padding: 13px 40px 13px 14px; position: relative;
  font-size: 14px; font-weight: 600; line-height: 1.4; color: var(--gray-800); min-height: 44px;
}
.tour-faq-item > summary::-webkit-details-marker { display: none; }
.tour-faq-item > summary::after { content: '▼'; position: absolute; right: 14px; top: 15px; font-size: 11px; color: var(--gray-400); transition: transform .2s; }
.tour-faq-item[open] > summary::after { transform: rotate(180deg); }
.tour-faq-a { padding: 12px 14px 14px; font-size: 14px; line-height: 1.65; color: var(--gray-600); border-top: 1px solid var(--gray-100); }
@media (prefers-reduced-motion: reduce) { #tour-root .tour-spot { transition: none; } }
