/* ── Design tokens — dark (default) ─────────────────────────────────────── */
:root {
  --bg: #07070f;
  --surface: #0e0e1a;
  --card-bg: var(--surface); /* resolves per-theme; was referenced but never defined */
  --surface2: #141426;
  --surface3: #1c1c30;
  --stroke: rgba(255,255,255,.07);
  --stroke2: rgba(255,255,255,.13);

  --text: #fff;
  --text1: #fff;
  --text2: rgba(255,255,255,.72);
  --muted: #7a7a9a;
  /* Was .32 -- contrast against surface3 was only 2.89:1, well under WCAG's
     4.5:1 minimum for normal-size text. Bumped so every dark surface clears
     4.5:1 (worst case surface3 lands at ~4.68:1). */
  --muted2: rgba(255,255,255,.47);

  --primary: #2563EB;
  --primary-dark: #1d4ed8;
  /* --primary at full saturation reads great as a button background (white
     text on it is a clean 5.17:1), but used directly AS text on a dark
     surface it was only 3.2-3.9:1 -- under the 4.5:1 minimum for normal
     text, and this brand blue is used as text/link color in ~80 places
     (active tabs, "Repeat Last" style labels, underlined links, the pace
     line in the hero). --primary-text is a lightened variant for exactly
     those text-on-dark-background usages; --primary itself is untouched so
     buttons/borders/backgrounds keep their original color. */
  --primary-text: #6692f1;
  --accent: #2563EB;
  --danger: #ef4444;
  /* Text color for buttons/badges whose background IS --primary (.btn.primary,
     .fr26BtnPrimary, etc). White on this blue is a clean 5.17:1, so it stays
     the default here and for Classic. The preset themes below override it —
     see the comment on those blocks. */
  --on-primary: #fff;

  /* --r-xs added during a sizing-consolidation pass: several small elements
     (tags, thin skeleton bars, tiny dots) were hardcoding 4px instead of
     using a token — this gives them one instead of inventing a one-off.
     Some genuinely one-off radii (2/3/5/6/9/10/13/14/24px) remain literal on
     purpose: they're single-use micro-details that don't belong to this
     card/button scale, and snapping them to the nearest step would visibly
     round them more or less than designed — out of scope for a pass that's
     meant to be a no-visual-change cleanup. */
  --r-xs: 4px;
  --r-sm: 8px;
  --r-md: 12px;
  --r-lg: 16px;
  --r-xl: 20px;
  --r-pill: 999px;

  --shadow-sm: 0 1px 3px rgba(0,0,0,.4);
  --shadow-md: 0 4px 16px rgba(0,0,0,.5);
  --shadow-lg: 0 12px 40px rgba(0,0,0,.6);

  --ease: cubic-bezier(.25,.46,.45,.94);
  --ease-bounce: cubic-bezier(.34,1.56,.64,1);
}

/* ── Light tokens — system auto (CSS level, no JS needed) ───────────────── */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f2f2f7;
    --surface: #ffffff;
    --surface2: #e5e5ea;
    --surface3: #d1d1d6;
    --stroke: rgba(0,0,0,.12);
    --stroke2: rgba(0,0,0,.18);
    --text: #000000;
    --text1: #000000;
    --text2: rgba(0,0,0,.60);
    --muted: #3c3c43;
    /* Was .60 -- contrast against surface3 was only 2.90:1. Bumped so every
       light surface clears 4.5:1 (worst case ~4.73:1). */
    --muted2: rgba(60,60,67,.82);
    /* Darkened variant of --primary for text-on-light-background usages --
       see the --primary-text comment in the dark :root block above. */
    --primary-text: #1d4db7;
    --shadow-sm: 0 1px 3px rgba(0,0,0,.10);
    --shadow-md: 0 4px 16px rgba(0,0,0,.12);
    --shadow-lg: 0 12px 40px rgba(0,0,0,.16);
  }
}

/* ── Explicit overrides (user setting or JS-set) ─────────────────────────── */
[data-theme="dark"] {
  --bg: #07070f;
  --surface: #0e0e1a;
  --surface2: #141426;
  --surface3: #1c1c30;
  --stroke: rgba(255,255,255,.07);
  --stroke2: rgba(255,255,255,.13);
  --text: #fff;
  --text1: #fff;
  --text2: rgba(255,255,255,.72);
  --muted: #7a7a9a;
  --muted2: rgba(255,255,255,.47);
  --primary-text: #6692f1;
  --shadow-sm: 0 1px 3px rgba(0,0,0,.4);
  --shadow-md: 0 4px 16px rgba(0,0,0,.5);
  --shadow-lg: 0 12px 40px rgba(0,0,0,.6);
}

/* ── Color themes ─────────────────────────────────────────────────────────
   A full-app palette swap on top of the tokens above, picked from the Theme
   row in More ▸ Display and applied as [data-color-theme] on <html>; "classic"
   needs no block here, it's exactly the :root/[data-theme] tokens already
   defined above. Every preset theme now has BOTH a light and a dark variant
   — data-theme (Auto/Light/Dark) applies to every theme, not just Classic —
   selected with a compound selector: :root[data-color-theme="…"][data-theme
   ="light"|"dark"]. That compound is (0,3,0) specificity, which beats every
   other rule in this file on its own (no source-order tie-breaking needed).
   Each block covers the full token set used across the app (surfaces, text,
   primary/accent) so nothing partially inherits a mismatched color from
   whichever rule happened to run first. All values below are chosen and
   verified (via a WCAG contrast script, not eyeballing) to clear 4.5:1 for
   text and 3:1 for large/UI elements — see the per-block comments for the
   specific numbers. */
:root[data-color-theme="sunset"][data-theme="light"] {
  --bg: #FFF9F2;
  --surface: #FFFFFF;
  --surface2: #F3EDE1;
  --surface3: #EDE6DA;
  --stroke: rgba(43,33,64,.08);
  --stroke2: rgba(43,33,64,.14);
  --text: #2B2140;
  --text1: #2B2140;
  --text2: rgba(43,33,64,.72);
  /* --muted/--muted2 were #8A7EA8 / ink@.55 — only 3.6-3.7:1 against the
     near-white surfaces, under WCAG's 4.5:1 minimum for normal text (real,
     measured: "Auto follows system setting" and similar subtext was hard to
     read). Darkened both so every light surface clears ~4.6:1+. */
  --muted: #777185;
  --muted2: rgba(43,33,64,.64);
  --primary: #FF6F61;
  --primary-dark: #E85C4E;
  --primary-text: #E14A3B;
  --accent: #4CC9F0;
  /* White text on this coral primary was only 2.73:1 — fails contrast even
     for large/UI text (3:1 min), let alone body text. Black clears 7.70:1. */
  --on-primary: #000;
}
:root[data-color-theme="sunset"][data-theme="dark"] {
  /* A dark, coral-tinted near-black ramp (same hue as --primary) rather than
     a generic gray-black, so it still reads as "Sunset" after dark. */
  --bg: #180d0c;
  --surface: #241514;
  --surface2: #311d1c;
  --surface3: #3d2624;
  --stroke: rgba(255,255,255,.07);
  --stroke2: rgba(255,255,255,.13);
  --text: #f5f0ef;
  --text1: #f5f0ef;
  --text2: rgba(245,240,239,.72);
  --muted: #ab7773;
  --muted2: rgba(245,240,239,.49);
  --primary: #FF6F61;
  --primary-dark: #E85C4E;
  /* --primary itself already clears 6.46:1 as text on this surface, so no
     separate lightened value is needed here (unlike the base dark theme's
     --primary/--primary-text split). */
  --primary-text: #FF6F61;
  --accent: #4CC9F0;
  --on-primary: #000;
}
:root[data-color-theme="carnival"][data-theme="light"] {
  --bg: #FFFFFF;
  --surface: #F7F3FF;
  --surface2: #EDE6FB;
  --surface3: #E0D6F7;
  --stroke: rgba(36,24,84,.08);
  --stroke2: rgba(36,24,84,.14);
  --text: #241854;
  --text1: #241854;
  --text2: rgba(36,24,84,.72);
  /* See the Sunset block above for why these are darker than the original
     #8A7EB8 / ink@.55 — same 3.3-3.6:1 contrast failure, same fix. Carnival's
     surface (#F7F3FF) is slightly tinted rather than pure white, so this
     needed a touch more than Sunset/Tropic to clear 4.5:1 against it. */
  --muted: #6e678e;
  --muted2: rgba(36,24,84,.66);
  --primary: #FF2D78;
  --primary-dark: #E0245F;
  --primary-text: #D81B60;
  --accent: #7B2FF7;
  /* White on this hot pink was 3.56:1 — under the 4.5:1 text minimum. */
  --on-primary: #000;
}
:root[data-color-theme="carnival"][data-theme="dark"] {
  --bg: #180c10;
  --surface: #24141a;
  --surface2: #311c23;
  --surface3: #3d242d;
  --stroke: rgba(255,255,255,.07);
  --stroke2: rgba(255,255,255,.13);
  --text: #f5eff1;
  --text1: #f5eff1;
  --text2: rgba(245,239,241,.72);
  --muted: #ab7386;
  --muted2: rgba(245,239,241,.50);
  --primary: #FF2D78;
  --primary-dark: #E0245F;
  --primary-text: #FF2D78;
  /* The light theme's violet accent (#7B2FF7) only reaches 3.02:1 on this
     dark surface — brightened it to #9a60f9 (4.55:1) for the dark variant
     only; the light variant's --accent is unaffected. */
  --accent: #9a60f9;
  --on-primary: #000;
}
:root[data-color-theme="tropic"][data-theme="light"] {
  --bg: #FFFDF4;
  --surface: #FFFFFF;
  --surface2: #EAF9F1;
  --surface3: #DFF3E9;
  --stroke: rgba(14,59,46,.08);
  --stroke2: rgba(14,59,46,.14);
  --text: #0E3B2E;
  --text1: #0E3B2E;
  --text2: rgba(14,59,46,.72);
  /* Was #6FA593 / ink@.55 — worst case (muted on surface) was 2.81:1, well
     under 4.5:1. Same fix as Sunset/Carnival above. */
  --muted: #5b7a71;
  --muted2: rgba(14,59,46,.68);
  --primary: #E8590C;
  --primary-dark: #C94A08;
  --primary-text: #C94A08;
  --accent: #00C853;
  /* White on this burnt orange was 3.58:1 — under the 4.5:1 text minimum. */
  --on-primary: #000;
}
:root[data-color-theme="tropic"][data-theme="dark"] {
  --bg: #18100c;
  --surface: #241a14;
  --surface2: #31231c;
  --surface3: #3d2d24;
  --stroke: rgba(255,255,255,.07);
  --stroke2: rgba(255,255,255,.13);
  --text: #f5f1ef;
  --text1: #f5f1ef;
  --text2: rgba(245,241,239,.72);
  --muted: #a57e69;
  --muted2: rgba(245,241,239,.50);
  --primary: #E8590C;
  --primary-dark: #C94A08;
  --primary-text: #E8590C;
  --accent: #00C853;
  --on-primary: #000;
}
:root[data-color-theme="neon"][data-theme="dark"] {
  --bg: #120E22;
  --surface: #1B1533;
  --surface2: #241D40;
  --surface3: #2E2650;
  --stroke: rgba(255,255,255,.08);
  --stroke2: rgba(255,255,255,.14);
  --text: #F2F0FF;
  --text1: #F2F0FF;
  --text2: rgba(242,240,255,.72);
  --muted: #A99CE0;
  --muted2: rgba(242,240,255,.5);
  --primary: #FF2FD0;
  --primary-dark: #D620AD;
  --primary-text: #FF6FE0;
  --accent: #00F0FF;
  /* White on this neon magenta was only 3.20:1. Counterintuitively black
     reads far better here (6.57:1) — the color's bright enough that dark
     text is the clearer choice even on a "neon" theme. */
  --on-primary: #000;
}
:root[data-color-theme="neon"][data-theme="light"] {
  /* A light version of "neon" trades the glow for a soft magenta/lavender
     backdrop, keeping the same brand primary/on-primary so buttons match
     the dark variant exactly — only the page around them changes. */
  --bg: #fbf6fb;
  --surface: #FFFFFF;
  --surface2: #f5f0f8;
  --surface3: #ede4f1;
  --stroke: rgba(41,20,52,.08);
  --stroke2: rgba(41,20,52,.14);
  --text: #291434;
  --text1: #291434;
  --text2: rgba(41,20,52,.72);
  --muted: #886699;
  --muted2: rgba(41,20,52,.61);
  --primary: #FF2FD0;
  --primary-dark: #D620AD;
  /* Full-brightness magenta is too light to use as text on this bg (would
     fail contrast) — darkened for text use only, buttons keep --primary. */
  --primary-text: #db00aa;
  /* Neon's cyan accent is near-white — unreadable as-is on a light bg, so
     it's darkened here to a legible teal. It's used as a decorative fill
     (goal-bar completion), not as text, so the 3:1 UI-component minimum
     applies rather than 4.5:1. */
  --accent: #00a3ad;
  --on-primary: #000;
}

/* Theme picker swatches — More ▸ Display */
.moreRow--stack { flex-direction: column; align-items: stretch; }
.themeSwatchRow { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 10px; }
.themeSwatch {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  width: 50px; background: none; border: none; padding: 0; cursor: pointer;
}
.themeSwatchDot {
  display: block; width: 40px; height: 40px; border-radius: 50%;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--stroke2);
  transition: box-shadow .15s var(--ease);
}
.themeSwatch[aria-pressed="true"] .themeSwatchDot {
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 3px var(--primary);
}
.themeSwatchName { font-size: 10.5px; font-weight: 700; color: var(--muted); text-align: center; }
.themeSwatch[aria-pressed="true"] .themeSwatchName { color: var(--text); }
.themeSwatchDot--sunset   { background: linear-gradient(135deg, #FF6F61 50%, #4CC9F0 50%); }
.themeSwatchDot--classic  { background: linear-gradient(135deg, #07070f 50%, #2563EB 50%); }
.themeSwatchDot--carnival { background: linear-gradient(135deg, #7B2FF7 50%, #FF2D78 50%); }
.themeSwatchDot--tropic   { background: linear-gradient(135deg, #00C853 50%, #00B7C3 50%); }
.themeSwatchDot--neon     { background: linear-gradient(135deg, #120E22 40%, #00F0FF 70%, #FF2FD0 100%); }

*, *::before, *::after { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
html { height: 100%; overflow-x: hidden; overscroll-behavior-x: none; }

/* ── WKWebView tap & gesture performance ─────────────────────────────────────
   touch-action:manipulation tells iOS "no double-tap zoom, no wait" — eliminates
   the 300ms tap delay and the 22-second gesture-recognizer blocking seen in Capacitor. */
a, button, input, textarea, select, label,
[role="button"], [tabindex], .btn, .card, .moreRow,
.segBtn, .fr26HourBtn, .fr26Chip, .tabItem, .moreTab,
.typeIconBtn, .clockInBtn, .repeatChip, .comboSuggestChip,
summary, details { touch-action: manipulation; }

/* Prevent iOS text-selection callout on UI chrome (not on text inputs) */
header, nav, .tabBar, .moreTabBar, .fr26QuickHours,
.fr26QuickTools, .heroGoalRingWrap, .heroChartCard,
button, [role="button"] { -webkit-user-select: none; user-select: none; }

body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior-y: none;
  overscroll-behavior-x: none;
}

/* iOS-safe body scroll lock when overlay is open */
body.modal-open {
  position: fixed;
  left: 0;
  right: 0;
  overflow: hidden;
}
/* Lock #spa-more's scroll container too when a modal is open */
body.modal-open #spa-more {
  overflow: hidden;
}

button, a, [role="button"], summary, label { touch-action: manipulation; }

/* ── SPA page switching ────────────────────────────────────────────────────────
   #spa-more is NEVER hidden with display:none — iOS WKWebView excludes
   display:none subtrees from its touch hit-test tree and doesn't reliably
   re-add them, making every button inside permanently unresponsive.
   Instead we keep #spa-more in the DOM at all times, just slid off-screen.
   The bottom .tabBar (always-in-DOM, always tappable) proves this pattern works.
────────────────────────────────────────────────────────────────────────────── */
#spa-more {
  position: fixed;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior-x: none;
  background: var(--bg);
  z-index: 10;
  /* Slid off-screen right by default; CSS moves it on-screen for the active page.
     showSpaPage() in boot.js drives the actual left/right slide via inline
     transform (direction depends on tab order) — this transition is what
     turns those JS-set values into real motion instead of an instant snap. */
  transform: translateX(100%);
  transition: transform 320ms var(--ease);
  -webkit-overflow-scrolling: touch;
  /* Mis-tap fix: translateX(100%) should put this fully outside the viewport,
     but iOS WKWebView's hit-test area can lag the painted position by a frame
     (dynamic toolbar/viewport resize, mid page-switch). Disabling
     pointer-events while off-screen means a stray hit here is simply ignored
     instead of landing on a control that isn't visually where the finger is. */
  pointer-events: none;
}
body[data-page="more"] #spa-more {
  transform: translateX(0);
  pointer-events: auto;
}

/* ── Stats / Breakdown page ──────────────────────────────────────────────── */
#spa-stats {
  position: fixed;
  inset: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  overscroll-behavior-x: none;
  background: var(--bg);
  z-index: 10;
  transform: translateX(100%);
  transition: transform 320ms var(--ease);
  -webkit-overflow-scrolling: touch;
  padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
  /* See #spa-more above: same mis-tap defense for the off-screen state. */
  pointer-events: none;
}
body[data-page="stats"] #spa-stats {
  transform: translateX(0);
  pointer-events: auto;
}
.statsPageInner {
  padding: 0 0 24px;
}
/* Horizontal scrollable period chips */
/* Fix #9: Fade right edge to hint scrollability */
.statsChipsOuter {
  position: relative;
}
.statsChipsOuter::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 12px;
  width: 48px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--bg));
}
.statsChipsWrap {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 0 16px 12px;
  scrollbar-width: none;
  -ms-overflow-style: none;
  margin-bottom: 8px;
  /* Momentum scrolling inside a position:fixed panel — without this iOS
     refuses to scroll the row at all (the reason the period chips were stuck
     while the identical .recentTypeChips row worked fine). pan-x also tells
     the browser we own horizontal here, so the anti-bounce handler stays out. */
  -webkit-overflow-scrolling: touch;
  touch-action: pan-x;
  /* The page-level bounce fix (html/body overscroll-behavior-x: none) only
     stops the WHOLE PAGE from rubber-banding — it does nothing for a child
     element's own overflow box, which defaults to overscroll-behavior: auto
     and happily bounces (and can chain into a page-level bounce) when
     dragged past its own edge. This is the row that was still wobbling. */
  overscroll-behavior-x: contain;
}
/* Chip buttons inherit manipulation from the global button rule, which stops
   a drag that starts ON a chip from panning the row. They're the whole row. */
.statsChipsWrap .statsChip { touch-action: pan-x; }
.statsChipsWrap::-webkit-scrollbar { display: none; }
.statsChip {
  flex-shrink: 0;
  padding: 7px 14px;
  min-height: 40px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--r-xl);
  border: 1.5px solid var(--surface2, #1e2d42);
  background: transparent;
  color: var(--text2, #8a9bb8);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.statsChip.active {
  background: var(--primary, #2563EB);
  border-color: var(--primary, #2563EB);
  color: #fff;
}
/* Custom date range */
.statsCustomRange {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px 12px;
  flex-wrap: wrap;
}
.statsDateInput {
  flex: 1;
  min-width: 120px;
  font-size: 16px; /* was 13px — iOS zoom-on-focus trigger */
  padding: 8px 10px;
}
.statsDateSep {
  color: var(--muted, #6b7280);
  font-size: 14px;
  flex-shrink: 0;
}
.statsDateApply {
  flex-shrink: 0;
  padding: 8px 16px;
  font-size: 13px;
}
/* Inner content padding for chart/list */
#breakdownChart, #breakdownSummary, #breakdownList {
  padding-left: 16px;
  padding-right: 16px;
}
/* Stats page header subtitle */
.statsHeaderSub {
  font-size: 12px;
  font-weight: 500;
  color: var(--muted, #6b7280);
  margin-top: 2px;
  letter-spacing: 0.02em;
}
/* Donut + legend — stacked so the donut's position never depends on how much
   legend content there is. Side-by-side (donut left, legend right) made the
   donut visually drift left/right as the legend's width changed between
   periods (1 job type vs 6). Centering the donut on its own row and putting
   the legend in a centered wrapping row below fixes that permanently. */
.breakdownChart {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-bottom: 16px;
}
.brkDonutWrap {
  flex-shrink: 0;
}
.brkLegend {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 18px;
  min-width: 0;
  width: 100%;
}
.brkLegendItem {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  color: var(--text1);
  cursor: pointer;
  touch-action: manipulation;
  padding: 3px 2px;
  margin: -3px -2px;
  border-radius: var(--r-sm);
  transition: background 120ms var(--ease, ease);
}
.brkLegendItem:active { background: var(--surface2, rgba(255,255,255,.06)); }
.brkLegendName {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 150px;
}
.brkDot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex-shrink: 0;
}
/* Summary cells */
.breakdownSummary {
  display: flex;
  gap: 10px;
  margin-bottom: 16px;
}
.brkSumCell {
  flex: 1;
  background: var(--card-bg);
  border-radius: var(--r-md);
  padding: 12px 8px;
  text-align: center;
}
.brkSumVal {
  font-size: 18px;
  font-weight: 700;
  color: var(--primary-text);
  line-height: 1.1;
}
.brkSumLabel {
  font-size: 11px;
  color: var(--muted, #6b7280);
  margin-top: 3px;
  text-transform: uppercase;
  letter-spacing: .04em;
}
/* Per-type rows */
.breakdownList {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.brkRow {
  background: var(--card-bg);
  border-radius: var(--r-md);
  padding: 14px 16px;
  display: flex;
  gap: 12px;
  align-items: flex-start;
  width: 100%;
  border: none;
  text-align: left;
  font: inherit;
  color: inherit;
  cursor: pointer;
  touch-action: manipulation;
  transition: background 140ms var(--ease, ease), transform 120ms var(--ease, ease);
}
.brkRow:active { transform: scale(.985); background: var(--surface3, var(--card-bg)); }

/* Donut segments sweep open on render */
.brkArc { transition: stroke-dasharray 620ms cubic-bezier(.22,1,.36,1); }

/* Share-of-work bar under each type name */
.brkRowBar {
  margin-top: 7px;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--surface2, rgba(255,255,255,.07));
  overflow: hidden;
}
.brkRowBar > span {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  width: 0;
  animation: brkBarGrow 620ms cubic-bezier(.22,1,.36,1) forwards;
}
@keyframes brkBarGrow { from { width: 0; } }

/* Chevron rotates when the type is expanded */
.brkRowChev {
  align-self: center;
  width: 7px; height: 7px;
  border-right: 2px solid var(--muted, #6b7280);
  border-bottom: 2px solid var(--muted, #6b7280);
  transform: rotate(45deg);
  transition: transform 200ms var(--ease, ease);
  flex-shrink: 0;
}
.brkRowWrap.open .brkRowChev { transform: rotate(-135deg); }

/* The jobs behind a type — collapsed until tapped */
.brkRowJobs {
  max-height: 0;
  overflow: hidden;
  transition: max-height 280ms var(--ease, ease), opacity 200ms var(--ease, ease);
  opacity: 0;
}
.brkRowWrap.open .brkRowJobs { max-height: 1200px; opacity: 1; }
.brkJob {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--stroke);
  cursor: pointer;
  touch-action: manipulation;
  transition: background 120ms var(--ease, ease);
}
.brkJob:active { background: var(--surface2, rgba(255,255,255,.05)); }
.brkJob:last-child { border-bottom: none; }
.brkJobLeft  { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.brkJobRef   { font-size: 12.5px; font-weight: 600; color: var(--text2); }
.brkJobDate  { font-size: 11px; color: var(--muted); }
.brkJobRight { text-align: right; flex-shrink: 0; }
.brkJobPay   { display: block; font-size: 13.5px; font-weight: 700; color: var(--accent); }
.brkJobHrs   { display: block; font-size: 11px; color: var(--muted); margin-top: 1px; }

@media (prefers-reduced-motion: reduce) {
  /* Inline width="pct%" still applies — only the growth animation is dropped. */
  .brkArc, .brkRowBar > span, .brkRowJobs, .brkRowChev { transition: none; animation: none; }
}
.brkRowBody {
  flex: 1;
  min-width: 0;
}
.brkRowName {
  font-weight: 600;
  font-size: 15px;
  color: var(--text1);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.brkRowCount {
  font-size: 12px;
  color: var(--muted, #6b7280);
  margin-top: 3px;
}
.brkRowRight {
  text-align: right;
  flex-shrink: 0;
}
.brkRowEarnings {
  font-weight: 700;
  font-size: 15px;
  color: var(--primary-text);
}
.brkRowHours {
  font-size: 12px;
  color: var(--muted, #6b7280);
  margin-top: 3px;
}

/* ── Monthly trend bar chart (Stats page) ───────────────────────────── */
.breakdownMonthly { margin-bottom: 16px; }
.mnthTrend {
  background: var(--surface2);
  border-radius: var(--r-lg);
  padding: 14px 16px 10px;
}
.mnthTrendTitle {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .05em;
  margin-bottom: 10px;
}
/* Outer hosts the fade so it overlays the row instead of scrolling away
   with it — same split as .statsChipsOuter/.recentTypeChipsOuter. Fade
   color matches .mnthTrend's own background (--surface2), not the page
   background, since this row is nested inside that card. */
.mnthBarsScrollOuter {
  position: relative;
}
.mnthBarsScrollOuter::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 32px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--surface2));
}
.mnthBarsScroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 2px;
  /* Contain this row's own scroll bounce so dragging past its edge doesn't
     rubber-band (and potentially chain into the page) — see the longer note
     on .statsChipsWrap above. */
  overscroll-behavior-x: contain;
}
.mnthBarsWrap {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  padding-bottom: 24px; /* room for labels */
  min-width: min-content;
}
.mnthBarCol {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 0 0 auto;
}
.mnthBarAmt {
  font-size: 8px;
  color: var(--muted);
  margin-bottom: 3px;
  white-space: nowrap;
}
.mnthBar {
  width: 30px;
  min-height: 4px;
  background: var(--primary);
  border-radius: var(--r-xs) var(--r-xs) 0 0;
  opacity: 0.75;
}
.mnthBarLabel {
  font-size: 9px;
  color: var(--muted);
  margin-top: 5px;
  white-space: nowrap;
}

/* ── Floating details save bar ───────────────────── */
/* Save button spacing when moved inside the Details panel */
#detailsPanel .fr26BtnPrimary { margin-top: 16px; }

.detailsSaveBar {
  position: fixed;
  bottom: calc(72px + env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  z-index: 250;
  animation: slideUpBar 180ms var(--ease);
}

.detailsSaveFloatBtn {
  white-space: nowrap;
  font-size: 14px;
  font-weight: 700;
  padding: 10px 28px;
  border-radius: var(--r-pill);
  box-shadow: 0 4px 16px rgba(0,0,0,.45);
}

@keyframes slideUpBar {
  from { transform: translateY(100%); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

/* ── Header ─────────────────────────────────────── */
header {
  position: sticky;
  top: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 10px;
  /* Was a hardcoded rgba(7,7,15,.94) — matched the default dark theme's --bg
     exactly, but stayed that same near-black in every OTHER theme/mode (all
     4 color presets, and light mode outside Classic), leaving a jarring
     black bar pinned above a light or colored page. color-mix keeps the same
     94%-opaque-over-content look while tracking whichever --bg is active. */
  background: color-mix(in srgb, var(--bg) 94%, transparent);
  border-bottom: 1px solid var(--stroke);
  /* Background is already 94% opaque, so a heavy blur here was buying almost
     no visible difference — barely anything shows through to blur — while
     costing real GPU time on every scroll frame on mobile (this bar sits over
     scrolling content on every single screen, unlike a modal backdrop that's
     only blurred once while open). 8px reads the same, costs a fraction as
     much. See also .tabBar and .top below — same fix, same reasoning. */
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

/* Standalone PWA: guarantee status-bar clearance on every iPhone model */
@media (display-mode: standalone) {
  header {
    padding-top: max(52px, calc(12px + env(safe-area-inset-top, 44px)));
  }
  .top {
    padding-top: max(52px, calc(12px + env(safe-area-inset-top, 44px)));
  }
  #swUpdateBanner {
    padding-top: max(52px, calc(10px + env(safe-area-inset-top, 44px)));
  }
}

h1 {
  font-size: 12px;
  font-weight: 500;
  margin: 0;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted2);
}

main {
  padding: 12px 16px calc(80px + env(safe-area-inset-bottom));
  max-width: 680px;
  margin: 0 auto;
}

/* Uniform content centering — every top-level full-bleed section (page
   headers, the More page's tab strip and team banner, the Stats page's
   content wrapper) caps at the same 680px and centers, matching `main`
   above. This is unconditional, not gated behind a min-width media query:
   max-width is simply a no-op below 680px, so phones are unaffected.
   Before this, only `main` (and .heroSection/.fr26Wrap, styled the same
   way elsewhere) centered at every width -- header/.tabBar only centered
   above 961px, and .statsPageInner/.moreTabBar/.teamEntryBanner never did.
   That left tablets and any desktop window between ~681-960px wide with
   a centered content column but an edge-to-edge header and tab bar: the
   lopsided, "not centered" layout reported during a UI pass. */
header.top,
.statsPageInner,
.moreTabBar,
.teamEntryBanner {
  max-width: 680px;
  margin-left: auto;
  margin-right: auto;
}

/* ── Bottom tab bar ──────────────────────────────── */
.tabBar {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 680px;
  z-index: 200;
  display: flex;
  /* Was a hardcoded near-black rgba(17,17,19,.96) in every theme — see the
     header comment above, same bug, same fix: track --surface instead so
     the tab bar isn't a black stripe under a light/colored theme. */
  background: color-mix(in srgb, var(--surface) 96%, transparent);
  /* See header comment above — 96% opaque background, heavy blur here was
     nearly invisible but sits under every scroll gesture in the app. */
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-top: 1px solid var(--stroke2);
  /* max() instead of a bare env() call: on some WKWebView/standalone-PWA
     renders the safe-area-inset-bottom env var reports 0 even though the
     device really does have a home-indicator inset, which left an
     uncovered strip of the page's own --bg color peeking out below this
     bar (very visible against a dark bar on a bright theme bg; invisible
     on Classic where the bar itself is a near-white color, so it went
     unnoticed there). 20px is a safe floor close to the real ~34pt inset
     on notch/Dynamic-Island iPhones, and max() still defers to the real
     env() value whenever the browser reports one larger than that. */
  padding-bottom: max(env(safe-area-inset-bottom), 20px);
}

.tabItem {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 10px 0 8px;
  color: var(--muted);
  text-decoration: none;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .2px;
  transition: color 150ms var(--ease);
  position: relative;
  border: none;
  background: transparent;
}

.tabItem--active {
  color: var(--primary-text);
}

.tabItem svg { transition: transform 150ms var(--ease-bounce); }
.tabItem:active svg { transform: scale(.88); }

.tabBadge {
  position: absolute;
  top: 6px;
  right: calc(50% - 18px);
  min-width: 16px;
  height: 16px;
  background: var(--danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  border-radius: var(--r-pill);
  padding: 0 4px;
  line-height: 16px;
  text-align: center;
}

.stripLabel {
  font-size: 8px;
  font-weight: 500;
  color: var(--muted2);
  text-transform: uppercase;
  letter-spacing: .6px;
  white-space: nowrap;
}

.stripValue {
  font-size: 18px;
  font-weight: 500;
  letter-spacing: -.5px;
  margin-top: 3px;
  white-space: nowrap;
}

.stripValue--accent { color: var(--accent); }
.stripValue--primary { color: var(--primary-text); }

/* ── Cards ───────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--stroke);
  border-radius: var(--r-xl);
  padding: 16px;
  margin-bottom: 16px;
  /* Scope layout recalculation to this card — prevents a change inside from
     forcing a full-page reflow on every render cycle */
  contain: layout style;
}

.card--tight { padding: 12px; }

/* ── Typography ──────────────────────────────────── */
.h {
  font-weight: 700;
  font-size: 16px;
  letter-spacing: -.2px;
  margin: 0 0 8px;
}

.muted  { color: var(--muted); font-size: 13px; }
.small  { font-size: 12px; color: var(--muted); }
.linkBtn { background: none; border: none; padding: 0; color: var(--primary-text); font-size: inherit; font-weight: 600; cursor: pointer; text-decoration: underline; touch-action: manipulation; }
.mono   { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; }

/* ── Layout helpers ──────────────────────────────── */
.row     { margin-top: 12px; }
.u-row   { display: flex; align-items: center; gap: 10px; }
.u-wrap  { flex-wrap: wrap; }

.list    { display: grid; gap: 8px; }

.item {
  border-radius: var(--r-lg);
  border: 1px solid var(--stroke);
  overflow: hidden;
  position: relative;
  touch-action: pan-y;
  /* content-visibility: skip render for off-screen items (big win for long lists).
     contain-intrinsic-size gives the browser a height estimate for accurate scrollbar. */
  content-visibility: auto;
  contain-intrinsic-size: 0 76px;
  contain: layout style paint;
}

.itemInner {
  padding: 16px;
  background: var(--surface2);
  border-radius: inherit;
  position: relative;
  z-index: 2;
  transition: transform 200ms ease;
  will-change: transform;
}
.item.swiping .itemInner { transition: none; }
.item:not(.swiping):active .itemInner { background: var(--surface3); }

/* Swipe action zones — sit behind .itemInner, revealed on swipe */
.swipeZone {
  position: absolute;
  top: 0; bottom: 0; width: 90px;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  font-size: 11px;
  font-weight: 700;
  color: #fff;
  letter-spacing: .3px;
}
.swipeZone span { font-size: 18px; line-height: 1; }
.swipeDeleteZone { right: 0; background: var(--danger); border-radius: 0 var(--r-lg) var(--r-lg) 0; }
.swipeEditZone   { left: 0;  background: var(--primary); border-radius: var(--r-lg) 0 0 var(--r-lg); }

/* ── Entry card layout ───────────────────────────── */
.itemTop {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
}
.right { text-align: right; flex-shrink: 0; }
.itemLeft { flex: 1; min-width: 0; }

/* Headline row: checkbox + badge + ref */
.itemHeadline {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: nowrap;
  overflow: hidden;
}

.itemCheck {
  /* Was 20px, sitting bare (no wrapping label) among other inline badges
     in a dense row — a raw native checkbox is the most reliable way to
     grow its real hit area, since ::before/::after support on <input>
     elements isn't consistent enough to trust across browsers. */
  flex-shrink: 0;
  width: 24px;
  height: 24px;
  box-sizing: border-box;
  margin: 0;
  accent-color: var(--primary);
  cursor: pointer;
  touch-action: manipulation;
}

.itemRef {
  font-size: 13px;
  color: var(--text2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.itemRef:active { opacity: .6; }

/* Compact meta line below headline */
.itemMeta {
  margin-top: 5px;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.4;
}

.itemNotes {
  margin-top: 4px;
  font-size: 12px;
  color: var(--text2);
  font-style: italic;
  line-height: 1.4;
}

/* Right: pay + hours stacked */
.itemRight {
  text-align: right;
  flex-shrink: 0;
}
.itemPay {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -.6px;
  line-height: 1;
  color: var(--accent);
}
.itemHrs {
  font-size: 11px;
  color: var(--muted);
  margin-top: 3px;
}

/* ── Collapsible entries ─────────────────────────── */
.item .itemTop { cursor: pointer; user-select: none; -webkit-user-select: none; }

.itemMeta, .itemNotes, .itemActions {
  overflow: hidden;
  transition: max-height 220ms var(--ease), opacity 180ms var(--ease), margin-top 200ms var(--ease);
}
.itemMeta   { max-height: 56px; }
.itemNotes  { max-height: 80px; }
.itemActions { max-height: 60px; }

.item.collapsed .itemMeta,
.item.collapsed .itemNotes,
.item.collapsed .itemActions {
  max-height: 0;
  opacity: 0;
  margin-top: 0;
  pointer-events: none;
}
.item.collapsed .entryThumbWrap {
  max-height: 0 !important;
  margin-top: 0;
}

/* CSS chevron — more consistent than Unicode across devices */
.itemChevron {
  width: 16px;
  height: 16px;
  margin-top: 6px;
  margin-left: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.itemChevron::after {
  content: "";
  display: block;
  width: 7px;
  height: 7px;
  border-right: 2px solid var(--muted2);
  border-bottom: 2px solid var(--muted2);
  transform: rotate(45deg) translateY(-2px);
  transition: transform 180ms var(--ease);
}
.item.collapsed .itemChevron::after {
  transform: rotate(-45deg) translateY(2px);
}

/* Action buttons — compact pill style */
.itemActions {
  margin-top: 8px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.iBtn {
  padding: 10px 16px;
  min-height: 44px; /* Apple HIG / Android minimum tap target — was 40px */
  font-size: 12px;
  font-weight: 600;
  border-radius: var(--r-pill);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text2);
  cursor: pointer;
  transition: background 120ms var(--ease), transform 80ms var(--ease);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  touch-action: manipulation;
}
.iBtn:active { background: var(--surface3); transform: scale(.94); }
.iBtn--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 25%, transparent); background: color-mix(in srgb, var(--danger) 6%, transparent); }
.iBtn--sameRO { color: var(--primary-text); border-color: color-mix(in srgb, var(--primary) 30%, transparent); background: color-mix(in srgb, var(--primary) 8%, transparent); font-weight: 600; }

/* ── Week group header ───────────────────────────── */
.weekGroupHdr {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 16px 0 4px;
  padding: 0 2px;
}
.weekGroupRange {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--primary-text);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
.weekGroupTotals {
  font-size: 12px;
  font-weight: 600;
  color: var(--text2);
}

/* ── Entry photo thumbnail ───────────────────────── */
.entryThumbWrap {
  border-radius: var(--r-md);
  overflow: hidden;
  max-width: 240px;
  max-height: 0;
  margin-top: 0;
  transition: max-height 300ms var(--ease), margin-top 200ms var(--ease);
}
.entryThumbWrap.loaded {
  background: var(--surface3);
  max-height: 90px;
  min-height: 44px;
  margin-top: 8px;
}
.entryThumb {
  width: 100%;
  display: block;
  max-height: 90px;
  min-height: 44px;
  object-fit: cover;
  opacity: 0;
  transition: opacity 300ms var(--ease);
}
.entryThumbWrap.loaded .entryThumb { opacity: 1; }

/* ── Day group header ────────────────────────────── */
.dayGroupHeader {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin: 8px 0;
  padding: 0 2px;
}

/* ── Inputs ──────────────────────────────────────── */
label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  margin-bottom: 6px;
  letter-spacing: .2px;
  text-transform: uppercase;
}

input, select, textarea, button { font-family: inherit; font-size: 16px; } /* 16px prevents iOS auto-zoom */

input, select, textarea {
  width: 100%;
  padding: 13px 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text);
  outline: none;
  transition: border-color 150ms var(--ease), box-shadow 150ms var(--ease);
  -webkit-appearance: none;
}

input::placeholder, textarea::placeholder { color: var(--muted2); }
textarea { min-height: 88px; resize: vertical; }

input:focus, select:focus, textarea:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent);
}

/* Payroll report scan buttons -- was inline flex:1 side-by-side with .btn's
   white-space:nowrap and default min-width:auto, so neither button could
   ever shrink below its own text width (Take Photo + Choose from Library
   together don't fit in a ~310px card on a real phone) -- the second
   button's label ran flush into the card's edge with zero margin. Stack
   instead of fighting for shrink room; still side-by-side once there's
   real width to spare. */
.payrollScanBtnRow {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
}
.payrollScanBtnRow .btn { width: 100%; }
@media (min-width: 480px) {
  .payrollScanBtnRow { flex-direction: row; }
  .payrollScanBtnRow .btn { flex: 1; width: auto; }
}

/* ── Buttons ─────────────────────────────────────── */
.btn,
button.btn,
a.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 9px 16px;
  min-height: 44px; /* Apple HIG / Android minimum tap target — was 40px */
  border-radius: var(--r-pill);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: background 150ms var(--ease), transform 100ms var(--ease), opacity 150ms var(--ease);
  -webkit-appearance: none;
}

.btn:active { transform: scale(.95); opacity: .8; transition-duration: 60ms; }

.btn.primary {
  background: var(--primary);
  border-color: transparent;
  color: var(--on-primary);
}

.btn.primary:active { background: var(--primary-dark); }

.btn.danger {
  background: var(--danger);
  border-color: transparent;
  color: #fff;
}

.btn.danger-ghost {
  background: transparent;
  border-color: color-mix(in srgb, var(--danger) 35%, transparent);
  color: var(--danger);
  font-size: 13px;
}

/* ── Segmented controls ──────────────────────────── */
/* Every segmented toggle in the app (RO/STK, Hours/Pay, history range,
   dark mode, pay period) shares these two classes — this is one of the most
   common "tapped one, hit the neighbor" spots since options sit shoulder to
   shoulder. A slightly bigger gap + a real min-height fixes all of them
   app-wide from one place. */
.seg {
  display: inline-flex;
  background: var(--surface2);
  border-radius: var(--r-md);
  padding: 3px;
  gap: 4px;
  border: 1px solid var(--stroke);
}

.segBtn {
  border: 0;
  background: transparent;
  color: var(--muted);
  padding: 9px 14px;
  min-height: 44px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 150ms var(--ease), color 150ms var(--ease);
}

.segBtn.active {
  background: var(--surface3);
  color: var(--text);
}

.rangeTabs .segBtn.active {
  box-shadow: 0 2px 8px rgba(0,0,0,.4);
}

/* ── FR26 form ───────────────────────────────────── */
.fr26 { padding: 0; overflow: hidden; }
.fr26Form { position: relative; padding-bottom: 0; }

.fr26EmployeeBar {
  display: grid;
  grid-template-columns: auto minmax(0,1fr);
  align-items: center;
  gap: 10px;
  padding: 9px 16px;
  border-bottom: 1px solid var(--stroke);
  background: var(--surface2);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  transition: max-height 250ms var(--ease), opacity 200ms var(--ease), padding 250ms var(--ease);
}
/* When empId is already set, collapse the bar to a subtle indicator */
.fr26EmployeeBar.empSet {
  grid-template-columns: 1fr;
  padding: 5px 16px;
}
.fr26EmployeeBar.empSet .fr26EmployeeLabel { display: none; }
.fr26EmployeeBar.empSet .fr26Input {
  font-size: 11px;
  height: 22px;
  opacity: 0.45;
  background: transparent;
  border: none;
  padding: 0;
  box-shadow: none;
  color: var(--muted);
  cursor: pointer;
}
.fr26EmployeeBar.empSet .fr26Input:focus {
  opacity: 1;
  background: var(--surface3);
  border: 1.5px solid var(--stroke);
  padding: 6px 10px;
  height: auto;
  /* The collapsed 11px badge text is fine unfocused (nothing to type into),
     but the moment this becomes a real text field to edit, it has to clear
     16px or iOS zooms the whole page in on tap. */
  font-size: 16px;
}

.fr26EmployeeLabel {
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .4px;
  white-space: nowrap;
}

.fr26InputCompact { padding: 10px 12px; }

.fr26Group {
  border-top: 1px solid var(--stroke);
  overflow: hidden;
}

.fr26Group:first-child { border-top: 0; }

.fr26PrimaryEntry > .fr26Row:first-child { border-top: 0; }

.fr26GroupHeader {
  padding: 10px 16px 4px;
  font-size: 12px;
  font-weight: 700;
  color: var(--text2);
  letter-spacing: .1px;
}

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

.fr26TopRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px 6px;
}

.fr26Row {
  display: grid;
  gap: 6px;
  padding: 8px 16px;
  border-top: 1px solid var(--stroke);
  background: var(--surface);
}

/* Hours field hero sizing */
#hours.fr26Input {
  font-size: 34px;
  font-weight: 900;
  text-align: center;
  padding: 8px 16px;
  letter-spacing: -1px;
  background: transparent;
  border-color: transparent;
  border-bottom-color: var(--stroke2);
  border-radius: 0;
  box-shadow: none;
}

#hours.fr26Input:focus {
  border-color: transparent;
  border-bottom-color: var(--primary);
  box-shadow: none;
}

.fr26Label {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  letter-spacing: .4px;
  text-transform: uppercase;
}

.fr26Input, .fr26Textarea, .fr26File {
  width: 100%;
  padding: 10px 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text);
  outline: none;
  /* 16px, not 15 -- anything smaller re-triggers iOS Safari's auto-zoom-on-
     focus, which is what "the app scrolls left and right" actually was:
     the whole page zooms in on tap and you can then pan around the
     zoomed viewport. This is the app's main entry-form input class (RO#,
     STK#, VIN, hours, request fields, ...), so it was the biggest offender. */
  font-size: 16px;
  transition: border-color 150ms var(--ease), box-shadow 150ms var(--ease);
}

.fr26Input:focus, .fr26Textarea:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(0,149,246,.2);
}

.fr26Input::placeholder, .fr26Textarea::placeholder { color: var(--muted2); }

.fr26InlineField {
  position: relative;
}

.fr26InlineField .fr26Input {
  width: 100%;
  padding-right: 56px;
}

.fr26InlineClearBtn {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  padding: 6px 10px;
  font-size: 13px;
  border-radius: var(--r-sm);
}

.typeSuggestStrip {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding-top: 4px;
}

.typeSuggestChip {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  min-height: 40px;
  padding: 5px 10px 4px;
  border-radius: var(--r-pill);
  background: var(--surface3);
  border: 1px solid var(--stroke2);
  color: var(--text1);
  cursor: pointer;
  white-space: nowrap;
  max-width: 200px;
  overflow: hidden;
  transition: background 120ms, border-color 120ms;
  gap: 1px;
}

.typeSuggestChip:active { background: var(--surface2); opacity: .75; }

.tscName {
  font-size: 13px;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 180px;
  display: block;
}

.tscMeta {
  font-size: 10px;
  color: var(--muted2);
  letter-spacing: 0.02em;
}

.typeSuggestHint {
  font-size: 12px;
  color: var(--muted2);
  padding: 4px 2px;
}

.fr26FieldHint {
  font-size: 11px;
  color: var(--muted2);
  margin-top: -1px;
}

/* Outer hosts the fade so it overlays the row instead of scrolling away with
   it -- same split as .statsChipsOuter/.recentTypeChipsOuter/.mnthBarsScrollOuter.
   Fades to --surface since this row sits directly on the fr26 card's own
   background, same context as .recentTypeChips below it.
   min-width:0 matters here specifically because .fr26Row (the parent) is a
   CSS grid: grid items default to min-width:auto, sizing to their content's
   intrinsic width instead of shrinking to the track. With 8 hour chips plus
   the "+" button this row's natural width (516px) blew right past the grid
   track (~340px on a phone), silently pushing #smartHourChips' later chips
   (4.5, 6.5) off past the card's overflow:hidden edge -- untappable, no
   scrollbar, no visual sign anything was missing. min-width:0 lets the grid
   item shrink to the track so .fr26QuickHours' own overflow-x:auto actually
   gets to do its job. Found live at 375px with a full 8-chip set. */
.fr26QuickHoursOuter {
  position: relative;
  min-width: 0;
}
.fr26QuickHoursOuter::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 4px;
  width: 36px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--surface));
}
.fr26QuickHours {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  gap: 6px;
  padding: 2px 0 4px;
  scrollbar-width: none;
  -ms-overflow-style: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.fr26QuickHours::-webkit-scrollbar { display: none; }
/* Without this, #smartHourChips (a plain div, no layout of its own) becomes
   a single flex item that wraps its own <button> children internally --
   that's what was producing the "2-row grid" look instead of one real
   scrolling row. display:contents makes its buttons direct flex items of
   .fr26QuickHours instead, same as #customHourChips already does below. */
#smartHourChips { display: contents; }

/* Label shown when chips are tuned to a specific job type */
.fr26ChipHint {
  flex: 0 0 auto;
  align-self: center;
  font-size: 10px;
  font-weight: 600;
  color: var(--primary-text);
  letter-spacing: .03em;
  opacity: .75;
  white-space: nowrap;
  padding-right: 2px;
}

.fr26HourBtn {
  /* Blue-tinted to match .recentTypeChip and .fr26HourBtnCustom — these are
     all the same kind of thing (a tap-to-fill shortcut) and used to look
     like two different UI systems: this one plain gray, the others blue.
     Reusing the existing custom-chip treatment here instead of inventing a
     new one. */
  flex: 0 0 auto;
  min-width: 64px;
  height: 44px;
  padding: 0 8px;
  border: 1.5px solid color-mix(in srgb, var(--primary) 35%, transparent);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--primary) 8%, transparent);
  color: var(--primary-text);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  transition: background 100ms var(--ease), border-color 100ms var(--ease), color 100ms var(--ease), transform 100ms var(--ease-bounce);
}

.fr26HourBtn:active { transform: scale(.88); transition-duration: 60ms; }

.fr26HourBtn.selected {
  background: rgba(37, 99, 235, .18);
  border-color: var(--primary);
  border-width: 2px;
  color: var(--primary-text);
  font-weight: 700;
}

/* Custom hour chips */
#customHourChips { display: contents; }
.fr26HourBtnCustom {
  background: color-mix(in srgb, var(--primary) 8%, transparent);
  border-color: color-mix(in srgb, var(--primary) 35%, transparent);
  color: var(--primary-text);
}
/* + button */
/* Job-type hours chip — appears first when a known job type is selected */
.fr26HourBtnType {
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  border-color: var(--primary);
  color: var(--primary-text);
  font-weight: 700;
  order: -1;
  position: relative;
}
.fr26HourBtnType::after {
  content: "↑";
  font-size: 10px;
  position: absolute;
  top: 2px;
  right: 3px;
  opacity: .7;
}

.fr26BtnSaveRoll {
  margin-top: 8px;
  width: 100%;
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  color: var(--primary-text);
  border: 1.5px solid color-mix(in srgb, var(--primary) 40%, transparent);
  border-radius: var(--r-md);
  padding: 9px 14px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
}
.fr26HourBtnAdd {
  min-width: 42px;
  color: var(--primary-text);
  border-color: color-mix(in srgb, var(--primary) 40%, transparent);
  background: transparent;
  font-size: 20px;
  font-weight: 400;
}

.fr26Check {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--muted);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  user-select: none;
  text-transform: none;
  letter-spacing: 0;
}

.fr26Check input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 42px;
  height: 26px;
  border-radius: var(--r-pill);
  background: var(--surface3);
  border: 1px solid var(--stroke2);
  position: relative;
  flex-shrink: 0;
  cursor: pointer;
  transition: background 200ms, border-color 200ms;
}

.fr26Check input[type="checkbox"]::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--muted);
  transition: transform 200ms var(--ease-bounce), background 200ms;
}

.fr26Check input[type="checkbox"]:checked {
  background: var(--primary);
  border-color: transparent;
}

.fr26Check input[type="checkbox"]:checked::after {
  transform: translateX(16px);
  background: #fff;
}

.fr26Textarea { min-height: 88px; resize: vertical; }

.fr26Hint {
  padding: 8px 16px 12px;
  color: var(--muted2);
  font-size: 11px;
}

.fr26Chip {
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text2);
  border-radius: var(--r-pill);
  padding: 6px 13px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background 120ms var(--ease);
  touch-action: manipulation;
}

.fr26Chip:active { background: var(--surface3); }
.fr26ChipGhost { background: transparent; color: var(--muted); }

.fr26PrimaryAction {
  padding: 12px 14px 14px;
  border-top: 1px solid var(--stroke);
  background: var(--surface);
}

.fr26BtnPrimary, .fr26BtnGhost {
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  transition: opacity 150ms var(--ease), transform 100ms var(--ease), box-shadow 150ms var(--ease);
}

.fr26BtnPrimary {
  width: 100%;
  min-height: 56px;
  border: none;
  background: var(--primary);
  color: var(--on-primary);
  letter-spacing: .2px;
  font-size: 17px;
  font-weight: 800;
  border-radius: var(--r-lg);
  box-shadow: 0 4px 16px color-mix(in srgb, var(--primary) 28%, transparent);
}

.fr26BtnPrimary:active { opacity: .85; transform: scale(.99); }
.fr26BtnPrimary:disabled { background: var(--surface2); color: var(--muted2); opacity: 1; box-shadow: none; }

.fr26BtnGhost {
  border: 1px solid var(--stroke2);
  background: transparent;
  color: var(--text);
  padding: 10px 18px;
}

.fr26OptionalGroup { overflow: hidden; }

.fr26OptionalSummary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  cursor: pointer;
  list-style: none;
  font-weight: 600;
  font-size: 14px;
  color: var(--text2);
  border-top: 1px solid var(--stroke);
}

.fr26OptionalSummary::-webkit-details-marker { display: none; }

.fr26OptionalSummary::after {
  content: "+";
  color: var(--primary-text);
  font-size: 20px;
  font-weight: 300;
  line-height: 1;
}

.fr26OptionalGroup[open] .fr26OptionalSummary::after { content: "−"; }

.fr26OptionalMeta {
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
}

.fr26OptionalBody { border-top: 1px solid var(--stroke); }

.fr26PhotoActions {
  display: grid;
  grid-template-columns: repeat(3, minmax(0,1fr));
  gap: 8px;
  padding: 14px 16px;
}

.fr26PhotoActions .btn { width: 100%; }

/* ── Scan hero ─────────────────────────────────────────────────────── */
.scanHeroWrap {
  padding: 14px 16px 8px;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.scanHeroBtn {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 14px 18px;
  background: var(--primary);
  color: #fff;
  border: none;
  /* Flat bottom — .scanAltBtn sits flush underneath as one continuous card
     instead of two separate floating buttons with a gap between them.
     They're really one action (get the job details from a photo). */
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  cursor: pointer;
  text-align: left;
  box-shadow: 0 4px 18px color-mix(in srgb, var(--primary) 30%, transparent);
  transition: transform .12s var(--ease), box-shadow .12s var(--ease);
}
.scanHeroBtn:active {
  transform: scale(.97);
  box-shadow: 0 2px 8px color-mix(in srgb, var(--primary) 20%, transparent);
}
/* Scanning state: a bright bar sweeps across like a flatbed scanner, so the
   OCR wait reads as active progress instead of a frozen button. */
.scanHeroBtn.scanning {
  cursor: default;
}
.scanHeroBtn.scanning::after {
  content: "";
  position: absolute;
  top: 0;
  left: -35%;
  width: 35%;
  height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.45), transparent);
  animation: scanSweepBar 1.15s ease-in-out infinite;
}
@keyframes scanSweepBar {
  0%   { left: -35%; }
  100% { left: 100%; }
}
.scanHeroBtn.scanning .scanHeroIcon {
  animation: scanIconPulse 1.15s ease-in-out infinite;
}
@keyframes scanIconPulse {
  0%, 100% { opacity: .7; transform: scale(1); }
  50%      { opacity: 1;  transform: scale(1.12); }
}
.scanHeroIcon {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  opacity: .95;
  transition: opacity .15s, transform .15s;
}
.scanHeroLabel {
  font-size: 16px;
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.2;
  display: block;
}
.scanHeroSub {
  font-size: 11px;
  font-weight: 500;
  opacity: .75;
  display: block;
  margin-top: 1px;
}
.scanHeroAlt {
  display: flex;
  gap: 8px;
}
.scanAltBtn {
  /* Sits flush under .scanHeroBtn as the bottom of one card — see the
     border-radius split above. Light blue tint instead of plain gray so it
     still reads as part of the same blue "capture a job" module rather
     than an unrelated secondary button. */
  flex: 1;
  padding: 10px 0;
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  color: var(--primary-text);
  border: none;
  border-top: 1px solid color-mix(in srgb, var(--primary) 18%, transparent);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  text-align: center;
  transition: background .1s;
}
.scanAltBtn:active { background: color-mix(in srgb, var(--primary) 18%, transparent); }

.fr26InlineStatus {
  padding: 0 16px 12px;
  color: var(--muted);
  font-size: 13px;
}

.fr26ScanStatus {
  padding: 0 16px 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--primary-text);
  min-height: 20px;
  transition: opacity 0.3s;
}
.fr26ScanStatus.scanning {
  color: var(--muted);
  font-weight: 400;
}

/* Alternative job chips shown after OCR scan */
.fr26ScanJobAlts {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0 16px 10px;
}
.fr26ScanJobAlt {
  font-size: 12px;
  padding: 4px 10px;
  border: 1px solid color-mix(in srgb, var(--primary) 50%, transparent);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--primary-text);
  cursor: pointer;
  white-space: nowrap;
}
.fr26ScanJobAlt:active {
  background: var(--primary);
  color: #fff;
}
.fr26ScanJobAlt.selected {
  background: var(--primary);
  border-color: var(--primary);
  color: #fff;
}
.fr26ScanJobAlt.selected::before {
  content: "✓ ";
}
.fr26ScanJobHint {
  flex-basis: 100%;
  font-size: 11px;
  color: var(--muted);
  padding-top: 2px;
}

/* Outer holds the border/background and stays put; the ::after fade lives
   here (not on the scrolling element itself) so it overlays the content
   instead of scrolling away with it — same split as .statsChipsOuter /
   .statsChipsWrap below. */
.fr26QuickToolsOuter {
  position: relative;
  border-top: 1px solid var(--stroke);
  background: var(--surface2);
}
.fr26QuickToolsOuter::after {
  content: "";
  position: absolute;
  top: 1px; right: 0; bottom: 0;
  width: 40px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--surface2));
}
.fr26QuickTools {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  padding: 14px 16px;
  overscroll-behavior-x: contain;
}
.fr26QuickTools::-webkit-scrollbar { display: none; }
.fr26QuickTools > * { flex: 0 0 auto; }

/* ── Stats pills ─────────────────────────────────── */
.pillRow {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 2px;
  overscroll-behavior-x: contain;
}
.pillRow::-webkit-scrollbar { display: none; }

.pill {
  flex: 1 1 0;
  min-width: 52px;
  background: var(--surface2);
  border-radius: var(--r-md);
  padding: 9px 6px;
  border: 1px solid var(--stroke);
  text-align: center;
}

.k { font-size: 10px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .3px; }
.v { font-size: 15px; font-weight: 800; margin-top: 3px; letter-spacing: -.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pill .muted { font-size: 11px; margin-top: 1px; }

/* ── Type badges ─────────────────────────────────── */
.typeBadge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--r-pill);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .2px;
}

.typeBadge--sold     { background: rgba(34,197,94,.15);   color: #4ade80; }
.typeBadge--preowned { background: rgba(59,130,246,.15);  color: #60a5fa; }
.typeBadge--fpf      { background: rgba(251,146,60,.15);  color: #fb923c; }
.typeBadge--warranty { background: rgba(250,204,21,.15);  color: #fde047; }
.typeBadge--default  { background: var(--surface3);       color: var(--muted); }

/* ── Goal bar ────────────────────────────────────── */
.goalBar {
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--surface3);
  overflow: hidden;
  margin-top: 10px;
}

.goalBarFill {
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--primary);
  transition: width 600ms var(--ease);
  width: 0%;
}

.goalBarFill.complete { background: var(--accent); }

/* ── Warnings ────────────────────────────────────── */
#dupWarnGlobal {
  display: none;
  margin-bottom: 10px;
  padding: 10px 14px;
  border-radius: var(--r-md);
  border: 1px solid rgba(251,146,60,.3);
  background: rgba(251,146,60,.1);
  color: #fb923c;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.45;
}

#dupWarnGlobal[data-level="strong"] {
  border-color: color-mix(in srgb, var(--danger) 35%, transparent);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  color: var(--danger);
}

/* ── Earnings preview ────────────────────────────── */
#earningsPreview {
  text-align: center;
  font-size: 13px;
  font-weight: 600;
  color: transparent;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 200ms var(--ease), opacity 200ms var(--ease);
}

#earningsPreview.hasValue {
  color: var(--accent);
  font-size: 16px;
  font-weight: 700;
  max-height: 32px;
  opacity: 1;
  margin-top: 10px;
}

/* ── Toast ───────────────────────────────────────── */
.toast {
  position: fixed;
  bottom: calc(80px + env(safe-area-inset-bottom, 0px));
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: rgba(40,40,44,.96);
  color: #fff;
  padding: 11px 18px;
  border-radius: var(--r-pill);
  border: 1px solid rgba(255,255,255,.12);
  box-shadow: var(--shadow-lg);
  font-size: 13px;
  font-weight: 500;
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  max-width: calc(100vw - 48px);
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
  z-index: 9000;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

.toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* ── Modal ───────────────────────────────────────── */
/* All 8 modalShell instances (upgrade, entry detail, photo viewer/modal,
   onboarding, payday summary, what's new, lost time) now open/close through
   the shared openModalShell()/closeModalShell() helpers in utils.js — same
   backdrop fade + content scale for every one instead of each call site
   inventing its own instant display:none/flex snap.
   .closing exists only so the close fade has display:flex to animate against
   for its 240ms — display can't be transitioned directly, and dropping
   straight to the base rule's display:none the instant .open is removed
   would cut the fade off before a single frame of it rendered. */
.modalShell {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.75);
  display: none;
  z-index: 200;
  padding: 16px;
  overflow-y: auto;
  overscroll-behavior: contain;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 220ms var(--ease);
}

.modalShell.open, .modalShell.closing { display: flex; }
.modalShell.open { opacity: 1; }

/* The dialog itself settles into place slightly ahead of the backdrop
   finishing its fade — matches iOS's alert/sheet presentation, where the
   content arrives a beat before the dim reaches full strength, rather than
   both popping in at once. Targets the direct child structurally (every
   modalShell's single content wrapper has its own class already —
   .modalContent, .onboardCard, .whatsNewSheet, etc.) so this applies
   uniformly without needing to touch each modal's markup. */
.modalShell > :first-child {
  transform: scale(.94);
  opacity: 0;
  transition: transform 220ms var(--ease), opacity 180ms var(--ease);
}
.modalShell.open > :first-child {
  transform: scale(1);
  opacity: 1;
}

/* Pinch/double-tap zoom wrapper for photo viewers (photoModal, photoViewer).
   overflow:hidden crops panned/zoomed image to the frame.
   initPhotoZoom() (utils.js) now handles every touch gesture itself via raw
   touchstart/touchmove/touchend Touch Events — not the browser's own pinch
   recognition — so touch-action:none is correct and intentional here: it
   tells the browser not to start any gesture of its own on this element at
   all, so there's no race between "browser decides this might be a scroll"
   and our own e.preventDefault() in touchmove. The whole PAGE still can't
   visually zoom regardless — that's separately capped via scrollView.
   minimumZoomScale/maximumZoomScale in MainViewController.swift. */
.photoZoomWrap {
  position: relative;
  overflow: hidden;
  border-radius: 14px;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

.photoZoomWrap img {
  width: 100%;
  display: block;
  border-radius: inherit;
  transform-origin: 0 0;
  cursor: zoom-in;
}

.photoZoomWrap.photoZoomed img { cursor: grab; }

.photoZoomHint {
  text-align: center;
  margin-top: 6px;
}

.photo-error {
  margin-top: 12px;
  padding: 14px;
  border-radius: var(--r-md);
  border: 1px dashed rgba(255,255,255,.2);
  color: #fca5a5;
  background: rgba(239,68,68,.1);
  text-align: center;
  font-size: 13px;
}

.warningBanner {
  padding: 12px 14px;
  border-radius: var(--r-md);
  border: 1px solid rgba(255,196,0,.25);
  color: #fde68a;
  background: rgba(255,196,0,.08);
  font-size: 13px;
}

/* ── History overlay ─────────────────────────────── */
/* showHistory() in main-page.js toggles .open/.closing the same way the
   modalShell helpers do (see there for why .closing exists) — the sheet
   already had a nice slideUp on the way in, it just had no way back down;
   this gives the backdrop a fade to match and the sheet a proper exit. */
.historyOverlay {
  position: fixed;
  inset: 0;
  z-index: 500;
  background: rgba(0,0,0,.6);
  display: none;
  align-items: flex-end;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity 220ms var(--ease);
}

.historyOverlay.open, .historyOverlay.closing { display: flex; }
.historyOverlay.open { opacity: 1; }

.historySheet {
  width: 100%;
  max-height: 92dvh;
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  border-top: 1px solid var(--stroke2);
  overscroll-behavior: contain;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: slideUp 240ms var(--ease);
}
.historyOverlay.closing .historySheet {
  animation: slideDown 220ms var(--ease) forwards;
}

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

.historyHeader {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 16px 12px;
  border-bottom: 1px solid var(--stroke);
  flex-shrink: 0;
}

.historyTitle {
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -.2px;
}

.histCloseBtn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 0;
  background: var(--surface3);
  color: var(--muted);
  font-size: 15px;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 150ms;
}

.histCloseBtn:active { background: var(--stroke2); }

.historyControls {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--stroke);
  flex-shrink: 0;
}

.histRangeSeg { width: 100%; }
.histRangeSeg .segBtn { flex: 1; font-size: 13px; padding: 8px 4px; }

.histNavRow, .histCustomRow {
  display: flex;
  align-items: center;
  gap: 10px;
}
.histNavLabel {
  flex: 1;
  text-align: center;
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.1px;
}
.histCustomInput { flex: 1; }
.histCustomSep { color: var(--muted); font-size: 13px; }

.historySummary {
  display: flex;
  border-bottom: 1px solid var(--stroke);
  flex-shrink: 0;
  background: var(--surface2);
  transition: opacity 140ms var(--ease);
}

.histSumCell {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 10px 6px;
  border-right: 1px solid var(--stroke);
}

.histSumCell:last-child { border-right: 0; }

/* A quick fade-through whenever the range/search changes, so the numbers
   and list feel like they refreshed rather than just snapping to new
   values mid-scroll. */
.historySummary.histRefresh, .historyList.histRefresh { animation: histRefresh 220ms var(--ease); }
@keyframes histRefresh {
  from { opacity: .35; transform: translateY(3px); }
  to   { opacity: 1; transform: translateY(0); }
}

.historyExportRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 16px;
  border-bottom: 1px solid var(--stroke);
  background: var(--surface2);
  flex-shrink: 0;
}
.historyExportNote {
  font-size: 12px;
  color: var(--muted);
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.historyList {
  flex: 1;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding: 10px 16px 32px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.histDayHeader {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 14px 0 8px;
  border-bottom: 1px solid var(--stroke);
  margin-bottom: 8px;
  position: sticky;
  top: 0;
  background: var(--surface);
  z-index: 1;
}

.histDayKey {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.histDayTotals {
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
}

.histDayPay {
  color: var(--accent);
  font-weight: 700;
}

.histEntryRow {
  background: var(--surface2);
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  padding: 14px;
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 10px;
}

[data-color-theme="classic"][data-theme="light"] .histEntryRow { background: var(--surface); }

.histEntryLeft { flex: 1; min-width: 0; }

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

/* New cleaner entry layout */
.histEntryTopLine {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.histEntryRefNum {
  font-size: 16px;
  font-weight: 700;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  letter-spacing: -.2px;
  color: #fff;
}

[data-color-theme="classic"][data-theme="light"] .histEntryRefNum { color: var(--text); }

/* Tappable ref when entry has a photo */
.histEntryRefNum--photo {
  color: var(--primary-text);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.histEntryRefNum--photo:active { opacity: .7; }
.histEntryRefPhotoIcon { font-size: 13px; line-height: 1; }
[data-color-theme="classic"][data-theme="light"] .histEntryRefNum--photo { color: var(--primary-text); }

.histPhotoTag {
  font-size: 14px;
  line-height: 1;
}

.histEntryTypeLine {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 5px;
  flex-wrap: wrap;
}

.histEntryVin {
  font-size: 12px;
  color: var(--muted);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  margin-top: 5px;
}

.histEntryVinVal {
  color: #fff;
  font-weight: 600;
  letter-spacing: .4px;
}

[data-color-theme="classic"][data-theme="light"] .histEntryVinVal { color: var(--text); }

.histEntryNotes {
  font-size: 12px;
  color: var(--muted);
  margin-top: 4px;
  line-height: 1.35;
}

.histEntryRef {
  font-size: 12px;
  color: var(--muted);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  margin-top: 4px;
}

.histEntryMeta {
  font-size: 11px;
  color: var(--muted2);
  margin-top: 4px;
}

.histEntryRight {
  text-align: right;
  flex-shrink: 0;
}

.histEntryPay {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -.4px;
  line-height: 1;
  color: var(--accent);
}

.histEntryHrs {
  font-size: 11px;
  color: var(--muted);
  margin-top: 3px;
}

.histEntryActions {
  display: flex;
  gap: 6px;
  margin-top: 10px;
  flex-wrap: wrap;
}

/* ── More page ───────────────────────────────────── */
.top {
  position: sticky;
  top: 0;
  z-index: 100;
  /* Was hardcoded near-black like header/.tabBar — same theme-adherence
     bug, same color-mix fix. */
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  /* Same fix as the main header/.tabBar — near-opaque background made the
     heavy blur mostly invisible, but it still had to run on every scroll
     frame of the More page. */
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  padding: calc(12px + env(safe-area-inset-top, 0px)) 16px 12px;
  border-bottom: 1px solid var(--stroke);
}

.topRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-weight: 700;
  font-size: 18px;
}

.wrap {
  max-width: 680px;
  margin: 0 auto;
  padding: 12px 16px calc(80px + env(safe-area-inset-bottom));
}

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

@media (max-width: 640px) { .grid { grid-template-columns: 1fr; } }

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3px;
}

@media (max-width: 480px) { .gallery { grid-template-columns: repeat(2, 1fr); } }

.thumb {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--r-xs);
}

.danger {
  border-color: color-mix(in srgb, var(--danger) 25%, transparent);
  background: color-mix(in srgb, var(--danger) 6%, transparent);
}

/* ── Input class on more page ────────────────────── */
.input {
  display: block;
  width: 100%;
  padding: 12px 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text);
  font-size: 16px; /* was 15px — iOS zoom-on-focus trigger */
  font-family: inherit;
  outline: none;
  margin-top: 8px;
  -webkit-appearance: none;
  transition: border-color 150ms var(--ease), box-shadow 150ms var(--ease);
}

.input:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(0,149,246,.2);
}

/* ── More page cards ─────────────────────────────── */
.topTitle {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -.3px;
}

/* Persistent entry point to the Team dashboard — sits above the tab panels so
   it's visible no matter which More sub-tab is open, since it was previously
   only reachable from a buried Settings row or a tech-only "join" prompt.
   width+margin:auto (not a hard-coded 16px side margin) so it centers under
   the uniform-centering rule above 680px wide instead of hugging the left
   edge -- found during live verification of that fix: same class name/
   specificity, so whichever rule sits later in the file was winning, and a
   flat `margin: 12px 16px 0` here was clobbering the `margin-left/right:auto`
   set higher up. calc(100% - 32px) preserves the original 16px mobile inset
   on both sides since margin:auto splits the leftover space evenly. */
.teamEntryBanner {
  display: flex;
  align-items: center;
  gap: 12px;
  width: calc(100% - 32px);
  margin: 12px auto 0;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg);
  text-decoration: none;
  transition: transform 130ms cubic-bezier(.34,1.56,.64,1);
}
.teamEntryBanner:active { transform: scale(.985); }
.teamEntryIcon { font-size: 22px; flex: 0 0 auto; line-height: 1; }
.teamEntryBody { flex: 1; min-width: 0; }
.teamEntryTitle { display: block; font-size: 14.5px; font-weight: 700; color: var(--text); }
.teamEntrySub { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; line-height: 1.4; }
.teamEntryGo { flex: 0 0 auto; font-size: 13px; font-weight: 700; color: var(--primary-text); white-space: nowrap; }

.moreCard { padding: 0; overflow: hidden; }

.moreCardHeader {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted2);
  text-transform: uppercase;
  letter-spacing: .8px;
  padding: 10px 16px 8px;
}

.moreCardSub {
  font-size: 13px;
  color: var(--muted);
  padding: 0 16px 12px;
  margin-top: -4px;
}

.moreDivider {
  height: 1px;
  background: var(--stroke);
  margin: 0 16px;
}

/* Group label above a cluster of Settings cards (Account / Preferences /
   Payroll Tools / Support) — iOS-Settings-style section header so the tab
   reads as grouped sections instead of one flat stack of identical cards. */
.moreGroupLabel {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted2);
  text-transform: uppercase;
  letter-spacing: .6px;
  padding: 4px 6px 8px;
  margin-top: 22px;
}
.moreGroupLabel--first { margin-top: 12px; }

/* Sub-section labels inside a grouped collapsible (e.g. Insights & Trends) */
.insightsSubHead {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted2);
  padding: 12px 16px 2px;
  letter-spacing: .2px;
}

.moreRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 16px;
  min-width: 0;
}

.moreRow > * { min-width: 0; }

.moreRowLabel {
  font-size: 15px;
  font-weight: 500;
  color: var(--text);
}

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

.moreInput {
  width: 100%;
  padding: 11px 14px;
  border-radius: var(--r-md);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text);
  font-size: 16px; /* 16px prevents iOS auto-zoom on focus */
  font-family: inherit;
  outline: none;
  -webkit-appearance: none;
  transition: border-color 150ms var(--ease), box-shadow 150ms var(--ease);
}

.moreInput:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(var(--primary-rgb, 0,149,246),.2);
}

.moreInputNarrow { width: auto; min-width: 90px; max-width: 140px; text-align: right; }
/* Native time inputs (Payroll Cutoff Time, Reminder Time, Payday Time)
   need more room than the 90px default -- at that width the browser's
   own clock-icon + AM/PM text gets clipped to something like "12:(",
   found during a UI pass. Widening just the time inputs, not every
   .moreInputNarrow, since the plain number ones (Standard Day, Weekly
   Goal) already fit fine at 90px and shouldn't grow unnecessarily.
   118px (the first attempt) was still short by a hair -- a 12-hour value
   like "12:00 AM" rendered as "12:00 A" with the M clipped behind the
   clock icon. Found on a later pass; 150px clears it with room to spare. */
input[type="time"].moreInputNarrow { min-width: 150px; max-width: 160px; text-align: left; }

.moreInputGroup {
  display: flex;
  gap: 8px;
  align-items: center;
  /* Never let this shrink below what its input+button actually need --
     it sits in a space-between .moreRow next to a label/sub-label that
     wraps freely (min-width: 0 further up). Without flex-shrink: 0 here,
     a longer label string (e.g. "Expected Hours (This Week)") competes
     for width and can squeeze this group narrower than its children's
     own min-content size, which spills the Set button out past the row
     and off the right edge of the screen instead of just wrapping the
     label text more. Found via live viewport check after a copy change. */
  flex-shrink: 0;
}

.moreInputStack { border-top: 1px solid var(--stroke); }

/* Auth */
.authFields { padding: 0 16px; }

.moreAuthActions {
  display: flex;
  gap: 8px;
  padding: 12px 16px 0;
}

.moreAuthSecondary {
  display: flex;
  gap: 8px;
  padding: 8px 16px 4px;
}

.moreTextBtn {
  background: transparent;
  border-color: transparent;
  color: var(--muted);
  font-size: 13px;
  padding: 8px 10px;
}

.moreAuthStatus {
  font-size: 13px;
  color: var(--muted);
  padding: 8px 16px 14px;
  min-height: 16px;
}

/* Accent color picker */
.accentPickerLabel {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  cursor: pointer;
  overflow: visible;
}
.accentPickerLabel input[type="color"] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  border: none;
  padding: 0;
}
.accentColorPreview {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 3px solid var(--stroke2);
  background: var(--primary);
  pointer-events: none;
  transition: border-color 150ms, transform 120ms;
}
.accentPickerLabel:active .accentColorPreview { transform: scale(.9); }

/* Accent Color row when a preset color theme (not Classic) is active — the
   picker is genuinely disabled then (see initSettingsUI in more-page.js), so
   it should look it instead of sitting there fully bright as if it were live. */
.moreRow--disabled .moreRowLabel { color: var(--muted); }
.moreRow--disabled .accentPickerLabel { opacity: .45; cursor: not-allowed; }
.moreRow--disabled .accentColorPreview { border-color: var(--stroke); }
.moreRow--disabled input[type="color"]:disabled { cursor: not-allowed; }

/* Toggle switch (More page). This label IS the tap target — the row
   around it doesn't also toggle it — so it needs to earn its own size.
   Height nudged from 26 to 30px, matching a real iOS switch's proportions
   rather than the flat 44x44 floor, which would look broken here. */
.moreToggle {
  position: relative;
  display: inline-block;
  width: 44px;
  height: 30px;
  flex-shrink: 0;
  cursor: pointer;
}

.moreToggle input { opacity: 0; width: 0; height: 0; position: absolute; }

.moreToggleTrack {
  position: absolute;
  inset: 0;
  border-radius: var(--r-pill);
  background: var(--surface3);
  border: 1px solid var(--stroke2);
  transition: background 200ms, border-color 200ms;
}

.moreToggleTrack::after {
  content: "";
  position: absolute;
  /* Re-centered for the taller (30px) track — was top/left:3px sized for
     the old 26px track, which would now sit off-center. */
  top: 6px;
  left: 6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--muted);
  transition: transform 200ms var(--ease-bounce), background 200ms;
}

.moreToggle input:checked ~ .moreToggleTrack {
  background: var(--primary);
  border-color: transparent;
}

.moreToggle input:checked ~ .moreToggleTrack::after {
  transform: translateX(14px);
  background: #fff;
}

/* Save row */
.moreSaveRow {
  padding: 12px 16px 16px;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.moreSaveBtn { flex: 1; min-width: 120px; min-height: 48px; }

/* Export grid */
.exportGrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--stroke);
  border-top: 1px solid var(--stroke);
}

.exportBtn {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 18px 12px;
  background: var(--surface);
  border: 0;
  cursor: pointer;
  transition: background 150ms var(--ease);
}

.exportBtn:active { background: var(--surface2); }

.exportBtnIcon {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .5px;
  color: var(--primary-text);
  background: rgba(0,149,246,.12);
  padding: 6px 10px;
  border-radius: var(--r-md);
}

.exportBtnLabel {
  font-size: 13px;
  font-weight: 600;
  color: var(--text2);
}

/* Review controls */
.reviewControls {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0 16px 12px;
}

.reviewRangeSeg,
.reviewFocusSeg { width: 100%; }

.reviewRangeSeg .segBtn,
.reviewFocusSeg .segBtn { flex: 1; }

/* Pay stub result */
.moreResultText {
  font-size: 13px;
  color: var(--muted);
  padding: 8px 16px 0;
}

.moreResultMono {
  font-size: 13px;
  color: var(--muted);
  font-family: inherit;
  padding: 4px 16px 6px;
}

/* Danger */
.moreDangerCard { border-color: color-mix(in srgb, var(--danger) 20%, transparent); background: color-mix(in srgb, var(--danger) 4%, transparent); }
.moreDangerHeader { color: var(--danger); }

.dangerActions {
  display: flex;
  gap: 8px;
  padding: 0 16px 16px;
}

.dangerBtn {
  flex: 1;
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  border-color: color-mix(in srgb, var(--danger) 25%, transparent);
  color: var(--danger);
}

.dangerBtnStrong {
  background: color-mix(in srgb, var(--danger) 18%, transparent);
  border-color: color-mix(in srgb, var(--danger) 35%, transparent);
  color: var(--danger);
}

/* ── Page-level helpers ──────────────────────────── */
.statusMsg {
  max-width: 680px;
  margin: 8px auto 0;
  padding: 0 16px;
}

.offlineBanner {
  max-width: 680px;
  margin: 8px auto 0;
  padding: 10px 16px;
  border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  color: #fca5a5;
  font-size: 13px;
  font-weight: 600;
  text-align: center;
}

.headerTotal {
  font-size: 15px;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: -.3px;
  opacity: 0;
  transition: opacity 300ms;
}

/* ── Strip comeback hint ─────────────────────────── */
.stripComebackHint {
  font-size: 10px;
  font-weight: 700;
  color: #fbbf24;
  text-transform: uppercase;
  letter-spacing: .3px;
  margin-top: 2px;
}

/* ── Insights card ───────────────────────────────── */
.insightGrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  padding: 8px 0;
}
.insightCell {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.insightLabel {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--muted);
}
.insightValue {
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.4px;
}
.insightValue--warn { color: #fbbf24; }
.insightTopEarner {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--stroke);
  font-size: 13px;
  color: var(--text2);
}
.insightTopEarner strong { color: var(--text); }

/* ── Cloud nudge / install banners ───────────────── */
.cloudNudge,
.installBanner {
  max-width: 680px;
  margin: 8px auto 0;
  padding: 10px 16px;
  border-radius: var(--r-md);
  font-size: 13px;
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.cloudNudge {
  background: rgba(0,149,246,.12);
  border: 1px solid rgba(0,149,246,.3);
  color: #60b8ff;
}
.installBanner {
  background: rgba(41,217,165,.12);
  border: 1px solid rgba(41,217,165,.3);
  color: var(--accent);
}

/* ── Comeback badge ───────────────────────────────── */
.comebackBadge {
  display: inline-block;
  padding: 2px 7px;
  border-radius: var(--r-pill);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .4px;
  background: rgba(250,204,21,.18);
  color: #fde047;
  vertical-align: middle;
}

.entryListHeader {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}
.entryListHeader > div > div:first-child {
  font-weight: 700;
  font-size: 15px;
  line-height: 1.3;
}
.entryListHeader > div > .small {
  font-size: 11px;
  color: var(--muted);
  margin-top: 1px;
}

.searchRow {
  margin-top: 8px;
}

/* Primary entry actions: scrollable chip row */
.entryActions {
  display: flex;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  gap: 8px;
  margin-top: 10px;
  padding-bottom: 2px;
  overscroll-behavior-x: contain;
}
.entryActions::-webkit-scrollbar { display: none; }
.entryActions .btn {
  flex: 0 0 auto;
  white-space: nowrap;
  font-size: 13px;
  padding: 8px 14px;
  border-radius: var(--r-pill);
}

/* Selection actions: hidden until items selected */
.entrySelectionActions {
  display: none;
  gap: 8px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--stroke);
}

.has-selection .entrySelectionActions { display: flex; }

/* ── Filter select pill ──────────────────────────── */
#filterSelect {
  width: auto;
  padding: 8px 12px;
  border-radius: var(--r-pill);
  font-size: 13px;
  font-weight: 600;
  background: var(--surface2);
  border: 1px solid var(--stroke2);
  color: var(--text);
  cursor: pointer;
  -webkit-appearance: none;
}

/* ── Search highlight ────────────────────────────── */
.srchHl {
  background: rgba(250,204,21,.35);
  color: inherit;
  border-radius: 2px;
  padding: 0 1px;
}

/* ── Empty state ─────────────────────────────────── */
.emptyState {
  text-align: center;
  padding: 32px 16px;
}

.emptyStateTitle {
  font-size: 16px;
  font-weight: 700;
  color: var(--text2);
  margin-bottom: 6px;
}

.emptyStateSub {
  font-size: 13px;
  color: var(--muted);
}

/* ── Light theme — forced dark (app is always dark) ── */
[data-color-theme="classic"][data-theme="light"] {
  /* iOS system light — standard, accessible, universal */
  --bg: #f2f2f7;
  --surface: #ffffff;
  --surface2: #e5e5ea;
  --surface3: #d1d1d6;
  --stroke: rgba(0,0,0,.12);
  --stroke2: rgba(0,0,0,.18);
  --text: #000000;
  --text1: #000000;
  --text2: rgba(0,0,0,.60);
  --muted: #3c3c43;
  --muted2: rgba(60,60,67,.82);
  --primary-text: #1d4db7;
  --shadow-sm: 0 1px 3px rgba(0,0,0,.10);
  --shadow-md: 0 4px 16px rgba(0,0,0,.12);
  --shadow-lg: 0 12px 40px rgba(0,0,0,.16);
}

[data-color-theme="classic"][data-theme="light"] header {
  background: rgba(240,242,245,.94);
  border-bottom-color: rgba(0,0,0,.10);
}

[data-color-theme="classic"][data-theme="light"] .tabBar {
  background: rgba(255,255,255,.97);
  border-top-color: rgba(0,0,0,.10);
}

[data-color-theme="classic"][data-theme="light"] .card {
  box-shadow: 0 1px 4px rgba(0,0,0,.06);
}

[data-color-theme="classic"][data-theme="light"] .item { box-shadow: 0 1px 3px rgba(0,0,0,.05); }

[data-color-theme="classic"][data-theme="light"] .moreTabBar {
  background: #ffffff;
  border-bottom-color: rgba(0,0,0,.08);
}

[data-color-theme="classic"][data-theme="light"] .moreToggleTrack {
  background: var(--surface3);
  border-color: var(--stroke2);
}

/* Light theme — hero & chart fixes */
[data-color-theme="classic"][data-theme="light"] .heroSection { background: var(--surface); border-bottom: 1px solid var(--stroke); }
[data-color-theme="classic"][data-theme="light"] .heroChartInner { background: var(--surface); border-color: var(--stroke2); border-top-color: color-mix(in srgb, var(--primary) 30%, transparent); }
[data-color-theme="classic"][data-theme="light"] .heroGoalPctText { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .hcChartArea { border-top-color: var(--stroke2); }
[data-color-theme="classic"][data-theme="light"] .heroAmt { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .heroEyebrow { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .heroSubLine { color: var(--text2); }
[data-color-theme="classic"][data-theme="light"] .heroPaceLine { color: var(--primary-text); }
[data-color-theme="classic"][data-theme="light"] .hcTab { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .hcStat { border-right-color: var(--stroke2); }
[data-color-theme="classic"][data-theme="light"] .hcStatsRow { border-top-color: var(--stroke2); }
[data-color-theme="classic"][data-theme="light"] .hcGoalBarTrack { background: rgba(0,0,0,.12); }
[data-color-theme="classic"][data-theme="light"] .hcEntriesList { border-top-color: var(--stroke2); }
[data-color-theme="classic"][data-theme="light"] .hcEntryRow { border-bottom-color: var(--stroke2); }
[data-color-theme="classic"][data-theme="light"] .hcEntryMore { border-top-color: var(--stroke2); }

/* Light theme — form & input visibility */
[data-color-theme="classic"][data-theme="light"] .fr26Input { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
[data-color-theme="classic"][data-theme="light"] .fr26Input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent); }
[data-color-theme="classic"][data-theme="light"] .fr26Textarea { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
[data-color-theme="classic"][data-theme="light"] input::placeholder, [data-color-theme="classic"][data-theme="light"] textarea::placeholder { color: #94a3b8; }
[data-color-theme="classic"][data-theme="light"] .fr26Label { color: var(--text); font-weight: 600; }
[data-color-theme="classic"][data-theme="light"] .fr26HourBtn.selected { background: color-mix(in srgb, var(--primary) 15%, transparent); border-color: var(--primary); color: var(--primary-dark); }
[data-color-theme="classic"][data-theme="light"] .fr26HourBtn:hover { background: color-mix(in srgb, var(--primary) 14%, transparent); }
[data-color-theme="classic"][data-theme="light"] .fr26Chip { background: var(--surface2); color: var(--text); border-color: rgba(0,0,0,.18); }
[data-color-theme="classic"][data-theme="light"] .fr26Check span { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .segBtn { background: transparent; color: var(--text2); border-color: rgba(0,0,0,.15); }
[data-color-theme="classic"][data-theme="light"] .segBtn.active { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
[data-color-theme="classic"][data-theme="light"] .pill { background: var(--surface2); color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .tabItem { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .tabItem--active { color: var(--primary-text); }
[data-color-theme="classic"][data-theme="light"] .goalBar { background: var(--surface3); }
[data-color-theme="classic"][data-theme="light"] .entryListHeader { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] select { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
/* More page light mode */
[data-color-theme="classic"][data-theme="light"] .moreInput { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
[data-color-theme="classic"][data-theme="light"] .moreRowLabel { color: var(--text); font-weight: 600; }
[data-color-theme="classic"][data-theme="light"] .moreRowSub { color: var(--muted2); }
[data-color-theme="classic"][data-theme="light"] .moreCardHeader { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .moreTab { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .moreTab.active { color: var(--primary-text); border-bottom-color: var(--primary); }
/* Type chips light mode */
[data-color-theme="classic"][data-theme="light"] .typeSuggestChip { background: #fff; border-color: rgba(0,0,0,.20); color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .typeSuggestChip:active { background: var(--surface2); }
[data-color-theme="classic"][data-theme="light"] .tscMeta { color: var(--muted2); }
/* Bulk list light mode */
[data-color-theme="classic"][data-theme="light"] .bulkEntryRef { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .bulkEntryType { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .bulkEntryMeta { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .bulkDeleteBar { background: #fff; border-top-color: rgba(0,0,0,.14); }
/* Entry items light mode */
[data-color-theme="classic"][data-theme="light"] .itemRef { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .itemType { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .itemNotes { color: var(--muted); }
/* Misc */
[data-color-theme="classic"][data-theme="light"] .hcRangeLabel { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .hcStatVal { color: var(--text); }
[data-color-theme="classic"][data-theme="light"] .hcStatLabel { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .k { color: var(--muted); }
[data-color-theme="classic"][data-theme="light"] .tourMascotAvatar { background: var(--surface3); }

/* ── System light mode — same rules without needing JS to set data-theme ── */
@media (prefers-color-scheme: light) {
  :root[data-color-theme="classic"]:not([data-theme="dark"]) header { background: rgba(240,242,245,.94); border-bottom-color: rgba(0,0,0,.10); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .tabBar { background: rgba(255,255,255,.97); border-top-color: rgba(0,0,0,.10); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .card { box-shadow: 0 1px 4px rgba(0,0,0,.06); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .item { box-shadow: 0 1px 3px rgba(0,0,0,.05); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .moreTabBar { background: #ffffff; border-bottom-color: rgba(0,0,0,.08); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .heroSection { background: var(--surface); border-bottom: 1px solid var(--stroke); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .heroChartInner { background: var(--surface); border-color: var(--stroke2); border-top-color: color-mix(in srgb, var(--primary) 30%, transparent); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .hcGoalBarTrack { background: rgba(0,0,0,.12); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .fr26Input { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .fr26Input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .fr26Textarea { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) input::placeholder, :root[data-color-theme="classic"]:not([data-theme="dark"]) textarea::placeholder { color: #94a3b8; }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .fr26HourBtn.selected { background: color-mix(in srgb, var(--primary) 15%, transparent); border-color: var(--primary); color: var(--primary-dark); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .fr26Chip { background: var(--surface2); color: var(--text); border-color: rgba(0,0,0,.18); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .segBtn { background: transparent; color: var(--text2); border-color: rgba(0,0,0,.15); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .segBtn.active { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) select { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .moreInput { background: #fff; color: var(--text); border: 1.5px solid rgba(0,0,0,.22); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .typeSuggestChip { background: #fff; border-color: rgba(0,0,0,.20); color: var(--text); }
  :root[data-color-theme="classic"]:not([data-theme="dark"]) .bulkDeleteBar { background: #fff; border-top-color: rgba(0,0,0,.14); }
}

/* ── Short-pay trend ─────────────────────────────── */
.ptSummary {
  padding: 12px 16px;
  font-size: 13px;
  font-weight: 600;
  border-bottom: 1px solid var(--stroke);
}
.ptSummaryWarn { color: var(--danger); background: color-mix(in srgb, var(--danger) 7%, transparent); }
.ptSummaryOk   { color: #10b981; }

.ptList { }

.ptRow {
  padding: 12px 16px;
  border-bottom: 1px solid var(--stroke);
}
.ptRow:last-child { border-bottom: 0; }
.ptRow--short { background: color-mix(in srgb, var(--danger) 5%, transparent); }

.ptWeek {
  font-size: 12px;
  font-weight: 700;
  color: var(--text2);
  margin-bottom: 8px;
  text-transform: uppercase;
  letter-spacing: .3px;
}

.ptCols {
  display: flex;
  gap: 0;
}

.ptCol { flex: 1; }
.ptColRight { text-align: right; }

.ptColLabel {
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .3px;
}

.ptColVal {
  font-size: 15px;
  font-weight: 700;
  margin-top: 2px;
  letter-spacing: -.3px;
}

.ptColSub {
  font-size: 11px;
  color: var(--muted);
  margin-top: 1px;
}

.ptActions {
  margin-top: 10px;
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  flex-wrap: wrap;
}

.ptActionBtn {
  min-width: 72px;
}

.ptDeltaShort { color: var(--danger); }
.ptDeltaOver  { color: #10b981; }
.ptDeltaEven  { color: var(--muted); }

/* ── Compact list mode ───────────────────────────── */
body.compact .itemInner { padding: 9px 12px; }
body.compact .itemTop { font-size: 13px; }
body.compact .itemPay { font-size: 16px; }
body.compact .itemMeta { margin-top: 3px; }
body.compact .itemActions { margin-top: 5px; }
body.compact .small   { font-size: 11px; }
body.compact .list    { gap: 1px; }

/* ── iBtn active state (comeback toggle) ─────────── */
.iBtn--active {
  background: rgba(0,149,246,.18);
  color: var(--primary-text);
  border-color: rgba(0,149,246,.5);
}

/* ── Collapsible More-page sections ─────────────── */
/* Previously a bare <details>/<summary> — the browser's native open/close
   is an instant, un-animated cut (no transition possible on `display`),
   which reads as janky next to the rest of the app's springy, haptic-backed
   interactions. initMoreSectionAnimations() in boot.js intercepts the
   summary click, keeps `details.open` true for the duration of the
   animation either way, and runs a JS-driven height animation on
   .moreSectionBody (via the Web Animations API) instead of letting the
   browser snap it open/closed. This is purely a motion upgrade — layout,
   spacing and padding are unchanged from before the wrapper was added. */
.moreSectionDetails { }
.moreSectionSummary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  transition: background-color 120ms var(--ease);
}
.moreSectionSummary:active {
  background-color: color-mix(in srgb, var(--primary) 7%, transparent);
}
.moreSectionSummary::-webkit-details-marker { display: none; }
.moreSectionSummary::after {
  content: "›";
  font-size: 18px;
  color: var(--muted2);
  transition: transform 220ms var(--ease);
  line-height: 1;
}
.moreSectionDetails[open] > .moreSectionSummary::after {
  transform: rotate(90deg);
}
/* Clips the content during the height animation; the animation itself
   sets an explicit height while running and clears it afterward. */
.moreSectionBody {
  overflow: hidden;
}

/* While a section is opening/closing, every row below it is sliding to a
   new position for ~260ms. A tap during that window can land on whatever
   just slid under the user's finger instead of the thing they meant to
   tap. Locking the whole panel to ignore taps until the layout settles
   fixes that; the section that's actually animating stays tappable (its
   own summary) so a quick re-tap can still reverse it mid-animation. */
.moreTabPanel--animating {
  pointer-events: none;
}
.moreTabPanel--animating .moreSectionDetails--animating {
  pointer-events: auto;
}

/* ── Job type list rows (More page) ──────────────── */
/* Quick-add form (always visible) */
.typeAddInline {
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.typeAddNameInput {
  width: 100%;
  box-sizing: border-box;
}
.typeAddRow2 {
  display: flex;
  gap: 8px;
  align-items: center;
}
.typeAddRow2 .moreInputNarrow {
  width: 72px;
  flex-shrink: 0;
}
.typeAddRow2 .typeAddSubmitBtn {
  flex: 1;
  min-height: 40px;
}
.typeAddMiniLabel {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 11px;
  color: var(--muted);
  font-weight: 500;
}

/* Keep old classes for backwards compat */
.typeAddDetails { border-top: 1px solid var(--stroke); }
.typeAddForm { padding: 0 16px 14px; border-top: 1px solid var(--stroke); }
.typeAddFields { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding-top: 12px; }
.typeAddFields .typeNameField { grid-column: 1 / -1; }
.typeAddFields .btn { grid-column: 1 / -1; width: 100%; }

.typeRow {
  padding: 12px 16px;
  border-top: 1px solid var(--stroke);
  transition: background 120ms;
}
.typeRow:first-child { border-top: none; }
.typeRow:active { background: var(--surface2); }
.typeRowMain {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
}
.typeRowInfo {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.typeRowName {
  font-weight: 600;
  font-size: 15px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.typeRowMeta {
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
}
.typeRowActions {
  display: flex;
  gap: 12px;
  flex-shrink: 0;
}
.typeRowActions .iBtn {
  padding: 8px 14px;
  font-size: 12px;
  min-height: 38px;
}
.typeEditForm {
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--stroke);
  animation: itemSlideIn 200ms ease both;
}
.typeEditFields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  align-items: end;
}
.typeEditActions {
  grid-column: 1 / -1;
  display: flex;
  gap: 6px;
}
.typeEditActions .btn { flex: 1; }
.typeEditLabel {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 11px;
  color: var(--muted);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .4px;
}
.typeEditLabel input { font-size: 16px; } /* was 14px — iOS zoom-on-focus trigger */

/* ── Inner More-page tab bar ─────────────────────────── */
.moreTabBar {
  display: flex;
  background: var(--surface);
  border-bottom: 1px solid var(--stroke);
  padding: 0 4px;
  position: sticky;
  top: 0;
  z-index: 110; /* above .top header (z-index:100) so it's never covered when scrolled */
}

.moreTab {
  flex: 1;
  padding: 12px 4px 11px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .1px;
  color: var(--muted);
  background: none;
  border: none;
  cursor: pointer;
  position: relative;
  transition: color 140ms var(--ease);
  touch-action: manipulation;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}

.moreTab.active { color: var(--primary-text); }

.moreTab.active::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 12%;
  right: 12%;
  height: 2px;
  background: var(--primary);
  border-radius: 2px 2px 0 0;
}

.moreTabPanel { display: none; }
.moreTabPanel.active { display: block; }

/* ── Icon-only buttons for job type rows ─────────────── */
.typeIconBtn {
  /* Was 32px, sitting 10px from its Edit/Delete twin — bumped for the
     same reason as the Log-tab entry actions (see .hcEntryEditBtn). */
  width: 36px;
  height: 36px;
  border-radius: var(--r-sm);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 120ms, color 120ms;
  touch-action: manipulation;
  padding: 0;
}
.typeIconBtn:active { opacity: .65; }
.typeIconBtn--del {
  color: var(--danger);
  border-color: rgba(239,68,68,.2);
  background: rgba(239,68,68,.05);
}

/* ── Bulk delete (History tab) ───────────────────────── */
.bulkEntryRow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 16px;
  border-top: 1px solid var(--stroke);
  transition: background 120ms;
  cursor: default;
  /* pan-y: iOS handles vertical scroll natively; we own horizontal for swipe-to-delete */
  touch-action: pan-y;
}
.bulkEntryRow:first-child { border-top: none; }
.bulkEntryRow.is-selected { background: color-mix(in srgb, var(--primary) 8%, transparent); }
.bulkEntryCheck { display: flex; align-items: center; flex-shrink: 0; padding: 10px; margin: -10px; }
.bulkEntryCheck input { width: 20px; height: 20px; accent-color: var(--primary); cursor: pointer; }
.bulkEntryInfo { flex: 1; min-width: 0; }
.bulkEntryRef { font-size: 14px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bulkEntryType { font-weight: 400; color: var(--muted); font-size: 13px; }
.bulkEntryNoRef { color: var(--muted); font-weight: 400; font-style: italic; }
.bulkEntryMeta { font-size: 12px; color: var(--muted); margin-top: 2px; }

.bulkDeleteBar {
  position: sticky;
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  border-top: 1px solid var(--stroke);
  background: var(--surface);
  z-index: 10;
}

/* RO# always-visible row */
.fr26RowRef .fr26LabelRow { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.fr26RefTypeSeg { font-size: 11px; }
.fr26RefTypeSeg .segBtn { padding: 8px 12px; font-size: 11px; min-height: 40px; } /* was 36px — this toggle gets tapped on every entry */

.moreCardHeaderRow {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-right: 12px;
}

/* ── Offline dot ─────────────────────────────────── */
.offlineDot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--danger);
  flex-shrink: 0;
  transition: background 400ms;
}
/* 3 states: offline (red pulse), pending (orange pulse), synced (green dim)
   will-change only on animated states — avoids a permanent compositor layer */
.offlineDot--offline  { background: var(--danger); animation: pulseDot 1.4s ease-in-out infinite; will-change: transform, opacity; }
.offlineDot--pending  { background: #f59e0b; animation: pulseDot 1s ease-in-out infinite; will-change: transform, opacity; }
.offlineDot--synced   { background: var(--primary); opacity: .55; }

@keyframes pulseDot {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%       { opacity: .5; transform: scale(.75); }
}

/* Rate hint in earnings preview */
.epRate { font-size: 11px; color: var(--muted2); margin-left: 4px; }

/* ── Week summary bar (History tab) ──────────────── */
.histWeekBar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: var(--surface2);
  border-bottom: 1px solid var(--stroke);
  font-size: 12px;
}
.histWeekLabel { color: var(--muted); font-weight: 600; flex-shrink: 0; }
.histWeekStat  { color: var(--text2); }
.histWeekPay   { color: var(--primary-text); font-weight: 700; margin-left: auto; }

/* ── Short-pay badge on More button ──────────────── */
.headerMoreBtn { position: relative; }
.shortPayBadge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--r-sm);
  background: var(--danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

/* ── Onboarding modal ────────────────────────────── */
#onboardingModal {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10000;
  padding: 24px;
}
.onboardCard {
  width: 100%;
  max-width: 360px;
  padding: 28px 24px 24px;
}
.onboardTitle {
  font-size: 20px;
  font-weight: 700;
  margin-bottom: 4px;
}
.onboardSub {
  font-size: 14px;
  color: var(--muted);
  margin-bottom: 20px;
}
.onboardField {
  margin-bottom: 14px;
}
.onboardLabel {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .3px;
  margin-bottom: 6px;
}

/* ── Dup warning — make it impossible to miss ────── */
#dupWarnGlobal {
  border-radius: var(--r-md);
  padding: 8px 12px;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 6px;
}
#dupWarnGlobal[data-level="strong"] {
  background: color-mix(in srgb, var(--danger) 15%, transparent);
  color: #f87171;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  animation: shakeWarn .35s ease;
}
#dupWarnGlobal[data-level="weak"] {
  background: rgba(234,179,8,.12);
  color: #fbbf24;
  border: 1px solid rgba(234,179,8,.3);
}
@keyframes shakeWarn {
  0%,100% { transform: translateX(0); }
  20%     { transform: translateX(-6px); }
  40%     { transform: translateX(6px); }
  60%     { transform: translateX(-4px); }
  80%     { transform: translateX(4px); }
}

/* ── Onboarding Tour ─────────────────────────────── */
#tourOverlay {
  position: fixed;
  inset: 0;
  z-index: 10000;
  pointer-events: all;
  background: rgba(0,0,0,0.72);
  transition: background 300ms ease;
}

#tourOverlay.tour-has-target { background: transparent; }

#tourOverlay:not(.tour-has-target) .tourTooltip {
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
}

/* Spring-based spotlight — feels physical when it jumps between elements */
#tourSpotlight {
  position: fixed;
  border-radius: var(--r-lg);
  box-shadow: 0 0 0 9999px rgba(0,0,0,.72);
  border: 2.5px solid var(--primary);
  transition:
    top    420ms cubic-bezier(0.34, 1.3, 0.64, 1),
    left   420ms cubic-bezier(0.34, 1.3, 0.64, 1),
    width  420ms cubic-bezier(0.34, 1.3, 0.64, 1),
    height 420ms cubic-bezier(0.34, 1.3, 0.64, 1);
  pointer-events: none;
  display: none;
}

/* Tooltip slides up on first appear */
.tourTooltip {
  position: fixed;
  left: 12px;
  right: 12px;
  bottom: calc(76px + env(safe-area-inset-bottom));
  background: var(--surface);
  border: 1px solid rgba(0,149,246,.25);
  border-radius: var(--r-xl);
  padding: 20px 18px 16px;
  box-shadow: 0 12px 48px rgba(0,0,0,.55), 0 0 0 1px rgba(0,149,246,.1);
  z-index: 10001;
  max-width: 520px;
  margin: 0 auto;
  animation: tourTooltipIn 480ms cubic-bezier(0.34, 1.4, 0.64, 1) both;
}

@keyframes tourTooltipIn {
  from { opacity: 0; transform: translateY(36px) scale(0.93); }
  to   { opacity: 1; transform: translateY(0)    scale(1);    }
}

/* Each step's content fades+slides in */
.tourTooltip.step-enter .tourStepLabel { animation: tourContentIn 280ms cubic-bezier(0.4,0,.2,1) both; }
.tourTooltip.step-enter .tourTitle     { animation: tourContentIn 300ms cubic-bezier(0.4,0,.2,1) 40ms  both; }
.tourTooltip.step-enter .tourBody      { animation: tourContentIn 300ms cubic-bezier(0.4,0,.2,1) 90ms  both; }

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

.tourHeader {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}

.tourStepLabel {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--primary-text);
}

.tourSkip {
  background: none;
  border: none;
  padding: 0;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
  line-height: 1;
  transition: color 150ms;
}
.tourSkip:hover  { color: var(--text); }
.tourSkip:active { color: var(--text2); }

.tourTitle {
  font-size: 18px;
  font-weight: 700;
  margin-bottom: 8px;
  letter-spacing: -.2px;
  color: var(--text);
}

.tourBody {
  font-size: 14px;
  color: var(--text2);
  line-height: 1.65;
  margin-bottom: 18px;
}

.tourFooter {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

/* Pill-shaped progress dots — active dot expands into a capsule */
.tourDots {
  display: flex;
  gap: 5px;
  align-items: center;
}

.tourDot {
  height: 6px;
  width: 6px;
  border-radius: 3px;
  background: var(--stroke2);
  transition: width 300ms cubic-bezier(0.34,1.4,.64,1), background 200ms ease;
}

.tourDot--active {
  width: 20px;
  background: var(--primary);
}

/* Next button press feedback */
#tourNextBtn {
  transition: transform 100ms ease, opacity 100ms ease;
  min-width: 80px;
}
#tourNextBtn:active { transform: scale(0.94); opacity: .85; }

/* Double-ring expanding pulse on spotlight */
@keyframes tourPulse {
  0%   {
    box-shadow: 0 0 0 9999px rgba(0,0,0,.72),
                0 0 0 0   rgba(0,149,246,.7),
                0 0 0 0   rgba(0,149,246,.3);
  }
  60%  {
    box-shadow: 0 0 0 9999px rgba(0,0,0,.72),
                0 0 0 10px rgba(0,149,246,.0),
                0 0 0 20px rgba(0,149,246,.0);
  }
  100% {
    box-shadow: 0 0 0 9999px rgba(0,0,0,.72),
                0 0 0 0   rgba(0,149,246,.7),
                0 0 0 0   rgba(0,149,246,.3);
  }
}

#tourSpotlight.pulse {
  animation: tourPulse 1.8s cubic-bezier(0.4,0,0.6,1) infinite;
}

/* ── Tour mascot — FR Buddy guide character ──────────────────────────────── */
.tourMascotBar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-bottom: 14px;
  margin-bottom: 4px;
  border-bottom: 1px solid rgba(255,255,255,0.07);
}

.tourMascotAvatar {
  width: 46px;
  height: 46px;
  border-radius: 22%;
  background: #0b1220; /* light override below */
  border: 2px solid var(--primary);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  overflow: hidden;
  animation: mascotBob 2.8s ease-in-out infinite;
  box-shadow: 0 0 12px color-mix(in srgb, var(--primary) 25%, transparent);
}

@keyframes mascotBob {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  45%       { transform: translateY(-5px) rotate(1.5deg); }
  70%       { transform: translateY(-2px) rotate(0deg); }
}

.tourMascotName {
  font-size: 13px;
  font-weight: 800;
  color: var(--primary-text);
  letter-spacing: .03em;
}

.tourMascotSub {
  font-size: 11px;
  color: var(--text2);
  margin-top: 2px;
}

/* ── Responsive ─────────────────────────────────────────────────────────── */

/* Very small phones (< 380px) */
@media (max-width: 380px) {
  .moreSaveBtn { min-width: 0; font-size: 13px; padding: 10px 8px; }
  .moreInputNarrow { min-width: 72px; }
  .seg { font-size: 13px; }
  .segBtn { padding: 8px 6px; }
  .fr26HourBtn { font-size: 13px; min-width: 52px; }
  .histEntryRefNum { font-size: 14px; }
}

/* Tablet / small desktop (681px – 960px) */
@media (min-width: 681px) {
  .wrap, main { padding-left: 24px; padding-right: 24px; }
  .moreCard { border-radius: var(--r-xl); }
  .card { border-radius: var(--r-xl); }
  .historySheet { max-width: 640px; margin: 0 auto; border-radius: var(--r-xl); align-self: center; }
  .historyOverlay { align-items: center; }
  .moreSaveBtn { min-width: 0; }
}

/* Wide desktop (> 960px) — decorative only now; header/.top/.tabBar/.wrap
   already center at every width via the unconditional rules above (see
   "Uniform content centering" near the main{} rule). This block just adds
   the card-like framing (rounded corners, shadow, breathing room off the
   browser chrome) once there's enough width for it to read as a floating
   panel rather than a pasted-on border. */
@media (min-width: 961px) {
  header { border-radius: 0 0 var(--r-lg) var(--r-lg); }
  .top   { border-radius: 0 0 var(--r-lg) var(--r-lg); }
  body   { background: var(--surface2); }
  main, .wrap {
    background: var(--bg);
    border-radius: var(--r-xl);
    margin-top: 16px;
    margin-bottom: 24px;
    box-shadow: 0 8px 40px rgba(0,0,0,.4);
  }
  .tabBar { border-radius: var(--r-xl) var(--r-xl) 0 0; }
  .historySheet { max-width: 640px; margin: 0 auto; border-radius: var(--r-xl); align-self: center; }
  .historyOverlay { align-items: center; }
}

/* ── Entry type-color left border ─────────────────── */
.item {
  border-left: 3px solid var(--surface3);
  border-radius: var(--r-lg);
}
.item[data-tc="sold"]     { border-left-color: #22c55e; }
.item[data-tc="warranty"] { border-left-color: #f59e0b; }
.item[data-tc="fpf"]      { border-left-color: #60a5fa; }
.item[data-tc="preowned"] { border-left-color: #a78bfa; }
.item[data-tc="default"]  { border-left-color: var(--surface3); }

/* ── Hero section ─────────────────────────────────── */
.heroSection {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 14px 16px 16px;
  max-width: 680px;
  margin: 0 auto;
  background: linear-gradient(to bottom, color-mix(in srgb, var(--primary) 7%, transparent) 0%, transparent 100%);
  position: relative;
}

.heroGoalRingWrap {
  position: relative;
  width: 102px;
  height: 102px;
  flex-shrink: 0;
}
.heroGoalRingWrap svg { display: block; width: 102px; height: 102px; }
.heroGoalRingCenter {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.heroGoalPctText {
  font-size: 21px;
  font-weight: 800;
  color: #fff;
  line-height: 1;
  letter-spacing: -.8px;
}
.heroGoalSubText {
  font-size: 10px;
  color: var(--muted);
  letter-spacing: 0.05em;
  margin-top: 3px;
  text-transform: uppercase;
}

.heroRight { flex: 1; min-width: 0; }
.heroEyebrow {
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  margin-bottom: 2px;
}
.heroAmt {
  font-size: 40px;
  font-weight: 700;
  color: #fff;
  letter-spacing: -1.8px;
  line-height: 1;
}
.heroSubLine {
  font-size: 12px;
  color: var(--text2);
  margin-top: 5px;
}
.heroPaceLine {
  font-size: 11px;
  font-weight: 500;
  color: var(--primary-text);
  margin-top: 3px;
  min-height: 14px;
}
.heroPayPeriodLine {
  font-size: 10px;
  font-weight: 500;
  color: var(--muted);
  margin-top: 2px;
  letter-spacing: .01em;
}
[data-color-theme="classic"][data-theme="light"] .heroPayPeriodLine { color: var(--muted2); }
.heroPaceWarn {
  font-size: 11px;
  font-weight: 600;
  /* Amber, not red: being behind pace mid-week is a nudge, not an error. */
  color: #f59e0b;
  margin-top: 2px;
  min-height: 14px;
  letter-spacing: .01em;
}
.heroEfficiency {
  font-size: 11px;
  margin-top: 3px;
  line-height: 1.3;
}
.effLabel { color: var(--muted); }

/* ── Clock-in button ──────────────────────────────── */
.clockInBtn {
  position: absolute;
  top: 10px;
  right: 12px;
  font-size: 11px;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--r-xl);
  border: 1.5px solid var(--stroke);
  background: var(--surface2);
  color: var(--muted);
  cursor: pointer;
  letter-spacing: .02em;
  transition: background .15s, color .15s, border-color .15s;
}
/* The pill itself stays visually compact (it's a corner badge sitting over
   other card content) but gets an invisible, bigger real tap zone rather
   than growing the visible shape, which risks overlapping neighboring
   card content. */
.clockInBtn::before {
  content: "";
  position: absolute;
  inset: -12px;
}
.clockInBtn--active {
  border-color: var(--primary);
  color: var(--primary-text);
  background: color-mix(in srgb, var(--primary) 8%, transparent);
}
.clockInBtn:active { transform: scale(.95); }

/* ── Repeat / combo chips ─────────────────────────── */
.repeatChip, .comboSuggestChip {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  text-align: center;
  border: 1.5px dashed var(--stroke);
  background: var(--surface2);
  color: var(--text2);
  transition: background .12s, border-color .12s;
}
.repeatChip:active, .comboSuggestChip:active { background: var(--surface3); }
.comboSuggestChip { border-style: dashed; border-color: color-mix(in srgb, var(--primary) 40%, transparent); color: var(--primary-text); }

/* ── Owe Me tracker ───────────────────────────────── */
.oweMeTotalBadge {
  font-size: 11px;
  font-weight: 700;
  color: var(--danger);
  background: color-mix(in srgb, var(--danger) 10%, transparent);
  padding: 2px 8px;
  border-radius: 10px;
}
.oweMeRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 14px;
  border-top: 1px solid var(--stroke);
  gap: 8px;
}
.oweMeDesc { font-size: 13px; color: var(--text); flex: 1; }
.oweMeRight { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.oweMeAmt { font-size: 13px; font-weight: 700; color: var(--danger); }
.oweMeDate { font-size: 11px; color: var(--muted2); }

/* ── Hero chart card (unified range + chart + stats) ─ */
.heroChartCard {
  padding: 0 16px 10px;
  max-width: 680px;
  margin: 0 auto;
}
.heroChartInner {
  background: var(--surface);
  border: 1px solid var(--stroke);
  border-top: 2px solid color-mix(in srgb, var(--primary) 25%, transparent);
  border-radius: var(--r-xl);
  overflow: hidden;
}

/* Range tabs row */
/* Day/Week/Month/Year + the date-nav group (label + 2 arrow buttons, pushed
   right via margin-left:auto on .hcNavGroup) add up to ~363px of content in
   ~317px of available width on a 375px phone (the width .hcTab actually
   ships at) -- heroChartInner's overflow:hidden then silently ate whatever
   didn't fit, usually the nav arrows, with no visual sign anything was
   missing. flex-wrap lets .hcNavGroup drop to its own row (still right-
   aligned there, since margin-left:auto applies per flex-line) instead of
   being clipped. Found auditing for elements that don't fit on a real phone
   width -- every row like this in the app needs to either scroll or wrap,
   never just get chopped off. */
.hcRangeTabs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px;
  row-gap: 4px;
  padding: 10px 12px 8px;
}
.hcTab {
  padding: 7px 14px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  border-radius: var(--r-pill);
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background 150ms, color 150ms, box-shadow 150ms;
  touch-action: manipulation;
}
.hcTab.hcTab--active {
  background: var(--primary);
  color: #fff;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--primary) 35%, transparent);
}
/* Nav group: label flanked by prev/next arrows */
.hcNavGroup {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 4px;
}
.hcRangeLabel {
  font-size: 12px;
  color: var(--muted2);
  font-weight: 600;
  letter-spacing: .2px;
  min-width: 52px;
  text-align: center;
}

/* Chart area — always visible, skeleton → real bars */
.hcChartArea {
  padding: 8px 10px 0;
  border-top: 1px solid var(--stroke);
}
.heroChartSvg {
  width: 100%;
  height: 56px;
  display: block;
  overflow: visible;
}
/* Skeleton bars shown before JS loads */
.hcBarSkel {
  fill: rgba(255,255,255,.07);
}
.hcBarSkel--today {
  fill: color-mix(in srgb, var(--primary) 18%, transparent);
}
[data-color-theme="classic"][data-theme="light"] .hcBarSkel { fill: rgba(0,0,0,.07); }
[data-color-theme="classic"][data-theme="light"] .hcBarSkel--today { fill: color-mix(in srgb, var(--primary) 18%, transparent); }

.heroChartLabelsRow {
  display: flex;
  justify-content: space-between;
  padding: 3px 2px 8px;
}
.heroChartLabel {
  font-size: 10px;
  color: var(--muted2);
  font-weight: 500;
  text-align: center;
  flex: 1;
}
.heroChartLabel--now { color: var(--primary-text); font-weight: 700; }

/* Stats row */
.hcStatsRow {
  display: flex;
  border-top: 1px solid var(--stroke);
}
.hcStat {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 12px 6px;
  border-right: 1px solid var(--stroke);
}
.hcStat:last-child { border-right: none; }
.hcStatVal {
  font-size: 19px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.5px;
  line-height: 1;
}
.hcStat--pay .hcStatVal { color: var(--primary-text); }
.hcStatLbl {
  font-size: 10px;
  color: var(--muted2);
  text-transform: uppercase;
  letter-spacing: .5px;
  margin-top: 4px;
}

/* Goal bar — now lives inside heroRight */
.hcGoalBar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 5px;
}
.hcGoalBarTrack {
  flex: 1;
  height: 2px;
  background: rgba(255,255,255,.12);
  border-radius: var(--r-pill);
  overflow: hidden;
}
.hcGoalBarFill {
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--primary);
  transition: width 600ms var(--ease);
  width: 0%;
}
.hcGoalBarFill.complete { background: var(--accent); }
.hcGoalBarMeta {
  font-size: 10px;
  font-weight: 600;
  color: var(--muted2);
  white-space: nowrap;
  flex-shrink: 0;
}

/* Nav buttons — live inside .hcNavGroup flanking the label. Was 32px;
   the existing flex-wrap fallback on .hcRangeTabs (see comment above)
   already absorbs the extra width on a narrow phone, so this is safe. */
.hcNavBtn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: none;
  background: var(--surface2);
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  touch-action: manipulation;
  flex-shrink: 0;
  transition: background 120ms, color 120ms;
}
.hcNavBtn:not(:disabled):active { background: var(--primary); color: var(--on-primary); }
.hcNavBtn:disabled { opacity: .3; cursor: default; }

/* VIN search bar */
.hcVinSearch {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px 4px;
}
.hcVinSearchInput {
  flex: 1;
  padding: 6px 10px;
  height: auto;
  border-radius: var(--r-sm);
  letter-spacing: .05em;
}
.hcVinClearBtn {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 16px;
  cursor: pointer;
  padding: 4px 6px;
  line-height: 1;
  flex-shrink: 0;
}
.hcVinClearBtn:active { color: var(--fg); }

/* Inline entries list */
.hcEntriesList {
  border-top: 1px solid var(--stroke);
}
/* Entry row wrapper */
.hcEntryWrap {
  border-bottom: 1px solid var(--stroke);
}
.hcEntryWrap:last-child { border-bottom: none; }

.hcEntryRow {
  display: flex;
  align-items: center;
  padding: 10px 12px;
  gap: 8px;
  min-height: 52px;
  background: var(--card-bg, var(--bg));
  transition: background 120ms;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: pan-y;
}
.hcEntryRow:active { background: var(--surface2); }

/* Inline action buttons — edit + delete, always visible */
.hcEntryActions {
  display: flex;
  /* Was 6px — Edit and Delete sitting almost flush was the single most
     common mis-tap in the app (this row renders on every logged job).
     10px puts real air between a safe action and a destructive one. */
  gap: 10px;
  flex-shrink: 0;
  align-items: center;
}
.hcEntryEditBtn {
  /* Was 34px — under Apple's 44pt / Material's 48dp guidance, and with
     Delete sitting right next to it. Bumped toward that floor. */
  width: 38px;
  height: 38px;
  border-radius: var(--r-sm);
  border: none;
  background: var(--surface2);
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.hcEntryEditBtn:active { background: var(--surface3); color: var(--text); }

.hcEntryDelBtn {
  width: 38px;
  height: 38px;
  border-radius: var(--r-sm);
  border: none;
  background: rgba(239,68,68,.1);
  color: #ef4444;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  flex-shrink: 0;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.hcEntryDelBtn:active { background: rgba(239,68,68,.22); }

.hcEntryLeft {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}
.hcEntryRef {
  font-size: 13px;
  font-weight: 700;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: var(--text);
  line-height: 1.2;
}
.hcEntryRef--photo {
  color: var(--text);
}
/* Dedicated photo chip — clearly tappable */
.hcEntryPhotoChip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary) 28%, transparent);
  border-radius: var(--r-pill);
  padding: 2px 8px 2px 6px;
  font-size: 11px;
  font-weight: 600;
  color: var(--primary-text);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  align-self: flex-start;
  margin-top: 2px;
}
.hcEntryPhotoChip:active { background: color-mix(in srgb, var(--primary) 22%, transparent); opacity: .85; }
/* Sits in a tightly-stacked column (2px gaps) with plain, non-tappable
   text above and below it, so a modest invisible hit-area is safe here —
   it can only ever compete with the chip itself, never another control. */
.hcEntryPhotoChip::before {
  content: "";
  position: absolute;
  inset: -6px;
}
.hcEntryPhotoIcon { font-size: 12px; }
.hcEntryType {
  font-size: 13px;
  color: var(--text2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.hcEntryVin {
  font-size: 10px;
  color: var(--muted);
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  letter-spacing: .04em;
}
.hcEntryDate {
  font-size: 10px;
  color: var(--muted);
}
.hcEntryRight {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  flex-shrink: 0;
}
.hcEntryPay {
  font-size: 15px;
  font-weight: 700;
  color: var(--primary-text);
}
.hcEntryHrs {
  font-size: 11px;
  color: var(--muted);
}
/* .hcEntryEditBtn defined above in .hcEntryActions block */
.hcEntryEmpty {
  padding: 20px 14px;
  font-size: 13px;
  color: var(--muted2);
  text-align: center;
}
.hcEntryMore {
  padding: 10px 14px;
  font-size: 11px;
  color: var(--muted2);
  text-align: center;
  border-top: 1px solid var(--stroke);
}

/* Action buttons inside heroChartCard */
.hcActionRow {
  display: flex;
  gap: 8px;
  padding: 10px 14px 14px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  border-top: 1px solid var(--stroke);
  overscroll-behavior-x: contain;
}
.hcActionRow::-webkit-scrollbar { display: none; }
.hcActionRow .btn {
  flex: 0 0 auto;
  font-size: 13px;
  padding: 7px 14px;
  white-space: nowrap;
  border-radius: var(--r-pill);
  border: 1px solid var(--stroke2);
  background: var(--surface2);
  color: var(--text2);
  cursor: pointer;
  touch-action: manipulation;
}

/* JS compat — always hidden, JS can't override */
.js-compat { display: none !important; }

/* ─── Animations & Live Effects ─────────────────────── */
/* ── Action Sheet ──────────────────────────────────────────────────── */
.asOverlay {
  position: fixed;
  inset: 0;
  z-index: 9900;
  background: rgba(0,0,0,0);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  transition: background 220ms var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.asOverlay--in {
  background: rgba(0,0,0,.52);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}
.asCard {
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 0 16px calc(env(safe-area-inset-bottom, 20px) + 12px);
  transform: translateY(100%);
  transition: transform 220ms var(--ease);
}
.asOverlay--in .asCard {
  transform: translateY(0);
}
.asHandle {
  width: 36px; height: 4px;
  background: var(--stroke2);
  border-radius: var(--r-pill);
  margin: 10px auto 14px;
}
.asTitle {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  text-align: center;
  margin-bottom: 4px;
}
.asMsg {
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  margin-bottom: 16px;
  line-height: 1.4;
}
.asConfirm {
  width: 100%;
  padding: 15px;
  border-radius: var(--r-lg);
  border: none;
  background: var(--surface2);
  color: var(--text);
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  touch-action: manipulation;
  margin-bottom: 10px;
}
.asConfirm.asDanger {
  background: rgba(239,68,68,.12);
  color: #ef4444;
}
.asConfirm:active { opacity: .75; }
.asCancel {
  width: 100%;
  padding: 15px;
  border-radius: var(--r-lg);
  border: none;
  background: var(--surface2);
  color: var(--muted);
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  touch-action: manipulation;
}
.asCancel:active { opacity: .75; }

[data-color-theme="classic"][data-theme="light"] .asCard { background: #f2f2f7; }
[data-color-theme="classic"][data-theme="light"] .asConfirm { background: #fff; }
[data-color-theme="classic"][data-theme="light"] .asCancel { background: #fff; }

@keyframes itemSlideIn {
  from { opacity:0; transform:translateY(18px) scale(.96); }
  to   { opacity:1; transform:translateY(0) scale(1); }
}
.item.item-enter { animation:itemSlideIn 340ms cubic-bezier(.34,1.56,.64,1) both; }

@keyframes btnPop {
  0%,100% { transform:scale(1); }
  40%     { transform:scale(.90); }
  70%     { transform:scale(1.07); }
}
.fr26BtnPrimary.btn-success { animation:btnPop 400ms cubic-bezier(.34,1.56,.64,1); }

.fr26HourBtn { transition:transform 130ms cubic-bezier(.34,1.56,.64,1),background 120ms,border-color 120ms,color 120ms; }
.fr26HourBtn:active { transform:scale(.83); }

@keyframes heroNumPop {
  0%,100% { transform:scale(1); }
  50%     { transform:scale(1.09); }
}
.heroAmt.pop { animation:heroNumPop 360ms cubic-bezier(.34,1.56,.64,1); }

@keyframes cfDrop {
  0%   { opacity:1; transform:translate(0,0) rotate(0deg); }
  100% { opacity:0; transform:var(--cf-end) rotate(var(--cf-rot)); }
}
.cfp {
  position:fixed; width:8px; height:8px; border-radius:2px;
  pointer-events:none; z-index:9999;
  animation:cfDrop var(--cf-dur,700ms) ease-in forwards;
}

/* Pull-to-refresh */
#ptrBar {
  height:0; overflow:hidden; display:flex; align-items:center;
  justify-content:center; gap:7px; font-size:11px; font-weight:600; color:var(--primary);
  transition:height 220ms cubic-bezier(.34,1.56,.64,1), color 150ms; background:var(--bg);
}
/* No transition while the finger is actually down — height tracks the drag
   1:1 (rubber-banded) instead of lagging behind it. */
#ptrBar.pulling { transition: color 150ms; }
#ptrBar.armed { color: #22c55e; }
#ptrBar.done { color: #22c55e; }
#ptrDot {
  width:14px; height:14px; border:2px solid currentColor;
  border-top-color:transparent; border-radius:50%;
  transition: transform 220ms cubic-bezier(.34,1.56,.64,1), border-color 150ms;
}
#ptrBar.armed #ptrDot { transform: scale(1.2) rotate(180deg); }
#ptrBar.done #ptrDot { border-color: #22c55e; border-top-color: #22c55e; transform: scale(1.05); }
@keyframes spinDot { to { transform:rotate(360deg); } }
#ptrDot.spin { animation:spinDot 500ms linear infinite; }

/* Milestone toast */
.mToast {
  position:fixed; bottom:88px; left:50%;
  transform:translateX(-50%) translateY(14px);
  background:var(--surface2); border:1.5px solid var(--primary);
  color:#fff; padding:10px 22px; border-radius:24px;
  font-size:13px; font-weight:600; opacity:0;
  transition:opacity 230ms ease, transform 230ms ease;
  pointer-events:none; z-index:998; white-space:nowrap;
}
.mToast.show { opacity:1; transform:translateX(-50%) translateY(0); }

/* Streak pill */
.heroStreak {
  display:inline-flex; align-items:center; gap:3px;
  font-size:10px; font-weight:700; color:var(--primary);
  background:color-mix(in srgb, var(--primary) 10%, transparent); border:1px solid color-mix(in srgb, var(--primary) 20%, transparent);
  border-radius:var(--r-sm); padding:2px 7px; margin-top:4px; letter-spacing:.04em;
}

/* Short-pay flag */
.shortPayFlag {
  font-size:10px; font-weight:700;
  background:rgba(245,158,11,.15); color:#f59e0b;
  border-radius:var(--r-xs); padding:1px 5px; margin-left:4px; vertical-align:middle;
}

/* Empty state */
.emptyWrap {
  padding:36px 16px; display:flex; flex-direction:column;
  align-items:center; gap:6px; text-align:center;
}
.emptyIcon { font-size:40px; line-height:1.1; }
.emptyTitle { font-size:15px; font-weight:600; color:var(--text); margin-top:4px; }
.emptySub { font-size:12px; color:var(--muted); max-width:220px; line-height:1.55; }

/* ── fr26Wrap — form positioned outside <main> ────── */
.fr26Wrap {
  padding: 0 16px 8px;
  max-width: 680px;
  margin: 0 auto;
}

/* ── Label row (label + timer button on same line) ── */
.fr26LabelRow {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* ── Job timer button ───────────────────────────── */
.fr26TimerBtn {
  height: 34px;
  padding: 0 10px;
  border: 1px solid var(--stroke2);
  border-radius: var(--r-pill);
  background: var(--surface2);
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  transition: background 120ms, color 120ms, border-color 120ms;
  touch-action: manipulation;
  letter-spacing: .3px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.fr26TimerBtn::before { content: "⏱"; font-size: 14px; font-family: inherit; }
.fr26TimerBtn--running {
  background: rgba(239,68,68,.12);
  border-color: rgba(239,68,68,.45);
  color: #ef4444;
}
.fr26TimerBtn--running::before { content: "■"; font-size: 10px; }
.fr26TimerBtn:active { transform: scale(.93); transition-duration: 60ms; }
[data-color-theme="classic"][data-theme="light"] .fr26TimerBtn { background: var(--surface2); border-color: var(--stroke); }

/* ── Comeback chip active state ─────────────────── */
.fr26ChipComeback--active {
  background: rgba(239,68,68,.12) !important;
  border-color: rgba(239,68,68,.45) !important;
  color: #ef4444 !important;
}

/* ── Short-pay alert banner ─────────────────────── */
.shortPayAlert {
  display: none;
  margin: 0 16px 6px;
  max-width: calc(680px - 32px);
  margin-left: auto;
  margin-right: auto;
  padding: 10px 14px;
  background: rgba(245,158,11,.1);
  border: 1px solid rgba(245,158,11,.3);
  border-radius: var(--r-lg);
  font-size: 13px;
  font-weight: 600;
  color: #f59e0b;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
}
.shortPayAlert a {
  color: inherit;
  text-decoration: none;
  margin-left: auto;
  font-size: 12px;
  opacity: .85;
  white-space: nowrap;
}
.shortPayAlert:empty, .shortPayAlert[hidden] { display: none !important; }

/* ── Hero records (best day / best week) ──────────── */
.heroRecords {
  font-size: 11px;
  color: var(--muted);
  margin-top: 5px;
  line-height: 1.4;
  letter-spacing: .1px;
}

/* ── Tech Rank badge ─────────────────────────────── */
.techRankBadge {
  font-size: 11px;
  font-weight: 600;
  color: var(--primary-text);
  margin-top: 5px;
  letter-spacing: .1px;
  opacity: .9;
}

/* ── Level-up overlay ────────────────────────────── */
.levelUpOverlay {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0,0,0,.72);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  cursor: pointer;
}
.levelUpCard {
  text-align: center;
  padding: 40px 48px;
  background: var(--surface2);
  border-radius: 24px;
  border: 1px solid var(--stroke);
  box-shadow: 0 24px 80px rgba(0,0,0,.5);
}
.levelUpEmoji {
  font-size: 72px;
  line-height: 1;
  margin-bottom: 12px;
  display: block;
}
.levelUpLabel {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 2px;
  color: var(--primary-text);
  text-transform: uppercase;
  margin-bottom: 6px;
}
.levelUpRank {
  font-size: 32px;
  font-weight: 800;
  color: var(--text);
  margin-bottom: 8px;
  letter-spacing: -.5px;
}
.levelUpSub {
  font-size: 14px;
  color: var(--muted);
}
@keyframes levelUpIn {
  from { opacity: 0; transform: scale(.8); }
  to   { opacity: 1; transform: scale(1); }
}
@keyframes levelUpOut {
  from { opacity: 1; transform: scale(1); }
  to   { opacity: 0; transform: scale(1.05); }
}
.levelUp-enter .levelUpCard { animation: levelUpIn 350ms cubic-bezier(.34,1.56,.64,1) forwards; }
.levelUp-exit  { animation: levelUpOut 400ms ease forwards; }

/* ── Goal setter popover ─────────────────────────── */
.heroGoalRingWrap { cursor: pointer; }
.heroGoalRingWrap:active { opacity: .85; }

.goalPopover {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 9990;
  background: rgba(0,0,0,.6);
  backdrop-filter: blur(4px);
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.goalPopoverInner {
  width: 100%;
  max-width: 320px;
  padding: 20px 20px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.goalPopoverTitle {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.3px;
}
.goalTypeSeg { display: flex; gap: 6px; }
.goalTypeSeg .segBtn { flex: 1; }
.goalPopoverHint {
  font-size: 12px;
  color: var(--muted);
  margin-top: -4px;
}
.goalPopoverActions {
  display: flex;
  gap: 8px;
  flex-direction: column;
}
.goalSaveBtn {
  min-height: 48px;
  font-size: 15px;
}

/* ── 8-week earnings chart ────────────────────────── */
.eightWkLabels {
  display: flex;
  justify-content: space-between;
  padding: 0 2px;
  margin-top: 4px;
}
.eightWkLabel {
  font-size: 10px;
  color: var(--muted);
  text-align: center;
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
}
.eightWkLabel--now {
  color: var(--primary-text);
  font-weight: 700;
}
.eightWkEmptyState {
  padding: 16px 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}

/* ── Comeback stats grid ─────────────────────────── */
.cbStatsGrid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 12px;
}
.cbStat {
  background: var(--surface2);
  border-radius: var(--r-md);
  padding: 10px 8px;
  text-align: center;
}
.cbStat--cost { background: rgba(239,68,68,.08); }
.cbStatVal {
  font-size: 18px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -.5px;
  line-height: 1.2;
}
.cbStat--cost .cbStatVal { color: #ef4444; }
.cbStatLbl {
  font-size: 10px;
  color: var(--muted);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cbAlert {
  font-size: 12px;
  color: #f59e0b;
  background: rgba(245,158,11,.08);
  border: 1px solid rgba(245,158,11,.2);
  border-radius: var(--r-md);
  padding: 8px 12px;
  line-height: 1.4;
}
.cbGood {
  font-size: 12px;
  color: var(--primary-text);
  background: color-mix(in srgb, var(--primary) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary) 18%, transparent);
  border-radius: var(--r-md);
  padding: 8px 12px;
}

[data-color-theme="classic"][data-theme="light"] .cbStat { background: var(--surface2); }
[data-color-theme="classic"][data-theme="light"] .cbStat--cost { background: rgba(239,68,68,.07); }
[data-color-theme="classic"][data-theme="light"] .shortPayAlert { background: rgba(245,158,11,.08); border-color: rgba(245,158,11,.35); }

/* ── What's New / Payday Summary modal sheet ────── */
.whatsNewSheet {
  background: var(--card-bg);
  border: 1px solid var(--stroke);
  box-shadow: var(--shadow-lg);
  border-radius: var(--r-lg);
  padding: 20px 20px 24px;
  max-width: 420px;
  width: calc(100% - 32px);
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-height: 90vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.whatsNewHeader { display:flex; align-items:center; gap:12px; }
.whatsNewTitle { font-size:18px; font-weight:800; color:var(--text1); line-height:1.1; }
.whatsNewVersion { font-size:12px; color:var(--primary); font-weight:700; margin-top:2px; }
.modalCloseBtn {
  margin-left: auto;
  background: var(--surface2);
  border: none;
  border-radius: 50%;
  width: 44px; height: 44px;
  font-size: 14px; color: var(--text2);
  cursor: pointer;
  display: flex; align-items:center; justify-content:center;
  flex-shrink: 0;
}
.whatsNewList { list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:10px; }
.whatsNewList li { display:flex; gap:10px; align-items:flex-start; font-size:14px; color:var(--text1); line-height:1.4; }
.whatsNewList li::before { content:"✓"; color:var(--primary); font-weight:800; flex-shrink:0; margin-top:1px; }

/* ── What's New: past-versions log (collapsed by default) ────── */
.whatsNewHistory { border-top: 1px solid var(--stroke); padding-top: 12px; }
.whatsNewHistory summary {
  cursor: pointer;
  list-style: none;
  user-select: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
}
.whatsNewHistory summary::-webkit-details-marker { display: none; }
.whatsNewHistory summary::after {
  content: "›";
  font-size: 16px;
  color: var(--muted2);
  transition: transform 200ms var(--ease);
  line-height: 1;
}
.whatsNewHistory[open] summary::after { transform: rotate(90deg); }
.whatsNewHistGroup { padding-top: 14px; }
.whatsNewHistGroup + .whatsNewHistGroup { margin-top: 14px; border-top: 1px dashed var(--stroke); padding-top: 14px; }
.whatsNewHistVer { font-size: 12px; font-weight: 800; color: var(--primary-text); margin-bottom: 8px; }
.whatsNewHistGroup .whatsNewList li { font-size: 13px; color: var(--muted); }

/* ── UX Polish fixes ────────────────────────────────────────── */

/* Fix #1: Save button — always looks actionable */
.fr26PrimaryAction { padding: 10px 14px 12px; }
.fr26BtnPrimary { box-shadow: 0 4px 20px color-mix(in srgb, var(--primary) 35%, transparent); }

/* Fix #5: Clear chip — danger tint so it's not confused with positive actions */
.fr26ChipDanger {
  color: var(--danger) !important;
  border-color: rgba(239,68,68,.4) !important;
}
.fr26ChipDanger:active { background: rgba(239,68,68,.10) !important; }

/* Fix #11: Stats header — make "Breakdown" smaller, just a label not a hero h1 */
#spa-stats header h1 { font-size: 18px; font-weight: 700; }

/* The Breakdown header was ~120px tall: a big safe-area inset (Dynamic Island)
   plus 12px of its own padding top AND bottom, wrapped around two stacked
   lines. The inset is non-negotiable, so claw the space back everywhere else —
   title and period sit on one baseline instead of two. */
#spa-stats header.top {
  padding-top: calc(2px + env(safe-area-inset-top, 0px));
  padding-bottom: 8px;
  display: flex;
  align-items: baseline;
  gap: 10px;
}
@media (display-mode: standalone) {
  #spa-stats header.top {
    padding-top: max(46px, calc(2px + env(safe-area-inset-top, 44px)));
  }
}
#spa-stats header.top > div { display: flex; align-items: baseline; gap: 10px; min-width: 0; }
#spa-stats header.top h1 { margin: 0; line-height: 1.15; }
#spa-stats .statsHeaderSub {
  margin-top: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Fix #15: Accordion rows in History — add descriptive sub-text color */
.moreSectionSummary { cursor: pointer; }
.moreSectionSummary::marker { color: var(--primary-text); }

/* Fix #16: Auth form — add a hint label so fields don't look broken */
.authHint {
  font-size: 12px;
  color: var(--muted);
  padding: 0 0 10px;
}

/* Fix #19: Standardize .btn border-radius to match fr26BtnPrimary */
.btn { border-radius: var(--r-md); }

/* Fix #20: Header title styling */
header h1 .headerIcon { font-size: 15px; margin-right: 4px; }

/* Light theme: stats chips fade */
[data-color-theme="classic"][data-theme="light"] .statsChipsOuter::after {
  background: linear-gradient(to right, transparent, var(--bg));
}

/* ── Recent Job Type Quick-Tap Chips ────────────────────────────── */
/* Outer has no height/padding of its own — it only exists to host the fade
   ::after without the fade scrolling away with the row. When the inner row
   is empty (.recentTypeChips:empty collapses to display:none) this wrapper
   has nothing left inside it and collapses to zero height too, so the fade
   naturally disappears with no :has() needed. */
.recentTypeChipsOuter {
  position: relative;
  /* Same latent grid-blowout risk as .fr26QuickHoursOuter above -- this is
     also a direct child of the .fr26Row grid, so without min-width:0 it'll
     inherit min-width:auto and refuse to shrink below its chips' full
     content width once someone racks up enough recent job types to
     overflow. Doesn't visibly misbehave with today's short chip set, but
     fixing it now rather than waiting for a bug report once it does. */
  min-width: 0;
}
.recentTypeChipsOuter::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 8px;
  width: 40px;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--surface));
}
.recentTypeChips {
  display: flex;
  gap: 8px;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  margin-bottom: 8px;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.recentTypeChips:empty { display: none; }
.recentTypeChips::-webkit-scrollbar { display: none; }

.recentTypeChip {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  min-height: 40px;
  gap: 1px;
  flex-shrink: 0;
  padding: 6px 11px;
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  border: 1.5px solid color-mix(in srgb, var(--primary) 28%, transparent);
  cursor: pointer;
  transition: background 120ms var(--ease), transform 100ms var(--ease-bounce), border-color 120ms;
  max-width: 140px;
}
.recentTypeChip:active { transform: scale(.91); background: color-mix(in srgb, var(--primary) 22%, transparent); }
.recentTypeChipName {
  font-size: 12px;
  font-weight: 700;
  color: var(--primary-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 120px;
}
.recentTypeChipHours {
  font-size: 10px;
  color: var(--muted);
  font-weight: 500;
}

@keyframes recentChipPop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}
.recentTypeChip.tapped { animation: recentChipPop 260ms cubic-bezier(.34,1.56,.64,1); }

/* ── Snappy Polish Pack ─────────────────────────────────────────── */

/* Validation shake */
@keyframes fieldShake {
  0%,100% { transform:translateX(0); }
  18%     { transform:translateX(-9px); }
  36%     { transform:translateX(8px); }
  54%     { transform:translateX(-6px); }
  72%     { transform:translateX(5px); }
  88%     { transform:translateX(-2px); }
}
.shake { animation:fieldShake 370ms ease !important; }
.invalid {
  border-color: var(--danger) !important;
  box-shadow: 0 0 0 3px rgba(239,68,68,.18) !important;
  transition: border-color 100ms, box-shadow 100ms !important;
}
.invalid-chip { box-shadow: 0 0 0 3px rgba(239,68,68,.2); }

/* Input focus glow — snappy pop on focus */
@keyframes inputFocusPop {
  from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 45%, transparent); }
  to   { box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 18%, transparent); }
}
input.fr26Input:focus, textarea.fr26Input:focus {
  animation: inputFocusPop 180ms ease forwards;
}

/* Entry list rows flow in on every render (save, refresh, filter change)
   instead of popping in all at once — each row's JS-set animation-delay
   staggers them slightly. */
@keyframes entryPopIn {
  0%   { opacity: 0; transform: translateY(8px) scale(.985); }
  100% { opacity: 1; transform: translateY(0)    scale(1); }
}
.entryPopIn { animation: entryPopIn 260ms cubic-bezier(.22,1,.36,1) backwards; }

/* Chip bounce on programmatic select */
@keyframes chipSelect {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.26); }
  100% { transform: scale(1); }
}
.fr26HourBtn.chipPop { animation: chipSelect 290ms cubic-bezier(.34,1.56,.64,1); }

/* Earnings preview pop when value changes */
@keyframes epPop {
  from { opacity:0.55; transform:scale(.93) translateY(5px); }
  to   { opacity:1;    transform:scale(1)   translateY(0); }
}
#earningsPreview.pop { animation: epPop 230ms cubic-bezier(.34,1.56,.64,1); }

/* Stronger save button press */
.fr26BtnPrimary:active {
  transform: scale(0.93) !important;
  transition-duration: 45ms !important;
}

/* Page fade-in on SPA tab switch */
@keyframes spaPageIn {
  from { opacity:0; transform:translateY(9px); }
  to   { opacity:1; transform:translateY(0); }
}
.spaPageIn { animation: spaPageIn 210ms cubic-bezier(.25,.46,.45,.94) both; }

/* Tab icon pulse on switch */
@keyframes tabIconPop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.32); }
  100% { transform: scale(1); }
}
.tabItem--active .tabIcon { animation: tabIconPop 280ms cubic-bezier(.34,1.56,.64,1); }

/* Save success: background flash green */
@keyframes saveBtnFlash {
  0%,100% { background: var(--primary); transform: scale(1); }
  35%     { background: var(--primary-dark); transform: scale(.96); }
  65%     { background: #4ade80; transform: scale(1.04); }
}
.fr26BtnPrimary.btn-success {
  animation: saveBtnFlash 380ms cubic-bezier(.34,1.56,.64,1) !important;
}

/* Earnings preview — rate sub-label */
.epRate { font-size: 11px; opacity: 0.65; font-weight: 500; margin-left: 2px; }

/* ── Chart card: dollar amounts above day labels + vs-last-week meta ── */
.heroChartLabel {
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  gap: 2px !important;
  cursor: pointer;
}
.heroChartLabelAmt {
  font-size: 8px;
  font-weight: 700;
  color: var(--primary-text);
  min-height: 10px;
  line-height: 1;
  white-space: nowrap;
}
.heroChartMeta {
  font-size: 10px;
  color: var(--muted);
  text-align: center;
  padding: 2px 4px 6px;
  opacity: 0.8;
  letter-spacing: .1px;
}

/* ── Motion Pack v1.5 ──────────────────────────────────────────────────────── */

/* Pay total lands with a reward glow (fires only when earnings grew) */
@keyframes heroAmtGlow {
  0%   { text-shadow: 0 0 0 rgba(255,255,255,0); }
  35%  { text-shadow: 0 0 22px rgba(255,255,255,.65), 0 0 6px rgba(96,158,255,.55); }
  100% { text-shadow: 0 0 0 rgba(255,255,255,0); }
}
.heroAmt.landed { animation: heroAmtGlow 680ms ease-out; }

/* Entry rows give a subtle press-in so taps feel physical */
.item { transition: transform 130ms cubic-bezier(.34,1.56,.64,1); }
.item:active { transform: scale(.985); }

/* Scan hero: bouncier spring on press (primary action deserves the best feel) */
.scanHeroBtn { transition: transform .14s cubic-bezier(.34,1.56,.64,1), box-shadow .14s var(--ease); }
.scanHeroBtn:active { transform: scale(.955); }

/* Tab bar: label lifts slightly on the active tab */
.tabItem { transition: transform 160ms cubic-bezier(.34,1.56,.64,1); }
.tabItem:active { transform: scale(.9); }

/* Generic pressable pill/chip springiness */
.recentTypeChip { transition: transform 140ms cubic-bezier(.34,1.56,.64,1); }
.recentTypeChip:active { transform: scale(.9); }

/* ── History search + row detail ───────────────────────────────────────────── */
.entrySearchCount { font-size: 11px; color: var(--muted); margin-top: 6px; letter-spacing: .02em; }
.bulkEntryVin {
  font-size: 11px; color: var(--muted); margin-top: 2px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .04em;
}
.bulkEntryNotes {
  font-size: 11px; color: var(--muted); margin-top: 3px; line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.bulkEntryPhoto {
  flex: 0 0 auto; align-self: center;
  width: 38px; height: 38px; margin-left: 8px;
  background: var(--surface2); border: 1px solid var(--stroke2, var(--stroke));
  border-radius: 10px; font-size: 17px; line-height: 1; cursor: pointer;
  touch-action: manipulation;
  transition: transform 130ms cubic-bezier(.34,1.56,.64,1), border-color 120ms;
}
.bulkEntryPhoto:active { transform: scale(.88); border-color: var(--primary); }

/* ── Requests to manager ───────────────────────────────────────────────────── */
.reqBadge {
  display: inline-block; min-width: 18px; padding: 1px 6px; margin-left: 6px;
  background: var(--primary); color: var(--on-primary); border-radius: 9px;
  font-size: 11px; font-weight: 700; text-align: center; vertical-align: middle;
}
.reqGate { padding: 6px 0; }
.reqGateText { font-size: 12.5px; color: var(--muted); line-height: 1.55; margin: 0 0 10px; }
.reqEmpty { font-size: 12px; color: var(--muted); line-height: 1.55; padding: 8px 0; }

.reqItem {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--stroke);
  border-radius: var(--r-lg, 12px); padding: 11px 12px; margin-bottom: 7px;
  transition: transform 130ms cubic-bezier(.34,1.56,.64,1), border-color 120ms;
}
.reqItem:active { transform: scale(.985); }
.reqItemTop { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.reqItemSubject { font-size: 13.5px; font-weight: 600; color: var(--text); }
.reqItemMeta { font-size: 11px; color: var(--muted); margin-top: 3px; }
.reqItemNote {
  font-size: 11.5px; color: var(--text2); margin-top: 6px;
  padding-top: 6px; border-top: 1px solid var(--stroke); line-height: 1.5;
}

.reqSt { font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 6px; white-space: nowrap; letter-spacing: .02em; }
.reqSt--open      { background: rgba(245,158,11,.16); color: #f59e0b; }
.reqSt--ack       { background: color-mix(in srgb, var(--primary) 15%, transparent);  color: var(--primary-text); }
.reqSt--done      { background: rgba(74,222,128,.16); color: #4ade80; }
.reqSt--declined  { background: rgba(239,68,68,.14);  color: var(--danger); }

.reqKinds { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 10px; margin-bottom: 14px; }
.reqKind {
  display: flex; align-items: center; gap: 7px; padding: 10px 11px;
  min-height: 44px;
  background: var(--surface2); border: 1px solid var(--stroke2, var(--stroke));
  border-radius: var(--r-lg, 12px); cursor: pointer; touch-action: manipulation;
  transition: transform 130ms cubic-bezier(.34,1.56,.64,1), background 120ms, border-color 120ms, color 120ms;
}
.reqKind:active { transform: scale(.94); }
.reqKind--on { background: color-mix(in srgb, var(--primary) 13%, transparent); border-color: color-mix(in srgb, var(--primary) 50%, transparent); }
.reqKindEmoji { font-size: 15px; }
.reqKindLabel { font-size: 12.5px; font-weight: 600; color: var(--text); }

.reqLabel { display: block; font-size: 11px; font-weight: 700; color: var(--muted); letter-spacing: .03em; margin: 10px 0 4px; }
.reqOpt { font-weight: 500; opacity: .7; }
.reqGrid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; }
.reqErr { font-size: 12px; color: var(--danger); margin-top: 10px; line-height: 1.5; }

.tcGroup {
  background: var(--surface); border: 1px solid var(--stroke);
  border-radius: var(--r-lg, 12px); padding: 12px; margin-top: 10px;
}
.tcGroupHead { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.tcGroupInto { font-size: 11.5px; font-weight: 700; color: var(--muted); white-space: nowrap; }
.tcCanonicalInput { flex: 1; padding: 8px 10px; font-size: 16px; font-weight: 700; } /* was 13.5px — iOS zoom-on-focus trigger */
.tcVariants { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.tcVariant { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text2); }
.tcVariant input[type="checkbox"] { width: 17px; height: 17px; accent-color: var(--primary); flex: 0 0 auto; }
.tcApplyBtn { width: 100%; font-size: 13px; padding: 9px; }

.reqDetailsHead { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin: 10px 0 4px; }
.reqDraftBtn {
  display: flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 700;
  color: var(--primary-text); background: color-mix(in srgb, var(--primary) 12%, transparent); border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
  border-radius: var(--r-pill); padding: 5px 10px; cursor: pointer; touch-action: manipulation;
  transition: transform 130ms cubic-bezier(.34,1.56,.64,1), opacity 120ms;
}
.reqDraftBtn:active { transform: scale(.94); }
.reqDraftBtn:disabled { opacity: .55; cursor: default; transform: none; }
.reqDraftErr { font-size: 11.5px; color: var(--danger); margin-top: 6px; line-height: 1.5; }

.reqMeta { margin-bottom: 12px; }
.reqMetaRow { font-size: 11.5px; color: var(--primary-text); font-weight: 600; margin-bottom: 5px; }
.reqMetaDetails { font-size: 12.5px; color: var(--text2); line-height: 1.55; white-space: pre-wrap; }
.reqMetaNote {
  font-size: 12px; color: var(--text2); line-height: 1.55; margin-top: 9px;
  background: var(--surface2); border-radius: var(--r-lg, 12px); padding: 9px 11px;
}
.reqMetaNote strong { color: var(--text); }

.reqMsgs { max-height: 34vh; overflow-y: auto; display: flex; flex-direction: column; gap: 7px; margin-bottom: 12px; }
.reqNoMsgs { font-size: 12px; color: var(--muted); padding: 6px 0; }
.reqMsg { max-width: 86%; padding: 8px 11px; border-radius: 13px; }
.reqMsg--mine { align-self: flex-end; background: color-mix(in srgb, var(--primary) 15%, transparent); }
.reqMsg--them { align-self: flex-start; background: var(--surface2); }
.reqMsgBody { font-size: 12.5px; color: var(--text); line-height: 1.5; white-space: pre-wrap; }
.reqMsgWho { font-size: 10px; color: var(--muted); margin-top: 3px; }

.reqReplyRow { display: flex; gap: 8px; align-items: center; }
.reqReplyRow .fr26Input { flex: 1; }

/* ── "Set your pay rate" banner ─────────────────────────────────────────────── */
.rateSetupBanner {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 12px 16px 0;
  padding: 14px 16px;
  border-radius: var(--r-lg, 12px);
  background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 16%, transparent), color-mix(in srgb, var(--primary) 7%, transparent));
  border: 1px solid color-mix(in srgb, var(--primary) 45%, transparent);
  animation: rateSetupIn 380ms cubic-bezier(.22,1,.36,1) both;
}
@keyframes rateSetupIn { from { opacity: 0; transform: translateY(-6px); } }
.rateSetupIcon  { font-size: 22px; line-height: 1; flex-shrink: 0; }
.rateSetupBody  { flex: 1; min-width: 0; }
.rateSetupTitle { font-size: 14px; font-weight: 700; color: var(--text); }
.rateSetupSub   { font-size: 12px; color: var(--muted); line-height: 1.45; margin-top: 2px; }
.rateSetupBtn {
  flex-shrink: 0;
  background: var(--primary);
  color: #fff;
  font-size: 13px;
  font-weight: 700;
  border: none;
  border-radius: var(--r-pill, 999px);
  padding: 10px 16px;
  min-height: 40px;
  cursor: pointer;
  touch-action: manipulation;
  transition: transform 120ms var(--ease, ease);
}
.rateSetupBtn:active { transform: scale(.94); }

/* Brief highlight when we send someone to a specific field */
.fieldPulse { animation: fieldPulse 1.5s ease-out; }
@keyframes fieldPulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 0%, transparent); }
  15%      { box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 45%, transparent); }
  60%      { box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 25%, transparent); }
}

/* ── Pay stub audit: missing-work review rows ───────────────────────────────── */
.reviewItem {
  border-radius: var(--r-lg, 12px);
  border: 1px solid var(--stroke);
  background: var(--surface2);
  padding: 14px;
  margin-bottom: 10px;
}
.reviewItemTop { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.reviewItemLeft { flex: 1; min-width: 0; }
.reviewItemHead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.reviewItemRef { font-size: 13.5px; font-weight: 700; color: var(--text); }
.reviewItemMeta { font-size: 12px; color: var(--muted); line-height: 1.5; }
.reviewItemNotes { font-size: 12px; color: var(--text2); font-style: italic; margin-top: 4px; line-height: 1.4; }
.reviewItemRight { text-align: right; flex-shrink: 0; }
.reviewItemPay { font-size: 19px; font-weight: 800; letter-spacing: -.5px; color: var(--accent); line-height: 1; }
.reviewItemHrs { font-size: 11px; color: var(--muted); margin-top: 4px; white-space: nowrap; }
.reviewItemActions { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }

/* ── Payroll reconciliation results ────────────────────────────────────────── */
.reconSummary { font-size: 12px; color: var(--muted); margin-bottom: 10px; line-height: 1.6; }
.reconLine strong { color: var(--text); }
.reconOk {
  font-size: 13px; font-weight: 600; color: #4ade80;
  background: rgba(74,222,128,.1); border: 1px solid rgba(74,222,128,.35);
  border-radius: var(--r-lg, 12px); padding: 12px 14px;
}
.reconHit {
  background: linear-gradient(135deg, rgba(239,68,68,.16), rgba(239,68,68,.05));
  border: 1px solid rgba(239,68,68,.45);
  border-radius: var(--r-lg, 12px); padding: 13px 14px; margin-bottom: 10px;
}
.reconHitVal   { font-size: 22px; font-weight: 800; color: var(--danger); letter-spacing: -.5px; }
.reconHitLabel { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.reconRow {
  display: flex; justify-content: space-between; gap: 10px; align-items: flex-start;
  padding: 10px 0; border-bottom: 1px solid var(--stroke);
}
.reconThumb {
  width: 54px; height: 54px; flex-shrink: 0;
  object-fit: cover; border-radius: var(--r-sm);
  border: 1px solid var(--stroke);
  background: var(--surface2);
  cursor: pointer;
}
.reconThumb:active { opacity: .7; }
.reconRowTop   { font-size: 13.5px; font-weight: 600; color: var(--text); }
.reconRowSub   { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.reconRowRight { text-align: right; flex-shrink: 0; }
.reconRowPay   { font-size: 14px; font-weight: 700; color: var(--accent); }
.reconRowHrs   { font-size: 11px; color: var(--muted); margin-top: 2px; }
.reconNote     { font-size: 11.5px; color: var(--muted); line-height: 1.55; margin-top: 9px; }
.reconWhy      { font-size: 11px; color: var(--muted); font-style: italic; margin-top: 4px; line-height: 1.45; }
.reconUncertain {
  margin-top: 12px; padding: 12px 14px;
  border-radius: var(--r-lg, 12px);
  background: rgba(245,158,11,.09);
  border: 1px solid rgba(245,158,11,.4);
}
.reconUncertainHead { font-size: 12.5px; font-weight: 700; color: #f59e0b; margin-bottom: 6px; }
.reconUncertainRow  { font-size: 12px; color: var(--text2); line-height: 1.5; padding: 3px 0; }

/* ── Missing-work candidate cards (shown to a service manager) ─────────────── */
.mwCard {
  background: var(--surface2);
  border: 1px solid var(--stroke);
  border-radius: var(--r-lg, 12px);
  padding: 13px 14px;
  margin-bottom: 9px;
}
.mwTop   { display: flex; justify-content: space-between; gap: 12px; }
.mwLeft  { flex: 1; min-width: 0; }
.mwRight { text-align: right; flex-shrink: 0; }
.mwType  { font-size: 14px; font-weight: 700; color: var(--text); line-height: 1.3; }
.mwRef   { font-size: 12.5px; font-weight: 600; color: var(--primary-text); margin-top: 3px; }
.mwMeta  { font-size: 11.5px; color: var(--muted); margin-top: 3px; }
.mwNotes { font-size: 12px; color: var(--text2); font-style: italic; margin-top: 5px; line-height: 1.4; }
.mwPay   { font-size: 17px; font-weight: 800; color: var(--accent); letter-spacing: -.4px; }
.mwHrs   { font-size: 11px; color: var(--muted); margin-top: 3px; white-space: nowrap; }
.mwActions {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center;
  margin-top: 11px; padding-top: 10px; border-top: 1px solid var(--stroke);
}
/* Generous targets — this gets tapped while holding the phone out to someone. */
.mwActions .iBtn { min-height: 46px; padding: 12px 16px; font-size: 13px; }
.mwActions .mwPhotoBtn { flex: 1 1 auto; justify-content: center; font-weight: 700; }
.mwCard--tappable { cursor: pointer; transition: transform 120ms var(--ease, ease), border-color 120ms; }
.mwCard--tappable:active { transform: scale(.985); border-color: color-mix(in srgb, var(--primary) 50%, transparent); }
.mwPhotoBtn { color: var(--primary-text); border-color: color-mix(in srgb, var(--primary) 40%, transparent); background: color-mix(in srgb, var(--primary) 9%, transparent); }
.mwNoPhoto  { font-size: 11.5px; color: var(--muted); font-style: italic; }

/* ── Efficiency card ────────────────────────────────────────────────────────── */
.effCard {
  margin: 0 16px 16px;
  padding: 15px 16px;
  border-radius: var(--r-lg, 12px);
  background: var(--surface2);
  border: 1px solid var(--stroke);
}
.effCard.eff--strong { border-color: rgba(74,222,128,.45); background: linear-gradient(135deg, rgba(74,222,128,.12), rgba(74,222,128,.04)); }
.effCard.eff--ok     { border-color: color-mix(in srgb, var(--primary) 40%, transparent);  background: linear-gradient(135deg, color-mix(in srgb, var(--primary) 12%, transparent), color-mix(in srgb, var(--primary) 4%, transparent)); }
.effCard.eff--low    { border-color: rgba(245,158,11,.4); background: linear-gradient(135deg, rgba(245,158,11,.12), rgba(245,158,11,.04)); }

.effTop { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.effPctWrap { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.effPct { font-size: 30px; font-weight: 800; letter-spacing: -1px; line-height: 1; }
.eff--strong .effPct { color: #4ade80; }
.eff--ok     .effPct { color: var(--primary-text); }
.eff--low    .effPct { color: #f59e0b; }
.effPctLabel {
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.effTrend { font-size: 11.5px; font-weight: 700; color: var(--muted); white-space: nowrap; }
.effTrend--up   { color: #4ade80; }
.effTrend--down { color: #f59e0b; }

.effBar {
  height: 6px; border-radius: var(--r-pill); margin: 11px 0 9px;
  background: var(--surface3, rgba(255,255,255,.08)); overflow: hidden;
}
.effBar > span {
  display: block; height: 100%; border-radius: var(--r-pill); width: 0;
  animation: effBarGrow 620ms cubic-bezier(.22,1,.36,1) forwards;
}
.eff--strong .effBar > span { background: #4ade80; }
.eff--ok     .effBar > span { background: var(--primary); }
.eff--low    .effBar > span { background: #f59e0b; }
@keyframes effBarGrow { from { width: 0; } }

.effMath    { font-size: 11.5px; color: var(--muted); }
.effVerdict { font-size: 12.5px; color: var(--text2); margin-top: 5px; font-weight: 600; }

.effWorstDays {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid color-mix(in srgb, var(--text) 10%, transparent);
}
.effWorstDaysTitle {
  font-size: 10px; color: var(--muted); text-transform: uppercase;
  letter-spacing: .04em; margin-bottom: 4px;
}
/* min-width: 0 + flex-shrink: 0 on the value side: same overflow failure mode
   as the Settings/Team-dashboard fixes earlier -- a longer day label must
   never be allowed to squeeze the value past the row. */
.effDayRow {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px; font-size: 12px; padding: 2px 0;
}
.effDayLabel { color: var(--text2); min-width: 0; }
.effDayVal   { color: var(--muted); font-weight: 600; flex-shrink: 0; }

@media (prefers-reduced-motion: reduce) { .effBar > span { animation: none; } }

/* ── Dispute win tracker — money clawed back ────────────────────────────────── */
.reqWins {
  border-radius: var(--r-lg, 12px);
  padding: 14px 16px;
  margin-bottom: 12px;
  background: linear-gradient(135deg, rgba(74,222,128,.16), rgba(74,222,128,.05));
  border: 1px solid rgba(74,222,128,.4);
  animation: reqWinsIn 420ms cubic-bezier(.22,1,.36,1) both;
}
@keyframes reqWinsIn { from { opacity: 0; transform: translateY(-5px); } }
.reqWins--quiet {
  background: var(--surface2);
  border-color: var(--stroke);
  padding: 11px 14px;
}
.reqWinsQuiet { font-size: 12px; color: var(--muted); }

.reqWinsTop { display: flex; align-items: center; gap: 11px; }
.reqWinsTrophy { font-size: 26px; line-height: 1; }
.reqWinsLabel {
  font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--muted);
}
.reqWinsValue {
  font-size: 25px; font-weight: 800; letter-spacing: -.7px;
  line-height: 1.1; color: #4ade80;
}
.reqWinsRow {
  display: flex; gap: 8px; margin-top: 12px;
  padding-top: 11px; border-top: 1px solid rgba(74,222,128,.22);
}
.reqWinsCell { flex: 1; text-align: center; min-width: 0; }
.reqWinsCellVal { font-size: 15px; font-weight: 700; color: var(--text); }
.reqWinsCellLabel {
  font-size: 10px; color: var(--muted); margin-top: 2px;
  text-transform: uppercase; letter-spacing: .04em;
}
.reqWinsFoot { font-size: 10.5px; color: var(--muted); margin-top: 9px; }

/* ── Pro lock (teaser shown to free users when billing is live) ────────────── */
.proLock {
  text-align: center; padding: 20px 14px;
  background: color-mix(in srgb, var(--primary) 6%, transparent);
  border: 1px dashed color-mix(in srgb, var(--primary) 35%, transparent);
  border-radius: var(--r-lg, 12px);
}
.proLockIcon { font-size: 24px; line-height: 1; margin-bottom: 8px; }
.proLockTitle { font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 5px; }
.proLockText {
  font-size: 12px; color: var(--muted); line-height: 1.55;
  max-width: 300px; margin: 0 auto 14px;
}
.proLockBtn { width: auto; padding: 10px 22px; font-size: 13.5px; }

/* ── Lost Time: capture modal ──────────────────────────────────────────────── */
.ltModal {
  position: fixed; inset: 0; z-index: 1000;
  background: rgba(0,0,0,.55);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0; animation: ltFadeIn 160ms ease both;
}
/* Had an entrance but no exit — close() just set display:none outright.
   .closing plays the reverse pair and openLostTimeModal()'s close() now
   delays the actual display:none until they finish. */
.ltModal.closing { animation: ltFadeOut 200ms ease forwards; }
.ltModal.closing .ltInner { animation: ltSlideDown 200ms cubic-bezier(.25,.46,.45,.94) forwards; }
@keyframes ltFadeIn { from { opacity:0; } to { opacity:1; } }
@keyframes ltFadeOut { from { opacity:1; } to { opacity:0; } }
@keyframes ltSlideDown { from { transform: translateY(0); } to { transform: translateY(28px); } }
.ltInner {
  width: 100%; max-width: 520px;
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
  margin: 0; max-height: 88vh; overflow-y: auto;
  animation: ltSlideUp 260ms cubic-bezier(.25,.46,.45,.94) both;
}
@keyframes ltSlideUp { from { transform: translateY(28px); } to { transform: translateY(0); } }

.ltHead { margin-bottom: 12px; }
.ltTitle { font-size: 17px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.ltSub { font-size: 12px; color: var(--muted); margin-top: 3px; }

.ltRemainRow {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: 8px 12px; margin-bottom: 12px;
  background: var(--surface2); border-radius: var(--r-lg, 12px);
}
.ltRemainLabel { font-size: 11px; color: var(--muted); font-weight: 600; letter-spacing: .03em; }
.ltRemainVal { font-size: 17px; font-weight: 700; color: #f59e0b; }
.ltRemainVal--done { color: var(--primary-text); }

.ltChips { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
.ltChip {
  display: inline-flex; align-items: center;
  min-height: 40px;
  padding: 8px 12px; font-size: 12.5px; font-weight: 600;
  background: var(--surface2); color: var(--text2);
  border: 1px solid var(--stroke2, var(--stroke));
  border-radius: var(--r-pill, 999px);
  cursor: pointer; touch-action: manipulation;
  transition: transform 130ms cubic-bezier(.34,1.56,.64,1), background 120ms, color 120ms, border-color 120ms;
}
.ltChip:active { transform: scale(.9); }
.ltChip--on {
  background: color-mix(in srgb, var(--primary) 14%, transparent); color: var(--primary-text);
  border-color: color-mix(in srgb, var(--primary) 50%, transparent);
}

.ltRows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.ltRow {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 12px;
  background: var(--surface); border: 1px solid var(--stroke);
  border-radius: var(--r-lg, 12px);
}
.ltRowName { font-size: 13px; font-weight: 600; color: var(--text); }
.ltStepper { display: inline-flex; align-items: center; gap: 10px; }
.ltRowHrs {
  font-size: 13px; font-weight: 700; color: var(--primary-text);
  min-width: 42px; text-align: center;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
}
.ltStep {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--surface2); border: 1px solid var(--stroke2, var(--stroke));
  color: var(--text); font-size: 16px; font-weight: 700; line-height: 1;
  cursor: pointer; touch-action: manipulation;
  transition: transform 120ms cubic-bezier(.34,1.56,.64,1);
}
.ltStep:active { transform: scale(.86); }

.ltHint { font-size: 11px; color: var(--muted); line-height: 1.5; margin-bottom: 14px; }
.ltActions { display: flex; gap: 10px; align-items: center; }
.ltBtnGhost {
  flex: 0 0 auto; padding: 12px 18px;
  background: transparent; border: 1px solid var(--stroke2, var(--stroke));
  border-radius: var(--r-lg, 12px);
  color: var(--muted); font-size: 14px; font-weight: 600;
  cursor: pointer; touch-action: manipulation;
}
.ltBtnSave { flex: 1; }

/* ── Lost Time: breakdown card ─────────────────────────────────────────────── */
.ltHeadline { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.ltHeadlineNum {
  font-size: 32px; font-weight: 700; color: #f59e0b;
  line-height: 1; letter-spacing: -1px;
}
.ltHeadlineUnit { font-size: 17px; font-weight: 600; margin-left: 1px; }
.ltHeadlineMeta { font-size: 11.5px; color: var(--muted); line-height: 1.5; }
.ltHeadlineMeta strong { color: var(--text); font-weight: 700; }

.ltCallout {
  font-size: 12px; color: var(--text2); line-height: 1.5;
  background: rgba(245,158,11,.10);
  border: 1px solid rgba(245,158,11,.24);
  border-radius: var(--r-lg, 12px);
  padding: 8px 11px; margin-bottom: 12px;
}
.ltCallout strong { color: #f59e0b; font-weight: 700; }

.ltBars { display: flex; flex-direction: column; gap: 9px; }
.ltBarTop { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.ltBarName { font-size: 12.5px; font-weight: 600; color: var(--text); }
.ltBarVal { font-size: 11px; color: var(--muted); font-weight: 600; white-space: nowrap; }
.ltBarTrack { height: 6px; background: var(--surface2); border-radius: 3px; overflow: hidden; }
.ltBarFill {
  height: 100%; background: #f59e0b; border-radius: 3px;
  transition: width 500ms cubic-bezier(.25,.46,.45,.94);
}

.ltFootnote { font-size: 10.5px; color: var(--muted); line-height: 1.5; margin-top: 12px; opacity: .85; }
.ltEmpty { padding: 14px 0; }
.ltEmptyTitle { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.ltEmptySub { font-size: 11.5px; color: var(--muted); line-height: 1.55; }

/* ── Emp # collapsed chip ──────────────────────────────────────────────────── */
.fr26EmployeeBar--collapsed .fr26EmployeeLabel,
.fr26EmployeeBar--collapsed #empId { display: none; }
.fr26EmployeeBar--collapsed {
  /* The inner chip (.empChip below) was already styled as a quiet, muted
     label — but this bar's own background/border/radius (solid surface2,
     rounded top corners) kept rendering as a full-width card behind it, so
     it still read as a prominent stripe between the scan card and the form
     instead of the small aside it's meant to be. */
  justify-content: flex-end;
  padding: 6px 4px;
  background: transparent;
  border-bottom: none;
  border-radius: 0;
}
.empChip {
  position: relative;
  background: transparent; border: none;
  color: var(--muted); font-size: 11px; font-weight: 600;
  letter-spacing: .02em; padding: 2px 4px; cursor: pointer;
  touch-action: manipulation; opacity: .75;
  transition: opacity 120ms, transform 130ms cubic-bezier(.34,1.56,.64,1);
}
.empChip:active { transform: scale(.92); opacity: 1; }
/* Deliberately a tiny label chip — grows the real tap zone invisibly
   instead of the visible text, which is meant to stay unobtrusive. */
.empChip::before {
  content: "";
  position: absolute;
  inset: -14px;
}

/* ── Hero "More stats" disclosure ──────────────────────────────────────────── */
.heroDetailsToggle {
  display: inline-flex; align-items: center; gap: 4px;
  margin-top: 6px; padding: 3px 9px 3px 10px;
  background: transparent;
  border: 1px solid var(--stroke2, var(--stroke));
  border-radius: var(--r-pill, 999px);
  color: var(--muted);
  font-size: 10.5px; font-weight: 600; letter-spacing: .02em;
  cursor: pointer; touch-action: manipulation;
  transition: color 120ms, border-color 120ms, transform 130ms cubic-bezier(.34,1.56,.64,1);
}
.heroDetailsToggle:active { transform: scale(.93); }
.heroDetailsToggle--open { color: var(--primary-text); border-color: color-mix(in srgb, var(--primary) 45%, transparent); }
.heroDetailsChev {
  font-size: 12px; line-height: 1;
  transition: transform 200ms cubic-bezier(.4,0,.2,1);
}
.heroDetailsToggle--open .heroDetailsChev { transform: rotate(180deg); }

.heroDetails { animation: heroDetailsIn 220ms cubic-bezier(.25,.46,.45,.94) both; }
.heroDetails[hidden] { display: none; }
@keyframes heroDetailsIn {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ── Job Scorecard ─────────────────────────────────────────────────────────── */
.jobScorecard { margin: 4px 0 18px; }
.jobScorecard:empty { display: none; }

.jsHeader {
  display: flex; align-items: baseline; gap: 8px;
  padding: 0 2px 8px;
}
.jsTitle { font-size: 14px; font-weight: 700; color: var(--text); letter-spacing: -.01em; }
.jsSubtitle { font-size: 11px; color: var(--muted); }

.jsHeadline {
  font-size: 12px; line-height: 1.5; color: var(--text2);
  background: color-mix(in srgb, var(--primary) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--primary) 22%, transparent);
  border-radius: var(--r-lg, 12px);
  padding: 9px 12px; margin-bottom: 10px;
}
.jsHeadline strong { color: var(--primary-text); font-weight: 700; }
.jsHeadline--muted {
  background: var(--surface2); border-color: var(--stroke);
  color: var(--muted);
}

.jsRows { display: flex; flex-direction: column; gap: 6px; }

.jsRow {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--stroke);
  border-radius: var(--r-lg, 12px);
  padding: 10px 12px;
  transition: transform 130ms cubic-bezier(.34,1.56,.64,1);
}
.jsRow:active { transform: scale(.985); }
.jsRow--best { border-color: color-mix(in srgb, var(--primary) 45%, transparent); background: color-mix(in srgb, var(--primary) 7%, transparent); }

.jsRowMain { flex: 1; min-width: 0; }
.jsRowName {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 13.5px; font-weight: 600; color: var(--text);
}
.jsRowSub { font-size: 11px; color: var(--muted); margin-top: 3px; }

.jsRowRate { text-align: right; flex-shrink: 0; }
.jsRateVal { font-size: 15px; font-weight: 700; color: var(--primary-text); line-height: 1.1; }
.jsRateLbl { font-size: 9.5px; color: var(--muted); letter-spacing: .03em; margin-top: 1px; }

.jsBadge {
  font-size: 9px; font-weight: 700; letter-spacing: .04em;
  border-radius: 5px; padding: 2px 5px; white-space: nowrap;
}
.jsBadge--best { background: color-mix(in srgb, var(--primary) 18%, transparent); color: var(--primary-text); }
.jsBadge--warn { background: rgba(245,158,11,.16); color: #f59e0b; }
.jsBadge--thin { background: var(--surface3, var(--surface2)); color: var(--muted); font-weight: 600; }

/* ── Accessibility: honor reduced-motion ──────────────────────────────────────
 * Users who enable "Reduce Motion" in iOS get near-instant, motion-free UI.
 * Haptics still fire — only visual animation is neutralized. */
@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;
  }
  .cfp { display: none !important; }
}
