/* ============ VVT Hub design system ============ */
/* RESTYLE 2026-07-11 — self-hosted brand faces (assets/ already present; $0, offline, no CDN),
   metric-matched fallbacks so the swap is ~zero-CLS. Activates --font (Inter) + --font-serif
   (Fraunces display) so the Hub speaks the marketing site's editorial voice, not system default. */
@font-face { font-family:'Fraunces'; src:url('assets/fraunces-var.woff2') format('woff2'); font-weight:100 900; font-style:normal; font-display:swap; }
@font-face { font-family:'Inter'; src:url('assets/inter-var.woff2') format('woff2'); font-weight:100 900; font-style:normal; font-display:swap; unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'Inter-fallback'; src:local('Arial'); size-adjust:107%; ascent-override:90%; descent-override:22%; line-gap-override:0%; }
@font-face { font-family:'Fraunces-fallback'; src:local('Georgia'); size-adjust:104%; ascent-override:92%; descent-override:24%; line-gap-override:0%; }
/* SPEC-DHSHELL D1 — Plus Jakarta Sans, staged by Fable (assets/plus-jakarta-var.woff2).
   DECLARATION ONLY this wave: it is the client-facing dark-cinematic display face
   (design-handoff/README.md); no internal-Hub selector switches to it here — that's a
   later wave once the client-facing surfaces (out of scope for DHSHELL) get redesigned. */
@font-face { font-family:'Plus Jakarta Sans'; src:url('assets/plus-jakarta-var.woff2') format('woff2'); font-weight:200 800; font-style:normal; font-display:swap; }

/* SPEC267 quick-wins — a11y utilities: screen-reader-only text + keyboard skip link */
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
.skip-link { position:fixed; top:8px; left:8px; z-index:200; transform:translateY(-150%); background:var(--panel); color:var(--text); border:1px solid var(--border-strong); border-radius:8px; padding:10px 16px; font-weight: var(--fw-semibold); box-shadow:0 6px 20px rgba(0,0,0,.18); transition:transform .15s ease; }
.skip-link:focus { transform:translateY(0); outline:2px solid var(--accent); outline-offset:2px; }
.sched-drive { font-size:10.5px; padding:1px 0 1px 8px; display:flex; align-items:center; gap:3px; opacity:.8; }
:root {
  --bg: #f6f6f4;        /* SPEC175 — a whisper-warmer page neutral so cards read as paper, not cold screen */
  --bg-2: #eef0f4;
  --panel: #ffffff;
  --card: var(--panel);  /* SPEC198 — alias used by stat cards/chips/dividers; was undefined → transparent */
  --text-soft: var(--muted);  /* SPEC198 — secondary-label ink alias (was undefined) */
  --subtle: #fbfbfa;    /* SPEC175 — warmer soft fill (table headers, input wells) */
  --hover: #f2f4f7;
  --chip-ink: #0b0d13;
  --border: #e4e7ec;
  --border-strong: #d0d5dd;
  --text: #101828;
  --muted: #565e6b;   /* AA on all three light surfaces: 6.54 on --panel, 6.05 on --bg, 5.74 on --bg-2 */
  --faint: #656d7b;   /* AA on all three light surfaces: 5.22 on --panel, 4.82 on --bg, 4.57 on --bg-2; stays a visible step lighter than --muted */
  --accent: #2b608f;          /* RESTYLE 2026-07-11 — Deep Harbor steel-blue (from the VVT logo); was the AI-tell indigo #4f46e5 */
  --accent-hover: #1e3f66;    /* deeper cube navy — also the primary-button fill */
  --accent-soft: #eaf1f7;     /* soft steel wash (matches order.html exactly) */
  --accent-warm: #b18f52;     /* champagne — DECORATIVE ONLY (hairlines/dividers/delight edges), never text-on-fill */
  --accent-warm-soft: #f0e6d4;
  --gold: #c19a4e;            /* review stars / celebratory ticks ONLY */
  --danger: #d92d20;
  --danger-soft: #fef3f2;
  --danger-ink: #b42318;
  /* RESILIENCE 07-30 — ink that sits ON a --danger FILL (toasts, unread badges).
     Light: white is fine (4.83:1). Dark: --danger flips to a LIGHT red (#f97066) but the
     text stayed #fff, measuring 2.79:1 — a FAIL on the error messages that matter most.
     Dark uses a near-black instead (6.12:1). --danger-ink stays text-on-surface; this is
     text-on-fill, which is the opposite problem. */
  --on-danger: #ffffff;   /* text-only red ink — AA on light (6.05 on --danger-soft, 6.57 on --panel). --danger stays the fill/stroke red. */
  --ok: #079455;
  --ok-soft: #ecfdf3;
  --warn: #dc6803;
  --warn-soft: #fffaeb;
  --ok-ink: #067647;    /* text-only green ink — AA on light (5.69 on --panel, 5.50 on --subtle, 5.40 on --ok-soft). --ok stays the fill/stroke green. */
  --warn-ink: #b54708;  /* text-only amber ink — AA on light (5.43 on --panel, 5.24 on --subtle, 5.20 on --warn-soft). --warn stays the fill/stroke amber. */
  /* LAW-A 2026-08-15 (#19) — --info was the only state colour missing its text-ink sibling
     (--ok-ink/--warn-ink/--danger-ink already existed). Measured, WCAG relative-luminance:
     5.99 on --panel, 5.53 on --bg, 5.78 on --subtle, 5.57 on --info-soft, 5.25 on --bg-2 — all AA.
     --info stays the fill/stroke blue; use --info-ink for text on a light/neutral surface. */
  --info-ink: #175cd3;
  --info: #2e90fa;       /* SPEC208 — a distinct blue for "healthy/neutral" status (was hardcoded #0ea5e9/#3b82f6) */
  --info-soft: #eff8ff;
  --code-bg: #f6f8fa;    /* SPEC208 — tokenized code blocks (were hardcoded dark, invisible in light mode) */
  --code-text: #1a1a1a;
  --radius: 10px;
  --radius-sm: 7px;
  --radius-lg: 14px;
  /* accent channel → theme-aware focus rings/glows (one RGB triple drives every ring) */
  --ring-rgb: 43, 96, 143;
  --focus-ring: 0 0 0 3px rgba(var(--ring-rgb), .35);
  --focus-ring-soft: 0 0 0 3px rgba(var(--ring-rgb), .15);
  /* motion — one ease + a fast/base/slow scale by element heft */
  --ease: cubic-bezier(.4, 0, .2, 1);
  --ease-spring: cubic-bezier(.22, 1, .36, 1);
  --t-fast: 120ms;
  --t-base: 160ms;
  --t-slow: 260ms;
  /* elevation — SPEC175: a soft wide ambient layer under the contact shadow so every
     surface reads as floating paper, not flat. Real depth still in --shadow-lg. */
  --shadow-sm: 0 1px 2px rgba(16,24,40,.04), 0 1px 1px rgba(16,24,40,.03);
  --shadow: 0 1px 2px rgba(16,24,40,.05), 0 2px 6px -1px rgba(16,24,40,.08);
  --shadow-lg: 0 18px 40px -12px rgba(16,24,40,.18), 0 6px 14px -6px rgba(16,24,40,.08);
  --card-hi: inset 0 1px 0 rgba(255,255,255,.5);   /* paper top-highlight */
  --sidebar-hover: rgba(255,255,255,.06);          /* SPEC175 — stepped below the accent-tinted active */
  --sidebar-active-bg: color-mix(in srgb, var(--accent) 16%, transparent);   /* SPEC175 — current location is unmistakable yet calm */
  --font-serif: 'Fraunces', 'Fraunces-fallback', Georgia, Cambria, "Times New Roman", serif;   /* RESTYLE — editorial display face (self-hosted, $0); marketing-site parity */
  --sidebar-bg: #1e3f66;   /* SPEC-DHSHELL D2 — Deep Harbor rail navy (design-handoff/README.md "Rail background"; was #16202e). Print re-pin at :9449 kept in step (r4/A5). */
  --sidebar-border: #232639;
  /* SPEC-DHSHELL D2 — #9aa1b9 measured 6.38:1 against the OLD near-black #16202e rail but
     only 4.17:1 (a real AA FAIL, <4.5) against the new lighter #1e3f66 navy above; #aab1c6
     restores 5.01:1 against #1e3f66 and stays the same muted slate-blue family. Dark
     theme's rail bg is untouched (still #0f1722, 7.01:1 with the original #9aa1b9 — see
     :189), so only this light value moves; print re-pin at :9580 kept in step (r4/A5). */
  --sidebar-text: #aab1c6;
  --sidebar-active: #ffffff;
  /* SPEC267 — Inter first (self-host the woff2 in assets/ to activate; see GO-LIVE.md),
     gracefully degrading to the system stack until then so this is render-safe today. */
  --font: 'Inter var', 'Inter', 'Inter-fallback', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* ---- SPEC119: dimensional scales (theme-independent → 100% dark-mode-safe).
     4px spacing grid + ~7-role type scale + line-height/weight/tracking, the
     shared vocabulary Linear/Stripe/Notion/Vercel/M3 all converge on. ---- */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-5: 24px; --space-6: 32px; --space-7: 48px;
  --fs-micro: 11px; --fs-meta: 12px; --fs-caption: 13px; --fs-body: 14px; --fs-md: 16px; --fs-lg: 18px; --fs-h1: 22px; --fs-display: 28px; --fs-hero: 34px;
  /* SPEC-DHSHELL D4 — README's page-title spec is 30-34px; --fs-display (28px, used by
     .tss-hero-title/an icon badge too) sits just under that range, and widening it would
     ripple into those unrelated components. Dedicated token, only .view-title uses it. */
  --fs-page-title: 32px;
  --lh-tight: 1.2; --lh-snug: 1.35; --lh-normal: 1.5;
  --fw-medium: 500; --fw-semibold: 600; --fw-bold: 700; --fw-black: 800; --fw-heavy: 900;
  --ls-tight: -.02em; --ls-body: 0; --ls-label: .04em;
  --icon-sm: 16px; --icon-md: 24px; --icon-lg: 32px;
  --focus-offset: 2px;
  --shadow-xs: 0 0 0 1px var(--border);
  --shadow-pop: var(--shadow-lg);
  --ghost-border: transparent;   /* D2 */
  /* SPEC-DHSHELL D1 — token layer completion (design-handoff/README.md, Design tokens).
     Purely additive: nothing existing is renamed or removed, so no call site changes.
     Hairline/control borders (#e4e7ec/#d0d5dd) are already --border/--border-strong —
     nothing to add there. Semantic tone TRIPLES: bg/fg alias the existing --danger-soft/
     --danger-ink (etc, values already match the README exactly) so they inherit the
     dark-theme swap for free; only the border leg + the neutral bg are genuinely new
     literals, so only those two get dark-block + print-block entries (r4/A4/A5). */
  --tone-red-bg: var(--danger-soft); --tone-red-bd: rgba(217,45,32,.24); --tone-red-fg: var(--danger-ink);
  --tone-amber-bg: var(--warn-soft); --tone-amber-bd: rgba(220,104,3,.28); --tone-amber-fg: var(--warn-ink);
  --tone-green-bg: var(--ok-soft); --tone-green-bd: rgba(7,148,85,.26); --tone-green-fg: var(--ok-ink);
  --tone-neutral-bg: #f2f4f7; --tone-neutral-bd: var(--border); --tone-neutral-fg: var(--muted);
  /* radius scale — additive; existing --radius/--radius-sm/--radius-lg untouched */
  --radius-1: 5px; --radius-2: 8px; --radius-3: 10px; --radius-4: 12px; --radius-pill: 999px;
  /* named shadow roles per README (card/popover/modal/dark-modal); existing --shadow-sm/
     --shadow/--shadow-lg untouched, still used everywhere they already were */
  --shadow-card: 0 1px 2px rgba(16,24,40,.04);
  --shadow-popover: 0 20px 44px -10px rgba(16,24,40,.28);
  --shadow-modal: 0 24px 60px -12px rgba(16,24,40,.35);
  --shadow-dark-modal: 0 30px 70px -16px rgba(0,0,0,.75);   /* theme-invariant by design (already max-black) */
  --t-motion: 180ms;   /* the ~180ms/ease motion token named in the brief; existing --t-fast/--t-base/--t-slow untouched */
  /* pipeline stage-ramp DEFAULTS ONLY — CSS vars, no call site yet. Real stage colors
     live in settings-stored statuses (App.state.settings.statuses); do NOT wire these
     to replace that — they exist so a future stage-band component has a documented
     fallback ramp to reach for, per the brief's explicit instruction. */
  --stage-1: #b3d7e8; --stage-2: #8cc4dd; --stage-3: #67b3d5; --stage-4: #4492b6;
  --stage-5: #387e9f; --stage-6: #2b608f; --stage-7: #233e6b;
  /* SPEC-DHCLIENT D1 — client-facing dark-cinematic token group (design-handoff/README.md
     "Client-facing — dark cinematic"). Dedicated --cf- namespace, NOT an alias of the
     internal --bg/--panel/--text tokens above: those stay Deep Harbor light/navy-dark for
     the admin app's own light/dark toggle. The three client surfaces (portal.html,
     clientstatus.js's render + in-app preview, property.html) read these — scoped at each
     surface's own root container — so the look is a fixed luxury-dark brand identity,
     independent of theme state. Values verbatim from the README table. */
  --cf-ground: #0d0d0d;
  --cf-card: #1e1e21;
  --cf-hairline: rgba(255,255,255,.10);
  --cf-hairline-hover: rgba(255,255,255,.24);
  --cf-text: #ffffff;
  --cf-text-2: rgba(255,255,255,.68);
  --cf-text-3: rgba(255,255,255,.5);
  --cf-text-4: rgba(255,255,255,.34);
  --cf-champagne: #e6d2b1;
  --cf-blue: #67b3d5;
  --cf-blue-hover: #8fc9e0;
  --cf-green: #5fbf9b;
  --cf-green-bg: rgba(18,128,92,.16);
  --cf-scrim: linear-gradient(to top, rgba(13,13,13,.78), rgba(13,13,13,0) 42%);
  --cf-font: 'Plus Jakarta Sans', 'Inter var', 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  color-scheme: light;
}

/* ============ Dark mode — set html[data-theme="dark"]; everything reflows off
   these tokens. Surfaces step bg → bg-2 → panel → subtle → hover so depth still
   reads on a dark screen; accent is Deep Harbor steel (#5f92b4) — AA both as a button fill
   (white text) and as link text on the dark background. ============ */
html[data-theme="dark"] {
  --bg: #101722;         /* RESTYLE — anchor the dark ground to logo navy (matches order.html) */
  --bg-2: #16202e;       /* logo blue-black */
  --panel: #1f2433;       /* D1 — was #1a1d27; lifts card:page contrast 1.07:1 → 1.16:1 */
  --card: var(--panel);  /* SPEC198 — re-aliased per theme so dark cards aren't transparent */
  --text-soft: var(--muted);  /* SPEC198 */
  --subtle: #252b3c;      /* D1 — was #20242f */
  --hover: #2c3346;       /* D1 — was #272c39 */
  --chip-ink: #ffffff;
  --border: #333b4e;      /* D1 — was #2a2f3c */
  --border-strong: #47516a;  /* D1 — was #3a4151 */
  --text: #e7e9ee;
  --muted: #9aa1b3;
  --faint: #939bad;   /* 6.03 on --panel, 6.45 on --bg, 5.88 on --bg-2; still dimmer than dark --muted #9aa1b3, so the hierarchy holds */
  --accent: #5f92b4;         /* RESTYLE — lighter steel, AA on dark for links/icons (was indigo #6366f1) */
  --accent-hover: #7fadc9;
  --accent-soft: #182634;    /* navy-black wash (matches order.html) */
  --accent-warm: #c2a367;    /* champagne, nudged lighter for AA on dark; DECORATIVE only */
  --accent-warm-soft: #2a2318;
  --gold: #c9a659;
  --danger: #f97066;
  --danger-soft: #2b1a1a;
  --danger-ink: var(--danger);
  --on-danger: #2a0a08;
  --ok: #3ccb7f;
  --ok-soft: #13251d;
  --warn: #f5a524;
  --warn-soft: #2a2012;
  --ok-ink: var(--ok);
  --warn-ink: var(--warn);
  /* LAW-A 2026-08-15 (#19) — dark-mode sibling. Aliasing --info (matches how --ok-ink/
     --warn-ink alias their fill colour on dark). Measured: 6.68 on --panel, 7.77 on --bg,
     6.09 on --subtle — all AA. */
  --info-ink: var(--info);
  --info: #53b1fd;       /* SPEC208 */
  --info-soft: #102a43;
  --code-bg: #14161f;    /* SPEC208 */
  --code-text: #e7eaf2;
  --ring-rgb: 95, 146, 180;
  --shadow-sm: 0 1px 2px rgba(0,0,0,.4);
  --shadow: 0 1px 2px rgba(0,0,0,.45), 0 1px 3px rgba(0,0,0,.5);
  --shadow-lg: 0 18px 40px -12px rgba(0,0,0,.6), 0 6px 14px -6px rgba(0,0,0,.45);   /* SPEC175 — wider, softer ambient */
  --card-hi: inset 0 1px 0 rgba(255,255,255,.07);   /* D1 — was .04 */
  --sidebar-bg: #0f1722;   /* RESTYLE — navy-black, not neutral-black */
  --sidebar-border: #1c2030;
  --sidebar-text: #9aa1b9;
  --sidebar-active: #ffffff;
  --sidebar-active-bg: color-mix(in srgb, var(--accent) 22%, transparent);   /* SPEC175 — deeper accent tint on the dark rail */
  --ghost-border: #3a4254;   /* D2 — ghost buttons need a visible edge on dark */
  /* SPEC-DHSHELL D1 dark counterparts — only the tokens that are genuine NEW literals in
     :root (the tone borders + neutral bg + the three named shadows) need a dark entry;
     the tone bg/fg legs alias existing tokens (--danger-soft etc) that already have their
     own dark values above, so they need no redeclaration here. Kept in step with the
     @media print re-pin below per r4/A4/A5. */
  --tone-red-bd: rgba(249,112,102,.32); --tone-amber-bd: rgba(245,165,36,.32); --tone-green-bd: rgba(60,203,127,.32);
  --tone-neutral-bg: var(--subtle);
  --shadow-card: 0 1px 2px rgba(0,0,0,.4);
  --shadow-popover: 0 20px 44px -10px rgba(0,0,0,.55);
  --shadow-modal: 0 24px 60px -12px rgba(0,0,0,.6);
  color-scheme: dark;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
body { font-family: var(--font); background: var(--bg); color: var(--text); font-size: var(--fs-body); line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility;
  font-variant-numeric: tabular-nums; letter-spacing: var(--ls-body); }
/* running prose keeps proportional figures + natural spacing */
.markdown-body, .view-sub, .chat-text, p { font-variant-numeric: normal; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* RESTYLE — editorial serif display at the TOP of the hierarchy only (restraint): page
   titles. Fraunces at a light weight + negative tracking against the Inter body is the
   single strongest "a person designed this" signal, and unifies the Hub with the
   marketing site.
   DESIGNA 2026-08-19 (RULING — Vincent: "aligned look") — the big money/KPI FIGURES moved
   OFF Fraunces and back to Inter: fraunces-var.woff2 ships NO `tnum` GSUB feature, so the
   tabular-nums declared below silently never worked ($1,111 vs $8,000 measured 23.4px
   apart in a stacked KPI column). Inter's tnum is real. Serif stays on titles/headings
   only — NEVER on numerals; do not route a numeric class back to var(--font-serif). */
.view-title { font-family: var(--font-serif); font-weight: 400; letter-spacing: var(--ls-tight); }
.kpi-value, .stat-n, .cstatus-impact-n, .fin-n, .metric-big, .scoreboard-n { font-family: var(--font); font-weight: var(--fw-medium); letter-spacing: var(--ls-tight); font-variant-numeric: tabular-nums; }
::placeholder { color: var(--faint); }
::selection { background: var(--accent-soft); color: var(--text); }
/* keyboard-only focus ring (mouse clicks stay clean); never strip outline without this */
:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: var(--radius-sm); transition: box-shadow var(--t-fast) var(--ease); }
/* refined scrollbars on the main scroll area */
#main { scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
#main::-webkit-scrollbar { width: 10px; height: 10px; }
#main::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
#main::-webkit-scrollbar-thumb:hover { background: var(--faint); background-clip: padding-box; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* slick pick — 120ms cross-fade on view switches (App.refresh / App._canViewTransition
   in core.js). Uses the existing --t-fast motion token so it matches every other
   quick transition in the app. The pseudo-element tree the View Transitions API
   creates lives outside the normal DOM, so the *,*::before,*::after reduced-motion
   net above doesn't reach it — hence the explicit rule below. */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--t-fast); }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
}

/* ============ layout ============ */
#app { display: flex; height: 100vh; height: 100dvh; }   /* SAFPOLISH 2026-08-25 D3 — dvh fallback, same two-line pattern as .chat-layout (:2603) */
/* SPEC-DHSHELL D3 — #content-col groups the new #topheader with #main so the header
   sits above the content and to the right of the rail (see index.html comment). #main
   keeps its own flex:1/overflow-y:auto below, unchanged. */
#content-col { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* SPEC-V2POLISH1 D1 — re-measured against the prototype header (VVT-Hub-Owner.dc.html
   ~line 44: "height:54px;...gap:14px;padding:0 24px"). Height already matched (54px);
   gap 10→14 and padding 18→24 were the two hand-typed one-offs. */
#topheader {
  height: 54px; flex: 0 0 54px; display: flex; align-items: center; gap: 14px; padding: 0 24px;
  background: var(--panel); border-bottom: 1px solid var(--border); z-index: 2;
}
/* SPEC-DHSHELL D3 — the four reparented controls (search/sync/bell/role-indicator) keep
   their existing classes/ids (see core.js buildSidebarSearch/buildNotifBell/
   buildViewAsSwitcher); they were tuned for the dark rail (rgba(255,255,255,..)
   overlays), so they need a light-surface reskin now that they sit on --panel. */
#topheader .sidebar-search {
  /* SPEC-V2POLISH1 D1 — prototype's search field is a fixed 34px tall with 0 12px
     padding (min-width 260px, ours stays 320px — a real, wider search input, not a
     mockup placeholder, so the width difference is a deliberate product difference,
     not a fidelity miss). 7px 10px padding (no explicit height) rendered ~1-2px
     shorter and off the shared 34px rhythm the sync pill/bell/view-as button (see
     below) already sit on. */
  height: 34px;
  width: 320px; max-width: 40vw; margin: 0; padding: 0 12px;
  border: 1px solid var(--border); background: var(--subtle); color: var(--muted);
}
#topheader .sidebar-search:hover { background: var(--hover); color: var(--text); border-color: var(--border-strong); }
#topheader .sidebar-search .ss-kbd { color: var(--muted); background: var(--panel); border-color: var(--border-strong); }
#topheader .sync-pill { background: var(--tone-neutral-bg); flex: 0 0 auto; }
#topheader .view-as { background: var(--tone-neutral-bg); flex: 0 0 auto; }
#topheader .view-as-select { background-color: var(--panel); color: var(--text); border-color: var(--border-strong); }
#topheader .view-as-select:focus { border-color: var(--accent); }
#topheader .spacer { flex: 1; }
/* the bell collapses to an icon-only circular button in the header (was a labeled
   footer row); .notif-bell-badge's halo ring tracks --panel here, not the dark rail */
#topheader #notif-bell { flex-direction: row; width: 36px; height: 36px; padding: 0; justify-content: center; border-radius: 50%; color: var(--muted); }
#topheader #notif-bell:hover { background: var(--hover); color: var(--text); }
#topheader #notif-bell span:not(.help-q):not(.notif-bell-badge) { display: none; }
#topheader #notif-bell .help-q { background: transparent; width: auto; height: auto; }
#topheader .notif-bell-badge { box-shadow: 0 0 0 2px var(--panel); }
#sidebar {
  width: 82px; flex: 0 0 82px; background: var(--sidebar-bg); color: var(--sidebar-text);
  /* align-items left at its flex default (stretch) — NOT center — so #nav/.nav-group/
     .nav-item/#nav-shortcuts all stretch to the rail's own content width instead of
     sizing to their own (wider) content and bleeding out past the 82px edge, which is
     exactly what .nav-sc-head's "Shortcuts + [+]" row did with align-items:center. */
  display: flex; flex-direction: column; padding: 14px 6px; gap: 4px;
  /* SPEC175 — a crisp inner edge + a faint cast shadow so the rail reads as a
     separate, slightly-raised surface (the expensive-app cue). */
  box-shadow: inset -1px 0 0 var(--sidebar-border), 2px 0 14px -6px rgba(0,0,0,.4);
  z-index: 2;
  border-top: 3px solid var(--role-tint);   /* SPEC209 L2 — ambient per-role context band */
}
/* SPEC209 L2 — per-role context tint (calm ambient signal; Arc "Spaces" idea). Owner =
   brand accent (default, subtle), Manager = teal, Crew = amber. Never competes with the
   single primary accent — it's just a hairline that says which context you're in. */
:root { --role-tint: var(--accent); }
html[data-tier="manager"] { --role-tint: #1e3f66; }   /* RESTYLE — deep harbor (was teal #14a8a0) */
html[data-tier="crew"]    { --role-tint: #b18f52; }   /* RESTYLE — champagne (was amber #e0871d); one navy+brass system */
#main { flex: 1; overflow-y: auto; min-width: 0; }
#view { padding: 26px 30px 60px; max-width: 1340px; margin: 0 auto; }

/* SPEC-DHSHELL D2 — 82px rail: logo mark only (brand-text/name/sub return in the
   ≤860px labeled drawer, SPEC86 query). */
.brand { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 2px 0 14px; }
.brand-text { display: none; }
.brand-logo {
  width: 34px; height: 34px; border-radius: 9px; background: linear-gradient(150deg, #3f77ab, #2b608f 55%, #1e3f66);   /* RESTYLE — the logo cube navies (was indigo→violet) */
  color: #fff; font-weight: var(--fw-black); font-size: 17px; display: flex; align-items: center; justify-content: center;
}
.brand-logo--img { background: #fff !important; } /* SPEC235 — a logo image overrides the gradient letter-mark */
.brand-name { color: #fff; font-weight: var(--fw-bold); font-size: 15px; letter-spacing: .2px; }
.brand-sub { font-size: var(--fs-micro); color: var(--sidebar-text); }
/* SPEC235 — subtle "Powered by [studio]" credit for client-facing pages (toggle-gated) */
.powered-by { font-size: var(--fs-micro); color: var(--muted); text-align: center; letter-spacing: var(--ls-label); opacity: .8; margin-top: 8px; }

/* SPEC236 — demo-mode ribbon: an always-visible, non-dismissible safety signal. Fixed to
   the bottom-center so it never reflows the fixed shell; floats above everything. */
.demo-ribbon {
  position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 9000;
  display: flex; align-items: center; gap: 10px; max-width: calc(100vw - 24px);
  padding: 8px 10px 8px 14px; border-radius: 999px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 92%, #000 8%), var(--accent));
  color: #fff; box-shadow: 0 8px 28px rgba(0,0,0,.28), 0 0 0 1px rgba(255,255,255,.12) inset;
  font-size: 12.5px; backdrop-filter: saturate(1.2); -webkit-backdrop-filter: saturate(1.2);   /* SAFPOLISH 2026-08-25 D4 */
}
.demo-ribbon-dot { width: 8px; height: 8px; border-radius: 50%; background: #fff; flex: 0 0 auto; box-shadow: 0 0 0 4px rgba(255,255,255,.25); animation: demoPulse 2s ease-in-out infinite; }
@keyframes demoPulse { 0%,100% { box-shadow: 0 0 0 3px rgba(255,255,255,.22); } 50% { box-shadow: 0 0 0 6px rgba(255,255,255,.08); } }
.demo-ribbon-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.demo-ribbon-txt strong { font-weight: var(--fw-black); letter-spacing: .02em; }
.demo-ribbon-sub { opacity: .9; }
.demo-ribbon-acts { display: flex; gap: 6px; flex: 0 0 auto; }
.demo-ribbon-acts .btn { background: rgba(255,255,255,.16); color: #fff; border-color: rgba(255,255,255,.28); }
.demo-ribbon-acts .btn:hover { background: rgba(255,255,255,.28); }
.demo-ribbon-acts .btn-primary { background: #fff; color: var(--accent); border-color: #fff; font-weight: var(--fw-bold); }
.demo-ribbon-acts .btn-primary:hover { background: #fff; opacity: .92; }
@media (max-width: 640px) { .demo-ribbon-sub { display: none; } .demo-ribbon { bottom: calc(12px + env(safe-area-inset-bottom, 0px)); } }
@media (prefers-reduced-motion: reduce) { .demo-ribbon-dot { animation: none; } }

#nav, #nav-bottom { display: flex; flex-direction: column; gap: 1px; }
#nav { flex: 0 0 auto; }
/* RAILFIT 2026-09-25 — the desktop rail (nav + Settings + avatar) measures ~896px
   tall, but #app is exactly one viewport tall and #sidebar never scrolled, so on
   any window under ~900px (every MacBook) the rail spilled out and the WHOLE window
   scrolled ~135px on every view (Vincent: "the pages are ever so slightly too
   big"). The rail now scrolls itself, scrollbar hidden; the page never does.
   Desktop only — ≤860px is the off-canvas drawer with its own rules. */
@media (min-width: 861px) {
  #sidebar { overflow-y: auto; overflow-x: hidden; scrollbar-width: none; min-height: 0; }
  #sidebar::-webkit-scrollbar { display: none; }
}
.nav-item {
  display: flex; align-items: center; gap: 11px; padding: 9px 10px; border-radius: 8px;
  color: var(--sidebar-text); cursor: pointer; font-weight: var(--fw-medium); font-size: 13.5px;
  border: none; background: none; width: 100%; text-align: left; font-family: inherit;
}
.nav-item { position: relative; transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease); }
.nav-item:hover { background: var(--sidebar-hover); color: #fff; }
.nav-item.active { background: var(--sidebar-active-bg); color: var(--sidebar-active); font-weight: var(--fw-semibold); }
/* SPEC175 — the active rail is a confident accent tab flush to the edge + an accent-lit icon */
.nav-item.active::before { content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%); width: 3px; height: 18px; border-radius: 0 999px 999px 0; background: var(--accent); }
.nav-item svg { width: 17px; height: 17px; flex: 0 0 17px; }
.nav-item.active svg { color: var(--accent); }

/* SPEC28 D — grouped sidebar (App.NAV_GROUPS).
   A .nav-group wraps a small uppercase label + that group's nav-items. */
.nav-group { display: flex; flex-direction: column; gap: 1px; }
.nav-group + .nav-group { margin-top: 11px; }
.nav-group-label {
  font-size: 10px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .8px;
  /* LAW-A 2026-08-15 (#22) — was var(--faint), a PAGE ink meant for the light --panel/--bg
     surfaces. The sidebar rail is #16202e in BOTH themes (--sidebar-bg), so the light-theme
     --faint (#656d7b) landed on it too. Measured on --sidebar-bg: --faint 3.15:1 (FAIL) →
     --sidebar-text 6.38:1 (PASS). Dark theme was already fine (--faint dark 6.46:1 →
     --sidebar-text 7.01:1) so this is a light-mode-only fix, no dark regression. */
  color: var(--sidebar-text); padding: 2px 10px 4px; user-select: none;
}
/* first label hugs the search/brand a little less than inter-group gaps */
#nav > .nav-group:first-child { margin-top: 2px; }

/* FLOWDROP 2026-08-20 — the nav 'More' fold (navMoreFold, core.js). .nav-group-body
   holds the folded items with the SAME rhythm as .nav-group itself; .nav-more-toggle
   is a plain .nav-item reused as the fold's own row so it costs nothing new visually. */
.nav-group-body { display: flex; flex-direction: column; gap: 1px; }
.nav-more-toggle { opacity: .82; }
.nav-more-toggle .nav-ico { font-weight: var(--fw-bold); font-size: 15px; justify-content: center; display: inline-flex; }

/* ============================================================
   SPEC-DHSHELL D2 — 82px icon rail: navItem() still emits the exact same two spans
   (.nav-ico + a plain label span) it always has; everything below is box-model only.
   Same #nav/#nav-bottom DOM, same residencyShows/canViewId/View-as filtering, same
   click handlers/ids, same badge elements — restyled to read as icon-over-label.
   Reverted for the ≤860px labeled drawer by the existing SPEC86 query further down.
   ============================================================ */
.nav-item {
  flex-direction: column; justify-content: center; gap: 3px; padding: 9px 3px; text-align: center; width: 100%;
}
.nav-item span:not(.nav-ico):not(.nav-badge) {
  font-size: 10px; line-height: 1.15; font-weight: var(--fw-medium); max-width: 100%; word-break: break-word;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.nav-item svg { width: 19px; height: 19px; flex: 0 0 19px; }
/* the accent tab now spans the item's (much taller, stacked) height instead of a fixed
   centered 18px pill sized for the old single-line row */
.nav-item.active::before { top: 6px; bottom: 6px; height: auto; transform: none; }
.nav-badge {
  position: absolute; top: 4px; right: 12px; margin-left: 0;   /* was margin-left:auto, for the old horizontal row */
  min-width: 16px; height: 16px; line-height: 16px; font-size: 10px; padding: 0 4px; border-radius: var(--radius-pill);
}
.nav-group-label { display: none; }   /* icon-only rail; group rhythm still reads from .nav-group's own spacing below */
.nav-group + .nav-group { margin-top: 10px; }

.sidebar-footer { margin-top: auto; display: flex; flex-direction: column; gap: 6px; width: 100%; }   /* stretch (default), same overflow reasoning as #sidebar above */
.sync-pill {
  display: flex; align-items: center; gap: 7px; font-size: 11.5px; padding: 7px 10px;
  border-radius: 8px; background: rgba(255,255,255,.05); cursor: default;
}
.sync-pill .dot { width: 8px; height: 8px; border-radius: 50%; background: #6b7280; flex: 0 0 8px; }
.sync-pill.ok .dot { background: var(--ok); }
.sync-pill.syncing .dot { background: var(--warn); animation: pulse 1s infinite; }
.sync-pill.error .dot { background: var(--danger); }
/* SPEC237 — offline is EXPECTED, not broken: a calm slate dot, never alarming red. */
.sync-pill.offline .dot { background: #64748b; }
@keyframes pulse { 50% { opacity: .5; } }

/* SPEC237 — Field connectivity banner: honest "saved on your phone" reassurance. */
.field-conn-wrap { display: flex; flex-direction: column; gap: 8px; margin: 0 0 12px; }
.field-conn {
  display: flex; align-items: flex-start; gap: 9px; padding: 10px 13px; border-radius: 12px;
  font-size: var(--fs-caption); line-height: var(--lh-normal); font-weight: var(--fw-semibold);
}
.field-conn-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 9px; margin-top: 4px; }
.field-conn.offline { background: color-mix(in srgb, #64748b 16%, var(--card)); color: var(--text); border: 1px solid color-mix(in srgb, #64748b 30%, transparent); }
.field-conn.offline .field-conn-dot { background: #64748b; }
.field-conn.pending { background: var(--accent-soft); color: var(--accent-hover); border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent); }
.field-conn.pending .field-conn-dot { background: var(--accent); animation: pulse 1.2s infinite; }
/* AUDIT 08-01 — "stalled": the phone thinks it is online but sync is not getting through
   (basement/garage/dying cell). Amber, not the calm grey of a known offline stint and not
   the red of a broken app — something IS wrong and only this person can act on it. */
.field-conn.stalled { background: var(--warn-soft); color: var(--warn-ink); border: 1px solid color-mix(in srgb, var(--warn) 34%, transparent); }
.field-conn.stalled .field-conn-dot { background: var(--warn); }
.field-conn-body { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; flex: 1 1 auto; min-width: 0; }
.field-conn-retry { min-height: 44px; flex: 0 0 auto; }   /* a11y — ≥44px touch target, one-handed in the field */
.field-wrap.field-sun .field-conn.stalled { background: #ffffff; color: #7c2d12; border: 2px solid #b45309; }

/* SPEC237 — Install-to-phone chip (Android prompt / iOS Add-to-Home-Screen hint). */
.install-chip {
  display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  padding: 10px 13px; border-radius: 12px; font-size: var(--fs-caption); line-height: 1.45;
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
}
.install-chip-txt { display: flex; align-items: center; gap: 4px; flex: 1 1 220px; min-width: 0; color: var(--text); }
.install-chip-acts { display: flex; gap: 6px; flex: 0 0 auto; }
.install-chip-acts .btn { min-height: 44px; }   /* SPEC237 a11y — ≥44px touch target on the field install chip */
/* SPEC31 — "View as" role previewer (sidebar footer, login-off only) */
.view-as {
  display: flex; align-items: center; gap: 7px; font-size: 11.5px; padding: 6px 10px;
  border-radius: 8px; background: rgba(255,255,255,.05);
}
.view-as-label { color: var(--faint); white-space: nowrap; }
.view-as-select {
  flex: 1; min-width: 0; font-size: 11.5px; padding: 4px 6px; border-radius: 6px;
  background-color: rgba(255,255,255,.07); color: inherit; border: 1px solid rgba(255,255,255,.12);
  cursor: pointer;
}
.view-as-select:focus { outline: none; border-color: var(--accent); }

/* SPEC39 — back/forward nav arrows */
.nav-arrows { display: flex; gap: 6px; padding: 0 2px 6px; }
.nav-arrow {
  flex: 1; height: 30px; border: 1px solid var(--sidebar-border); background: rgba(255,255,255,.04);
  color: var(--sidebar-text); border-radius: 7px; cursor: pointer; font-size: var(--fs-lg); line-height: 1;
}
.nav-arrow:hover:not(:disabled) { background: rgba(255,255,255,.1); color: #fff; }
.nav-arrow:disabled { opacity: .5; cursor: not-allowed; }

/* ============ view header & toolbar ============ */
/* SPEC175 — every view opens with a calm editorial masthead (serif title + hairline)
   echoing the freshly-cinematic client surfaces; this header IS the per-view chrome. */
.view-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: var(--space-5); padding-bottom: var(--space-3); border-bottom: 1px solid var(--border); flex-wrap: wrap; }
/* SPEC-DHSHELL D4 — README type table: page title = Fraunces 600, 30-34px, -.02em. Weight
   was already 600 (--fw-semibold, the WIP font-swap had that half done); only the size
   was short at --fs-display (28px, shared with unrelated components — see --fs-page-title's
   own comment in :root). New dedicated token, nothing else touched. */
.view-title { display: inline-flex; align-items: center; gap: 10px; font-family: var(--font-serif); font-size: var(--fs-page-title); font-weight: var(--fw-semibold); margin: 0; letter-spacing: var(--ls-tight); line-height: var(--lh-tight); }
.view-title svg { color: var(--accent); }   /* SPEC208 — title icons carry the brand accent for one consistent header language */
.view-sub { color: var(--muted); font-size: var(--fs-caption); margin-top: var(--space-2); }
.toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.toolbar .spacer { flex: 1; }
/* SPEC-PHONEBOARD 2026-09-04 — .toolbar already wraps (above), but MEASURED at 430px the
   Tracker's search <input> and the Table/Kanban/Calendar/Timeline .seg refused to shrink and
   ran off the right edge with the Display and New-job buttons behind them. Under the phone
   breakpoint the search takes its own full row and a segmented control may wrap its buttons;
   nothing is hidden, nothing is resized — it just stops overflowing. */
@media (max-width: 700px) {
  /* specificity matters here: `.jt-view .toolbar .search-input { min-width: 190px }`
     (styles.css ~9991) is (0,3,0) and silently beat the first, lighter version of this
     rule — measured: the search box still ran off the edge with the rule "applied". */
  .jt-view .toolbar .search-input, .toolbar > input, .toolbar > .input { flex: 1 1 100%; min-width: 0; width: 100%; }
  .jt-view .toolbar .select, .toolbar > .select { min-width: 0; flex: 1 1 auto; }
  .toolbar .seg { flex-wrap: wrap; }
  .toolbar .seg > button { flex: 1 1 auto; }
  .jt-header-btns { flex-wrap: wrap; }
  .jt-header-btns > * { flex: 1 1 auto; }
}
/* WORLD-CLASS ② — the tucked "More filters" row: reads as a secondary shelf under the
   toolbar (subtle inset panel) so revealing it feels like opening a drawer, not adding clutter. */
.toolbar-more { margin-top: -6px; margin-bottom: 14px; padding: 9px 11px; background: var(--bg-2);
  border: 1px solid var(--border); border-radius: var(--radius); animation: tile-in .2s var(--ease) both; }

/* ============ buttons ============ */
.btn {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--panel); color: var(--text);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold); cursor: pointer; font-family: inherit; white-space: nowrap;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
.btn:hover { background: var(--subtle); }
.btn:active { transform: translateY(.5px); box-shadow: inset 0 2px 4px rgba(0,0,0,.12); }   /* black darkens in both themes */
/* SPEC208 — clean, modern primary: a flat top-to-bottom accent gradient + a single
   soft tinted shadow (the old inset-white gloss read skeuomorphic / "templated"). */
.btn-primary { background: linear-gradient(180deg, var(--accent), var(--accent-hover)); border-color: transparent; color: #fff; box-shadow: 0 1px 2px rgba(var(--ring-rgb), .28); }
.btn-primary:hover { background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 88%, #fff), var(--accent)); box-shadow: 0 4px 12px -3px rgba(var(--ring-rgb), .45); transform: translateY(-.5px); }
.btn-primary:active { background: var(--accent-hover); box-shadow: inset 0 2px 5px rgba(0,0,0,.18); transform: scale(.97); }   /* SPEC-DHSHELL D1 — README motion spec: primary-button press is scale(.97), not a nudge */
.btn-primary:disabled { background: var(--bg-2); }
/* RESTYLE — on dark the steel --accent is too light for white button text; fill with a
   deep harbor navy so white stays AA (9.6:1) and the button still pops off the dark ground. */
/* RESILIENCE 07-30 — a pure specificity bug with a real consequence. This selector is
   (0,2,1) and beat `.btn-primary:disabled` at (0,2,0), so in DARK MODE a disabled primary
   button painted pixel-identical to an enabled one (measured: both
   `linear-gradient(rgb(39,80,127), rgb(30,63,102))`). Every incomplete-form Save button
   in the app looked clickable and silently did nothing — light mode was fine, which is
   exactly why it survived. `:not(:disabled)` scopes the dark paint to enabled buttons and
   lets the disabled rule win, without touching either colour. */
html[data-theme="dark"] .btn-primary:not(:disabled) { background: linear-gradient(180deg, #27507f, #1e3f66); box-shadow: 0 1px 2px rgba(0,0,0,.45); }
html[data-theme="dark"] .btn-primary:hover { background: linear-gradient(180deg, #2f5c90, #234978); }
html[data-theme="dark"] .btn-primary:active { background: #1a3557; }
.btn-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--border)); background: var(--panel); }
.btn-danger:hover { background: var(--danger-soft); }
.btn-ghost { border-color: var(--ghost-border); background: transparent; color: var(--muted); }
.btn-ghost:hover { background: var(--hover); color: var(--text); }
.btn-sm { padding: 6px 12px; font-size: var(--fs-meta); }
.btn-icon { padding: 8px; line-height: 0; min-width: 32px; min-height: 32px; justify-content: center; }
.btn svg { width: 15px; height: 15px; }
.btn:disabled { color: var(--muted); background: var(--bg-2); border-color: var(--border); cursor: not-allowed; box-shadow: none; }

/* ============ inputs ============ */
/* SPEC175 — fields rest as a soft inset well, then lift to white paper on focus */
.input, .select, .textarea {
  padding: 7px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: var(--fs-caption); font-family: inherit; background-color: var(--subtle); color: var(--text); outline: none; min-width: 0;
}
/* background-color (not the `background` shorthand) — a shorthand here would reset
   .select's custom chevron background-image to `none` on every rest/focus repaint */
.input:focus, .select:focus, .textarea:focus { border-color: var(--accent); background-color: var(--panel); box-shadow: var(--focus-ring-soft); outline: none; }
.input, .select, .textarea { transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease), background var(--t-fast) var(--ease); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--muted); }
.textarea { resize: vertical; min-height: 70px; line-height: var(--lh-normal); }
.select { cursor: pointer; }
.field { display: flex; flex-direction: column; gap: 5px; }
.field > label { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--muted); }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-grid .full { grid-column: 1 / -1; }
.search-input { min-width: 200px; }

/* ============ custom form controls — checkboxes, radios, selects, date/time, range ============
   AUDIT-UI-FORMS-2026-07-30 — every checkbox/select/date/range in the Hub painted with
   the browser's own OS-native widget (a different look on macOS/Windows/iOS/Android,
   unmistakably default). Pure CSS, zero markup/JS changes: appearance:none + hand-drawn
   chrome built entirely from the existing token set — every fill/border below is a
   `var(--…)` reference, contrast-checked in both themes (numbers in the audit report).
   Targets the bare element (input[type=…], select) rather than a class, since these
   controls are created all over the codebase with different or no wrapper class — this
   way every existing call site inherits the new look for free, no JS/markup touched. */

/* ---- checkboxes + radios (206 checkbox call sites, 0 radios today but styled the same
   for whenever one appears) ---- */
input[type="checkbox"], input[type="radio"] {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  width: 18px; height: 18px; min-width: 18px; min-height: 18px; margin: 0; padding: 0;
  flex: 0 0 auto; position: relative;
  /* --border-strong (the usual card/table hairline) reads too faint here — 1.47:1 /
     1.65:1 against --panel, the exact kind of number this audit flags. A checkbox's
     whole job is showing on/off, so it gets the stricter --muted (6.54:1 / 6.50:1,
     clears the WCAG 1.4.11 3:1 non-text floor with real margin — see report). */
  border: 1.5px solid var(--muted);
  border-radius: 5px;
  background-color: var(--panel);
  display: inline-grid; place-content: center;
  vertical-align: middle;
  cursor: pointer;
  transition: background-color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
input[type="radio"] { border-radius: 50%; }
input[type="checkbox"]:not(:disabled):hover, input[type="radio"]:not(:disabled):hover { border-color: var(--accent); }

/* checkmark — a two-border "tick" (no icon font, no SVG needed here), scaled to 0 at
   rest and springing in on check so the state change reads as a deliberate tap */
input[type="checkbox"]::before {
  content: '';
  width: 5px; height: 9px;
  border: solid #fff; border-width: 0 2px 2px 0;   /* white ink on a filled accent square — the same #fff-on-accent convention .btn-primary already uses */
  transform: rotate(45deg) translate(-1px, -1px) scale(0);
  transition: transform var(--t-fast) var(--ease-spring);
}
input[type="checkbox"]:checked, input[type="checkbox"]:indeterminate { background-color: var(--accent); border-color: var(--accent); }
input[type="checkbox"]:checked::before { transform: rotate(45deg) translate(-1px, -1px) scale(1); }
input[type="checkbox"]:indeterminate::before { width: 9px; height: 0; border-width: 0 0 2px 0; transform: scale(1); }
/* dark theme: bare --accent only clears 3.36:1 for the white tick — a thin margin.
   Darken via color-mix (never a second hardcoded hex) so it stays 100% token-derived;
   lands at 5.15:1. */
html[data-theme="dark"] input[type="checkbox"]:checked,
html[data-theme="dark"] input[type="checkbox"]:indeterminate {
  background-color: color-mix(in srgb, var(--accent) 78%, black);
  border-color: color-mix(in srgb, var(--accent) 78%, black);
}

/* radio — a ringed dot, not a filled square (the near-universal on/off-vs-choose-one
   visual split): the dot paints in --accent sitting on --panel, so the white-on-accent
   contrast question above never even arises here */
input[type="radio"]::before {
  content: ''; width: 9px; height: 9px; border-radius: 50%;
  background-color: var(--accent);
  transform: scale(0);
  transition: transform var(--t-fast) var(--ease-spring);
}
input[type="radio"]:checked { border-color: var(--accent); }
input[type="radio"]:checked::before { transform: scale(1); }

input[type="checkbox"]:disabled, input[type="radio"]:disabled { cursor: not-allowed; opacity: .55; }   /* WCAG exempts inactive controls from the contrast floor — opacity is signal enough */

input[type="checkbox"]:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-1); }   /* DHSWEEP W9 — was a literal 5px, exactly --radius-1's value; zero-diff token wire */
input[type="radio"]:focus-visible { box-shadow: var(--focus-ring); border-radius: 50%; }

/* ---- selects (431 call sites) — kill the native arrow, draw our own ---- */
select {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  /* chevron stroked with currentColor so it always matches the select's own text ink —
     zero hex baked into the icon, so it re-tints correctly in dark mode (and on the
     sidebar's `.view-as-select`, which uses `color: inherit` on a dark rail) with no
     extra rule. Verified 12.76–17.75:1 against every rest/focus background this select
     can sit on — nowhere close to the 3:1 floor (see report). */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1.5 2L6 6.2 10.5 2' stroke='currentColor' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  background-size: 11px 7px;
}
/* padding-right has to out-specificity .input/.select/.textarea's padding SHORTHAND
   above, so this matches it class-for-class rather than relying on the bare tag */
select.select, select.input, select.cell-input, select.view-as-select { padding-right: 30px; }
select:disabled { background-color: var(--bg-2); color: var(--muted); cursor: not-allowed; border-color: var(--border); }

/* ---- date / time — the OS still draws the calendar/clock GLYPH itself; we own its
   affordance (pointer cursor, a soft hover/focus halo) so it reads as part of the same
   field system as every text input, not a bolted-on OS widget. The field SHELL itself
   (border/padding/background) already comes free from the shared .input class every
   date/time site already carries. WebKit-only pseudo-element (Chrome/Edge/Safari);
   Firefox's compact date/time widget has no equivalent hook and already inherits the
   field's own border/background/color from .input. ---- */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
  cursor: pointer; border-radius: 4px; padding: 3px; margin-right: -3px;
  opacity: .7;
  transition: opacity var(--t-fast) var(--ease), background-color var(--t-fast) var(--ease);
}
input[type="date"]:not(:disabled):hover::-webkit-calendar-picker-indicator,
input[type="time"]:not(:disabled):hover::-webkit-calendar-picker-indicator,
input[type="date"]:focus::-webkit-calendar-picker-indicator,
input[type="time"]:focus::-webkit-calendar-picker-indicator {
  opacity: 1; background-color: var(--hover);
}
input[type="date"]:disabled, input[type="time"]:disabled { background-color: var(--bg-2); color: var(--muted); cursor: not-allowed; border-color: var(--border); }

/* ---- range (7 call sites) — track + thumb, both engines. Excludes the two special
   image-comparison drag-handles (order.js's before/after slider, cstatus's revision
   slider) — those are already fully custom, invisible-track overlays for a completely
   different interaction (a full-bleed drag handle, not a value slider); this
   track/thumb design is for genuine VALUE sliders (Conditions' time-of-day dial, the
   self-review comfort/tone sliders). ---- */
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range) {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  width: 100%; height: 6px; padding-block: 12px; background: transparent; cursor: pointer; margin: 0;
}
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-webkit-slider-runnable-track { height: 6px; border-radius: 999px; background-color: var(--border-strong); }
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; margin-top: -6px; border-radius: 50%;
  background-color: var(--accent); border: 2px solid var(--panel); box-shadow: 0 1px 3px rgba(0,0,0,.2);
  cursor: pointer;
  transition: transform var(--t-fast) var(--ease);
}
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-moz-range-track { height: 6px; border-radius: 999px; background-color: var(--border-strong); }
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-moz-range-progress { height: 6px; border-radius: 999px; background-color: var(--accent); }
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%; box-sizing: border-box;
  background-color: var(--accent); border: 2px solid var(--panel); box-shadow: 0 1px 3px rgba(0,0,0,.2);
  cursor: pointer;
  transition: transform var(--t-fast) var(--ease);
}
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range):not(:disabled):hover::-webkit-slider-thumb,
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range):not(:disabled):hover::-moz-range-thumb { transform: scale(1.1); }
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range):focus-visible { box-shadow: none; }   /* suppress the sitewide bare :focus-visible ring on the (transparent, 6px-tall) outer element — the thumb below carries its own ring instead */
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range):focus-visible::-webkit-slider-thumb,
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range):focus-visible::-moz-range-thumb { box-shadow: var(--focus-ring); }
/* dark theme: the plain --accent thumb only clears 3.05:1 against the (dark)
   --border-strong track — thin margin. Swap to --accent-hover (dark theme's LIGHTER
   variant — an existing token, not a new color) for 4.25:1 instead. */
html[data-theme="dark"] input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-webkit-slider-thumb,
html[data-theme="dark"] input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-moz-range-thumb { background-color: var(--accent-hover); }
input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range):disabled { opacity: .5; cursor: not-allowed; }

/* ============ cards & KPIs ============ */
/* SPEC175 — cards read as floating paper: larger radius (matches kpi/modal) + the
   softened two-layer resting shadow. */
.card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow), var(--card-hi); transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-base) var(--ease); }
.card.is-clickable { cursor: pointer; transition: transform var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease); }
.card.is-clickable:hover { border-color: var(--border-strong); box-shadow: var(--shadow-lg); transform: translateY(-1px); }
.card-pad { padding: 18px 20px; }
.card-title { display: flex; align-items: center; gap: 7px; font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--muted); text-transform: uppercase; letter-spacing: .4px; margin: 0 0 12px; }
.card-title svg { flex: 0 0 auto; }   /* SPEC208 — card icons inherit the muted title color (calm secondary-header hierarchy) */
.kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; margin-bottom: 18px; }
/* WORLD-CLASS ① — a fixed 4-metric scoreboard row lays out 4-up on wide screens and a
   clean 2×2 on narrower ones, never an orphaned 3+1 (the mobile rules below take over ≤400). */
.kpi-row--quad { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 1120px) { .kpi-row--quad { grid-template-columns: repeat(2, 1fr); } }
.kpi-card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm), var(--card-hi); padding: 15px 17px; transition: box-shadow var(--t-base) var(--ease), border-color var(--t-base) var(--ease); }
.kpi-card:hover { box-shadow: var(--shadow), var(--card-hi); border-color: var(--border-strong); }
/* ④ fix — a KPI card must be able to shrink inside its grid track (grid items floor at
   min-content by default) and its embedded revenue sparkline (a fixed 140px span) must
   scale down, so two-up never forces a horizontal scroll on ~360px phones. */
.kpi-card { min-width: 0; }
.kpi-card .spark, .kpi-card .spark svg { max-width: 100%; }
.kpi-label { font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--muted); text-transform: uppercase; letter-spacing: .4px; }
.kpi-value { font-size: 26px; font-weight: var(--fw-black); letter-spacing: var(--ls-tight); margin-top: 4px; }
.kpi-hint { font-size: 11.5px; color: var(--faint); margin-top: 3px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
/* let cards shrink with their track instead of forcing min-content width
   (otherwise narrow viewports get horizontal page scroll) */
.grid-2 > *, .grid-3 > *, .settings-grid > * { min-width: 0; }

/* WAVE0-A A11 (should-land polish, taken up by Section B in Wave 1) — the
   Media view's per-job browser (media.js): asset grid + a 300px sidebar,
   unreadably squeezed together on a phone; stacks at ≤640, sidebar second. */
.media-body-grid { display: grid; grid-template-columns: 1fr 300px; gap: 20px; }
@media (max-width: 640px) { .media-body-grid { grid-template-columns: 1fr; } }

/* ============ tables ============ */
.table-wrap { overflow-x: auto; background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-sm), var(--card-hi); }
/* D17(a)/D22 — right-edge scroll affordance (App.wireScrollFade, core.js) so a
   horizontally-clipped mobile table/board signals "more this way" instead of
   just cutting content off mid-badge with no cue. Mobile-only: at desktop widths
   these containers rarely overflow, and the base table/board layout doesn't
   change here. */
@media (max-width: 640px) {
  .scroll-fade-x { position: relative; }
  .scroll-fade-x::after {
    content: ''; position: absolute; top: 0; right: 0; bottom: 0; width: 24px;
    background: linear-gradient(to right, transparent, var(--panel));
    pointer-events: none; opacity: 1; transition: opacity var(--t-fast) var(--ease);
  }
  /* the mobile task board sits directly on the page ground, not a panel card */
  .tk-board.scroll-fade-x::after { background: linear-gradient(to right, transparent, var(--bg)); }
  .scroll-fade-x.at-end::after { opacity: 0; }
  @media (prefers-reduced-motion: reduce) { .scroll-fade-x::after { transition: none; } }
}
table.table { border-collapse: collapse; width: 100%; font-size: var(--fs-caption); }
/* SPEC175 — calmer, taller rows + crisper hairlines + a soft accent-tinted hover rail */
.table th {
  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted);
  padding: 11px 14px; border-bottom: 1px solid var(--border); background: var(--subtle); white-space: nowrap; position: relative;
}
.table td { padding: 12px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.table th:first-child, .table td:first-child { padding-left: 18px; }
.table th:last-child, .table td:last-child { padding-right: 18px; }

/* WAVE0-A A10 (should-land polish, taken up by Section B in Wave 1) — the
   Clients Directory table (clients.js: List AND Ranked modes both mark
   their cells with these cl-col-* hooks) hides its least decision-relevant
   columns at ≤1179 and keeps the Name column pinned while the rest scrolls,
   the same sticky-first-column idiom the Tracker table already uses
   (.jt-table-wrap ~9142 above) — mirrored here rather than reused directly
   since that block is checkbox-column-aware and this table never has one. */
@media (max-width: 1179px) {
  .cl-col-aov, .cl-col-last { display: none; }
  table.table thead th.cl-col-name { position: sticky; left: 0; z-index: 6; background: var(--subtle); }
  table.table tbody tr:not(.group-head) > td.cl-col-name { position: sticky; left: 0; z-index: 2; background: var(--panel); }   /* sticky cells MUST be opaque */
  table.table tbody tr:not(.group-head):hover > td.cl-col-name { background: var(--hover); }
  /* one-line Contact: the channel chip + email share a row, truncated; the
     phone (least urgent of the three at this width) steps aside — still one
     tap away in the client's own profile. */
  .cl-col-contact { display: flex; align-items: center; gap: 6px; max-width: 230px; overflow: hidden; }
  .cl-col-contact > div:nth-child(1) { flex: none; margin-bottom: 0; }
  .cl-col-contact > div:nth-child(2) { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cl-col-contact > div:nth-child(3) { display: none; }
}
/* CONSOL-3 — row DENSITY presets (comfortable = the default above). The differences are
   REAL: compact shrinks padding + font + line-height AND the in-row controls (the status/
   priority selects + chips dominate row height, so they must shrink too); big is roomy and
   expands the services. */
.table-wrap.density-compact table.table { font-size: 11.5px; line-height: var(--lh-tight); }
.table-wrap.density-compact table.table td { padding: 1px 6px; }
.table-wrap.density-compact table.table th { padding: 3px 6px; }
.table-wrap.density-compact table.table th:first-child, .table-wrap.density-compact table.table td:first-child { padding-left: 10px; }
.table-wrap.density-compact .btn, .table-wrap.density-compact .btn-sm,
.table-wrap.density-compact select, .table-wrap.density-compact .select,
.table-wrap.density-compact .chip, .table-wrap.density-compact .pill,
.table-wrap.density-compact .status-select, .table-wrap.density-compact .svc-menu-trigger {
  font-size: var(--fs-micro); padding-top: 1px; padding-bottom: 1px; min-height: 0; line-height: 1.25; }
.table-wrap.density-compact .clip-mini, .table-wrap.density-compact .row-sub, .table-wrap.density-compact .cell-sub { font-size: 10px; }

.table-wrap.density-big table.table { font-size: 14.5px; line-height: 1.6; }
.table-wrap.density-big table.table td { padding: 16px 16px; }
.table-wrap.density-big table.table th { padding: 15px 16px; }
.table-wrap.density-big select, .table-wrap.density-big .select, .table-wrap.density-big .btn-sm { font-size: 13.5px; }

/* PEOPLE-PIPELINES-ACCESS 2026-09-26 Phase 3 — directory.js renders BOTH a
   desktop table and a phone card list (not one table with columns hidden,
   like .cl-col-* above): the plan's own test requirement is "at 375px there
   is no horizontal scroll", and .table-wrap's overflow-x:auto still lets a
   narrowed table scroll sideways. Below 641px the table hides outright and
   the card list (already a vertical stack, no fixed-width columns to
   overflow) takes over. */
.dir-mobile { display: none; }
@media (max-width: 640px) {
  .dir-desktop { display: none; }
  .dir-mobile { display: flex; flex-direction: column; gap: 10px; }
}

/* CONSOL-3 — Excel-style column resize grip on the right edge of each header cell */
.col-resizable { position: relative; }
.col-resize { position: absolute; top: 0; right: -4px; width: 9px; height: 100%; cursor: col-resize; z-index: 3; touch-action: none; }
.col-resize::after { content: ''; position: absolute; top: 22%; bottom: 22%; right: 4px; width: 2px; border-radius: 2px; background: transparent; transition: background var(--t-fast) var(--ease); }
.col-resizable:hover .col-resize::after { background: var(--border-strong); }
.table tbody tr:last-child td { border-bottom: none; }
.table tbody tr { transition: background var(--t-fast) var(--ease); }
.table tbody tr:hover { background: var(--hover); box-shadow: inset 2px 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
/* SPEC-V2CLICKEASE 2026-08-31 — an editable cell is no longer its OWN clickable
   thing (that was the double-click collision — a click meant for the row
   popped this cell's editor open instead). It reads as an ordinary cell of a
   clickable row now (tbody tr:hover above already washes the whole row), so
   the old whole-cell "this is special" outline is gone. What replaces it is
   `.jt-cell-pencil` — a small, quiet edit affordance, visible only on row
   hover/focus (mirrors the chat `--act` reveal, RULES.md §5.6) so editing
   reads as deliberate. position:relative on the td anchors the pencil's
   absolute placement without affecting cell text layout. */
.table td.editable { position: relative; }
.table .row-click { cursor: pointer; }
.jt-cell-pencil {
  position: absolute; top: 50%; right: 4px; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; padding: 0; margin: 0; border-radius: 6px;
  border: 1px solid transparent; background: var(--panel); color: var(--muted);
  cursor: pointer; opacity: 0; transition: opacity var(--t-fast) var(--ease); z-index: 2;
}
.table tbody tr:hover .jt-cell-pencil,
.table tbody tr:focus-within .jt-cell-pencil,
.jt-cell-pencil:focus-visible { opacity: 1; }
.jt-cell-pencil:hover { background: var(--hover); color: var(--text); border-color: var(--border); }
.jt-cell-pencil:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* SPEC-WAVE0-A A5 2026-09-02 — a mouse/trackpad laptop between 641px and
   1279px had NO hover-reveal cue at all (opacity 0 at rest, only shown on a
   row hover it never got a signal to invite) — "hard to quickly edit",
   measured opacity 0 at 1440/1280/1180/1024/900 in reports-2026-09/audit-
   sys/inv-smallscreen.json. Pencils go permanently visible at <=1279px, but
   stay the SAME 22x22 mouse size here — the 44px TOUCH floor below is a
   separate concern (hover:none), untouched. */
@media (max-width: 1279px) {
  .jt-cell-pencil { opacity: 1; }
}
/* touch has no hover to reveal the pencil on — stay permanently visible there,
   at the 44px floor RULES.md §3.5 sets for any touch hit target. */
@media (max-width: 640px), (hover: none) {
  .jt-cell-pencil {
    opacity: 1; width: 44px; height: 44px; right: 0; top: 0; transform: none;
    border-radius: 0 4px 4px 0; background: transparent;
  }
}
.cell-input { width: 100%; border: 1px solid var(--accent); border-radius: 4px; padding: 4px 6px; font: inherit; outline: none; }
.th-x { position: absolute; top: 2px; right: 2px; font-size: 10px; color: var(--faint); cursor: pointer; padding: 2px 4px; border-radius: 4px; display: none; background: none; border: none; font-family: inherit; line-height: 1; }
.table th:hover .th-x { display: inline; }
.th-x:hover { background: var(--danger-soft); color: var(--danger); }

/* ============ chips & badges ============ */
.chip {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 11px; border-radius: 999px;
  font-size: 11.5px; font-weight: var(--fw-semibold); letter-spacing: .1px; white-space: nowrap; line-height: 1.4;
  /* SPEC175 — a hairline ring in the chip's own hue (currentColor) → printed-label tactility */
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 20%, transparent);
}
.chip.is-ok { background: var(--ok-soft); color: var(--ok-ink); }  /* SPEC-A11Y — was var(--ok) (3.70:1 in light, fails AA); ink token is 5.40:1. Dark theme unaffected: --ok-ink aliases var(--ok) there. */
.chip.is-danger { background: var(--danger-soft); color: var(--danger-ink); }  /* was var(--danger) (4.44:1 in light, fails AA); ink token is 6.05:1. Dark theme unaffected: --danger-ink aliases var(--danger) there. */
.chip.is-warn { background: var(--warn-soft); color: var(--warn-ink); }  /* was var(--warn) (3.34:1 in light, fails AA); ink token is 5.20:1. Dark theme unaffected: --warn-ink aliases var(--warn) there. */
.chip.is-info { background: var(--accent-soft); color: var(--accent-hover); }  /* SPEC198 — AA in dark (accent-on-accent-soft was 3.6:1) */
.chip .chip-dot { width: 6px; height: 6px; border-radius: 50%; }
/* SPEC127 — chip-x / view-chip-x are now <button>s (keyboard-focusable, Enter/Space
   activate). Reset native button chrome so they render exactly like the old spans. */
.chip-x, .view-chip-x { background: none; border: 0; padding: 0; color: inherit; font: inherit; line-height: 1; }
.chip-x { cursor: pointer; opacity: .55; font-weight: var(--fw-bold); margin-left: 2px; }
.chip-x:hover { opacity: 1; }
.view-chip-x { cursor: pointer; }
.chip-x:focus-visible, .view-chip-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; opacity: 1; }
.badge { display: inline-block; padding: 1px 7px; border-radius: 999px; background: var(--hover); color: var(--muted); font-size: var(--fs-micro); font-weight: var(--fw-semibold); }
.tag { display: inline-block; padding: 2px 8px; border-radius: 5px; background: var(--accent-soft); color: var(--accent-hover); font-size: var(--fs-micro); font-weight: var(--fw-semibold); margin: 1px 3px 1px 0; }  /* SPEC198 — AA in dark */

/* ============ SPEC-DHTRACK D1/D3 — stage band ============
   Shared by the Job Tracker table's group header AND its kanban column head
   ("same stage-band header anatomy" — a solid count chip in the live status
   color + an uppercase stage label). Color/ink are computed in tracker.js per
   render (App.tintOf/App.isDarkColor, core.js) since a status color is
   settings-stored data, not a token — these classes carry only the SHAPE.
   Checked against the actual prototype's own render logic
   (design-handoff/VVT-Hub-Owner.dc.html ~line 3488): the chip's ink flips
   light/dark with the stage color (`onColor`), but the LABEL never does
   (`labelFg` stays a dark ink at every stage, incl. the darkest) — the label
   sits on a low-alpha tint or a neutral surface, not the full-strength color,
   so it keeps the app's normal theme-aware text color here instead. */
.stage-band { display: inline-flex; align-items: center; gap: 8px; width: 100%; min-height: 20px; }
.stage-band-chip {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 20px; padding: 0 7px; border-radius: var(--radius-1);
  font-size: 11.5px; font-weight: var(--fw-black); font-variant-numeric: tabular-nums;
}
.stage-band-label {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13px; font-weight: var(--fw-black); letter-spacing: .1em; text-transform: uppercase; color: var(--text);
}
.stage-band-of { flex: none; font-size: var(--fs-meta); font-weight: var(--fw-medium); color: var(--muted); white-space: nowrap; }

/* SPEC-V2TRACKFIT gap 1 2026-08-29 — the Job Tracker's STAGE column pill (tracker.js
   jobStagePill). Measured against the prototype's own cellFor.status render
   (VVT-Hub-Owner.dc.html ~4127-4145): tinted rounded pill (999px), a leading dot in
   the live stage color, the stage name UPPERCASE, a trailing grey "n/N" counter.
   The wrapped <select> keeps its base `select cell-select` classes UNCHANGED
   (jobStagePill wraps statusCellSelect's own return value verbatim, per styles.css
   §6's "do NOT edit tintSelect/statusCellSelect" ruling — a real mobile tap-target
   fix lives on that shared control) and ADDS `jt-stage-select` alongside — so the
   phone-only ≥44px/16px anti-zoom floor a few hundred lines down
   (`select.cell-select` @media max-width:640px) still applies here for free, and
   only needs overriding on desktop/tablet. Selector is the COMPOUND
   `select.cell-select.jt-stage-select` ((0,2,1), beats the bare `select.cell-select`
   (0,1,1) on specificity regardless of source order) so this cell's own look wins
   without touching the shared rule or its cascade position. `--stage-c` is set PER
   ROW from the job's live status color (never a literal), matching the tint law
   every other stage read in this file already follows (App.tintOf / color-mix). */
.jt-stage-pill {
  position: relative; display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 4px 9px 4px 7px; border-radius: 999px;
  background: color-mix(in srgb, var(--stage-c, var(--muted)) 16%, var(--panel));
  border: 1px solid color-mix(in srgb, var(--stage-c, var(--muted)) 36%, transparent);
}
.jt-stage-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--stage-c, var(--muted)); z-index: 1; }
/* FABLE-VERIFY fix (morning ruling) — this label, not the <select> below, is
   now the ONLY visible stage-name text (the first version left the select's
   own text rendering as a second, competing layer — a grey artifact behind
   the dot/counter, caught in the owner's own screenshot review). 110px caps a
   long custom (admin-renamed) status label with an ellipsis rather than
   stretching the column; the default palette's longest label ("In Progress")
   sits well under it. */
.jt-stage-label {
  position: relative; z-index: 1; min-width: 0; max-width: 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 10.5px; font-weight: var(--fw-semibold); letter-spacing: .04em; text-transform: uppercase; color: var(--text);
}
.jt-stage-idx { position: relative; z-index: 1; flex: none; font-size: 10.5px; font-weight: var(--fw-semibold); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* the real <select> — sized to cover the WHOLE pill (position:absolute;inset:0
   on a position:relative pill) so a click/tap/keyboard user hits the native
   control anywhere on it, but rendered FULLY INVISIBLE (opacity:0 — never
   display:none/visibility:hidden, which would drop it from the accessibility
   tree and tab order). `.jt-stage-label`/`.jt-stage-dot`/`.jt-stage-idx` above
   are what actually paints; z-index keeps them visually on top of this
   overlay even though paint order alone would already do that (the select is
   transparent) — belt and suspenders for the outline/focus ring below. */
select.cell-select.jt-stage-select {
  position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0;
  appearance: none; -webkit-appearance: none; background: transparent; background-image: none;
  border: 0; border-radius: 999px; padding: 0; margin: 0; min-height: 0; min-width: 0;
  opacity: 0; cursor: pointer; text-align: left;
}
.jt-stage-pill:has(select.jt-stage-select:hover),
.jt-stage-pill:has(select.jt-stage-select:focus-visible) { outline: 2px solid var(--accent); outline-offset: 1px; }
@media (max-width: 640px) {
  /* LAW-A 2026-08-19 anti-zoom floor (≥16px, ≥44px tall) — the select has no
     visible text to auto-zoom on anymore, but an invisible focused <select>
     below 16px still triggers iOS zoom, so the font-size floor stays; the
     44px TAP-TARGET floor now belongs to the PILL itself (the select's
     inset:0 overlay can only ever be as large as its positioned parent), so
     it's applied there instead of to the (now non-visually-sized) select. */
  .jt-stage-pill { min-height: 44px; }
  select.cell-select.jt-stage-select { font-size: var(--fs-md); }
}

/* SPEC-V2TRACKFIT gap 6 2026-08-29 — the row's inline drawer (tracker.js
   buildRowDrawer). Measured against the prototype's own drawer render
   (VVT-Hub-Owner.dc.html ~line 316-336): a bordered card sitting on the table's
   subtle background, a small thumbnail box, three field columns, two actions
   stacked at the trailing edge. */
.jt-drawer-row > td { padding: 0 var(--space-4) var(--space-4); background: var(--bg-2); border-bottom: 1px solid var(--border); }
/* SPEC-WAVE0-A A6 2026-09-02 — the drawer became the quick-edit surface (Q18 =
   a), which added a Services chip row and a Notes textarea that need the FULL
   drawer width, not a 1/3 grid cell — so `.jt-drawer` is now a COLUMN of two
   bands: `.jt-drawer-top` (thumb + fields + actions, the original flex row,
   unchanged shape) and the new full-width svc/notes rows beneath it. */
.jt-drawer { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-3) var(--space-4); background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-2); cursor: default; }
.jt-drawer-top { display: flex; gap: var(--space-4); align-items: flex-start; }
.jt-drawer-thumb { flex: none; width: 150px; height: 88px; border-radius: var(--radius-1); overflow: hidden; border: 1px solid var(--border); background: var(--bg-2); }
.jt-drawer-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* honest empty state — no job carries a real thumbnail field yet (see the fn's
   own doc comment); a dashed box says "no photo" rather than faking one. */
.jt-drawer-thumb-empty { border-style: dashed; }
.jt-drawer-fields { flex: 1; min-width: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4) var(--space-5); }
.jt-drawer-label { font-size: 11.5px; font-weight: var(--fw-semibold); letter-spacing: .04em; text-transform: uppercase; color: var(--muted); margin-bottom: 4px; }
.jt-drawer-value { font-size: var(--fs-caption); font-variant-numeric: tabular-nums; }
/* A6 — an editable drawer field reuses the exact `.jt-cell-pencil` affordance
   the table cells already have (position:relative anchor + the pencil's own
   position:absolute), never a new widget kind. */
.jt-drawer-value.editable { position: relative; padding-right: 22px; min-height: 18px; }
/* A3 2026-09-02 — was a fixed 150px column, which clipped the drawer's 4th
   action ("Open in Post & Review") at 1280 and 1180 (measured,
   reports-2026-09/audit-sys/inv-smallscreen.json finding 3). Buttons wrap
   their own label instead of a column wide enough to never wrap one. */
.jt-drawer-actions { flex: none; width: auto; min-width: 150px; max-width: 190px; display: flex; flex-direction: column; gap: var(--space-2); }
.jt-drawer-actions .btn { white-space: normal; text-align: left; }
/* A6 — Services (chip row + "+ add") and Notes (one textarea, saves on blur)
   ride full-width below the thumb/fields/actions row. */
.jt-drawer-svc-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.jt-drawer-notes { display: flex; flex-direction: column; gap: 4px; }
.jt-drawer-notes textarea { width: 100%; min-height: 52px; resize: vertical; }
@media (max-width: 900px) {
  .jt-drawer-top { flex-wrap: wrap; }
  .jt-drawer-fields { grid-template-columns: repeat(2, 1fr); }
  .jt-drawer-actions { width: 100%; max-width: none; flex-direction: row; flex-wrap: wrap; }
}

/* SPEC-V2TRACKFIT Q6b 2026-08-29 — Display modal column-picker drag-to-reorder
   (tracker.js openJobColumnsPicker). Minimal, native-HTML5-DnD feedback: a
   lift on the dragged row, a highlight on the row about to receive it. */
.jt-colpick-row:hover { background: var(--hover); }
.jt-colpick-draggable.jt-colpick-dragging { opacity: .5; }
.jt-colpick-draggable.jt-colpick-dragover { background: var(--accent-soft); outline: 1px dashed var(--accent); outline-offset: -1px; }

/* ============ kanban ============ */
.kanban { display: flex; gap: 12px; overflow-x: auto; align-items: flex-start; padding-bottom: 14px; }
.kanban-col { flex: 0 0 260px; background: var(--bg-2); border-radius: var(--radius); padding: 10px; min-height: 120px; }
/* SPEC-WAVE0-A A4 2026-09-02 — `.kanban-col`'s fixed 260px meant only 3-4 of the
   Job Tracker's 9 stage columns ever fit a laptop (measured: 4 visible at 1440,
   3 at 1024/1180, 2 at 900 — reports-2026-09/audit-sys/inv-smallscreen.json).
   Scoped to `.jt-board` ONLY (the Tracker's own scoping hook, tracker.js
   buildKanban) — Leads/Tasks/Marketing/Pipelines/Video share the bare
   `.kanban-col` class and keep their fixed 260px unchanged. clamp() lets a
   column shrink to fit ~4 across at >=1024 without ever going below a 200px
   floor (cards still readable) or above the original 260px ceiling. */
.jt-board .kanban-col { flex: 0 0 clamp(200px, calc((100% - 8 * var(--space-3)) / 4), 260px); }
.kanban-col.drag-over { outline: 2px dashed var(--accent); outline-offset: -2px; background: var(--accent-soft); }
.kanban-col-head { display: flex; align-items: center; gap: 8px; padding: 2px 4px 10px; font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.kanban-col-head .count { margin-left: auto; }
.kanban-col-head .stage-band-label { flex: 1 1 auto; }
/* SPEC-DHTRACK D3 — card tokens: radius-2 (8px) is the SAME value the card already
   painted (border-radius:8px), now named off the DHSHELL radius scale rather than a
   literal; shadow-card replaces the generic --shadow (both are real, distinct tokens
   — see styles.css :root — this swaps the card onto the one the brief names for
   cards specifically); hover lift matches the .card.is-clickable convention already
   used elsewhere in this file (translateY + a deeper shadow on hover), calm, no
   bounce, using the shared --t-fast/--ease motion tokens. */
.kanban-card {
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-2); box-shadow: var(--shadow-card);
  padding: 10px 12px; margin-bottom: 8px; cursor: grab; font-size: 12.5px;
  transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.kanban-card:hover { box-shadow: var(--shadow); border-color: var(--border-strong); transform: translateY(-1px); }
/* SPEC215 — render within a frame at scale: skip layout/paint of OFF-SCREEN tracker
   rows + kanban cards. CSS-only on the leaf node, so the full DOM stays intact —
   drag-and-drop, filters, select-all, group-collapse, Ctrl-F all keep working. The
   `auto` keyword in contain-intrinsic-size makes the browser remember each element's
   real height after first paint, so the scrollbar settles (no jitter). Headers, group
   rows, and kanban columns are deliberately left un-contained. */
.table tbody tr.cv-row { content-visibility: auto; contain-intrinsic-size: auto 96px; }
.kanban-card.cv-card { content-visibility: auto; contain-intrinsic-size: auto 140px; }
.kanban-card:focus-visible { border-color: var(--accent); }
.kanban-card:active { cursor: grabbing; }
.kanban-card.dragging { opacity: .45; }
.kanban-card .kc-title { font-weight: var(--fw-bold); font-size: var(--fs-caption); margin-bottom: 3px; }
.kanban-card .kc-meta { color: var(--muted); display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: center; }
.kanban-card .task-link { display: block; margin-top: 7px; font-size: 11.5px; color: var(--accent); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kanban-card .task-link:hover { text-decoration: underline; }

/* ============ progress ============ */
.progress { height: 5px; border-radius: 999px; background: var(--border); overflow: hidden; min-width: 50px; }
.progress-bar { height: 100%; border-radius: 999px; background: var(--accent); transition: width var(--t-slow) var(--ease); }
.progress-row { display: flex; align-items: center; gap: 8px; font-size: 11.5px; color: var(--muted); white-space: nowrap; }

/* ============ modal & toast ============ */
/* SPEC-V2MODALFIT (2026-08-29) — Vincent's ruling: a popup "doesn't fill the
   screen but fills enough to really see everything easily and then still be
   easy to click out of it." The CONTRACT lives here + in App.modal/App.slideOver
   (core.js) so every one of the ~250 call sites app-wide inherits it for free —
   no per-popup forks. Three width/height tiers by viewport (desktop ≥1024,
   tablet 640-1023, phone <640 — the phone bottom-sheet posture finishes in the
   existing `@media (max-width: 640px)` block below, alongside the rest of that
   block's touch-target rules). Desktop: width is min(content-class, 92vw) so a
   680px "standard" modal never crowds a 700px-wide laptop window; height caps at
   85vh with the SCROLL living inside .modal-body (flex column) rather than on
   the overlay, so the header/× and footer/buttons stay pinned instead of
   scrolling away with long content — that was the pre-existing desktop bug
   (overlay had `overflow-y:auto`, `.modal` had no max-height, so a tall modal's
   own header could scroll off top). */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(16,18,28,.55); display: flex; align-items: center;
  justify-content: center; padding: 16px; z-index: 100;
}
.modal {
  background: var(--panel); border-radius: var(--radius-lg); box-shadow: var(--shadow-lg), var(--card-hi);
  width: min(680px, 92vw); max-height: 85vh;
  display: flex; flex-direction: column;
  animation: modal-in var(--t-base) var(--ease-spring);
}
.modal.wide { width: min(920px, 92vw); }
.modal.narrow { width: min(420px, 92vw); }
@keyframes modal-in { from { transform: translateY(8px) scale(.99); opacity: 0; } to { transform: none; opacity: 1; } }
.modal-grab { display: none; } /* shown only in the phone bottom-sheet posture, below */
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px 0; flex: 0 0 auto; }
/* PIPEFIX 2026-09-08 — the head is `display: flex; justify-content: space-between`, and the
   h3 had neither min-width:0 nor a wrap rule, so as a flex item it kept min-width:auto. For
   an unbroken token min-content == max-content, so the title refused to shrink and shoved the
   close button past the modal's edge — measured on a 375px phone sheet with a 47-char email
   as the card name: h3 408px against a 375px head, and the × landing 80px off-screen, which
   on a phone means the sheet can only be dismissed by tapping the backdrop. Reached from
   pipelines.js's card sheet, whose title is the raw lead name, but the fix belongs on the
   shared rule: every modal in the app titled with user text has the same exposure. */
.modal-head h3 { margin: 0; font-size: var(--fs-lg); font-family: var(--font-serif); font-weight: var(--fw-semibold); letter-spacing: var(--ls-tight); min-width: 0; overflow-wrap: anywhere; }
.modal-close { border: none; background: none; font-size: 20px; color: var(--faint); cursor: pointer; padding: 2px 8px; border-radius: 6px; flex: 0 0 auto; }
.modal-close:hover { background: var(--hover); color: var(--text); }
.modal-body { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 16px 20px; min-height: 0; }
.modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 0 20px 18px; flex: 0 0 auto; }
.modal-foot .left { margin-right: auto; }
/* tablet tier — a fixed vw share regardless of content class (no upper content-class
   cap named for this band in the contract, unlike desktop's min(class,92vw)) */
@media (max-width: 1023px) {
  .modal, .modal.wide, .modal.narrow { width: 80vw; max-width: 80vw; }
}

/* SPEC211 — right-docked slide-over panel (App.slideOver). Shares the modal scrim
   colour + elevation language; slides in from the right with a spring ease. z 112/115
   sits above modals (100), below toasts (200) — a free slot in the layering map.
   SPEC-V2MODALFIT: same three-tier contract as .modal above — desktop keeps its
   fixed 420px dock (already ≤92vw at any real desktop width), tablet caps to 80vw
   instead of the old 100%-at-≤860px rule (a 768px iPad was getting an edge-to-edge
   drawer, which is exactly the "fills everything" the ruling forbids), and phone
   converts to the SAME bottom-sheet posture as .modal (finished in the 640px block
   below) rather than a second, inconsistent full-screen gesture. */
.slideover-scrim {
  position: fixed; inset: 0; background: rgba(16,18,28,.55); z-index: 112;
  display: flex; justify-content: flex-end; align-items: stretch;
  opacity: 0; transition: opacity var(--t-base) var(--ease);
}
.slideover-scrim.open { opacity: 1; }
.slideover-scrim.closing { opacity: 0; }
.slideover-panel {
  background: var(--panel); box-shadow: var(--shadow-lg), var(--card-hi);
  border-left: 1px solid var(--border); border-radius: var(--radius-lg) 0 0 var(--radius-lg);
  width: min(var(--so-width, 420px), 92vw); max-width: 100%; height: 100%; display: flex; flex-direction: column;
  position: relative; z-index: 115; transform: translateX(100%);
  transition: transform var(--t-base) var(--ease-spring);
}
.slideover-scrim.open .slideover-panel { transform: none; }
.slideover-grab { display: none; } /* shown only in the phone bottom-sheet posture, below */
.slideover-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 15px 18px; border-bottom: 1px solid var(--border); flex: 0 0 auto; }
.slideover-head h3 { margin: 0; font-size: var(--fs-lg); font-family: var(--font-serif); font-weight: var(--fw-semibold); letter-spacing: var(--ls-tight); }
.slideover-body { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 14px 18px; min-height: 0; }
.slideover-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 18px; border-top: 1px solid var(--border); flex: 0 0 auto; }
.slideover-foot:empty { display: none; }
@media (max-width: 1023px) {
  .slideover-panel { width: 80vw; }
}
@media (prefers-reduced-motion: reduce) {
  .slideover-scrim, .slideover-panel { transition: none; }
}
/* SPEC211 — chat quick-reply peek inside the slide-over */
.chat-peek-bar { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.chat-peek-ch { flex: 1 1 auto; min-width: 0; }
.chat-peek-list { display: flex; flex-direction: column; gap: 2px; }
.chat-peek-list .chat-msg { padding: 4px 4px; }
.chat-peek-composer { flex: 1 1 auto; width: 100%; }
.chat-peek-composer .chat-composer-row { display: flex; gap: 8px; align-items: flex-end; }
.chat-peek-composer .chat-input { flex: 1 1 auto; }
/* SPEC213 — record peek (job/client/lead glance in the slide-over) */
.peek-head { display: flex; align-items: center; gap: 10px; justify-content: space-between; margin-bottom: 14px; }
.peek-title { font-size: var(--fs-md); font-weight: var(--fw-bold); font-family: var(--font-serif); letter-spacing: var(--ls-tight); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.peek-facts { display: flex; flex-direction: column; }
.peek-fact { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; padding: 8px 0; border-bottom: 1px solid var(--border); font-size: var(--fs-caption); }
.peek-fact:last-child { border-bottom: none; }
.peek-fact-l { flex: 0 0 auto; color: var(--muted); }
.peek-fact-v { min-width: 0; text-align: right; font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.peek-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; width: 100%; }
/* SPEC214 — live health dot/pill (peeks, ⌘K rows, lists) */
.health-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.health-pill { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-meta); font-weight: var(--fw-semibold); }
.peek-healthline { margin: 2px 0 14px; }
.peek-head { margin-bottom: 10px; }
/* SPEC217 — listing performance (analytics), visually simple */
.sa-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; margin-bottom: 14px; }
.sa-stat { background: var(--subtle); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; }
.sa-big { font-size: 26px; font-weight: var(--fw-black); letter-spacing: -.5px; line-height: 1.1; }
.sa-stat-l { font-size: var(--fs-micro); color: var(--muted); margin-top: 2px; text-transform: uppercase; letter-spacing: .4px; font-weight: var(--fw-bold); }
.sa-stat-h { font-size: var(--fs-meta); color: var(--muted); margin-top: 3px; }
.sa-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 12px 14px; margin-bottom: 12px; background: var(--panel); }
.sa-card-h { font-size: var(--fs-micro); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin-bottom: 8px; }
.sa-media { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.sa-media-thumb { position: relative; aspect-ratio: 4 / 3; background-color: var(--hover); background-size: cover; background-position: center; border-radius: 8px; display: flex; align-items: center; justify-content: center; color: var(--muted); }
.sa-media-play { color: #fff; font-size: var(--fs-lg); text-shadow: 0 1px 4px rgba(0,0,0,.6); }
.sa-media-meta { display: flex; justify-content: space-between; align-items: baseline; margin-top: 4px; font-size: 11.5px; }
.sa-rank { font-weight: var(--fw-black); color: var(--muted); }
.sa-clicks { color: var(--text); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.sa-city { display: flex; align-items: center; gap: 10px; padding: 4px 0; font-size: 12.5px; }
.sa-city-name { flex: 0 0 110px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sa-city-bar { flex: 1; height: 7px; background: var(--hover); border-radius: 4px; overflow: hidden; }
.sa-city-fill { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
.sa-city-n { flex: 0 0 auto; color: var(--muted); font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); }
.sa-sources { display: flex; flex-direction: column; gap: 4px; }
.sa-source { display: flex; align-items: center; gap: 8px; font-size: var(--fs-caption); padding: 3px 0; }
.sa-source-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ok); flex: 0 0 auto; }
.sa-source-link { color: var(--accent); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.sa-source-clicks { color: var(--muted); font-size: var(--fs-meta); flex: 0 0 auto; }
.sa-insight { border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--radius); padding: 12px 14px; margin-bottom: 14px; background: var(--accent-soft); }
.sa-insight--hot { border-left-color: var(--ok); background: var(--ok-soft); }
.sa-insight--cool { border-left-color: var(--warn); background: var(--warn-soft); }
.sa-insight-h { font-weight: var(--fw-black); font-size: var(--fs-body); }
.sa-insight-n { font-size: 12.5px; color: var(--muted); margin-top: 4px; }
.sa-lead { display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.sa-lead:last-child { border-bottom: none; }
.sa-lead-main { flex: 1; min-width: 0; }
.sa-lead-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.sa-lead-name { font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.sa-lead-contact { font-size: var(--fs-meta); color: var(--accent); }
.sa-lead-when { font-size: var(--fs-micro); color: var(--faint); }
.sa-lead-msg { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.sa-lead-act { flex: 0 0 auto; }
@media (max-width: 560px) { .sa-stats, .sa-media { grid-template-columns: repeat(2, 1fr); } }
/* SPEC219 — Marketing Kit (lives in the slide-over): editable copy-ready blocks. */
.mkit-sub { margin-bottom: 14px; }
.mkit-addr { font-weight: var(--fw-black); font-size: 15px; color: var(--text); }
.mkit-hint { font-size: 12.5px; color: var(--muted); margin-top: 3px; line-height: var(--lh-normal); }
.mkit-card { background: var(--bg); border: 1px solid var(--border); border-radius: 12px; padding: 12px 12px 14px; margin-bottom: 12px; }
.mkit-card-h { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.mkit-card-l { font-weight: var(--fw-bold); font-size: 11.5px; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); }
.mkit-ta { width: 100%; box-sizing: border-box; resize: vertical; min-height: 56px; border: 1px solid var(--border); border-radius: 9px; padding: 10px 12px; font: inherit; font-size: 13.5px; line-height: 1.6; color: var(--text); background: var(--panel); white-space: pre-wrap; }
.mkit-ta:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft, rgba(14,124,90,.14)); }
.mkit-ai { margin-top: 8px; color: var(--accent); }
.mkit-more { margin: 2px 0 14px; }
.mkit-more > summary { cursor: pointer; font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--accent); padding: 6px 0; list-style: none; }
.mkit-more > summary::-webkit-details-marker { display: none; }
.mkit-more > summary::before { content: '▸ '; color: var(--muted); }
.mkit-more[open] > summary::before { content: '▾ '; }
.mkit-section-h { font-weight: var(--fw-bold); font-size: 11.5px; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); margin: 6px 0 8px; }
.mkit-actions { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 8px; }
#toast-zone { position: fixed; bottom: calc(20px + env(safe-area-inset-bottom, 0px)); left: 50%; transform: translateX(-50%); z-index: 200; display: flex; flex-direction: column; gap: 8px; align-items: center; }
/* SPEC208 — theme-aware toast (was hardcoded #1f2433/16px): a high-contrast ink chip
   that reads on both themes, on the body type scale. */
.toast {
  background: var(--chip-ink); color: var(--panel); padding: 10px 16px; border-radius: var(--radius); font-size: var(--fs-caption); font-weight: var(--fw-semibold);
  box-shadow: var(--shadow-lg); animation: modal-in var(--t-base) var(--ease-spring); display: flex; align-items: center; gap: 8px;
}
.toast svg { width: 16px; height: 16px; }
.toast.error { background: var(--danger); color: var(--on-danger); }
.toast.toast-action { display: flex; align-items: center; gap: 14px; padding-right: 8px; }
/* UI-FEEDBACK 07-30 — this is the UNDO button: the only recovery path for every
   App.softDelete, and it lives inside a toast that disappears after ~6 seconds. It
   rendered ~25px tall, well under the 44px minimum the rest of the app now meets — so
   the one control with a hard deadline and a data-loss consequence was the hardest to
   hit on a phone. min-height + centred content; padding unchanged so short toasts keep
   their shape. */
.toast-btn { background: color-mix(in srgb, currentColor 16%, transparent); color: inherit; border: none; border-radius: 7px; padding: 5px 12px;
  min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  font-size: 12.5px; font-weight: var(--fw-black); cursor: pointer; white-space: nowrap; }
.toast-btn:hover { background: color-mix(in srgb, currentColor 28%, transparent); }

/* ============ empty state ============ */
.empty-state { text-align: center; padding: 48px 20px; color: var(--muted); }
.empty-state .es-icon, .empty-state .es-ico { font-size: 32px; margin-bottom: 8px; line-height: 1; }
/* SPEC208 — when an empty state uses a line-icon instead of an emoji, render it large + soft
   (unscoped: some es-icon/es-ico divs render outside .empty-state) */
.es-icon svg, .es-ico svg { width: 34px; height: 34px; opacity: .75; color: var(--muted); }
.es-icon, .es-ico { display: flex; justify-content: center; }
.empty-state h4 { margin: 0 0 4px; color: var(--text); }
.empty-state p { margin: 0 0 14px; font-size: var(--fs-caption); }

/* ============ SPEC208 — line-icon system base ============ */
.icon { display: inline-flex; align-items: center; justify-content: center; vertical-align: -.18em; flex: 0 0 auto; line-height: 0; }
.icon svg { display: block; }
/* icons sit on the text baseline + inherit color in the common chrome */
.chip svg { width: 13px; height: 13px; }
.view-title svg { width: .92em; height: .92em; vertical-align: -.06em; }
.btn-primary:disabled { color: var(--muted); }

/* ============ SOP hub ============ */
.sops-layout { display: flex; gap: 16px; align-items: flex-start; }
.sops-side { flex: 0 0 264px; position: sticky; top: 0; }
.sops-main { flex: 1; min-width: 0; }
.doc-list { display: flex; flex-direction: column; gap: 1px; }
.doc-cat { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .5px; color: var(--faint); padding: 12px 10px 4px; }
.doc-item {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 7px; cursor: pointer;
  font-size: var(--fs-caption); font-weight: var(--fw-medium); color: var(--text);
}
.doc-item:hover { background: var(--hover); }
.doc-item.active { background: var(--accent-soft); color: var(--accent); font-weight: var(--fw-bold); }
.doc-item .doc-pin { margin-left: auto; font-size: var(--fs-micro); }
.editor-title {
  width: 100%; border: none; outline: none; font-size: 24px; font-weight: var(--fw-black); font-family: inherit;
  padding: 4px 0; background: transparent; color: var(--text); letter-spacing: -.3px;
}
.editor-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 6px 0 14px; border-bottom: 1px solid var(--border); margin-bottom: 14px; }
.editor-meta .right { margin-left: auto; display: flex; gap: 6px; align-items: center; }
.md-editor {
  width: 100%; min-height: 56vh; border: none; outline: none; resize: vertical; font-family: var(--mono);
  font-size: var(--fs-caption); line-height: 1.7; color: var(--text); background: transparent;
}
/* SPEC-DHTRACK D4 — the Job Tracker's row-density control (compact/comfortable/big
   — Vincent's "different views: compact, big view", already shipped as CONSOL-3)
   IS this shared .seg segmented control (the Display picker's "Row size" seg);
   there is no separate density feature to build this wave, only this shared
   component to restyle onto the token scale — radius-2 is the SAME 8px value,
   now named off the DHSHELL radius scale instead of a literal. */
.seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-2); overflow: hidden; }
.seg button { border: none; background: var(--panel); padding: 5px 12px; font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); cursor: pointer; font-family: inherit; transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease); }
.seg button:not(.active):hover { background: var(--hover); color: var(--text); }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* SPEC175 — selected segment uses the elegant accent-soft pill (solid accent reserved
   for .btn-primary). Text is --accent-hover (not --accent) so it clears AA on the soft
   fill in BOTH themes — dark #818cf8/#1e2030 = 5.4:1, light #4338ca/#eef2ff ≈ 6.5:1. */
.seg button.active { background: var(--accent-soft); color: var(--accent-hover); }
/* segmented controls glide on switch (snap → smooth highlight) */
.seg button, .subtab, .range-preset { transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease); }
@media (max-width: 640px) { .seg button { min-height: 44px; padding: 9px 12px; } }   /* SPEC126 — touch target */

/* ============ login screen ============ */
.login-screen { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; padding: 20px; background: linear-gradient(135deg, #11131f, #1e2235); }
.login-card { width: 100%; max-width: 360px; background: var(--panel); border-radius: 16px; box-shadow: var(--shadow-lg); padding: 26px 24px; }
.login-brand { display: flex; align-items: center; gap: 12px; }
.login-title { font-weight: var(--fw-black); font-size: var(--fs-lg); letter-spacing: .2px; }
.login-sub { color: var(--muted); font-size: 12.5px; }
.login-card .input { width: 100%; }
.login-err { color: var(--danger); font-size: 12.5px; margin-top: 10px; min-height: 16px; }
.login-foot { color: var(--faint); font-size: 11.5px; margin-top: 14px; text-align: center; }

/* TRUTH 2026-08-18 (§B) — the not-connected wall + the connection-wall additions to
   the existing sign-in wall (W1/W2). min-height: 44px on every new tappable control —
   TAPFLOOR_CEIL may only go DOWN (tests/suite.js), so nothing new here may add to it. */
.wall-body { color: var(--text); font-size: 13.5px; line-height: 1.55; margin: 14px 0 0; text-align: left; }
.wall-cta { width: 100%; min-height: 44px; justify-content: center; margin-top: 14px; }
.wall-cta-sm { min-height: 44px; width: 100%; justify-content: center; }
.wall-note { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); text-align: left; }
.wall-note .login-foot { text-align: left; margin: 0 0 8px; }
.wall-escape-row { margin-top: 16px; text-align: center; }
.wall-escape { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 6px 10px; background: transparent; border: none; color: var(--faint); font-size: 11.5px; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.wall-escape:hover { color: var(--muted); }

/* TRUTH 2026-08-18 (§A) — App.recordReachMark's quiet-by-default mark. Renders
   nothing for the happy path ('team'); these three cover 'device' / 'waiting' /
   'failed'. Text only, no chrome pictograph (ICONGRAM) — colour carries the state. */
.record-reach { font-size: 11px; font-weight: var(--fw-semibold); padding: 1px 6px; border-radius: 999px; white-space: nowrap; }
.record-reach-off { color: var(--muted); background: var(--subtle); }
.record-reach-offline { color: var(--warn-ink); background: var(--warn-soft); }
.record-reach-error { color: var(--danger-ink); background: var(--danger-soft); }

/* ============ markdown body ============ */
.markdown-body { font-size: var(--fs-body); line-height: 1.7; max-width: 760px; }
.markdown-body h1 { font-size: var(--fs-h1); margin: 18px 0 8px; letter-spacing: -.3px; }
.markdown-body h2 { font-size: var(--fs-lg); margin: 18px 0 6px; }
.markdown-body h3 { font-size: 15px; margin: 14px 0 4px; }
.markdown-body p { margin: 8px 0; }
.markdown-body ul, .markdown-body ol { margin: 8px 0; padding-left: 24px; }
.markdown-body li { margin: 3px 0; }
.markdown-body li.md-check { list-style: none; margin-left: -20px; }
.markdown-body li.md-check input { margin-right: 7px; accent-color: var(--accent); cursor: pointer; }
.markdown-body code { background: var(--hover); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; font-family: var(--mono); font-size: var(--fs-meta); }
.markdown-body pre { background: var(--code-bg); color: var(--code-text); border: 1px solid var(--border); padding: 12px 14px; border-radius: 8px; overflow-x: auto; }  /* SPEC208 — tokenized: light-gray code block in light mode, dark in dark mode */
.markdown-body pre code { background: none; border: none; color: inherit; padding: 0; }
.markdown-body blockquote { margin: 8px 0; padding: 6px 14px; border-left: 3px solid var(--accent); background: var(--accent-soft); border-radius: 0 6px 6px 0; color: var(--accent); }
.markdown-body hr { border: none; border-top: 1px solid var(--border); margin: 16px 0; }
.markdown-body a { font-weight: var(--fw-semibold); }

/* ============ bar chart ============ */
.bar-chart { display: flex; align-items: flex-end; gap: 10px; height: 150px; padding-top: 18px; }
.bar-wrap { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 5px; height: 100%; justify-content: flex-end; }
.bar { width: 100%; max-width: 46px; background: linear-gradient(180deg, var(--accent), var(--accent-hover)); border-radius: 6px 6px 2px 2px; min-height: 3px; }
.bar-val { font-size: 10.5px; font-weight: var(--fw-bold); color: var(--muted); }
.bar-label { font-size: var(--fs-micro); color: var(--faint); font-weight: var(--fw-semibold); }

/* ============ misc lists ============ */
.list-row { display: flex; align-items: center; gap: 10px; padding: 9px 4px; border-bottom: 1px solid var(--bg-2); font-size: var(--fs-caption); }
.list-row:last-child { border-bottom: none; }
.list-row .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row .grow > div { overflow: hidden; text-overflow: ellipsis; }
.list-row .muted { color: var(--muted); font-size: var(--fs-meta); }
.overdue { color: var(--danger); font-weight: var(--fw-bold); }
.muted { color: var(--muted); }
.faint { color: var(--faint); }
.mono { font-family: var(--mono); font-size: var(--fs-meta); }
.nowrap { white-space: nowrap; }
.mt0 { margin-top: 0; } .mb0 { margin-bottom: 0; }
.settings-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
.settings-grid .full { grid-column: 1 / -1; }

/* WAVE3S 2026-09-02 (SPEC-WAVE3-SETTINGS §S1) — the design's left section rail
   (design-handoff/VVT-Settings.dc.html: a sticky ~212px list, one label + count
   per section, plus an "Elsewhere → Booking setup" card at the bottom). Each
   section stays a plain `.settings-grid` (unchanged 2-col card grid) — core.js
   shows one at a time via element.style.display, never the `hidden` attribute:
   an author rule with equal specificity to `[hidden]` (like `.settings-grid{
   display:grid}`) would win the cascade and silently defeat it. */
.settings-shell { display: flex; align-items: flex-start; gap: 20px; }
.settings-rail { position: sticky; top: 16px; flex: 0 0 212px; display: flex; flex-direction: column; gap: 3px; }
.settings-rail-btn { display: flex; align-items: baseline; gap: 9px; width: 100%; min-height: 44px; padding: 11px 13px; border: 0; border-radius: 9px; background: transparent; font-family: inherit; text-align: left; cursor: pointer; color: var(--muted); }
.settings-rail-btn:hover { background: var(--hover); }
.settings-rail-btn.active { background: var(--panel); box-shadow: 0 1px 2px rgba(16,24,40,.06); }
.settings-rail-label { flex: 1; font-size: 13.5px; font-weight: 500; color: inherit; letter-spacing: -.01em; }
.settings-rail-btn.active .settings-rail-label { font-weight: 700; color: var(--text); }
.settings-rail-count { font-size: 11.5px; color: var(--border-strong); font-variant-numeric: tabular-nums; }
.settings-rail-btn.active .settings-rail-count { color: var(--accent); }
.settings-elsewhere { display: block; margin-top: 12px; padding: 12px 13px; border-radius: 10px; background: var(--panel); border: 1px solid var(--border); text-decoration: none; cursor: pointer; }
.settings-elsewhere:hover { border-color: var(--border-strong); }
.settings-elsewhere-label { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--border-strong); }
.settings-elsewhere-body { margin-top: 6px; font-size: 12.5px; line-height: 1.6; color: var(--accent); }
.settings-body { flex: 1; min-width: 0; }
@media (max-width: 1023px) {
  /* map-settings.json flagged the old flat grid squeezing between 861-1023px;
     the rail has the same problem plus nowhere to go beside a narrowed body —
     stack it above instead, as a horizontally-scrollable row of chips. */
  .settings-shell { flex-direction: column; }
  .settings-rail { position: static; flex: none; width: 100%; flex-direction: row; overflow-x: auto; gap: 6px; padding-bottom: 4px; }
  .settings-rail-btn { flex: 0 0 auto; white-space: nowrap; }
  .settings-elsewhere { flex: 0 0 auto; margin-top: 0; white-space: nowrap; }
}
.row-line { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; flex-wrap: wrap; }
.color-swatch { width: 26px; height: 26px; padding: 0; border: 1px solid var(--border-strong); border-radius: 6px; cursor: pointer; background: none; }
pre.code-snippet { background: #14161f; color: #e7eaf2; font-family: var(--mono); font-size: 11.5px; padding: 12px; border-radius: 8px; overflow-x: auto; user-select: all; }  /* SPEC198 — fixed light ink (was var(--border-strong) → near-invisible in dark) */

/* ============ responsive ============ */
/* SPEC86 — mobile top bar (☰ + title + search). Hidden on desktop. */
#mobile-topbar { display: none; }
#mobile-topbar { position: fixed; top: 0; left: 0; right: 0; z-index: 50; height: 52px; align-items: center; gap: 10px; padding: 0 12px; background: var(--sidebar-bg); border-bottom: 1px solid var(--sidebar-border); }
#mobile-topbar .mt-burger, #mobile-topbar .mt-search { width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--sidebar-border); background: rgba(255,255,255,.05); border-radius: 9px; font-size: 19px; line-height: 1; cursor: pointer; color: var(--sidebar-active); }
#mobile-topbar .mt-search { font-size: var(--fs-md); color: var(--sidebar-text); }
/* D9(a) — was a static 15px/black "VVT Hub" literal; now carries "Section › View"
   (App.refreshMobileTitle, core.js) so it can run longer — smaller/semibold + an
   end-ellipsis so it never wraps or overflows the bar. (Middle-ellipsis would need
   JS text-measurement; end-ellipsis is the standard $0 CSS pattern and reads fine
   here since the SECTION word is always the leading, most-recognizable part.) */
#mobile-topbar .mt-title { flex: 1; font-weight: var(--fw-semibold); font-size: 13.5px; color: var(--sidebar-active); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
#nav-backdrop { display: none; }

@media (max-width: 860px) {
  /* SPEC86 — the sidebar becomes a LABELED off-canvas drawer (no more bare-icon rail).
     It slides in over a backdrop from the ☰ button, so every destination keeps its
     name and is easy to scan + tap. Desktop is untouched (rules are inside this query). */
  #app { display: block; }
  #mobile-topbar { display: flex; height: calc(52px + env(safe-area-inset-top, 0px)); padding-top: env(safe-area-inset-top, 0px); }
  /* SPEC-DHSHELL D3 — the desktop 54px header disappears at mobile; the drawer path
     (#mobile-topbar + nav-open) is the one true mobile shell, unchanged by this wave.
     #sync-pill/search/notif-bell/view-as all still exist and still work (see their JS/
     CSS) — they just render inside #topheader, which is hidden here like it always
     conceptually was hidden before this wave existed. */
  #topheader { display: none; }

  #sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; z-index: 70;
    width: 270px; max-width: 84vw; flex: 0 0 auto; flex-direction: column;
    padding: 12px 10px; overflow-y: auto;
    transform: translateX(-102%); transition: transform .24s var(--ease-spring);   /* DESIGNA 2026-08-19 — the most-felt motion on mobile ran on default `ease`; settle, don't snap */
    box-shadow: 6px 0 34px rgba(0,0,0,.45);
  }
  body.nav-open #sidebar { transform: translateX(0); }
  #nav-backdrop { display: block; position: fixed; inset: 0; z-index: 65; background: rgba(0,0,0,.5); opacity: 0; visibility: hidden; transition: opacity .24s var(--ease); }   /* DESIGNA 2026-08-19 — token, not default ease */
  body.nav-open #nav-backdrop { opacity: 1; visibility: visible; }

  /* drawer shows the FULL labeled, grouped nav + comfy touch targets — SPEC-DHSHELL D2
     reverted back to the pre-rail row layout (the icon-over-label column above is a
     desktop-only rail treatment). */
  .brand { flex-direction: row; align-items: center; gap: 10px; padding: 6px 8px 16px; }
  .brand-text { display: block; }
  #nav { flex-direction: column; }
  .nav-group { flex-direction: column; }
  .nav-group + .nav-group { margin-top: 11px; margin-left: 0; }
  .nav-group-label { display: block; }
  .nav-item { flex-direction: row; width: auto; text-align: left; padding: 11px 12px; font-size: 14.5px; }
  .nav-item span:not(.nav-ico) { display: inline; -webkit-line-clamp: unset; max-width: none; }
  .nav-badge { position: static; top: auto; right: auto; margin-left: auto; }
  .sidebar-footer { margin-top: auto; align-items: stretch; flex-direction: column; }
  .sync-pill .sync-label { display: inline; }
  .help-btn { flex-direction: row; width: 100%; justify-content: flex-start; }
  .help-btn span:not(.help-q):not(.notif-bell-badge) { display: inline; max-width: none; white-space: normal; }

  #main { padding-top: calc(52px + env(safe-area-inset-top, 0px)); }
  #view { padding: 16px 14px 50px; }
  .form-grid, .grid-2, .grid-3, .settings-grid { grid-template-columns: 1fr; }
  .sops-layout { flex-direction: column; }
  .sops-side { position: static; flex: 1 1 auto; width: 100%; }

  /* C2 — the sidebar bell/chat badges (.notif-bell-badge / .nav-badge) live inside this
     off-canvas drawer, so a phone had NO unread indicator anywhere. App.refreshNotifBadge
     / App.refreshChatBadge (core.js) now mirror the same .notif-bell-badge span onto the
     burger and the tab bar; it needs a positioned ancestor + a sane offset here since the
     base rule (.notif-bell-badge { position: absolute; top: -7px; right: -8px; }, tuned
     for the bell's .help-q anchor) would float above these hosts' top edge otherwise.
     #tabbar .tb-item already declares position: relative — only the burger needs it. */
  #mobile-topbar .mt-burger { position: relative; overflow: visible; }
  #tabbar .tb-more .notif-bell-badge,
  #tabbar .tb-item[data-tab="chat"] .notif-bell-badge { top: 4px; right: 18%; }
  #tabbar .tb-item[data-tab="chat"] .notif-bell-badge { background: #475569; }
  #tabbar .tb-item[data-tab="chat"] .notif-bell-badge.mention { background: var(--danger); }
}

/* ============================================================
   SPEC2 additions — Goals rings · Calendar · Insight bars
   (appended block; existing rules above untouched)
   ============================================================ */

/* ---- goal rings (dashboard "This month's goals") ---- */
.goal-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.goal-card {
  display: flex; align-items: center; gap: 14px;
  border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; background: var(--panel);
}
.ring { width: 92px; height: 92px; flex: 0 0 92px; position: relative; transform: rotate(-90deg); }
.ring-track { fill: none; stroke: var(--bg-2); }
.ring-fill { fill: none; stroke: var(--accent); stroke-linecap: round; transition: stroke-dasharray .4s ease; }
.ring-fill.ok { stroke: var(--ok); }
.ring-center {
  position: absolute; inset: 0; transform: rotate(90deg);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-md); font-weight: var(--fw-black); color: var(--text);
}
.goal-meta { min-width: 0; }
.goal-meta .goal-name { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); text-transform: uppercase; letter-spacing: .3px; }
.goal-meta .goal-value { font-size: 20px; font-weight: var(--fw-black); letter-spacing: -.3px; margin-top: 2px; }
.goal-meta .goal-of { font-size: var(--fs-meta); color: var(--faint); }
.goal-pace { font-size: 11.5px; color: var(--muted); margin-top: 4px; }
.goal-pace.ok { color: var(--ok); font-weight: var(--fw-bold); }

/* ---- calendar ---- */
.cal-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.cal-toolbar .cal-month { font-size: var(--fs-md); font-weight: var(--fw-black); letter-spacing: -.2px; }
.cal-toolbar .spacer { flex: 1; }
.cal-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-bottom: 6px; }
.cal-dow > div { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--faint); text-transform: uppercase; letter-spacing: .4px; text-align: center; padding: 2px 0; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-cell {
  min-height: 92px; max-height: 150px; overflow-y: auto; border: 1px solid var(--border);
  border-radius: var(--radius-sm); background: var(--panel); padding: 5px 6px; display: flex; flex-direction: column; gap: 3px;
}
.cal-cell.other-month { background: var(--subtle); opacity: .6; }
.cal-cell.today { outline: 2px solid var(--accent); outline-offset: -2px; }
.cal-daynum { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--muted); }
.cal-cell.today .cal-daynum { color: var(--accent); }
.cal-event {
  font-size: var(--fs-micro); font-weight: var(--fw-semibold); padding: 2px 6px; border-radius: 5px; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.4;
  border-left: 3px solid var(--accent);
}
.cal-event:hover { filter: brightness(.97); }
.cal-more { font-size: 10.5px; font-weight: var(--fw-bold); color: var(--muted); cursor: pointer; padding: 1px 4px; }
.cal-more:hover { color: var(--accent); }
/* week view: taller columns, full event list (SPEC19 §D) */
.cal-cell-week { min-height: 180px; max-height: 420px; }
.cal-empty-day { font-size: var(--fs-micro); color: var(--faint); text-align: center; padding: 6px 0; }
/* SPEC21 §C — async weather chip on upcoming shoots (today..+7d) */
.cal-wx {
  display: inline-block; margin-left: 5px; font-size: 10px; font-weight: var(--fw-bold);
  color: var(--muted); white-space: nowrap; vertical-align: baseline;
}
.cal-wx-loading { opacity: .45; }
.cal-event .cal-wx { color: inherit; opacity: .85; }

/* ---- insight bars (dashboard "Upsell & client insights") ---- */
.insight-bars { display: flex; flex-direction: column; gap: 8px; }
.bar-row { display: flex; align-items: center; gap: 10px; font-size: 12.5px; }
.bar-row .lbl { flex: 0 0 120px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--fw-semibold); }
.bar-row .track { flex: 1; height: 8px; border-radius: 999px; background: var(--bg-2); overflow: hidden; min-width: 40px; }
.bar-row .fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent), var(--accent-hover)); min-width: 2px; transition: width .3s; }
.bar-row .val { flex: 0 0 auto; color: var(--muted); font-weight: var(--fw-bold); white-space: nowrap; }

@media (max-width: 860px) {
  .goal-grid { grid-template-columns: 1fr; }
  .cal-dow > div { font-size: 9.5px; }
  .cal-cell { min-height: 68px; padding: 3px 4px; }
  .cal-daynum { font-size: 10.5px; }
  .cal-event { font-size: 9.5px; padding: 1px 4px; }
  .bar-row .lbl { flex-basis: 84px; }
}

/* ============================================================
   SPEC3 additions — Quick Search · Sidebar search button
   (appended block; existing rules above untouched)
   ============================================================ */

/* ---- sidebar "Search…" button (under .brand) ---- */
.sidebar-search {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; margin: 0 0 8px; border-radius: 8px;
  border: 1px solid var(--sidebar-border); background: rgba(255,255,255,.05);
  color: var(--sidebar-text); cursor: pointer; font-family: inherit;
  font-size: var(--fs-caption); font-weight: var(--fw-medium); text-align: left;
}
.sidebar-search:hover { background: rgba(255,255,255,.1); color: var(--sidebar-active); }
.sidebar-search .ss-mag { font-size: 15px; line-height: 1; flex: 0 0 auto; }
.sidebar-search .ss-label { flex: 1; min-width: 0; }
.sidebar-search .ss-kbd {
  font-family: var(--mono); font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--sidebar-text);
  background: rgba(255,255,255,.08); border: 1px solid var(--sidebar-border);
  border-radius: 5px; padding: 1px 6px; flex: 0 0 auto;
}

/* ---- quick search overlay (command palette) ---- */
.qs-overlay {
  position: fixed; inset: 0; z-index: 250; background: rgba(16,18,28,.5);
  display: flex; align-items: flex-start; justify-content: center;
  padding: 12vh 16px 16px; overflow-y: auto;
}
.qs-panel {
  width: 100%; max-width: 560px; background: var(--panel); border-radius: 14px;
  box-shadow: var(--shadow-lg); overflow: hidden; animation: modal-in var(--t-base) var(--ease-spring);
}
.qs-inputwrap {
  display: flex; align-items: center; gap: 10px; padding: 14px 16px;
  border-bottom: 1px solid var(--border);
}
.qs-mag { font-size: var(--fs-lg); color: var(--faint); line-height: 1; flex: 0 0 auto; }
.qs-input {
  flex: 1; min-width: 0; border: none; outline: none; background: transparent;
  font-family: inherit; font-size: var(--fs-md); color: var(--text);
}
.qs-esc {
  font-family: var(--mono); font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--faint);
  background: var(--hover); border: 1px solid var(--border); border-radius: 5px; padding: 2px 6px; flex: 0 0 auto;
}
.qs-list { max-height: 56vh; overflow-y: auto; padding: 6px; }
/* CMDK-5 — drill-in (Tab-to-act) transition; the prefers-reduced-motion net (line ~160) flattens it */
.qs-list.qs-slide { animation: qs-slide-in .16s var(--ease, ease) both; }
@keyframes qs-slide-in { from { opacity: .35; transform: translateX(7px); } to { opacity: 1; transform: none; } }
.qs-hint { padding: 22px 14px; text-align: center; color: var(--faint); font-size: var(--fs-caption); }
.qs-group {
  font-size: 10.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .5px;
  color: var(--faint); padding: 10px 10px 4px;
}
.qs-row {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px;
  cursor: pointer;
}
.qs-row.active { background: var(--accent-soft); }
/* SPEC211 — the keycap hint on a view row (e.g. "G J"); quiet, hidden on small phones */
.qs-kbd { flex: 0 0 auto; display: inline-block; padding: 2px 7px; border-radius: 5px; font-family: var(--mono);
  font-size: var(--fs-micro); background: var(--hover); border: 1px solid var(--border); color: var(--muted); white-space: nowrap; letter-spacing: .5px; }
@media (max-width: 480px) { .qs-kbd { display: none; } }
.qs-type {
  flex: 0 0 auto; font-size: 9.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); background: var(--hover); border-radius: 5px; padding: 2px 6px; min-width: 46px; text-align: center;
}
.qs-type-job { color: var(--accent); background: var(--accent-soft); }
.qs-type-client { color: var(--ok); background: var(--ok-soft); }
.qs-type-sop { color: var(--warn); background: var(--warn-soft); }
/* ⌘K command verbs — soft accent badge; the glyph centers in the chip */
.qs-type-command { color: var(--accent); background: var(--accent-soft); }
.qs-type svg { display: block; margin: 0 auto; }
.qs-text { flex: 1; min-width: 0; }
.qs-label { font-size: 13.5px; font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qs-sub { font-size: var(--fs-meta); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qs-row .chip { flex: 0 0 auto; }

@media (max-width: 860px) {
  .sidebar-search { width: auto; margin: 0 4px 0 0; padding: 8px; flex: 0 0 auto; }
  .sidebar-search .ss-label, .sidebar-search .ss-kbd { display: none; }
}
@media (max-width: 480px) {
  .qs-overlay { padding: 8vh 10px 10px; }
  .qs-input { font-size: 15px; }
}

/* ============ roles, comments, login extras (SPEC4) ============ */

/* --- team & roles (Settings) --- */
.roles-list { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.role-row {
  display: flex; align-items: center; gap: 8px; padding: 7px 4px;
  border-bottom: 1px solid var(--bg-2);
}
.role-row:last-child { border-bottom: none; }
.role-email { flex: 1; min-width: 0; font-size: var(--fs-caption); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.role-select { flex: 0 0 auto; min-width: 96px; }
.role-badge {
  display: inline-flex; align-items: center; flex: 0 0 auto;
  font-size: 10.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px;
  color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 2px 9px;
}
.role-badge.crew { color: var(--muted); background: var(--hover); }

/* --- job comments --- */
.cmt-wrap { display: flex; flex-direction: column; gap: 10px; }
.cmt-list {
  max-height: 240px; overflow-y: auto; display: flex; flex-direction: column; gap: 10px;
  padding: 4px 2px;
}
.cmt-empty { color: var(--faint); font-size: var(--fs-caption); padding: 14px 4px; text-align: center; }
.cmt-row {
  background: var(--subtle); border: 1px solid var(--border); border-radius: 9px;
  padding: 8px 11px;
}
.cmt-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 3px; }
.cmt-author { font-size: 12.5px; font-weight: var(--fw-bold); color: var(--text); }
.cmt-time { font-size: var(--fs-micro); color: var(--faint); }
.cmt-text { font-size: var(--fs-caption); color: var(--text); white-space: pre-wrap; word-break: break-word; }
.cmt-box { display: flex; align-items: flex-end; gap: 8px; }
.cmt-box .cmt-input { flex: 1; min-width: 0; resize: vertical; }
.cmt-box .btn { flex: 0 0 auto; }

/* --- collapsible section (SPEC28 A3 · App.collapsibleSection) ---
   Structure: .collapse > button.collapse-head ( .collapse-title + .collapse-chevron )
   followed by .collapse-body. Open state is signalled by an `.open` class on the
   wrapper and/or head, or aria-expanded="true" on the head button — all handled. */
.collapse { border-top: 1px solid var(--border); margin-top: 4px; }
.collapse:first-child { border-top: none; margin-top: 0; }
.collapse-head {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 11px 2px; border: none; background: none; cursor: pointer;
  font-family: inherit; font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--text);
  text-align: left; letter-spacing: -.1px;
}
.collapse-head:hover { color: var(--accent); }
.collapse-head:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.collapse-title { flex: 1; min-width: 0; }
.collapse-chevron {
  flex: 0 0 auto; color: var(--faint); font-size: var(--fs-meta); line-height: 1;
  /* default (collapsed) points right; rotates down when open */
  transition: transform .15s ease; transform: rotate(-90deg);
}
.collapse.open > .collapse-head .collapse-chevron,
.collapse-head.open .collapse-chevron,
.collapse-head[aria-expanded="true"] .collapse-chevron { transform: none; }
.collapse-body { padding: 0 2px 12px; }
/* collapsed body is hidden by JS (display:none) or by these fallbacks */
.collapse:not(.open) > .collapse-body[hidden],
.collapse-head[aria-expanded="false"] + .collapse-body[hidden] { display: none; }
/* the wrapped section already carries its own borderTop in the job modal —
   neutralize the doubled rule so the collapse owns the divider */
.collapse-body > .full { border-top: none !important; padding-top: 0 !important; margin-top: 0 !important; }

/* --- login extras --- */
/* GOLIVE 2026-08-07 — the row can hold TWO links ("Forgot password?" +
   "Use a one-time code instead") and text-align:center alone ran them together with no
   space: "Forgot password?Use a one-time code instead". Flex + gap separates them at any
   width and wraps cleanly on a phone; a single link still centres exactly as before. */
.login-linkrow { margin-top: 12px; display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 6px 16px; text-align: center; }
.login-link { font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--accent); cursor: pointer; }
.login-link:hover { text-decoration: underline; }

@media (max-width: 860px) {
  .role-row { flex-wrap: wrap; }
  .role-email { flex: 1 1 100%; }
  .cmt-list { max-height: 200px; }
}

/* ============================================================
   SPEC5 additions — Finances (expenses · profit · forecast)
   (appended block; existing rules above untouched)
   ============================================================ */

/* profit/loss number coloring (green ≥ 0, red < 0) */
/* LAW-A 2026-08-15 (#21) — was var(--ok)/var(--danger) (fill colours meant for swatches/
   dots, not body text). Measured on --panel (light): --ok 3.91:1 (FAIL AA) / --ok-ink
   5.69:1 (PASS); --danger 4.83:1 (borderline PASS) / --danger-ink 6.57:1 (PASS, more
   headroom). Dark theme unaffected — both ink tokens alias their fill colour there
   (--ok 7.39:1, --danger 5.55:1 on dark --panel), so this is a light-mode-only fix that
   costs nothing on dark. Same pattern already shipped at styles.css:647-649 (.chip.is-ok
   etc). */
.fin-pos { color: var(--ok-ink); font-weight: var(--fw-bold); }
.fin-neg { color: var(--danger-ink); font-weight: var(--fw-bold); }

/* compact finance/profit table — reuses .table look but right-aligns numbers */
.fin-table { width: 100%; border-collapse: collapse; font-size: var(--fs-caption); }
.fin-table th {
  text-align: left; font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); padding: 7px 10px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.fin-table td { padding: 7px 10px; border-bottom: 1px solid var(--bg-2); vertical-align: middle; }
.fin-table tbody tr:last-child td { border-bottom: none; }
.fin-table .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.fin-table th.num { text-align: right; }
/* profit cell: sign-colored number + a small fixed-width bar, right-aligned so the
   figure and bar never overlap inside the nowrap .num column */
.fin-table .fin-bar-cell { display: inline-flex; align-items: center; gap: 8px; justify-content: flex-end; width: 100%; }
.databar.db-mini { width: 64px; min-width: 64px; flex: 0 0 auto; }

/* two-row revenue-vs-expenses mini chart for "profit over time" */
.fin-months { display: flex; align-items: stretch; gap: 10px; }
.fin-month {
  flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center;
  gap: 4px; justify-content: flex-end; text-align: center;
}
.fin-month .fin-bars { display: flex; align-items: flex-end; gap: 3px; height: 120px; }
.fin-month .fin-bar { width: 14px; border-radius: 4px 4px 2px 2px; min-height: 2px; }
.fin-month .fin-bar.rev { background: linear-gradient(180deg, var(--accent), var(--accent-hover)); }
.fin-month .fin-bar.exp { background: linear-gradient(180deg, color-mix(in srgb, var(--danger) 60%, var(--card)), var(--danger)); }
.fin-month .fin-mlabel { font-size: var(--fs-micro); color: var(--faint); font-weight: var(--fw-semibold); }
.fin-month .fin-mprofit { font-size: var(--fs-micro); font-weight: var(--fw-bold); }
.fin-legend { display: flex; gap: 14px; align-items: center; font-size: 11.5px; color: var(--muted); margin-bottom: 12px; }
.fin-legend .lg { display: inline-flex; align-items: center; gap: 5px; }
.fin-legend .sw { width: 10px; height: 10px; border-radius: 3px; }
.fin-legend .sw.rev { background: var(--accent); }
.fin-legend .sw.exp { background: var(--danger); }

@media (max-width: 860px) {
  .fin-month .fin-bars { height: 92px; }
  .fin-month .fin-bar { width: 11px; }
}

/* ============================================================
   SPEC6 — job costing: labor lines · cost summary · rate settings
   (appended; existing rules above untouched)
   ============================================================ */

/* one editable labor line in the job-modal costing block: who + hours + cost
   + remove. (Rendered by tracker.js in Phase B; styles live here.) */
.labor-line {
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  padding: 5px 0; border-bottom: 1px solid var(--bg-2);
}
.labor-line:last-of-type { border-bottom: none; }
.labor-line .ll-who { flex: 1 1 auto; min-width: 0; }
.labor-line .ll-hours { width: 78px; flex: 0 0 78px; }
.labor-line .ll-cost { width: 72px; flex: 0 0 72px; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--muted); font-size: var(--fs-caption); white-space: nowrap; }
.labor-line .ll-x { flex: 0 0 auto; }

/* label/value rows for the costing summary (labor, expenses, total, profit…) */
.cost-summary { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.cost-summary .cs-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: var(--fs-caption); padding: 3px 0;
}
.cost-summary .cs-row.total { border-top: 1px solid var(--border); margin-top: 2px; padding-top: 7px; font-weight: var(--fw-semibold); }
.cost-summary .cs-label { color: var(--muted); }
.cost-summary .cs-val { font-variant-numeric: tabular-nums; white-space: nowrap; }
.cost-note { font-size: 11.5px; color: var(--faint); margin-top: 8px; line-height: 1.4; }

/* Settings → Rates & estimating */
.rate-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; }
.rate-row .rate-name { flex: 1 1 auto; min-width: 0; font-weight: var(--fw-semibold); }
.tpl-list { display: flex; flex-direction: column; gap: 2px; }
.tpl-row { display: flex; align-items: center; gap: 8px; padding: 3px 0; }
.tpl-row .tpl-name { flex: 1 1 auto; min-width: 0; font-size: var(--fs-caption); }
.tpl-row .tpl-input { width: 70px; flex: 0 0 70px; }
.tpl-row.tpl-head { padding-bottom: 2px; }
.tpl-row.tpl-head .tpl-h { width: 70px; flex: 0 0 70px; font-size: var(--fs-micro); text-transform: uppercase;
  letter-spacing: .4px; color: var(--faint); font-weight: var(--fw-bold); text-align: center; }

@media (max-width: 860px) {
  .labor-line { flex-wrap: wrap; }
  .labor-line .ll-who { flex: 1 1 100%; }
  .tpl-row .tpl-input, .tpl-row.tpl-head .tpl-h { width: 56px; flex-basis: 56px; }
}

/* ============================================================
   SPEC9 — Retainers hub + shared Brand Profile editor
   (used by retainers.js and clients.js; foundation classes)
   ============================================================ */

/* shared brand-profile editor (App.renderBrandEditor) */
.brand-editor { display: flex; flex-direction: column; gap: 18px; }
.brand-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; }
.brand-grid .brand-field.full { grid-column: 1 / -1; }
.brand-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.brand-field > label { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--muted); }
.brand-field .textarea { resize: vertical; min-height: 58px; }
.brand-field .asset-open { font-size: 11.5px; font-weight: var(--fw-semibold); margin-top: 2px; }

.brand-subhead {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--text); margin-bottom: 8px;
}
.brand-hint { font-size: 11.5px; color: var(--faint); margin: 0 0 10px; line-height: 1.45; }

/* end-card contact block — grouped, lightly tinted to read as one unit */
.endcard-block, .assets-block {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--subtle); padding: 12px 14px;
}
.endcard-grid { gap: 10px 14px; }

/* brand asset link rows (label + url + open + remove) */
.asset-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.asset-link { display: flex; align-items: center; gap: 8px; }
.asset-link .asset-label { flex: 0 0 34%; min-width: 0; }
.asset-link .asset-url { flex: 1 1 auto; min-width: 0; }
.asset-link .asset-go { flex: 0 0 auto; white-space: nowrap; }
.asset-link .asset-go-ph { flex: 0 0 auto; color: var(--faint); padding: 0 6px; }
.asset-link .asset-x { flex: 0 0 auto; }

/* social stats before/after table */
.social-table { width: 100%; border-collapse: collapse; font-size: var(--fs-caption); }
.social-table th {
  text-align: left; font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); padding: 7px 10px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.social-table td { padding: 7px 10px; border-bottom: 1px solid var(--bg-2); vertical-align: middle;
  font-variant-numeric: tabular-nums; }
.social-table tbody tr:last-child td { border-bottom: none; }

/* script pipeline rows */
.script-row {
  display: flex; align-items: center; gap: 10px; padding: 9px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel);
}
.script-row .script-title { flex: 1 1 auto; min-width: 0; font-weight: var(--fw-semibold);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.script-row .script-meta { color: var(--faint); font-size: var(--fs-meta); white-space: nowrap; }

/* retainer roster card */
.retainer-card {
  display: flex; flex-direction: column; gap: 8px; padding: 14px 16px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel);
  box-shadow: var(--shadow); cursor: pointer; transition: border-color .12s, box-shadow .12s;
}
.retainer-card:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }
.retainer-card .rc-name { font-size: 15px; font-weight: var(--fw-bold); }
.retainer-card .rc-company { font-size: 12.5px; color: var(--muted); }
.retainer-card .rc-meta { display: flex; flex-wrap: wrap; gap: 6px 14px;
  font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.retainer-card .rc-fee { font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; }

/* social delta arrows (▲/▼) — green up, red down */
.delta-up { color: var(--ok); font-weight: var(--fw-bold); }
.delta-down { color: var(--danger); font-weight: var(--fw-bold); }

@media (max-width: 860px) {
  .brand-grid { grid-template-columns: 1fr; }
  .asset-link { flex-wrap: wrap; }
  .asset-link .asset-label, .asset-link .asset-url { flex: 1 1 100%; }
}

/* ============ per-service production progress (SPEC10) ============
   Operational, NOT money — shown to everyone incl. crew. */

/* the "Service progress" section in the job modal */
.svc-prog { display: flex; flex-direction: column; gap: 8px; }
.svc-prog-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.svc-prog-head .svc-prog-label { font-size: 12.5px; font-weight: var(--fw-bold); color: var(--text); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* one service row: name on the left, stage picker on the right */
.svc-step { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-top: 1px solid var(--border); }
.svc-step:first-of-type { border-top: none; }
.svc-step .svc-step-name { flex: 1 1 auto; min-width: 0; font-size: var(--fs-caption); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.svc-step .svc-step-select { flex: 0 0 auto; width: 140px; max-width: 45%; }

/* compact mini progress bar for the table cell / kanban card / modal head.
   Reuses the .progress track look; the fill is green (delivered share). */
.svc-bar { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; vertical-align: middle; }
.svc-bar .svc-bar-track { height: 5px; width: 54px; border-radius: 999px; background: var(--border); overflow: hidden; flex: 0 0 auto; }
.svc-bar .svc-bar-fill { height: 100%; border-radius: 999px; background: var(--ok); transition: width .25s; }
.svc-bar .svc-bar-text { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--muted); font-variant-numeric: tabular-nums; }

/* dashboard "Production pipeline" card — one row per stage with a bar sized
   to that stage's share of all in-flight service-pieces */
.prod-pipeline { display: flex; flex-direction: column; gap: 9px; }
.prod-pipeline .pp-row { display: grid; grid-template-columns: 88px 1fr 28px; align-items: center; gap: 10px; }
.prod-pipeline .pp-label { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prod-pipeline .pp-track { height: 8px; border-radius: 999px; background: var(--bg-2); overflow: hidden; }
.prod-pipeline .pp-fill { height: 100%; border-radius: 999px; transition: width .25s; min-width: 2px; }
.prod-pipeline .pp-count { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--text); text-align: right; font-variant-numeric: tabular-nums; }
.prod-pipeline .pp-empty { font-size: 12.5px; color: var(--muted); padding: 4px 0; }

@media (max-width: 480px) {
  .svc-step { flex-wrap: wrap; }
  .svc-step .svc-step-select { width: 100%; max-width: none; }
  .prod-pipeline .pp-row { grid-template-columns: 76px 1fr 26px; gap: 8px; }
}

/* ============ birthdays/gifts + QA grading (SPEC11) ============
   Admin-only surfaces. The view files mostly inline-style these for resilience;
   these classes give the required structure/spacing and a mobile-friendly
   fallback. Inline styles (e.g. the QA segmented control's selected colors)
   intentionally win where set. */

/* QA scorecard rubric in the job modal: one row per factor + a 1–5 control */
.qa-rubric { display: flex; flex-direction: column; gap: 6px; }
.qa-factor { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 4px 0; }
.qa-factor .qa-factor-name { flex: 1 1 160px; min-width: 0; font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; }
/* 1–5 segmented control */
.qa-score { display: inline-flex; gap: 3px; flex: 0 0 auto; }
.qa-score .qa-score-btn { width: 30px; height: 28px; line-height: 1; padding: 0; font: inherit; font-size: 12.5px;
  font-weight: var(--fw-bold); cursor: pointer; border: 1px solid var(--border-strong); border-radius: 6px;
  background: var(--panel); color: var(--muted); }
.qa-score .qa-score-btn:hover { border-color: var(--accent); }

/* AUDIT-2026-07-25 [16] — .grade-badge deleted: App.gradeLabel is used 5x but always
   renders through App.chip or an inline-styled span, never this class; zero emitters. */

/* a single logged gift row (client detail) — date · gift · note + remove */
.gift-row { display: flex; align-items: flex-start; gap: 8px; padding: 6px 0; border-top: 1px solid var(--border); }
.gift-row:first-of-type { border-top: none; }
.gift-row .gift-date { flex: 0 0 auto; min-width: 92px; font-size: var(--fs-meta); color: var(--muted); white-space: nowrap; }
.gift-row .gift-body { flex: 1 1 auto; min-width: 0; white-space: normal; }

/* an upcoming-birthday row (dashboard) — date · name/company · when · gift btn */
.bday-row { display: flex; align-items: center; gap: 10px; }
.bday-row .bday-date { flex: 0 0 56px; font-weight: var(--fw-bold); white-space: nowrap; font-variant-numeric: tabular-nums; }
.bday-row .bday-when { flex: 0 0 52px; text-align: right; font-size: var(--fs-meta); color: var(--muted); white-space: nowrap; }

@media (max-width: 480px) {
  .qa-factor { flex-direction: column; align-items: stretch; gap: 5px; }
  .qa-score { align-self: flex-start; }
  .gift-row { flex-wrap: wrap; }
}

/* ============ client journey + consult-prep (SPEC12) ============
   Admin-only surfaces. The job-modal journey checklist mostly inline-styles its
   rows for resilience; these classes give the consult-prep panel its structure
   + spacing and a mobile-friendly fallback. */

/* one journey step row in the job modal: check + label + date + note */
.journey { display: flex; flex-direction: column; gap: 8px; }
.journey-step { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }
.journey-step .journey-label { flex: 1 1 150px; font-size: var(--fs-caption); font-weight: var(--fw-semibold); }
.journey-step .journey-date { flex: 0 0 auto; min-width: 64px; text-align: right; font-size: 11.5px; color: var(--muted); }

/* read-only consult-prep panel (shared: clients detail + tracker modal) */
.consult-prep { display: block; }
.consult-prep .cp-who { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.consult-prep .cp-name { font-weight: var(--fw-black); font-size: 15px; }
.cp-stat-strip { display: flex; gap: 8px; flex-wrap: wrap; }
.cp-stat { flex: 1; min-width: 108px; padding: 9px 12px; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; }
.cp-stat .cp-stat-value { font-size: 17px; font-weight: var(--fw-black); letter-spacing: -.3px; margin-top: 2px; }
.cp-section { margin-top: 14px; }
.cp-section .cp-section-title { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); text-transform: uppercase; letter-spacing: .4px; margin-bottom: 6px; }
.cp-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip.cp-chip { background: var(--hover); color: var(--text); }  /* SPEC198 — theme-aware ink (was hard #3a4256 → dark-on-dark) */

@media (max-width: 480px) {
  .journey-step { align-items: flex-start; }
  .cp-stat { min-width: calc(50% - 4px); }
}

/* ============ Sales command-center (SPEC13, admin-only) ============
   Mobile-first: this tab is used live on a phone during sales calls. Reuses
   the design tokens; the segmented section control scrolls horizontally on
   narrow screens, scripts/templates copy with one tap. */

/* segmented control across the 7 sections (scrolls on phone) */
.sales-tabs {
  display: flex; gap: 6px; flex-wrap: nowrap; overflow-x: auto;
  -webkit-overflow-scrolling: touch; padding-bottom: 6px; margin-bottom: 16px;
  scrollbar-width: thin;
}
.sales-tab {
  flex: 0 0 auto; padding: 8px 14px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--panel); color: var(--muted);
  font-size: var(--fs-caption); font-weight: var(--fw-semibold); cursor: pointer; font-family: inherit; white-space: nowrap;
  transition: background .12s, color .12s, border-color .12s;
}
.sales-tab:hover { background: var(--subtle); color: var(--text); }
.sales-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.sales-section { display: flex; flex-direction: column; gap: 12px; }
.sales-hint { color: var(--muted); font-size: var(--fs-caption); margin: 0 0 4px; }

/* consultation wizard: progress bar, step pills, step card */
.wizard { display: flex; flex-direction: column; gap: 12px; }
.wizard-progress { display: flex; gap: 4px; }
.wizard-progress .wp-seg {
  flex: 1; height: 5px; border-radius: 3px; background: var(--border);
  cursor: pointer; transition: background .2s;
}
.wizard-progress .wp-seg.done { background: var(--accent); }
.wizard-pills { display: flex; gap: 6px; flex-wrap: wrap; }
.wizard-pill {
  padding: 6px 11px; border-radius: 999px; border: 1px solid var(--border-strong);
  background: var(--panel); color: var(--muted); font-size: var(--fs-meta); font-weight: var(--fw-semibold);
  cursor: pointer; font-family: inherit; white-space: nowrap;
}
.wizard-pill.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.wizard-step { border-left: 3px solid var(--accent); }
.wizard-step-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.wizard-step-title { font-size: var(--fs-md); font-weight: var(--fw-black); letter-spacing: -.2px; }
.wizard-time { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--accent); background: var(--accent-soft); padding: 3px 10px; border-radius: 999px; white-space: nowrap; }
.wizard-prompt { color: var(--muted); font-size: var(--fs-caption); margin: 0 0 12px; }
.wizard-nav { display: flex; gap: 8px; }
.wizard-tip { font-size: 12.5px; color: var(--muted); padding: 5px 9px; background: var(--bg); border-radius: 6px; }

/* a copy-able script / template block: text + copy button */
.script-block {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px;
}
.script-block .script-text { flex: 1; font-size: 13.5px; line-height: var(--lh-normal); color: var(--text); }

/* copy button — turns green on success */
.copy-btn {
  flex: 0 0 auto; padding: 5px 12px; border-radius: 6px; border: 1px solid var(--accent);
  background: var(--accent); color: #fff; font-size: var(--fs-meta); font-weight: var(--fw-bold); cursor: pointer;
  font-family: inherit; white-space: nowrap; transition: background .15s, border-color .15s, color .15s;
}
.copy-btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.copy-btn.copied { background: var(--ok); border-color: var(--ok); color: #fff; }

/* objection cards */
.objection-card { border-left: 3px solid var(--accent); }
.objection-card .obj-title { font-size: 15px; font-weight: var(--fw-black); margin-bottom: 10px; }
.obj-script { background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin-bottom: 8px; }
.obj-script .obj-name { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--accent); margin-bottom: 5px; }
.obj-why { font-size: var(--fs-meta); color: var(--muted); font-style: italic; margin-top: 6px; }

/* text templates */
.text-tpl .tpl-label { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--accent); margin-bottom: 6px; }

/* calculators */
.calc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; }
.calc-cell { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; border-left: 3px solid var(--accent); }
.calc-cell .calc-name { font-size: var(--fs-meta); font-weight: var(--fw-bold); }
.calc-cell .calc-price { font-size: 19px; font-weight: var(--fw-black); letter-spacing: -.4px; margin-top: 2px; }
.calc-cell .calc-over { font-size: var(--fs-micro); color: var(--warn); margin-top: 2px; }

/* upsell flow nodes */
.upsell-node { border-left: 3px solid var(--accent); }
.upsell-node .upsell-trigger { font-size: var(--fs-caption); font-weight: var(--fw-black); margin-bottom: 8px; }
.upsell-branches { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.upsell-branch { font-size: 11.5px; padding: 4px 9px; border-radius: 6px; line-height: 1.4; }
.upsell-branch.yes { color: var(--ok); background: var(--ok-soft); }
.upsell-branch.no { color: var(--warn); background: var(--warn-soft); }

/* quick-ref rows */
.qref-row { display: flex; justify-content: space-between; gap: 12px; padding: 6px 0; border-bottom: 1px solid var(--border); }
.qref-row:last-child { border-bottom: none; }
.qref-row .qref-label { font-size: 12.5px; color: var(--muted); flex: 0 0 auto; }
.qref-row .qref-val { font-size: 12.5px; font-weight: var(--fw-semibold); text-align: right; }

/* quick-links — copy-paste by price tier */
.qlink-tier { font-size: var(--fs-body); font-weight: var(--fw-black); letter-spacing: -.2px; margin-bottom: 4px; }
.qlink-rec { font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--accent); margin-bottom: 4px; }
.qlink-why { font-size: 12.5px; line-height: var(--lh-normal); margin-bottom: 10px; }

/* SOD/EOD report preview */
.report-preview {
  font-family: var(--mono); font-size: 12.5px; white-space: pre-wrap; line-height: 1.6;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; margin: 0;
}

@media (max-width: 480px) {
  .wizard-step-title { font-size: 15px; }
  .script-block { flex-direction: column; }
  .script-block .copy-btn { align-self: flex-end; }
  .calc-grid { grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); }
}

/* ---------------- Today command-center (SPEC21 A) ---------------- */
.today-group { margin-bottom: 18px; }
.today-group > .card-title { margin-bottom: 10px; }
.today-shoot { margin-bottom: 10px; cursor: pointer; transition: border-color .12s, box-shadow .12s, transform .12s; }
.today-shoot:hover { border-color: var(--accent); box-shadow: var(--shadow); }
.today-shoot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.today-shoot-addr { font-size: 14.5px; font-weight: 650; letter-spacing: -.1px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.today-line { font-size: 12.5px; line-height: 1.6; margin-top: 4px; display: flex; flex-wrap: wrap; gap: 4px 7px; align-items: center; }
.today-line .today-dot { color: var(--border-strong); }
.today-shoot-actions { margin-top: 10px; display: flex; gap: 8px; flex-wrap: wrap; }

/* FRONT-DOOR 2026-08-09 — the editor "Your edits" hero card (today.js
   editHeroCard). Same clickable-card hover/focus treatment as .today-shoot,
   deliberately WITHOUT its 2-column time-gutter grid (this card has no time
   gutter — applying .today-shoot squeezed the address into an implicit 54px
   column and shoved the "Start here" button up beside it). */
.edit-hero { margin-bottom: 10px; cursor: pointer; transition: border-color .12s, box-shadow .12s, transform .12s; }
.edit-hero:hover { border-color: var(--accent); box-shadow: var(--shadow); }
.edit-hero:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ONE-3/ONE-4 (RESEARCH-ONE-TODAY-2026-07-28.md) — the Focus-order ladder + its
   Layer-2 collapse. Each ladder section is a plain heading + one card of rows
   (reuses .list-row); a zero-item section never reaches the DOM at all (decided
   in JS, not hidden via CSS). */
.today-section { margin-bottom: 18px; }
.today-section-head { margin-bottom: 8px; }
.today-section-head h3 { display: flex; align-items: center; gap: 7px; margin: 0; }

/* Layer-2 "one-line link" — collapsed by default (native <details>, no JS
   needed to toggle). The summary reads as a single row; the full, unchanged
   card only renders once it's opened — nothing is deleted, just tucked one
   click deeper (NN/g's 2-level progressive-disclosure limit). */
.today-collapse { margin-bottom: 10px; }
.today-collapse > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px; padding: 10px 14px; background: var(--bg-2); border: 1px solid var(--border); border-radius: 10px; font-weight: 650; font-size: var(--fs-caption); }
.today-collapse > summary::-webkit-details-marker { display: none; }
.today-collapse > summary::after { content: '›'; margin-left: auto; color: var(--muted); font-size: var(--fs-md); transition: transform .15s; }
.today-collapse[open] > summary { border-radius: 10px 10px 0 0; }
.today-collapse[open] > summary::after { transform: rotate(90deg); }
.today-collapse-body { margin-top: 8px; }
.today-collapse.card > summary { background: none; border: none; padding: 0; margin-bottom: 4px; }

/* ============================================================
   SPEC28 D — subtle density / calm pass
   Polish only: nudge secondary text down a hair, calm a few borders,
   and trim roomy card padding. NO color-identity changes, no layout
   restructuring. Kept last so it gently refines earlier rules.
   ============================================================ */

/* Secondary/supporting text reads a touch quieter on busy screens.
   (Primary text, KPI values, titles and the accent are untouched.) */
.view-sub { font-size: 12.5px; }
.kpi-hint { font-size: var(--fs-micro); }
.card-title { letter-spacing: .35px; }

/* SPEC175 — refined, calmer rows: a touch more air than the old dense pass for an
   expensive feel, but not bloated on long tracker lists. The accent-tinted hover rail
   from the base .table rule now shows through (this no longer flattens it). */
.table th { padding: 10px 14px; }
.table td { padding: 10px 14px; }

/* SPEC175 — card padding is now a single source of truth (.card-pad = 18px 20px);
   the old 15px shave is removed so cards breathe like the client surfaces. */

/* Calmer borders where two strong edges stack (cards in tight grids).
   Uses the existing --border token — no new colors. */
.grid-2 > .card, .grid-3 > .card { box-shadow: var(--shadow-sm), var(--card-hi); }

/* Chips/badges: a touch less shouty in dense rows (weight only, colors kept). */
.badge { font-weight: var(--fw-semibold); }

/* Faint helper text settles a hair smaller in supporting roles. */
.list-row .muted { font-size: 11.5px; }

/* ============ SPEC32 — onboarding + inline help ============ */

/* sidebar "?" Help button (footer) — quiet until hovered.
   SPEC-DHSHELL D2 — icon-only in the 82px rail (title="" tooltips already on every
   caller: 'Help & tips'/'Toggle dark mode'/'Quick chat'/'Notifications'); the SPEC86
   ≤860px drawer query below restores the roomy labeled row. */
.help-btn {
  display: flex; flex-direction: column; align-items: center; gap: 3px; width: auto;
  padding: 7px 4px; border-radius: var(--radius-sm); border: 1px solid transparent;
  background: transparent; color: var(--sidebar-text); cursor: pointer;
  font: inherit; font-size: 12.5px; text-align: center;
}
.help-btn:hover { background: rgba(255,255,255,.06); color: var(--sidebar-active); }
.help-btn span:not(.help-q):not(.notif-bell-badge) {
  font-size: 9.5px; line-height: 1.1; max-width: 68px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.help-btn .help-q {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: 0 0 18px; border-radius: 50%;
  background: rgba(255,255,255,.1); font-weight: var(--fw-bold); font-size: var(--fs-meta);
}

/* AUDIT-2026-07-25 [16] — .help-hint (+hover/:focus-visible) deleted along with its
   only emitter, App.helpHint (core.js) — zero callers anywhere, no test coverage. */

/* SPEC-V2CHATSHELL 2026-08-29 (audit §Shell, "User avatar at rail foot"
   MISSING) — the monogram/photo avatar pinned below Settings, matching the
   prototype's own rail foot (click-mapped, :8123). App.avatar renders the
   circle itself; this just centers it with a thin top separator from the
   Settings/Help/theme rows above it. */
.rail-avatar { display: flex; align-items: center; justify-content: center; padding: 9px 4px 3px; border-top: 1px solid rgba(255,255,255,.08); }

/* empty-state block — "nothing here yet" with an optional action */
.empty-state {
  text-align: center; padding: 34px 20px; color: var(--muted);
  display: flex; flex-direction: column; align-items: center; gap: 7px;
}
.empty-state .es-ico { font-size: 30px; line-height: 1; opacity: .9; }
/* old-convention children (.es-icon/h4/p) carry their own margins; this block adds
   flex gap to the parent, so zero those margins to avoid double vertical spacing. */
.empty-state .es-icon, .empty-state h4, .empty-state p { margin: 0; }
.empty-state .es-title { font-weight: var(--fw-bold); color: var(--text); font-size: 15px; }
.empty-state .es-msg { font-size: var(--fs-caption); max-width: 360px; }
.empty-state .es-action { margin-top: 6px; }

/* keyboard shortcut cheatsheet */
.shortcut-table { width: 100%; border-collapse: collapse; }
.shortcut-table td { padding: 7px 8px; border-bottom: 1px solid var(--border); font-size: var(--fs-caption); vertical-align: middle; }
.shortcut-table tr:last-child td { border-bottom: none; }
.sc-kbd, .help-tip kbd {
  display: inline-block; padding: 3px 8px; border-radius: 6px; font-family: var(--mono);
  font-size: var(--fs-meta); background: var(--hover); border: 1px solid var(--border-strong);
  color: var(--text); white-space: nowrap;
}
.help-actions { display: flex; flex-wrap: wrap; gap: 10px; margin: 4px 0 14px; }
.help-tip { font-size: 12.5px; margin: 0; }

/* first-run welcome overlay */
.modal-body.welcome { padding-top: 4px; }
.welcome-hero { text-align: center; padding: 6px 6px 4px; }
.welcome-wave { font-size: 38px; line-height: 1; }
.welcome-h { margin: 8px 0 4px; font-size: 23px; }
.welcome-tag {
  display: inline-block; padding: 3px 11px; border-radius: 999px;
  background: var(--accent-soft); color: var(--accent); font-weight: var(--fw-bold);
  font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .5px;
}
.welcome-lead { margin: 11px auto 4px; max-width: 480px; color: var(--muted); font-size: var(--fs-body); }
.welcome-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 11px; margin: 16px 0 6px;
}
.welcome-card {
  display: flex; gap: 11px; align-items: flex-start; padding: 13px 14px;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--subtle);
}
/* WELCOME-REWORK 2026-08-10 — highlight cards are real buttons (tap → jump there) */
.welcome-card.clickable {
  width: 100%; font: inherit; color: inherit; text-align: left; cursor: pointer;
  transition: border-color .12s, background .12s;
}
.welcome-card.clickable:hover, .welcome-card.clickable:focus-visible { border-color: var(--accent); background: var(--accent-soft); }
.welcome-card.clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.welcome-card .wc-ico { font-size: 21px; line-height: 1.1; flex: 0 0 auto; }
.welcome-card .wc-name { font-weight: var(--fw-bold); font-size: 13.5px; }
.welcome-card .wc-desc { font-size: 12.5px; color: var(--muted); margin-top: 2px; line-height: 1.4; }
.welcome-add {
  margin: 12px 0 2px; padding: 11px 13px; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--text); font-size: var(--fs-caption); text-align: center;
}
.welcome-add b { color: var(--accent); }
@media (max-width: 560px) {
  .welcome-grid { grid-template-columns: 1fr; }
}

/* floating ＋ Quick-Add button — add from anywhere */
.quick-add-fab {
  position: fixed; right: 22px; bottom: 22px; z-index: 60;
  width: 52px; height: 52px; border-radius: 50%; border: none; cursor: pointer;
  background: var(--accent); color: #fff; font-size: var(--fs-display); line-height: 1;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 18px -3px rgba(var(--ring-rgb),.5), 0 2px 6px rgba(16,24,40,.2);
  transition: transform .12s ease, background .12s ease;
}
.quick-add-fab:hover { background: var(--accent-hover); transform: translateY(-2px) scale(1.03); }
.quick-add-fab:active { transform: scale(.96); }

/* SPEC303 — global voice mic FAB; stacks above the ＋ FAB when it's present */
.voice-fab {
  position: fixed; right: 22px; bottom: 22px; z-index: 60;
  width: 52px; height: 52px; border-radius: 50%; border: 1px solid var(--border); cursor: pointer;
  background: var(--panel); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 18px -3px rgba(16,24,40,.26), 0 2px 6px rgba(16,24,40,.16);
  transition: transform .12s ease, background .12s ease, color .12s ease;
}
.voice-fab.stacked { bottom: 86px; }
.voice-fab:hover { transform: translateY(-2px) scale(1.03); background: var(--accent-soft); }
.voice-fab:active { transform: scale(.96); }
.voice-fab.listening { background: var(--accent); color: #fff; border-color: transparent; animation: voicePulse 1.4s ease-in-out infinite; }
@keyframes voicePulse { 0%,100% { box-shadow: 0 0 0 0 rgba(var(--ring-rgb),.5); } 50% { box-shadow: 0 0 0 10px rgba(var(--ring-rgb),0); } }
.voice-pill {
  position: fixed; left: 50%; bottom: 92px; transform: translateX(-50%); z-index: 70;
  max-width: 80vw; padding: 9px 16px; border-radius: 999px;
  background: var(--ink, #14140f); color: #fff; font-size: 13.5px; font-weight: var(--fw-semibold);
  box-shadow: 0 8px 24px -6px rgba(0,0,0,.4);
}
/* SPEC303 — small inline mic dropped inside a field's row (job brief, notes, chat) */
.voice-mic {
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
  width: 30px; height: 30px; border-radius: 50%; border: 1px solid var(--border);
  background: var(--panel); color: var(--muted); cursor: pointer;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.voice-mic:hover { color: var(--accent); border-color: var(--accent); }
.voice-mic.listening { color: #fff; background: var(--accent); border-color: transparent; animation: voicePulse 1.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .voice-fab.listening, .voice-mic.listening { animation: none; } }
@media (max-width: 640px) {
  .voice-fab { right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); width: 56px; height: 56px; }
  .voice-fab.stacked { bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
}

/* SPEC310 — bug reporter
   The floating "Report" pill (stacks ABOVE the ＋/mic FABs), the devtools-style
   pick-mode overlay (transparent capture layer + accent outline + type label + top
   banner), and the report-form chips + screenshot thumb. Token-driven + theme-aware. */
.bug-fab {
  position: fixed; right: 22px; bottom: 150px; z-index: 61;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 16px; border-radius: 999px; border: 1px solid var(--border); cursor: pointer;
  background: var(--panel); color: var(--accent); font: inherit; font-size: 13.5px; font-weight: var(--fw-bold);
  box-shadow: 0 6px 18px -3px rgba(16,24,40,.26), 0 2px 6px rgba(16,24,40,.16);
  transition: transform .12s ease, background .12s ease, color .12s ease;
}
.bug-fab:hover { transform: translateY(-2px) scale(1.03); background: var(--accent-soft); }
.bug-fab:active { transform: scale(.96); }
@media (max-width: 640px) {
  .bug-fab { right: 16px; bottom: calc(150px + env(safe-area-inset-bottom, 0px)); padding: 9px 13px; }
}
@media (prefers-reduced-motion: reduce) { .bug-fab { transition: none; } }

/* D7 — mobile FAB dial (fab-dial.js): ONE expandable control replaces the five-deep
   floating cluster (Report pill / mic / phone / ＋ / FRIDAY pill) that clipped content
   on 6+ screens at 375×812.
   STAGE1 2026-08-14 (design item 10 / D1) — the dial now covers DESKTOP too: the four
   individual FABs stacked bottom-right on every desktop view, overlapping the tracker's
   Payment column, the chat feed, and the video table (parity-feel.md D1). Desktop keeps
   the same right:22px/bottom:22px slot the old cluster's bottom-most button (quick-add)
   used, so nothing moves for muscle memory — it's just ONE circle now, not four. The
   four originals stay in the DOM (their own JS still creates/wires them, other callers
   still reference the ids) but never paint, at any width. */
.fab-dial-btn {
  position: fixed; right: 22px; bottom: 22px;
  z-index: 60; width: 56px; height: 56px; border-radius: 50%; border: 0; cursor: pointer;
  background: var(--accent); color: #fff; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 18px -3px rgba(var(--ring-rgb),.5), 0 2px 6px rgba(16,24,40,.2);
  transition: transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.fab-dial-btn .icon { transition: transform var(--t-fast) var(--ease); }
.fab-dial-btn.open .icon { transform: rotate(45deg); }
.fab-dial-btn:hover { background: var(--accent-hover); }
.fab-dial-btn:active { transform: scale(.96); }
/* STAGE1 2026-08-14 — explicit ring shaped to the circle; the sitewide bare
   :focus-visible (styles.css:169) already fires here (real <button>, native tab
   order — no JS change needed for reachability), this just matches the shape. */
.fab-dial-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); border-radius: 50%; }
.fab-dial-btn-dot {
  position: absolute; top: 3px; right: 3px; width: 8px; height: 8px;
  border-radius: 50%; background: var(--ok); border: 2px solid var(--panel); display: none;
}
.fab-dial-btn.has-live .fab-dial-btn-dot { display: block; }
.fab-dial-scrim { position: fixed; inset: 0; z-index: 59; background: rgba(16,24,40,.32); display: none; border: 0; padding: 0; }
.fab-dial-scrim.open { display: block; }
.fab-dial-menu {
  position: fixed; right: 22px; z-index: 61; display: none; flex-direction: column; gap: var(--space-2);
  bottom: calc(22px + 68px); align-items: flex-end;
}
.fab-dial-menu.open { display: flex; }
.fab-dial-item {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 16px 0 14px;
  border-radius: var(--radius, 999px); border: 1px solid var(--border); background: var(--panel);
  color: var(--text); box-shadow: var(--shadow-lg); font: inherit; font-size: 13.5px; font-weight: var(--fw-semibold);
  cursor: pointer;
}
.fab-dial-item:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.fab-dial-item .icon { flex: 0 0 auto; color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .fab-dial-btn, .fab-dial-btn .icon { transition: none; } }
/* STAGE1 2026-08-14 — the four originals never paint at any width now (were previously
   only hidden ≤860px, with the dial taking over just on mobile). */
#quick-add-fab, #voice-fab, #bug-fab, #callFab { display: none !important; }
@media (max-width: 860px) {
  .fab-dial-btn { right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px) + 62px); }
  .fab-dial-menu { right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px) + 62px + 68px); }
}

/* SPEC-V2FRIDAYFIX D1 — the always-there FRIDAY bubble. Base rule mirrors
   App.buildFridayBubble's inline geometry (voice.js) byte-for-byte — the
   SAME belt-and-suspenders posture the FRIDAY mini/full widgets already use
   (inline is LAW-CRITICAL because tests.html never loads this file; this
   class only adds hover/transition polish + the mobile clearance below). */
.fd-bubble {
  position: fixed; right: 22px; bottom: 96px; z-index: 58;
  width: 52px; height: 52px; border-radius: 50%; border: none; cursor: pointer;
  background: #e3bcab; color: #2a1c16;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 18px -3px rgba(227,188,171,.55), 0 2px 6px rgba(16,24,40,.22);
  transition: transform .12s ease, box-shadow .12s ease;
}
.fd-bubble:hover { transform: translateY(-2px) scale(1.03); }
.fd-bubble:active { transform: scale(.96); }
@media (prefers-reduced-motion: reduce) { .fd-bubble { transition: none; } }
/* voice.js's App.buildFridayBubble sets right/bottom INLINE (law-critical —
   tests.html never loads this file) and recomputes them on resize, so it
   is the actual source of truth for position at both breakpoints; this
   media query mirrors those same two values for a no-JS/print fallback and
   as a single place documenting the intended geometry. */
@media (max-width: 860px) {
  .fd-bubble { right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px) + 62px + 68px); }
}
/* chat: dial clears the composer at every width — nothing floats over an input
   (SPEC328's rule, index.html:120-121 / core.js:18613). MEASURED live (1280×800,
   #general): .chat-composer's top sits a constant 142px above the viewport bottom
   (the chat layout's own `calc(100vh - 104px)` math, independent of window height).
   The old desktop cluster's SPEC336 offset (140px) left only a 2px gap there — a
   real, if hairline, violation of the same SPEC328 rule it was named for — so this
   is 152px, not a re-derivation of that value: a true ~10px clear margin instead of
   reproducing the near-miss. */
body[data-view="chat"] .fab-dial-btn { bottom: 152px; }
body[data-view="chat"] .fab-dial-menu { bottom: calc(152px + 68px); }
/* BUG4-FABCOMPOSER 2026-08-18 — the 152px above only clears a SINGLE-LINE composer.
   `.chat-input` (styles.css ~2652) auto-grows with the message — min-height:42px,
   max-height:140px — and on a phone the growth eats straight into that margin.
   MEASURED live at an explicit 375×812 (#general): empty composer top sits 134px
   above the viewport bottom (matching the desktop-tuned offset); typing an 8-line
   message grows `.chat-input` from 37px to its 140px cap and pulls the composer's
   top up to 23px from the bottom — a 103px swing. A hit-test at that point
   (elementFromPoint over the now-taller textarea) returned the FAB button, not the
   textarea: a real "floats over an input" violation, not just close boxes.
   `.chat-input`'s 140px cap makes this a BOUNDED, provable worst case, so a static
   +134px (103px of real growth + a ~30px margin, re-measured after a first pass at
   +120px left only ~1px of hairline clearance at the textarea's actual top — the
   exact near-miss shape the 152px comment above was written to avoid) guarantees
   real clearance at every message length, not just the common one-line case.
   Scoped to the two mobile tiers only (desktop's extra vertical room made this
   untestable as an on-screen violation there); the 152px desktop rule above is
   unchanged. */
@media (max-width: 860px) {
  body[data-view="chat"] .fab-dial-btn { bottom: calc(152px + 62px + 134px); }
  body[data-view="chat"] .fab-dial-menu { bottom: calc(152px + 62px + 134px + 68px); }
}
@media (max-width: 640px) {
  body[data-view="chat"] .fab-dial-btn { bottom: calc(152px + env(safe-area-inset-bottom, 0px) + 62px + 134px); }
  body[data-view="chat"] .fab-dial-menu { bottom: calc(152px + env(safe-area-inset-bottom, 0px) + 62px + 134px + 68px); }
}
body.kb-open .fab-dial-btn, body.kb-open .fab-dial-scrim, body.kb-open .fab-dial-menu { display: none; }
/* STAGE1-FIX 2026-08-14 (checker 2) — "nothing floats over data" was only half kept.
   The chat rule above lifts the dial off the composer, but the two BOARD views scroll
   their last row straight under it: MEASURED at 1024x800, Job Tracker scrolled to the
   bottom, the last row's box is 708-749 and the dial's is 722-778, and a hit-test at
   (989, 728) and (969, 728) — inside that row — returns the FAB, not the row. At 1024
   the table's right edge is 992px, so what the circle is sitting on is the Due cell:
   the row's urgency, covered on exactly the row a person scrolled down to read. Same
   geometry on the Video Tracker. The fix is a bottom gutter on the scroll container so
   the last row can always clear the dial (78px of occupied corner -> 88px), rather than
   moving the dial per-view: the board's bottom-right is data at every scroll position,
   the chat composer is only ever in one place. Desktop only — <=860px keeps the mobile
   tab-bar offsets above untouched. */
@media (min-width: 861px) {
  body[data-view="jobs"] main,
  body[data-view="video"] main,
  /* WAVE5U FIX (Finding 7) 2026-09-03 — the identical geometry recurs on Money
     now that Unpaid folded in (moneyhub.js): the table's new Actions column
     puts the primary "Paid" button as the rightmost element of the last row,
     same corner this rule already exists to clear. App.navigate sets
     body[data-view] to the PARENT id for a segmented view (core.js), and
     moneyhub resolves to the 'money' parent (App.CHILD_PARENT.moneyhub,
     moneyhub.js addSegment()), so 'money' — not 'moneyhub' — is the selector
     that actually matches. Kept alongside the new rule below (belt-and-
     suspenders for a short Open scope that IS effectively the last content
     on the page — no leak/refund cards rendering). */
  body[data-view="money"] main { padding-bottom: 88px; }
}
/* WAVE5U FIX (Finding 7), continued — unlike Job/Video Tracker, the money
   table is NOT the last thing on the page (Who owes you / leak / refund /
   aging / accountant cards follow it), so the page-END gutter above never
   engages at the scroll position a person actually stops at to read the
   table's own last row. MEASURED live at 1024×800 with a real 20-row Open
   scope, scrolled to bring that row's bottom edge flush with the viewport
   (the natural "I scrolled to read it" stop, App.el's scrollIntoView({block:
   'end'}) reproduces this exactly): the FAB (.fab-dial-btn, right:22/
   bottom:22, 56px) still clipped the Paid button's top-right corner WITH the
   rule above in place. This margin (moneyhub.js's own '.money-scoped-table'
   wrapper, right after the scoped table) recreates the same "scrolling
   further clears it" mechanic locally around the table itself, not only at
   the page's true end. */
@media (min-width: 861px) {
  body[data-view="money"] .money-scoped-table { margin-bottom: 88px; }
}

/* pick mode — a transparent full-viewport layer that captures the click/hover */
.bugpick-overlay { position: fixed; inset: 0; z-index: 2147483000; background: transparent; cursor: crosshair; }
.bugpick-outline {
  position: fixed; z-index: 2147483001; pointer-events: none;
  border: 2px solid var(--accent); border-radius: 4px;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 28%, transparent);
  transition: top .06s ease, left .06s ease, width .06s ease, height .06s ease;
}
.bugpick-label {
  position: fixed; z-index: 2147483002; pointer-events: none;
  padding: 3px 8px; border-radius: 6px; font-size: var(--fs-meta); font-weight: var(--fw-bold); white-space: nowrap;
  background: var(--accent); color: #fff; box-shadow: 0 2px 8px rgba(0,0,0,.3);
}
.bugpick-banner {
  position: fixed; top: 0; left: 50%; transform: translateX(-50%); margin-top: 14px;
  z-index: 2147483003; pointer-events: auto;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: center;
  max-width: 92vw; padding: 11px 18px; border-radius: 999px;
  background: var(--ink, #14140f); color: #fff; font-size: 13.5px; font-weight: var(--fw-semibold);
  box-shadow: 0 10px 30px -6px rgba(0,0,0,.5);
}
/* D15 — was an underlined text link ("Skip — just describe it"); now a real
   button so the escape hatch reads as an equal option, not an afterthought.
   The banner is always dark (var(--ink)) regardless of app theme, so this is
   styled directly rather than through .btn's light/dark tokens. */
.bugpick-skip {
  background: transparent; border: 1px solid rgba(255,255,255,.4); border-radius: 999px;
  color: #fff; cursor: pointer; padding: 5px 13px;
  font: inherit; font-size: 12.5px; font-weight: var(--fw-bold);
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.bugpick-skip:hover { background: rgba(255,255,255,.14); border-color: #fff; }
@media (prefers-reduced-motion: reduce) { .bugpick-skip { transition: none; } }
@media (prefers-reduced-motion: reduce) { .bugpick-outline { transition: none; } }

/* report form — kind/severity toggle chips, pinpoint row, screenshot thumb */
.bugrep-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 13px; border-radius: 999px; border: 1px solid var(--border); cursor: pointer;
  background: var(--panel); color: var(--text); font: inherit; font-size: var(--fs-caption); font-weight: var(--fw-semibold);
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.bugrep-chip:hover { border-color: var(--accent); }
.bugrep-chip.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.bugrep-pin {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 12px; border-radius: var(--radius-sm, 8px);
  background: var(--accent-soft); border: 1px solid var(--border);
  font-size: 12.5px; color: var(--text);
}
.bugrep-shot-thumb {
  display: block; max-width: 240px; max-height: 140px;
  border-radius: 8px; border: 1px solid var(--border); cursor: zoom-in;
}
.bugrep-where { margin-top: 4px; font-size: var(--fs-meta); }
.bugrep-where > summary { cursor: pointer; color: var(--muted); font-weight: var(--fw-semibold); list-style: revert; }
.bugrep-where .bugrep-tech {
  margin-top: 6px; padding: 8px 10px; border-radius: 8px; background: var(--hover);
  font-size: 11.5px; line-height: 1.55; color: var(--muted); word-break: break-word;
}
.bugrep-where .bugrep-tech strong { color: var(--text); font-weight: var(--fw-semibold); }
@media (prefers-reduced-motion: reduce) { .bugrep-chip { transition: none; } }

/* the ＋ menu (compact modal) */
.modal-body.quick-add { display: flex; flex-direction: column; gap: 8px; padding-top: 4px; }
.qa-item {
  display: flex; align-items: center; gap: 13px; width: 100%; text-align: left;
  padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--panel); cursor: pointer; font: inherit; transition: all .1s ease;
}
.qa-item:hover { border-color: var(--accent); background: var(--accent-soft); }
.qa-item .qa-ico { font-size: var(--fs-h1); line-height: 1; flex: 0 0 auto; }
.qa-item .qa-text { display: flex; flex-direction: column; }
.qa-item .qa-label { font-weight: var(--fw-bold); font-size: var(--fs-body); }
.qa-item .qa-sub { font-size: var(--fs-meta); color: var(--muted); }

/* quick-note client picker */
.qn-chosen { font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--ok); margin-top: 5px; }
.qn-drop {
  margin-top: 5px; border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--panel); box-shadow: var(--shadow); max-height: 220px; overflow-y: auto;
}
.qn-opt {
  display: flex; flex-direction: column; gap: 1px; width: 100%; text-align: left;
  padding: 8px 11px; border: none; border-bottom: 1px solid var(--border);
  background: transparent; cursor: pointer; font: inherit;
}
.qn-opt:last-child { border-bottom: none; }
.qn-opt:hover { background: var(--accent-soft); }
.qn-opt .qn-opt-name { font-weight: var(--fw-semibold); font-size: var(--fs-caption); }
.qn-opt .qn-opt-sub { font-size: 11.5px; color: var(--muted); }
.qn-head { font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .5px; color: var(--faint); padding: 7px 11px 3px; }

/* SPEC39 — type-ahead job/client picker */
.linkpick { position: relative; }
.linkpick .qn-drop { position: absolute; left: 0; right: 0; z-index: 30; margin-top: 2px; }
.linkpick-chosen { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 9px 11px; border: 1px solid var(--accent); border-radius: var(--radius-sm); background: var(--accent-soft); font-size: var(--fs-caption); font-weight: var(--fw-semibold); }

/* ⌘K palette: new "action" + "view" row types */
.qs-type-action { background: var(--accent); color: #fff; }
.qs-type-view { background: var(--accent); color: #fff; }

/* on phones the FAB sits a touch inward so it clears the collapsed rail, and
   respects the iPhone home-indicator safe area */
@media (max-width: 640px) {
  .quick-add-fab {
    right: 16px; bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    width: 56px; height: 56px; font-size: 30px;
  }
}

/* ============ SPEC32 — mobile / touch polish (field shooters on phones) ============
   The sidebar becomes a LABELED off-canvas drawer ≤860px (SPEC86 — no more bare-icon
   rail; comment corrected DESIGNA 2026-08-19). These rules make
   the phone experience genuinely touch-first: 44px+ tap targets, sheet-style modals,
   16px form fields (so iOS doesn't zoom on focus), momentum-scrolling tables, and a
   decluttered top rail. Desktop is untouched. */
@media (max-width: 640px) {
  /* never let the FAB cover the last row of content */
  #view { padding: 16px 13px calc(96px + env(safe-area-inset-bottom, 0px)); }

  /* top rail: smooth momentum scroll, no scrollbar, comfy tap targets */
  #sidebar { -webkit-overflow-scrolling: touch; scrollbar-width: none; gap: 2px; }
  #sidebar::-webkit-scrollbar { display: none; }
  /* DESIGNA 2026-08-19 — dropped justify-content:center: a leftover from the pre-SPEC86
     bare-icon rail that centred every LABELED drawer row against its own group labels. */
  .nav-item { min-width: 44px; min-height: 44px; padding: 11px; }
  .nav-item svg { width: 20px; height: 20px; flex-basis: 20px; }

  /* 16px form fields prevent iOS auto-zoom; roomier hit areas */
  /* SAFARI 07-30 — `.chat-input` was missing from this list, so the ONE input the team
     uses most (the chat composer, 14px) still triggered iOS auto-zoom on every tap while
     every other field was already protected. Same fix, same reason. */
  .input, .select, .textarea, .cell-input, .qs-input, .view-as-select, .chat-input { font-size: var(--fs-md); }
  .input, .select, .textarea { padding: 10px 12px; min-height: 44px; }
  textarea.textarea { min-height: 80px; }
  /* LAW-A 2026-08-15 (#24) — 42px and 40px were both under the 44px mobile tap-target
     floor this SAME file already documents on .btn-sm one line below (and enforces in
     32 other places — .input/.textarea/.select two lines up, .nav-item, etc). Raised
     both to close the gap; ~14 tap-target violations per screen clear on these two
     numbers alone. */
  .btn { min-height: 44px; padding: 9px 15px; font-size: var(--fs-body); }
  .btn-sm { min-height: 44px; }   /* SPEC-A11Y — was 36px, 8px under the 44px mobile touch-target floor used everywhere else (.input/.nav-item/SPEC202) */
  select.select, .table select { min-height: 44px; }

  /* checkboxes/radios: a real 44px tap catch-zone via an invisible ::after halo — not a
     bigger visual box (the glyph only grows to 20px; a 44px SQUARE glyph would read as
     decorative bulk next to 13px label text). Scoped off dense .table rows (bulk-select
     checkboxes): those run as tight as 1px vertical padding in compact table density
     (.density-compact), and a 44px zone centered on an 18-20px box would overlap the
     row above/below — a real miscl-ick risk, not a theoretical one. Table checkboxes
     still get the bigger 20px glyph above, just not the invisible expanded catch-zone. */
  input[type="checkbox"], input[type="radio"] { width: 20px; height: 20px; }
  input[type="checkbox"]:not(.table *), input[type="radio"]:not(.table *) { position: relative; }
  input[type="checkbox"]:not(.table *)::after, input[type="radio"]:not(.table *)::after {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 44px; height: 44px;
  }
  /* LAW-A 2026-08-15 (#25) — a reusable version of the halo trick two rules up, for any
     small icon-only control that can't grow its own visual box (a chip's "×", a row's
     "⋯"). Apply class="tap" to the control itself (or a wrapper around it) to get an
     invisible centered 44×44 catch-zone without changing how it looks.
     Ships pre-wired for .jarvis-chip-x — the one flagged violation this closes
     (measured 13.1×14px at 375×812, permanent + destructive, "never show this again").
     CORRECTION 2026-08-15 (checker, LAW-A) — an earlier version of this comment claimed
     `.jarvis-chip` / `.jarvis-chip-x` had ZERO live call sites. THAT WAS WRONG. Both are
     live at dailybrief.js:146 and :205 (SPEC334's weekly Feature Spotlight chip and its
     forever-dismiss "✕"); the ✕ only renders when App.featureSpotlight() returns a card,
     which is why a grep-by-eye missed it. Verified live on Home at 375×812: the ✕ paints
     at 13.1×14 and this halo hit-tests as the ✕ out to ±22px from its centre. The
     aria-label the plan also asked for now ships at dailybrief.js:205.
     ⚠ KNOWN TRADE-OFF, measured: the 44×44 zone is centred on a ✕ that sits INSIDE its
     parent .jarvis-chip--spot button (315×58 there), so it now covers ~22px of the chip's
     own label. A tap in that strip dismisses the tip instead of opening the feature. That
     is the cost of the 44 floor on a control this small; the cleaner shape is a padded
     WRAPPER outside the chip, or a same-tap undo — flag for the next round. */
  .tap, .jarvis-chip-x { position: relative; }
  .tap::after, .jarvis-chip-x::after {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 44px; height: 44px;
  }
  /* LAW-A 2026-08-15 (#26) — label.qc-check is the shooter's most-tapped control (×9 per
     job sheet's checklist). Effective height was ~29px (5px padding + a ~19px text line
     at --fs-caption/1.45 line-height) — under the 44px floor. padding 5px→11px per the
     plan; min-height:44px is the actual guarantee (padding math depends on font metrics
     this file can't fully control) — belt + suspenders, not a visual change beyond the
     padding bump. The row is a <label> wrapping the checkbox, so this whole box (not
     just the 20px glyph) is the real tap target.
     LIVE-VERIFIED CASCADE BUG, then fixed: a bare `.qc-check` here is (0,1,0), the SAME
     specificity as the base rule (styles.css, non-mobile), which sits LATER in the file
     and so won the padding tiebreak on source order — min-height still applied (the base
     rule never sets it, so there was no conflict there), but the padding silently stayed
     5px 2px. Built the real DOM shape checklistplay.js:120 creates and measured it via
     getBoundingClientRect to catch this. `.qc-check` is always applied to a <label>
     (checklists.js:83, checklistplay.js:114/120) so adding the tag raises this to
     (0,1,1), which reliably wins regardless of source order. */
  label.qc-check { padding: 11px 2px; min-height: 44px; }
  /* range: the visual track/thumb stay slim (restrained, matches desktop) — padding-block
     grows the drag CATCH area to ~44px tall without thickening the line itself */
  input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range) { padding-block: 19px; }
  input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-webkit-slider-thumb { width: 22px; height: 22px; margin-top: -8px; }
  input[type="range"]:not(.order-ba-range):not(.cstatus-lb-rev-range)::-moz-range-thumb { width: 22px; height: 22px; }

  /* SPEC-V2MODALFIT — modals (and slide-overs) become BOTTOM SHEETS on phone,
     never full-screen. The old rule here (`min-height:100%; border-radius:0`)
     was the literal thing Vincent's ruling called out — a phone modal that
     fills the whole screen with no way to see or tap the app behind it. Rounded
     top + a visible grab pill say "this is a sheet"; the 92dvh cap (dvh not vh
     — iOS Safari's address bar shrinks vh mid-gesture) always leaves a strip of
     backdrop showing so "tap outside to close" stays reachable, same as desktop.
     Long forms get .modal-body's internal scroll instead of the sheet growing
     past 92dvh — same trade the desktop 85vh cap already makes. */
  .modal-overlay { padding: 0; align-items: flex-end; }
  .modal, .modal.wide, .modal.narrow {
    width: 100%; max-width: 100%; max-height: 92dvh; min-height: 0;
    border-radius: 16px 16px 0 0;
    display: flex; flex-direction: column;
  }
  .modal-grab { display: block; flex: 0 0 auto; width: 36px; height: 4px; border-radius: 999px; background: var(--border); margin: 8px auto 0; }
  .modal-head { padding: 6px 18px 0; }
  .modal-body { flex: 1 1 auto; overflow-y: auto; -webkit-overflow-scrolling: touch; padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
  .modal-foot {
    position: sticky; bottom: 0; background: var(--panel); margin-top: auto;
    padding: 12px 18px calc(14px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--border);
  }
  .modal-close { font-size: 26px; padding: 4px 10px; }

  /* slide-over: same bottom-sheet posture as .modal (rises from the bottom, not
     the side) so phone users get ONE consistent overlay gesture app-wide instead
     of two (a centred sheet here, an edge-docked drawer there). */
  .slideover-scrim { align-items: flex-end; justify-content: center; }
  .slideover-panel {
    width: 100%; max-width: 100%; height: auto; max-height: 92dvh;
    border-radius: 16px 16px 0 0; border-left: none; border-top: 1px solid var(--border);
    transform: translateY(100%);
  }
  .slideover-scrim.open .slideover-panel { transform: none; }
  .slideover-grab { display: block; flex: 0 0 auto; width: 36px; height: 4px; border-radius: 999px; background: var(--border); margin: 8px auto 0; }
  .slideover-head { padding: 6px 16px 13px; }
  .slideover-body { padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px)); }
  .slideover-foot { padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }

  /* tables: momentum scroll + a hair tighter so more fits before scrolling */
  .table-wrap { -webkit-overflow-scrolling: touch; }
  .table th, .table td { padding: 9px 10px; }

  /* toolbars wrap and breathe; pagers/segmented controls easier to thumb */
  .toolbar { gap: 7px; }
  /* LAW-A 2026-08-15 (#24 follow-up) — LIVE-VERIFIED GAP: this selector (.toolbar .btn,
     2 classes) beats the plain .btn rule above (1 class) regardless of source order, so
     the #24 fix silently never reached the highest-traffic buttons in the app — every
     list screen's own toolbar (Filters/Display/+New job measured at 40px, not 44px,
     via getBoundingClientRect at 375×812 before this line was found and fixed). */
  .toolbar .btn, .pager button { min-height: 44px; }

  /* two-up card grids go single-column for readability */
  .kpi-row { grid-template-columns: 1fr 1fr; }
}

/* SPEC-WAVE0-A A5 2026-09-02 — the 44px touch floor two blocks above was scoped
   to max-width:640px ONLY, so an iPad (820px wide, hover:none, pointer:coarse)
   never got it: measured 67 of 67 .btn under 44px on Today at 820
   (reports-2026-09/audit-sys/inv-smallscreen.json). A phone has no width this
   wide, so width alone can't tell "held in the hand" from "sat on a desk" —
   touch has no hover to reveal a bigger target on request, so the SAME floor
   applies whenever the primary input is coarse+hoverless, independent of
   viewport width (mirrors the identical (hover:none) escape the pencil rule
   above already uses). A standalone block, not a widened media list on the
   640px rules above, so the phone-only padding/font-size/layout rules in that
   block stay phone-only. */
@media (hover: none) and (pointer: coarse) {
  .btn { min-height: 44px; }
  .btn-sm { min-height: 44px; }
  select.select, .table select { min-height: 44px; }
  .toolbar .btn, .pager button { min-height: 44px; }
  /* the Stage pill's own 44px floor (SPEC-V2TRACKFIT gap 1's `.jt-stage-pill`,
     below) was max-width:640px too — same iPad gap. */
  .jt-stage-pill { min-height: 44px; }
}

/* WORLD-CLASS ④ — small phones keep KPI tiles TWO-UP for density (one giant metric
   per screenful was too sparse for field crew); tighten padding + numerals so two fit
   cleanly. Only the tiniest phones fall back to a single column. */
@media (max-width: 400px) {
  .kpi-row { grid-template-columns: 1fr 1fr; gap: 8px; }
  .kpi-card { padding: 12px 13px; }
  .kpi-value { font-size: 21px; }
}
@media (max-width: 330px) {
  .kpi-row { grid-template-columns: 1fr; }
}

/* ============ SPEC33 — consolidated admin tabs (parent sub-tabs) ============
   A segmented control under a parent tab (Home/Jobs/Clients/Growth/Library) that
   swaps between the existing child views. Quietly distinct from the dark sidebar
   nav so there's no "which level am I on" confusion. */
/* D9(b) — a persistent "Parent › Child" line ABOVE .subtabs, so a view can never
   render looking orphaned under a stale sub-tab bar (FRIDAY opened from anywhere
   reads "Business › FRIDAY" before .subtabs even paints).
   SPEC-V2POLISH1 D1 2026-08-28 — measured the prototype shell directly (design-
   handoff/VVT-Hub-Owner.dc.html, header+segment markup, lines ~28-75): there is NO
   breadcrumb row on any screen — only the segment bar sits under the header. Ours
   stacked header hairline → .crumb → .subtabs hairline, which read as "muddy"
   (Vincent's word) next to the prototype's single clean seam. .crumb's own DOM/text
   is untouched (tests/suite.js design-D9 + the NAV crumb-content tests read
   textContent, never visibility, so they stay green) — this is a DESKTOP-ONLY
   display:none; it stays visible ≤640px because #mobile-topbar's title is static
   ("VVT Hub", App.refreshMobileTitle only updates .mt-title) and a phone has no
   segment-bar-adjacent "which level am I on" cue otherwise. Decision + evidence
   logged in reports-2026-08/v2polish1-report.md. */
.crumb { font-size: 12px; color: var(--faint); text-transform: uppercase; letter-spacing: var(--ls-label); margin: 0 0 var(--space-2); }
@media (min-width: 641px) { .crumb { display: none; } }

/* SPEC-DHSHELL D3 — the segment bar: was a wrapping row of pill buttons (desktop) that
   only scrolled sideways ≤640px; the brief wants it ALWAYS horizontally scrolling, with
   active = underline + weight rather than a filled pill. Same .subtabs/.subtab DOM,
   same registerParentView click handlers/dataset — restyle only.
   SPEC-V2POLISH1 D1 — re-measured against the prototype's own segment row (segs.map,
   VVT-Hub-Owner.dc.html ~2895-2908): label 13px (was 13.5, a hand-typed one-off),
   active weight 600/--fw-semibold (was --fw-bold/700 — the prototype never goes past
   600 here), active text color the DARKER --accent-hover #1e3f66 (was --accent
   #2b608f, the SAME color as the underline below it — the prototype deliberately
   splits ink vs rule: bd:'#2b608f' but fg:'#1e3f66'). Underline color/weight (2px
   solid --accent) already matched and is untouched. */
.subtabs {
  display: flex; gap: 22px; align-items: center; flex-wrap: nowrap;
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
  margin: 0 0 18px; padding-bottom: 0; border-bottom: 1px solid var(--border);
}
.subtabs::-webkit-scrollbar { display: none; }
.subtab {
  appearance: none; border: none; border-bottom: 2px solid transparent; background: transparent;
  color: var(--muted); font: inherit; font-size: 13px; font-weight: var(--fw-medium);
  padding: 11px 1px; border-radius: 0; cursor: pointer; white-space: nowrap; flex: 0 0 auto;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.subtab:hover { color: var(--text); }
.subtab.active { color: var(--accent-hover); font-weight: var(--fw-semibold); border-bottom-color: var(--accent); }
.subtab-body { min-height: 40px; }

@media (max-width: 640px) {
  .subtabs { gap: 16px; margin-bottom: 14px; }
  .subtab { padding: 12px 1px; min-height: 40px; }
}

/* WAVE0-A A7 (should-land polish, taken up by Section B in Wave 1) — a
   parent view with many segments (Clients: Directory/Retainers/Top Agents/
   Leads/Consult/Opportunities — 15 segments measure 1258px) overflows the
   nowrap/hidden-scrollbar strip well before 640px; tighter gap/padding buys
   room, and a right-edge fade cue (reusing the SAME .scroll-fade-x mechanism
   D17(a)/D22 already use for tables/boards, styles.css ~760-769 — generic
   enough to apply to any horizontally-scrolling container) says "more this
   way" instead of the row just cutting off mid-label. CSS-only here: wiring
   App.wireScrollFade(bar) at the .subtabs build site is core.js (~23232),
   Section A's file this wave — left as a note in the Wave-1B report rather
   than an edit here. Without that JS hook the fade renders statically
   (always visible when the strip actually overflows, per the plain
   max-width query below — .at-end just won't fade out at the scrolled-end
   until the hook lands). */
@media (max-width: 1439px) {
  .subtabs { gap: 15px; }
  .subtab { padding: 10px 1px; }
  .subtabs.scroll-fade-x { position: relative; }
  .subtabs.scroll-fade-x::after {
    content: ''; position: absolute; top: 0; right: 0; bottom: 0; width: 28px;
    background: linear-gradient(to right, transparent, var(--bg));
    pointer-events: none; opacity: 1; transition: opacity var(--t-fast) var(--ease);
  }
  .subtabs.scroll-fade-x.at-end::after { opacity: 0; }
  @media (prefers-reduced-motion: reduce) { .subtabs.scroll-fade-x::after { transition: none; } }
}

/* ============ SPEC34 — SOP/Training role audience ============ */
/* "★ For you" section header — accented so the viewer's own docs stand out */
.doc-cat-foryou { color: var(--accent); font-weight: var(--fw-black); }
/* "Other roles" collapsible toggle in the SOP list */
.doc-cat-toggle { color: var(--faint); user-select: none; }
.doc-cat-toggle:hover { color: var(--text); }
/* audience picker (modal) */
.audience-picker { display: flex; flex-direction: column; gap: 4px; margin-top: 10px; }
.audience-opt {
  display: flex; align-items: center; gap: 10px; padding: 9px 11px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; font-size: var(--fs-body);
}
.audience-opt:hover { background: var(--accent-soft); border-color: var(--accent); }
.audience-opt input { width: 17px; height: 17px; flex: 0 0 auto; accent-color: var(--accent); }

/* ============ SPEC35 — Shot List ============ */
.shotlist-rule {
  margin: 12px 0 4px; padding: 10px 13px; border-radius: var(--radius-sm);
  background: var(--accent-soft); color: var(--text); font-size: var(--fs-caption);
}
.shotlist-zone {
  margin: 18px 0 6px; font-size: 11.5px; font-weight: var(--fw-bold); text-transform: uppercase;
  letter-spacing: .5px; color: var(--muted);
}
.shot-row {
  display: flex; align-items: flex-start; gap: 12px; padding: 9px 4px;
  border-bottom: 1px solid var(--border);
}
.shot-row:last-child { border-bottom: none; }
.shot-num {
  flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%;
  background: var(--hover); color: var(--muted); font-size: var(--fs-meta); font-weight: var(--fw-bold);
  display: flex; align-items: center; justify-content: center; margin-top: 1px;
}
.shot-body { flex: 1; min-width: 0; }
.shot-name { font-weight: var(--fw-semibold); font-size: var(--fs-body); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.shot-core {
  font-size: 10px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .4px;
  color: var(--accent); background: var(--accent-soft); padding: 1px 7px; border-radius: 999px;
}
.shot-example { font-size: var(--fs-micro); font-weight: var(--fw-semibold); color: var(--accent); }

/* ============ SPEC42 — date-range bar + delta badges ============ */
.range-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.range-presets { display: inline-flex; gap: 2px; background: var(--bg-2); border-radius: 999px; padding: 3px; }
.range-preset {
  border: none; background: transparent; color: var(--muted); font: inherit; font-size: 12.5px; font-weight: var(--fw-semibold);
  padding: 5px 12px; border-radius: 999px; cursor: pointer; white-space: nowrap;
}
.range-preset:hover { color: var(--text); }
.range-preset.active { background: var(--panel); color: var(--accent); box-shadow: var(--shadow); }
.range-custom { display: inline-flex; align-items: center; gap: 6px; }
.range-custom .input { padding: 5px 8px; font-size: 12.5px; }
.range-label { font-size: var(--fs-meta); color: var(--muted); }
.delta { display: inline-flex; align-items: center; gap: 3px; font-size: var(--fs-meta); font-weight: var(--fw-bold); padding: 1px 7px; border-radius: 999px; white-space: nowrap; }
.delta.good { color: var(--ok); background: var(--ok-soft); }
.delta.bad { color: var(--danger); background: var(--danger-soft); }
.delta.flat { color: var(--muted); background: var(--hover); }
@media (max-width: 640px) {
  .range-presets { overflow-x: auto; max-width: 100%; }
}

/* SPEC43 — outreach day-sheet (spreadsheet-like inline editing) */
.day-sheet td { vertical-align: middle; padding: 4px 8px; }
.day-sheet .cell-bare { border: none !important; background: transparent !important; box-shadow: none !important; padding: 5px 6px; width: 100%; min-width: 90px; }
.day-sheet .cell-bare:focus { background: var(--accent-soft) !important; border-radius: 6px; }
.day-sheet td:nth-child(4) .cell-bare { min-width: 200px; }
.shot-detail { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.shot-actions { flex: 0 0 auto; display: flex; gap: 2px; }
.icon-btn {
  border: none; background: transparent; color: var(--faint); cursor: pointer;
  font-size: var(--fs-body); padding: 4px 7px; border-radius: 6px; line-height: 1;
}
.icon-btn:hover { background: var(--hover); color: var(--text); }
.shotlist-foot { margin-top: 16px; font-size: 12.5px; padding: 10px 12px; background: var(--subtle); border-radius: var(--radius-sm); }

/* per-job shot checklist (job modal) */
.jobshots-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.jobshots-progress { font-size: 12.5px; font-weight: var(--fw-bold); color: var(--accent); }
.jobshot {
  display: flex; align-items: flex-start; gap: 10px; padding: 7px 2px; cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.jobshot:last-child { border-bottom: none; }
.jobshot input { width: 18px; height: 18px; flex: 0 0 auto; margin-top: 1px; accent-color: var(--ok); cursor: pointer; }
.jobshot.done .js-name { text-decoration: line-through; color: var(--muted); }
.js-name { font-size: 13.5px; font-weight: var(--fw-semibold); }
.js-detail { font-size: var(--fs-meta); color: var(--muted); margin-top: 1px; }

/* ============ SPEC36 — private "you're improving" QA trend card ============ */
.improve-card { display: flex; align-items: center; gap: 16px; margin-bottom: 16px; }
.improve-ring {
  flex: 0 0 auto; width: 62px; height: 62px; border-radius: 50%;
  display: flex; align-items: baseline; justify-content: center; gap: 1px;
  background: var(--accent-soft); border: 2px solid var(--accent);
}
.improve-ring.up { background: var(--ok-soft); border-color: var(--ok); }
.improve-ring.flat { background: var(--hover); border-color: var(--border-strong); }
.improve-score { font-size: var(--fs-h1); font-weight: var(--fw-black); color: var(--text); }
.improve-of { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--muted); }
.improve-body { min-width: 0; }
.improve-title { font-weight: var(--fw-bold); font-size: 15px; display: flex; align-items: center; gap: 7px; }
.improve-arrow { font-size: var(--fs-caption); }
.improve-arrow.up { color: var(--ok); }
.improve-arrow.flat { color: var(--muted); }
.improve-line { font-size: var(--fs-caption); color: var(--text); margin-top: 2px; }
.improve-sub { font-size: 11.5px; color: var(--faint); margin-top: 3px; }

/* ============ SPEC155 — growth / coaching ============ */
.growth-node { display: flex; flex-direction: column; gap: 8px; }
.growth-signals { display: flex; flex-wrap: wrap; gap: 6px; }
.growth-chip { display: inline-flex; align-items: baseline; gap: 5px; padding: 3px 9px; border-radius: 999px;
  background: var(--subtle); border: 1px solid var(--border); font-size: var(--fs-meta); }
.growth-chip-k { color: var(--muted); font-size: var(--fs-micro); }
.growth-chip-v { font-weight: var(--fw-bold); }
.growth-group { display: flex; flex-direction: column; gap: 4px; }
.growth-group-title { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); }
.growth-item { display: flex; align-items: baseline; gap: 7px; font-size: var(--fs-caption); line-height: 1.4; }
.growth-item-text { flex: 1 1 auto; min-width: 0; }
.growth-tag { font-size: var(--fs-micro); flex: 0 0 auto; }
.growth-by { color: var(--faint); font-size: 11.5px; white-space: nowrap; flex: 0 0 auto; }
.growth-empty { font-size: 12.5px; line-height: 1.45; }
.growth-ai-card { margin-top: 8px; padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--accent-soft); border: 1px solid var(--accent); }
.growth-ai-label { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--accent); margin-bottom: 4px; }
.growth-ai-text { font-size: var(--fs-caption); line-height: var(--lh-normal); white-space: pre-wrap; }
.growth-summary { cursor: pointer; font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--accent); user-select: none; }
.growth-details[open] .growth-summary { margin-bottom: 4px; }
@media (max-width: 760px) {
  .growth-compose .btn, .growth-ai .btn { min-height: 44px; }
}

/* ============ SPEC156 — video review (Frame.io-style) ============ */
/* library grid */
.review-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
.review-filters { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.review-fchip { padding: 5px 12px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel);
  font-size: 12.5px; font-weight: var(--fw-semibold); cursor: pointer; color: var(--muted); }
.review-fchip:hover { background: var(--hover); }
.review-fchip.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.review-card { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--panel);
  cursor: pointer; transition: transform .08s ease, box-shadow .12s ease; display: flex; flex-direction: column; }
.review-card:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,.12); }
.review-thumb { position: relative; aspect-ratio: 16/9; background: #11141c center/cover no-repeat;
  display: flex; align-items: center; justify-content: center; }
.review-thumb-ico { font-size: 38px; opacity: .65; }
.review-thumb-badge { position: absolute; left: 8px; bottom: 8px; font-size: var(--fs-micro); font-weight: var(--fw-bold); color: #fff;
  background: rgba(0,0,0,.6); padding: 2px 8px; border-radius: 999px; }
.review-card-body { padding: 12px; }
.review-card-title { font-weight: var(--fw-bold); font-size: var(--fs-body); line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* full-screen player overlay */
.review-overlay { position: fixed; inset: 0; z-index: 1000; display: flex; background: rgba(8,10,14,.92);
  backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }   /* SAFPOLISH 2026-08-25 D4 */
.review-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; position: relative; padding: 18px; gap: 12px; }
.review-close { position: absolute; top: 14px; right: 14px; z-index: 3; background: rgba(0,0,0,.5); color: #fff; border-color: transparent; }
.review-stage { flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center; position: relative; }
.review-video { max-width: 100%; max-height: 100%; border-radius: 8px; background: #000; outline: none; }
.review-err { position: absolute; bottom: 12px; left: 12px; right: 12px; color: #fde68a; background: rgba(0,0,0,.7);
  padding: 10px 12px; border-radius: 8px; font-size: 12.5px; text-align: center; }
.review-transport { display: flex; align-items: center; gap: 10px; color: #e7e9ee; }
.review-play { min-width: 42px; font-size: var(--fs-body); }
.review-scrub-wrap { flex: 1 1 auto; min-width: 0; }
.review-scrub { position: relative; height: 16px; display: flex; align-items: center; cursor: pointer; touch-action: none; }
.review-scrub::before { content: ''; position: absolute; left: 0; right: 0; height: 5px; border-radius: 999px; background: rgba(255,255,255,.22); }
.review-scrub-fill { position: absolute; left: 0; height: 5px; border-radius: 999px; background: var(--accent); width: 0; }
.review-scrub-markers { position: absolute; left: 0; right: 0; top: 0; bottom: 0; }
.review-marker { position: absolute; top: 50%; transform: translate(-50%, -50%); width: 12px; height: 12px;
  border-radius: 50%; background: var(--warn); border: 2px solid #fff; cursor: pointer; padding: 0; }
.review-marker.resolved { background: var(--ok); opacity: .8; }
.review-marker:hover { transform: translate(-50%, -50%) scale(1.3); }
.review-tc-wrap { font-variant-numeric: tabular-nums; font-size: var(--fs-caption); white-space: nowrap; }
.review-tc { font-weight: var(--fw-bold); }
.review-tc.muted { color: rgba(255,255,255,.6); font-weight: var(--fw-medium); }

/* comments side panel */
.review-panel { flex: 0 0 360px; max-width: 40vw; background: var(--bg); border-left: 1px solid var(--border);
  display: flex; flex-direction: column; min-height: 0; }
.review-panel-head { padding: 16px; border-bottom: 1px solid var(--border); display: flex; gap: 10px; align-items: flex-start; justify-content: space-between; }
.review-title { font-weight: var(--fw-black); font-size: 15px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; }
.review-panel-sub { padding: 10px 16px; display: flex; align-items: center; gap: 8px; font-weight: var(--fw-bold); font-size: var(--fs-caption); border-bottom: 1px solid var(--border); }
.review-clist { flex: 1 1 auto; overflow-y: auto; padding: 8px 12px; }
.review-crow { padding: 10px; border-radius: var(--radius-sm); margin-bottom: 8px; background: var(--panel); border: 1px solid var(--border); }
.review-crow.resolved { opacity: .62; }
.review-crow.reply { margin: 6px 0 0 14px; background: var(--subtle); }
.review-crow-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; flex-wrap: wrap; }
.review-tcchip { font-variant-numeric: tabular-nums; font-weight: var(--fw-bold); font-size: var(--fs-meta); color: var(--accent);
  background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 6px; padding: 1px 7px; cursor: pointer; }
.review-cby { font-weight: var(--fw-bold); font-size: 12.5px; }
.review-cago { font-size: 11.5px; }
.review-ctext { font-size: var(--fs-caption); line-height: 1.45; white-space: pre-wrap; word-break: break-word; }
.review-resolve { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--muted); background: none; border: 1px solid var(--border);
  border-radius: 999px; padding: 2px 9px; cursor: pointer; }
.review-resolve.on { color: var(--ok); border-color: var(--ok); }
.review-crow-foot { margin-top: 6px; }
.review-replybtn { font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--muted); background: none; border: none; cursor: pointer; padding: 0; }
.review-replyin { margin-top: 6px; }
.review-compose { padding: 12px 16px; border-top: 1px solid var(--border); }
.review-note { width: 100%; resize: vertical; }
.review-capture { font-variant-numeric: tabular-nums; font-weight: var(--fw-bold); font-size: var(--fs-meta); color: var(--accent);
  background: var(--accent-soft); border-radius: 6px; padding: 2px 8px; }

/* P2 — frame drawing */
.review-canvas { position: absolute; pointer-events: none; z-index: 2; touch-action: none; }   /* draw, don't scroll the page on touch */
.review-palette { position: absolute; top: 10px; left: 10px; z-index: 4; display: flex; flex-direction: column; gap: 6px;
  background: rgba(18,20,28,.92); border: 1px solid rgba(255,255,255,.15); border-radius: 10px; padding: 8px; }
.review-palette-row { display: flex; gap: 6px; }
.review-tool { width: 30px; height: 30px; border-radius: 7px; border: 1px solid rgba(255,255,255,.18); background: rgba(255,255,255,.06);
  color: #fff; font-size: var(--fs-body); cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.review-tool.on { background: var(--accent); border-color: var(--accent); }
.review-swatch { width: 24px; height: 24px; border-radius: 50%; border: 2px solid rgba(255,255,255,.35); cursor: pointer; padding: 0; }
.review-swatch.on { border-color: #fff; box-shadow: 0 0 0 2px var(--accent); }
.review-drawtoggle.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.review-hasdraw { font-size: var(--fs-meta); flex: 0 0 auto; }
/* P3 — versions */
.review-vers { display: flex; align-items: center; gap: 8px; }

/* ---- P5 client/internal split + visual polish ---- */
.review-marker { transition: transform .12s ease, background .15s ease; }
.review-marker.client { background: #ff7a3d; }
.review-marker.client.resolved { background: var(--ok); }   /* resolved wins over the client tint */
.review-scrub-fill { transition: width .06s linear; }
.review-play, .review-tcchip, .review-resolve, .review-tool, .review-swatch, .review-drawtoggle, .review-marker, .review-fchip { transition: background .15s ease, color .15s ease, transform .1s ease, box-shadow .15s ease; }
.review-tcchip:hover { transform: translateY(-1px); }
.review-resolve:hover { background: var(--hover); }
.review-crow { animation: reviewIn .22s cubic-bezier(.2,.7,.3,1) both; }
@keyframes reviewIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.review-card { animation: reviewIn .22s cubic-bezier(.2,.7,.3,1) both; }
.btn.pop { animation: reviewPop .24s ease; }
@keyframes reviewPop { 0%{transform:scale(1)} 40%{transform:scale(.93)} 100%{transform:scale(1)} }
.review-clientbadge { font-size: 10.5px; font-weight: var(--fw-black); color: #c2410c; background: rgba(255,122,61,.16);
  border: 1px solid rgba(255,122,61,.4); border-radius: 999px; padding: 1px 7px; flex: 0 0 auto; }
.review-crow.client { border-left: 3px solid #ff7a3d; }
.review-brandline { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--accent); margin-bottom: 4px; }
.review-empty-notes { text-align: center; padding: 28px 14px; }
.review-empty-ico { font-size: 30px; margin-bottom: 8px; opacity: .8; }

/* client mode: cleaner, brand-forward, bigger touch targets, calmer chrome */
.review-client .review-panel { background: var(--panel); }
.review-client .review-panel-head { padding: 18px 16px; }
.review-client .review-title { font-size: 17px; }
.review-client .review-note { min-height: 64px; font-size: var(--fs-body); }
.review-client .review-compose .btn-primary { padding: 9px 18px; font-weight: var(--fw-bold); font-size: 13.5px; }
.review-client .review-marker { width: 14px; height: 14px; }
.review-client .review-drawtoggle { font-size: 12.5px; }
.review-client .review-tcchip { font-size: 12.5px; padding: 2px 9px; }

@media (max-width: 860px) {
  .review-overlay { flex-direction: column; }
  /* STAGE1 2026-08-14 (design item 7 / G1, parity-frameio.md) — MEASURED DEFECT: at
     375×812 with a 9:16 (540×960) source, .review-main refused to shrink (705px of a
     812px column) and the panel was squeezed to 107px, collapsing the comments list to
     16px holding 1022px of content. Root cause is the classic flex trap: a column-flex
     item's default min-height is `auto` (= its content's intrinsic min size), and a
     tall vertical <video> pushes that intrinsic size past the space .review-panel's
     44vh cap actually leaves it — `flex:1 1 auto` alone can shrink BELOW content only
     once min-height:0 removes that floor. `.review-stage` already had it (line ~2324);
     `.review-main` — one level up the same chain — did not. Every deliverable in scope
     is 9:16 and agents review on phones, so this is the one G1 blocker. Two-part fix,
     both needed: min-height:0 lets main actually shrink to the space the panel's cap
     leaves it; the video's own max-height stops it from RE-forcing that same fight the
     next time content changes (belt + suspenders, not redundant — the min-height fix
     alone still lets a giant intrinsic size win a shrink/grow tug-of-war before layout
     settles on some runs). */
  .review-main { flex: 1 1 auto; padding: 10px; min-height: 0; }
  .review-video { max-height: 46vh; }
  /* SECOND defect, same root symptom, found live-testing the fix above (a real
     540×960 blob loaded into the player, not just measured statically): with main
     correctly shrinking now, .review-panel gets its full intended 357px (44vh @
     812px) — but panel-head + panel-sub + the comments list + the compose block
     (audience row, draw/attach/emoji controls, textarea) TOGETHER measured 484px,
     and `.review-panel` itself had no overflow rule (only `.review-clist` did), so
     the extra 128px spilled silently past the fixed, non-scrolling `.review-overlay`
     — the compose textarea and Send button sat off-screen and unreachable, same
     "composer sat below the fold" symptom the audit named, just no longer masked by
     the bigger main-shrinking bug. Making the panel itself the scroll container (the
     clist's own overflow-y:auto still applies inside it, redundant but harmless)
     guarantees every control stays reachable regardless of how tall the compose
     block gets on a narrow screen. */
  .review-panel { flex: 0 0 auto; max-width: none; max-height: 44vh; overflow-y: auto; border-left: none; border-top: 1px solid var(--border); }
  .review-transport .btn { min-height: 40px; }
  .review-tool { width: 40px; height: 40px; }     /* 44px-ish touch targets on phones */
  .review-swatch { width: 34px; height: 34px; }
}

/* ============ SPEC37 — Team Chat ============ */
.nav-badge {
  margin-left: auto; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px;
  background: #475569; color: #fff; font-size: var(--fs-micro); font-weight: var(--fw-bold); line-height: 18px;
  text-align: center; flex: 0 0 auto;
}
.nav-badge.mention { background: var(--danger); }

.chat-layout { display: flex; gap: 16px; height: calc(100vh - 104px); height: calc(100dvh - 104px); min-height: 420px; }
.chat-rail {
  /* SPEC-DHCHAT D1 2026-08-27 — README's Chat spec: "Left column, 252px" (was 210px,
     a hand-typed one-off never on the app's own spacing scale). Presentation only —
     the three-group content (Channels / Direct messages, no job-thread group since
     none exists yet — see chat.js buildRail) is untouched.
     SPEC-V2CHATB item 7 2026-08-28 — re-measured the prototype directly (raw CDP,
     design-handoff/VVT-Hub-Owner.dc.html, README §Chat's own text) and it reads
     "Left rail (262px)", not 252px — 1a's D1 note misquoted the README by 10px.
     Corrected here; the SLACK-M @1280 width literal test (tests/suite.js) is moved
     alongside this to assert 262, not weakened. */
  flex: 0 0 262px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
  border-right: 1px solid var(--border); padding-right: 8px;
}
.chat-rail-head {
  font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .6px;
  color: var(--faint); margin: 14px 8px 4px;
}
/* SPEC-V2POLISH1 D2 — row padding measured against the prototype's rail rows
   (VVT-Hub-Owner.dc.html chatGroups items: "padding:5px 10px", row height 34/44px
   depending on whether a sub-line is present) — was 7px 10px, a hand-typed one-off
   that ran every row taller than the prototype's. font-size here is superseded by
   the later --fs-caption (13px) rule below (styles.css SPEC-DHCHAT D1 type-scale
   pass) which already equals the prototype's measured 13px — left as-is, not
   touched twice. */
.chat-ch {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 5px 10px; border: none; background: transparent; border-radius: 7px;
  cursor: pointer; font: inherit; font-size: 13.5px; color: var(--text);
}
.chat-ch:hover { background: var(--hover); }
.chat-ch.active { background: var(--accent-soft); color: var(--accent); font-weight: var(--fw-semibold); }
/* VISUAL 07-30 — an unread channel now reads as unread from weight alone, not just the
   count pill (Slack's cue). Read channels sit back so the unread ones come forward. */
.chat-ch.has-unread .chat-ch-label { font-weight: var(--fw-bold); color: var(--text); }
.chat-ch-av { flex: 0 0 auto; }
.chat-ch svg { flex: 0 0 auto; opacity: .75; }
/* SPEC-V2CHAT D1 2026-08-28 — the label wrapper carries the flex:1/ellipsis a bare
   .chat-ch-label used to own by itself; it now also holds the optional scoped-DM
   subtitle (.chat-ch-sub — "<address> · <service>", chat.js scopedDmContext) on a
   second line, so a row still reads at a glance while carrying job context. */
.chat-ch-labelwrap { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.chat-ch-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* SPEC-V2POLISH1 D2 — prototype row sub-line is 11.5px / #656d7b (--faint), a hand-
   typed 11px/--muted one-off before this. */
.chat-ch-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11.5px; color: var(--faint); }
.chat-ch.active .chat-ch-sub { color: inherit; opacity: .75; }
/* SPEC-V2CHATSHELL 2026-08-29 (audit §Chat, "Scoped DM rows" PARTIAL) — the
   ~19×14 job-thumb box in front of the sub-line, click-mapped on the running
   prototype (:8123, VVT-Hub-Owner.dc.html DIRECT rows). A styled empty box,
   same convention as .chat-scope-thumb — no per-job photo field exists to bind
   a real <img> to, so this never fabricates a picture. */
.chat-ch-sub-row { display: flex; align-items: center; gap: 5px; min-width: 0; }
.chat-ch-sub-thumb { width: 19px; height: 14px; flex: none; border-radius: 3px; border: 1px solid var(--border); background: var(--subtle); }
.chat-ch-sub-row .chat-ch-sub { flex: 1; min-width: 0; }
.chat-ch-badge { background: var(--danger); color: var(--on-danger); font-size: 10.5px; font-weight: var(--fw-bold); border-radius: 999px; min-width: 17px; height: 17px; line-height: 17px; text-align: center; padding: 0 5px; }
/* SPEC-V2CHATB item 4 2026-08-28 — Organize's manual-order drag handle; only
   rendered (and only draggable) while sort:'manual' is chosen, chat.js buildRail. */
.chat-ch--manual { cursor: grab; }
.chat-ch-grip { flex: 0 0 auto; color: var(--faint); font-size: 12px; letter-spacing: -1px; cursor: grab; }

/* PLAN-CHATV2-2026-09-06 phase 3 item 1 — the "You have N unsent files — Open" nudge,
   a persistent rail child (chat.js: railOrphanWrap) same tier as railSearchWrap/
   railPresenceWrap right below. Quiet by default (display:none until there is
   something to say), same accent-soft treatment .chat-pending already uses for
   "something is waiting on you" rather than an alarming red. */
.chat-rail-orphan { padding: 0 8px 6px; }
.chat-orphan-banner {
  width: 100%; display: flex; align-items: center; gap: 6px; text-align: left;
  border: 1px solid var(--border); border-radius: 8px; background: var(--accent-soft);
  color: var(--accent); font-size: var(--fs-caption); padding: 7px 10px; cursor: pointer;
}
.chat-orphan-banner:hover { filter: brightness(0.97); }

/* SPEC-V2CHAT D1 — rail search box + presence block, both persistent siblings of the
   rebuilt channel/DM list (chat.js: railSearchWrap/railPresenceWrap/railGroupsWrap). */
.chat-rail-search { padding: 2px 8px 8px; display: flex; align-items: center; gap: 6px; }
/* SPEC-V2POLISH1 D2 — the prototype's rail search box + Organize button are both a
   fixed 34px tall (VVT-Hub-Owner.dc.html chat rail head: search "height:34px", the
   Organize trigger "width:34px;height:34px") so they read as one even row; ours had
   no explicit height on the input (6px 10px padding only, ~28px rendered) beside a
   30px button — a visible 1-2px stagger the prototype doesn't have. */
.chat-rail-search-input { flex: 1 1 auto; min-width: 0; height: 34px; font-size: 13px; padding: 0 10px; }
/* SPEC-V2CHATB item 4 2026-08-28 — the Organize trigger + its popover. */
.chat-rail-organize-btn {
  flex: 0 0 auto; width: 34px; height: 34px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--muted); cursor: pointer; font-size: 13px;
}
.chat-rail-organize-btn:hover { background: var(--hover); color: var(--text); }
.chat-organize-pop { width: 260px; padding: 10px; }
.chat-organize-head { font-size: var(--fs-meta); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin-bottom: 8px; }
.chat-organize-sorts { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
.chat-organize-sort {
  text-align: left; border: 0; background: none; border-radius: 7px; padding: 7px 8px;
  font: inherit; font-size: var(--fs-caption); color: var(--text); cursor: pointer;
}
.chat-organize-sort:hover { background: var(--hover); }
.chat-organize-sort.on { background: var(--accent-soft); color: var(--accent); font-weight: var(--fw-semibold); }
.chat-organize-toggles { display: flex; flex-direction: column; gap: 4px; padding-top: 8px; border-top: 1px solid var(--border); }
.chat-organize-toggle { display: flex; align-items: center; gap: 8px; font-size: var(--fs-caption); padding: 3px 2px; cursor: pointer; }
.chat-organize-hint { margin-top: 8px; font-size: var(--fs-micro); }
.chat-rail-presence { padding-bottom: 4px; border-bottom: 1px solid var(--border); margin-bottom: 4px; }
.chat-rail-presence-row { display: flex; align-items: center; gap: 8px; padding: 5px 10px; }
.chat-presence-lg { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; }
/* filled = app connected (reuses App.chatPresence's own 'active' signal — VISUAL 07-30 (a));
   hollow = scheduled but not signed in, "out working", never a third meaning (README §Chat). */
.chat-presence-lg--active { background: var(--ok); }
.chat-presence-lg--hollow { background: transparent; border: 1.5px solid var(--muted); }
.chat-add { color: var(--muted); }

.chat-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.chat-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-bottom: 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px;
}
.chat-head-title { font-size: var(--fs-md); font-weight: var(--fw-bold); }
/* SLACK-M 2026-08-13 — base state is OFF, unscoped (desktop never sees this button,
   full stop — no media query means no chance of it). Turned on ONLY inside the
   ≤760px query below, and only for .m-convo (chat.js) — see that block. */
.chat-back-btn { display: none; }
.chat-status { font-size: 11.5px; font-weight: var(--fw-semibold); padding: 3px 9px; border-radius: 999px; white-space: nowrap; }
.chat-status.live { background: var(--ok-soft); color: var(--ok); }
/* CHATSTATUS-2026-08-10 — "sync healthy, realtime just isn't instant" is a GOOD state
   and must read as one: same green family as .live, distinguished by wording + tooltip
   (App.chatStatusSignal), not by a duller color that would look like a lesser status. */
.chat-status.ok { background: var(--ok-soft); color: var(--ok-ink); }
.chat-status.wait { background: var(--warn-soft); color: var(--warn); }
/* a REAL problem (erroring pushes, or too long since the last successful sync) —
   distinct from .wait's "in progress, nothing to worry about" amber. */
.chat-status.warn { background: var(--danger-soft); color: var(--danger-ink); }
.chat-status.off { background: var(--hover); color: var(--muted); }

.chat-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; padding: 10px 4px; gap: 2px; }
.chat-msg { display: flex; gap: 10px; padding: 3px 6px; border-radius: 8px; position: relative; }
.chat-msg:hover { background: var(--subtle); }
.chat-msg:hover .chat-msg-reply { opacity: 1; }
.chat-msg.mentions-me { background: var(--warn-soft); box-shadow: inset 3px 0 0 var(--warn); }
.chat-av {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 50%; color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: var(--fs-meta); font-weight: var(--fw-bold); margin-top: 2px;
}
.chat-av-spacer { flex: 0 0 34px; }
.chat-msg-body { flex: 1; min-width: 0; }
.chat-msg-meta { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.chat-author { font-weight: var(--fw-bold); font-size: 13.5px; }
.chat-role { font-size: 10.5px; color: var(--muted); background: var(--hover); padding: 0 6px; border-radius: 999px; }
.chat-time { font-size: var(--fs-micro); color: var(--faint); }
/* VISUAL 07-30 — the reply quote was a hairline + muted text and read as throwaway.
   Treated as a real quoted block (tinted ground, accent rule) the way Basecamp and
   Whop do, so a reply's context is legible at a glance instead of scanned for. */
.chat-quote {
  font-size: var(--fs-meta); color: var(--muted); line-height: var(--lh-snug);
  border-left: 3px solid color-mix(in srgb, var(--accent) 55%, var(--border));
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  padding: 5px 10px; margin: 3px 0 5px; border-radius: 0 7px 7px 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-text { font-size: 13.5px; line-height: 1.45; word-wrap: break-word; overflow-wrap: anywhere; }
.chat-text a { color: var(--accent); }
.chat-mention { background: var(--accent-soft); color: var(--accent); font-weight: var(--fw-semibold); border-radius: 4px; padding: 0 3px; }
.chat-msg-reply { position: absolute; top: 4px; right: 6px; opacity: 0; border: none; background: var(--panel); box-shadow: var(--shadow); border-radius: 6px; cursor: pointer; font-size: var(--fs-caption); padding: 2px 7px; transition: opacity .1s; }

.chat-composer { border-top: 1px solid var(--border); padding-top: 10px; }
.chat-replybar { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: var(--fs-meta); padding: 6px 10px; background: var(--hover); border-radius: 7px; margin-bottom: 6px; }
.chat-reply-x { border: none; background: transparent; cursor: pointer; color: var(--muted); font-size: var(--fs-caption); }
.chat-composer-row { display: flex; gap: 8px; align-items: flex-end; }
.chat-input {
  /* SPEC-V2CHATB item 5 2026-08-28 — README §Chat: "15.5px type, generous padding".
     14px -> 15.5px + roomier padding is DESKTOP-ONLY on purpose: the ≤640px override
     right below (unchanged, still 16px = var(--fs-md)) is the SAFARI 07-30 anti-zoom
     floor — iOS Safari zooms the page on any focused input under 16px, and that fix
     depends on the media query being physically BELOW this rule (equal specificity,
     source order decides — see the DESIGNA trap note staying right where it was). */
  flex: 1; resize: none; border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  padding: 12px 14px; font: inherit; font-size: 15.5px; max-height: 140px; min-height: 42px; outline: none;
}
.chat-input:focus { border-color: var(--accent); box-shadow: var(--focus-ring-soft); }
/* DESIGNA 2026-08-19 — TRAP: the base `.chat-input` rule above is (0,1,0), the SAME
   specificity as the SAFARI 07-30 anti-zoom list near the top of the ≤640 block (~:2150),
   and it sits LATER in the file — media queries add no specificity, so its 14px silently
   won on phones and iOS zoomed on every chat message again. Any ≤640 font-size for
   .chat-input must live BELOW the base rule (here), or the fix dies the same death. */
@media (max-width: 640px) { .chat-input { font-size: var(--fs-md); } }
.chat-send { flex: 0 0 auto; height: 42px; }

/* SLACK-M 2026-08-13 — mobile Chat becomes Slack's list-first pattern: LIST (the
   rail, full-width/full-height, vertically scrolling) or CONVO (chat-main), never
   both, never a horizontal strip. chat.js keeps exactly one of .m-list/.m-convo on
   .chat-layout at all times; EVERY rule below is scoped inside THIS media query so
   the classes are inert on desktop (≥761px) — the side-by-side rail+main is byte-
   identical to before, unchanged.
   REPLACES the old broken block: it flipped .chat-rail to flex-direction:row +
   overflow-x:auto (a horizontal strip) while `.chat-ch { width: 100% }` (the base
   rule above) still applied — so every chip rendered at full VIEWPORT width.
   Measured live: 7 chips at x = 14, 367, 720, 1073…, one visible, six off-screen,
   zero hint more existed. It also set `.chat-rail-head { display: none }` and
   `.chat-add { display: none }`, hiding the "Channels"/"Direct messages" section
   heads AND both "＋ New channel" and "＋ Browse channels" rows. Net effect
   (Vincent's exact report): mobile chat stuck on #general, no DMs, no switching.
   None of that hiding is needed anymore — deleting the override rules restores
   .chat-rail to its own BASE flex-direction:column layout above, which is exactly
   a vertical list; `.chat-ch { width: 100% }` (the very rule that broke the old
   strip) is exactly correct for a full-width list row. */
@media (max-width: 760px) {
  .chat-layout { height: calc(100vh - 150px); height: calc(100dvh - 150px); }

  /* the rail becomes the LIST screen: full width/height instead of a fixed 210px
     sidebar column. Its own internal layout (column, scrolling, section heads,
     rows) is untouched — this only changes .chat-rail's OWN size within the row
     .chat-layout still is. */
  .m-list .chat-rail { flex: 1 1 auto; border-right: none; padding-right: 0; }
  .m-list .chat-main { display: none; }
  .m-convo .chat-rail { display: none; }
  /* .chat-main already gets `display:flex; flex:1` from its own base rule once
     .m-list's display:none above no longer matches — no .m-convo override needed. */

  /* ≥44px touch target, full width — was fighting the row-strip parent before;
     now it IS the list. */
  .chat-ch { min-height: 44px; }

  /* back control — CONVO screen only, left of the header (chat.js prepends it as
     .chat-head's first child). D8 below (MOBILE HEAD BUDGET) gives the channel
     name flex:1 1 100% — its own full line — because an earlier attempt squeezing
     FOUR controls onto one line measured the title at 0px wide. This is a single
     small, fixed-width sibling ahead of it, not four, so sharing line 1 is safe;
     .m-convo's extra class beats D8's plain .chat-head-title on specificity
     regardless of which rule appears later in the file. */
  .m-convo .chat-back-btn {
    display: inline-flex; align-items: center; gap: 2px; flex: 0 0 auto;
    min-height: 44px; padding: 6px 6px 6px 0; margin-right: 2px;
    border: none; background: transparent; cursor: pointer; color: var(--accent);
    font: inherit; font-weight: var(--fw-semibold); font-size: var(--fs-body);
  }
  .m-convo .chat-head-title { flex: 1 1 auto; }

  /* DISCOVERY-C5a (2026-08-13) — the page above .chat-layout keeps ~76px of scroll
     slack at 375×812, so the CONVO header (and its “‹ Chats” back control) slid up
     UNDER the fixed #mobile-topbar (hit-test-proven: elementFromPoint over the back
     control returned mt-burger at scrollY≥52 — rects can't see occlusion, trap 11).
     Stick the header just below the topbar instead: 52px + safe-area matches
     #mobile-topbar's own height rule, background paints over messages scrolling
     beneath, z-index sits above chat content but far below the topbar (50 is the
     topbar's own layer; 5 is plenty here since they never overlap once stuck).
     THE LOAD-BEARING PAIR: sticky binds to the NEAREST scrollable ancestor, and
     #main (overflow-y:auto, its base rule) is one — but on a phone the 76px slack
     lives on the WINDOW (body scrollHeight > viewport), so a #main-bound sticky
     rides straight under the topbar with the page (measured live: headTop 32 at
     scrollY 76). Releasing #main's overflow on the mobile CHAT view only makes the
     window the nearest scrollport, which is the scroll that actually happens here;
     .chat-list keeps its own internal overflow-y:auto untouched, and every other
     view keeps #main exactly as it was. */
  body[data-view="chat"] #main { overflow-y: visible; }
  .m-convo .chat-head {
    position: sticky; top: calc(52px + env(safe-area-inset-top, 0px));
    z-index: 5; background: var(--bg);
  }
}

/* ---- SPEC37.A — chat attachments, reactions, emoji, composer tools ---- */
.chat-msg-actions { position: absolute; top: 2px; right: 6px; display: flex; gap: 2px; opacity: 0; transition: opacity .1s; }
.chat-msg:hover .chat-msg-actions { opacity: 1; }
.chat-msg-act { border: 1px solid var(--border); background: var(--panel); box-shadow: var(--shadow); border-radius: 6px; cursor: pointer; font-size: var(--fs-caption); padding: 2px 7px; }
.chat-msg-act:hover { background: var(--hover); }

.chat-jobcard {
  display: flex; align-items: center; gap: 10px; margin: 6px 0; padding: 9px 11px; max-width: 380px;
  border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--radius-sm);
  background: var(--subtle); cursor: pointer;
}
.chat-jobcard:hover { background: var(--accent-soft); }

/* SPEC-V2CHATB item 1 2026-08-28 — the scope strip under a scoped DM's header
   (measured against the prototype's own anatomy: 38x27 thumbnail, address + two
   pills + a muted why-line, "Open the job →" — design-handoff/VVT-Hub-Owner.dc.html
   raw CDP + README §Chat). Colors map to this app's theme tokens (not the
   prototype's fixed hex — those don't survive dark mode) rather than being copied
   literally. */
.chat-scope-strip {
  flex: none; display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  background: var(--panel); border-bottom: 1px solid var(--border);
}
.chat-scope-thumb {
  width: 38px; height: 27px; flex: none; border-radius: 4px;
  border: 1px solid var(--border); background: var(--subtle);
}
.chat-scope-body { min-width: 0; }
.chat-scope-toprow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.chat-scope-addr { font-size: 13.5px; font-weight: var(--fw-bold); letter-spacing: -.005em; }
.chat-scope-pill { padding: 2px 8px; border-radius: 5px; font-size: 10.5px; font-weight: var(--fw-bold); }
.chat-scope-pill--svc { background: var(--accent-soft); border: 1px solid var(--accent); color: var(--accent); }
.chat-scope-pill--stage { background: var(--tone-neutral-bg); border: 1px solid var(--border); color: var(--tone-neutral-fg); letter-spacing: .04em; }
.chat-scope-why { margin-top: 3px; font-size: var(--fs-meta); color: var(--faint); }
.chat-scope-open {
  flex: none; border: 0; background: none; padding: 0; font-size: 12.5px;
  font-weight: var(--fw-semibold); color: var(--accent); cursor: pointer; white-space: nowrap;
}
.chat-scope-open:hover { text-decoration: underline; }
@media (max-width: 640px) { .chat-scope-strip { padding: 8px 10px; gap: 8px; } .chat-scope-why { display: none; } }

/* SPEC-V2CHATB item 3 2026-08-28 — the #shoots day board: measured against the
   prototype's own row anatomy (shooter-colour left edge, time column, thumbnail-
   sized address column, service chips, a status flag, one CTA), color mapped to
   this app's theme tokens. App.crewColor supplies the edge color inline per row
   (chat.js) since it's per-person data, not a fixed token. */
.chat-board { flex: none; padding: 14px 14px 4px; }
.chat-board-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; padding: 0 2px 10px; }
.chat-board-title { font-size: 15px; font-weight: var(--fw-bold); letter-spacing: -.01em; }
.chat-board-sub { margin-top: 2px; font-size: var(--fs-meta); color: var(--faint); }
.chat-board-rows { display: flex; flex-direction: column; gap: 8px; }
.chat-board-row {
  display: flex; align-items: center; gap: 12px; background: var(--panel);
  border: 1px solid var(--border); border-left: 3px solid var(--border-strong); border-radius: 11px;
  padding: 10px 12px; box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
}
.chat-board-time { flex: 0 0 auto; width: 48px; font-size: var(--fs-body); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.chat-board-body { flex: 1; min-width: 0; }
.chat-board-addr { font-size: var(--fs-body); font-weight: var(--fw-bold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-board-metarow { display: flex; align-items: center; gap: 6px; margin-top: 3px; flex-wrap: wrap; }
.chat-board-agent { font-size: var(--fs-meta); color: var(--faint); }
.chat-board-svc { padding: 1px 7px; border-radius: 5px; background: var(--tone-neutral-bg); border: 1px solid var(--border); font-size: 10.5px; font-weight: var(--fw-semibold); color: var(--tone-neutral-fg); }
.chat-board-flag { flex: none; padding: 2px 9px; border-radius: 999px; background: var(--tone-neutral-bg); border: 1px solid var(--border); font-size: 11px; font-weight: var(--fw-bold); color: var(--tone-neutral-fg); white-space: nowrap; }
.chat-board-cta {
  flex: none; display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 11px;
  border: 1px solid var(--border-strong); border-radius: 8px; background: var(--panel);
  font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--accent); cursor: pointer;
}
.chat-board-cta:hover { border-color: var(--accent); background: var(--accent-soft); }
.chat-board-divider { display: flex; align-items: center; gap: 12px; padding: 12px 2px 0; }
.chat-board-divider::before, .chat-board-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.chat-board-divider span { font-size: 11.5px; color: var(--faint); white-space: nowrap; }
@media (max-width: 640px) { .chat-board-row { flex-wrap: wrap; } .chat-board-cta span { display: none; } }

/* SPEC-V2CHATB item 2 2026-08-28 — the right panel: Decided/Media/Links. Docked
   position:fixed drawer (implementation note №3, see the chat.js comment on
   infoPanelWrap for why fixed over a permanent third column), width matches the
   prototype's own 304px right column. */
.chat-info-panel {
  position: fixed; top: 0; right: 0; bottom: 0; width: 304px; max-width: 92vw;
  background: var(--panel); border-left: 1px solid var(--border); box-shadow: -8px 0 24px -8px rgba(16, 24, 40, .18);
  z-index: 400; display: flex; flex-direction: column; overflow: hidden;
}
.chat-panel-close {
  position: absolute; top: 10px; right: 10px; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  border: 0; background: none; border-radius: 7px; color: var(--muted); cursor: pointer; font-size: 14px;
}
.chat-panel-close:hover { background: var(--hover); color: var(--text); }
.chat-panel-tabs { flex: none; display: flex; gap: 4px; padding: 14px 44px 12px 14px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.chat-panel-tab {
  display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--panel); font-size: var(--fs-meta); font-weight: var(--fw-medium); color: var(--muted); cursor: pointer;
}
.chat-panel-tab:hover { border-color: var(--accent); }
.chat-panel-tab.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: var(--fw-semibold); }
.chat-panel-tab-count { font-size: 11px; font-weight: var(--fw-semibold); color: var(--faint); }
.chat-panel-tab.on .chat-panel-tab-count { color: var(--accent); }
.chat-panel-body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 16px 24px; }
.chat-panel-pin { background: var(--subtle); border: 1px solid var(--border); border-radius: 10px; padding: 11px 12px; margin-bottom: 8px; cursor: pointer; }
.chat-panel-pin:hover { background: var(--hover); }
.chat-panel-pin-text { font-size: var(--fs-caption); line-height: 1.5; }
.chat-panel-pin-meta { margin-top: 6px; font-size: var(--fs-micro); }
.chat-panel-media-head { display: flex; align-items: baseline; gap: 8px; margin: 14px 0 8px; font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.chat-panel-media-head:first-child { margin-top: 0; }
.chat-panel-media-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.chat-panel-media-cell { position: relative; aspect-ratio: 1; border-radius: 7px; overflow: hidden; border: 1px solid var(--border); background: var(--subtle); cursor: pointer; }
.chat-panel-media-cell:hover { border-color: var(--accent); }
.chat-panel-media-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.chat-panel-media-badge { position: absolute; bottom: 4px; left: 5px; padding: 1px 5px; border-radius: 4px; background: rgba(16, 24, 40, .72); font-size: 9px; font-weight: var(--fw-bold); letter-spacing: .04em; color: #fff; }
.chat-panel-hint { margin-top: 14px; font-size: var(--fs-micro); line-height: 1.5; }
.chat-panel-link { display: flex; gap: 11px; padding: 11px 0; border-bottom: 1px solid var(--border); text-decoration: none; color: inherit; }
.chat-panel-link:hover { opacity: .78; }
.chat-panel-link-ico { flex: none; width: 28px; height: 28px; border-radius: 7px; background: var(--tone-neutral-bg); display: flex; align-items: center; justify-content: center; font-size: 13px; }
.chat-panel-link-body { flex: 1; min-width: 0; }
.chat-panel-link-title { font-size: var(--fs-caption); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-panel-link-meta { margin-top: 2px; font-size: var(--fs-micro); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* SPEC-V2CHATSHELL 2026-08-29 (audit §Chat, "Right panel" PARTIAL) — the THIS
   JOB facts card, pinned at the bottom of the drawer (flex:none sibling of the
   scrollable .chat-panel-body, above) so it stays visible under any tab —
   matching the prototype's own persistent placement (click-mapped, :8123). */
.chat-thisjob { flex: none; padding: 14px 16px 16px; border-top: 1px solid var(--border); }
.chat-thisjob-head { font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 8px; }
.chat-thisjob-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 3px 0; font-size: var(--fs-caption); }
.chat-thisjob-key { flex: none; color: var(--faint); }
.chat-thisjob-val { font-weight: var(--fw-semibold); text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-thisjob-val--danger { color: var(--danger); }
.chat-thisjob-open { margin-top: 8px; }
@media (max-width: 640px) { .chat-info-panel { width: 100vw; max-width: 100vw; } }
.chat-jobcard-ico { font-size: var(--fs-md); }
.chat-jobcard-body { flex: 1; min-width: 0; }
.chat-jobcard-addr { font-weight: var(--fw-semibold); font-size: var(--fs-caption); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-jobcard-sub { font-size: 11.5px; color: var(--muted); }

.chat-imgs { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0; }
.chat-img { max-width: 240px; max-height: 200px; border-radius: 8px; border: 1px solid var(--border); cursor: pointer; object-fit: cover; }
.chat-file { display: inline-flex; align-items: center; gap: 6px; margin: 4px 6px 4px 0; padding: 6px 10px; border: 1px solid var(--border); border-radius: 7px; font-size: 12.5px; background: var(--subtle); }

.chat-reactions { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.chat-react { border: 1px solid var(--border); background: var(--panel); border-radius: 999px; padding: 1px 9px; font-size: 12.5px; cursor: pointer; }
.chat-react:hover { background: var(--hover); }
.chat-react.mine { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: var(--fw-semibold); }

/* SPEC40 — private-channel member picker */
.chan-members { display: flex; flex-direction: column; gap: 4px; max-height: 240px; overflow-y: auto; margin-top: 8px; padding: 4px; border: 1px solid var(--border); border-radius: var(--radius-sm); }

.chat-tool { flex: 0 0 auto; border: 1px solid var(--border-strong); background: var(--panel); border-radius: var(--radius-sm); cursor: pointer; font-size: 17px; width: 42px; height: 42px; line-height: 1; }
.chat-tool:hover { background: var(--hover); }
.chat-pending { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: var(--fs-meta); padding: 6px 10px; background: var(--accent-soft); color: var(--accent); border-radius: 7px; margin-bottom: 6px; }

.emoji-pop {
  position: fixed; z-index: 300; display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-lg);
  padding: 8px; width: 248px;
}
.emoji-btn { border: none; background: transparent; cursor: pointer; font-size: 19px; padding: 5px; border-radius: 6px; }
.emoji-btn:hover { background: var(--hover); }

/* SPEC-V2CHAT D2 2026-08-28 — the word-reaction popover reuses .emoji-pop's box (fixed
   position, panel/border/shadow) but overrides the 6-col emoji GRID with a wrapping
   flex of auto-width pills — a word like "Thank you" cannot sit in a 1/6-width emoji
   cell. Pill visual language matches .chat-react (the reaction chip itself) so a word
   reads as the same family whether it's in the picker or already applied. */
.chat-word-react-pop { display: flex; flex-wrap: wrap; gap: 4px; width: 220px; }
.chat-word-react-btn {
  border: 1px solid var(--border); background: var(--panel); border-radius: 999px;
  padding: 4px 10px; font-size: 12.5px; cursor: pointer; white-space: nowrap; color: var(--text);
}
.chat-word-react-btn:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

@media (max-width: 640px) {
  .chat-msg-actions { opacity: 1; }
  .chat-tool { width: 44px; height: 44px; }
  /* SPEC-DHCHAT D3 2026-08-27 — README: "Minimum hit target is 44px on any touch
     surface." .chat-tool (above) already met this; Send (36px via the SPEC336
     composer glow-up, styles.css ~6810) did not — the one composer control that
     was under the floor on a phone. Height only; width stays auto (it's a
     labeled "Send" button, not an icon square). */
  .chat-send { height: 44px; min-height: 44px; }
}

/* ---- SPEC37.B — file upload (pending tray + drag state) ---- */
.chat-pending { display: flex; flex-wrap: wrap; gap: 6px; }
.chat-pend-chip { display: inline-flex; align-items: center; gap: 6px; background: var(--accent-soft); color: var(--accent); font-size: var(--fs-meta); padding: 4px 6px 4px 10px; border-radius: 999px; }
.chat-pend-chip.uploading { background: var(--hover); color: var(--muted); }
.chat-main.chat-drag { outline: 2px dashed var(--accent); outline-offset: -6px; border-radius: var(--radius); background: var(--accent-soft); }

/* ---- SPEC44 — Slack-style threads ---- */
/* "N replies" pill under a root message in the main feed */
.chat-thread-link {
  display: inline-flex; align-items: center; gap: 8px; margin: 6px 0 2px;
  padding: 4px 12px 4px 6px; border: 1px solid var(--border); background: var(--panel);
  border-radius: 999px; cursor: pointer; font: inherit; color: var(--accent); font-weight: var(--fw-semibold); font-size: 12.5px;
}
.chat-thread-link:hover { background: var(--accent-soft); border-color: var(--accent); }
.chat-thread-avs { display: inline-flex; }
.chat-thread-avs .chat-av { width: 22px; height: 22px; font-size: 9px; margin-top: 0; margin-right: -7px; border: 2px solid var(--panel); }
.chat-thread-avs .chat-av:last-child { margin-right: 0; }
.chat-thread-count { white-space: nowrap; }
.chat-thread-last { color: var(--faint); font-weight: var(--fw-medium); }

/* the docked thread panel (3rd flex column inside .chat-layout) */
.chat-thread {
  flex: 0 0 360px; min-width: 0; display: flex; flex-direction: column;
  border-left: 1px solid var(--border); padding-left: 14px;
}
.chat-thread-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-bottom: 10px; border-bottom: 1px solid var(--border); margin-bottom: 4px;
}
.chat-thread-title { display: flex; align-items: baseline; gap: 8px; }
.chat-thread-title strong { font-size: 15px; }
.chat-thread-sub { font-size: var(--fs-meta); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-thread-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; padding: 10px 4px; gap: 2px; }
.chat-thread-divider {
  display: flex; align-items: center; gap: 10px; margin: 8px 2px 4px;
  font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--faint); text-transform: uppercase; letter-spacing: .4px;
}
.chat-thread-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.chat-thread-composer { margin-top: 4px; }
/* the root message sits visually apart at the top of the thread */
.chat-thread-list > .chat-msg:first-child { background: var(--subtle); border-radius: 8px; }

@media (max-width: 1100px) {
  /* Below ~1100px there isn't room for a 3rd docked column without crushing the
     message feed to a sliver (rail 210 + thread 360 + gaps + sidebar leaves <200px
     for .chat-main). So the thread becomes a full-screen overlay here, same as phones.
     z-index 90 sits above app chrome + the +FAB (60) but BELOW modals (100), toasts
     (200), command palette (250) and login (300) — so a job-card modal opened from
     inside a thread still paints on top instead of hiding behind the overlay. */
  .chat-thread {
    position: fixed; inset: 0; z-index: 90; flex: 1 1 auto;
    background: var(--panel); border-left: none; padding: 12px 14px 14px;
    box-shadow: var(--shadow-lg);
  }
}

/* ============ SPEC46 — shooter Field job sheet (phone-first) ============ */
.field-wrap { max-width: 640px; }

/* SPEC238 — Sun mode: a scoped high-contrast skin for shooting in bright sunlight. Forces
   a bright, max-contrast palette + bigger/bolder type + larger targets, regardless of the
   app's light/dark theme. Redefining the theme vars on the container cascades cleanly to
   every card/button/text inside without touching the rest of the app. */
.field-wrap.field-sun {
  --card: #ffffff; --panel: #ffffff; --bg: #ffffff;
  --text: #000000; --muted: #1f2937; --faint: #334155;
  --border: #475569; --hover: #f1f5f9;
  background: #ffffff; color: #000000; border-radius: 14px;
  padding: 12px 12px 28px; font-size: var(--fs-md); box-shadow: 0 0 0 2px #0f172a;
}
.field-wrap.field-sun .view-sub,
.field-wrap.field-sun .muted,
.field-wrap.field-sun .faint { color: #1f2937 !important; }
.field-wrap.field-sun .card,
.field-wrap.field-sun .field-card { border: 2px solid #475569; background: #ffffff; }
.field-wrap.field-sun .card-title { color: #000000; font-weight: var(--fw-black); }
.field-wrap.field-sun .field-addr { font-size: 25px; color: #000000; }
/* SPEC-DHCREW D2 — sun mode's whole point is max contrast in direct sunlight;
   a navy band under the black-text override above would be black-on-navy.
   Neutralize the band back to the sun-mode white card + reset its two children
   to the same overrides they'd get outside the band, same idiom as every other
   .field-wrap.field-sun rule on this list. */
.field-wrap.field-sun .field-navy-head { background: none; margin: 0; padding: 0; border-radius: 0; }
.field-wrap.field-sun .field-navy-head .field-date-chip { background: #f1f5f9; color: #1f2937; }
.field-wrap.field-sun .field-navy-head .field-addr { color: #000000; }
.field-wrap.field-sun .field-line,
.field-wrap.field-sun .field-meta { font-size: 14.5px; color: #111827; }
.field-wrap.field-sun .btn { min-height: 48px; font-size: 15px; font-weight: var(--fw-bold); border-width: 2px; }
.field-sun-btn { min-height: 44px; }   /* SPEC238 a11y — ≥44px touch target for the field toggle */
/* SPEC-A11Y — removed the `.field-wrap.field-sun .btn-sm.field-sun-btn { min-height: 0; }`
   override that used to collapse the toggle back below 44px the instant sun mode turned on
   (higher specificity than both the 44px rule above and the general 48px sun-mode .btn rule
   below). Now the toggle just falls through to `.field-wrap.field-sun .btn` like every other
   button in sun mode, so it grows to 48px instead of shrinking. */
.field-wrap.field-sun .btn-primary { box-shadow: 0 0 0 1px #1e1b4b; }
.field-wrap.field-sun .field-svc { border-top: 2px solid #475569; }
.field-wrap.field-sun .field-svc-name { font-size: 15px; font-weight: var(--fw-bold); color: #000000; }
.field-wrap.field-sun .select,
.field-wrap.field-sun .input,
.field-wrap.field-sun .textarea { border: 2px solid #475569; color: #000000; background: #ffffff; min-height: 44px; }
.field-wrap.field-sun .field-date-chip,
.field-wrap.field-sun .field-pick { border-color: #475569; }

/* SPEC239 — "Prep for field" pre-cache button */
.field-prep-wrap { margin: 0 0 12px; }
.field-prep-btn { width: 100%; justify-content: center; min-height: 42px; font-weight: var(--fw-bold); }
.field-wrap.field-sun .field-prep-btn { min-height: 48px; }
/* CHECKER FIX 2026-08-15 (LAW-A, §J.4 #24 completion) — `.field-prep-btn` above is
   (0,1,0), the SAME specificity as the phone `.btn { min-height:44px }` rule at
   ~:2119, and it sits LATER in the file, so it won the tiebreak and this button
   still measured 42px on a phone after #24 landed (getBoundingClientRect at
   375x812 on the Field screen: 347x42). Same class of miss #24's own follow-up
   already caught for `.toolbar .btn`. Phone-scoped so the desktop pill is
   unchanged; Sun mode's (0,3,0) 48px rule above still wins by specificity. */
@media (max-width: 640px) { .field-prep-btn { min-height: 44px; } }

.field-picker {
  display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 10px; margin-bottom: 4px;
  -webkit-overflow-scrolling: touch;
}
.field-pick {
  flex: 0 0 auto; display: flex; flex-direction: column; gap: 2px; align-items: flex-start;
  padding: 8px 12px; border: 1px solid var(--border); background: var(--panel);
  border-radius: var(--radius-sm); cursor: pointer; font: inherit; min-width: 120px; max-width: 200px;
}
.field-pick:hover { background: var(--hover); }
.field-pick.active { border-color: var(--accent); background: var(--accent-soft); }
.field-pick-date { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--accent); text-transform: uppercase; letter-spacing: .4px; }
.field-pick.active .field-pick-date { color: var(--accent); }
.field-pick-addr { font-size: 12.5px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 176px; }

.field-sheet { display: flex; flex-direction: column; gap: 12px; }
.field-card { padding: 16px; }
/* SPEC-DHCREW D2 — the navy header band (photo-header else-branch: no thumbnail
   exists on this view, so no scrim/media — see field.js jobSheet). Negative
   margins bleed it to the card's own 16px padding + border-radius so it reads
   as a proper full-bleed band, not an inset box. */
.field-navy-head {
  background: var(--sidebar-bg);
  margin: -16px -16px 12px;
  padding: 16px 16px 14px;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
}
.field-navy-head .field-date-chip { background: rgba(255,255,255,.16); color: #fff; }
.field-navy-head .field-addr { color: #fff; }
.field-date-chip {
  display: inline-block; font-size: 11.5px; font-weight: var(--fw-bold); color: var(--accent);
  background: var(--accent-soft); border-radius: 999px; padding: 3px 10px; margin-bottom: 8px;
}
.field-addr { font-size: 21px; font-weight: var(--fw-black); line-height: 1.25; }
.field-meta { font-size: var(--fs-caption); color: var(--muted); margin-top: 6px; }
.field-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 4px; }
/* SPEC-DHCREW D2 — the mockup's "big Directions/call/text row" (48px pills);
   these ARE that row (Open in Maps / Directions / Call / On my way / channel
   preference / Conditions / Full job details) — restyled bigger, nothing added
   or removed. */
.field-actions .btn { min-height: 48px; }
.field-map { flex: 1; min-width: 160px; }
.field-line { font-size: var(--fs-caption); color: var(--text); margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.field-line.muted { color: var(--muted); }

/* SPEC-DHCREW D2 — access code, promoted to its own prominent mono card. */
.field-access-card { display: flex; align-items: center; gap: 12px; }
.field-access-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 15px; font-weight: var(--fw-bold); letter-spacing: .04em; color: var(--text);
}
/* SPEC-DHCREW D2 — the raws/Dropbox quiet confirmation card (job.rawLink). */
.field-raws-card { display: flex; align-items: center; gap: 12px; }
.field-raws-check { flex: none; display: inline-flex; color: var(--ok); }
.field-raws-link { font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field-raws-open { flex: none; font-size: 12.5px; font-weight: var(--fw-bold); min-height: 44px; display: inline-flex; align-items: center; }

/* SPEC-DHCREW D2 — the mockup's full-width "Wrap the shoot" primary at the foot. */
.field-finish-btn { min-height: 52px; font-size: 14.5px; }

.field-overall { font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--muted); margin-bottom: 8px; }
/* SPEC-DHCREW D2 — "big 44px rows, check animation calm": a floor on the row
   itself (not just its select/button children, already 44px via the phone
   media query below) + a calm color transition on the stage dot instead of an
   instant flip. */
.field-svc { border-top: 1px solid var(--border); padding: 10px 0; min-height: 44px; }
.field-svc:first-of-type { border-top: none; padding-top: 0; }
.field-svc-main { display: flex; align-items: center; gap: 10px; }
.field-svc-dot { flex: 0 0 auto; width: 12px; height: 12px; border-radius: 50%; background: var(--faint); transition: background-color .25s ease; }
.field-svc-name { flex: 1; min-width: 0; font-size: var(--fs-body); font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field-svc-sel { flex: 0 0 auto; min-height: 38px; max-width: 150px; }
.field-svc-cmt {
  flex: 0 0 auto; border: 1px solid var(--border); background: var(--panel); border-radius: var(--radius-sm);
  cursor: pointer; font-size: var(--fs-caption); padding: 7px 10px; min-height: 38px; color: var(--muted);
}
.field-svc-cmt.has { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); font-weight: var(--fw-semibold); }
.field-svc-cmt:hover { background: var(--hover); }
.field-cmt-panel { margin: 8px 0 2px; padding: 10px; background: var(--subtle); border-radius: var(--radius-sm); }
.field-cmt-compose { display: flex; gap: 8px; align-items: flex-end; margin-top: 8px; }
.field-cmt-compose .textarea { flex: 1; min-height: 40px; }
.field-cmt-compose .btn { flex: 0 0 auto; white-space: nowrap; min-height: 44px; }

.today-shoot-actions { margin-top: 10px; }
.today-shoot-actions .btn { min-height: 40px; }
/* CHECKER FIX 2026-08-15 (LAW-A, §J.4 #24 completion) — the rule above is (0,2,0)
   and so beats the phone `.btn { min-height:44px }` (0,1,0) at ~:2119 regardless of
   order, exactly like `.toolbar .btn` did. These are the actions on the Today
   shoot card — the row a shooter taps on the way out the door. Phone-scoped;
   desktop keeps its 40px. */
@media (max-width: 640px) { .today-shoot-actions .btn { min-height: 44px; } }

@media (max-width: 760px) {
  .field-addr { font-size: 19px; }
  .field-map { flex: 1 1 100%; }
  /* SPEC144 — field is a phone-in-the-sun tool: give the shooter controls real
     44px touch targets, and 16px on the <select> so iOS doesn't auto-zoom on focus. */
  .field-svc-sel { max-width: 140px; min-height: 44px; font-size: var(--fs-md); }
  .field-svc-cmt { min-height: 44px; }
  .field-pick { min-height: 44px; }
}
/* SPEC152 — on very narrow phones (<400px) stack the service row controls below the
   name so the dot + name + select + note button never squeeze or overflow. */
@media (max-width: 400px) {
  .field-svc-main { flex-wrap: wrap; }
  .field-svc-sel, .field-svc-cmt { flex: 1 1 calc(50% - 5px); max-width: none; }
}

/* ============ SPEC145 — Shoot Conditions (sun / weather / drone) ============ */
.cond-card { display: flex; flex-direction: column; gap: 4px; }
.cond-sec { padding: 14px 0; border-top: 1px solid var(--border); }
.cond-sec:first-child { padding-top: 2px; border-top: none; }
.cond-compass { margin: 6px 0 2px; }
.cond-readout { text-align: center; font-size: var(--fs-caption); padding: 8px 10px; background: var(--subtle); border-radius: var(--radius-sm); margin: 6px auto 0; max-width: 340px; line-height: var(--lh-normal); }
.cond-readout b { color: var(--text); }
.cond-slider { width: 100%; max-width: 340px; display: block; margin: 12px auto 4px; accent-color: var(--accent); height: 6px; cursor: pointer; }
.cond-pickrow { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; justify-content: center; }
.cond-chip { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: var(--fw-bold); padding: 3px 9px; border-radius: 999px; background: var(--subtle); color: var(--muted); border: 1px solid var(--border); }
.cond-chip.gold { background: var(--warn-soft); color: var(--warn); border-color: var(--warn); }
.cond-rec { margin-top: 10px; padding: 10px 12px; background: var(--accent-soft); border-radius: var(--radius-sm); }
.cond-week { display: flex; gap: 6px; overflow-x: auto; padding: 4px 2px 8px; -webkit-overflow-scrolling: touch; }
.cond-day { flex: 0 0 auto; min-width: 56px; text-align: center; padding: 8px 4px; border-radius: var(--radius-sm); background: var(--subtle); border: 1px solid transparent; transition: transform .12s ease, box-shadow .12s ease; }
.cond-day.shoot { border-color: var(--accent); background: var(--accent-soft); box-shadow: var(--shadow); }
.cd-dow { font-size: var(--fs-micro); font-weight: var(--fw-black); color: var(--muted); }
.cd-emoji { font-size: var(--fs-h1); line-height: 1.3; }
.cd-hi { font-size: var(--fs-body); font-weight: var(--fw-black); }
.cd-lo { font-size: var(--fs-meta); }
.cd-rain { font-size: 10.5px; color: #0ea5e9; font-weight: var(--fw-bold); }
.cond-dayline { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; margin-top: 8px; font-size: var(--fs-caption); }
.cond-dayline .muted { font-size: var(--fs-meta); }
.cond-tips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.cond-tip { font-size: var(--fs-meta); padding: 4px 9px; border-radius: var(--radius-sm); background: var(--subtle); border: 1px solid var(--border); }
.cond-fly { display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; font-weight: var(--fw-black); padding: 3px 10px; border-radius: 999px; }
.cond-fly-ok { background: var(--ok-soft); color: var(--ok); }
.cond-fly-caution { background: var(--warn-soft); color: var(--warn); }
.cond-fly-no { background: var(--danger-soft); color: var(--danger); }
/* LENS-S16 — the 4th drone level: wind/rain unknown. Deliberately NEUTRAL, never green —
   "we have no weather data" must not read as a go-fly. */
.cond-fly-unknown { background: var(--subtle); color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .cond-day { transition: none; } }

/* ============ SPEC146 — premium micro-interactions (tasteful, token-based) ============ */
/* dense rows highlight on hover for scannability (JS-set inline hovers still win) */
.list-row { transition: background var(--t-fast) var(--ease); }
.list-row:hover { background: var(--hover); }
@media (prefers-reduced-motion: reduce) {
  .kanban-card, .card.is-clickable, .list-row { transition: none; }
  .kanban-card:hover, .card.is-clickable:hover { transform: none; }
}

/* ============ SPEC47 — notification bell + panel ============ */
/* count rides the top-right corner of the 🔔 glyph (its .help-q is the anchor),
   so it hugs the bell whether the row shows its label or collapses to an icon */
.notif-bell .help-q { position: relative; overflow: visible; }
.notif-bell-badge {
  position: absolute; top: -7px; right: -8px; left: auto;
  background: var(--danger); color: #fff; font-size: 9.5px; font-weight: var(--fw-bold);
  border-radius: 999px; min-width: 15px; height: 15px; line-height: 15px; text-align: center; padding: 0 4px;
  box-shadow: 0 0 0 2px var(--sidebar-bg);
}

.notif-list { display: flex; flex-direction: column; gap: 5px; max-height: 62vh; overflow-y: auto; }
.notif-row {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 10px 12px; border: 1px solid var(--border); background: var(--panel);
  border-radius: var(--radius-sm); cursor: pointer; font: inherit;
}
.notif-row:hover { background: var(--hover); }
.notif-row.unread { border-color: var(--accent); background: var(--accent-soft); }
/* SPEC211 — "Needs you" standing-work rows: calm (no accent), muted icon. The accent
   highlight stays reserved for true unread events, so "new" and "to-do" read distinctly. */
.notif-subhead { font-size: var(--fs-micro); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); padding: 12px 4px 4px; }
.notif-row.action { background: var(--subtle); }
.notif-row.action .notif-ico { color: var(--muted); }
/* inside the slide-over the panel body already scrolls — let the lists flow naturally */
.slideover-body .notif-list { max-height: none; overflow: visible; }
.notif-ico { flex: 0 0 auto; font-size: 17px; line-height: 1.4; }
.notif-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.notif-title { font-size: 13.5px; font-weight: var(--fw-semibold); color: var(--text); }
.notif-body { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notif-time { flex: 0 0 auto; font-size: var(--fs-micro); color: var(--faint); white-space: nowrap; margin-top: 1px; }

/* BELLDROP 2026-08-20 — per-row dismiss (× or swipe) + bulk clear. `.notif-row-x` is a
   MODIFIER on the existing row: it hands the padding to the inner .notif-go button so the
   whole text area stays tappable, and opens a 44px column for the ×. The plain .notif-row
   above is untouched, which is what keeps the "Needs you" rows (no ×) byte-identical. */
.notif-row-x { padding: 0; gap: 0; align-items: stretch; overflow: hidden; }
.notif-go {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: flex-start; gap: 10px;
  padding: 10px 12px; text-align: left; background: none; border: 0; color: inherit;
  font: inherit; cursor: pointer;
}
.notif-x {
  flex: 0 0 44px; min-width: 44px; min-height: 44px; display: flex; align-items: center;
  justify-content: center; background: none; border: 0; border-left: 1px solid var(--border);
  color: var(--faint); cursor: pointer;
}
.notif-x:hover, .notif-x:focus-visible { background: var(--hover); color: var(--text); }
.notif-tools { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 2px 2px 8px; }
.notif-count { font-size: var(--fs-micro); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); }
.notif-clear { min-height: 44px; }   /* §I.1 LAW 1 — every visible control clears 44px */
.notif-snoozed-note { font-size: 12.5px; color: var(--muted); padding: 6px 2px; }
/* SEENENGINE 2026-09-06 BEGIN — §C.4: sticky rows say so, in the header count too. */
.notif-snoozed-inline { flex: 1; text-align: right; font-size: 11.5px; color: var(--muted); padding: 0; }
.notif-sticky-chip {
  align-self: flex-start; font-size: 10.5px; font-weight: var(--fw-semibold); color: var(--muted);
  background: var(--subtle); border: 1px solid var(--border); border-radius: 999px;
  padding: 1px 7px; margin-top: 1px; white-space: nowrap;
}
/* SEENENGINE END */

/* ============ SPEC48 — Copy Library (snippets) ============ */
.snippet-toolbar { margin: 4px 0 10px; }
.snippet-search { width: 100%; }
.snippet-cats { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.chip-btn {
  flex: 0 0 auto; border: 1px solid var(--border); background: var(--panel); color: var(--muted);
  border-radius: 999px; padding: 5px 12px; font: inherit; font-size: 12.5px; font-weight: var(--fw-semibold); cursor: pointer;
}
.chip-btn:hover { background: var(--hover); }
.chip-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }
/* DESIGN 2026-08-11 — FRIDAY-panel citation chips only: a long address label may
   shrink + ellipsize instead of poking past the card edge at 375px. */
.chip-btn.fri-cite { flex: 0 1 auto; min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.snippet-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; }
.snippet-card { display: flex; flex-direction: column; gap: 8px; }
.snippet-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; }
.snippet-title { font-weight: var(--fw-bold); font-size: 14.5px; line-height: 1.3; min-width: 0; }
.snippet-cat {
  flex: 0 0 auto; font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .3px;
  color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 2px 8px; white-space: nowrap;
}
.snippet-body {
  font-size: var(--fs-caption); line-height: var(--lh-normal); color: var(--text); white-space: pre-wrap; word-break: break-word;
  background: var(--subtle); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 10px 12px; max-height: 168px; overflow-y: auto; flex: 1;
}
.snippet-actions { display: flex; gap: 8px; }

@media (max-width: 760px) {
  .snippet-grid { grid-template-columns: 1fr; }
  .snippet-cats { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 4px; }
}

/* ============ SPEC49 — Territory heat map ============ */
.table .num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* (SPEC126) removed dead .heat-grid/.heat-tile/.heat-city/.heat-val — replaced by the ranked top-list */

/* ============ SPEC51 — shared visual primitives ============ */
.avatar {
  display: inline-flex; align-items: center; justify-content: center; border-radius: 50%;
  color: #fff; font-weight: var(--fw-bold); flex: 0 0 auto; line-height: 1; letter-spacing: .2px;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
}
/* LAW-A 2026-08-15 (#6) → CHATEXTRA 2026-08-20 (THE REAL FIX, landed) → EXTRACLOSE
   2026-08-20 (D4, threshold corrected) — this used to be a hardcoded 3-hex
   attribute-selector stopgap for exactly Erika/Jeffrey/Vitaliy's fill colours
   (white-on-fill measured 2.21:1 / 1.90:1 / 2.26:1, all FAIL). App.avatar()
   (core.js) now sets ink inline from the fill's OWN measured luminance —
   `color: App.relLum(fill) > 0.179 ? '#000' : '#fff'` (0.3/'#0b0d13' let hire
   #8's #ef4444 through at 3.76:1 FAIL; see core.js's App.relLum comment for
   the full derivation) — which covers every past AND future roster/tag colour
   with no enumerable list to maintain here. The base `.avatar{color:#fff}`
   above is just the pre-JS-paint default now; App.avatar always overrides it
   inline. */
.avatar-stack { display: inline-flex; }
.avatar-stack .avatar { margin-right: -8px; border: 2px solid var(--panel); }
.avatar-stack .avatar:last-child { margin-right: 0; }
.spark { display: inline-block; vertical-align: middle; line-height: 0; }
.spark svg { display: block; }
.spark-empty { background: var(--subtle); border-radius: 4px; opacity: .5; }
.databar { display: inline-flex; align-items: center; gap: 8px; min-width: 80px; width: 100%; }
.databar-track { flex: 1; min-width: 28px; height: 8px; border-radius: 999px; background: var(--bg-2); overflow: hidden; }
.databar-fill { display: block; height: 100%; border-radius: 999px; min-width: 2px; transition: width .5s cubic-bezier(.22,1,.36,1); }
.databar-label { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); }
.gauge { display: inline-block; width: 48px; height: 7px; border-radius: 999px; background: var(--bg-2); overflow: hidden; vertical-align: middle; }
.gauge-fill { display: block; height: 100%; border-radius: 999px; transition: width .5s cubic-bezier(.22,1,.36,1); }

/* ============ SPEC51 — polish baseline (a11y, motion, feedback) ============ */
/* Visible keyboard focus everywhere (was nearly absent). Mouse users unaffected. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.btn:focus-visible, .nav-item:focus-visible, .chat-ch:focus-visible, .help-btn:focus-visible, .modal-close:focus-visible { outline-offset: 1px; }

/* Respect reduced-motion: kill transitions/animations for users who ask for it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* Richer toasts: type color + icon (success/error), still one tidy pill. */
.toast { display: flex; align-items: center; gap: 8px; }
.toast-ico { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; font-size: var(--fs-micro); font-weight: var(--fw-black); }
.toast.error { box-shadow: inset 3px 0 0 var(--danger); }
.toast.error .toast-ico { background: var(--danger); color: var(--on-danger); }
.toast.success { box-shadow: inset 3px 0 0 var(--ok); }
.toast.success .toast-ico { background: var(--ok); color: #fff; }

/* ============ SPEC52 — status-at-a-glance ============ */
.due-overdue { color: var(--danger); font-weight: var(--fw-bold); }
.due-today { color: var(--warn); font-weight: var(--fw-bold); }
.due-soon { color: var(--warn); }
.age-badge { display: inline-flex; align-items: center; font-size: 10.5px; font-weight: var(--fw-bold); border-radius: 999px; padding: 1px 7px; margin-left: 6px; vertical-align: middle; }
.age-badge.age-warm { background: var(--warn-soft); color: var(--warn); }
.age-badge.age-hot { background: var(--danger-soft); color: var(--danger); }
/* status-group pipeline bar on tracker headers */
.statusgroup-bar { display: inline-flex; height: 6px; width: 90px; border-radius: 999px; overflow: hidden; margin-left: 10px; vertical-align: middle; background: var(--bg-2); }
.statusgroup-bar > span { display: block; height: 100%; }

/* ============ SPEC53 — delight (confetti) ============ */
.confetti-layer { position: fixed; inset: 0; pointer-events: none; z-index: 400; overflow: hidden; }
.confetti-piece {
  position: absolute; top: -14px; width: 9px; height: 14px; opacity: .95;
  animation-name: confetti-fall; animation-timing-function: cubic-bezier(.3,.5,.7,1); animation-fill-mode: forwards;
}
@keyframes confetti-fall {
  0% { transform: translateY(-14px) translateX(0) rotate(0); opacity: 1; }
  100% { transform: translateY(106vh) translateX(var(--drift,0)) rotate(var(--rot,360deg)); opacity: .9; }
}
@media (prefers-reduced-motion: reduce) { .confetti-layer { display: none; } }

/* ============ SPEC54 — best clients leaderboard ============ */
.rank-medal { font-size: 15px; line-height: 1; flex: 0 0 auto; }
.bestclient-card { margin-bottom: 14px; }
.bestclient-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }
/* SPEC273 — Top Agents */
.ta-lenses { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px; margin-bottom: 14px; }
.ta-lens { border: 1px solid var(--border); border-radius: 12px; padding: 13px 14px; background: var(--card); }
.ta-lens-h { display: flex; align-items: center; gap: 7px; font-weight: var(--fw-bold); font-size: var(--fs-body); }
.ta-lens-ico { font-size: var(--fs-md); }
.ta-lens-n { margin-left: auto; background: var(--subtle); border-radius: 999px; padding: 1px 9px; font-size: var(--fs-meta); }
.ta-lens-blurb { font-size: var(--fs-meta); color: var(--muted); margin: 4px 0 8px; }
.ta-lens-list { display: flex; flex-direction: column; gap: 4px; }
.ta-lens-item { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; text-align: left; background: transparent; border: 0; border-radius: 7px; padding: 4px 6px; cursor: pointer; }
.ta-lens-item:hover { background: var(--subtle); }
.ta-lens-name { font-weight: var(--fw-semibold); font-size: var(--fs-caption); }
.ta-lens-meta { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.ta-lens-act { font-size: 11.5px; margin-top: 8px; }
.ta-rows { display: flex; flex-direction: column; }
.ta-row { display: grid; grid-template-columns: 30px 34px minmax(120px, 1.4fr) minmax(120px, 1fr) minmax(90px, 0.9fr) auto; align-items: center; gap: 10px; width: 100%; text-align: left; background: transparent; border: 0; border-bottom: 1px solid var(--border); padding: 9px 4px; cursor: pointer; }
.ta-row:last-child { border-bottom: 0; }
.ta-row:hover { background: var(--subtle); }
.ta-rank { font-size: 15px; font-weight: var(--fw-black); text-align: center; color: var(--muted); }
.ta-who { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ta-name { font-weight: var(--fw-bold); font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ta-broker { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ta-metrics { display: flex; flex-direction: column; gap: 1px; }
.ta-rev { font-weight: var(--fw-bold); font-size: 13.5px; }
.ta-sub { font-size: var(--fs-micro); color: var(--muted); }
.ta-scorewrap { display: flex; align-items: center; gap: 7px; }
.ta-scorebar { flex: 1; height: 7px; border-radius: 999px; background: var(--subtle); overflow: hidden; min-width: 40px; }
.ta-scorefill { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--accent, #2b608f), #22c55e); }
.ta-scoren { font-weight: var(--fw-black); font-size: var(--fs-caption); min-width: 22px; text-align: right; }
.ta-tail { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; justify-content: flex-end; }
.ta-recency { font-size: 10.5px; padding: 1px 7px; border-radius: 999px; white-space: nowrap; }
.ta-recency--hot { background: rgba(34,197,94,.16); color: #16a34a; }
.ta-recency--warm { background: rgba(253,171,61,.18); color: #c2740b; }
.ta-recency--cold { background: var(--subtle); color: var(--muted); }
.ta-flag { font-size: 10.5px; font-weight: var(--fw-bold); padding: 1px 7px; border-radius: 999px; white-space: nowrap; }
.ta-flag--lapse { background: rgba(239,68,68,.14); color: #dc2626; }
.ta-flag--ref { background: rgba(43,96,143,.14); color: #1e3f66; }
.ta-flag--rise { background: rgba(14,165,233,.16); color: #0284c7; }
.ta-broker-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 10px; margin-top: 4px; }
.ta-broker-card { border: 1px solid var(--border); border-radius: 11px; padding: 12px 13px; }
.ta-broker-top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.ta-broker-name { font-weight: var(--fw-bold); font-size: var(--fs-body); }
.ta-broker-rev { font-weight: var(--fw-black); font-size: var(--fs-body); }
.ta-broker-sub { font-size: 11.5px; margin-top: 2px; }
.ta-broker-names { font-size: var(--fs-meta); margin-top: 6px; }
.ta-broker-why { font-size: 11.5px; margin-top: 7px; line-height: var(--lh-snug); }
.ta-note { font-size: 11.5px; margin-top: 14px; padding: 11px 13px; border-radius: 10px; background: var(--subtle); line-height: 1.4; }
@media (max-width: 640px) { .ta-row { grid-template-columns: 26px 30px 1fr auto; } .ta-row .ta-scorewrap, .ta-row .ta-metrics { display: none; } }
/* SPEC275 — Incoming review inbox */
.inc-row { padding: 11px 2px; border-bottom: 1px solid var(--border); }
.inc-row:last-child { border-bottom: 0; }
.inc-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.inc-vendor { font-weight: var(--fw-bold); font-size: 13.5px; text-transform: capitalize; }
.inc-what { font-size: 12.5px; color: var(--text); }
.inc-when { font-size: 11.5px; margin-left: auto; }
.inc-addr { font-size: var(--fs-meta); margin-top: 2px; }
.inc-actions { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-top: 8px; }
.inc-actions .input { flex: 1 1 200px; min-width: 160px; }
/* SPEC-TONOMO-UPD — a parked Tonomo order-change proposal. Its OWN class rather than
   reusing .inc-row, so this stacked field layout can't be knocked sideways by an
   unrelated feature. (The leaderboard used to redeclare .inc-row with display:flex
   further down this file and win by source order; it is now .inc-lb-row.) */
.ou-row { padding: 12px 2px; border-bottom: 1px solid var(--border); }
.ou-row:last-child { border-bottom: 0; }
.ou-fields { margin: 8px 0 4px; display: flex; flex-direction: column; gap: 4px; }
.ou-field { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; flex-wrap: wrap; }
.ou-field input[type="checkbox"] { flex: 0 0 auto; }
.ou-field-label { flex: 0 0 92px; font-weight: var(--fw-semibold); }
.ou-vals { flex: 1 1 auto; }
.ou-from { color: var(--muted); text-decoration: line-through; }
.ou-arrow { color: var(--muted); }
.ou-to { font-weight: var(--fw-semibold); }
/* narrow: keep the tick + its field NAME together on one line, wrap only the
   old→new values underneath (indented past the checkbox) */
@media (max-width: 640px) {
  .ou-field-label { flex: 1 1 auto; }
  .ou-vals { flex-basis: 100%; padding-left: 24px; }
}
.bestclient-tile {
  display: flex; flex-direction: column; gap: 7px; align-items: flex-start; text-align: left;
  padding: 12px 14px; border: 1px solid var(--border); background: var(--subtle);
  border-radius: var(--radius-sm); cursor: pointer; font: inherit;
}
.bestclient-tile:hover { border-color: var(--accent); background: var(--accent-soft); }
.bestclient-label { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--muted); text-transform: uppercase; letter-spacing: .4px; }
.bestclient-main { display: flex; align-items: center; gap: 10px; min-width: 0; width: 100%; }
.bestclient-text { display: flex; flex-direction: column; min-width: 0; }
.bestclient-name { font-weight: var(--fw-bold); font-size: var(--fs-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bestclient-val { font-size: var(--fs-caption); color: var(--accent); font-weight: var(--fw-bold); }

/* ============ SPEC54 — client branding image previews ============ */
.brand-img-prev { margin-top: 6px; }
.brand-img-link { display: inline-block; line-height: 0; border-radius: 6px; }
.brand-img-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.brand-img { max-width: 130px; max-height: 72px; border-radius: 6px; border: 1px solid var(--border); cursor: pointer; object-fit: contain; background: var(--subtle); padding: 4px; }

/* ============ SPEC55 — client tiers + referrals ============ */
.tier-badge { display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-micro); font-weight: var(--fw-bold); color: #fff; border-radius: 999px; padding: 2px 9px; line-height: var(--lh-normal); }
.tier-badge-sm { padding: 1px 7px; }
/* SPEC270 (post-C #2) — Clients/Directory legend modal */
.legend-section { margin-top: 14px; }
.legend-section:first-child { margin-top: 4px; }
.legend-h { font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
.legend-row { display: flex; align-items: flex-start; gap: 10px; padding: 5px 0; }
.legend-sample { flex: 0 0 92px; display: flex; align-items: center; min-height: 20px; }
.legend-text { display: flex; flex-direction: column; gap: 1px; }
.legend-label { font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--text); }
.legend-desc { font-size: var(--fs-meta); color: var(--muted); }
.reflink-row {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 7px 10px; border: 1px solid var(--border); background: var(--panel);
  border-radius: var(--radius-sm); cursor: pointer; font: inherit; font-size: var(--fs-caption); margin-bottom: 6px;
}
.reflink-row:hover { background: var(--hover); border-color: var(--accent); }

/* ============ SPEC56 — Ask / FAQ ============ */
.faq-ask { width: 100%; }
.faq-section-label { font-size: 11.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); margin: 8px 2px 2px; }
.faq-card { border-left: 3px solid var(--ok); }
.faq-card.faq-open { border-left-color: var(--warn); }
.faq-q-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.faq-q { font-weight: var(--fw-bold); font-size: 15px; line-height: var(--lh-snug); }
.faq-cat { flex: 0 0 auto; font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .3px; color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
.faq-meta { font-size: 11.5px; color: var(--faint); margin-top: 3px; }
.faq-a { font-size: var(--fs-body); line-height: 1.55; white-space: pre-wrap; margin-top: 10px; padding: 10px 12px; background: var(--subtle); border-radius: var(--radius-sm); }
.faq-answered-by { color: var(--ok); font-weight: var(--fw-semibold); }
.faq-pending { font-size: var(--fs-caption); color: var(--warn); font-weight: var(--fw-semibold); margin-top: 10px; }
.faq-actions { display: flex; gap: 8px; margin-top: 10px; }

/* ============ SPEC57 — per-role QA accountability ============ */
/* AUDIT-2026-07-25 [16] — .qa-role-row/.qa-role-name deleted (zero emitters, the
   SPEC172 chain UI they belonged to is gone); .qa-role-who stays — live at tracker.js. */
.qa-role-who { font-size: 11.5px; color: var(--muted); background: var(--subtle); border-radius: 999px; padding: 1px 8px; }

/* ============ SPEC172 — role-based QC cards ============ */
.qa-rolecards { display: flex; flex-direction: column; gap: 10px; }
.qa-rolecard { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: var(--panel); }
.qa-rolecard-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 2px; }
.qa-rolecard-title { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.qa-rolecard-grade { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.qa-chain-chip { display: inline-block; font-size: var(--fs-micro); font-weight: var(--fw-semibold); color: var(--muted); background: var(--subtle);
  border-radius: 999px; padding: 1px 9px; margin: 2px 0 8px; }
.qa-crit-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; padding: 3px 0; }
.qa-crit-name { flex: 1 1 180px; min-width: 0; font-size: 12.5px; color: var(--text); }
.qa-owner-audit { border: 1.5px solid var(--accent); border-radius: 12px; padding: 12px;
  background: color-mix(in srgb, var(--accent) 6%, var(--panel)); }
.qa-owner-audit-head { display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
.qa-owner-audit .qa-rolecard { background: var(--card, var(--panel)); }
@media (max-width: 640px) {
  .qa-crit-row { flex-direction: column; align-items: stretch; gap: 4px; }
  .qa-crit-row .qa-crit-name { flex-basis: auto; }   /* column axis: don't reserve 180px of height */
  .qa-crit-row .qa-score { align-self: flex-start; }
}

/* ============ SPEC58 — socials quick-links ============ */
.socials-row { display: flex; flex-wrap: wrap; gap: 8px; }
.social-link { text-decoration: none; }
.social-direct { border-color: var(--accent); color: var(--accent); font-weight: var(--fw-bold); }

/* ============ SPEC59 — Social Studio ============ */
.soc-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
@media (max-width: 860px) { .soc-grid-2 { grid-template-columns: 1fr; } }

/* section heads inside cards + the post modal */
.soc-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.soc-section-head .faint { font-size: 11.5px; font-weight: var(--fw-medium); }
.soc-sub-head { font-size: var(--fs-micro); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); margin: 12px 0 6px; }
.soc-modal-section { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 12px; }
.soc-metric-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
@media (max-width: 560px) { .soc-metric-grid { grid-template-columns: repeat(2, 1fr); } }
.soc-assets { display: flex; flex-direction: column; gap: 8px; }
.soc-asset-row { display: grid; grid-template-columns: 1fr 1.4fr auto; gap: 8px; align-items: center; }

/* trends */
.soc-trend-row { display: flex; align-items: center; gap: 12px; margin: 8px 0; }
.soc-trend-label { width: 90px; font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--muted); }
.soc-trend-now { margin-left: auto; font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }
.soc-trend-months { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-top: 8px; }
.soc-trend-month { font-size: var(--fs-micro); color: var(--faint); font-variant-numeric: tabular-nums; }

/* platform split */
.soc-plat-row { display: grid; grid-template-columns: 110px 1fr auto; align-items: center; gap: 10px; padding: 5px 0; }
.soc-plat-stat { font-size: 11.5px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* top posts table */
.soc-top-table th.num, .soc-top-table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.soc-top-row { cursor: pointer; }
.soc-top-row:hover { background: var(--hover); }
.soc-top-title { font-weight: var(--fw-semibold); }

/* pipeline rows */
.soc-stage-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.soc-stage-chip { font-size: var(--fs-meta); background: var(--subtle); border: 1px solid var(--border); border-radius: 999px; padding: 2px 10px; color: var(--muted); }
.soc-stage-chip b { color: var(--text); }
.soc-row { display: flex; align-items: center; gap: 8px; padding: 6px 6px; border-radius: 8px; cursor: pointer; }
.soc-row:hover { background: var(--hover); }
.soc-row-title { font-weight: var(--fw-semibold); font-size: var(--fs-caption); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.soc-row-when { margin-left: auto; font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.soc-row-warn .soc-row-when { color: var(--warn); font-weight: var(--fw-bold); }

/* captions */
.soc-caption { border-top: 1px solid var(--border); padding: 8px 0; }
.soc-caption:first-of-type { border-top: 0; }
.soc-caption-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.soc-caption-title { font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.soc-caption-body { font-size: 12.5px; color: var(--muted); margin-top: 4px; white-space: pre-wrap; }

/* AI brief */
.soc-brief { width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-meta); margin-top: 10px; }

/* ============ SPEC60 — Incentives & Recognition ============ */
/* your-standing hero */
.inc-hero { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 14px;
  background: linear-gradient(120deg, var(--accent-soft), var(--panel)); }
.inc-hero-main { flex: 1; min-width: 140px; }
.inc-hero-name { font-weight: var(--fw-black); font-size: var(--fs-md); display: flex; align-items: center; gap: 8px; }
.inc-hero-rank { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.inc-hero-right { text-align: right; }
.inc-hero-pts { font-size: 30px; font-weight: var(--fw-black); letter-spacing: -.5px; color: var(--accent); line-height: 1; }
.inc-hero-lbl { font-size: var(--fs-micro); color: var(--muted); text-transform: uppercase; letter-spacing: .5px; }
.inc-hero-badges { flex-basis: 100%; }

/* filter chips */
.inc-filter { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }

/* leaderboard rows */
.inc-list { display: flex; flex-direction: column; }
/* NOTE the -lb- : this is the LEADERBOARD row. Plain `.inc-row` belongs to the
   SPEC275 Incoming queue further up this file — the two features share the `inc-`
   prefix, and when this rule was also called `.inc-row` it won by source order and
   flex-laid-out (+ pointer-cursored) the incoming queue's stacked vendor rows. */
.inc-lb-row { display: flex; align-items: center; gap: 12px; padding: 9px 8px; border-radius: 10px; cursor: pointer; border: 1px solid transparent; }
.inc-lb-row:hover { background: var(--hover); }
.inc-lb-row-me { background: var(--accent-soft); border-color: var(--accent); }
.inc-lb-row-top .inc-rank { font-size: 20px; }
.inc-rank { width: 30px; text-align: center; font-weight: var(--fw-black); font-variant-numeric: tabular-nums; color: var(--muted); flex: 0 0 auto; }
.inc-name-wrap { flex: 1; min-width: 0; }
.inc-name { font-weight: var(--fw-bold); font-size: var(--fs-body); display: flex; align-items: center; gap: 6px; }
.inc-you { font-size: 10px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .5px; color: #fff; background: var(--accent); border-radius: 999px; padding: 1px 6px; }
.inc-sub { display: flex; align-items: center; gap: 6px; margin-top: 3px; flex-wrap: wrap; }
.inc-badge-mini { font-size: var(--fs-caption); }
.inc-pts-wrap { width: 130px; flex: 0 0 auto; text-align: right; }
.inc-pts { font-size: 19px; font-weight: var(--fw-black); font-variant-numeric: tabular-nums; letter-spacing: -.3px; }
.inc-pts-bar { margin-top: 3px; }
@media (max-width: 560px) { .inc-pts-wrap { width: 84px; } .inc-pts-bar .databar-label { display: none; } }

/* badges */
.inc-badge-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0; }
.inc-badge { font-size: var(--fs-meta); font-weight: var(--fw-bold); background: var(--subtle); border: 1px solid var(--border); border-radius: 999px; padding: 3px 10px; }

/* recent kudos feed */
.inc-kudo { display: flex; gap: 10px; padding: 8px 0; border-top: 1px solid var(--border); }
.inc-kudo:first-of-type { border-top: 0; }
.inc-kudo-body { flex: 1; font-size: var(--fs-caption); }
.inc-kudo-tag { font-size: 11.5px; background: var(--subtle); border-radius: 999px; padding: 1px 7px; }
.inc-kudo-pts { font-size: var(--fs-meta); font-weight: var(--fw-black); color: var(--ok); }
.inc-kudo-reason { color: var(--muted); font-style: italic; margin-top: 2px; }
.inc-kudo-date { font-size: var(--fs-micro); color: var(--faint); margin-top: 2px; }

/* legend */
.inc-legend-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; font-size: var(--fs-caption); }
.inc-legend-ic { width: 22px; text-align: center; }
.inc-legend-what { flex: 1; }
.inc-legend-pts { font-weight: var(--fw-bold); color: var(--accent); font-size: 12.5px; white-space: nowrap; }

/* person modal */
.inc-person-head { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.inc-person-name { font-weight: var(--fw-black); font-size: 17px; display: flex; align-items: center; gap: 8px; }
.inc-breakdown { display: flex; flex-direction: column; gap: 6px; }
.inc-bd-row { display: grid; grid-template-columns: 92px 1fr 38px; align-items: center; gap: 10px; }
.inc-bd-label { font-size: 12.5px; font-weight: var(--fw-semibold); }
.inc-bd-val { text-align: right; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.inc-stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-top: 16px; }
@media (max-width: 560px) { .inc-stat-grid { grid-template-columns: repeat(2, 1fr); } }
.inc-stat { background: var(--subtle); border-radius: 10px; padding: 10px; text-align: center; }
.inc-stat-val { font-size: var(--fs-lg); font-weight: var(--fw-black); }
.inc-stat-label { font-size: var(--fs-micro); color: var(--muted); margin-top: 2px; }

/* ============ SPEC61 — Customizable Pipelines ============ */
.pl-tabs { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.pl-tab { padding: 6px 12px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel); cursor: pointer; font-weight: var(--fw-semibold); font-size: var(--fs-caption); }
.pl-tab:hover { background: var(--hover); }
.pl-tab.active { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); color: var(--accent-hover); }   /* SPEC175 — accent-soft pill; --accent-hover text clears AA on the soft fill in both themes */
.pl-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.pl-stats { display: flex; align-items: center; gap: 14px; font-size: var(--fs-caption); color: var(--muted); }
.pl-stat-val { font-weight: var(--fw-black); color: var(--text); }
.pl-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* board reuses .kanban; add a per-column value sum + add button + card extras */
.pl-board { align-items: flex-start; }
/* SPEC-PIPEFIT 2026-09-03 — ONE column rule for BOTH pipeline boards.
   Vincent: "the original pipelines load beautiful boxes. the other signature custom
   loads horribly." MEASURED, and it was not a matter of taste — pipelines.js has two
   board renderers and only one of them was ever fixed. The built-in tabs
   (pbBuildBoard, ~line 975) carried this exact rule as an INLINE style, so they fit;
   a pipeline he created himself (buildBoard, ~line 307) built a bare `.kanban-col`
   and inherited the global `flex: 0 0 260px` (styles.css:1039), overflowing its pane
   by 68px at 1440, 210px at 1280 and 466px at 1024.
   Both renderers put `.pl-board` on the board element (pipelines.js:297 and :948), so
   scoping the rule here reaches both AND removes the inline copy that let them drift
   apart in the first place. Values are byte-identical to the inline override being
   deleted, so the built-in boards render exactly as they do today. */
.pl-board .kanban-col { flex: 1 1 190px; min-width: 190px; max-width: 240px; }
.pl-col-sum { font-size: var(--fs-meta); font-weight: var(--fw-black); color: var(--ok); margin: -4px 4px 8px; }
.pl-add-card { width: 100%; margin-top: 8px; opacity: .8; }
.pl-add-card:hover { opacity: 1; }
.pl-card .pl-value { font-weight: var(--fw-black); color: var(--ok); }
.pl-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; min-width: 0; }
/* PIPEFIX 2026-09-08 — nothing a kanban card renders may paint outside the card.
   `.chip` is `white-space: nowrap` (this file, ~899) and a custom-pipeline card renders
   its custom-field values through App.chip, so ONE 53-char instagram.com URL on Vincent's
   "Signature x IG" board laid out 409px wide inside a 170px card and ran 252px past its
   right edge, across the next two columns — measured live at 1440 on all 22 cards.
   SPEC-PIPEFIT 2026-09-03 fixed the COLUMN width for this same complaint and stopped
   there; the card CONTENTS were never fixed, which is why he is looking at it again.

   Scope is deliberate. The first rule is the general safety net and reaches only chips
   INSIDE a `.kanban-card`: a bare-text chip (Job Tracker, Tasks, Marketing, Video — they
   all reuse this class) now WRAPS rather than spilling, which is strictly better than
   today and is the only containment that works on App.chip's bare text node. Chips
   outside a card — `.kanban-col-head`'s stage chip, tables, toolbars, stage bands — keep
   nowrap untouched, so column headings still read on one line.
   The `.pl-chip-*` rules then upgrade the pipeline board's own custom-field chip from
   "wrapped over four lines" to one ellipsized line, because it renders the value in its
   own element (pipelines.js customFieldChip) instead of a text node. */
.kanban-card .chip { max-width: 100%; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
.kanban-card .chip .pl-chip-in { display: flex; align-items: baseline; min-width: 0; }
/* The label half needs its own cap, or it just moves the bug: `flex: 0 0 auto` means it can
   neither shrink nor wrap, so a long field NAME paints out of the card exactly like the long
   VALUE used to (measured at a 190px column: a 37-char label ran 63.4px past the card border
   and 53.4px past the column; 41 chars ran 105px/95px, and even the 240px cap left 4.6px
   showing). It also starved the value — at a 22-char label the chip rendered the field name
   and 0px of its value. His own board can't hit this today ("IG Link" holds URLs, which take
   customFieldChip's link branch and emit no label element at all), but the field-name input
   has no maxlength, so the second descriptive field he adds would.
   max-width rather than `flex: 0 1 auto`: proportional shrinking clips the SHORT labels too —
   measured, a plain "IG Link:" lost 41.9px -> 30.6px on a card that had no overflow at all.
   With the cap, a 7-char label measures byte-identically to today and every long one is
   contained. */
.kanban-card .chip .pl-chip-lbl { flex: 0 0 auto; max-width: 60%; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-right: 4px; }
.kanban-card .chip .pl-chip-val { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The chip's coloured dot has width:6px but no flex basis of its own, and a flex item's
   automatic minimum size is 0 for an empty span — so the moment the rules above put the chip
   under shrink pressure the dot started giving up width too (measured on his real board:
   4.3px instead of 6px, a 28% collapse). It is a fixed-size mark, never a flexible one.
   Scoped to cards so the bare `.chip` rule — which tools/extract-*-css.js copy VERBATIM into
   the generated client-facing slices (portal.css / site.css) — is not touched. */
.kanban-card .chip .chip-dot { flex: 0 0 auto; }
/* The "why to call" line carries only inline styles and matches none of the rules above, so
   a reason naming a long unbroken token (a handle, an address) had nothing to break on. */
.kanban-card .pl-reason-chip { overflow-wrap: anywhere; }
/* Same containment, one level up: the COLUMN HEAD's stage chip. It is not inside a
   `.kanban-card`, so none of the rules above reach it, and as a nowrap flex item of
   `.kanban-col-head` its automatic minimum size is the full label — flex-shrink has literally
   no effect, and the column is hard-capped at 190–240px, so a long stage name paints straight
   out of the column's background. His current labels fit ("Intro Call Offered" clears by
   11.1px), but the ceiling is ~19 characters and the stage-name input has no maxlength, so
   "Contract Sent - Waiting" would escape by ~18px the day he types it — and he named these
   stages himself. `white-space: normal` rather than ellipsis because App.chip drops the label
   in as a bare text node, which text-overflow cannot act on; a short label is unaffected
   (it never reaches a wrap point), a long one takes a second line instead of escaping.
   Scoped to `.pl-board` so the Job Tracker's own column heads are untouched. */
.pl-board .kanban-col-head { min-width: 0; }
.pl-board .kanban-col-head .chip { min-width: 0; max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
.pl-board .kanban-col-head .count { flex: 0 0 auto; }
/* The card SHEET shows a custom field's saved value in a full-width button with no wrap rule,
   and after PIPEFIX-MOVE that is exactly where a moved card's 53-char Instagram URL is read.
   Scoped to the sheet's own class, which only pbInlineRow emits. */
.pb-inline-val { overflow-wrap: anywhere; }
a.pl-chip-val { color: inherit; text-decoration: none; }
a.pl-chip-val:hover { text-decoration: underline; }
/* Belt and braces for the card's other free-text nodes: both already wrap ordinary
   prose, so this changes behaviour only for a token that offers no break opportunity —
   a pasted URL in an opportunity title, or a next-step note that is one long word. */
.kanban-card .kc-title, .kanban-card .pl-next { overflow-wrap: anywhere; }

/* section heads inside modals */
.pl-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  font-size: var(--fs-meta); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .4px; color: var(--muted);
  margin: 16px 0 8px; border-top: 1px solid var(--border); padding-top: 12px; }
.pl-custom { margin-top: 4px; }

/* stage + field editors */
.pl-edit-list { display: flex; flex-direction: column; gap: 8px; }
.pl-edit-row { display: flex; align-items: center; gap: 8px; }
.pl-edit-row .input { flex: 1; min-width: 0; }
.pl-color-sel { width: 92px; flex: 0 0 auto; }
.pl-field-row .select { flex: 0 0 auto; width: 110px; }

/* template picker */
.pl-templates { display: flex; flex-direction: column; gap: 10px; }
.pl-template { display: flex; align-items: center; gap: 14px; text-align: left; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--border); background: var(--panel); cursor: pointer; }
.pl-template:hover { border-color: var(--accent); background: var(--hover); }
.pl-template-ico { font-size: 26px; flex: 0 0 auto; }
.pl-template-name { font-weight: var(--fw-bold); }
.pl-template-sub { font-size: var(--fs-meta); color: var(--muted); margin-top: 2px; }

/* ============ SPEC62 — Conversation Coaching ============ */
.cc-section { margin-top: 16px; border-top: 1px solid var(--border); padding-top: 12px; }
.cc-section-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-weight: var(--fw-bold); font-size: var(--fs-caption); margin-bottom: 8px; }
.cc-section-head .faint { font-weight: var(--fw-medium); font-size: 11.5px; }
.cc-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }
.cc-transcript, .cc-prompt { width: 100%; }
.cc-prompt { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-meta); }

/* coaching trend strip */
.cc-trend-row { display: flex; align-items: center; gap: 10px; padding: 5px 0; }
.cc-trend-name { font-weight: var(--fw-semibold); font-size: 13.5px; }
.cc-trend-n { font-size: 11.5px; color: var(--muted); }
.cc-trend-row .spark { margin-left: auto; }
.cc-trend-avg { font-weight: var(--fw-black); font-size: var(--fs-caption); font-variant-numeric: tabular-nums; }
.cc-trend-row .spark + .cc-trend-avg { margin-left: 8px; }
.cc-trend-row:not(:has(.spark)) .cc-trend-avg { margin-left: auto; }

/* session list + cards */
.cc-list { display: flex; flex-direction: column; gap: 12px; margin-top: 12px; }
.cc-card-head { display: flex; align-items: center; gap: 10px; }
.cc-card-meta { flex: 1; min-width: 0; }
.cc-card-person { font-weight: var(--fw-bold); font-size: var(--fs-body); display: flex; align-items: center; gap: 8px; }
.cc-score { color: #fff; font-size: var(--fs-micro); font-weight: var(--fw-black); border-radius: 999px; padding: 1px 8px; }
.cc-card-sub { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 3px; font-size: var(--fs-meta); }
.cc-card-title { font-weight: var(--fw-semibold); margin: 8px 0 4px; }
.cc-analysis { font-size: var(--fs-caption); color: var(--text); white-space: pre-wrap; background: var(--subtle); border-radius: 10px; padding: 10px 12px; margin-top: 6px;
  max-height: 150px; overflow: auto; line-height: var(--lh-normal); }
.cc-card-actions { display: flex; gap: 8px; margin-top: 10px; }

/* ============ SPEC63 — Custom Dashboards ============ */
.dw-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 14px; }
.dw-tile { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow);
  padding: 12px 14px; cursor: grab; transition: box-shadow .15s, border-color .15s; }
.dw-tile.dragging { opacity: .5; }
.dw-tile.dw-over { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
/* CUSTOM-1 — 12-column span grid (resizable widgets). Resting board is calm; the
   resize/reorder affordances only appear in Edit mode. Mobile collapses to 1 col. */
.dwx-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 14px; align-items: start; }
.dwx-tile { grid-column: span var(--w, 6); min-width: 0; position: relative; cursor: default; }
.dwx-editing .dwx-tile { cursor: grab; outline: 1px dashed var(--border-strong); outline-offset: 2px; }
.dwx-editing .dwx-tile.dragging { cursor: grabbing; }
.dwx-chips { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border); }
.dwx-chip { font-size: var(--fs-micro); font-weight: var(--fw-semibold); padding: 2px 9px; border-radius: 99px; border: 1px solid var(--border); background: var(--subtle); color: var(--muted); cursor: pointer; line-height: 1.6; }
.dwx-chip:hover { border-color: var(--accent); color: var(--accent); }
.dwx-chip.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.dwx-resize { position: absolute; right: 3px; bottom: 2px; width: 18px; height: 18px; display: flex; align-items: flex-end; justify-content: flex-end; color: var(--muted); font-size: var(--fs-micro); cursor: nwse-resize; opacity: .55; touch-action: none; user-select: none; }
.dwx-resize:hover { opacity: 1; color: var(--accent); }
@media (max-width: 700px) {
  .dwx-grid { grid-template-columns: 1fr; }
  .dwx-tile { grid-column: 1 / -1 !important; min-height: 0 !important; }
  .dwx-resize { display: none; }   /* mobile = chip-resize only, no corner-drag */
}
@media (prefers-reduced-motion: reduce) { .dwx-tile { transition: none; } }
/* CUSTOM-3/4 — configurable chart tile (bar = DOM, donut = SVG numbers only) */
.chart-empty { color: var(--muted); font-size: var(--fs-meta); padding: 18px 4px; text-align: center; }
.chart-plot { display: flex; align-items: flex-end; gap: 6px; position: relative; }
.chart-col { flex: 1 1 0; min-width: 0; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 2px; }
.chart-colval { font-size: 10px; color: var(--muted); font-weight: var(--fw-semibold); white-space: nowrap; }
.chart-colbar { width: 100%; min-height: 2px; border-radius: 4px 4px 0 0; transition: height .3s ease; }
.chart-goalline { position: absolute; left: 0; right: 0; border-top: 1.5px dashed var(--muted); opacity: .7; pointer-events: none; }
.chart-xlabels { display: flex; gap: 6px; margin-top: 5px; }
.chart-xlbl { flex: 1 1 0; min-width: 0; font-size: 10px; color: var(--muted); text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chart-donut { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.chart-donut-svg { flex: 0 0 auto; line-height: 0; }
.chart-legend { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
.chart-legrow { display: flex; align-items: center; gap: 6px; font-size: var(--fs-meta); min-width: 0; }
.chart-legdot { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 auto; }
.chart-leglbl { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
.chart-legval { font-weight: var(--fw-bold); color: var(--text); flex: 0 0 auto; }
.chart-numwrap { padding: 10px 0 4px; }
.chart-number { font-size: 30px; font-weight: var(--fw-black); letter-spacing: -.5px; color: var(--text); }
@media (prefers-reduced-motion: reduce) { .chart-colbar { transition: none; } }
/* CUSTOM-8 — section shell (per-person collapse / reorder / hide on any view) */
.sec-shell-bar { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
.sec-shell-title { font-weight: var(--fw-bold); font-size: 15px; }
.sec-block { margin-bottom: 14px; }
.sec-head { display: flex; align-items: center; gap: 6px; }
.sec-caret { background: none; border: 0; cursor: pointer; color: var(--muted); font-size: var(--fs-caption); padding: 2px 5px; line-height: 1; border-radius: 6px; flex: 0 0 auto; }
.sec-caret:hover { background: var(--hover); color: var(--text); }
.sec-title { font-weight: var(--fw-bold); font-size: var(--fs-caption); flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sec-ctrl { display: flex; gap: 2px; flex: 0 0 auto; }
.sec-body { margin-top: 6px; }
.sec-hidden-note { display: flex; align-items: center; gap: 10px; padding: 8px 2px; }
/* CUSTOM-11 — starter template picker */
.dw-tpl-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-top: 12px; }
.dw-tpl { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; padding: 14px; border: 1px solid var(--border); border-radius: 12px; background: var(--subtle); cursor: pointer; text-align: left; font: inherit; color: var(--text); transition: border-color .12s, box-shadow .12s; }
.dw-tpl:hover { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.dw-tpl-ico { font-size: var(--fs-h1); line-height: 1; }
.dw-tpl-name { font-weight: var(--fw-bold); font-size: 13.5px; }
.dw-tpl-sub { font-size: var(--fs-micro); }
.dw-tpl-blank { border-style: dashed; align-items: center; justify-content: center; text-align: center; color: var(--muted); }
.dw-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.dw-title { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); text-transform: uppercase; letter-spacing: .4px; min-width: 0; }
.dw-title > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dw-title-ico { flex: 0 0 auto; color: var(--muted); }
/* SPEC210 — header drill button: reads as the plain title, gains a chevron + accent on hover/focus. */
.dw-title-go { display: inline-flex; align-items: center; gap: 5px; border: none; background: transparent; cursor: pointer;
  font: inherit; font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); text-transform: uppercase; letter-spacing: .4px;
  padding: 0; border-radius: 6px; max-width: calc(100% - 28px); transition: color .12s; }
.dw-title-go > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dw-title-go:hover, .dw-title-go:focus-visible { color: var(--accent); outline: none; }
.dw-title-arrow { opacity: 0; transform: translateX(-2px); transition: opacity .12s, transform .12s; flex: 0 0 auto; }
.dw-title-go:hover .dw-title-arrow, .dw-title-go:focus-visible .dw-title-arrow { opacity: 1; transform: translateX(0); }
/* SPEC210 — any clickable widget row/stat: subtle hover + keyboard focus, generous tap target. */
.dw-go { cursor: pointer; border-radius: 7px; margin: 0 -6px; padding: 3px 6px; transition: background .12s; }
.dw-li.dw-go, .dw-rank.dw-go { padding: 4px 6px; }
.dw-go:hover { background: var(--hover); }
.dw-go:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; background: var(--hover); }
.dw-x { border: none; background: transparent; color: var(--faint); cursor: pointer; font-size: var(--fs-caption); padding: 2px 4px; border-radius: 6px; }
.dw-x:hover { background: var(--hover); color: var(--danger); }
.dw-body { min-height: 40px; }
.dw-stat { margin-bottom: 6px; }
.dw-big { font-size: 26px; font-weight: var(--fw-black); letter-spacing: -.5px; line-height: 1.1; }
.dw-stat-label { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.dw-list { display: flex; flex-direction: column; gap: 4px; margin-top: 6px; }
.dw-li { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; }
.dw-li-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dw-li-s { flex: 0 0 auto; color: var(--muted); font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); }
.dw-rank { display: flex; align-items: center; gap: 6px; font-size: 12.5px; }
.dw-rank .dw-li-t { flex: 1; font-weight: var(--fw-semibold); }
.dw-medal { width: 18px; text-align: center; }

/* add-widget catalog */
.dw-catalog { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
@media (max-width: 520px) { .dw-catalog { grid-template-columns: 1fr; } }
.dw-cat { display: flex; flex-direction: column; align-items: stretch; gap: 6px; text-align: left; padding: 10px 12px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--panel); cursor: pointer; }
.dw-cat:hover { border-color: var(--accent); background: var(--hover); }
.dw-cat-on { border-color: var(--accent); }
.dw-cat-ico { font-size: var(--fs-lg); }
.dw-cat-head { display: flex; align-items: center; gap: 8px; }
.dw-cat-label { flex: 1; font-weight: var(--fw-semibold); font-size: var(--fs-caption); display: inline-flex; align-items: center; gap: 4px; min-width: 0; }
/* CUSTOM-5 — clipped, non-interactive live preview of the widget in the catalog */
.dw-cat-preview { max-height: 92px; overflow: hidden; pointer-events: none; opacity: .82; border-top: 1px solid var(--border); padding-top: 6px; font-size: var(--fs-micro); -webkit-mask-image: linear-gradient(to bottom, #000 65%, transparent); mask-image: linear-gradient(to bottom, #000 65%, transparent); }
.dw-cat-preview .dw-big { font-size: 20px; }
.dw-cat-tag { font-size: 10px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; color: var(--muted);
  background: var(--subtle); border-radius: 999px; padding: 1px 6px; }

/* ============ SPEC77 — live AI answer (FAQ) ============ */
.faq-ai-answer { margin-top: 12px; background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 12px; padding: 12px 14px; }
.faq-ai-label { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; color: var(--accent); margin-bottom: 6px; }
.faq-ai-text { font-size: var(--fs-body); line-height: 1.55; white-space: pre-wrap; }
/* SPEC79 — one-click AI draft box (App.aiButton result) */
.ai-gen-result { margin-top: 12px; }
.ai-gen-result:empty { display: none; }
.ai-gen-label { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; color: var(--accent); margin-bottom: 6px; }
.ai-gen-text { font-size: var(--fs-body); line-height: 1.55; white-space: pre-wrap; background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 12px; padding: 12px 14px; }
/* SPEC81 — reactive home "Pulse" strip */
.pulse-strip { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 14px; }
.pulse-pill { display: inline-flex; align-items: center; gap: 7px; padding: 7px 13px; border-radius: 999px; font-size: var(--fs-caption); font-weight: var(--fw-bold); cursor: pointer; border: 1px solid var(--border); background: var(--card); color: var(--text); transition: transform .08s ease, box-shadow .12s ease, background .12s ease; }
.pulse-pill:hover { transform: translateY(-1px); box-shadow: 0 3px 10px rgba(0,0,0,.10); }
.pulse-pill .pulse-ic { font-size: var(--fs-body); }
.pulse-pill.pulse-warn { border-color: var(--warn); background: var(--warn-soft); color: var(--warn); }
.pulse-pill.pulse-go { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); }
.today-focus { margin: 0 0 12px; }
/* SPEC84 — tags / labels */
.tag-chips { display: inline-flex; flex-wrap: wrap; gap: 4px; vertical-align: middle; }
.tag-input-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.tag-input-chips:empty { display: none; }
.tag-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 8px; border-radius: 999px; font-size: var(--fs-meta); font-weight: var(--fw-bold); border: 1px solid transparent; }
.tag-chip .tag-x { border: none; background: transparent; color: inherit; cursor: pointer; font-size: var(--fs-micro); line-height: 1; padding: 0; opacity: .65; }
.tag-chip .tag-x:hover { opacity: 1; }
/* SPEC85 — bulk actions */
.bulk-cb { width: 16px; height: 16px; cursor: pointer; accent-color: var(--accent); }
tr.row-selected > td { background: var(--accent-soft) !important; }
.bulk-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 10px; padding: 8px 12px; border: 1px solid var(--accent); background: var(--accent-soft); border-radius: 10px; position: sticky; top: 6px; z-index: 5; }
.bulk-bar .bulk-count { font-weight: var(--fw-black); color: var(--accent); font-size: var(--fs-caption); }
.kanban-card .kc-sel { float: right; margin: 0 0 4px 6px; }
.kanban-card.card-selected { outline: 2px solid var(--accent); outline-offset: 1px; }
/* SPEC87 — saved / named views bar */
.views-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 10px; }
.views-label { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin-right: 2px; }
.view-chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 999px; font-size: 12.5px; font-weight: var(--fw-bold); cursor: pointer; border: 1px solid var(--border); background: var(--card); color: var(--text); transition: border-color .12s ease, background .12s ease; }
.view-chip:hover { border-color: var(--accent); }
.view-chip.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.view-chip-x { font-size: 10px; opacity: .5; }
.view-chip-x:hover { opacity: 1; }
.view-chip-add { border-style: dashed; color: var(--muted); font-weight: var(--fw-semibold); }
/* SPEC90 — Books (Beancount reports) */
.books-section { margin-bottom: 16px; }
.books-sec-head { display: flex; justify-content: space-between; align-items: baseline; font-weight: var(--fw-black); font-size: var(--fs-body); padding: 6px 8px; border-bottom: 2px solid var(--border); }
.books-tree { padding: 3px 0; }
.books-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 4px 8px; font-size: var(--fs-caption); border-radius: 5px; }
.books-row:hover { background: var(--hover); }
.books-row.books-parent { font-weight: var(--fw-bold); }
.books-amt { font-variant-numeric: tabular-nums; white-space: nowrap; cursor: pointer; padding: 1px 4px; border-radius: 4px; }
.books-amt:hover { background: var(--accent-soft); color: var(--accent); text-decoration: underline; }
.books-grandtotal { display: flex; justify-content: space-between; align-items: baseline; margin-top: 14px; padding: 10px 8px; border-top: 2px solid var(--text); font-weight: var(--fw-black); font-size: 14.5px; }
.books-grandtotal.neg { color: var(--danger); }
.books-link { cursor: pointer; color: var(--accent); }
.books-link:hover { text-decoration: underline; }
.books-tb td, .books-drill td { font-size: 12.5px; }
.books-total td { font-weight: var(--fw-black); border-top: 2px solid var(--text); }

/* ---- SPEC100 Receivables ---- */
.field-label { display: block; font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); margin: 0 0 4px; }
.ar-bar { display: flex; height: 16px; border-radius: 8px; overflow: hidden; background: var(--border); margin: 4px 0 10px; }
.ar-bar-seg { display: flex; align-items: center; justify-content: center; min-width: 2px; cursor: pointer; transition: opacity .15s; }
.ar-bar-seg.dim { opacity: .35; }
.ar-bar-seg:hover { filter: brightness(1.08); }
.ar-bar-lbl { font-size: 10px; font-weight: var(--fw-black); color: #fff; text-shadow: 0 1px 1px rgba(0,0,0,.25); }
.ar-legend { display: flex; flex-wrap: wrap; gap: 6px; }
.ar-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--border); background: var(--panel); border-radius: 999px; padding: 4px 10px; font-size: var(--fs-meta); cursor: pointer; font-family: inherit; color: var(--text); }
.ar-chip.on { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.ar-chip strong { font-variant-numeric: tabular-nums; }
.ar-dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: none; }
.ar-age { display: inline-flex; align-items: center; gap: 6px; justify-content: flex-end; font-variant-numeric: tabular-nums; }
.ar-table td { font-size: var(--fs-caption); vertical-align: middle; }
.ar-table tr.ar-overdue td { background: color-mix(in srgb, var(--danger) 7%, transparent); }
.ar-actions { white-space: nowrap; text-align: right; }
.ar-actions .btn { margin-left: 4px; }
/* WAVE5U FIX (Finding 7), continued 2026-09-03 — the margin-bottom on
   .money-scoped-table (above) clears the FAB once a person scrolls to the
   table's true end, but MEASURED live: a scroll that stops with this row's
   own bottom edge flush against the viewport (a real scrollIntoView-style
   "align the row I'm reading" stop, not the table's true end) still landed
   the row's own rect under the FAB — margin AFTER an element can't prevent
   ITS OWN bottom edge from being able to align exactly with the viewport
   edge; only scroll-margin on the element ITSELF changes where an aligning
   scroll actually stops. scroll-margin-bottom is the standards mechanism
   Element.scrollIntoView() (and native browser "scroll to reveal" behavior)
   both respect for exactly this "reserve clearance for a fixed overlay"
   case — reserves the same 56px+padding the FAB (.fab-dial-btn, styles.css)
   occupies, so a stop here never ends flush with that corner. */
@media (min-width: 861px) {
  body[data-view="money"] .ar-actions { scroll-margin-bottom: 88px; }
}

/* ---- SPEC104 Earnings + tip modal ---- */
.field { margin-bottom: 12px; }
.field:last-child { margin-bottom: 0; }
.earn-list { display: flex; flex-direction: column; }
.earn-row { display: flex; align-items: center; gap: 11px; padding: 9px 4px; border-bottom: 1px solid var(--border); }
.earn-row:last-child { border-bottom: none; }
.earn-dot { width: 26px; height: 26px; border-radius: 7px; display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-caption); flex: none; filter: saturate(.9); }
.earn-main { flex: 1 1 auto; min-width: 0; }
.earn-label { font-size: var(--fs-caption); font-weight: var(--fw-semibold); line-height: var(--lh-snug); }
.earn-note { font-size: 11.5px; line-height: 1.4; margin-top: 1px; }
.earn-date { font-size: var(--fs-meta); white-space: nowrap; }
.earn-amt { font-variant-numeric: tabular-nums; font-weight: var(--fw-black); white-space: nowrap; min-width: 64px; text-align: right; }

/* ---- SPEC105 Grow the Business ---- */
.grow-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 12px; }
.grow-play { margin: 0; display: flex; flex-direction: column; }
.grow-play-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 8px; }
.grow-play-title { font-weight: var(--fw-black); font-size: 14.5px; letter-spacing: -.2px; }
.grow-earn { flex: none; max-width: 100%; overflow-wrap: anywhere; font-size: var(--fs-micro); font-weight: var(--fw-black); color: var(--ok-ink); background: var(--ok-soft); border-radius: 999px; padding: 3px 9px; white-space: normal; }
.grow-what { font-size: 12.5px; line-height: var(--lh-normal); color: var(--muted); margin-top: 8px; }
.grow-pitch { font-size: var(--fs-caption); line-height: var(--lh-normal); margin-top: 10px; padding: 9px 11px; background: var(--hover); border-radius: 8px; border-left: 3px solid var(--accent); }
.grow-pitch-q { color: var(--accent); font-weight: var(--fw-black); }
.grow-earn-grid { display: flex; flex-wrap: wrap; gap: 7px; }
.grow-earn-chip { display: inline-flex; flex-direction: column; gap: 1px; border: 1px solid var(--border); background: var(--panel); border-radius: 9px; padding: 6px 10px; font-size: var(--fs-meta); }
.grow-earn-chip strong { font-size: 12.5px; }
.grow-earn-chip .muted { font-size: var(--fs-micro); }
.grow-job .grow-job-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px solid var(--border); }
.grow-job .grow-job-row:last-of-type { border-bottom: none; }

/* ---- SPEC106 ownership loop: goal bar, scoreboard, earned chip ---- */
.goal-bar { height: 9px; border-radius: 6px; background: var(--border); overflow: hidden; }
.goal-fill { height: 100%; border-radius: 6px; background: linear-gradient(90deg, var(--accent), #16a34a); transition: width .35s ease; min-width: 2px; }
.earn-chip { border: 1.5px solid var(--accent); background: var(--accent-soft); }
.earn-chip:hover { filter: brightness(1.02); }
.grow-score { display: flex; flex-direction: column; }
.grow-score-row { display: flex; align-items: center; gap: 10px; padding: 7px 2px; border-bottom: 1px solid var(--border); }
.grow-score-row:last-child { border-bottom: none; }
.grow-score-rank { width: 26px; text-align: center; font-weight: var(--fw-black); flex: none; }
.grow-score-name { font-weight: var(--fw-bold); font-size: var(--fs-caption); flex: 1 1 auto; }

/* ---- SPEC107 Production metrics ---- */
.cov-wrap { margin-top: 8px; }
.cov-bar { height: 5px; border-radius: 4px; background: var(--border); overflow: hidden; }
.cov-fill { height: 100%; background: var(--accent); border-radius: 4px; }
.cov-txt { font-size: 10.5px; color: var(--muted); margin-top: 3px; }
.cov-pill { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--muted); background: var(--panel); border: 1px solid var(--border); border-radius: 999px; padding: 4px 11px; }
.metric-dualbar { display: flex; gap: 2px; align-items: flex-end; height: 100%; justify-content: center; }
.metric-dualbar .bar { width: 12px; }
.bar.bar-alt { background: var(--ok); }
.lg-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; vertical-align: middle; margin-right: 2px; }

/* ---- SPEC110 win moments (sparkle + pop) ---- */
.spark-layer { position: fixed; inset: 0; z-index: 300; pointer-events: none; overflow: hidden; }
.spark-layer .spark { position: fixed; transform: translate(-50%, -50%); will-change: transform, opacity; animation: spark-burst .7s var(--ease) forwards; line-height: 1; }
@keyframes spark-burst {
  0% { transform: translate(-50%, -50%) scale(.3); opacity: 0; }
  18% { opacity: 1; }
  100% { transform: translate(calc(-50% + var(--sx)), calc(-50% + var(--sy))) scale(1); opacity: 0; }
}
.win-pop { animation: win-pop .46s var(--ease-spring); }
@keyframes win-pop { 0% { transform: scale(1); } 38% { transform: scale(1.13); } 100% { transform: scale(1); } }

/* ---- SPEC111 Team Wins feed ---- */
.wins-feed { display: flex; flex-direction: column; }
.wins-empty { font-size: var(--fs-caption); padding: 8px 2px; line-height: var(--lh-normal); }
.win-row { display: flex; align-items: flex-start; gap: 10px; padding: 8px 2px; border-bottom: 1px solid var(--border); }
.win-row:last-child { border-bottom: none; }
.win-emoji { font-size: 19px; line-height: 1.3; flex: none; width: 24px; text-align: center; }
.win-mid { flex: 1 1 auto; min-width: 0; }
.win-text { font-size: var(--fs-caption); line-height: 1.4; }
.win-reacts { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; position: relative; }
.win-react { font-size: 11.5px; font-weight: var(--fw-semibold); border: 1px solid var(--border); background: var(--panel); border-radius: 999px; padding: 2px 8px; cursor: pointer; font-family: inherit; color: var(--text); transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease); }
.win-react:hover { background: var(--hover); }
.win-react.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.win-react-add { color: var(--muted); font-weight: var(--fw-black); padding: 2px 9px; }
.win-react-pop { position: absolute; bottom: calc(100% + 4px); left: 0; display: flex; gap: 2px; background: var(--panel); border: 1px solid var(--border-strong); border-radius: 999px; padding: 3px 5px; box-shadow: var(--shadow-lg); z-index: 5; }
.win-react-opt { font-size: var(--fs-md); line-height: 1; background: none; border: none; cursor: pointer; padding: 3px 4px; border-radius: 6px; transition: transform var(--t-fast) var(--ease); }
.win-react-opt:hover { transform: scale(1.25); }
.win-time { font-size: var(--fs-micro); white-space: nowrap; flex: none; padding-top: 2px; }
.milestone-banner { border: 1.5px solid var(--accent); background: var(--accent-soft); }
.milestone-line { font-size: 14.5px; font-weight: var(--fw-bold); padding: 3px 0; letter-spacing: -.1px; }

/* ============ SPEC76 — Integrations hub ============ */
.intg-row { border-top: 1px solid var(--border); padding: 12px 0; }
.intg-row:first-of-type { border-top: 0; }
.intg-head { display: flex; align-items: flex-start; gap: 10px; }
.intg-ico { font-size: 20px; flex: 0 0 auto; }
.intg-meta { flex: 1; min-width: 0; }
.intg-name { font-weight: var(--fw-bold); font-size: var(--fs-body); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.intg-blurb { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.intg-status { font-size: 10.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
.intg-soon { background: var(--subtle); color: var(--muted); }
.intg-off { background: var(--warn-soft, rgba(220,104,3,.12)); color: var(--warn-ink); }
.intg-live { background: var(--ok-soft, rgba(7,148,85,.14)); color: var(--ok-ink); }

/* ============ SPEC73 — proactive nudges (stale deals) ============ */
.pl-idle { margin-top: 6px; font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--warn); background: var(--warn-soft, rgba(220,104,3,.12));
  border-radius: 6px; padding: 2px 7px; display: inline-flex; align-items: center; gap: 5px; }

/* SPEC244 — Reactive CRM card affordances: next-step line, "add a step" prompt, tiered rot */
.pl-next { display: block; width: 100%; text-align: left; margin-top: 6px; font-size: 11.5px; font-weight: var(--fw-bold);
  color: var(--accent-hover); background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  border-radius: 6px; padding: 4px 8px; cursor: pointer; white-space: normal; line-height: var(--lh-snug); }
.pl-next.overdue { color: var(--danger); background: var(--danger-soft, rgba(239,68,68,.12)); border-color: color-mix(in srgb, var(--danger) 35%, transparent); }
.pl-needstep { display: inline-flex; align-items: center; margin-top: 6px; font-size: var(--fs-micro); font-weight: var(--fw-bold); cursor: pointer;
  color: var(--muted); background: var(--subtle); border: 1px dashed var(--border); border-radius: 6px; padding: 3px 8px; }
.pl-needstep:hover { color: var(--accent-hover); border-color: var(--accent); }
.pl-rot-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px; background: var(--warn); }
.pl-rot-cool .pl-rot-dot { background: #f59e0b; }
.pl-rot-warm .pl-rot-dot { background: #f97316; }
.pl-rot-hot { color: var(--danger); background: var(--danger-soft, rgba(239,68,68,.12)); }
.pl-rot-hot .pl-rot-dot { background: var(--danger); animation: pulse 1.4s infinite; }
.pl-nudge { margin-left: auto; font-size: 10.5px; font-weight: var(--fw-black); cursor: pointer; color: #fff; background: var(--accent);
  border: none; border-radius: 999px; padding: 2px 9px; }
.pl-nudge:hover { filter: brightness(1.06); }

/* ============ SPEC72 — auto Shoot Prep ============ */
.prep-facts { font-size: 13.5px; font-weight: var(--fw-bold); background: var(--subtle); border: 1px solid var(--border); border-radius: 8px; padding: 6px 10px; margin-bottom: 4px; }
.prep-head { font-size: 11.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin: 12px 0 5px; }
.prep > .prep-head:first-child { margin-top: 0; }
.prep-svc { font-weight: var(--fw-bold); font-size: var(--fs-caption); margin: 8px 0 2px; }
.prep-list { margin: 0 0 4px; padding-left: 20px; }
.prep-list li { font-size: var(--fs-caption); line-height: var(--lh-normal); color: var(--text); margin-bottom: 2px; }
.prep-links { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.prep-links a { text-decoration: none; }

/* ============ SPEC71 — Activity Log ============ */
.act-day { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); margin: 12px 0 4px; padding-top: 8px; border-top: 1px solid var(--border); }
.act-day:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.act-row { display: flex; align-items: flex-start; gap: 10px; padding: 6px 4px; border-radius: 8px; }
.act-clickable { cursor: pointer; }
.act-clickable:hover { background: var(--hover); }
.act-ico { font-size: 15px; width: 22px; text-align: center; flex: 0 0 auto; }
.act-body { flex: 1; min-width: 0; }
.act-text { font-size: var(--fs-caption); line-height: 1.45; }
.act-name { font-weight: var(--fw-semibold); }
.act-from { color: var(--muted); }
.act-to { color: var(--accent); font-weight: var(--fw-semibold); }
.act-when { font-size: var(--fs-micro); color: var(--faint); margin-top: 1px; }
.act-person-row { display: flex; align-items: center; gap: 10px; padding: 5px 0; }
.act-person-name { font-weight: var(--fw-semibold); font-size: var(--fs-caption); width: 110px; flex: 0 0 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.act-person-n { font-weight: var(--fw-black); font-variant-numeric: tabular-nums; width: 32px; text-align: right; flex: 0 0 auto; }
.act-history { display: flex; flex-direction: column; gap: 2px; max-height: 190px; overflow: auto; }

/* ============ SPEC69 — click-to-copy chip ============ */
.copyable { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; border-radius: 6px; padding: 0 4px; max-width: 100%; vertical-align: bottom; }
.copyable:hover { background: var(--hover); }
.copyable:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.copyable-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.copyable-ico { font-size: var(--fs-micro); opacity: 0; flex: 0 0 auto; color: var(--muted); transition: opacity .12s ease; }
.copyable:hover .copyable-ico, .copyable:focus-visible .copyable-ico { opacity: .7; }
.copyable.copied, .copyable.copied .copyable-ico { color: var(--ok); opacity: 1; }

/* ============ SPEC68 — get-started checklist + accent picker ============ */
.help-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  font-size: var(--fs-meta); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; color: var(--muted);
  margin: 18px 0 8px; }
.setup-list { display: flex; flex-direction: column; gap: 6px; }
.setup-row { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--panel); cursor: pointer; }
.setup-row:hover { background: var(--hover); border-color: var(--accent); }
.setup-row.done { opacity: .7; cursor: default; }
.setup-row.done:hover { background: var(--panel); border-color: var(--border); }
.setup-check { width: 20px; height: 20px; flex: 0 0 auto; border-radius: 50%; border: 2px solid var(--border);
  display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-meta); font-weight: var(--fw-heavy); color: #fff; }
.setup-row.done .setup-check { background: var(--ok); border-color: var(--ok); }
.setup-label { flex: 1; font-weight: var(--fw-semibold); font-size: 13.5px; }
.setup-go { color: var(--accent); font-weight: var(--fw-black); }
.setup-done-tag { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .4px; color: var(--ok); }
.accent-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.accent-dot { width: 28px; height: 28px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; padding: 0;
  box-shadow: inset 0 0 0 2px var(--panel); }
.accent-dot.active { border-color: var(--text); }
.accent-reset { border: 1px solid var(--border); background: var(--panel); border-radius: 999px; padding: 4px 12px;
  font-size: var(--fs-meta); font-weight: var(--fw-bold); cursor: pointer; color: var(--muted); }
.accent-reset.active { border-color: var(--text); color: var(--text); }
.accent-reset:hover { background: var(--hover); }

/* ============ SPEC67 — interaction & motion polish ============ */
/* smooth transitions on interactive surfaces */
.btn, .chip-btn, .pl-tab, .dw-tile, .card, .input, .select, .textarea, .kanban-card, .pl-template,
.dw-cat, .inc-lb-row, .soc-top-row, .call-row, .cc-card, .snippet-card, .pl-card {
  transition: background-color .15s ease, border-color .15s ease, box-shadow .15s ease, transform .12s ease, color .15s ease;
}
/* tactile button press */
.btn:active, .chip-btn:active, .pl-tab:active, .dw-cat:active, .pl-template:active, .toast-btn:active { transform: translateY(1px); }
/* clear, modern keyboard focus rings (accessibility + polish) */
.btn:focus-visible, .chip-btn:focus-visible, .pl-tab:focus-visible, .inc-lb-row:focus-visible,
.dw-cat:focus-visible, a:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
/* DESIGNA 2026-08-19 — a duplicate `.input:focus, .select:focus, .textarea:focus` rule was
   deleted here: identical to :404's except it lacked background-color, so being later-in-file
   it added nothing and only invited the two rules to drift apart. :404 is the one source. */
/* clickable cards/tiles lift a touch */
.kanban-card:hover, .pl-card:hover, .pl-template:hover, .dw-cat:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
/* glassy backdrop behind modals + command palette */
.modal-overlay, .qs-overlay { backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); }
/* a soft fade-in for grids of cards/tiles (matches the count-up feel) */
@media (prefers-reduced-motion: no-preference) {
  .kpi-row > *, .dw-grid > *, .dwx-grid > *, .soc-grid-2 > *, .pl-templates > * { animation: tile-in .26s ease-out both; }
  /* SPEC175 — each view settles in on navigation (#view re-renders per route → fires
     naturally, no JS). A whisper of opacity + rise; reduced-motion disables it.
     SLACK-M checker1 2026-08-13 — fill `both` → `backwards`: with `both`, the finished
     animation kept APPLYING its (implicit-to) end state forever, which Chrome computes
     as transform matrix(1,0,0,1,0,0) — not `none` — so #view was permanently a fixed-
     position CONTAINING BLOCK. Every position:fixed element rendered inside #view
     (the ≤1100px .chat-thread full-screen overlay is the one that ships today) pinned
     to #view's box instead of the viewport — measured live on a 375×812 phone: the
     thread overlay sat at y=56 with its composer's Send button 59px BELOW the screen,
     unreachable. `backwards` is frame-identical (the implicit end state IS the base
     state) but stops applying once finished, so transform computes back to `none` and
     fixed children anchor to the real viewport — exactly what .chat-thread's own
     z-index comment ("above app chrome, below modals") always assumed. */
  #view { animation: view-enter var(--t-slow) var(--ease) backwards; }
  /* mobile: slide modals up from the bottom for a native-app feel.
     SPEC-V2MODALFIT — threshold moved 860->640 to match the ACTUAL phone
     bottom-sheet posture breakpoint (styles.css' own @media max-width:640px
     block, below). Above that (the new 640-1023 tablet tier), a modal is a
     centered floating card, not bottom-docked — animating it in with
     translateY(100%) would slide a CENTERED card up from off-screen, which
     reads as a glitch, not a "native app" feel; the plain modal-in
     scale/fade (the default, non-mobile animation) is correct there. */
  @media (max-width: 640px) { .modal, .modal.wide, .modal.narrow { animation: sheet-up .22s cubic-bezier(.22,1,.36,1); } }
}
@keyframes view-enter { from { opacity: 0; transform: translateY(4px); } }
@keyframes tile-in { from { opacity: 0; transform: translateY(6px); } }
@keyframes sheet-up { from { transform: translateY(100%); } }
/* refined desktop scrollbars */
@media (min-width: 861px) {
  .modal-body, .table-wrap, #view, .cc-analysis { scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
  .modal-body::-webkit-scrollbar, .table-wrap::-webkit-scrollbar, #view::-webkit-scrollbar, .cc-analysis::-webkit-scrollbar { width: 10px; height: 10px; }
  .modal-body::-webkit-scrollbar-thumb, .table-wrap::-webkit-scrollbar-thumb, #view::-webkit-scrollbar-thumb, .cc-analysis::-webkit-scrollbar-thumb { background: var(--border); border-radius: 999px; }
  .modal-body::-webkit-scrollbar-thumb:hover, .table-wrap::-webkit-scrollbar-thumb:hover { background: var(--muted); }
}

/* ============ SPEC64 — Call Tracking ============ */
.call-dir-row { display: grid; grid-template-columns: 110px 1fr auto; align-items: center; gap: 10px; padding: 5px 0; }
.call-dir-stat { font-size: var(--fs-meta); color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.call-log-table th.num, .call-log-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.call-row { cursor: pointer; }
.call-row:hover { background: var(--hover); }
.table th.num, .table td.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---------- Schedule / crew dispatch (SPEC94) ---------- */
.sched-grid-wrap { overflow-x: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); }
.sched-grid { border-collapse: collapse; width: 100%; min-width: 760px; }
.sched-grid th, .sched-grid td { border: 1px solid var(--border); vertical-align: top; }
.sched-grid thead th { position: sticky; top: 0; background: var(--panel); z-index: 1; padding: 6px 8px; font-size: var(--fs-meta); text-align: center; }
.sched-grid .sched-name-h { text-align: left; min-width: 110px; }
.sched-grid .sched-day-h.today { background: var(--accent-soft); color: var(--accent); }
.sched-cap-h { min-width: 130px; }
.sched-name { padding: 8px; font-weight: var(--fw-bold); white-space: nowrap; }
.sched-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 6px; vertical-align: middle; }
.sched-daycell { width: 13%; min-width: 92px; padding: 4px; }
.sched-daycell.drop-hot { background: var(--accent-soft); outline: 2px dashed var(--accent); outline-offset: -2px; }
.sched-block { border: 1px solid var(--border); border-left: 3px solid #94a3b8; border-radius: 6px; padding: 4px 6px; margin-bottom: 4px; background: var(--bg); font-size: var(--fs-meta); }
.sched-block.conflict { background: var(--danger-soft); border-color: var(--danger); }
.sched-block.soft { background: var(--warn-soft); border-color: var(--warn); }
.sched-block-top { display: flex; align-items: center; gap: 4px; }
.sched-time { border: none; background: transparent; font-size: 11.5px; font-weight: var(--fw-bold); color: var(--text); padding: 0; width: 84px; }
.sched-block-client { font-weight: var(--fw-semibold); cursor: pointer; margin-top: 2px; }
.sched-block-client:hover { color: var(--accent); }
.sched-block-addr { color: var(--muted); font-size: var(--fs-micro); cursor: pointer; }
.sched-cap { padding: 6px 8px; }
.sched-cap-row { display: flex; align-items: center; gap: 6px; margin: 2px 0; }
.sched-cap-row .databar { flex: 1; }
.sched-unassigned { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 7px; border: 1px dashed var(--border); margin-bottom: 6px; cursor: grab; }
.sched-unassigned.dragging { opacity: .5; }
.sched-unassigned:hover { background: var(--hover); }
.sched-chiprow { display: flex; gap: 5px; flex-wrap: wrap; justify-content: flex-end; }
.sched-chip { font-size: 11.5px; padding: 3px 9px; border-radius: 999px; border: 1.5px solid var(--border); background: var(--panel); cursor: pointer; font-weight: var(--fw-semibold); }
.sched-chip:hover { background: var(--hover); }
.sched-myrow { display: flex; align-items: center; gap: 8px; padding: 6px 4px; border-radius: 6px; cursor: pointer; }
.sched-myrow:hover { background: var(--hover); }

/* ---------- Personal sidebar shortcuts (SPEC95) ---------- */
.nav-shortcuts { margin-bottom: 6px; }
.nav-sc-head { display: flex; align-items: center; justify-content: space-between; }
/* LAW-A 2026-08-15 (#22) — same bug as .nav-group-label above: var(--muted)/var(--faint)
   are PAGE inks, applied here on the permanently-dark sidebar rail. Measured on
   --sidebar-bg (light theme, the failing case): --muted 2.51:1 (FAIL) → --sidebar-text
   6.38:1 (PASS); --faint 3.15:1 (FAIL) → --sidebar-text 6.38:1 (PASS). Dark theme was
   already AA (6.46–6.97:1) and stays AA (7.01:1) — no regression. .nav-sc-cat wasn't in
   the original 3-site list but is the same rail, same bug, same fix — included for
   consistency (one ink, not three near-misses). */
.nav-sc-add { border: none; background: transparent; color: var(--sidebar-text); font-size: 15px; font-weight: var(--fw-bold); line-height: 1; cursor: pointer; padding: 0 4px; border-radius: 4px; }
.nav-sc-add:hover { background: var(--hover); color: var(--accent); }
.nav-sc-empty { font-size: 11.5px; color: var(--sidebar-text); padding: 4px 10px 8px; cursor: pointer; line-height: var(--lh-snug); }
.nav-sc-empty:hover { color: var(--accent); }
.nav-sc-cat { font-size: 10.5px; text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--sidebar-text); padding: 4px 10px 2px; }
.nav-sc-item .nav-sc-lbl { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: left; }
.nav-sc-item .nav-sc-x { opacity: 0; color: var(--muted); font-size: var(--fs-micro); padding: 0 2px; border-radius: 4px; }
.nav-sc-item:hover .nav-sc-x { opacity: .7; }
.nav-sc-item .nav-sc-x:hover { opacity: 1; color: #ef4444; background: var(--hover); }
.datalist-host { height: 0; overflow: hidden; }

/* ---------- Snippet quick-insert (SPEC96) ---------- */
.snip-dd { z-index: 9000; background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-lg, 0 12px 32px rgba(0,0,0,.18)); max-height: 280px; overflow-y: auto; padding: 4px; }
.snip-opt { padding: 7px 9px; border-radius: 7px; cursor: pointer; }
.snip-opt.active, .snip-opt:hover { background: var(--accent-soft); }
.snip-opt-title { font-size: 12.5px; font-weight: var(--fw-bold); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.snip-opt-body { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 1px; }
.snip-dd-hint { font-size: 10.5px; color: var(--faint); padding: 5px 9px 3px; border-top: 1px solid var(--border); margin-top: 3px; }
/* SPEC267 — @-mention typeahead dropdown (mirrors .snip-dd) */
.mention-dd { z-index: 9000; background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-lg, 0 12px 32px rgba(0,0,0,.18)); max-height: 260px; overflow-y: auto; padding: 4px; }
.mention-opt { display: flex; align-items: center; gap: 8px; padding: 6px 9px; border-radius: 7px; cursor: pointer; }
.mention-opt.active, .mention-opt:hover { background: var(--accent-soft); }
.mention-av { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; color: #fff; font-size: 10px; font-weight: var(--fw-bold); display: inline-flex; align-items: center; justify-content: center; }
.mention-opt-name { font-size: 12.5px; font-weight: var(--fw-bold); color: var(--text); }
.mention-opt-role { font-size: var(--fs-micro); color: var(--muted); margin-left: auto; }

/* ---------- Time tracking / timesheets (SPEC98) ---------- */
.ts-clock { border-left: 3px solid var(--accent); }
.ts-elapsed { font-variant-numeric: tabular-nums; }
.ts-live-dot { width: 10px; height: 10px; border-radius: 50%; background: #ef4444; display: inline-block; animation: tsPulse 1.4s infinite; }
@keyframes tsPulse { 0%, 100% { opacity: 1; } 50% { opacity: .3; } }
.ts-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.ts-day { border: 1px solid var(--border); border-radius: 8px; background: var(--bg); padding: 7px 4px; text-align: center; cursor: pointer; }
.ts-day:hover { background: var(--hover); }
.ts-day.has { background: var(--accent-soft); border-color: var(--accent); }
.ts-day.today { outline: 2px solid var(--accent); outline-offset: -2px; }
.ts-day-h { font-size: var(--fs-micro); font-weight: var(--fw-bold); }
.ts-day-d { font-size: 10.5px; }
.ts-day-hrs { font-size: var(--fs-body); font-weight: var(--fw-black); margin-top: 3px; font-variant-numeric: tabular-nums; }

/* ---------- Time off / PTO (SPEC99) ---------- */
.to-row { display: flex; align-items: center; gap: 8px; padding: 7px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.to-row:last-child { border-bottom: none; }
.sched-off { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--ok); background: var(--ok-soft); border: 1px dashed var(--ok); border-radius: 5px; padding: 2px 6px; margin-bottom: 4px; }

/* ---------- Calendar busy overlay in the scheduler (SPEC97) ---------- */
.sched-busy { font-size: 10.5px; color: var(--muted); background: repeating-linear-gradient(45deg, var(--hover), var(--hover) 5px, transparent 5px, transparent 10px); border: 1px dashed var(--border); border-radius: 5px; padding: 2px 5px; margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ============ SPEC112 v3 — client-facing "watch your numbers climb" report ============ */
.tss-report { max-width: 900px; margin: 0 auto; }
.tss-hero { background: linear-gradient(135deg, var(--accent), var(--accent-hover)); color: #fff; border-radius: var(--radius-lg); padding: 28px 24px; text-align: center; box-shadow: var(--shadow-lg); }
.tss-hero-kicker { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: 2px; opacity: .85; }
.tss-hero-title { font-size: var(--fs-display); font-weight: var(--fw-black); letter-spacing: -.5px; margin: 6px 0 4px; }
.tss-hero-sub { font-size: var(--fs-body); opacity: .92; }
.tss-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-top: 16px; }
.tss-stat { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 16px; text-align: center; box-shadow: var(--shadow-sm), var(--card-hi); }
.tss-stat-label { font-size: var(--fs-micro); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); }
.tss-stat-val { font-size: 30px; font-weight: var(--fw-black); letter-spacing: -1px; margin: 4px 0 2px; font-variant-numeric: tabular-nums; }
.tss-stat-delta { font-size: var(--fs-meta); font-weight: var(--fw-bold); }
.tss-stat-delta.up { color: var(--ok); }
.tss-stat-delta.down { color: var(--danger); }
.tss-badges { display: flex; flex-wrap: wrap; gap: 8px; }
.tss-badge { display: inline-flex; align-items: center; gap: 8px; background: var(--accent-soft); color: var(--accent); border-radius: 999px; padding: 7px 14px; font-size: var(--fs-caption); font-weight: var(--fw-bold); }
.tss-badge-emoji { font-size: var(--fs-md); }
.tss-ba { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.tss-ba-col { flex: 1; min-width: 130px; text-align: center; font-size: var(--fs-caption); line-height: 1.7; }
.tss-ba-when { font-size: var(--fs-micro); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .5px; color: var(--muted); margin-bottom: 4px; }
.tss-ba-arrow { font-size: var(--fs-h1); color: var(--accent); font-weight: var(--fw-black); }
.tss-funnel > div { padding: 6px 0; border-bottom: 1px solid var(--bg-2); font-size: var(--fs-body); }
.tss-funnel > div:last-child { border-bottom: none; }
.tss-funnel strong { font-size: var(--fs-lg); }
.tss-close { text-align: center; font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--muted); padding: 14px; font-style: italic; }
@media (max-width: 560px) { .tss-hero-title { font-size: var(--fs-h1); } .tss-stat-val { font-size: 24px; } .tss-ba-arrow { transform: rotate(90deg); } .tss-ba-col { min-width: auto; flex-basis: calc(50% - 7px); } }

/* SPEC113 — Slack/IG-style chat day dividers */
.chat-day-divider { display: flex; align-items: center; gap: 12px; margin: 16px 4px 8px; }
.chat-day-divider::before, .chat-day-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.chat-day-divider span { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--muted); background: var(--card); padding: 2px 12px; border: 1px solid var(--border); border-radius: 999px; white-space: nowrap; }

/* SPEC116 — territory geo cluster map */
.geo-map-host { background: var(--subtle); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px; }
/* responsive SVG charts/maps never force horizontal scroll on phones */
.geo-map-host svg { width: 100%; height: auto; max-width: 100%; display: block; }

/* ============================================================
   SPEC119 — premium + responsive pass (research-backed: Linear /
   Stripe / Notion / Vercel / Material-3 / Apple-HIG). Additive +
   appended last so equal-specificity overrides win cleanly.
   ============================================================ */

/* 44px touch-target utility for icon-only / tiny JS controls */
.tap { min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }

/* drag affordance — keep the dragged element legible instead of washed-out opacity
   (the Linear/Notion convention: a dashed accent outline, not a ghost) */
.kanban-card.dragging, .dw-tile.dragging, .sched-unassigned.dragging {
  opacity: .92; outline: 2px dashed var(--accent); outline-offset: 1px;
}

/* keyboard users can reach hover-revealed actions (no keyboard trap) */
.chat-msg-reply:focus-visible, .icon-btn:focus-visible, .th-x:focus-visible, .row-actions :focus-visible { opacity: 1 !important; }

/* smoother grid degradation: 3 → 2 → 1 (was 3 → 1 at 860). */
@media (min-width: 861px) and (max-width: 1140px) { .grid-3, .goal-grid { grid-template-columns: 1fr 1fr; } }

/* KPI value eases down on the smallest screens (auto-fit already wraps the cards) */
@media (max-width: 480px) { .kpi-value { font-size: var(--fs-h1); } }

/* search field never overflows a tiny phone */
@media (max-width: 380px) { .search-input { min-width: 0; width: 100%; } }

/* safe-area insets so content clears the notch / home-indicator (2-arg env fallback
   keeps non-notched browsers unaffected) */
@supports (padding: max(0px)) {
  #view { padding-left: max(30px, env(safe-area-inset-left, 0px)); padding-right: max(30px, env(safe-area-inset-right, 0px)); }
}
@media (max-width: 860px) {
  @supports (padding: max(0px)) {
    #view { padding-left: max(14px, env(safe-area-inset-left, 0px)); padding-right: max(14px, env(safe-area-inset-right, 0px)); }
    #sidebar { padding-top: max(12px, env(safe-area-inset-top, 0px)); }
  }
}

/* dense tables + cards breathe a little less on phones so more fits before scroll */
@media (max-width: 560px) {
  .table th, .table td { padding: 8px 10px; }
  .table { font-size: 12.5px; }
  .card-pad { padding: 14px; }
}

/* SPEC164 — client status portal (preview + future client page). Branded, money-free. */
/* SPEC233 — Client Hub "Presented by [agent]" foregrounds the agent brand at the top of the unified hub */
.ch-presented { font-size: var(--fs-meta); font-weight: var(--fw-bold); letter-spacing: .05em; text-transform: uppercase; color: var(--accent); margin-bottom: 4px; }
/* SPEC-DHCLIENT D3 — the client status render + its in-app preview (App.openClientStatus,
   App.openClientPortal's Progress/Delivery/Unbranded tabs) both build off THIS one root
   class (renderClientStatus + renderDelivery both return a .cstatus card), so a single
   scoped var remap — same idiom .site-page (D4) uses — converges every existing
   var(--text)/var(--muted)/var(--border)/var(--bg-2)/var(--panel) consumer below onto the
   D1 tokens with zero other rule changes. No padding added: the card already sat flush
   (no gutter) against its own max-width before this wave, so recoloring only, no layout
   move — the cinematic cover images keep their existing edge-to-edge bleed untouched. */
.cstatus { max-width: 560px; margin: 0 auto;
  background: var(--cf-ground); color: var(--cf-text); border-radius: 16px;
  --text: var(--cf-text); --text-soft: var(--cf-text-3); --muted: var(--cf-text-3);
  --border: var(--cf-hairline); --border-strong: var(--cf-hairline-hover);
  --bg: var(--cf-ground); --bg-2: var(--cf-card); --panel: var(--cf-card); }
.cstatus-brand { font-weight: var(--fw-black); letter-spacing: .02em; color: var(--accent); font-size: var(--fs-caption); text-transform: uppercase; }
.cstatus-title { margin: 4px 0 2px; font-size: 20px; line-height: var(--lh-tight); }
.cstatus-hi { color: var(--muted); font-size: var(--fs-caption); margin-bottom: 14px; }
.cstatus-prog-label { font-weight: var(--fw-bold); font-size: var(--fs-caption); margin: 8px 0 6px; }
.cstatus-track { height: 10px; border-radius: 999px; background: var(--bg-2); overflow: hidden; }
.cstatus-fill { height: 100%; background: var(--ok); border-radius: 999px; transition: width .35s ease; }
.cstatus-services { margin: 14px 0; display: flex; flex-direction: column; gap: 8px; }
.cstatus-svc { display: flex; align-items: center; gap: 10px; padding: 11px 13px; border: 1px solid var(--border); border-radius: 12px; background: var(--card-bg, var(--bg)); }
.cstatus-svc-main { display: flex; align-items: center; gap: 8px; flex: 1 1 auto; flex-wrap: wrap; min-width: 0; }
.cstatus-svc-name { font-weight: var(--fw-bold); font-size: var(--fs-body); }
.cstatus-chip { color: #fff; font-size: var(--fs-micro); font-weight: var(--fw-bold); padding: 2px 9px; border-radius: 999px; white-space: nowrap; }
.cstatus-link { flex: 0 0 auto; font-weight: var(--fw-bold); font-size: var(--fs-caption); color: var(--accent); text-decoration: none; padding: 6px 10px; border-radius: 8px; background: var(--accent-soft); }
.cstatus-allfiles { display: inline-block; margin-top: 4px; font-weight: var(--fw-bold); font-size: var(--fs-caption); color: var(--accent); text-decoration: none; }
.cstatus-empty { color: var(--muted); font-size: var(--fs-caption); padding: 12px 0; }
.cstatus-rate { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.cstatus-rate-q { font-weight: var(--fw-bold); font-size: 13.5px; margin-bottom: 6px; }
/* SPEC271 (post-C #3) — client-facing Impact proof strip */
.cstatus-impact { margin-top: 22px; padding: 18px 18px 6px; border-radius: 14px; background: color-mix(in srgb, var(--cover-accent, #2b608f) 8%, var(--card, #fff)); border: 1px solid color-mix(in srgb, var(--cover-accent, #2b608f) 20%, transparent); }
.cstatus-impact-head { text-align: center; margin-bottom: 4px; }
.cstatus-impact-title { font-family: var(--serif, Georgia, serif); font-size: 19px; font-weight: var(--fw-semibold); letter-spacing: -.01em; }
.cstatus-impact-since { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.cstatus-impact-stats { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px 26px; padding: 12px 0 14px; }
.cstatus-impact-stat { text-align: center; min-width: 64px; }
.cstatus-impact-n { font-size: 26px; font-weight: var(--fw-black); line-height: 1; letter-spacing: var(--ls-tight); color: var(--cover-accent, #2b608f); }
.cstatus-impact-l { font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-top: 4px; }
.cstatus-stars { display: flex; gap: 4px; }
.cstatus-star { background: none; border: 0; cursor: pointer; font-size: 26px; line-height: 1; color: var(--warn); padding: 9px; min-width: 44px; min-height: 44px; }   /* SPEC280 wave 1 — real touch targets */
/* SPEC280 wave 1 — the invoice as a quiet text link below the gallery, never a hero CTA */
.cstatus-paylink { text-align: center; padding: 14px 0 4px; }
.cstatus-paylink a { font-size: 13.5px; font-weight: var(--fw-semibold); color: var(--muted); text-decoration: underline; text-underline-offset: 3px; }
.cstatus-paylink a:hover { color: var(--text); }
.cstatus-req { margin-top: 16px; }

/* SPEC165 — branded delivery page extras */
.cstatus-facts { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 4px; }
.cstatus-fact { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--text-soft); background: var(--bg-2); border-radius: 999px; padding: 3px 10px; }
.cstatus-dlall { display: inline-block; margin-top: 12px; }

/* SPEC206 — "what happens next" journey stepper (client status page) */
.cstatus-steps { display: flex; align-items: flex-start; gap: 2px; margin: 4px 0 18px; }
.cstatus-step { flex: 1 1 0; min-width: 0; position: relative; display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px; }
/* connector line between dots (drawn to the left of each step except the first) */
.cstatus-step::before { content: ''; position: absolute; top: 14px; right: 50%; width: 100%; height: 2px; background: var(--border); z-index: 0; }
.cstatus-step:first-child::before { display: none; }
.cstatus-step--done::before, .cstatus-step--now::before { background: color-mix(in srgb, var(--cover-accent, var(--accent)) 60%, var(--border)); }
.cstatus-step-dot { position: relative; z-index: 1; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-caption); line-height: 1; background: var(--bg-2); border: 2px solid var(--border); color: var(--text-soft); transition: transform .25s var(--ease-spring); }
.cstatus-step--done .cstatus-step-dot { background: var(--cover-accent, var(--accent)); border-color: transparent; color: #15181c; font-weight: var(--fw-black); }
.cstatus-step--now .cstatus-step-dot { background: color-mix(in srgb, var(--cover-accent, var(--accent)) 18%, var(--panel)); border-color: var(--cover-accent, var(--accent)); color: var(--text); transform: scale(1.12);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--cover-accent, var(--accent)) 18%, transparent); }
.cstatus-step-label { font-size: 10.5px; font-weight: var(--fw-bold); color: var(--text-soft); line-height: var(--lh-tight); }
.cstatus-step--now .cstatus-step-label { color: var(--text); }

/* SPEC206 — location block (delivery page) */
.cstatus-loc { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; margin: 12px 0 4px; padding: 11px 13px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-2); }
.cstatus-loc-ic { font-size: var(--fs-md); line-height: 1; }
.cstatus-loc-addr { flex: 1 1 auto; min-width: 120px; font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--text); }
.cstatus-loc-link { font-size: 12.5px; font-weight: var(--fw-black); color: var(--cover-accent, var(--accent)); white-space: nowrap; }

/* SPEC206 — referral ask (delivery happy-path) */
.cstatus-refer { margin-top: 6px; }

/* SPEC166 — delivery gallery grid + lightbox (the "see everything at a glance" view) */
.cstatus-gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 6px; margin: 12px 0 4px; }
.cstatus-thumb { position: relative; padding: 0; border: 0; border-radius: 10px; overflow: hidden; cursor: pointer; aspect-ratio: 1 / 1; background: var(--bg-2); display: block; }
.cstatus-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .25s ease; }
.cstatus-thumb:hover img { transform: scale(1.06); }
.cstatus-thumb-video { display: flex; align-items: center; justify-content: center; text-decoration: none; }
.cstatus-play { width: 34px; height: 34px; border-radius: 999px; background: rgba(0,0,0,.55); color: #fff; display: flex; align-items: center; justify-content: center; font-size: var(--fs-body); }
.cstatus-lightbox { position: fixed; inset: 0; z-index: 4000; background: rgba(8,10,16,.92); display: flex; align-items: center; justify-content: center; }
.cstatus-lightbox img { max-width: 92vw; max-height: 86vh; border-radius: 8px; box-shadow: 0 20px 60px rgba(0,0,0,.5); }
/* SPEC268 (Bundle C item 6) — before/after "Reveal" slider */
.cstatus-lb-reveal { position: relative; width: min(92vw, 1400px); aspect-ratio: 3 / 2; max-height: 86vh; border-radius: 8px; overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,.5); user-select: none; touch-action: pan-y; }
.cstatus-lb-rev-before, .cstatus-lb-rev-after { position: absolute; inset: 0; background-size: cover; background-position: center; }
.cstatus-lb-rev-after { will-change: clip-path; }
.cstatus-lb-rev-range { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; opacity: 0; cursor: ew-resize; -webkit-appearance: none; appearance: none; background: transparent; }
.cstatus-lb-rev-range::-webkit-slider-thumb { -webkit-appearance: none; width: 46px; height: 100%; cursor: ew-resize; }
.cstatus-lb-rev-range::-moz-range-thumb { width: 46px; height: 100%; border: 0; background: transparent; cursor: ew-resize; }
.cstatus-lb-rev-handle { position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px; background: rgba(255,255,255,.9); box-shadow: 0 0 0 1px rgba(0,0,0,.28); pointer-events: none; }
.cstatus-lb-rev-knob { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 40px; height: 40px; border-radius: 999px; background: rgba(255,255,255,.94); color: #14181f; font-size: var(--fs-caption); letter-spacing: -.03em; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 12px rgba(0,0,0,.45); }
.cstatus-lb-rev-tag { position: absolute; top: 12px; padding: 4px 10px; border-radius: 999px; font-size: var(--fs-micro); letter-spacing: .07em; text-transform: uppercase; background: rgba(10,12,18,.58); color: #fff; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); pointer-events: none; }
.cstatus-lb-rev-tag--after { left: 12px; }
.cstatus-lb-rev-tag--before { right: 12px; }
.cstatus-lb-rev-label { position: absolute; left: 12px; bottom: 12px; padding: 5px 11px; border-radius: 6px; font-size: var(--fs-meta); font-weight: var(--fw-semibold); background: rgba(10,12,18,.66); color: #fff; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); pointer-events: none; }
.cstatus-lightbox.chrome-hidden .cstatus-lb-rev-tag { opacity: 0; transition: opacity .3s var(--ease); }
.cstatus-lb-x { position: absolute; top: 16px; right: 18px; background: rgba(255,255,255,.12); color: #fff; border: 0; width: 40px; height: 40px; border-radius: 999px; font-size: var(--fs-lg); cursor: pointer; }
.cstatus-lb-nav { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,.12); color: #fff; border: 0; width: 46px; height: 46px; border-radius: 999px; font-size: 26px; line-height: 1; cursor: pointer; }
.cstatus-lb-prev { left: 14px; } .cstatus-lb-next { right: 14px; }
.cstatus-lb-cap { position: absolute; bottom: 16px; left: 0; right: 0; text-align: center; color: rgba(255,255,255,.85); font-size: var(--fs-caption); font-weight: var(--fw-bold); }

/* ============================================================
   SPEC167 — Marketing Studio: template cards (.tpl-*) + studio UI (.mkt-*).
   Cards use container units (cqw = 1% of card width) so type scales at any size.
   ============================================================ */
.tpl-card { container-type: inline-size; position: relative; overflow: hidden; border-radius: 14px;
  font-family: var(--tpl-font, sans-serif); color: #fff; background: var(--tpl-primary, #0e7c5a);
  display: flex; flex-direction: column; box-shadow: 0 6px 22px rgba(0,0,0,.18); }
.tpl-square { aspect-ratio: 1 / 1; }
.tpl-story  { aspect-ratio: 9 / 16; }
.tpl-hero { flex: 1 1 auto; min-height: 0; background-size: cover; background-position: center; background-color: #1b2433; }
.tpl-hero--empty { background-image: linear-gradient(150deg, var(--tpl-primary), rgba(0,0,0,.5)); }
.tpl-band { background: var(--tpl-primary); padding: 5.5cqw 6cqw 6cqw; display: flex; flex-direction: column; gap: 1.6cqw; flex: 0 0 auto; }
.tpl-badge { align-self: flex-start; background: var(--tpl-accent); color: #15181c; font-weight: var(--fw-black);
  letter-spacing: .14em; font-size: 3cqw; padding: 1.2cqw 2.8cqw; border-radius: 999px; text-transform: uppercase; }
.tpl-badge--accent { background: var(--tpl-accent); color: #15181c; }
.tpl-headline { font-size: 7cqw; font-weight: var(--fw-black); line-height: 1.04; }
.tpl-headline--center { text-align: center; font-size: 6.4cqw; }
.tpl-tagline { font-size: 3.1cqw; opacity: .85; }
.tpl-tagline--center { text-align: center; }
.tpl-when { font-size: 4.6cqw; font-weight: var(--fw-black); color: var(--tpl-accent); }
.tpl-price { font-size: 6cqw; font-weight: var(--fw-black); color: var(--tpl-accent); }
.tpl-price--center { text-align: center; }
.tpl-facts { font-size: 3.1cqw; font-weight: var(--fw-bold); letter-spacing: .03em; opacity: .92; }
.tpl-agent { display: flex; align-items: center; gap: 2.2cqw; margin-top: 1.6cqw; padding-top: 2.4cqw; border-top: 1px solid rgba(255,255,255,.25); }
.tpl-agent-pic { width: 9cqw; height: 9cqw; border-radius: 999px; background-size: cover; background-position: center; flex: 0 0 auto; }
.tpl-agent-text { display: flex; flex-direction: column; min-width: 0; }
.tpl-agent-name { font-size: 3.4cqw; font-weight: var(--fw-black); }
.tpl-agent-sub { font-size: 2.6cqw; opacity: .8; }
/* Just Sold — centered over a scrimmed hero */
.tpl-justsold .tpl-hero { position: absolute; inset: 0; }
.tpl-scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,.34), rgba(0,0,0,.66)); }
.tpl-center { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 9cqw; gap: 2cqw; }
.tpl-foot { position: absolute; left: 0; right: 0; bottom: 0; padding: 5cqw 6cqw; background: linear-gradient(0deg, rgba(0,0,0,.6), transparent); }
.tpl-foot .tpl-agent { border-top: 0; padding-top: 0; margin-top: 0; justify-content: center; }
/* Agent Brand — text-only, gradient bg (PNG-exportable) */
.tpl-ab-bg { position: absolute; inset: 0; background: linear-gradient(155deg, var(--tpl-primary), rgba(0,0,0,.6)); }
.tpl-ab-inner { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 9cqw; gap: 2.2cqw; }
.tpl-ab-pic { width: 26cqw; height: 26cqw; border-radius: 999px; background-size: cover; background-position: center; border: .8cqw solid var(--tpl-accent); }
.tpl-ab-name { font-size: 8cqw; font-weight: var(--fw-black); line-height: 1.05; }
.tpl-ab-title { font-size: 3.4cqw; opacity: .85; letter-spacing: .05em; }
.tpl-ab-quote { font-size: 4.2cqw; font-style: italic; color: var(--tpl-accent); max-width: 84%; }
.tpl-ab-cta { font-size: 3.6cqw; font-weight: var(--fw-black); background: var(--tpl-accent); color: #15181c; padding: 1.6cqw 4cqw; border-radius: 999px; }
.tpl-ab-contact { font-size: 2.8cqw; opacity: .82; }

/* studio + editor */
.mkt-templates { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 14px; }
.mkt-tile { padding: 0; border: 1px solid var(--border); border-radius: 14px; overflow: hidden; cursor: pointer; background: var(--card-bg, var(--bg)); display: flex; flex-direction: column; transition: transform .18s ease, box-shadow .18s ease; }
.mkt-tile:hover { transform: translateY(-2px); box-shadow: 0 10px 26px rgba(0,0,0,.14); }
.mkt-mini { padding: 12px; background: var(--bg-2); }
.mkt-mini .tpl-card { width: 100%; }
.mkt-tile-name { padding: 10px 12px; font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.mkt-editor { display: flex; gap: 18px; flex-wrap: wrap; align-items: flex-start; }
.mkt-stage { flex: 0 0 auto; }
.mkt-stage .tpl-card { width: min(420px, 62vw); }
.mkt-ctrl { flex: 1 1 240px; min-width: 220px; display: flex; flex-direction: column; gap: 11px; }
.mkt-field { display: flex; flex-direction: column; gap: 4px; }
.mkt-field--row { flex-direction: row; align-items: center; gap: 8px; }
.mkt-flabel { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--text-soft); text-transform: uppercase; letter-spacing: var(--ls-label); }
.mkt-full { position: fixed; inset: 0; z-index: 4000; background: rgba(8,10,16,.94); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 20px; }
.mkt-full .tpl-card.tpl-square { width: min(86vmin, 560px); }
.mkt-full .tpl-card.tpl-story { height: min(84vh, 820px); width: auto; }
.mkt-full-bar { display: flex; gap: 10px; align-items: center; color: #fff; flex-wrap: wrap; justify-content: center; }
.mkt-full-hint { font-size: var(--fs-caption); opacity: .85; }
@media (max-width: 560px) { .mkt-stage .tpl-card { width: min(420px, 86vw); } }

/* SPEC168 — world-class (Pixieset-style) delivery gallery */
.cstatus--delivery { max-width: 860px; }
.cstatus-cover { position: relative; width: 100%; aspect-ratio: 16 / 9; max-height: 380px; border-radius: 16px; overflow: hidden;
  background-size: cover; background-position: center; background-color: var(--cf-card); display: flex; align-items: flex-end; }
.cstatus-cover-scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,.12) 30%, rgba(0,0,0,.72)); }
.cstatus-cover-text { position: relative; padding: 22px 24px; color: #fff; }
.cstatus-cover-brand { font-weight: var(--fw-black); letter-spacing: .12em; text-transform: uppercase; font-size: var(--fs-meta); opacity: .9; }
/* SPEC268 — agent-forward cover credit (agent leads, studio demoted) */
.cstatus-cover-presented { display: flex; flex-direction: column; gap: 1px; text-transform: none; }
.cstatus-cover-eyebrow { font-size: 10.5px; letter-spacing: .18em; text-transform: uppercase; opacity: .82; }
.cstatus-cover-agent { font-size: 15px; letter-spacing: .03em; font-weight: var(--fw-bold); }
.cstatus-cover-mediaby { font-size: 10px; letter-spacing: .1em; opacity: .6; margin-top: 2px; }
.cstatus-cover-title { font-size: clamp(22px, 4vw, 34px); font-weight: var(--fw-black); line-height: 1.1; margin-top: 2px; text-shadow: 0 2px 16px rgba(0,0,0,.4); }
.cstatus-cover-sub { font-size: var(--fs-body); opacity: .92; margin-top: 4px; }
.cstatus-gal { display: flex; flex-wrap: wrap; gap: 5px; margin: 14px 0 6px; }
.cstatus-gal::after { content: ''; flex-grow: 999; }  /* keep the last row from stretching wide */
.cstatus-gal-item { position: relative; height: 250px; flex-grow: 1; flex-basis: 280px; padding: 0; border: 0; border-radius: 6px; overflow: hidden; cursor: pointer; background: color-mix(in srgb, var(--gal-acc, #2b608f) 13%, var(--cf-card)); }
.cstatus-gal-item img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .5s cubic-bezier(.2,.7,.2,1); }
.cstatus-gal-item:hover img { transform: scale(1.045); }
.cstatus-gal-video { display: flex; align-items: center; justify-content: center; text-decoration: none; }
@media (max-width: 620px) { .cstatus-gal-item { height: 180px; } .cstatus-cover { max-height: 260px; } }
/* premium lightbox additions */
.cstatus-lightbox img { opacity: 0; transition: opacity .25s ease; }
.cstatus-lightbox img.in { opacity: 1; }
.cstatus-lb-dl { position: absolute; top: 16px; right: 70px; background: rgba(255,255,255,.12); color: #fff; width: 40px; height: 40px; border-radius: 999px; display: flex; align-items: center; justify-content: center; text-decoration: none; font-size: var(--fs-md); }
.cstatus-lb-dl:hover, .cstatus-lb-x:hover, .cstatus-lb-nav:hover { background: rgba(255,255,255,.22); }

/* SPEC169 — delivery downloads (top bar + per-photo) */
.cstatus-dlbar { margin: 12px 0 4px; }
.cstatus-gal-open { position: absolute; inset: 0; padding: 0; border: 0; background: none; cursor: pointer; display: block; }
.cstatus-gal-dl { position: absolute; top: 7px; right: 7px; z-index: 2; width: 30px; height: 30px; border-radius: 999px; background: rgba(0,0,0,.55); color: #fff; display: flex; align-items: center; justify-content: center; text-decoration: none; font-size: var(--fs-caption); opacity: 0; transition: opacity .18s ease; }
.cstatus-gal-item:hover .cstatus-gal-dl, .cstatus-gal-dl:focus { opacity: 1; }
.cstatus-gal-dl:hover { background: rgba(0,0,0,.8); }
/* SPEC251 — one-tap Share, just left of the download */
.cstatus-gal-share { position: absolute; top: 7px; right: 43px; z-index: 2; width: 30px; height: 30px; border-radius: 999px; border: 0; cursor: pointer; background: rgba(0,0,0,.55); color: #fff; display: flex; align-items: center; justify-content: center; font-size: var(--fs-body); line-height: 1; opacity: 0; transition: opacity .18s ease; }
.cstatus-gal-item:hover .cstatus-gal-share, .cstatus-gal-share:focus { opacity: 1; }
.cstatus-gal-share:hover { background: rgba(0,0,0,.8); }
@media (hover: none) { .cstatus-gal-share, .cstatus-gal-dl { opacity: 1; } }
.cstatus-lb-mls { right: auto; left: 70px; width: auto; padding: 0 12px; font-size: var(--fs-meta); font-weight: var(--fw-black); letter-spacing: .06em; }
/* SPEC267 — plain-language download-size hint in the lightbox */
.cstatus-lb-sizehint { position: absolute; bottom: 52px; left: 50%; transform: translateX(-50%); color: rgba(255,255,255,.9); font-size: 11.5px; letter-spacing: .02em; background: rgba(0,0,0,.4); padding: 4px 13px; border-radius: 999px; pointer-events: none; white-space: nowrap; max-width: 92vw; overflow: hidden; text-overflow: ellipsis; }

/* SPEC-DHDELIVER — the redesigned client Delivery page (delivery.js, App.openClientDelivery).
   Dormant/preview-only this wave. Same scoped var-remap idiom as .cstatus just above (D1
   tokens redeclared onto --text/--muted/--border/--bg/--panel so every generic .btn/.input
   inside inherits the dark-cinematic palette for free) — a SEPARATE class namespace on
   purpose (not a .cstatus reskin): this page's copy/lock rules genuinely differ from
   clientstatus's (see delivery.js's own DTO comment), so sharing one root class would risk
   the two surfaces silently drifting onto each other's CSS. Plus Jakarta throughout; ≥44px
   targets; mobile-stacked under 640px per the design brief. */
.cfd { max-width: 640px; margin: 0 auto; font-family: var(--cf-font); padding: 26px;
  background: var(--cf-ground); color: var(--cf-text); border-radius: 16px;
  --text: var(--cf-text); --text-soft: var(--cf-text-3); --muted: var(--cf-text-3);
  --border: var(--cf-hairline); --border-strong: var(--cf-hairline-hover);
  --bg: var(--cf-ground); --bg-2: var(--cf-card); --panel: var(--cf-card); }
.cfd-unpaid { display: flex; align-items: center; gap: 10px; padding: 10px 14px; margin-bottom: 18px;
  background: rgba(230,210,177,.12); border: 1px solid rgba(230,210,177,.22); border-radius: 10px; color: var(--cf-champagne); }
.cfd-unpaid-txt { font-size: 12.5px; font-weight: var(--fw-bold); flex: 1; }
.cfd-unpaid-link { font-size: 12.5px; font-weight: var(--fw-bold); color: var(--cf-champagne); border: 1px solid rgba(230,210,177,.4); border-radius: 8px; padding: 6px 12px; min-height: 30px; display: inline-flex; align-items: center; }
.cfd-unpaid-link:hover { background: rgba(230,210,177,.14); text-decoration: none; }
.cfd-eyebrow { font-size: 11px; font-weight: var(--fw-black); letter-spacing: .26em; text-transform: uppercase; color: var(--cf-champagne); }
.cfd-headline { font-family: var(--cf-font); font-weight: 800; font-size: clamp(26px, 5vw, 40px); line-height: 1.08; letter-spacing: -.01em; margin: 10px 0 0; }
.cfd-message { margin: 12px 0 0; font-size: 15px; line-height: 1.6; color: var(--cf-text-2); max-width: 520px; }
.cfd-coming { margin-top: 8px; font-size: 13px; font-weight: var(--fw-bold); color: var(--cf-text-2); }
.cfd-film { margin-top: 18px; border-radius: 12px; overflow: hidden; background: var(--cf-card); }
.cfd-film-video { display: block; width: 100%; max-height: 400px; background: #000; }
.cfd-film--poster { display: flex; align-items: center; justify-content: center; gap: 10px; flex-direction: column; min-height: 200px; color: var(--cf-text); text-decoration: none; border: 1px solid var(--cf-hairline); }
.cfd-film--poster:hover { border-color: var(--cf-hairline-hover); text-decoration: none; }
.cfd-film--locked { color: var(--cf-text-3); cursor: default; }
.cfd-film-label { font-size: 13px; font-weight: var(--fw-bold); }
.cfd-actions { display: flex; gap: 10px; margin-top: 18px; flex-wrap: wrap; }
.cfd-menu-wrap { position: relative; }
.cfd-btn { display: inline-flex; align-items: center; gap: 7px; min-height: 44px; padding: 0 16px; border-radius: 999px; border: 1px solid var(--cf-hairline); background: rgba(255,255,255,.04); color: var(--cf-text); font-family: var(--cf-font); font-size: 13.5px; font-weight: var(--fw-bold); cursor: pointer; }
.cfd-btn:hover { border-color: var(--cf-hairline-hover); background: rgba(255,255,255,.08); }
.cfd-btn--primary { background: var(--cf-champagne); color: #2e2718; border-color: var(--cf-champagne); }
.cfd-btn--primary:hover { background: #d9c39d; }
.cfd-menu-panel { position: absolute; top: calc(100% + 6px); left: 0; z-index: 5; min-width: 220px; background: var(--cf-card); border: 1px solid var(--cf-hairline); border-radius: 10px; padding: 6px; box-shadow: var(--shadow-dark-modal); }
.cfd-menu-row { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 40px; padding: 0 10px; border: 0; background: none; color: var(--cf-text); font-family: var(--cf-font); font-size: 13px; text-align: left; border-radius: 7px; cursor: pointer; text-decoration: none; }
.cfd-menu-row:hover { background: rgba(255,255,255,.06); text-decoration: none; }
.cfd-menu-row--locked { color: var(--cf-text-3); cursor: default; }
.cfd-menu-empty { padding: 10px; font-size: 12px; color: var(--cf-text-3); }
.cfd-menu-champagne { padding: 8px 10px 2px; font-size: 11.5px; font-weight: var(--fw-bold); color: var(--cf-champagne); }
.cfd-rc { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--cf-hairline); }
.cfd-rc-opener { width: 100%; justify-content: center; }
.cfd-rc-panel { margin-top: 12px; padding: 14px; border: 1px solid var(--cf-hairline); border-radius: 11px; background: rgba(255,255,255,.02); }
/* SPEC-V2DELIVERWOW D3 — the pinned-timecode chip: champagne, same family as
   chat.js's own .chat-note-tc chip so a client sees one visual language for
   "this is about a specific moment" whether they're reading the thread or
   pinning the note. */
.cfd-rc-chip { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.cfd-rc-chip-tc { font-size: 12px; font-weight: var(--fw-black); color: #2e2718; background: var(--cf-champagne); padding: 3px 10px; border-radius: 999px; }
.cfd-rc-chip-note { font-size: 12px; color: var(--cf-text-3); }
.cfd-rc-name { margin-bottom: 6px; }
.cfd-rc-row { display: flex; gap: 8px; margin-top: 10px; }
.cfd-signoff { margin-top: 12px; }
.cfd-signoff-done { display: flex; align-items: center; gap: 8px; color: var(--cf-green); font-size: 13.5px; font-weight: var(--fw-bold); }
.cfd-section-h { font-size: 12px; font-weight: var(--fw-black); letter-spacing: .08em; text-transform: uppercase; color: var(--cf-text-3); margin: 22px 0 10px; }
.cfd-cuts { display: flex; flex-direction: column; gap: 6px; }
.cfd-cut { display: flex; align-items: center; gap: 8px; min-height: 36px; padding: 6px 10px; border-radius: 8px; background: rgba(255,255,255,.03); }
.cfd-cut--pending { flex-direction: column; align-items: stretch; gap: 6px; padding: 8px 10px; }
.cfd-cut-row { display: flex; align-items: center; gap: 8px; }
.cfd-cut-name { flex: 1; font-size: 13.5px; }
.cfd-cut-pill { font-size: 11px; font-weight: var(--fw-black); color: var(--cf-green); background: var(--cf-green-bg); padding: 2px 9px; border-radius: 999px; }
.cfd-cut-stage { font-size: 12px; color: var(--cf-text-3); }
.cfd-cut-eta { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--cf-champagne); }
/* D4 — the thin champagne progress bar on a pending cut (prototype value:
   a 3px hairline track, position-derived fill — never a fabricated number). */
.cfd-cut-bar { height: 3px; border-radius: 999px; background: rgba(255,255,255,.08); overflow: hidden; }
.cfd-cut-bar-fill { height: 100%; background: var(--cf-champagne); border-radius: 999px; }
/* ============================================================
   SPEC-V2LIVEWIRE 2026-08-28 (D2) — THE WORKSPACE. Vincent's ruling: "client
   review = a workspace — one active video, the job's whole folder in a
   switcher rail, one click to switch." Desktop: main pane + a right-hand
   rail, side by side (Frame.io's own "compact rail, frames lead" mental
   model). Mobile: the rail drops BELOW the main pane as a horizontally-
   scrolling strip — pure CSS (flex-wrap: nowrap + overflow-x), never a JS
   viewport read, same trap-free idiom as .cfd-highlights above. Replaces the
   old plain .cfd-cuts/.cfd-cut list (still defined above — untouched, just
   unused now that nothing renders it — see delivery.js's own removal note). */
.cfd-workspace { display: flex; gap: 14px; margin-top: 18px; align-items: flex-start; }
.cfd-main { flex: 1 1 auto; min-width: 0; }
.cfd-main .cfd-film { margin-top: 0; }
.cfd-rail { flex: 0 0 220px; display: flex; flex-direction: column; gap: 6px; }
.cfd-rail-h { font-size: 11px; font-weight: var(--fw-black); letter-spacing: .1em; text-transform: uppercase; color: var(--cf-text-3); margin-bottom: 2px; }
.cfd-rail-item { display: flex; align-items: center; gap: 9px; width: 100%; min-height: 56px; padding: 6px 8px; border-radius: 10px; border: 1px solid transparent; background: rgba(255,255,255,.03); color: var(--cf-text); font-family: var(--cf-font); text-align: left; cursor: pointer; box-sizing: border-box; }
.cfd-rail-item:hover { border-color: var(--cf-hairline-hover); background: rgba(255,255,255,.06); }
.cfd-rail-item--active { border-color: var(--cf-champagne); background: rgba(230,210,177,.1); }
.cfd-rail-item--pending { opacity: .72; }
.cfd-rail-thumb { position: relative; flex: 0 0 56px; width: 56px; height: 42px; border-radius: 7px; background-size: cover; background-position: center; background-color: var(--cf-card); display: flex; align-items: center; justify-content: center; color: var(--cf-text-3); }
.cfd-rail-clock { position: absolute; bottom: 2px; right: 2px; background: rgba(0,0,0,.55); border-radius: 999px; padding: 2px; display: flex; }
.cfd-rail-meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.cfd-rail-name { font-size: 12.5px; font-weight: var(--fw-bold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cfd-rail-stage { font-size: 11px; color: var(--cf-text-3); }
.cfd-rail-stage--ready { color: var(--cf-green); font-weight: var(--fw-bold); }
/* D4's champagne progress bar, now on the rail row it replaced */
.cfd-rail-bar { height: 3px; border-radius: 999px; background: rgba(255,255,255,.08); overflow: hidden; }
.cfd-rail-bar-fill { height: 100%; background: var(--cf-champagne); border-radius: 999px; }
.cfd-film--empty { display: flex; align-items: center; justify-content: center; min-height: 200px; border: 1px dashed var(--cf-hairline); border-radius: 12px; color: var(--cf-text-3); text-align: center; padding: 16px; }
.cfd-film--photos, .cfd-film--tour { cursor: pointer; }
/* ---- the disabled public-page composer (SPEC-V2LIVEWIRE D1 — no safe
   unauthenticated write path exists yet; honest copy, never a fake button) ---- */
.cfd-rc-disabled { display: flex; align-items: flex-start; gap: 9px; padding: 12px 14px; border: 1px solid var(--cf-hairline); border-radius: 11px; background: rgba(255,255,255,.02); color: var(--cf-text-2); font-size: 13px; line-height: 1.55; }
.cfd-card--static { cursor: default; }
.cfd-card--static:hover { border-color: var(--cf-hairline); }
/* ---- toast + not-found (public pages only — no admin App.toast/modal here) ---- */
.cfd-toast { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 300; padding: 10px 18px; border-radius: 999px; background: var(--cf-card); border: 1px solid var(--cf-hairline); color: var(--cf-text); font-family: var(--cf-font); font-size: 13px; font-weight: var(--fw-bold); box-shadow: var(--shadow-dark-modal); }
.cfd-toast--error { border-color: rgba(239,68,68,.5); color: #ff9d9d; }
.cfd--notfound { display: flex; align-items: center; justify-content: center; min-height: 60vh; text-align: center; }
@media (max-width: 720px) {
  .cfd-workspace { flex-direction: column; }
  .cfd-rail { flex: none; width: 100%; flex-direction: row; overflow-x: auto; -webkit-overflow-scrolling: touch; gap: 8px; padding-bottom: 2px; }
  .cfd-rail-h { display: none; }
  /* trap 6's spirit, applied to a rail row instead of a table column: a
     fixed, generous minimum keeps every row a real ≥44px touch target even
     though flex would otherwise let it shrink to fit the scroll strip. */
  .cfd-rail-item { flex: 0 0 168px; min-height: 56px; }
}
.cfd-section { margin-top: 4px; }
.cfd-card { display: flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 12px; margin-bottom: 6px; border: 1px solid var(--cf-hairline); border-radius: 10px; background: rgba(255,255,255,.02); color: var(--cf-text); font-family: var(--cf-font); font-size: 13.5px; text-decoration: none; cursor: pointer; width: 100%; box-sizing: border-box; text-align: left; }
.cfd-card:hover { border-color: var(--cf-hairline-hover); text-decoration: none; }
.cfd-card--locked { color: var(--cf-text-3); cursor: default; }
/* ---- SPEC-V2DELIVERWOW D1 — photography highlights + full-screen gallery ---- */
.cfd-highlights { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-bottom: 10px; }
.cfd-highlight { aspect-ratio: 1; border-radius: 8px; background-size: cover; background-position: center; background-color: var(--cf-card); }
.cfd-see-all { width: 100%; justify-content: center; }
.cfd-gallery { position: fixed; inset: 0; z-index: 200; background: var(--cf-ground); color: var(--cf-text); display: flex; flex-direction: column; font-family: var(--cf-font); }
.cfd-gal-head { display: flex; align-items: center; gap: 14px; padding: 14px 18px; border-bottom: 1px solid var(--cf-hairline); flex: none; }
.cfd-gal-back { display: inline-flex; align-items: center; gap: 6px; min-height: 38px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--cf-hairline); background: rgba(255,255,255,.04); color: var(--cf-text); font-family: var(--cf-font); font-size: 13px; font-weight: var(--fw-bold); cursor: pointer; }
.cfd-gal-back:hover { border-color: var(--cf-hairline-hover); }
.cfd-gal-addr { font-size: 14px; font-weight: var(--fw-bold); }
.cfd-gal-count { margin-left: auto; font-size: 12.5px; color: var(--cf-text-3); }
/* SPEC-GALDENSITY 2026-09-08 — columns come from a CSS var so the client's own
   "photos per row" choice (an INLINE style the gallery sets) beats both the desktop
   default below and the phone default in the media query, which is what choosing a
   density has to mean. Fallback keeps the pre-var rendering if the var is ever unset. */
.cfd-gal-grid { flex: 1; overflow-y: auto; display: grid; grid-template-columns: repeat(var(--cfd-gal-cols, 4), 1fr); gap: 3px; padding: 3px; }
/* PHASE D (PLAN-SNAPPER-2026-09-09) — a room-group heading is a DIRECT CHILD of
   .cfd-gal-grid (its own CSS grid), so it must span every column or it just
   eats one grid cell like a thumb would. */
.cfd-gal-group-h { grid-column: 1 / -1; padding: 10px 4px 4px; font-size: 12px; font-weight: var(--fw-bold); color: var(--cf-text-3); text-transform: uppercase; letter-spacing: .06em; }
.cfd-gal-group-h:first-child { padding-top: 4px; }
.cfd-gal-dens { margin-left: 10px; flex: none; font-size: 12.5px; padding: 4px 8px; border-radius: 6px; border: 1px solid var(--cf-hairline); background: var(--cf-card); color: var(--cf-text-2); cursor: pointer; }
/* SPEC-GALSELECT 2026-09-08 — "Select" mode: pick a few photos, download just those.
   A selected thumb gets an INSET champagne ring (outline, so the grid never reflows) and a
   check badge; the bar rides the overlay's flex column right under the grid. */
.cfd-gal-tool { display: inline-flex; align-items: center; gap: 6px; margin-left: 10px; flex: none; min-height: 34px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--cf-hairline); background: rgba(255,255,255,.04); color: var(--cf-text); font-family: var(--cf-font); font-size: 12.5px; font-weight: var(--fw-bold); cursor: pointer; }
.cfd-gal-tool:hover { border-color: var(--cf-hairline-hover); }
.cfd-gal-tool[aria-pressed="true"] { background: var(--cf-champagne); color: var(--cf-ground); border-color: transparent; }
.cfd-gal-thumb { position: relative; }
.cfd-gal-grid--select .cfd-gal-thumb:hover { outline: 2px solid var(--cf-hairline-hover); outline-offset: -2px; }
.cfd-gal-thumb--selected, .cfd-gal-grid--select .cfd-gal-thumb--selected:hover { outline: 3px solid var(--cf-champagne); outline-offset: -3px; }
.cfd-gal-thumb--selected::after { content: '✓'; position: absolute; top: 6px; right: 6px; width: 22px; height: 22px; border-radius: 50%; background: var(--cf-champagne); color: var(--cf-ground); font-size: 13px; font-weight: 700; line-height: 22px; text-align: center; }
.cfd-gal-selbar { flex: none; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 12px 18px; border-top: 1px solid var(--cf-hairline); background: var(--cf-card); }
.cfd-gal-selbar-count { font-size: 13.5px; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; margin-right: auto; }
.cfd-gal-selbar-note { flex-basis: 100%; font-size: 12px; color: var(--cf-text-3); }
.cfd-gal-selbar-note a { color: var(--cf-text-2); }
.cfd-gal-thumb { aspect-ratio: 1; border: 0; padding: 0; cursor: pointer; background-size: cover; background-position: center; background-color: var(--cf-card); }
.cfd-gal-lightbox { position: absolute; inset: 0; background: var(--cf-ground); display: flex; flex-direction: column; }
.cfd-gal-lb-head { display: flex; align-items: center; gap: 14px; padding: 14px 18px; flex: none; }
.cfd-gal-counter { margin-left: auto; font-size: 13px; color: var(--cf-text-3); font-variant-numeric: tabular-nums; }
.cfd-gal-lb-body { position: relative; flex: 1; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.cfd-gal-stage { width: 100%; height: 100%; background-size: contain; background-position: center; background-repeat: no-repeat; }
.cfd-gal-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--cf-hairline); background: rgba(0,0,0,.4); color: var(--cf-text); display: flex; align-items: center; justify-content: center; cursor: pointer; }
.cfd-gal-nav:hover { background: rgba(0,0,0,.6); }
.cfd-gal-nav--prev { left: 14px; }
.cfd-gal-nav--next { right: 14px; }
.cfd-gal-acts { display: flex; gap: 10px; flex-wrap: wrap; padding: 14px 18px; border-top: 1px solid var(--cf-hairline); flex: none; }
.cfd-gal-act { display: inline-flex; align-items: center; gap: 7px; min-height: 44px; padding: 0 16px; border-radius: 999px; border: 1px solid var(--cf-hairline); background: rgba(255,255,255,.04); color: var(--cf-text); font-family: var(--cf-font); font-size: 13px; font-weight: var(--fw-bold); cursor: pointer; text-decoration: none; }
.cfd-gal-act:hover { border-color: var(--cf-hairline-hover); text-decoration: none; }
.cfd-gal-act--locked { color: var(--cf-champagne); cursor: default; }
/* ---- Share menu — embed code (render-and-copy) ---- */
.cfd-menu-embed { padding: 8px 10px; }
.cfd-menu-embed-label { font-size: 11px; font-weight: var(--fw-bold); color: var(--cf-text-3); margin-bottom: 6px; }
.cfd-menu-embed-code { width: 100%; box-sizing: border-box; font-size: 11px; font-family: ui-monospace, monospace; color: var(--cf-text-2); resize: none; margin-bottom: 6px; }
.cfd-menu-embed-note { font-size: 11px; color: var(--cf-text-3); padding: 2px 0 0; }
.cfd-foot { display: flex; align-items: center; gap: 10px; margin-top: 30px; padding-top: 16px; border-top: 1px solid var(--cf-hairline); font-size: 11.5px; color: var(--cf-text-3); }
.cfd-foot-mark { font-weight: var(--fw-black); letter-spacing: .12em; color: var(--cf-text-2); }
@media (max-width: 640px) {
  .cfd { padding: 18px; border-radius: 0; }
  .cfd-actions { flex-direction: column; align-items: stretch; }
  .cfd-btn { width: 100%; justify-content: center; }
  .cfd-menu-panel { position: static; width: 100%; box-sizing: border-box; margin-top: 6px; }
  /* D1 — 8 highlights desktop / 6 mobile: the DOM always carries up to 8 (the
     honest first-N rule); under 640px the last two are hidden by CSS alone,
     never re-queried by JS viewport width. */
  .cfd-highlights { grid-template-columns: repeat(3, 1fr); }
  .cfd-highlight--mobile-hide { display: none; }
  .cfd-gal-grid { --cfd-gal-cols: 3; }   /* SPEC-GALDENSITY — phone DEFAULT, still overridable by the client's inline choice */
  .cfd-gal-head { flex-wrap: wrap; gap: 8px 10px; }   /* SPEC-GALSELECT — back · count · density · Select on one row, the address drops beneath */
  .cfd-gal-addr { flex-basis: 100%; order: 9; font-size: 13px; }
  .cfd-gal-selbar { padding: 10px 12px; }
  .cfd-gal-nav { width: 38px; height: 38px; }
  .cfd-gal-acts { flex-direction: column; align-items: stretch; }
}

/* ============================================================
   SPEC-V2CHECKIN — the monthly check-in (.cin-*) + the Instagram access
   guide's client-facing phone (.aig-*), both dark cinematic per README §11/§12.
   Share one phone silhouette (.vvt-phone-frame, lifted verbatim from .cfd's own
   border/radius/shadow) so a future third phone-shaped preview has one class to
   reuse instead of a third copy. All --cf- tokens, same as .cfd/.site-* above —
   NOT a new palette. The internal admin-only halves (checkin preview's own
   modal chrome, access guide's right column) deliberately use the Hub's
   existing light .card/.field/.btn classes inline (retainers.js's own idiom for
   one-off internal cards) rather than new CSS — only the CLIENT-facing surface
   gets a dedicated stylesheet, per delivery.js's precedent. */
.vvt-phone-frame { width: 390px; max-width: 100%; margin: 0 auto; background: var(--cf-ground); color: var(--cf-text);
  font-family: var(--cf-font); border: 1px solid var(--cf-hairline); border-radius: 22px; overflow: hidden;
  box-shadow: 0 12px 30px -10px rgba(16,24,40,.24); display: flex; flex-direction: column; }

/* ---- check-in (.cin-) ---- */
.cin-top { flex: none; padding: 18px 20px 0; display: flex; align-items: center; gap: 10px; }
.cin-brand { font-size: 9.5px; font-weight: var(--fw-black); letter-spacing: .24em; text-transform: uppercase; color: var(--cf-text-3); }
.cin-step { font-size: 11.5px; color: var(--cf-text-4); margin-left: auto; }
.cin-bars { flex: none; display: flex; gap: 5px; margin: 12px 20px 0; }
.cin-bar { flex: 1; height: 3px; border-radius: 2px; background: rgba(255,255,255,.14); }
.cin-bar--on { background: var(--cf-champagne); }
.cin-body { flex: 1; min-height: 320px; display: flex; flex-direction: column; padding: 26px 20px 20px; }
.cin-intro-row { display: flex; align-items: center; gap: 11px; padding: 12px 14px; background: var(--cf-card); border: 1px solid var(--cf-hairline); border-radius: 11px; margin-bottom: 9px; }
.cin-intro-num { width: 24px; height: 24px; flex: none; border-radius: 50%; background: rgba(230,210,177,.14); color: var(--cf-champagne); display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: var(--fw-bold); }
.cin-num-card { display: flex; align-items: center; gap: 14px; margin-top: 30px; padding: 20px 22px; background: var(--cf-card); border: 1px solid rgba(230,210,177,.3); border-radius: 14px; }
.cin-num-val { font-size: 52px; font-weight: var(--fw-black); letter-spacing: -.03em; line-height: 1; font-variant-numeric: tabular-nums; min-width: 1.4em; }
.cin-num-unit { font-size: 13px; font-weight: var(--fw-bold); color: var(--cf-text-2); }
.cin-num-hint { margin-top: 3px; font-size: 12px; line-height: 1.45; color: var(--cf-text-4); }
.cin-steppers { display: flex; gap: 8px; margin-top: 12px; }
.cin-step-btn { flex: 1; min-height: 46px; display: flex; align-items: center; justify-content: center; border: 1px solid var(--cf-hairline); border-radius: 11px; background: rgba(255,255,255,.03); font-family: var(--cf-font); font-size: 13.5px; font-weight: var(--fw-bold); color: var(--cf-text-2); cursor: pointer; }
.cin-step-btn:hover { border-color: rgba(230,210,177,.5); color: var(--cf-champagne); }
.cin-honest { display: flex; align-items: flex-start; gap: 9px; margin-top: 18px; padding: 12px 14px; background: rgba(230,210,177,.07); border: 1px solid rgba(230,210,177,.22); border-radius: 11px; font-size: 12px; line-height: 1.5; color: rgba(230,210,177,.82); }
.cin-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin-top: 22px; }
.cin-tile { cursor: pointer; border: 0; background: none; padding: 0; text-align: left; font-family: var(--cf-font); color: inherit; }
.cin-tile-img { position: relative; aspect-ratio: 1; border-radius: 11px; overflow: hidden; border: 2px solid rgba(255,255,255,.12); background-size: cover; background-position: center; background-color: var(--cf-card); }
.cin-tile--on .cin-tile-img { border-color: var(--cf-champagne); }
.cin-tile-check { display: none; position: absolute; top: 8px; right: 8px; width: 22px; height: 22px; border-radius: 50%; background: var(--cf-champagne); align-items: center; justify-content: center; }
.cin-tile--on .cin-tile-check { display: flex; }
.cin-tile-meta { position: absolute; left: 9px; bottom: 8px; font-size: 11px; font-weight: var(--fw-bold); color: #fff; text-shadow: 0 1px 4px rgba(0,0,0,.5); }
.cin-tile-title { margin-top: 7px; font-size: 12.5px; font-weight: var(--fw-bold); line-height: 1.4; color: var(--cf-text-2); }
.cin-tile--on .cin-tile-title { color: var(--cf-text); }
.cin-none { margin-top: 14px; min-height: 46px; width: 100%; display: flex; align-items: center; justify-content: center; border: 1px dashed rgba(255,255,255,.2); border-radius: 11px; background: none; font-family: var(--cf-font); font-size: 13px; color: var(--cf-text-3); cursor: pointer; }
.cin-none:hover, .cin-none--on { border-color: rgba(230,210,177,.5); color: var(--cf-champagne); }
.cin-chip { display: flex; align-items: center; gap: 12px; min-height: 52px; width: 100%; padding: 0 15px; margin-bottom: 8px; border: 1px solid var(--cf-hairline); border-radius: 12px; background: rgba(255,255,255,.02); font-family: var(--cf-font); text-align: left; cursor: pointer; }
.cin-chip--on { border-color: rgba(230,210,177,.42); background: rgba(230,210,177,.1); }
.cin-chip-box { width: 19px; height: 19px; flex: none; border-radius: 6px; border: 1.5px solid rgba(255,255,255,.28); display: flex; align-items: center; justify-content: center; }
.cin-chip--on .cin-chip-box { background: var(--cf-champagne); border-color: var(--cf-champagne); }
.cin-chip-label { font-size: 14px; font-weight: var(--fw-bold); color: var(--cf-text-2); }
.cin-chip--on .cin-chip-label { color: var(--cf-text); }
.cin-chip-note { margin-top: 2px; font-size: 11.5px; color: var(--cf-text-4); }
.cin-textarea { margin-top: 20px; min-height: 150px; width: 100%; box-sizing: border-box; padding: 14px 15px; background: var(--cf-card); border: 1px solid var(--cf-hairline); border-radius: 12px; font-family: var(--cf-font); font-size: 14.5px; line-height: 1.6; color: var(--cf-text); resize: vertical; }
.cin-textarea::placeholder { color: var(--cf-text-4); }
.cin-voice-row { display: flex; align-items: center; gap: 10px; margin-top: 11px; }
.cin-voice-btn { flex: none; width: 42px; height: 42px; display: flex; align-items: center; justify-content: center; border: 1px solid var(--cf-hairline); border-radius: 11px; background: none; color: var(--cf-text-2); cursor: pointer; }
.cin-voice-btn:hover { border-color: rgba(230,210,177,.5); color: var(--cf-champagne); }
.cin-done-icon { width: 46px; height: 46px; border-radius: 50%; background: rgba(230,210,177,.14); border: 1px solid rgba(230,210,177,.34); display: flex; align-items: center; justify-content: center; color: var(--cf-champagne); }
.cin-done-card { margin-top: 22px; padding: 15px 17px; background: var(--cf-card); border: 1px solid var(--cf-hairline); border-radius: 12px; }
.cin-actions { flex: none; padding: 16px 20px 20px; }
.cin-skip { margin-top: 10px; text-align: center; font-size: 12.5px; color: var(--cf-text-4); cursor: pointer; }
.cin-skip:hover { color: var(--cf-text-2); }

/* the check-in's own full-width CTA — deliberately NOT .cfd-btn (that class is a
   999px PILL, sized for delivery.js's small inline Share/Download chips). The mock's
   own Next/Send-it/Back button is a full-width BLOCK, 12px radius, 52px tall
   (VVT-Monthly-Check-In.dc.html's own inline style, measured via CDP). */
.cin-btn { width: 100%; box-sizing: border-box; min-height: 52px; display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  border-radius: 12px; border: 1px solid var(--cf-hairline); background: transparent; color: var(--cf-text);
  font-family: var(--cf-font); font-size: 15px; font-weight: var(--fw-bold); cursor: pointer; }
.cin-btn:hover { border-color: var(--cf-hairline-hover); background: rgba(255,255,255,.04); }
.cin-btn--primary { border: 0; background: var(--cf-champagne); color: #1a1a1a; }
.cin-btn--primary:hover { background: #d9c39d; }

/* ---- Instagram access guide client phone (.aig-) ---- */
.aig-hero { position: relative; padding: 20px 20px 26px; }
.aig-hero-glow { position: absolute; top: 0; left: 0; right: 0; height: 200px; pointer-events: none;
  background: radial-gradient(ellipse at 40% 0%, rgba(230,210,177,.14), rgba(13,13,13,0) 72%); }
.aig-hero-body { position: relative; }
.aig-trust { margin: 0 20px; background: var(--cf-card); border: 1px solid var(--cf-hairline); border-radius: 13px; overflow: hidden; }
.aig-trust-head { padding: 13px 15px 10px; border-bottom: 1px solid rgba(255,255,255,.07); font-size: 10px; font-weight: var(--fw-black); letter-spacing: .16em; text-transform: uppercase; color: var(--cf-champagne); }
.aig-trust-row { display: flex; align-items: flex-start; gap: 11px; padding: 11px 15px; border-bottom: 1px solid rgba(255,255,255,.05); }
.aig-trust-row:last-child { border-bottom: 0; }
.aig-trust-title { font-size: 13.5px; font-weight: var(--fw-bold); line-height: 1.4; }
.aig-trust-note { margin-top: 3px; font-size: 12px; line-height: 1.5; color: var(--cf-text-3); }
.aig-steps { padding: 24px 20px 0; }
.aig-steps-label { font-size: 10px; font-weight: var(--fw-black); letter-spacing: .16em; text-transform: uppercase; color: var(--cf-text-4); }
.aig-step { display: flex; gap: 13px; margin-top: 18px; }
.aig-step-num { width: 28px; height: 28px; flex: none; border-radius: 50%; background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.14); color: var(--cf-text-2); display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: var(--fw-black); }
.aig-step--active .aig-step-num { background: rgba(230,210,177,.16); border-color: rgba(230,210,177,.42); color: var(--cf-champagne); }
.aig-step-title { font-size: 15.5px; font-weight: var(--fw-bold); line-height: 1.3; }
.aig-step-body { margin-top: 6px; font-size: 13px; line-height: 1.55; color: var(--cf-text-2); }
.aig-step-taps { margin-top: 10px; background: var(--cf-card); border: 1px solid var(--cf-hairline); border-radius: 11px; overflow: hidden; }
.aig-step-tap { display: flex; align-items: center; gap: 9px; padding: 10px 13px; border-bottom: 1px solid rgba(255,255,255,.05); font-size: 12.5px; font-weight: var(--fw-bold); }
.aig-step-tap:last-child { border-bottom: 0; }
.aig-step-tap-i { color: var(--cf-text-4); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.aig-step-skip { margin-top: 10px; padding: 10px 13px; background: rgba(103,179,213,.08); border: 1px solid rgba(103,179,213,.24); border-radius: 10px; font-size: 12px; line-height: 1.5; color: var(--cf-blue-hover); }
.aig-done { margin: 24px 20px 0; padding: 18px 19px 20px; background: rgba(230,210,177,.09); border: 1px solid rgba(230,210,177,.3); border-radius: 13px; }
.aig-done-title { display: flex; align-items: center; gap: 9px; font-size: 15.5px; font-weight: var(--fw-bold); color: var(--cf-champagne); }
.aig-done-body { margin-top: 11px; font-size: 13px; line-height: 1.6; color: rgba(230,210,177,.82); }
.aig-revoke { display: flex; gap: 10px; margin: 20px 20px 0; font-size: 11.5px; line-height: 1.55; color: var(--cf-text-4); }
.aig-cta-wrap { padding: 22px 20px 24px; }
.aig-cta { width: 100%; box-sizing: border-box; min-height: 52px; border: 0; border-radius: 12px; background: var(--cf-champagne); font-family: var(--cf-font); font-size: 14.5px; font-weight: var(--fw-bold); color: #2e2718; cursor: pointer; }
.aig-cta:hover { background: #d9c39d; }
.aig-cta--off { background: rgba(255,255,255,.06); color: var(--cf-text-3); cursor: default; }
.aig-cta--off:hover { background: rgba(255,255,255,.06); }
.aig-foot { margin-top: 16px; padding-top: 15px; border-top: 1px solid var(--cf-hairline); display: flex; align-items: baseline; gap: 9px; font-size: 11px; color: var(--cf-text-4); }
@media (max-width: 640px) {
  .vvt-phone-frame { border-radius: 0; }
}

/* SPEC170 — single-property website (.site-*) + its editor */
/* SPEC-DHCLIENT D4 — the property site was already luxury-dark per template (.site-tpl--
   estate hand-rolled its own dark palette below); converge EVERY template onto the D1
   tokens at the base instead of leaving that as a one-template special case (README:
   "Client-facing — dark cinematic"; the Client-Seller-View.dc.html prototype shows this
   surface fully dark, no light variant). Scoped local var remap (same idiom
   .site-tpl--estate already used) so every existing --text/--text-soft/--muted/--border/
   --bg-2/--panel consumer below repaints correctly with ZERO other rule changes; agent
   brand tokens (--site-primary/--site-accent/--site-on-primary, set per-listing by
   propertysite.js) are untouched — the agent's own color keeps top billing per D4. */
.site-page { font-family: var(--site-font, 'Fraunces', Georgia, serif); max-width: 920px; margin: 0 auto;
  background: var(--cf-ground); color: var(--cf-text);
  --text: var(--cf-text); --text-soft: var(--cf-text-3); --muted: var(--cf-text-3);
  --border: var(--cf-hairline); --bg-2: var(--cf-card); --panel: var(--cf-card); }
.site-hero { position: relative; width: 100%; aspect-ratio: 16/9; max-height: 460px; border-radius: 14px; overflow: hidden; background-size: cover; background-position: center; background-color: var(--cf-card); display: flex; align-items: flex-end; }
.site-hero--empty { background-image: linear-gradient(150deg, var(--site-primary), rgba(0,0,0,.55)); }
.site-hero-scrim { position: absolute; inset: 0; background: var(--cf-scrim); }
.site-hero-text { position: relative; padding: 26px 28px; color: var(--cf-text); }
.site-hero-brand { font-weight: var(--fw-bold); letter-spacing: .18em; text-transform: uppercase; font-size: var(--fs-meta); opacity: .9; font-family: var(--font); }
.site-hero-title { font-size: clamp(26px, 5vw, 44px); font-weight: var(--fw-black); line-height: 1.05; margin: 4px 0 0; text-shadow: 0 2px 18px rgba(0,0,0,.45); }
.site-hero-sub { font-size: var(--fs-md); opacity: .94; margin-top: 6px; }
.site-facts { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin: 18px 0; }
.site-fact { flex: 1 1 90px; max-width: 140px; text-align: center; padding: 14px 8px; border: 1px solid var(--border); border-radius: 12px; }
.site-fact-n { font-size: 24px; font-weight: var(--fw-black); color: var(--site-primary); line-height: 1; }
.site-fact-l { font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: .08em; text-transform: uppercase; color: var(--text-soft); margin-top: 5px; }
.site-section { margin: 30px 0; }
.site-section--agent { border-top: 1px solid var(--border); padding-top: 24px; }
.site-h2 { font-size: var(--fs-caption); font-weight: var(--fw-black); letter-spacing: .14em; text-transform: uppercase; color: var(--site-primary); margin: 0 0 14px; font-family: var(--font); }
.site-detail { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 0 28px; }
.site-detail-row { display: flex; justify-content: space-between; gap: 12px; padding: 11px 0; border-bottom: 1px solid var(--border); }
.site-detail-k { color: var(--text-soft); font-size: 13.5px; }
.site-detail-v { font-weight: var(--fw-bold); font-size: 13.5px; text-align: right; }
.site-desc { font-size: var(--fs-md); line-height: 1.7; max-width: 680px; white-space: pre-wrap; }
.site-highlights { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 8px 18px; }
.site-hl { display: flex; align-items: flex-start; gap: 8px; font-size: 14.5px; }
.site-hl-check { color: var(--site-accent); font-weight: var(--fw-heavy); }
.site-map { width: 100%; aspect-ratio: 16/9; max-height: 380px; border: 0; border-radius: 14px; }
/* SPEC220 — enrichment sections: video/3D embeds, floor plans, schedule-a-showing CTA */
.site-embed { position: relative; width: 100%; aspect-ratio: 16/9; border-radius: 14px; overflow: hidden; background: #000; }
.site-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.site-video { width: 100%; max-height: 540px; border-radius: 14px; background: #000; display: block; }
.site-floorplans { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; }
.site-floorplan { width: 100%; border-radius: 12px; border: 1px solid var(--border); background: #fff; }
.site-cta { text-align: center; background: var(--site-primary, #0e7c5a); color: var(--site-on-primary, #fff); border-radius: 18px; padding: 30px 22px; }
.site-cta .site-h2 { color: var(--site-on-primary, #fff); }
.site-cta-sub { font-size: 15px; line-height: 1.6; max-width: 520px; margin: 6px auto 16px; color: var(--site-on-primary, #fff); }
.site-cta-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
/* SPEC220 — QR modal */
.qr-wrap { display: flex; flex-direction: column; gap: 12px; }
.qr-box { display: flex; align-items: center; justify-content: center; min-height: 220px; background: #fff; border: 1px solid var(--border); border-radius: 14px; padding: 14px; }
.qr-box svg { width: 220px; height: 220px; max-width: 100%; }
.qr-note { font-size: var(--fs-meta); }
.site-agent { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
.site-agent-pic { width: 92px; height: 92px; border-radius: 999px; background-size: cover; background-position: center; flex: 0 0 auto; border: 3px solid var(--site-accent); }
.site-agent-name { font-size: 21px; font-weight: var(--fw-black); }
.site-agent-sub { color: var(--text-soft); font-size: var(--fs-body); margin-top: 2px; }
.site-agent-lic { color: var(--text-soft); font-size: var(--fs-meta); margin-top: 2px; }
.site-agent-cta { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.site-footer { text-align: center; color: var(--text-soft); font-size: 12.5px; padding: 26px 0 6px; margin-top: 18px; border-top: 1px solid var(--border); font-family: var(--font); }

/* ============================================================
   SPEC221 — luxury property-site TEMPLATES + stat-hero. Base uses the template's
   display/body font vars; each .site-tpl--{id} re-skins palette/spacing/type for a
   distinct boutique feel. Money-safe (stat-hero shows only public price/status). */
.site-page { font-family: var(--site-body-font, var(--site-font, 'Fraunces', Georgia, serif)); }
.site-page .site-hero-title, .site-page .sh-price, .site-page .sh-street { font-family: var(--site-display-font, var(--site-font, 'Fraunces', Georgia, serif)); }
.site-hero-brand { font-family: var(--site-body-font, var(--font)); }
/* stat-hero — the "what everyone checks first" block */
.site-stathero { text-align: center; }
.sh-top { display: flex; gap: 10px; justify-content: center; align-items: center; margin-bottom: 12px; flex-wrap: wrap; }
.sh-status { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: .1em; text-transform: uppercase; padding: 5px 12px; border-radius: 999px; }
/* SPEC-DHCLIENT D4 — promoted from the old .site-tpl--estate-only dark-safe values (D4
   converges the WHOLE site-page dark now, not just estate — see the .site-page rule
   above); these were already AA-checked against #0d0d0f, near-identical to --cf-ground. */
.sh-status--ok { background: rgba(22,163,74,.22); color: #4ade80; }
.sh-status--warn { background: rgba(217,119,6,.22); color: #fbbf24; }
.sh-status--danger { background: rgba(220,38,38,.22); color: #f87171; }
.sh-status--muted { background: var(--bg-2); color: var(--text-soft); }
.sh-status--info { background: rgba(37,99,235,.22); color: #93c5fd; }
.sh-dom { font-size: var(--fs-meta); color: var(--text-soft); }
.sh-addr { margin-bottom: 6px; }
.sh-street { font-size: clamp(20px,3.4vw,28px); font-weight: var(--fw-bold); line-height: 1.15; }
.sh-city { font-size: var(--fs-caption); letter-spacing: var(--ls-label); color: var(--text-soft); margin-top: 3px; }
.sh-price { font-size: clamp(28px,5vw,40px); font-weight: var(--fw-black); color: var(--text); margin: 8px 0 20px; letter-spacing: -.01em; }
.sh-stats { display: flex; justify-content: center; flex-wrap: wrap; }
.sh-cell { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 6px 22px; position: relative; }
.sh-cell + .sh-cell::before { content: ''; position: absolute; left: 0; top: 16%; height: 68%; width: 1px; background: var(--border); }
.sh-ic { color: var(--site-primary); display: inline-flex; }
.sh-n { font-size: 19px; font-weight: var(--fw-black); line-height: 1; }
.sh-l { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .08em; text-transform: uppercase; color: var(--text-soft); }
.site-agent-eyebrow { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .16em; text-transform: uppercase; color: var(--site-primary); margin-bottom: 4px; }
/* ============================================================================
   PLAN-PREMIUM-2026-09-13 (N4/P0+P1+P1b) — Family A "full-bleed editorial":
   EDITORIAL (recreates Rela's "Stone") + boutique/"Quiet" (recreates "Aura").
   RESEARCH-RELA-TEMPLATES-2026-09-13.md §6. Every rule below is scoped to
   .site-tpl--editorial / .site-tpl--boutique ONLY — estate/lifestyle/modern
   keep their existing CSS + the 920px cap byte-for-byte. Regenerate site.css
   after any edit here: node tools/extract-site-css.js.
   ============================================================================ */

/* ---- scoped palette remap: warm paper, not the dark client-facing ground the
   other templates share (D4). Same idiom .site-page already uses for --text/
   --border/etc — declaring --cf-* here on the SAME element (.site-page also
   carries .site-tpl--editorial) overrides the inherited :root value for this
   subtree only. ---- */
.site-tpl--editorial, .site-tpl--boutique {
  --site-paper: #FAFAF8; --site-paper-2: #EEEEEC; --site-ink: #17181A;
  --cf-ground: var(--site-paper); --cf-card: var(--site-paper-2); --cf-text: var(--site-ink);
  --cf-text-2: rgba(23,24,26,.72); --cf-text-3: rgba(23,24,26,.55); --cf-text-4: rgba(23,24,26,.34);
  --cf-hairline: rgba(23,24,26,.12); --cf-hairline-hover: rgba(23,24,26,.24);
  --cf-scrim: linear-gradient(180deg, rgba(0,0,0,.38) 0%, rgba(0,0,0,0) 34%, rgba(0,0,0,.55) 100%);
  /* Family A type scale (DESIGN-BRIEF §1: numerals stay Inter/tabular — never Fraunces) */
  --site-fs-display: clamp(46px, 7.2vw, 82px); --site-fs-h2: clamp(30px, 3.6vw, 45px);
  --site-fs-h3: 22px; --site-fs-eyebrow: 11px; --site-fs-body: 16px;
}
/* ---- P0 #1 — full-bleed escape hatch. Other templates keep max-width:920px. ---- */
.site-page.site-tpl--editorial, .site-page.site-tpl--boutique { max-width: none; padding: 0; }
/* the SECTION itself stays full-width (so its background band can run edge-to-edge);
   its own direct children are constrained to the reading rail. .site-footer keeps the
   simpler "just center it" treatment since it renders no alternating band. */
.site-tpl--editorial .site-section, .site-tpl--boutique .site-section {
  padding-block: clamp(72px, 9vw, 140px); box-sizing: border-box;
}
.site-tpl--editorial .site-section > *, .site-tpl--boutique .site-section > * {
  max-width: min(1180px, 92vw); margin-inline: auto;
}
.site-tpl--editorial .site-footer, .site-tpl--boutique .site-footer { width: min(1180px, 92vw); margin: 0 auto; }
.site-bleed { width: 100vw; margin-inline: calc(50% - 50vw); }
/* a .site-bleed child needs to beat the generic "> *" reading-rail rule above (higher
   specificity: 3 classes vs 2) so the gallery genuinely reaches the viewport edges. */
.site-tpl--editorial .site-section > .site-bleed, .site-tpl--boutique .site-section > .site-bleed {
  width: 100vw; max-width: 100vw; margin-inline: calc(50% - 50vw);
}
/* alternating grounds — every other RENDERED section sits on the second paper tone.
   .site-section--alt is stamped by propertysite.js's render loop (editorial/boutique
   only) rather than :nth-of-type, because .site-section shares its tag (div) with
   non-section siblings (hero, footer, nav wrap, stickybar) that would shift the parity. */
.site-tpl--editorial .site-section--alt, .site-tpl--boutique .site-section--alt { background: var(--site-paper-2); }

/* ---- P0 #2 — 100svh square-cornered hero. Pure CSS on the EXISTING hero DOM —
   no JS/structure change, so the hero stays byte-for-byte for every template. ---- */
.site-tpl--editorial .site-hero, .site-tpl--boutique .site-hero, .site-tpl--cinema .site-hero {
  aspect-ratio: auto; height: 100svh; min-height: 640px; max-height: none; border-radius: 0;
}
.site-tpl--editorial .site-hero-scrim, .site-tpl--boutique .site-hero-scrim, .site-tpl--cinema .site-hero-scrim { background: var(--cf-scrim); }
.site-tpl--editorial .site-hero-text, .site-tpl--boutique .site-hero-text, .site-tpl--cinema .site-hero-text {
  padding: 0 clamp(20px, 5vw, 64px) 26vh; width: 100%; box-sizing: border-box;
}
.site-tpl--editorial .site-hero-title, .site-tpl--boutique .site-hero-title {
  font-size: var(--site-fs-display); letter-spacing: -.025em; line-height: 1.02; font-weight: 400;
}
.site-tpl--editorial .site-hero-street { font-size: 15px; letter-spacing: .04em; opacity: .82; margin-top: 6px; }
.site-tpl--editorial .site-hero-brand, .site-tpl--boutique .site-hero-brand {
  font-size: var(--site-fs-eyebrow); letter-spacing: .24em;
}
/* the facts strip (statHero) placed immediately after the hero in BOTH upgraded
   orders — pulled up to read as Stone's bottom-of-hero strip, on a 1px hairline. */
.site-tpl--editorial .site-hero + .site-stathero, .site-tpl--boutique .site-hero + .site-stathero {
  margin-top: -128px; position: relative; z-index: 2; padding: 22px clamp(20px, 5vw, 64px) 0;
  border-top: 1px solid rgba(255,255,255,.22); color: #fff;
}
.site-tpl--editorial .site-hero + .site-stathero .sh-l,
.site-tpl--editorial .site-hero + .site-stathero .sh-dom,
.site-tpl--boutique .site-hero + .site-stathero .sh-l,
.site-tpl--boutique .site-hero + .site-stathero .sh-dom { color: rgba(255,255,255,.72); }
.site-tpl--editorial .site-hero + .site-stathero .sh-cell + .sh-cell::before,
.site-tpl--boutique .site-hero + .site-stathero .sh-cell + .sh-cell::before { background: rgba(255,255,255,.22); }
.site-tpl--editorial .site-hero + .site-stathero .sh-ic,
.site-tpl--boutique .site-hero + .site-stathero .sh-ic { color: #fff; }
/* FABLE judge 2026-09-13 (hands-on, real photos): the hero already carries the street + city + price, so the
   pulled-up facts strip must NOT repeat the address — it read as a duplicated title on desktop and again
   under the hero on phones. Strip = price + the four cells only. */
.site-tpl--editorial .site-stathero .sh-addr, .site-tpl--boutique .site-stathero .sh-addr { display: none; }
@media (max-width: 720px) {
  .site-tpl--editorial .site-hero + .site-stathero, .site-tpl--boutique .site-hero + .site-stathero { margin-top: 0; position: static; border-top: 0; color: inherit; padding: 22px clamp(20px, 5vw, 64px); }
  .site-tpl--editorial .site-hero + .site-stathero .sh-l, .site-tpl--boutique .site-hero + .site-stathero .sh-l,
  .site-tpl--editorial .site-hero + .site-stathero .sh-dom, .site-tpl--boutique .site-hero + .site-stathero .sh-dom,
  .site-tpl--editorial .site-hero + .site-stathero .sh-ic, .site-tpl--boutique .site-hero + .site-stathero .sh-ic { color: inherit; }
  /* FABLE judge — four cells as a 2×2 grid on phones (the 3+1 wrap orphaned "per sq ft") */
  .site-tpl--editorial .site-stathero .sh-stats, .site-tpl--boutique .site-stathero .sh-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 18px 12px; }
  .site-tpl--editorial .site-stathero .sh-cell + .sh-cell::before, .site-tpl--boutique .site-stathero .sh-cell + .sh-cell::before { display: none; }
  .site-tpl--editorial .site-hero + .site-stathero .sh-cell + .sh-cell::before, .site-tpl--boutique .site-hero + .site-stathero .sh-cell + .sh-cell::before { background: var(--border); }
}

/* ---- P0 #4 numbered eyebrows — the premium tell ---- */
.site-tpl--editorial .site-section, .site-tpl--boutique .site-section { counter-increment: vvt-sec; }
/* the agent-accent hairline — Editorial's "rules/hairlines" structural accent touch
   (research §6 house-rule 2: every family expresses agent.accent somewhere structural) */
.site-tpl--editorial .site-section { border-top: 1px solid color-mix(in srgb, var(--site-primary) 35%, var(--border)); }
.site-tpl--editorial .site-hero + .site-stathero { border-top-color: color-mix(in srgb, var(--site-primary) 45%, rgba(255,255,255,.22)); }
.site-tpl--editorial .site-h2::before, .site-tpl--boutique .site-h2::before {
  content: "// " counter(vvt-sec, decimal-leading-zero) " — "; opacity: .55; letter-spacing: .18em;
}
.site-tpl--editorial .site-h2, .site-tpl--boutique .site-h2 {
  font-size: var(--site-fs-eyebrow); letter-spacing: .24em; color: var(--site-ink); opacity: .82; font-family: var(--site-body-font);
}
.site-tpl--editorial .site-desc, .site-tpl--boutique .site-desc { font-size: var(--site-fs-body); line-height: 1.68; max-width: 68ch; }
.site-gallery-viewall { display: inline-block; margin-top: 18px; background: none; border: 0; padding: 0; font-size: 13px; letter-spacing: .1em; text-transform: uppercase; cursor: pointer; color: var(--site-ink); border-bottom: 1px solid currentColor; min-height: 44px; }
.site-tpl--editorial .site-desc::first-letter { font-family: var(--site-display-font); font-size: 3em; line-height: .82; float: left; padding: 4px 10px 0 0; color: var(--site-primary); font-weight: var(--fw-bold); }
.site-tpl--editorial .site-section--agent { border-top: 0; }

/* ---- QUIET / Aura — TRUE type-first layout (PLAN-PREMIUM-2026-09-13 N4 slice2) ----
   The hero DOM is shared with Editorial (same .site-hero markup, pinned by tests), so this
   stays CSS-only: propertysite.js adds the .site-hero--quiet MODIFIER class to that SAME
   element and skips setting a background image for it (research §6 — "No photo behind the
   type"). The photo instead renders in a full-bleed .site-imageband section right after the
   hero (R.imageband); a SECOND, header-less statHero call (factsRow, .site-stathero--
   factsonly) renders the 5-up facts row after the image band (R.factsRow) — see
   propertysite.js for both renderers. */
.site-tpl--boutique .site-hero-scrim { background: linear-gradient(180deg, rgba(0,0,0,.5) 0%, rgba(0,0,0,.06) 46%, rgba(0,0,0,.34) 100%); }
.site-tpl--boutique .site-hero-text { align-self: flex-start; padding: 14vh clamp(20px, 5vw, 64px) 0; }
.site-tpl--boutique .site-hero { align-items: flex-start; position: relative; }
.site-tpl--boutique .site-hero-title { text-align: left; font-weight: 300; max-width: 14ch; }
.site-tpl--boutique .site-desc { margin: 0; text-align: left; }
/* the "[ OFFERED AT ]" card — right-aligned, price only (its own stat cells are hidden;
   the SAME numbers render again, unabridged, in the 5-up factsRow after the image band). */
.site-tpl--boutique .site-hero + .site-stathero {
  position: absolute; top: clamp(72px, 9vw, 140px); right: clamp(20px, 5vw, 64px); left: auto; width: min(300px, 40vw);
  margin-top: 0; border: 1px solid rgba(255,255,255,.4); border-top: 1px solid rgba(255,255,255,.4);
  border-radius: 2px; padding: 20px; background: rgba(0,0,0,.32);
}
/* the card shows PRICE only — its own stat cells are hidden; the SAME numbers render,
   unabridged, in the 5-up factsRow after the image band (R.factsRow). */
.site-tpl--boutique .site-hero + .site-stathero .sh-stats { display: none; justify-content: flex-start; }
@media (max-width: 900px) {
  .site-tpl--boutique .site-hero + .site-stathero { position: static; width: auto; margin: 0; border: 0; background: none; padding: 22px clamp(20px, 5vw, 64px); }
}
/* .site-hero--quiet — NO photo behind the headline. Height collapses to its own content
   (no longer the 100svh square-cornered hero every other upgraded template uses) because
   Aura's hero is a text block with generous padding, not a full screen. The "[ OFFERED AT ]"
   card above still anchors to this (now short) box via position:absolute — top uses the SAME
   clamp() as the hero's own top padding so it lines up with the headline's first line. */
.site-tpl--boutique .site-hero--quiet {
  height: auto; min-height: 0; max-height: none; aspect-ratio: auto; display: block;
  padding-block: clamp(72px, 9vw, 140px) 64px; box-sizing: border-box; background: var(--site-paper);
}
.site-tpl--boutique .site-hero--quiet .site-hero-img,
.site-tpl--boutique .site-hero--quiet .site-hero-scrim { display: none; }
.site-tpl--boutique .site-hero--quiet .site-hero-text { position: static; padding: 0; max-width: 58ch; }
/* the full-bleed image band — a plain block child of a max-width:none/padding:0 .site-page,
   so it is edge-to-edge with no extra bleed rule needed. */
.site-imageband { height: 62svh; min-height: 280px; background-size: cover; background-position: center; background-color: var(--site-paper-2); }
@media (max-width: 720px) { .site-imageband { height: 42svh; min-height: 220px; } }
/* the 5-up facts ROW — plain, on the paper ground, not a card. Not a .site-section (it wants
   none of the numbered-eyebrow/nav-listing/alternating-band machinery), so its own spacing
   + reading-rail width are declared here rather than inherited from .site-section rules. */
.site-stathero--factsonly { padding: clamp(28px, 5vw, 48px) clamp(20px, 5vw, 64px); max-width: min(1180px, 92vw); margin-inline: auto; box-sizing: border-box; }
.site-stathero--factsonly .sh-stats { justify-content: space-between; }
.site-stathero--factsonly .sh-cell { flex: 1 1 0; }

/* ---- Sticky nav + overlay menu (App.siteNav) — shared component, editorial/Quiet only this slice ---- */
.site-nav-wrap { position: relative; z-index: 20; }
.site-nav { position: fixed; top: 0; left: 0; right: 0; height: 52px; z-index: 30; display: flex; align-items: center; justify-content: space-between; padding: 0 clamp(20px, 5vw, 64px); background: transparent; color: #fff; pointer-events: none; transition: background-color .2s ease; }
.site-nav.site-nav--solid { background: var(--site-paper); color: var(--site-ink); box-shadow: 0 1px 0 var(--cf-hairline); }
.site-nav.site-nav--solid .site-nav-brand, .site-nav.site-nav--solid .site-nav-menu { text-shadow: none; }
.site-nav > * { pointer-events: auto; }
.site-nav-brand { font-size: 12.5px; letter-spacing: .12em; text-transform: uppercase; font-weight: var(--fw-bold); text-shadow: 0 1px 6px rgba(0,0,0,.4); }
.site-nav-menu { background: none; border: 0; color: inherit; font-size: 12px; letter-spacing: .22em; text-transform: uppercase; font-weight: var(--fw-bold); cursor: pointer; padding: 8px; text-shadow: 0 1px 6px rgba(0,0,0,.4); min-height: 44px; }
.site-nav-overlay { position: fixed; inset: 0; z-index: 40; background: var(--site-ink); color: #FAFAF8; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.site-nav-overlay[hidden] { display: none; }
.site-nav-close { position: absolute; top: 14px; right: clamp(16px, 4vw, 40px); background: none; border: 0; color: inherit; font-size: 20px; cursor: pointer; min-width: 44px; min-height: 44px; }
.site-nav-list { list-style: none; margin: 0; padding: 0; text-align: center; }
.site-nav-item { margin: 10px 0; }
.site-nav-link { display: inline-flex; align-items: baseline; gap: 14px; color: inherit; text-decoration: none; font-family: var(--site-display-font); font-size: clamp(22px, 4vw, 36px); }
.site-nav-num { font-family: var(--site-body-font); font-size: 12px; opacity: .5; font-variant-numeric: tabular-nums; }
.site-nav-link:hover .site-nav-label { text-decoration: underline; text-underline-offset: 4px; }
@media (prefers-reduced-motion: no-preference) { .site-nav-overlay { animation: none; } }

/* ---- Documents / open house sections ---- */
.site-documents { display: flex; flex-direction: column; }
.site-doc { display: flex; align-items: center; gap: 10px; padding: 16px 0; border-top: 1px solid var(--border); text-decoration: none; color: inherit; }
.site-doc:first-child { border-top: 0; }
.site-doc-label { flex: 1; font-size: 15px; }
.site-doc-arrow { opacity: .6; }
.site-openhouses { display: flex; flex-direction: column; gap: 14px; }
.site-oh { padding: 14px 18px; border: 1px solid var(--border); border-radius: 4px; }
.site-oh-when { font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.site-oh-note { color: var(--text-soft); font-size: 13.5px; margin-top: 3px; }

/* ---- Floor plan tabs ---- */
.site-fp-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 16px; }
.site-fp-tab { background: none; border: 1px solid var(--border); border-radius: 999px; padding: 8px 16px; font-size: 12.5px; letter-spacing: .06em; text-transform: uppercase; cursor: pointer; color: var(--text-soft); min-height: 40px; }
.site-fp-tab.is-active { background: var(--site-ink); color: var(--site-paper); border-color: var(--site-ink); }
.site-fp-panel { display: none; }
.site-fp-panel.is-active { display: block; }
.site-fp-panel .floorplan-pdf-frame, .site-fp-panel iframe { width: 100%; aspect-ratio: 4/3; border: 0; border-radius: 8px; }

/* ---- Ambient music pill (App.siteMusic) ---- */
.site-music { position: fixed; right: 20px; bottom: 20px; z-index: 25; }
.site-music-pill { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 16px; border-radius: 999px; border: 1px solid var(--cf-hairline, rgba(255,255,255,.2)); background: rgba(20,20,20,.72); color: #fff; font-size: 12.5px; letter-spacing: .06em; text-transform: uppercase; cursor: pointer; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.site-music-pill.is-on { background: var(--site-primary, #17181A); }
.site-music-ic { font-size: 14px; }
@media (max-width: 640px) { .site-music { bottom: 84px; } }   /* clears .site-stickybar on mobile */

/* EDITORIAL (Stone) + QUIET (Aura) — the full-bleed dark closing band (inquiry + agent),
   both research specs land one dark block right before the footer. */
.site-tpl--editorial .site-cta, .site-tpl--editorial .site-section--agent,
.site-tpl--boutique .site-cta, .site-tpl--boutique .site-section--agent {
  background: #1A1B1C; color: #fff; border-radius: 0; border-top-color: transparent;
}
.site-tpl--editorial .site-section--agent .site-agent-eyebrow, .site-tpl--editorial .site-section--agent .site-agent-sub, .site-tpl--editorial .site-section--agent .site-agent-lic,
.site-tpl--boutique .site-section--agent .site-agent-eyebrow, .site-tpl--boutique .site-section--agent .site-agent-sub, .site-tpl--boutique .site-section--agent .site-agent-lic { color: rgba(255,255,255,.72); }
.site-tpl--editorial .site-section--agent .site-agent-pic, .site-tpl--boutique .site-section--agent .site-agent-pic { border-color: var(--site-accent); }
/* BOUTIQUE (Quiet/Aura) — maximum whitespace, whispering weight; the offered-at card
   frames the agent's accent as the one structural colour touch. */
.site-tpl--boutique .site-fact, .site-tpl--boutique .sh-price { border-color: var(--site-accent); }
.site-tpl--boutique .site-highlights { display: flex; flex-wrap: wrap; gap: 8px; grid-template-columns: none; }
.site-tpl--boutique .site-hl { background: var(--site-paper-2); border-radius: 999px; padding: 8px 15px; align-items: center; }
/* research §6 Family A — .site-stickybar is a MOBILE-only affordance once the sticky nav
   exists (the nav's MENU already reaches the agent on desktop via the contact/agent bands) */
@media (min-width: 721px) {
  .site-tpl--editorial .site-stickybar, .site-tpl--boutique .site-stickybar { display: none; }
}
/* ============================================================================
   PLAN-PREMIUM-2026-09-13 (N4 slice2) — Family B "dark cinematic panels": CINEMA
   (recreates Dover). Stacked 100svh panels, near-black grounds A/B + a bone ground C,
   picked by data-sec (propertysite.js stamps each rendered section's renderer key there,
   for the 5 upgraded templates only — see the tpl.order.forEach loop in propertysite.js).
   Regenerate site.css after any edit here: node tools/extract-site-css.js.
   ============================================================================ */
.site-tpl--cinema {
  --cin-a: #202422; --cin-b: #1B1E1D; --cin-c: #E5E1D6; --cin-c-ink: #1B1E1D;
  --cf-ground: var(--cin-a); --cf-card: var(--cin-b); --cf-text: #EDEDEA;
  --cf-text-2: rgba(237,237,234,.78); --cf-text-3: rgba(237,237,234,.6); --cf-text-4: rgba(237,237,234,.4);
  --cf-hairline: rgba(237,237,234,.16); --cf-hairline-hover: rgba(237,237,234,.3);
  --cf-scrim: linear-gradient(180deg, rgba(0,0,0,.42) 0%, rgba(0,0,0,.06) 40%, rgba(0,0,0,.62) 100%);
  --site-ink: #141514;
  --site-fs-display: clamp(54px, 13vw, 148px); --site-fs-h2: clamp(26px, 3vw, 34px);
  --site-fs-h3: 20px; --site-fs-eyebrow: 10px; --site-fs-body: 15px;
}
.site-page.site-tpl--cinema { max-width: none; padding: 0; }
/* stacked 100svh panels — one idea per screen */
.site-tpl--cinema .site-section {
  min-height: 100svh; display: grid; align-content: center; box-sizing: border-box;
  padding-inline: clamp(24px, 6vw, 88px); padding-block: clamp(56px, 9vw, 100px);
}
.site-tpl--cinema .site-section > * { max-width: min(1180px, 92vw); margin-inline: auto; }
/* per-section grounds by ROLE (research §6 table), not even/odd alternation — Cinema names
   its grounds. Ground A (charcoal, the default — .site-page already sets --cf-ground to
   --cin-a): overview/tour/openHouse/detail/documents/contact/agent/inquiry. Ground B
   (near-black): gallery. Ground C (bone, light ink): features + map. */
.site-tpl--cinema .site-section[data-sec="gallery"] { background: var(--cin-b); }
.site-tpl--cinema .site-section[data-sec="highlights"],
.site-tpl--cinema .site-section[data-sec="map"] { background: var(--cin-c); color: var(--cin-c-ink); }
.site-tpl--cinema .site-section[data-sec="highlights"] .site-h2,
.site-tpl--cinema .site-section[data-sec="map"] .site-h2 { color: var(--cin-c-ink); opacity: .68; }
.site-tpl--cinema .site-section[data-sec="highlights"] .site-hl-check,
.site-tpl--cinema .site-section[data-sec="map"] a { color: var(--cin-c-ink); }
/* scroll-snap — CSS only, never a JS scroll-takeover (research §6 explicitly rejects Dover's
   own wheel-hijack: it breaks keyboard/trackpad/mobile/deep-linking). :has() targets the
   real scrolling element (html) from the render-tree class alone. */
@media (prefers-reduced-motion: no-preference) {
  html:has(.site-tpl--cinema) { scroll-snap-type: y proximity; }
  .site-tpl--cinema .site-section { scroll-snap-align: start; }
}
/* type — Plus Jakarta Sans at architectural scale, uppercase, tight negative tracking */
.site-tpl--cinema .site-hero-title { text-transform: uppercase; letter-spacing: -.05em; line-height: .9; font-weight: 400; }
.site-tpl--cinema .site-hero-brand { letter-spacing: .22em; }
.site-tpl--cinema .site-h2 { font-size: var(--site-fs-eyebrow); letter-spacing: .22em; text-transform: uppercase; opacity: .7; color: var(--cf-text); font-family: var(--site-body-font); }
.site-tpl--cinema .site-desc { font-size: var(--site-fs-body); line-height: 1.7; max-width: 62ch; }
/* the agent-accent hairline — Cinema's required structural accent touch (house rule 2) */
.site-tpl--cinema .site-section { border-top: 1px solid color-mix(in srgb, var(--site-primary) 30%, var(--cf-hairline)); }
.site-tpl--cinema .site-hero { border-top: 0; }
/* the Dover mosaic — 2-col grid, first tile spans 2 rows. Reuses the SAME gallery DOM
   (App.justifiedGallery) every other template uses; .site-mosaic just overrides the flex
   layout the .cstatus-gal-item inline styles set (flexGrow/flexBasis are inert once the
   parent is a CSS grid, so nothing needs to change in sitepage.js). */
/* FABLE judge 2026-09-13 (live, real photos): grid-auto-rows:1fr collapsed to the
   image's own intrinsic height with no definite row track to fill, leaving a large blank
   gap under the spanning tile — a definite px row track fixes it deterministically. */
.site-tpl--cinema .site-mosaic.cstatus-gal { display: grid; grid-template-columns: repeat(2, 1fr); grid-auto-rows: 260px; gap: 10px; }
.site-tpl--cinema .site-mosaic.cstatus-gal::after { content: none; }
.site-tpl--cinema .site-mosaic .cstatus-gal-item { height: 100%; min-height: 0; }
/* live judge (real photos): an in-flow percentage height on the <img> does not resolve
   against a STRETCHED grid item's used height (a classic CSS quirk — only a height set
   directly on the parent counts) so the photo rendered at its own intrinsic aspect ratio
   instead of filling the 2-row-tall cell, leaving a dark gap. Absolute-positioning the
   image against the item's own already-`position:relative` box (its base .cstatus-gal-item
   rule) sidesteps that: percentage sizing for an ABSOLUTELY positioned element resolves
   against the nearest positioned ancestor's definite height, which a grid item's own
   grid-area height IS. */
/* live judge again: a LATER, same-specificity rule elsewhere (.cstatus-gal-open{position:
   relative}) wins the cascade over the base .cstatus-gal-open{position:absolute} by source
   order, which broke the img's own containing block for percentage sizing. A higher-
   specificity re-assertion, scoped to this mosaic only, settles it unconditionally. */
.site-tpl--cinema .site-mosaic .cstatus-gal-item .cstatus-gal-open { position: absolute; inset: 0; width: 100%; height: 100%; }
.site-tpl--cinema .site-mosaic .cstatus-gal-item img { position: absolute; inset: 0; width: 100%; height: 100%; }
.site-tpl--cinema .site-mosaic .cstatus-gal-item:first-child { grid-row: span 2; }
@media (max-width: 720px) { .site-tpl--cinema .site-mosaic.cstatus-gal { grid-auto-rows: 170px; } }
/* nav — dark-solid variant (the shared component defaults to a LIGHT solid bar, which is
   wrong once scrolled on a dark-ground template) */
.site-tpl--cinema .site-nav.site-nav--solid { background: var(--cin-b); color: #EDEDEA; box-shadow: 0 1px 0 rgba(255,255,255,.12); }
/* floor-plan tabs — dark-safe (the default rule reads --site-ink/--site-paper, which on
   Cinema mean "near-black"/undefined; a light pill on the dark ground reads better anyway) */
.site-tpl--cinema .site-fp-tab { border-color: var(--cf-hairline); color: var(--cf-text-3); }
.site-tpl--cinema .site-fp-tab.is-active { background: #EDEDEA; color: #141514; border-color: #EDEDEA; }
/* the persistent pill — next open house, else "Request a private showing". Fades in after
   the hero panel (research §6); the fade itself is skipped under reduced motion. */
.site-cinema-pill {
  position: fixed; left: clamp(16px, 3vw, 28px); bottom: clamp(16px, 3vw, 28px); z-index: 25;
  min-height: 44px; padding: 0 18px; border-radius: 999px; border: 1px solid rgba(255,255,255,.22);
  background: rgba(20,21,20,.72); color: #fff; font-size: 12.5px; letter-spacing: .06em;
  cursor: pointer; backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  opacity: 0; transform: translateY(8px); pointer-events: none;
  transition: opacity .4s ease, transform .4s ease;
}
.site-cinema-pill.is-visible { opacity: 1; transform: none; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .site-cinema-pill { transition: none; } }
@media (min-width: 721px) { .site-tpl--cinema .site-stickybar { display: none; } }

/* ============================================================================
   PLAN-PREMIUM-2026-09-13 (N4 slice2) — Family C "Swiss photo wall": PRESS (recreates
   Jag/Dax). Pure white ground, one typeface (Inter), giant media list-rows, a CSS-columns
   photo wall, and a sentence-by-sentence text reveal on the description. Skips Jag's
   rotating circular badge (research §6: "charming… will look dated in 18 months").
   ============================================================================ */
.site-tpl--press {
  --site-paper: #FFFFFF; --site-ink: #0A0A0A;
  --cf-ground: #FFFFFF; --cf-card: #FAFAFA; --cf-text: #0A0A0A;
  --cf-text-2: rgba(10,10,10,.7); --cf-text-3: rgba(10,10,10,.52); --cf-text-4: rgba(10,10,10,.32);
  --cf-hairline: #E4E4E4; --cf-hairline-hover: #c9c9c9;
  --cf-scrim: linear-gradient(180deg, rgba(0,0,0,.34) 0%, rgba(0,0,0,0) 34%, rgba(0,0,0,.5) 100%);
  --site-fs-display: clamp(44px, 8vw, 108px); --site-fs-h2: 25px; --site-fs-h3: 20px;
  --site-fs-eyebrow: 12px; --site-fs-body: 16px;
}
.site-page.site-tpl--press { max-width: none; padding: 0; }
.site-tpl--press .site-section { padding-block: clamp(56px, 8vw, 110px); box-sizing: border-box; }
.site-tpl--press .site-section > * { max-width: min(1180px, 92vw); margin-inline: auto; }
.site-tpl--press .site-hero { aspect-ratio: auto; height: 100svh; min-height: 560px; max-height: none; border-radius: 0; }
/* live judge (real photos): Press's page ink is near-black (correct on the white page
   ground) but .site-hero-text inherits --cf-text by default, which made the HERO title
   render near-black over a photo too — illegible under the scrim. The hero always sits on
   a photo (or its dark empty-state gradient), so its text is white regardless of the
   template's page-ink color; Rail has the identical photo-hero + dark-ink combination. */
.site-tpl--press .site-hero-text, .site-tpl--rail .site-hero-text { color: #fff; }
.site-tpl--press .site-hero-text { padding: 0 clamp(20px, 5vw, 64px) 22vh; width: 100%; box-sizing: border-box; }
.site-tpl--press .site-hero-title { text-transform: none; font-weight: 500; letter-spacing: -.045em; line-height: .96; font-size: var(--site-fs-display); }
/* numbered section heads — "(01) Photos" — Jag's own parenthetical numbering */
.site-tpl--press .site-section { counter-increment: vvt-sec; }
.site-tpl--press .site-h2::before { content: "(" counter(vvt-sec, decimal-leading-zero) ") "; opacity: .5; }
.site-tpl--press .site-h2 { font-size: var(--site-fs-h2); font-weight: 500; letter-spacing: -.01em; text-transform: none; color: var(--site-ink); opacity: 1; font-family: var(--site-body-font); }
.site-tpl--press .site-desc { font-size: var(--site-fs-body); line-height: 1.62; max-width: 66ch; }
/* the sentence-by-sentence text reveal (R.description, press only) */
.site-revealtext span { color: #C9C9C9; transition: color .45s ease; }
.site-revealtext span.in { color: var(--site-ink); }
@media (prefers-reduced-motion: reduce) { .site-revealtext span { transition: none; color: var(--site-ink); } }
/* the photo wall — CSS columns, no JS. Reuses the SAME gallery DOM every other template
   uses (App.justifiedGallery); overrides its flex layout the same way Cinema's mosaic does. */
.site-tpl--press .site-wall.cstatus-gal { display: block; column-count: 3; column-gap: 10px; }
.site-tpl--press .site-wall.cstatus-gal::after { content: none; }
.site-tpl--press .site-wall .cstatus-gal-item { display: block; width: 100%; height: auto; margin-bottom: 10px; break-inside: avoid; border-radius: 0; }
@media (max-width: 900px) { .site-tpl--press .site-wall.cstatus-gal { column-count: 2; } }
@media (max-width: 560px) { .site-tpl--press .site-wall.cstatus-gal { column-count: 1; } }
/* the media list-rows (R.mediaRows, press only) — a reusable component (research §6: "wanted
   elsewhere in the Hub too"), each row a progressive-disclosure toggle. */
.site-mediarows { display: flex; flex-direction: column; }
.site-mediarow { border-top: 1px solid var(--cf-hairline); }
.site-mediarow:first-child { border-top: 0; }
.site-mediarow-btn { width: 100%; display: flex; align-items: baseline; justify-content: space-between; gap: 24px; padding: clamp(20px, 3vw, 36px) 0; background: none; border: 0; cursor: pointer; color: inherit; text-align: left; min-height: 44px; }
.site-mediarow-t { font-size: clamp(30px, 5.4vw, 68px); font-weight: 500; letter-spacing: -.04em; transition: transform .5s cubic-bezier(.16,1,.3,1); }
.site-mediarow-btn:hover .site-mediarow-t, .site-mediarow-btn[aria-expanded="true"] .site-mediarow-t { transform: translateX(10px); }
.site-mediarow-v { font-size: 13px; letter-spacing: .12em; text-transform: uppercase; opacity: .55; white-space: nowrap; }
.site-mediarow-panel { padding-bottom: clamp(20px, 3vw, 36px); }
@media (prefers-reduced-motion: reduce) { .site-mediarow-t { transition: none; } }
/* the mega closing "Contact" (research §6 — "Contact" at up to 150px) */
.site-tpl--press .site-cta, .site-tpl--press .site-inquiry {
  background: none; color: var(--site-ink); border-radius: 0; text-align: left; padding-block: clamp(56px, 8vw, 110px);
}
.site-tpl--press .site-cta .site-h2, .site-tpl--press .site-inquiry .site-h2 {
  color: var(--site-ink); font-size: clamp(48px, 10vw, 120px); font-weight: 500; letter-spacing: -.03em; text-transform: none; margin-bottom: 20px; opacity: 1;
}
.site-tpl--press .site-cta .site-h2::before, .site-tpl--press .site-inquiry .site-h2::before { opacity: .3; }
.site-tpl--press .site-cta-sub, .site-tpl--press .site-inquiry-sub { margin: 0 0 20px; color: var(--cf-text-2); text-align: left; max-width: 60ch; }
.site-tpl--press .site-cta-row { justify-content: flex-start; }
@media (min-width: 721px) { .site-tpl--press .site-stickybar { display: none; } }

/* ============================================================================
   PLAN-PREMIUM-2026-09-13 (N4 slice2) — Family D "fixed identity rail": RAIL (recreates
   Stak/Cobi). A fixed 96px rail (agent brand + a rotated address + a status chip) keeps the
   listing on screen at every scroll position with no header weight; un-rotates into a plain
   64px top bar under 1024px — Bren's rotated-address/photo collision (research §6) never
   happens here because the address simply un-rotates instead of staying vertical.
   ============================================================================ */
.site-tpl--rail {
  --site-paper: #FFFFFF; --site-ink: #17140F;
  --cf-ground: #FFFFFF; --cf-card: #F6F4EF; --cf-text: #17140F;
  --cf-text-2: rgba(23,20,15,.7); --cf-text-3: rgba(23,20,15,.54); --cf-text-4: rgba(23,20,15,.34);
  --cf-hairline: rgba(23,20,15,.14); --cf-hairline-hover: rgba(23,20,15,.28);
  --site-fs-display: clamp(34px, 5vw, 52px); --site-fs-h2: 12px; --site-fs-h3: 20px;
  --site-fs-eyebrow: 11px; --site-fs-body: 16px;
}
.site-page.site-tpl--rail { max-width: none; padding: 0; }
.site-tpl--rail .site-section { padding-block: clamp(56px, 8vw, 110px); box-sizing: border-box; }
.site-tpl--rail .site-section > * { max-width: min(1100px, 92vw); margin-inline: auto; }
.site-tpl--rail .site-hero { max-width: min(1100px, 92vw); margin-inline: auto; }
.site-tpl--rail .site-hero-title { font-size: var(--site-fs-display); font-weight: 700; letter-spacing: -.015em; }
.site-tpl--rail .site-h2 { font-size: var(--site-fs-eyebrow); letter-spacing: .2em; text-transform: uppercase; color: var(--site-ink); opacity: .7; font-family: var(--site-body-font); }
/* the rail itself — page furniture, not a section; Rail renders NO shared nav bar (see the
   isNavTpl comment in propertysite.js — live judge found the fixed nav + the rail's own
   mobile top bar overlapping), so the sticky agent contact bar stays visible at every width
   here, exactly like estate/lifestyle/modern (no rule hides it for rail). Desktop: fixed
   96px column, content pushed right via padding on .site-page (selector matched to
   .site-page.site-tpl--rail's own specificity — live judge: a lower-specificity
   `.site-tpl--rail{padding-left:96px}` lost the cascade to that rule's `padding:0`).
   Mobile (<1024px): a plain static top bar, in normal document flow (no fixed nav to clash
   with); the address un-rotates to horizontal. */
.site-rail { background: var(--site-primary); color: var(--site-on-primary, #fff); box-sizing: border-box; }
@media (min-width: 1024px) {
  .site-page.site-tpl--rail { padding-left: 96px; }
  .site-rail {
    position: fixed; top: 0; left: 0; width: 96px; height: 100vh; z-index: 25;
    display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 24px 0;
  }
  .site-rail-addr { writing-mode: vertical-rl; text-orientation: mixed; letter-spacing: .22em; text-transform: uppercase; font-size: 13px; font-weight: 600; }
}
@media (max-width: 1023px) {
  .site-rail { position: static; width: 100%; min-height: 56px; display: flex; flex-direction: row; align-items: center; justify-content: space-between; padding: 10px 16px; }
  .site-rail-addr { writing-mode: horizontal-tb; letter-spacing: .06em; text-transform: uppercase; font-size: 11.5px; font-weight: 600; }
}
.site-rail-brand { display: flex; align-items: center; }
.site-rail-logo { max-width: 64px; max-height: 40px; object-fit: contain; }
.site-rail-wordmark { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }
.site-rail-chip { font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; padding: 5px 10px; border-radius: 999px; background: rgba(255,255,255,.2); }
.site-rail-chip--ok { background: rgba(74,222,128,.28); }
.site-rail-chip--warn { background: rgba(251,191,36,.28); }
.site-rail-chip--danger { background: rgba(248,113,113,.28); }
.site-rail-chip--muted { background: rgba(255,255,255,.16); }
.site-rail-chip--info { background: rgba(147,197,253,.28); }

/* ESTATE — dark, dramatic, gold accent (the trophy template) */
/* SPEC-DHCLIENT D4 — estate used to hand-roll its OWN dark palette (background #0d0d0f,
   --text-soft #b8b2a6, --border rgba(255,255,255,.14) etc) because it was the only
   template that was dark; every OTHER template was light. Now .site-page itself (above)
   converges the whole surface onto the D1 --cf- tokens, so all of that — the container
   background/color, the four status-pill relights, and every secondary-text/border
   color restate — is exactly what the base already provides. Only genuinely
   estate-specific bits remain: the boxed shape and the gold agent-accent touches. */
.site-tpl--estate { padding: 0 22px 12px; border-radius: 16px; }
.site-tpl--estate .site-h2 { color: var(--site-accent, #d4af37); }
.site-tpl--estate .sh-price::after { content: ''; display: block; width: 56px; height: 2px; background: var(--site-accent, #d4af37); margin: 14px auto 0; }
.site-tpl--estate .sh-ic { color: var(--site-accent, #d4af37); }
.site-tpl--estate .site-agent-eyebrow { color: var(--site-accent, #d4af37); }
/* LIFESTYLE — warm, rounded, soft, pill highlights */
.site-tpl--lifestyle .site-hero { border-radius: 18px; }
.site-tpl--lifestyle .site-desc { line-height: 1.8; }
.site-tpl--lifestyle .site-h2 { letter-spacing: .1em; }
.site-tpl--lifestyle .site-highlights { display: flex; flex-wrap: wrap; gap: 8px; grid-template-columns: none; }
.site-tpl--lifestyle .site-hl { background: var(--bg-2); border-radius: 999px; padding: 8px 15px; align-items: center; }
/* MODERN — gallery-white, swiss grid, left-aligned, tabular price */
.site-tpl--modern .site-h2 { color: var(--text); letter-spacing: .18em; }
.site-tpl--modern .sh-price { font-variant-numeric: tabular-nums; font-family: var(--site-body-font); letter-spacing: var(--ls-body); }
.site-tpl--modern .site-section { border-top: 1.5px solid var(--text); padding-top: 16px; }
.site-tpl--modern .site-stathero, .site-tpl--modern .sh-top, .site-tpl--modern .sh-stats { text-align: left; justify-content: flex-start; }
.site-tpl--modern .sh-cell:first-child { padding-left: 0; }
/* SPEC221 — template picker (in the listing-site editor) */
.site-tplpick { display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px; margin: 6px 0 2px; }
.site-tplcard { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; text-align: left; padding: 9px 10px; border: 1px solid var(--border); border-radius: 11px; background: var(--panel); cursor: pointer; transition: border-color .12s, box-shadow .12s; }
.site-tplcard:hover { border-color: var(--accent); }
.site-tplcard.is-active { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft, rgba(14,124,90,.16)); }
.site-tplcard-sw { width: 100%; height: 26px; border-radius: 6px; border: 1px solid rgba(0,0,0,.06); }
.site-tplcard-n { font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--text); }
.site-tplcard-b { font-size: var(--fs-micro); color: var(--muted); line-height: 1.3; }
.site-tplsw--editorial { background: linear-gradient(120deg, #FAF9F6 60%, #c2410c); }
.site-tplsw--boutique { background: linear-gradient(120deg, #EDEAE0 60%, #C9C0A8); }
.site-tplsw--estate { background: linear-gradient(120deg, #141414 60%, #d4af37); }
.site-tplsw--lifestyle { background: linear-gradient(120deg, #FAF0E6 58%, #9CAF88); }
.site-tplsw--modern { background: linear-gradient(120deg, #ffffff 60%, #0e2a47); }
.site-tplsw--cinema { background: linear-gradient(120deg, #202422 55%, #E5E1D6); }
.site-tplsw--press { background: linear-gradient(120deg, #ffffff 55%, #0A0A0A); }
.site-tplsw--rail { background: linear-gradient(120deg, #ffffff 55%, #8b1e3f); }
/* SPEC221 Phase B — compliance footer + sticky agent contact bar */
.site-foot-agent { font-weight: var(--fw-semibold); color: var(--text); }
.site-foot-eho { font-size: var(--fs-micro); margin-top: 4px; opacity: .82; }
.site-foot-credit { margin-top: 7px; }
.site-stickybar { position: sticky; bottom: 14px; z-index: 6; display: flex; align-items: center; justify-content: space-between; gap: 12px; max-width: 640px; margin: 20px auto 8px; padding: 9px 11px 9px 14px; background: var(--panel); border: 1px solid var(--border); border-radius: 999px; box-shadow: 0 12px 34px rgba(0,0,0,.18); }
@supports ((backdrop-filter: blur(6px)) or (-webkit-backdrop-filter: blur(6px))) { .site-stickybar { background: color-mix(in srgb, var(--panel) 88%, transparent); backdrop-filter: saturate(1.3) blur(8px); -webkit-backdrop-filter: saturate(1.3) blur(8px); } }   /* SAFPOLISH 2026-08-25 D4 — gate widened to accept the -webkit- only form (older Safari), property paired inside */
.site-stickybar-l { display: flex; align-items: center; gap: 10px; min-width: 0; }
.site-stickybar-pic { width: 36px; height: 36px; border-radius: 999px; background-size: cover; background-position: center; flex: 0 0 auto; }
.site-stickybar-txt { display: flex; flex-direction: column; min-width: 0; }
.site-stickybar-name { font-weight: var(--fw-bold); font-size: 13.5px; line-height: var(--lh-tight); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.site-stickybar-sub { font-size: 11.5px; color: var(--text-soft); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.site-stickybar-r { display: flex; gap: 8px; flex: 0 0 auto; }
.site-tpl--estate .site-stickybar { background: rgba(20,20,22,.92); border-color: rgba(255,255,255,.16); }
.site-tpl--estate .site-stickybar-name { color: #fff; }
.site-tpl--estate .site-stickybar-sub { color: #b8b2a6; }
@media (max-width: 520px) { .site-stickybar { bottom: 8px; padding: 8px 9px 8px 11px; } .site-stickybar-sub { display: none; } .site-stickybar-calllbl { display: none; } }
/* SPEC221 Phase C — area research: walk badge, nearby chips, schools (amenity/lifestyle only) */
.site-walk { display: inline-flex; align-items: baseline; gap: 8px; margin-bottom: 12px; }
.site-walk-n { font-size: 26px; font-weight: var(--fw-black); color: var(--site-primary); line-height: 1; }
.site-walk-l { font-size: var(--fs-meta); font-weight: var(--fw-bold); letter-spacing: var(--ls-label); color: var(--text-soft); text-transform: uppercase; }
.site-nearby { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.site-nearby-chip { display: inline-flex; align-items: center; gap: 6px; padding: 7px 13px; border: 1px solid var(--border); border-radius: 999px; font-size: var(--fs-caption); }
.site-nearby-chip .icon { color: var(--site-primary); }
.site-subh { font-size: var(--fs-meta); font-weight: var(--fw-bold); letter-spacing: .1em; text-transform: uppercase; color: var(--text-soft); margin: 18px 0 8px; }
.site-schools { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 6px 18px; }
.site-school { display: flex; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.site-school-n { font-weight: var(--fw-semibold); font-size: var(--fs-body); }
.site-school-m { color: var(--text-soft); font-size: 12.5px; white-space: nowrap; }
.site-tpl--estate .site-nearby-chip { border-color: rgba(255,255,255,.16); }
.site-tpl--estate .site-school { border-color: rgba(255,255,255,.12); }
.site-tpl--estate .site-walk-n { color: var(--site-accent, #d4af37); }
/* SPEC298 — "Walkability" is an estimate, labelled as such (honesty + no Walk Score trademark) */
.site-walk { flex-wrap: wrap; }
.site-walk-est { flex-basis: 100%; font-size: var(--fs-micro); color: var(--text-soft); font-style: italic; margin-top: 2px; }
/* SPEC-SITEOPEN 2026-09-09 — Phase E: the held cinematic "welcome home" title card + the vivid
   agent contact block. The open is a SIBLING rendered just before .site-hero (never inside it),
   fixed full-viewport, CSS-only hold-then-dissolve, and fully inert (pointer-events:none) so it
   can never block a real click even mid-animation. */
.ps-open { position: fixed; inset: 0; z-index: 60; display: flex; align-items: flex-end; overflow: hidden;
  background: linear-gradient(150deg, var(--site-primary, #0e7c5a), rgba(0,0,0,.55)); pointer-events: none;
  animation: ps-open-seq 2.4s 1 both; }
.ps-open-img { position: absolute; inset: 0; background-size: cover; background-position: center; }
.ps-open-scrim { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,.15) 0%, rgba(0,0,0,.55) 60%, rgba(0,0,0,.82) 100%); }
.ps-open-text { position: relative; padding: 8vh 6vw; color: #fff; width: 100%; }
.ps-open-street { font-family: var(--site-display-font, var(--site-font, 'Fraunces', Georgia, serif)); font-size: clamp(30px, 6vw, 58px); font-weight: var(--fw-black); line-height: 1.05; text-shadow: 0 3px 22px rgba(0,0,0,.5); }
.ps-open-city { font-size: clamp(15px, 2.2vw, 20px); margin-top: 8px; opacity: .92; }
.ps-open-credit { font-size: 12.5px; letter-spacing: .12em; text-transform: uppercase; font-weight: var(--fw-bold); margin-top: 16px; opacity: .85; }
@keyframes ps-open-seq { 0%, 74% { opacity: 1; transform: scale(1); } 100% { opacity: 0; transform: scale(1.045); } }
@media (prefers-reduced-motion: reduce) { .ps-open { display: none; } }   /* no animation → the real, static hero underneath is all a visitor ever sees */

/* the vivid contact block: bigger round headshots + a real ≥44px tap target on every action,
   in BOTH the sticky bar and the full agent card (Snapper's "very vivid" detail). */
.ps-agent-vivid-pic.site-agent-pic { width: 108px; height: 108px; }
.ps-agent-vivid-pic.site-stickybar-pic { width: 44px; height: 44px; border: 2px solid var(--site-accent, transparent); }
.ps-agent-vivid-btn { min-height: 44px; min-width: 44px; display: inline-flex; align-items: center; justify-content: center; }
@media (max-width: 520px) { .ps-agent-vivid-btn { padding-left: 12px; padding-right: 12px; } }

/* SPEC298 — the branded, money-free "Request a private showing" inquiry form (agent accent) */
.site-inquiry { max-width: 620px; }
.site-inquiry-sub { font-size: 15px; line-height: 1.6; color: var(--text-soft); margin: 4px 0 16px; }
.site-inquiry-form { display: flex; flex-direction: column; gap: 12px; }
.site-inquiry-form .mkt-field { display: flex; flex-direction: column; gap: 5px; }
.site-inquiry-form .input { width: 100%; }
.site-hp { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; opacity: 0; }
.site-inquiry-actions { margin-top: 4px; }
.site-inquiry-send { background: var(--site-primary); color: var(--site-on-primary, #fff); border: 0; }
.site-inquiry-note { font-size: var(--fs-caption); margin-top: 8px; min-height: 1em; }
.site-inquiry-note.is-ok { color: var(--site-primary); }
.site-inquiry-note.is-err { color: var(--danger, #dc2626); }
/* SPEC298 — human-verify: "Auto-filled ✓ confirm" chip in the editor + the review panel */
.site-verify-chip { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; font-size: var(--fs-micro); padding: 3px 8px; border-radius: 999px; cursor: pointer; white-space: nowrap; }
.site-verify-chip:hover { background: #fde68a; }
.site-gate { display: none; margin-top: 10px; padding: 10px 12px; border: 1px solid #fcd34d; background: #fffbeb; border-radius: 10px; }
.site-gate.is-open { display: block; }
.site-gate-h { font-size: var(--fs-caption); font-weight: var(--fw-bold); color: #92400e; display: flex; align-items: center; gap: 4px; }
.site-gate-l { font-size: var(--fs-meta); color: #92400e; margin-top: 4px; }
.site-gate-l--soft { color: #a16207; }
/* SPEC298 — live-state pill in the editor footer */
.site-livepill { font-size: var(--fs-meta); font-weight: var(--fw-bold); padding: 4px 10px; border-radius: 999px; margin-right: auto; }
.site-livepill.is-live { background: #dcfce7; color: #166534; }
.site-livepill.is-draft { background: var(--bg-2, #f1f5f9); color: var(--text-soft, #64748b); }
.site-editor { display: flex; gap: 18px; flex-wrap: wrap; align-items: flex-start; }
/* SPEC222 — Mind Map: a free-form spatial board (positioned nodes + an SVG link layer) */
.mm-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.mm-hint { font-size: var(--fs-meta); color: var(--muted); }
.mm-wrap { position: relative; margin-top: 14px; height: 70vh; min-height: 420px; overflow: auto; border: 1px solid var(--border); border-radius: 14px; background: radial-gradient(circle, var(--border) 1px, transparent 1px) 0 0 / 26px 26px, var(--bg-2); -webkit-overflow-scrolling: touch; }
/* CONSOL 2026-07-12 — Canvas camera model: fixed viewport + one transformed world layer */
.mm-board { position: relative; margin-top: 14px; height: 72vh; min-height: 440px; overflow: hidden; touch-action: none; border: 1px solid var(--border); border-radius: 14px; background: radial-gradient(circle, var(--border) 1px, transparent 1px) 0 0 / 26px 26px, var(--bg-2); cursor: default; }
.mm-board.mm-space { cursor: grab; }
.mm-board.mm-panning { cursor: grabbing; }
.mm-world { position: absolute; top: 0; left: 0; transform-origin: 0 0; will-change: transform; }
.mm-links { position: absolute; top: 0; left: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; }
.mm-link { stroke: var(--muted); stroke-width: 2; fill: none; pointer-events: none; }
.mm-arrowhead { fill: var(--muted); }
.mm-temp { stroke: var(--accent); stroke-width: 2; fill: none; stroke-dasharray: 5 4; pointer-events: none; }
.mm-zbtn { min-width: 30px; font-weight: var(--fw-bold); }
.mm-zlabel { min-width: 52px; font-variant-numeric: tabular-nums; }
.mm-tools-gap { flex: 1 1 auto; }
.mm-color { width: 15px; height: 15px; border-radius: 50%; border: 1px solid var(--border); background: var(--mm-accent, var(--accent)); cursor: pointer; padding: 0; flex: 0 0 auto; }
.mm-node--drop { box-shadow: 0 0 0 3px var(--accent) !important; }
/* CONSOL — Canvas media nodes (photo / video-embed) + a SE resize grip */
.mm-node--image, .mm-node--embed { overflow: hidden; }
.mm-img { width: 100%; display: block; border-radius: 0 0 9px 9px; }
.mm-embed { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; border-radius: 0 0 9px 9px; overflow: hidden; }
.mm-embed iframe { width: 100%; height: 100%; border: 0; display: block; }
.mm-video { width: 100%; display: block; border-radius: 0 0 9px 9px; background: #000; max-height: 60vh; }

/* CONSOL-11/12 — deliverable guide (How-To lens) + QC checklist */
.dg-card { padding: 0; overflow: hidden; }
.dg-head { width: 100%; display: flex; align-items: center; gap: 8px; padding: 12px 14px; background: none; border: 0; cursor: pointer; text-align: left; font: inherit; color: inherit; }
.dg-head:hover { background: var(--bg-2); }
.dg-caret { color: var(--muted); font-size: var(--fs-meta); width: 12px; flex: none; }
.dg-title { font-weight: var(--fw-bold); font-size: var(--fs-body); }
.dg-badge { font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .03em; color: var(--accent); background: var(--accent-soft); padding: 2px 7px; border-radius: 999px; flex: none; }
.dg-count { font-size: 11.5px; margin-left: auto; flex: none; }
.dg-detail { padding: 2px 16px 16px; border-top: 1px solid var(--border); }
.dg-sec-h { font-weight: var(--fw-black); font-size: var(--fs-meta); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--muted); margin: 12px 0 6px; }
.dg-howto { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 7px; }
.dg-howto li { font-size: var(--fs-caption); line-height: var(--lh-normal); }
.dg-qc { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; }
.dg-qc li { font-size: var(--fs-caption); line-height: 1.45; padding-left: 22px; position: relative; }
.dg-qc li::before { content: '✓'; position: absolute; left: 0; top: 0; color: var(--accent); font-weight: var(--fw-black); }
.dg-watch { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; }
.dg-watch li { font-size: 12.5px; line-height: 1.45; padding-left: 22px; position: relative; color: var(--text-2, inherit); }
.dg-watch li::before { content: '⚠'; position: absolute; left: 0; top: 0; }

/* SOPQC-4 — the "📐 Compare against the standard" example-triad strip. Shared by
   deliverableDetail() (Learn → By Deliverable) and the ⚡ QC gate header (qc.js) — a
   quiet --bg-2 reference panel, same family as .qc-brief, deliberately NOT the
   .qc-gate gradient/animation treatment since it's reference material, not a payoff. */
.dg-examples { background: var(--bg-2); border: 1px solid var(--border); border-radius: 12px; padding: 10px 12px; margin: 10px 0; display: flex; flex-direction: column; gap: 7px; }
.dg-examples-head { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .03em; color: var(--muted); }
.dg-examples-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: 12.5px; line-height: 1.45; }
.dg-examples-link { font-weight: var(--fw-semibold); color: var(--accent); }
.dg-examples-why { color: var(--muted); font-style: italic; }

/* SOPQC-8 — "🎯 The standard" header block: the optional one-page style-guide summary
   (purpose, runtime, hook, music, required moments) that sits ABOVE the how-to steps
   when a deliverable carries a `standard`. An accent-left callout — this IS the
   headline of the card, unlike the quiet --bg-2 .dg-examples reference strip below it. */
.dg-standard { background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border)); border-left: 3px solid var(--accent); border-radius: 10px; padding: 10px 12px; margin-bottom: 14px; display: flex; flex-direction: column; gap: 7px; }
.dg-standard-head { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .03em; color: var(--accent); }
.dg-standard-purpose { font-size: 13.5px; line-height: var(--lh-normal); font-weight: var(--fw-semibold); }
.dg-standard-facts { display: flex; flex-direction: column; gap: 3px; }
.dg-standard-fact { font-size: 12.5px; line-height: 1.45; }
.dg-standard-fact-label { font-weight: var(--fw-bold); }
.dg-standard-moments-head { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .03em; color: var(--muted); margin-top: 2px; }
.dg-standard-moments { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 3px; }
.dg-standard-moments li { font-size: 12.5px; line-height: 1.45; }

.qc-check { display: flex; gap: 9px; align-items: flex-start; padding: 5px 2px; font-size: var(--fs-caption); line-height: 1.45; cursor: pointer; }
.qc-check input { margin-top: 2px; flex: none; width: 16px; height: 16px; accent-color: var(--accent); }

/* QCGAME — gamified, kinetic quality control (Persona-5 "movement", Deep Harbor palette) */
.qc-strip { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 10px 0 8px; }
.qc-strip-title { font-family: var(--font-serif); font-size: var(--fs-lg); font-weight: var(--fw-semibold); }
.qc-strip-r { display: flex; align-items: center; gap: 12px; }
.qc-ring-wrap { position: relative; width: 52px; height: 52px; flex: none; }
.qc-ring { width: 52px; height: 52px; display: block; }
.qc-ring-track { fill: none; stroke: var(--bg-2); stroke-width: 4; }
.qc-ring-bar { fill: none; stroke: var(--accent); stroke-width: 4; stroke-linecap: round; transform: rotate(-90deg); transform-origin: 24px 24px; transition: stroke-dashoffset .5s var(--ease-spring), stroke .3s var(--ease); }
.qc-ring-label { position: absolute; inset: 0; display: grid; place-items: center; font-size: var(--fs-meta); font-weight: var(--fw-black); font-variant-numeric: tabular-nums; color: var(--text); }
.qc-streak { display: inline-flex; align-items: center; gap: 3px; border: 1px solid var(--border); background: var(--panel); border-radius: 999px; padding: 4px 10px 4px 8px; cursor: pointer; font: inherit; line-height: 1; filter: grayscale(1) opacity(.55); transition: filter .2s var(--ease), transform .2s var(--ease-spring); }
.qc-streak.on { filter: none; border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); background: var(--warn-soft); }
.qc-streak:hover { transform: translateY(-1px); }
.qc-streak-n { font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }
.qc-sound { border: 1px solid var(--border); background: var(--panel); border-radius: 999px; width: 30px; height: 30px; padding: 0; cursor: pointer; font-size: var(--fs-body); line-height: 1; opacity: .8; transition: opacity .15s var(--ease), transform .15s var(--ease-spring); }
.qc-sound:hover { opacity: 1; transform: translateY(-1px); }
.qc-sound[aria-pressed="true"] { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: var(--accent-soft); }
.qc-meter { display: flex; gap: 4px; margin-bottom: 10px; }
.qc-seg { flex: 1; height: 8px; border-radius: 999px; background: var(--bg-2); overflow: hidden; }
.qc-seg > i { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--accent); transition: width .45s var(--ease-spring); }
.qc-seg.done > i { background: linear-gradient(90deg, var(--accent), var(--ok)); }
.qc-list { display: block; }
.qc-sec-h { display: flex; align-items: center; gap: 8px; margin-top: 12px; transition: color .2s var(--ease); }
.qc-sec-h.is-cleared { color: var(--ok-ink); }
.qc-sec-cleared { font-size: 10.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .03em; color: var(--ok-ink); background: var(--ok-soft); padding: 2px 8px; border-radius: 999px; }
/* SOPQC-1 — severity: "N must-fix · N judgment" open-count chip (section header)
   + a small ● dot marking a RED must-fix row. Additive-only classes — absent from
   every checklist that never sets it.sev. */
.qc-sec-sev { font-size: 10.5px; font-weight: var(--fw-bold); color: var(--muted); background: var(--bg-2); padding: 2px 8px; border-radius: 999px; white-space: nowrap; }
.clp-sev-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--danger); margin-top: 6px; box-shadow: 0 0 0 2px var(--danger-soft); }
.qc-row { opacity: 0; transform: translateX(-10px) translateY(6px); animation: qcRowIn .34s var(--ease-spring) both; animation-delay: calc(min(var(--i, 0), 8) * 42ms); border-radius: 8px; }
@keyframes qcRowIn { to { opacity: 1; transform: none; } }
.qc-row--just { animation: qcFlash .5s var(--ease) forwards; }
@keyframes qcFlash { 0% { background: color-mix(in srgb, var(--accent) 22%, transparent); } 100% { background: transparent; } }
.qc-check input:checked ~ span { text-decoration: line-through; color: var(--muted); opacity: .7; transition: color .18s var(--ease), opacity .18s var(--ease); }
/* rich checklist rows (hint subtitle + jump-to action) */
.qc-row--rich { display: flex; align-items: flex-start; gap: 8px; }
.clp-textwrap { display: flex; flex-direction: column; }
.clp-hint { font-size: 11.5px; color: var(--muted); margin-top: 1px; line-height: 1.4; }
.qc-row--rich.is-done .clp-label { text-decoration: line-through; color: var(--muted); opacity: .7; }
.clp-action { flex: none; margin-top: 1px; }
.clp-topper { margin-bottom: 12px; }
/* SOPQC-3 — the shooter's structured brief atop the QC gate (job.brief; App.jobBriefFields).
   A quiet reference card, not a gate — no animation, no gradient, so it never reads like a
   payoff state. Absent entirely when the job has no brief. */
.qc-brief { background: var(--bg-2); border: 1px solid var(--border); border-radius: var(--radius-4); padding: 10px 12px; margin-bottom: 12px; }   /* DHSWEEP W9 — was a literal 12px, exactly --radius-4's value; zero-diff token wire */
.qc-brief-head { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .03em; color: var(--muted); margin-bottom: 5px; }
.qc-brief-row { font-size: 12.5px; line-height: 1.45; }
.qc-brief-label { font-weight: var(--fw-bold); color: var(--text); }
.qc-brief-link { display: inline-block; margin-top: 5px; font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--accent); }
/* SOPQC-6 — the escalate row: a quiet release valve, not a gate action, so it sits
   ghost-styled below the brief/examples rather than competing with the checklist. */
.qc-escalate-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 2px 0 12px; }
.qc-verdict { transform: translateY(12px); opacity: 0; animation: qcUp .3s var(--ease-spring) forwards; border-radius: 12px; padding: 11px 14px; margin: 8px 0; font-size: var(--fs-caption); font-weight: var(--fw-semibold); }
@keyframes qcUp { to { transform: none; opacity: 1; } }
.qc-verdict--ok { background: var(--ok-soft); color: var(--ok-ink); box-shadow: inset 3px 0 0 var(--ok); }
.qc-verdict--warn { background: var(--warn-soft); color: var(--warn-ink); box-shadow: inset 3px 0 0 var(--warn); }
.qc-verdict--stop { background: var(--danger-soft); color: var(--danger-ink); box-shadow: inset 3px 0 0 var(--danger); }
.qc-gate { margin-top: 12px; padding: 20px 18px; text-align: center; border-radius: 16px; background: linear-gradient(160deg, var(--accent-soft), var(--panel)); border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border)); animation: qcGateIn .28s var(--ease-spring) both; }
@keyframes qcGateIn { from { transform: scale(.92); opacity: 0; } to { transform: none; opacity: 1; } }
/* SOPQC-1 — the PARTIAL gate (every must-fix clear, judgment items still open).
   Deliberately its OWN class, not a `.qc-gate` modifier: several tests (and any
   future code) treat `.qc-gate` presence as "the full cleared payoff fired", and
   this is a real but distinct, quieter state — reads amber, not the full-clear
   green/accent gradient, so it never looks identical to "fully done" either. */
.qc-gate-partial { margin-top: 12px; padding: 20px 18px; text-align: center; border-radius: 16px; background: linear-gradient(160deg, var(--warn-soft), var(--panel)); border: 1px solid color-mix(in srgb, var(--warn) 30%, var(--border)); animation: qcGateIn .28s var(--ease-spring) both; }
.qc-gate-partial .qc-score { color: var(--warn); }
.qc-score { position: relative; display: inline-block; font-family: var(--font-serif); font-weight: var(--fw-bold); font-size: 54px; line-height: 1; font-variant-numeric: tabular-nums; color: var(--accent); transform: skewX(-3deg); overflow: hidden; }
.qc-score::after { content: ''; position: absolute; inset: 0; background: linear-gradient(115deg, transparent 42%, rgba(255,255,255,.55) 50%, transparent 58%); transform: translateX(-120%); animation: qcShine .8s var(--ease) .15s; }
@keyframes qcShine { to { transform: translateX(120%); } }
.qc-gate-h { font-family: var(--font-serif); font-size: 20px; font-weight: var(--fw-semibold); margin: 4px 0 2px; }
.qc-stat-trio { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
.qc-stat { flex: 1 1 90px; max-width: 150px; padding: 10px 8px; border-radius: var(--radius-4); background: var(--panel); border: 1px solid var(--border); }   /* DHSWEEP W9 — was a literal 12px, exactly --radius-4's value; zero-diff token wire */
.qc-stat.ok { border-color: color-mix(in srgb, var(--ok) 40%, var(--border)); }
.qc-stat.accent { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.qc-stat.warn { border-color: color-mix(in srgb, var(--warn) 40%, var(--border)); }
.qc-stat-n { font-size: 20px; font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }
.qc-stat-l { font-size: var(--fs-micro); color: var(--muted); margin-top: 2px; }
.qc-week { display: flex; gap: 6px; }
.qc-dot { width: 22px; height: 22px; border-radius: 50%; background: var(--bg-2); border: 1px solid var(--border); }
.qc-dot.on { background: var(--warn); border-color: var(--warn); box-shadow: 0 0 0 3px var(--warn-soft); }
.qc-ai { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 8px; }
.qc-ai > summary { cursor: pointer; font-size: 12.5px; font-weight: var(--fw-bold); color: var(--muted); list-style: none; padding: 4px 0; }
.qc-ai > summary::-webkit-details-marker { display: none; }
.qc-ai > summary::before { content: '▸ '; }
.qc-ai[open] > summary::before { content: '▾ '; }
/* RELGATE-W3a — the example-strip toggle: collapsed by default for a
   certified viewer (compact gate), open by default for a novice
   (App.reviewTierInfo(viewer).certified === false — see qc.js openQcPass). */
.qc-examples-toggle { margin: 10px 0; }
.qc-examples-toggle > summary { cursor: pointer; font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .03em; color: var(--muted); list-style: none; padding: 2px 0; }
.qc-examples-toggle > summary::-webkit-details-marker { display: none; }
.qc-examples-toggle > summary::before { content: '▸ '; }
.qc-examples-toggle[open] > summary::before { content: '▾ '; }
/* the inner .dg-examples-head stays IN THE DOM (unchanged node, unchanged
   text — a test walks it) but is visually redundant once its text is
   already the <summary> label above it. */
.qc-examples-toggle .dg-examples-head { display: none; }
/* RELGATE-W3c — the gate header's work link (the thing a reviewer must
   actually open before a red row's "open the work first" hint clears). */
.qc-gate-worklink { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: var(--fw-bold); color: var(--accent); text-decoration: none; }
.qc-gate-worklink:hover { text-decoration: underline; }
/* QA score → teaching-moment callout (fires on a low grade) */
.qa-teach-cta { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-meta); font-weight: var(--fw-bold); text-decoration: none; color: var(--warn); background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 35%, var(--border)); border-radius: 8px; padding: 5px 10px; margin-top: 2px; transition: transform .15s var(--ease-spring); }
.qa-teach-cta:hover { transform: translateX(2px); }
.mm-embed-poster { position: absolute; inset: 0; width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; border: 0; background: linear-gradient(135deg, #1a2634, #0f1722); color: #fff; cursor: pointer; font-size: var(--fs-meta); }
.mm-embed-poster:hover { background: linear-gradient(135deg, #22384f, #16202e); }
.mm-linkchip { display: inline-flex; align-items: center; gap: 6px; margin: 10px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--panel); color: var(--accent); font-size: 12.5px; text-decoration: none; }
.mm-linkchip:hover { border-color: var(--accent); background: var(--accent-soft); }
.mm-media-fail { display: flex; align-items: center; gap: 6px; padding: 14px; color: var(--muted); font-size: 12.5px; text-decoration: none; }
.mm-resize { position: absolute; right: 1px; bottom: 1px; width: 16px; height: 16px; cursor: nwse-resize; z-index: 3; touch-action: none; opacity: 0; background: linear-gradient(135deg, transparent 45%, var(--accent) 45%); border-radius: 0 0 9px 0; transition: opacity .12s; }
.mm-node:hover .mm-resize { opacity: .85; }
.mm-link-del { position: absolute; transform: translate(-50%, -50%); z-index: 2; width: 22px; height: 22px; padding: 0; border-radius: 50%; border: 1px solid var(--border); background: var(--surface, var(--panel)); color: var(--muted); font-size: var(--fs-body); line-height: 1; display: grid; place-items: center; cursor: pointer; opacity: 0; transition: opacity .12s, color .12s, border-color .12s; }
.mm-board:hover .mm-link-del, .mm-link-del:hover, .mm-link-del:focus-visible { opacity: 1; }
.mm-link-del:hover, .mm-link-del:focus-visible { color: var(--danger, #dc2626); border-color: var(--danger, #dc2626); }

/* SPEC225 — sortable table header as an accessible (keyboard) button that inherits the th look */
.th-sort { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; font-weight: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; white-space: nowrap; }
.th-sort:hover { color: var(--text); }
.th-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.mm-node { position: absolute; width: 200px; background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--mm-accent, var(--accent)); border-radius: 10px; box-shadow: 0 6px 18px rgba(0,0,0,.08); }
.mm-node--linking { box-shadow: 0 0 0 2px var(--accent); }
.mm-node--task { --mm-accent: #2563eb; } .mm-node--job { --mm-accent: #0e7c5a; } .mm-node--client { --mm-accent: #b45309; } .mm-node--note { --mm-accent: #5f6b78; }
.mm-node-head { display: flex; align-items: center; gap: 6px; padding: 5px 6px 5px 8px; cursor: grab; border-bottom: 1px solid var(--border); touch-action: none; }
.mm-node.dragging { opacity: .94; box-shadow: 0 14px 30px rgba(0,0,0,.18); }
.mm-node.dragging .mm-node-head { cursor: grabbing; }
.mm-grip { color: var(--faint); display: inline-flex; }
.mm-kind { font-size: 10px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); flex: 1 1 auto; }
.mm-connect { width: 16px; height: 16px; border-radius: 999px; border: 2px solid var(--mm-accent, var(--accent)); background: transparent; cursor: pointer; padding: 0; flex: 0 0 auto; }
.mm-connect:hover { background: var(--mm-accent, var(--accent)); }
.mm-del { border: 0; background: transparent; color: var(--muted); font-size: 17px; line-height: 1; cursor: pointer; padding: 0 2px; }
.mm-del:hover { color: var(--danger, #dc2626); }
.mm-node-body { width: 100%; box-sizing: border-box; padding: 9px 11px; font-size: var(--fs-caption); line-height: 1.45; color: var(--text); min-height: 38px; }
.mm-node-body[contenteditable] { outline: none; cursor: text; white-space: pre-wrap; word-break: break-word; }
.mm-node-body[contenteditable]:empty::before { content: 'Type your idea…'; color: var(--faint); }
.mm-node-body--rec { text-align: left; background: transparent; border: 0; cursor: pointer; font: inherit; font-size: var(--fs-caption); }
.mm-node-body--rec:hover { color: var(--accent); }
.mm-empty { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; color: var(--muted); max-width: 320px; pointer-events: none; }
.mm-pick-wrap { display: flex; flex-direction: column; gap: 8px; }
.mm-pick-results { max-height: 320px; overflow: auto; display: flex; flex-direction: column; gap: 2px; }
.mm-pick-row { text-align: left; padding: 9px 11px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); cursor: pointer; font-size: var(--fs-caption); }
.mm-pick-row:hover { border-color: var(--accent); }
/* SPEC223 — view-mode switcher + Tasks Cards grid + Calendar */
.seg-viewmode button { display: inline-flex; align-items: center; justify-content: center; gap: 5px; }
/* WAVE1B B2 (LAWA-3c TAPFLOOR ratchet) — collapsing to icon-only below 700px
   left each button ~39px wide (16px icon + 2×12px padding), under the 44px
   floor; a 3rd mode (Ranked) made a pre-existing 2-violation gap worse
   instead of just repeating it. min-width floors every icon-only segment
   button at 44px regardless of how many modes a given .seg-viewmode carries. */
@media (max-width: 700px) { .seg-viewmode button > span:not(.icon) { display: none; } .seg-viewmode button { min-width: 44px; } }
.task-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(244px, 1fr)); gap: 12px; align-items: start; }
.cal-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.cal-title { font-weight: var(--fw-black); font-size: 15px; min-width: 150px; text-align: center; }
.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-dow { font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); text-align: center; padding: 2px 0; }
.cal-cell { min-height: 92px; border: 1px solid var(--border); border-radius: 8px; padding: 5px; background: var(--panel); display: flex; flex-direction: column; gap: 3px; }
.cal-cell--pad { border: 0; background: transparent; min-height: 0; }
.cal-cell--today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.cal-day { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--muted); }
.cal-cell--today .cal-day { color: var(--accent); }
.cal-task { display: flex; align-items: center; gap: 5px; width: 100%; text-align: left; border: 0; background: var(--bg-2); border-radius: 5px; padding: 3px 6px; font-size: 11.5px; cursor: pointer; color: var(--text); overflow: hidden; }
.cal-task:hover { background: var(--hover); }
.cal-task-dot { width: 7px; height: 7px; border-radius: 999px; flex: 0 0 auto; background: var(--accent); }
.cal-task-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cal-task--done .cal-task-t { text-decoration: line-through; opacity: .65; }
.cal-task--over { background: rgba(220,38,38,.1); }
.cal-more { font-size: 10.5px; color: var(--muted); padding: 1px 2px; border: 0; background: transparent; cursor: pointer; text-align: left; font-weight: var(--fw-semibold); font-family: inherit; border-radius: 4px; }
.cal-more:hover { color: var(--accent); background: var(--hover); }
.cal-more-list { display: flex; flex-direction: column; gap: 4px; }
.cal-more-row { display: flex; align-items: center; gap: 8px; text-align: left; border: 1px solid var(--border); border-radius: 8px; padding: 9px 11px; background: var(--panel); cursor: pointer; font-size: var(--fs-caption); font-family: inherit; color: var(--text); }
.cal-more-row:hover { border-color: var(--accent); }
.cal-nodate { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px; }
.cal-nodate-h { font-size: var(--fs-meta); font-weight: var(--fw-bold); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); margin-bottom: 8px; }
.cal-nodate .cal-task { display: inline-flex; width: auto; max-width: 220px; margin: 0 6px 6px 0; }
@media (max-width: 560px) { .cal-cell { min-height: 64px; } .cal-task-t { font-size: 10.5px; } }

/* SPEC230 — Timeline / Gantt view */
.tl-wrap { margin-top: 14px; }
.tl-scroll { overflow-x: auto; border: 1px solid var(--border); border-radius: 12px; background: var(--panel); -webkit-overflow-scrolling: touch; }
.tl-axis, .tl-row { display: grid; grid-template-columns: 150px 1fr; align-items: stretch; }
.tl-axis { position: sticky; top: 0; z-index: 2; background: var(--bg-2); border-bottom: 1px solid var(--border); }
.tl-axis-label { padding: 6px 10px; }
.tl-axis-days { display: grid; }
.tl-axis-day { text-align: center; font-size: 10.5px; color: var(--muted); padding: 6px 0; border-left: 1px solid var(--border); }
.tl-axis-day.tl-wknd { background: color-mix(in srgb, var(--bg-2) 70%, transparent); }
.tl-axis-day.tl-today { color: var(--accent); font-weight: var(--fw-black); }
.tl-row { border-bottom: 1px solid var(--border); min-height: 38px; }
.tl-row:last-child { border-bottom: 0; }
.tl-row-label { padding: 8px 10px; font-size: 12.5px; font-weight: var(--fw-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; border-right: 1px solid var(--border); display: flex; align-items: center; }
.tl-track { position: relative; display: grid; }
.tl-cellline { border-left: 1px solid color-mix(in srgb, var(--border) 50%, transparent); }
.tl-cellline.tl-wknd { background: color-mix(in srgb, var(--bg-2) 55%, transparent); }
.tl-cellline.tl-today { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.tl-bar { position: absolute; top: 6px; bottom: 6px; min-width: 12px; border: 0; border-radius: 6px; background: var(--tl-bar, var(--accent)); color: #fff; font-size: 11.5px; font-weight: var(--fw-bold); padding: 0 8px; display: flex; align-items: center; cursor: pointer; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,.14); font-family: inherit; }
.tl-bar-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tl-bar:hover { filter: brightness(1.07); }
.tl-bar:focus-visible { outline: 2px solid var(--text); outline-offset: 1px; }
.tl-bar--done { opacity: .5; text-decoration: line-through; }
.tl-bar--over { box-shadow: 0 0 0 2px var(--danger), 0 1px 3px rgba(0,0,0,.14); }
.tl-clipL { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.tl-clipR { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.tl-empty { padding: 24px; text-align: center; }
@media (max-width: 640px) { .tl-axis, .tl-row { grid-template-columns: 96px 1fr; } .tl-row-label { font-size: 11.5px; padding: 6px 8px; } }

/* SPEC234 — Your First Week onboarding */
.firstweek-card { margin-bottom: 14px; border: 1px solid var(--border); background: linear-gradient(135deg, color-mix(in srgb, var(--accent) 10%, var(--panel)), var(--panel)); }
.fw-welcome { font-weight: var(--fw-black); font-size: 14.5px; display: inline-flex; align-items: center; gap: 4px; }
.firstweek-hero { margin-bottom: 12px; }
/* AUDIT-2026-07-25 [16] — the pre-checklistPlay first-week markup (.firstweek-phase,
   .fw-phase-*, .fw-step*, .fw-check*, .firstweek-done + its @media rule) deleted here:
   firstweek.js was migrated to the shared App.checklistPlay engine and now emits only
   .firstweek-card/.fw-welcome/.firstweek-hero (kept above); proven zero JS/HTML
   emitters repo-wide before deletion. Regenerated order.css/portal.css/site.css —
   three of these rules had leaked into the client-facing slices. */

/* SPEC223 — Clients / Leads Cards layout */
.client-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); gap: 12px; align-items: start; }
.cl-card { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 14px; cursor: pointer; transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease); }
.cl-card:hover { border-color: var(--accent); box-shadow: 0 6px 18px rgba(0,0,0,.08); }
.cl-card-top { display: flex; align-items: flex-start; gap: 10px; }
.cl-card-id { flex: 1 1 auto; min-width: 0; }
.cl-card-name { display: flex; align-items: center; gap: 6px; font-weight: var(--fw-bold); flex-wrap: wrap; }
.cl-card-co { font-size: var(--fs-meta); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cl-card-stats { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px 10px; margin-top: 12px; }
.cl-stat-v { font-weight: var(--fw-black); font-size: 15px; }
.cl-stat-l { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); margin-top: 1px; }
.cl-card-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border); }
.cl-card-contact { font-size: 11.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.site-stage { flex: 1 1 360px; min-width: 300px; max-height: 70vh; overflow: auto; border: 1px solid var(--border); border-radius: 14px; padding: 14px; background: var(--bg-2); }
.site-stage .site-page { max-width: 100%; }
.site-stage .site-hero { max-height: 220px; }
.site-ctrl { flex: 1 1 250px; min-width: 230px; max-height: 70vh; overflow: auto; display: flex; flex-direction: column; gap: 9px; }

/* SPEC171 — gallery favorites / selects (delivery proofing) */
.cstatus-galbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 12px 0 2px; flex-wrap: wrap; }
.cstatus-galbar-n { font-size: 12.5px; font-weight: var(--fw-bold); color: var(--text-soft); }
.cstatus-gal-fav { position: absolute; top: 7px; left: 7px; z-index: 2; width: 30px; height: 30px; border-radius: 999px; border: 0; cursor: pointer; background: rgba(0,0,0,.5); color: #fff; font-size: var(--fs-body); line-height: 1; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity .18s ease, transform .15s ease; }
.cstatus-gal-item:hover .cstatus-gal-fav, .cstatus-gal-fav.on { opacity: 1; }
.cstatus-gal-fav.on { color: #ff4d6d; background: rgba(255,255,255,.92); }
.cstatus-gal-fav:active { transform: scale(1.25); }

/* ============================================================
   SPEC173 — shared premium client-surface layer (delivery + status + property site).
   One reveal helper + one cinematic image treatment + an editorial type/accent pass
   lift all three surfaces. Pure CSS, dark-mode + reduced-motion safe.
   ============================================================ */
/* reveal-on-scroll (driven by App.revealOnScroll) */
.reveal { opacity: 0; transform: translateY(13px); transition: opacity .6s var(--ease), transform .6s var(--ease-spring); }
.reveal.in { opacity: 1; transform: none; }

/* cinematic Ken Burns photo layer (cover + hero) — image pans slowly under a still scrim */
.cstatus-cover-img, .site-hero-img { position: absolute; inset: -2.5%; background-size: cover; background-position: var(--cover-pos, center);
  background-color: var(--cf-card); transform-origin: 50% 42%; animation: vvtKenBurns 26s var(--ease) both alternate infinite; will-change: transform; }
/* SPEC268 — a studio-set cover focal holds the pan still so the chosen framing stays exact */
.cstatus-cover-img--focal { animation: none; inset: 0; }
@keyframes vvtKenBurns { from { transform: scale(1.05); } to { transform: scale(1.16) translate(-1.6%, -1.4%); } }
.cstatus-cover, .site-hero { isolation: isolate; border-top: 3px solid var(--cover-accent, var(--accent)); }
/* taller, more cinematic delivery cover that opens the page */
.cstatus--delivery .cstatus-cover { aspect-ratio: 16 / 10; max-height: min(64vh, 600px); }
/* branded-gradient empty cover (no photo yet / image blocked) — still looks designed */
.cstatus-cover--empty { background-image: linear-gradient(140deg, var(--cover-primary, var(--accent)), rgba(10,12,20,.88)); }
.cstatus-cover--empty .cstatus-cover-img { display: none; }
/* upgraded two-stop bottom-weighted scrims (text always legible) */
/* SPEC-DHCLIENT D1/D4 — converged onto the README's exact rgba(13,13,13,*) ground channel
   (was a bluish rgba(10,12,20,*)); same proven 3-stop shape, kept rather than flattened to
   the literal var(--cf-scrim) 2-stop so the Ken Burns legibility tuning survives untouched. */
.cstatus-cover-scrim { background: linear-gradient(180deg, transparent 42%, rgba(13,13,13,.5) 76%, rgba(13,13,13,.84)); }
.site-hero-scrim { background: linear-gradient(180deg, transparent 40%, rgba(13,13,13,.48) 74%, rgba(13,13,13,.8)); }
/* editorial serif cover title */
.cstatus-cover-title { font-family: var(--font-serif); letter-spacing: -.012em; line-height: 1.04; }
.cstatus-cover-brand { color: #fff; }
.cstatus-cover-brand, .site-hero-brand { text-shadow: 0 1px 10px rgba(0,0,0,.4); }

/* blur-up gallery tiles (sharpen on load; keep hover-zoom transition intact) */
.cstatus-gal-item img { opacity: 0; filter: blur(14px); transition: opacity .5s var(--ease), filter .65s var(--ease), transform .5s cubic-bezier(.2,.7,.2,1); }
.cstatus-gal-item img.loaded { opacity: 1; filter: none; }
.cstatus-gal-item--err { background: linear-gradient(150deg, var(--bg-2), var(--hover)); }
.cstatus-gal-item--err::after { content: '🖼'; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 26px; opacity: .35; }

/* brand-tinted category rail + consistent line-icon */
.cstatus-svc { border-left: 3px solid var(--stage, var(--border-strong)); transition: box-shadow .18s var(--ease), transform .18s var(--ease); }
.cstatus-svc:hover { box-shadow: var(--shadow); transform: translateY(-1px); }
.cstatus-svc-name { display: inline-flex; align-items: center; gap: 9px; }
.cstatus-svc-ic { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 9px; flex: 0 0 auto;
  color: var(--stage, var(--accent)); background: color-mix(in srgb, var(--stage, var(--accent)) 13%, transparent); }

@media (prefers-reduced-motion: reduce) {
  .cstatus-cover-img, .site-hero-img { animation: none; }
  .reveal { transition: none; }
}

/* ============================================================
   SPEC173 — Marketing Studio: photo-forward templates, vibes, presets.
   ============================================================ */
.tpl-landscape { aspect-ratio: 1.91 / 1; }
/* photo-forward layouts: hero fills the frame, text over a legibility scrim */
.tpl-comingsoon .tpl-hero, .tpl-pricedrop .tpl-hero, .tpl-feature .tpl-hero,
.tpl-undercontract .tpl-hero, .tpl-carousel .tpl-hero { position: absolute; inset: 0; }
.tpl-overlay { position: absolute; inset: 0; background: linear-gradient(0deg, rgba(0,0,0,.85), rgba(0,0,0,.30) 56%, transparent 74%); }
.tpl-pf-body { position: absolute; left: 0; right: 0; bottom: 0; padding: 6cqw 6cqw 6.5cqw; display: flex; flex-direction: column; gap: 1.7cqw; }
.tpl-pf-body .tpl-headline, .tpl-pf-body .tpl-when, .tpl-pf-body .tpl-tagline, .tpl-pf-body .tpl-facts,
.tpl-pf-body .tpl-eyebrow, .tpl-pf-body .tpl-newprice, .tpl-pf-body .tpl-oldprice, .tpl-pf-body .tpl-bignum,
.tpl-center .tpl-headline, .tpl-center .tpl-price { text-shadow: 0 2px 16px rgba(0,0,0,.55); }
.tpl-pf-body .tpl-agent { border-top: 0; padding-top: .5cqw; margin-top: .5cqw; }
/* eyebrow kicker (editorial alt to the pill badge) */
.tpl-eyebrow { font-size: 2.7cqw; font-weight: var(--fw-bold); letter-spacing: .22em; text-transform: uppercase; color: var(--tpl-accent); }
/* price improvement */
.tpl-pricewrap { display: flex; align-items: baseline; gap: 3cqw; flex-wrap: wrap; }
.tpl-oldprice { font-size: 4.4cqw; font-weight: var(--fw-bold); opacity: .72; text-decoration: line-through; text-decoration-thickness: .35cqw; }
.tpl-newprice { font-size: 7cqw; font-weight: var(--fw-black); color: var(--tpl-accent); }
/* by the numbers */
.tpl-stats { display: flex; gap: 5.5cqw; margin: 1cqw 0; }
.tpl-stat-n { font-size: 8.4cqw; font-weight: var(--fw-black); line-height: 1; color: var(--tpl-accent); }
.tpl-stat-l { font-size: 2.5cqw; font-weight: var(--fw-bold); letter-spacing: .12em; text-transform: uppercase; opacity: .92; margin-top: .6cqw; }
/* testimonial (text-only, serif) */
.tpl-quote-inner { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 9cqw; gap: 2.6cqw; }
.tpl-quote-stars { font-size: 5cqw; color: var(--tpl-accent); letter-spacing: .4cqw; }
.tpl-quote { font-family: var(--font-serif); font-size: 5.4cqw; font-style: italic; line-height: 1.32; max-width: 92%; }
.tpl-quote-by { font-size: 3.4cqw; font-weight: var(--fw-black); opacity: .92; }
.tpl-testimonial .tpl-agent { border-top: 0; justify-content: center; margin-top: 1cqw; }
/* under contract (diagonal ribbon) */
.tpl-ribbon { position: absolute; top: 7cqw; right: -13cqw; transform: rotate(45deg); background: var(--tpl-accent); color: #15181c;
  font-weight: var(--fw-black); font-size: 3cqw; letter-spacing: .12em; padding: 1.4cqw 13cqw; text-transform: uppercase; box-shadow: 0 1.6cqw 4cqw rgba(0,0,0,.3); }
/* carousel cover */
.tpl-bignum { font-size: 19cqw; font-weight: var(--fw-black); line-height: .85; color: var(--tpl-accent); }
.tpl-swipe { font-size: 3.2cqw; font-weight: var(--fw-black); letter-spacing: .1em; opacity: .92; margin-top: .8cqw; }
/* VIBES — restyle the whole card, not just the font */
.tpl--serif .tpl-headline, .tpl--serif .tpl-ab-name, .tpl--serif .tpl-bignum, .tpl--serif .tpl-stat-n {
  font-family: var(--font-serif); letter-spacing: -.01em; }
.tpl--serif .tpl-badge { border-radius: 0; letter-spacing: .2em; background: transparent; color: var(--tpl-accent); border: 1px solid var(--tpl-accent); }
.tpl--condensed .tpl-headline, .tpl--condensed .tpl-ab-name { text-transform: uppercase; letter-spacing: .005em; }
.tpl--condensed .tpl-eyebrow { letter-spacing: .3em; }
/* editor: preset swatches + segmented size */
.mkt-swatches { display: flex; flex-wrap: wrap; gap: 7px; }
.mkt-swatch { width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--border-strong); cursor: pointer; padding: 0;
  transition: transform .12s var(--ease), box-shadow .12s var(--ease); }
.mkt-swatch:hover { transform: translateY(-1px) scale(1.07); box-shadow: var(--shadow); }
.mkt-seg { display: inline-flex; border: 1px solid var(--border-strong); border-radius: 9px; overflow: hidden; width: 100%; max-width: 320px; }
.mkt-seg-btn { flex: 1; padding: 8px 10px; font: inherit; font-size: 12.5px; font-weight: var(--fw-bold); border: 0; background: var(--panel); color: var(--muted); cursor: pointer; border-right: 1px solid var(--border); }
.mkt-seg-btn:last-child { border-right: 0; }
.mkt-seg-btn.on { background: var(--accent); color: #fff; }
.mkt-full .tpl-card.tpl-landscape { width: min(92vw, 760px); height: auto; }

/* ============================================================
   SPEC173 — Pixieset polish: status hero, glass download, lightbox, site mobile.
   ============================================================ */
/* client-status branded header card + animated % progress */
.cstatus-hero { position: relative; border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 20px 20px 18px; margin-bottom: 16px;
  background: linear-gradient(135deg, color-mix(in srgb, var(--cover-accent, var(--accent)) 14%, var(--panel)), var(--panel) 62%); overflow: hidden; }
.cstatus-hero::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--cover-accent, var(--accent)); }
.cstatus-hero .cstatus-title { margin-top: 2px; }
.cstatus-progwrap { margin-top: 15px; }
.cstatus-prog-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 7px; }
.cstatus-pct { font-size: 26px; font-weight: var(--fw-black); line-height: 1; color: var(--cover-accent, var(--accent)); }
.cstatus-hero .cstatus-track { height: 13px; box-shadow: inset 0 1px 2px rgba(0,0,0,.12); }
.cstatus-fill { background: linear-gradient(90deg, var(--ok), color-mix(in srgb, var(--ok) 58%, var(--cover-accent, var(--accent)))); transition: width 1s var(--ease-spring); position: relative; overflow: hidden; }
.cstatus-fill::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.4), transparent); transform: translateX(-100%); animation: cstatusShimmer 2.6s ease-in-out infinite; }
@keyframes cstatusShimmer { to { transform: translateX(240%); } }

/* frosted-glass "download all" on the delivery cover (top-right, clear of the
   bottom-left title that can wrap to two lines) */
.cstatus-cover-dl { position: absolute; right: 18px; top: 18px; z-index: 3; display: flex; flex-direction: column; gap: 1px;
  padding: 10px 16px; border-radius: var(--radius-lg); text-decoration: none; color: #fff;
  background: rgba(255,255,255,.16); backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); border: 1px solid rgba(255,255,255,.28);
  box-shadow: 0 6px 20px rgba(0,0,0,.28); transition: transform .15s var(--ease), background .15s var(--ease); }
.cstatus-cover-dl:hover { transform: translateY(-2px); background: rgba(255,255,255,.27); }
.cstatus-cover-dl-main { font-weight: var(--fw-black); font-size: var(--fs-body); }
.cstatus-cover-dl-sub { font-size: var(--fs-micro); opacity: .85; }

/* lightbox: blurred backdrop + auto-hiding chrome */
.cstatus-lightbox { background: rgba(8,10,16,.95); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.cstatus-lightbox .cstatus-lb-nav, .cstatus-lightbox .cstatus-lb-x, .cstatus-lightbox .cstatus-lb-dl, .cstatus-lightbox .cstatus-lb-cap, .cstatus-lightbox .cstatus-lb-sizehint { transition: opacity .3s var(--ease); }
.cstatus-lightbox.chrome-hidden .cstatus-lb-nav, .cstatus-lightbox.chrome-hidden .cstatus-lb-x,
.cstatus-lightbox.chrome-hidden .cstatus-lb-dl, .cstatus-lightbox.chrome-hidden .cstatus-lb-cap, .cstatus-lightbox.chrome-hidden .cstatus-lb-sizehint { opacity: 0; pointer-events: none; }

/* property site — seamless hairline-divided fact bar (one considered unit) */
.site-facts { gap: 0; border: 1px solid var(--border); border-radius: 14px; overflow: hidden; background: var(--bg-2); justify-content: stretch; }
.site-fact { flex: 1; max-width: none; border: 0; border-left: 1px solid var(--border); border-radius: 0; padding: 18px 10px; }
.site-fact:first-child { border-left: 0; }

/* property site — the previously-missing mobile layer */
@media (max-width: 640px) {
  .site-hero { aspect-ratio: 4 / 5; max-height: none; }
  .site-hero-text { padding: 18px 16px; }
  .site-hero-title { font-size: clamp(24px, 8vw, 34px); }
  .site-section { margin: 24px 0; }
  .site-detail { grid-template-columns: 1fr; }
  .site-agent { flex-direction: column; text-align: center; align-items: center; }
  .site-agent-cta { justify-content: center; }
  .site-map { max-height: 280px; }
  .cstatus-cover-dl { right: 12px; top: 12px; padding: 8px 12px; }
  .cstatus-cover-dl-sub { display: none; }
}

/* ============================================================
   SPEC174 — Pixieset cover landing + unbranded client preview.
   ============================================================ */
.cstatus--delivery .cstatus-cover--landing { aspect-ratio: auto; min-height: min(70vh, 640px); max-height: none; align-items: center; justify-content: center; text-align: center; }
.cstatus-cover--landing .cstatus-cover-scrim { background: linear-gradient(180deg, rgba(8,10,16,.34), rgba(8,10,16,.5)); }
.cstatus-cover--landing .cstatus-cover-text { padding: 30px 24px; max-width: 720px; }
.cstatus-cover--landing .cstatus-cover-title { font-size: clamp(28px, 5.2vw, 46px); }
.cstatus-cover--landing .cstatus-cover-brand { letter-spacing: .16em; }
.cstatus-cover-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 22px; }
.cstatus-cta { display: inline-flex; align-items: center; gap: 6px; padding: 11px 20px; border-radius: 999px; font: inherit; font-weight: var(--fw-bold); font-size: var(--fs-body); cursor: pointer; text-decoration: none;
  color: #fff; background: rgba(255,255,255,.15); backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); border: 1px solid rgba(255,255,255,.34);
  transition: transform .15s var(--ease), background .15s var(--ease); }
.cstatus-cta:hover { transform: translateY(-2px); background: rgba(255,255,255,.26); }
.cstatus-cta--primary { background: var(--cover-accent, var(--accent)); border-color: transparent; color: #15181c; }
.cstatus-cta--primary:hover { filter: brightness(1.08); }
/* unbranded preview: a subtle "PREVIEW" ribbon so the team never confuses it with the live page */
.cstatus--preview .cstatus-cover-title { font-family: var(--font-serif); }
@media (max-width: 620px) {
  .cstatus--delivery .cstatus-cover--landing { min-height: min(64vh, 520px); }
  .cstatus-cover-actions { gap: 8px; }
  .cstatus-cta { padding: 10px 16px; font-size: var(--fs-caption); }
}

/* ============================================================
   SPEC176 — gallery hover captions, tile lift, prominent video, designed empty state.
   ============================================================ */
.cstatus-gal-cap { position: absolute; left: 0; right: 0; bottom: 0; padding: 18px 11px 8px; z-index: 1;
  font-size: 11.5px; font-weight: var(--fw-bold); color: #fff; letter-spacing: .02em; text-shadow: 0 1px 6px rgba(0,0,0,.5);
  background: linear-gradient(0deg, rgba(0,0,0,.5), transparent); pointer-events: none;
  opacity: 0; transform: translateY(5px); transition: opacity .2s var(--ease), transform .2s var(--ease); }
.cstatus-gal-item:hover .cstatus-gal-cap, .cstatus-gal-item:focus-within .cstatus-gal-cap { opacity: 1; transform: none; }
.cstatus-gal-item:hover { z-index: 2; box-shadow: 0 12px 28px -8px rgba(0,0,0,.5); }
.cstatus-gal-cap--video { opacity: 1; transform: none; text-align: center; }
/* a more confident video play affordance */
.cstatus-gal-video .cstatus-play { width: 56px; height: 56px; font-size: 21px; box-shadow: 0 6px 20px rgba(0,0,0,.4);
  transition: transform .18s var(--ease), background .18s var(--ease); }
.cstatus-gal-video:hover .cstatus-play { transform: scale(1.08); background: var(--cover-accent, var(--accent)); }
/* designed empty / video-only delivery state */
.cstatus-gal-empty { text-align: center; padding: 46px 20px; margin: 14px 0 6px; border: 1px dashed var(--border-strong); border-radius: 16px;
  background: linear-gradient(160deg, color-mix(in srgb, var(--cover-accent, var(--accent)) 6%, var(--bg-2)), var(--bg-2)); }
.cstatus-gal-empty-ic { font-size: var(--fs-hero); opacity: .55; }
.cstatus-gal-empty-h { font-weight: var(--fw-black); font-size: var(--fs-md); margin-top: 8px; }
.cstatus-gal-empty-p { color: var(--muted); font-size: var(--fs-caption); margin-top: 4px; }
@media (prefers-reduced-motion: reduce) { .cstatus-gal-cap { transition: none; } }

/* ============================================================
   SPEC178 — Feature Guide (searchable "how to use everything").
   ============================================================ */
.guide-intro { font-size: 13.5px; color: var(--muted); margin: 0 0 14px; }
.guide-toolbar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.guide-search { flex: 1 1 240px; min-width: 180px; }
.guide-rolefilter { display: inline-flex; gap: 4px; flex-wrap: wrap; }
.guide-rf-btn { font: inherit; font-size: var(--fs-meta); font-weight: var(--fw-semibold); padding: 6px 11px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--panel); color: var(--muted); cursor: pointer; transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease); }
.guide-rf-btn:hover { background: var(--hover); color: var(--text); }
.guide-rf-btn.on { background: var(--accent-soft); color: var(--accent-hover); border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); }
.guide-count-row { margin: 8px 0 14px; }
.guide-count { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--faint); text-transform: uppercase; letter-spacing: .06em; }
.guide-list { display: flex; flex-direction: column; gap: 12px; }
/* SPEC178 — each category is ONE clean tap-to-open card (12 areas, not 98 rows) */
.guide-cat { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--panel); box-shadow: var(--shadow-sm); overflow: hidden; transition: box-shadow var(--t-fast) var(--ease); }
.guide-cat[open] { box-shadow: var(--shadow); }
.guide-cat-head { display: flex; align-items: center; gap: 12px; padding: 14px 16px; cursor: pointer; list-style: none; }
.guide-cat-head::-webkit-details-marker { display: none; }
.guide-cat-head:hover { background: var(--subtle); }
.guide-cat-ic { font-size: 19px; width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border-radius: 12px; background: var(--accent-soft); flex: 0 0 auto; }
.guide-cat-meta { flex: 1; min-width: 0; }
.guide-cat-title { font-family: var(--font-serif); font-size: var(--fs-lg); font-weight: var(--fw-semibold); letter-spacing: var(--ls-tight); line-height: var(--lh-tight); }
.guide-cat-blurb { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.guide-cat-n { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--muted); background: var(--subtle); border: 1px solid var(--border); border-radius: 999px; padding: 2px 9px; flex: 0 0 auto; }
.guide-cat-chev { color: var(--faint); font-size: 15px; transition: transform var(--t-fast) var(--ease); flex: 0 0 auto; }
.guide-cat[open] .guide-cat-chev { transform: rotate(180deg); }
.guide-cat-body { padding: 2px 14px 8px; border-top: 1px solid var(--border); }
/* features inside a category — flat rows with hairline dividers, not nested cards */
.guide-feat { border: 0; border-bottom: 1px solid var(--border); border-radius: 0; background: transparent; box-shadow: none; overflow: hidden; }
.guide-cat-body .guide-feat:last-child { border-bottom: 0; }
/* coverage banner (admin) — keeps the guide honest as the app grows */
.guide-coverage { font-size: 12.5px; border-radius: var(--radius); padding: 10px 13px; margin-bottom: 14px; }
.guide-coverage--ok { background: var(--ok-soft); color: var(--ok-ink); font-weight: var(--fw-semibold); }
.guide-coverage--gap { background: var(--warn-soft); border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent); }
.guide-coverage-sum { cursor: pointer; font-weight: var(--fw-bold); color: var(--warn); list-style: none; }
.guide-coverage-sum::-webkit-details-marker { display: none; }
.guide-coverage-list { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }
.guide-coverage-chip { font-size: var(--fs-micro); font-weight: var(--fw-semibold); background: var(--panel); border: 1px solid var(--border); border-radius: 999px; padding: 2px 9px; }

/* ============================================================
   SPEC179 — Coverage Center (nothing falls through the cracks).
   ============================================================ */
.cov { display: flex; flex-direction: column; gap: 12px; }
.cov-headline { display: flex; align-items: center; gap: 16px; padding: 16px 18px; border-radius: var(--radius-lg); border: 1px solid var(--border); }
.cov-headline--ok { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.cov-headline--gap { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.cov-head-pct { font-family: var(--font-serif); font-size: var(--fs-hero); font-weight: var(--fw-bold); line-height: 1; flex: 0 0 auto; }
.cov-headline--ok .cov-head-pct { color: var(--ok); }
.cov-headline--gap .cov-head-pct { color: var(--warn); }
.cov-head-title { font-weight: var(--fw-black); font-size: 15px; }
.cov-head-sub { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.cov-dim { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--panel); box-shadow: var(--shadow-sm); padding: 14px 16px; }
.cov-dim-head { display: flex; align-items: center; gap: 12px; }
.cov-dim-ic { font-size: var(--fs-lg); width: 38px; height: 38px; display: flex; align-items: center; justify-content: center; border-radius: 11px; background: var(--accent-soft); flex: 0 0 auto; }
.cov-dim-meta { flex: 1; min-width: 0; }
.cov-dim-label { font-weight: var(--fw-bold); font-size: var(--fs-body); }
.cov-dim-count { font-size: var(--fs-meta); color: var(--muted); margin-top: 1px; }
.cov-dim-pct { font-weight: var(--fw-black); font-size: 15px; color: var(--warn-ink); flex: 0 0 auto; }
.cov-dim-pct--ok { color: var(--ok-ink); }
.cov-bar { height: 8px; border-radius: 999px; background: var(--bg-2); overflow: hidden; margin: 10px 0 0; }
.cov-bar-fill { height: 100%; border-radius: 999px; background: var(--warn); transition: width .5s var(--ease-spring); }
.cov-bar-fill--ok { background: var(--ok); }
.cov-gaps { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.cov-gap-chip { font-size: 11.5px; font-weight: var(--fw-semibold); background: var(--subtle); border: 1px solid var(--border); border-radius: 999px; padding: 3px 10px; }
.cov-fix-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 11px; flex-wrap: wrap; }
.cov-fix-hint { font-size: var(--fs-meta); color: var(--muted); }
.cov-dim-allok { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--ok); margin-top: 9px; }

/* ============================================================
   SPEC180 — client notes log (accumulating, expandable entries).
   ============================================================ */
.cnotes-bar { display: flex; justify-content: flex-end; margin-bottom: 6px; }
.cnotes-list { display: flex; flex-direction: column; gap: 6px; }
.cnotes-item { border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel); overflow: hidden; }
.cnotes-item[open] { box-shadow: var(--shadow-sm); border-color: var(--border-strong); }
.cnotes-sum { display: flex; align-items: baseline; gap: 10px; padding: 9px 12px; cursor: pointer; list-style: none; }
.cnotes-sum::-webkit-details-marker { display: none; }
.cnotes-sum:hover { background: var(--subtle); }
.cnotes-preview { flex: 1; min-width: 0; font-size: var(--fs-caption); font-weight: var(--fw-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cnotes-meta { flex: 0 0 auto; font-size: var(--fs-micro); color: var(--faint); }
.cnotes-body { padding: 2px 12px 11px; }
.cnotes-text { font-size: var(--fs-caption); line-height: 1.55; white-space: pre-wrap; }
.cnotes-del { margin-top: 8px; color: var(--danger); }
.cnotes-empty { font-size: 12.5px; color: var(--muted); padding: 8px 2px; }
.cnotes-compose { margin-top: 10px; }

/* ============================================================
   SPEC182 — retainer consult script (first-meeting sales playbook).
   ============================================================ */
.cscript-cta { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border)); background: var(--accent-soft);
  border-radius: var(--radius-lg); padding: 12px 16px; margin: 10px 0 16px; }
.cscript { display: flex; flex-direction: column; gap: 10px; }
.cscript-philosophy { font-size: var(--fs-caption); line-height: 1.6; color: var(--text); background: var(--subtle); border-left: 3px solid var(--accent); border-radius: var(--radius-sm); padding: 11px 14px; }
.cscript-phase { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--panel); box-shadow: var(--shadow-sm); overflow: hidden; }
.cscript-phase[open] { box-shadow: var(--shadow); border-color: var(--border-strong); }
.cscript-extra { background: color-mix(in srgb, var(--accent) 4%, var(--panel)); }
.cscript-sum { display: flex; align-items: center; gap: 12px; padding: 13px 15px; cursor: pointer; list-style: none; }
.cscript-sum::-webkit-details-marker { display: none; }
.cscript-sum:hover { background: var(--subtle); }
.cscript-num { flex: 0 0 auto; width: 28px; height: 28px; border-radius: 999px; display: flex; align-items: center; justify-content: center; font-weight: var(--fw-black); font-size: var(--fs-caption); color: #fff; background: var(--accent); }
.cscript-num--alt { background: var(--accent-soft); color: var(--accent-hover); }
.cscript-meta { flex: 1; min-width: 0; }
.cscript-name { font-family: var(--font-serif); font-weight: var(--fw-semibold); font-size: var(--fs-md); letter-spacing: var(--ls-tight); }
.cscript-goal { font-size: var(--fs-meta); color: var(--muted); margin-top: 1px; }
.cscript-chev { color: var(--faint); font-size: 15px; transition: transform var(--t-fast) var(--ease); flex: 0 0 auto; }
.cscript-phase[open] .cscript-chev { transform: rotate(180deg); }
.cscript-body { padding: 2px 15px 14px; }
.cscript-label { font-size: var(--fs-micro); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .06em; color: var(--faint); margin: 12px 0 6px; }
.cscript-lines { display: flex; flex-direction: column; gap: 6px; }
.cscript-line { display: flex; align-items: flex-start; gap: 8px; background: var(--subtle); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 9px 11px; }
.cscript-line-t { flex: 1; min-width: 0; font-size: var(--fs-caption); line-height: 1.55; }
.cscript-copy { flex: 0 0 auto; padding: 2px 7px; }
.cscript-listen { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; }
.cscript-listen li { font-size: 12.5px; line-height: var(--lh-normal); color: var(--muted); }
.cscript-tip { font-size: 12.5px; line-height: 1.55; margin-top: 12px; background: var(--warn-soft); border-radius: var(--radius-sm); padding: 9px 11px; }
.cscript-obj { padding: 9px 0; border-bottom: 1px solid var(--border); }
.cscript-obj:last-child { border-bottom: 0; }
.cscript-obj-q { font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.cscript-obj-a { font-size: var(--fs-caption); line-height: 1.55; color: var(--text); margin-top: 3px; }
.guide-feat-sum { display: flex; align-items: flex-start; gap: 11px; padding: 12px 14px; cursor: pointer; list-style: none; }
.guide-feat-sum::-webkit-details-marker { display: none; }
.guide-feat-sum:hover { background: var(--subtle); }
.guide-feat-ic { font-size: var(--fs-lg); flex: 0 0 auto; line-height: 1.45; }
.guide-feat-main { flex: 1; min-width: 0; }
.guide-feat-name { font-weight: var(--fw-bold); font-size: var(--fs-body); }
.guide-feat-what { font-size: 12.5px; color: var(--muted); margin-top: 2px; line-height: var(--lh-normal); }
.guide-feat-chev { color: var(--faint); font-size: 15px; transition: transform var(--t-fast) var(--ease); flex: 0 0 auto; }
.guide-feat[open] .guide-feat-chev { transform: rotate(180deg); }
.guide-feat-body { padding: 0 14px 14px 47px; }
.guide-feat-where { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--accent-hover); margin-bottom: 8px; }
.guide-feat-how { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 5px; }
.guide-feat-how li { font-size: var(--fs-caption); line-height: 1.55; }
.guide-feat-roles { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 11px; }
.guide-role-chip { font-size: 10.5px; font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: var(--ls-label); padding: 2px 8px; border-radius: 999px;
  background: var(--subtle); color: var(--muted); box-shadow: inset 0 0 0 1px var(--border); }
.guide-role-client { background: var(--accent-soft); color: var(--accent-hover); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent); }
.guide-empty { padding: 34px 12px; text-align: center; color: var(--muted); font-size: 13.5px; }
@media (max-width: 640px) { .guide-feat-body { padding-left: 14px; } }

/* SPEC190 — live consult capture + Diagnose & Close */
.ccap-phase { border-bottom: 1px solid var(--border); }
.ccap-phase > summary { list-style: none; }
.ccap-phase > summary::-webkit-details-marker { display: none; }
.ccap-phase[open] > summary { color: var(--accent-hover); }
.ccap-input { width: 100%; box-sizing: border-box; }
.ccap-pill { transition: opacity .2s; }
.cscript-diagnose-card { border-left: 4px solid var(--accent); }

/* SPEC191 — Marketing Studio layout VARIANTS (pure-CSS repositions of the same zones) */
.tpl-card.tpl-var-framed::after { content: ''; position: absolute; inset: 3.4cqw; border: 0.7cqw solid rgba(255,255,255,.9); border-radius: 5px; pointer-events: none; z-index: 6; }
.tpl-card.tpl-var-top { flex-direction: column-reverse; }
.tpl-card.tpl-var-center .tpl-pf-body { top: 0; bottom: 0; justify-content: center; align-items: center; text-align: center; }
.tpl-card.tpl-var-center .tpl-overlay { background: linear-gradient(0deg, rgba(0,0,0,.6), rgba(0,0,0,.4)); }
.tpl-card.tpl-var-center .tpl-stats { justify-content: center; }
.tpl-card.tpl-var-center .tpl-agent { justify-content: center; }
/* a segmented control that wraps when it has several options (Layout) */
.mkt-seg--wrap { flex-wrap: wrap; }

/* SPEC192 — Studio stickers: a corner layer on the card + the editor picker */
.tpl-stickers { position: absolute; z-index: 7; display: flex; flex-wrap: wrap; gap: 2cqw; max-width: 42%; pointer-events: none; }
.tpl-stickers--tr { top: 4cqw; right: 4cqw; justify-content: flex-end; }
.tpl-stickers--tl { top: 4cqw; left: 4cqw; }
.tpl-stickers--br { bottom: 4cqw; right: 4cqw; justify-content: flex-end; }
.tpl-stickers--bl { bottom: 4cqw; left: 4cqw; }
.tpl-sticker { display: inline-flex; }
.tpl-sticker--emoji { font-size: 11cqw; line-height: 1; filter: drop-shadow(0 1px 2px rgba(0,0,0,.35)); }
.tpl-sticker--svg svg { width: 11cqw; height: 11cqw; filter: drop-shadow(0 1px 2px rgba(0,0,0,.35)); }
.mkt-stickers-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(40px, 1fr)); gap: 6px; margin-top: 6px; }
.mkt-sticker-btn { display: inline-flex; align-items: center; justify-content: center; height: 40px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); cursor: pointer; font-size: 20px; color: var(--text); padding: 0; }
.mkt-sticker-btn:hover { border-color: var(--accent); }
.mkt-sticker-btn.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-hover); box-shadow: inset 0 0 0 1px var(--accent); }
.mkt-sticker-ic { display: inline-flex; }

/* SPEC193 — Studio photo picker (swap the hero from the listing's gallery) */
.mkt-photo-strip { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.mkt-photo-thumb { width: 52px; height: 40px; border-radius: 7px; border: 1px solid var(--border); background: var(--hover) center/cover no-repeat; cursor: pointer; padding: 0; color: var(--muted); font-size: var(--fs-md); display: inline-flex; align-items: center; justify-content: center; }
.mkt-photo-thumb:hover { border-color: var(--accent); }
.mkt-photo-thumb.on { box-shadow: 0 0 0 2px var(--accent); border-color: var(--accent); }
.mkt-photo-thumb--none { background: var(--panel); }

/* SPEC195 — the $0 reel (Ken-Burns slideshow you screen-record) */
.reel-ov { position: fixed; inset: 0; z-index: 1000; background: rgba(8,10,16,.93); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 16px; }
.reel-empty { text-align: center; color: #e7eaf2; max-width: 320px; }
.reel-stage { position: relative; width: min(46vh, 92vw); aspect-ratio: 9 / 16; border-radius: 16px; overflow: hidden; background: #0c0f16; box-shadow: 0 24px 70px rgba(0,0,0,.55); }
.reel-slide { position: absolute; inset: 0; opacity: 0; transition: opacity .8s ease; background-size: cover; background-position: center; }
.reel-slide.on { opacity: 1; }
.reel-photo.on { animation: vvtKenBurns 5s ease both; }
.reel-end { display: flex; align-items: center; justify-content: center; text-align: center; background: linear-gradient(140deg, var(--reel-primary, #2b608f), rgba(10,12,20,.96)); }
.reel-end-inner { color: #fff; padding: 9%; }
.reel-end-title { font-size: 7vmin; font-weight: var(--fw-black); line-height: 1.12; }
.reel-end-sub { margin-top: 2.2vmin; font-size: 3.4vmin; opacity: .92; }
.reel-end-brand { margin-top: 4.5vmin; font-size: 2.8vmin; letter-spacing: .12em; text-transform: uppercase; opacity: .85; }
.reel-bar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: center; max-width: min(46vh, 92vw); transition: opacity .4s ease; }
.reel-hint { color: #cdd3df; font-size: var(--fs-meta); width: 100%; text-align: center; margin-bottom: 2px; }
.reel-dots { display: flex; gap: 6px; align-items: center; justify-content: center; flex-wrap: wrap; max-width: min(46vh, 92vw); transition: opacity .4s ease; }
.reel-dot { width: 6px; height: 6px; border-radius: 999px; background: rgba(255,255,255,.28); transition: background .2s ease, transform .2s ease; }
.reel-dot.on { background: var(--reel-primary, #2b608f); transform: scale(1.35); }
.reel-ov.reel-hidden .reel-bar, .reel-ov.reel-hidden .reel-dots { opacity: 0; }

/* SPEC268 (Bundle C) — cinematic slideshow (landscape, agent-branded, music) */
.ss-ov { position: fixed; inset: 0; z-index: 1000; background: #05070c; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.ss-empty { text-align: center; color: #e7eaf2; max-width: 340px; }
.ss-stage { position: relative; width: 100vw; height: 100vh; overflow: hidden; background: #05070c; }
.ss-slide, .ss-card { position: absolute; inset: 0; opacity: 0; transition: opacity 1.2s ease; }
.ss-slide { background-size: cover; background-position: center; }
.ss-slide.on, .ss-card.on { opacity: 1; }
.ss-slide.on { animation: vvtKenBurns 9s var(--ease) both; }
.ss-card { display: flex; align-items: center; justify-content: center; text-align: center; background: radial-gradient(120% 120% at 50% 30%, color-mix(in srgb, var(--ss-accent, #2b608f) 34%, #05070c), #05070c 72%); }
.ss-card-inner { color: #fff; padding: 6vmin; max-width: 88%; }
.ss-eyebrow { font-size: 2.1vmin; letter-spacing: .22em; text-transform: uppercase; opacity: .78; margin-bottom: 2.4vmin; }
.ss-headline { font-family: var(--serif, Georgia, 'Times New Roman', serif); font-size: 7vmin; font-weight: var(--fw-semibold); line-height: 1.1; letter-spacing: -.01em; }
.ss-sub { margin-top: 2.6vmin; font-size: 3vmin; opacity: .92; }
.ss-brand { margin-top: 5vmin; font-size: 2.2vmin; letter-spacing: .16em; text-transform: uppercase; opacity: .8; }
.ss-bar { position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); display: flex; gap: 8px; align-items: center; padding: 8px 12px; border-radius: 999px; background: rgba(14,17,25,.72); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); box-shadow: 0 10px 34px rgba(0,0,0,.5); transition: opacity .4s ease; }   /* SAFPOLISH 2026-08-25 D4 */
.ss-ov.ss-hidden .ss-bar { opacity: 0; pointer-events: none; }
.ss-btn { min-width: 38px; height: 38px; padding: 0 10px; border: 0; border-radius: 999px; background: rgba(255,255,255,.08); color: #eef1f7; font-size: var(--fs-md); line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; transition: background .16s ease, transform .16s ease; }
.ss-btn:hover { background: rgba(255,255,255,.18); }
.ss-btn:active { transform: scale(.94); }
.ss-btn.on { background: var(--ss-accent, #2b608f); color: #fff; }
.ss-btn-txt { font-size: var(--fs-caption); font-weight: var(--fw-bold); letter-spacing: .02em; }
.ss-btn-close { background: rgba(255,255,255,.05); }
.ss-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .ss-slide, .ss-card { transition: opacity .4s ease; }
  .ss-slide.on { animation: none; }
  .reel-photo.on { animation: none; }   /* SPEC280 wave 1 — the reel's Ken-Burns was the one vestibular trigger left */
}

/* SPEC197 — free-form text boxes on a card */
.tpl-ctext { position: absolute; z-index: 8; display: flex; flex-direction: column; gap: 1.5cqw; max-width: 84%; pointer-events: none; }
.tpl-ctext--tl { top: 5cqw; left: 5cqw; align-items: flex-start; text-align: left; }
.tpl-ctext--tr { top: 5cqw; right: 5cqw; align-items: flex-end; text-align: right; }
.tpl-ctext--bl { bottom: 5cqw; left: 5cqw; align-items: flex-start; text-align: left; }
.tpl-ctext--br { bottom: 5cqw; right: 5cqw; align-items: flex-end; text-align: right; }
.tpl-ctext--center { top: 50%; left: 50%; transform: translate(-50%, -50%); align-items: center; text-align: center; max-width: 80%; }
.tpl-ctext-box { font-family: var(--tpl-font); font-weight: var(--fw-black); line-height: 1.1; letter-spacing: -0.01em; text-shadow: 0 2px 10px rgba(0,0,0,.45); word-break: break-word; }
.tpl-ctext-s-s { font-size: 4cqw; }
.tpl-ctext-s-m { font-size: 6.5cqw; }
.tpl-ctext-s-l { font-size: 10cqw; }
.tpl-ctext-c-light { color: #fff; }
.tpl-ctext-c-dark { color: #14181f; text-shadow: 0 1px 4px rgba(255,255,255,.35); }
.tpl-ctext-c-accent { color: var(--tpl-accent); }
/* editor rows */
.mkt-ctext-list { display: flex; flex-direction: column; gap: 8px; }
.mkt-ctext-row { display: flex; flex-direction: column; gap: 6px; border: 1px solid var(--border); border-radius: 8px; padding: 8px; background: var(--panel); }
.mkt-ctext-controls { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.mkt-ctext-sel { flex: 1 1 auto; min-width: 0; }

/* SPEC202 — touch targets: small icon-only buttons get a 44px invisible hit-area on
   phones (visual size unchanged), matching the app's touch-first standard.
   SPEC-A11Y — added .pp-act (Post-Production Pipeline card icons, 27×24px, no
   mobile accommodation before this). */
@media (max-width: 640px) {
  .btn-icon, .icon-btn, .chip-x, .view-chip-x, .tag-x, .win-react-add, .pp-act,
  .vpr-play, .vpr-scrub-pin, .vpr-note-tcbtn, .vpr-notefilter, .vpr-stage-nameinput, .vpr-view-btn { position: relative; }
  .btn-icon::after, .icon-btn::after, .chip-x::after, .view-chip-x::after, .tag-x::after, .win-react-add::after, .pp-act::after,
  .vpr-play::after, .vpr-scrub-pin::after, .vpr-note-tcbtn::after, .vpr-notefilter::after, .vpr-stage-nameinput::after, .vpr-view-btn::after {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    min-width: 44px; min-height: 44px; width: 100%; height: 100%;
  }
}

/* ============ SPEC245 — Standup / TV board + benchmark badges ============ */
.standup { position: fixed; inset: 0; z-index: 99999; background: radial-gradient(1200px 800px at 50% -10%, #1b2030, #0b0e16 70%);
  color: #f1f5f9; display: flex; flex-direction: column; font-family: inherit; -webkit-font-smoothing: antialiased; animation: suIn .25s ease; }
@keyframes suIn { from { opacity: 0; } to { opacity: 1; } }
.su-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 18px 26px; }
.su-live { display: flex; align-items: center; gap: 8px; font-size: var(--fs-caption); font-weight: var(--fw-black); letter-spacing: .12em; text-transform: uppercase; color: #cbd5e1; }
.su-live-dot { width: 9px; height: 9px; border-radius: 50%; background: #22c55e; box-shadow: 0 0 0 4px rgba(34,197,94,.18); animation: pulse 1.6s infinite; }
.su-clock { font-size: 17px; font-weight: var(--fw-black); color: #e2e8f0; font-variant-numeric: tabular-nums; }
.su-exit { background: rgba(255,255,255,.08); color: #e2e8f0; border: 1px solid rgba(255,255,255,.16); border-radius: 999px; padding: 7px 16px; font-weight: var(--fw-bold); font-size: var(--fs-caption); cursor: pointer; min-height: 40px; }
.su-exit:hover { background: rgba(255,255,255,.16); }
.su-stage { flex: 1 1 auto; display: flex; align-items: center; justify-content: center; padding: 12px 32px; transition: background .4s ease; }
.su-stage.su-good { background: radial-gradient(900px 600px at 50% 40%, rgba(34,197,94,.12), transparent 70%); }
.su-stage.su-warn { background: radial-gradient(900px 600px at 50% 40%, rgba(245,158,11,.12), transparent 70%); }
.su-stage.su-bad  { background: radial-gradient(900px 600px at 50% 40%, rgba(239,68,68,.14), transparent 70%); }
.su-card { max-width: 1100px; width: 100%; text-align: center; animation: suCard .4s ease; }
@keyframes suCard { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.su-kicker { font-size: clamp(15px, 2.4vw, 26px); font-weight: var(--fw-black); letter-spacing: .02em; color: #94a3b8; margin-bottom: 10px; }
.su-big { font-size: clamp(64px, 17vw, 220px); font-weight: var(--fw-heavy); line-height: .95; letter-spacing: var(--ls-tight);
  background: linear-gradient(180deg, #ffffff, #c7d2fe); -webkit-background-clip: text; background-clip: text; color: transparent; }
.su-stage.su-good .su-big { background: linear-gradient(180deg, #fff, #86efac); -webkit-background-clip: text; background-clip: text; }
.su-stage.su-bad .su-big  { background: linear-gradient(180deg, #fff, #fca5a5); -webkit-background-clip: text; background-clip: text; }
.su-bar { max-width: 640px; margin: 18px auto 0; height: 14px; border-radius: 999px; background: rgba(255,255,255,.1); overflow: hidden; }
.su-bar-fill { height: 100%; border-radius: 999px; background: var(--accent); transition: width .6s ease; }
.su-sub { font-size: clamp(16px, 2.6vw, 30px); font-weight: var(--fw-bold); color: #e2e8f0; margin-top: 18px; }
.su-bench { display: inline-block; margin-top: 14px; font-size: clamp(13px, 1.8vw, 19px); font-weight: var(--fw-black); padding: 5px 16px; border-radius: 999px; }
.su-bench-up { color: #86efac; background: rgba(34,197,94,.14); }
.su-bench-down { color: #fca5a5; background: rgba(239,68,68,.14); }
.su-bench-flat { color: #cbd5e1; background: rgba(255,255,255,.08); }
.su-chips { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 20px; }
.su-chip { font-size: clamp(14px, 1.8vw, 20px); font-weight: var(--fw-bold); color: #e2e8f0; background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14); border-radius: 999px; padding: 7px 18px; }
.su-list { list-style: none; margin: 18px auto 0; padding: 0; max-width: 760px; text-align: left; }
.su-list li { font-size: clamp(15px, 2vw, 22px); font-weight: var(--fw-semibold); color: #e2e8f0; padding: 9px 4px; border-bottom: 1px solid rgba(255,255,255,.08); }
.su-list li::before { content: '→ '; color: var(--accent); font-weight: var(--fw-black); }
.su-prog { height: 4px; background: var(--accent, #2b608f); width: 0%; transition: width .12s linear; }
.su-dots { display: flex; gap: 9px; justify-content: center; padding: 16px; }
.su-dot { width: 9px; height: 9px; border-radius: 50%; background: rgba(255,255,255,.22); }
.su-dot.on { background: #fff; transform: scale(1.25); }
.su-hint { text-align: center; font-size: var(--fs-meta); color: #64748b; padding-bottom: 16px; letter-spacing: var(--ls-label); }
.su-paused .su-prog { opacity: .35; }
.su-paused .su-live-dot { animation: none; background: #f59e0b; }
/* reusable self-benchmark badge (KPI cards) */
.bm-badge { display: inline-flex; align-items: center; font-size: 10.5px; font-weight: var(--fw-black); padding: 1px 7px; border-radius: 999px; margin-left: 6px; white-space: nowrap; }
.bm-up { color: var(--ok, #16a34a); background: var(--ok-soft, rgba(22,163,74,.12)); }
.bm-down { color: var(--danger, #ef4444); background: var(--danger-soft, rgba(239,68,68,.12)); }
.bm-flat { color: var(--muted); background: var(--subtle); }

/* ============ SPEC246 — SOP sign-off (read-receipt) ============ */
.sop-signoff { margin-top: 18px; padding: 14px 16px; border: 1px solid var(--border); border-radius: 12px; background: var(--subtle); }
.sop-signoff-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; }
.sop-signoff-title { display: inline-flex; align-items: center; gap: 5px; font-weight: var(--fw-black); font-size: 13.5px; }
.sop-signoff-me { font-size: var(--fs-caption); font-weight: var(--fw-bold); }
.sop-signoff-me.done { color: var(--ok); }

/* ============ SPEC247 — bank reconciliation (Books) ============ */
.rec-result { margin-top: 12px; }
.rec-line { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 7px 0; border-bottom: 1px dashed var(--border); font-size: var(--fs-body); }
.rec-line strong { font-variant-numeric: tabular-nums; font-size: var(--fs-md); }
.rec-verdict { margin-top: 12px; padding: 10px 14px; border-radius: 10px; font-weight: var(--fw-black); font-size: var(--fs-body); }
.rec-verdict.ok { color: var(--ok); background: var(--ok-soft, rgba(22,163,74,.12)); border: 1px solid color-mix(in srgb, var(--ok) 30%, transparent); }
.rec-verdict.off { color: var(--danger); background: var(--danger-soft, rgba(239,68,68,.12)); border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent); }
.rec-txns { margin-top: 12px; }
.rec-hist { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--muted); padding: 4px 0; border-bottom: 1px solid var(--border); }

/* ============ SPEC248 — pre-handoff photo-proof gate ============ */
.proof-list { display: flex; flex-direction: column; gap: 6px; }
.proof-row { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border: 1px solid var(--border); border-radius: 10px; cursor: pointer; font-weight: var(--fw-semibold); font-size: var(--fs-body); min-height: 44px; }
.proof-row input { width: 20px; height: 20px; flex: 0 0 20px; }
.proof-row.got { background: var(--ok-soft, rgba(22,163,74,.1)); border-color: color-mix(in srgb, var(--ok) 28%, transparent); }
.proof-row.got span { color: var(--ok); }
.proof-warn { margin-top: 8px; padding: 9px 13px; border-radius: 10px; font-size: 12.5px; font-weight: var(--fw-bold); color: var(--danger); background: var(--danger-soft, rgba(239,68,68,.1)); border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent); }

/* ============ SPEC249 — Client Studio Snapshot (sell-ready one-pager) ============ */
.snap { max-width: 640px; }
.snap-head { display: flex; align-items: center; gap: 14px; padding-bottom: 14px; border-bottom: 2px solid var(--accent); margin-bottom: 16px; }
.snap-logo { width: 56px; height: 56px; border-radius: 12px; background: #fff center/cover no-repeat; flex: 0 0 56px; box-shadow: 0 0 0 1px var(--border); }
.snap-for { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.snap-agent { font-size: 20px; font-weight: var(--fw-black); line-height: 1.1; }
.snap-broker { font-size: var(--fs-caption); color: var(--muted); font-weight: var(--fw-semibold); }
.snap-title { font-size: var(--fs-h1); font-weight: var(--fw-black); margin: 0 0 4px; }
.snap-sub { font-size: 13.5px; color: var(--muted); margin: 0 0 16px; line-height: var(--lh-normal); }
.snap-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px; }
.snap-stat { background: var(--subtle); border: 1px solid var(--border); border-radius: 14px; padding: 16px 12px; text-align: center; }
.snap-big { font-size: var(--fs-hero); font-weight: var(--fw-heavy); line-height: 1; color: var(--accent-hover); letter-spacing: var(--ls-tight); }
.snap-label { font-size: 12.5px; font-weight: var(--fw-bold); margin-top: 6px; }
.snap-stat-sub { font-size: var(--fs-micro); color: var(--muted); margin-top: 2px; }
.snap-recent { margin-top: 18px; }
.snap-recent-head { font-size: var(--fs-meta); font-weight: var(--fw-black); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
.snap-recent-row { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.snap-recent-addr { font-weight: var(--fw-semibold); }
.snap-recent-date { color: var(--muted); white-space: nowrap; }
.snap-close { margin-top: 18px; font-size: var(--fs-body); font-weight: var(--fw-bold); text-align: center; color: var(--text); }

/* ============================================================
   SPEC256 — Output Matrix slide-over (wave-2 Bundle A). Reuses the Marketing-Kit
   visual language: bg cards, hairline borders, muted uppercase section heads.
   ============================================================ */
.omx-sub { margin-bottom: 14px; }
.omx-addr { font-weight: var(--fw-black); font-size: 15px; color: var(--text); }
.omx-hint { font-size: 12.5px; color: var(--muted); margin-top: 3px; line-height: var(--lh-normal); }
.omx-count { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--accent); margin-top: 6px; letter-spacing: .01em; }
.mkit-omx { margin-top: 9px; }
.omx-section-h { font-weight: var(--fw-bold); font-size: 11.5px; letter-spacing: .03em; text-transform: uppercase; color: var(--muted); margin: 4px 0 9px; }

.omx-list { display: flex; flex-direction: column; gap: 9px; }
.omx-row { background: var(--bg); border: 1px solid var(--border); border-radius: 12px; padding: 11px 12px 12px; transition: border-color .14s ease, box-shadow .14s ease; }
.omx-row:hover { border-color: color-mix(in oklch, var(--accent) 45%, var(--border)); }
.omx-row--muted { opacity: .72; }
.omx-row-h { display: flex; align-items: flex-start; gap: 10px; }
.omx-ico { flex: 0 0 auto; color: var(--accent); display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; margin-top: 1px; }
.omx-row-main { flex: 1 1 auto; min-width: 0; }
.omx-name { font-weight: var(--fw-bold); font-size: 13.5px; color: var(--text); }
.omx-from { font-size: var(--fs-micro); color: var(--muted); margin-top: 2px; }
.omx-status { flex: 0 0 auto; font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .02em; text-transform: uppercase; padding: 3px 8px; border-radius: 999px; white-space: nowrap; }
.omx-status--ready { background: var(--ok-soft); color: var(--ok); }
.omx-status--delivered { background: var(--ok-soft); color: var(--ok); }
.omx-status--building { background: var(--warn-soft); color: var(--warn); }
.omx-status--capture { background: var(--bg); color: var(--muted); border: 1px solid var(--border); }
.omx-row-hint { font-size: var(--fs-meta); color: var(--muted); line-height: var(--lh-normal); margin: 7px 0 9px; }
.omx-row-acts { display: flex; flex-wrap: wrap; gap: 7px; }

.omx-qr { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.omx-qr-grid { display: flex; gap: 12px; align-items: stretch; }
.omx-qr-box { flex: 0 0 116px; width: 116px; height: 116px; background: #fff; border: 1px solid var(--border); border-radius: 10px; display: flex; align-items: center; justify-content: center; overflow: hidden; padding: 5px; }
.omx-qr-box svg { width: 100%; height: 100%; display: block; }
.omx-qr-side { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 9px; }
.omx-qr-cap { font-size: var(--fs-meta); color: var(--muted); line-height: var(--lh-normal); }
.omx-qr-btns { display: flex; flex-wrap: wrap; gap: 7px; }

.omx-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-top: 14px; }
.omx-stat { background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 9px 6px; text-align: center; }
.omx-stat-n { font-weight: var(--fw-black); font-size: var(--fs-md); color: var(--text); line-height: 1.1; }
.omx-stat--last .omx-stat-n { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--muted); }
.omx-stat-l { font-size: 9.5px; color: var(--muted); margin-top: 3px; letter-spacing: .01em; }
.omx-note { font-size: var(--fs-micro); color: var(--muted); line-height: var(--lh-normal); margin-top: 11px; }

@media (max-width: 520px) {
  .omx-qr-grid { flex-direction: column; align-items: center; }
  .omx-qr-side { text-align: center; }
  .omx-qr-btns { justify-content: center; }
  .omx-stats { grid-template-columns: repeat(2, 1fr); }
}

/* SPEC256 review fixes — 'Set up' status (invites go-live setup, not "Not captured")
   + field-grade tap targets for the in-panel actions on phones. */
.omx-status--setup { background: color-mix(in oklch, var(--accent) 16%, var(--card)); color: var(--accent); }
@media (max-width: 640px) {
  .omx-row-acts .btn, .omx-qr-btns .btn { min-height: 40px; }
}

/* SPEC257 — Automation Outbox (wave-2 Bundle B). Queued message drafts awaiting review. */
.ob-card .ob-badge { display: inline-flex; min-width: 20px; height: 20px; padding: 0 6px; align-items: center; justify-content: center; border-radius: 999px; background: var(--accent); color: #fff; font-size: var(--fs-micro); font-weight: var(--fw-black); vertical-align: middle; }
.ob-row { display: flex; gap: 12px; align-items: flex-start; padding: 11px 0; border-bottom: 1px solid var(--border); }
.ob-row:last-child { border-bottom: 0; }
.ob-main { flex: 1 1 auto; min-width: 0; }
.ob-head { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.ob-who { font-weight: var(--fw-bold); font-size: var(--fs-caption); color: var(--text); }
.ob-kind { font-size: var(--fs-micro); color: var(--muted); background: var(--bg); border: 1px solid var(--border); border-radius: 999px; padding: 1px 8px; }
.ob-prev { font-size: var(--fs-meta); color: var(--muted); margin-top: 4px; line-height: var(--lh-normal); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.ob-acts { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; }
@media (max-width: 560px) { .ob-row { flex-direction: column; } .ob-acts { flex-direction: row; } }

/* SPEC257 review fix — field-grade tap targets for the Outbox actions on phones */
@media (max-width: 640px) { .ob-acts .btn { min-height: 40px; } }

/* SPEC258 — Studio Packs settings card (wave-2 Bundle C) */
.pack-incl { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.pack-chip { font-size: var(--fs-micro); color: var(--muted); background: var(--bg); border: 1px solid var(--border); border-radius: 999px; padding: 3px 9px; }
.pack-safe { font-size: 11.5px; color: var(--muted); margin: 10px 0 0; display: flex; align-items: center; gap: 4px; }

/* SPEC259 — Guided Studio Setup Wizard (wave-2 Bundle C) */
.sw-progress { display: flex; align-items: center; gap: 10px; margin: 12px 0; }
.sw-bar { flex: 1 1 auto; height: 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 999px; overflow: hidden; }
.sw-bar-fill { height: 100%; background: var(--accent); border-radius: 999px; transition: width .3s ease; }
.sw-pct { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); white-space: nowrap; }
.sw-fast { display: flex; gap: 12px; align-items: center; justify-content: space-between; padding: 12px 14px; border: 1px solid color-mix(in oklch, var(--accent) 40%, var(--border)); background: color-mix(in oklch, var(--accent) 7%, var(--card)); border-radius: 12px; margin-top: 6px; }
.sw-fast-main { min-width: 0; }
.sw-fast-t { font-weight: var(--fw-bold); font-size: var(--fs-caption); color: var(--text); }
.sw-fast-h { font-size: var(--fs-meta); color: var(--muted); margin-top: 3px; line-height: var(--lh-normal); }
.sw-row { align-items: flex-start; }
.sw-row-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.sw-row-hint { font-size: var(--fs-meta); color: var(--muted); margin-top: 2px; line-height: var(--lh-normal); }
.sw-opt { font-size: 10.5px; color: var(--muted); font-weight: var(--fw-semibold); }
.sw-card-h { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.sw-card-x { border: 0; background: transparent; color: var(--muted); cursor: pointer; font-size: var(--fs-body); line-height: 1; padding: 4px 6px; border-radius: 6px; }
.sw-card-x:hover { background: var(--bg); color: var(--text); }
.sw-card-sub { font-size: 12.5px; color: var(--muted); margin: 8px 0 10px; }

/* SPEC260 — Studio HQ (owner cockpit, wave-2 Bundle C) */
.hq-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 14px; }
.hq-card { border: 1px solid var(--border); border-radius: 12px; padding: 13px 14px; background: var(--bg); display: flex; flex-direction: column; }
.hq-card-h { display: flex; gap: 10px; align-items: flex-start; }
.hq-ico { font-size: var(--fs-lg); line-height: var(--lh-tight); flex: 0 0 auto; }
.hq-card-tt { min-width: 0; }
.hq-card-t { font-weight: var(--fw-bold); font-size: 13.5px; color: var(--text); }
.hq-card-s { font-size: 11.5px; color: var(--accent); font-weight: var(--fw-bold); margin-top: 1px; }
.hq-card-hint { font-size: var(--fs-meta); color: var(--muted); line-height: var(--lh-normal); margin: 8px 0 10px; flex: 1 1 auto; }
.hq-card-acts { display: flex; flex-wrap: wrap; gap: 7px; }
@media (max-width: 560px) { .hq-grid { grid-template-columns: 1fr; } }

/* SPEC261 — Referral flywheel card (wave-2 Bundle D) */
.refw-head { display: flex; gap: 22px; margin: 10px 0 2px; }
.refw-n { font-weight: var(--fw-black); font-size: 20px; color: var(--text); line-height: 1.1; }
.refw-l { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.refw-section-h { font-weight: var(--fw-bold); font-size: var(--fs-micro); letter-spacing: .03em; text-transform: uppercase; color: var(--muted); margin: 14px 0 7px; }
.refw-list { display: flex; flex-direction: column; gap: 2px; }
.refw-row { display: flex; align-items: center; gap: 9px; width: 100%; text-align: left; background: transparent; border: 0; border-radius: 8px; padding: 7px 8px; cursor: pointer; color: var(--text); font: inherit; }
.refw-row:hover { background: var(--bg); }
.refw-ask { cursor: default; }
.refw-name { flex: 1 1 auto; min-width: 0; font-weight: var(--fw-semibold); font-size: var(--fs-caption); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.refw-sub { font-weight: 400; color: var(--muted); font-size: var(--fs-meta); }
.refw-badge { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
.refw-val { font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); white-space: nowrap; }

/* SPEC262 — Studio Index scorecard (wave-2 Bundle D) */
.si-overall { display: flex; align-items: center; gap: 16px; margin: 14px 0; padding: 14px; border: 1px solid var(--border); border-radius: 12px; background: var(--bg); }
.si-grade { width: 54px; height: 54px; border-radius: 14px; display: flex; align-items: center; justify-content: center; font-weight: var(--fw-black); font-size: 26px; color: #fff; background: var(--muted); flex: 0 0 auto; }
.si-grade--a { background: #00a86b; }
.si-grade--b { background: #4a9d2f; }
.si-grade--c { background: #cab641; color: #3a2f06; }
.si-grade--d { background: #e0773b; }
.si-grade--na { background: var(--muted); font-size: 20px; }
.si-index { font-weight: var(--fw-black); font-size: var(--fs-lg); color: var(--text); }
.si-index-l { font-size: var(--fs-meta); margin-top: 2px; }
.si-list { display: flex; flex-direction: column; }
.si-row { display: flex; align-items: center; gap: 12px; padding: 10px 2px; border-bottom: 1px solid var(--border); }
.si-row:last-child { border-bottom: 0; }
.si-row-main { flex: 1 1 auto; min-width: 0; }
.si-label { font-weight: var(--fw-semibold); font-size: var(--fs-caption); color: var(--text); }
.si-hint { font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.si-val { font-weight: var(--fw-black); font-size: 15px; color: var(--text); white-space: nowrap; }
.si-band { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .02em; text-transform: uppercase; padding: 3px 9px; border-radius: 999px; white-space: nowrap; flex: 0 0 auto; }
.si-band--great { background: var(--ok-soft); color: var(--ok); }
.si-band--good { background: var(--ok-soft); color: var(--ok); }
.si-band--typical { background: var(--warn-soft); color: var(--warn); }
.si-band--work { background: var(--danger-soft); color: var(--danger); }
.si-band--na { background: var(--bg); color: var(--muted); border: 1px solid var(--border); }

/* SPEC278 — the in-Hub Book a Shoot view: the public order page embedded full-height */
.bookshoot-frame { width: 100%; height: calc(100vh - 150px); min-height: 480px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--bg); box-shadow: var(--shadow-sm); display: block; }
.bookshoot-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ============================================================
   SPEC278b — “Book a Shoot” (order.html): the Tonomo-mechanics ×
   Pixieset-skin editorial layer (RESEARCH-TONOMO-VISUAL-2026-07-01.md).
   Real VVT photography on every step · paper/ink chapter bands ·
   Fraunces display serif (self-hosted, OFL) · price styled as a quiet
   caption and never before the package step. Accent stays scoped to
   .order; --order-on-accent is the auto-contrast ink from order.js.
   ============================================================ */
@font-face {
  font-family: 'Fraunces';
  src: url('assets/fraunces-var.woff2') format('woff2');
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
.order {
  --order-accent: var(--accent);
  --order-on-accent: #ffffff;
  --order-serif: 'Fraunces', Georgia, 'Times New Roman', serif;
  /* chapter tone (paper by default) */
  --o-bg: var(--bg);
  --o-panel: var(--panel);
  --o-text: var(--text);
  --o-muted: var(--muted);
  --o-faint: var(--faint);
  --o-line: var(--border);
  --o-line-strong: var(--border-strong);
  min-height: 100svh;
  background: var(--o-bg);
  color: var(--o-text);
  transition: background .5s var(--ease);
  padding-bottom: calc(84px + env(safe-area-inset-bottom));
}
.order[data-tone="ink"] {
  --o-bg: #101013;
  --o-panel: #17171b;
  --o-text: #f4f2ee;
  --o-muted: rgba(255, 255, 255, .64);
  --o-faint: rgba(255, 255, 255, .55);
  --o-line: rgba(255, 255, 255, .13);
  --o-line-strong: rgba(255, 255, 255, .24);
}
html[data-theme="dark"] .order { --o-bg: #141416; --o-panel: #1a1a1d; }
html[data-theme="dark"] .order[data-tone="ink"] { --o-bg: #1c1c1f; --o-panel: #232327; }
.order button { font-family: var(--font); }
.order img { filter: saturate(.92) contrast(1.04); }
/* SPEC293 — Portal Studio font packs. order.js sets [data-fontpack] on .order;
   each pack overrides the display serif (--order-serif) only. The text face
   stays Inter (the body --font). All faces are self-hosted or system-safe —
   NO CDN. 'editorial' is the shipped default and needs no rule (the base
   --order-serif above already carries Fraunces). */
.order[data-fontpack="classic"] { --order-serif: Georgia, 'Times New Roman', 'Iowan Old Style', serif; }
.order[data-fontpack="modern"]  { --order-serif: 'Inter var', Inter, system-ui, -apple-system, 'Segoe UI', sans-serif; }
.order[data-fontpack="grotesk"] { --order-serif: 'Inter var', Inter, ui-sans-serif, system-ui, sans-serif; }

/* content rhythm: a centered column; media may run wider */
.order-col { max-width: 720px; margin-inline: auto; padding-inline: clamp(20px, 5vw, 32px); }
.order-wide { max-width: 1080px; margin-inline: auto; padding-inline: clamp(20px, 4vw, 32px); }
.order-step { padding-block: clamp(56px, 9vh, 110px) 24px; min-height: calc(100svh - 190px); animation: order-in .56s cubic-bezier(.16, 1, .3, 1) both; }
@keyframes order-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* ---- masthead ---- */
.order-mast { display: flex; align-items: center; justify-content: space-between; gap: 12px; max-width: 1080px; margin-inline: auto; padding: 18px clamp(20px, 4vw, 32px) 6px; }
.order-mast-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.order-mast-headshot { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; border: 2px solid var(--order-accent); }
.order-mast-logo { height: 30px; max-width: 140px; object-fit: contain; }
.order-mast-name { font-family: var(--order-serif); font-size: var(--fs-md); letter-spacing: .1px; color: var(--o-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.order-mast-credit { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .18em; text-transform: uppercase; color: var(--o-muted); white-space: nowrap; }

/* ---- progress rail ---- */
.order-rail { max-width: 720px; margin-inline: auto; padding: 8px clamp(20px, 5vw, 32px) 0; }
.order-rail.hide { display: none; }
.order-rail-meta { font-family: var(--order-serif); font-size: 12.5px; letter-spacing: .12em; text-transform: uppercase; color: var(--o-muted); margin-bottom: 8px; }
.order-rail-track { position: relative; height: 1px; background: var(--o-line); overflow: visible; }
.order-rail-fill { height: 1px; background: var(--order-accent); transition: width .5s var(--ease); }
.order-rail-nodes { display: flex; justify-content: space-between; margin-top: -4px; position: relative; }
.order-rail-node { width: 7px; height: 7px; border-radius: 50%; border: 1px solid var(--o-line-strong); background: var(--o-bg); padding: 0; cursor: default; transition: all var(--t-base) var(--ease); position: relative; }
.order-rail-node::after { content: ''; position: absolute; inset: -10px; }
.order-rail-node.done { border-color: var(--order-accent); background: var(--order-accent); cursor: pointer; }
.order-rail-node.cur { border-color: var(--order-accent); background: var(--o-bg); transform: scale(1.35); }

/* ---- editorial step head: eyebrow · ghost numeral · serif title ---- */
.order-eyebrow { font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: .22em; text-transform: uppercase; color: var(--order-accent); margin-bottom: 14px; }
.order-eyebrow.onink, .order[data-tone="ink"] .order-eyebrow { color: var(--o-muted); }
.order-title-row { position: relative; }
.order-ghost-no { position: absolute; right: -8px; top: 50%; transform: translateY(-58%); font-family: var(--order-serif); font-weight: 300; font-size: clamp(80px, 15vw, 150px); line-height: 1; color: transparent; -webkit-text-stroke: 1px var(--o-line-strong); pointer-events: none; user-select: none; }
.order[data-tone="ink"] .order-ghost-no { -webkit-text-stroke: 0; color: rgba(255, 255, 255, .07); }
.order-step-title { font-family: var(--order-serif); font-weight: 420; font-size: clamp(32px, 6vw, 54px); letter-spacing: -.7px; line-height: 1.08; margin: 0 0 12px; color: var(--o-text); outline: none; text-wrap: balance; position: relative; }
.order-step-sub { color: var(--o-muted); font-size: 15.5px; line-height: 1.6; margin: 0 0 clamp(28px, 4vh, 44px); max-width: 54ch; }
.order-step-head { margin-bottom: 6px; }
.order-optional { font-weight: var(--fw-medium); font-size: var(--fs-micro); letter-spacing: .05em; text-transform: uppercase; color: var(--o-muted); }

/* ---- the 21:9 chapter image ---- */
.order-chapter { margin: 0 0 clamp(28px, 5vh, 48px); border-radius: 14px; overflow: hidden; box-shadow: var(--shadow); }
.order-chapter-img { display: block; width: 100%; aspect-ratio: 21 / 9; object-fit: cover; }

/* ---- fields ---- */
.order-field { margin-bottom: 26px; position: relative; }
.order-label { display: block; font-size: 11.5px; font-weight: var(--fw-bold); letter-spacing: .08em; text-transform: uppercase; color: var(--o-muted); margin-bottom: 9px; }
.order-input { width: 100%; box-sizing: border-box; font: inherit; font-size: var(--fs-md); color: var(--o-text); background: var(--o-panel); border: 1px solid var(--o-line-strong); border-radius: 12px; padding: 14px 15px; transition: border-color var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease); }
.order-input::placeholder { color: var(--o-faint); }
.order-input:focus { outline: none; border-color: var(--order-accent); box-shadow: var(--focus-ring-soft); }
.order-input.area { min-height: 84px; resize: vertical; }
.order-input.sqft-lg { max-width: 230px; font-family: var(--order-serif); font-size: 26px; font-variant-numeric: tabular-nums; letter-spacing: -.3px; }
.order-input.mini { width: 86px; text-align: center; font-variant-numeric: tabular-nums; }
.order-sqft-row, .order-bb-row { display: flex; align-items: center; gap: 10px; }
.order-sqft-unit { color: var(--o-muted); font-size: var(--fs-caption); }
.order-err { color: #b45309; font-size: var(--fs-caption); font-weight: var(--fw-semibold); margin-top: 7px; display: none; }
html[data-theme="dark"] .order .order-err, .order[data-tone="ink"] .order-err { color: var(--warn); }
.order-err.show { display: block; }
.order-concierge { margin-top: 10px; font-family: var(--order-serif); font-size: 15.5px; color: var(--o-muted); }
.order-prefill { font-size: 12.5px; color: var(--o-muted); margin-top: 7px; }
.order-prefill:empty { display: none; }

/* address suggestions (Photon) */
.order-sugg { position: absolute; left: 0; right: 0; top: calc(100% - 22px); z-index: 30; display: none; flex-direction: column; background: var(--o-panel); border: 1px solid var(--o-line-strong); border-radius: 12px; overflow: hidden; box-shadow: var(--shadow-lg); }
.order-field.addr .order-sugg { top: auto; }
.order-sugg.show { display: flex; }
.order-sugg-item { font: inherit; font-size: 14.5px; text-align: left; padding: 12px 15px; background: none; border: 0; border-bottom: 1px solid var(--o-line); color: var(--o-text); cursor: pointer; }
.order-sugg-item:last-child { border-bottom: 0; }
.order-sugg-item:hover, .order-sugg-item:focus-visible { background: color-mix(in srgb, var(--order-accent) 8%, var(--o-panel)); }
.order-attrib { font-size: var(--fs-micro); color: var(--o-muted); margin-top: 6px; }
.order-attrib a { color: var(--o-muted); text-decoration: underline; }

/* ---- chips ---- */
.order-chiprow { display: flex; flex-wrap: wrap; gap: 8px; }
.order-chip { font: inherit; font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--o-text); background: var(--o-panel); border: 1px solid var(--o-line-strong); border-radius: 999px; padding: 10px 16px; min-height: 44px; cursor: pointer; transition: border-color var(--t-base) var(--ease), background var(--t-base) var(--ease), color var(--t-base) var(--ease); }
.order-chip:hover { border-color: var(--order-accent); }
.order-chip.on { background: var(--order-accent); border-color: var(--order-accent); color: var(--order-on-accent); }

/* ---- landing hero (ink chapter, real twilight) ---- */
.order-hero { position: relative; overflow: hidden; min-height: min(78svh, 760px); display: flex; align-items: flex-end; }
.order-hero-media { position: absolute; inset: 0; }
.order-hero-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.order-hero-img.kenburns { animation: order-kb 1.4s var(--ease) both; }
@keyframes order-kb { from { transform: scale(1.06); } to { transform: scale(1); } }
.order-hero-scrim { position: absolute; inset: 0; background:
  linear-gradient(to top, rgba(10, 10, 12, .82) 0%, rgba(10, 10, 12, .38) 40%, transparent 70%),
  linear-gradient(to bottom, rgba(10, 10, 12, .38), transparent 26%); }
.order-hero-body { position: relative; width: 100%; max-width: 1080px; margin-inline: auto; padding: clamp(48px, 10vh, 110px) clamp(20px, 4vw, 32px) clamp(40px, 7vh, 72px); color: #fff; }
.order-hero-title { font-family: var(--order-serif); font-weight: 400; font-size: clamp(42px, 8.2vw, 82px); line-height: 1.05; letter-spacing: -1px; margin: 0 0 16px; color: #fff; text-shadow: 0 1px 24px rgba(0, 0, 0, .25); outline: none; text-wrap: balance; max-width: 16ch; }
.order-hero-sub { color: rgba(255, 255, 255, .82); font-size: clamp(15px, 2vw, 17px); line-height: 1.6; max-width: 46ch; margin: 0 0 30px; }
.order-resume { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 18px; }
.order-fine { font-size: 12.5px; color: var(--o-muted); margin-top: 18px; line-height: 1.6; }
.order-fine.center { text-align: center; }
.order-fine.onink { color: rgba(255, 255, 255, .55); }
.order-logos-kicker { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .2em; text-transform: uppercase; color: var(--o-faint); text-align: center; margin: clamp(32px, 6vh, 56px) 0 18px; }
.order-logos { display: flex; align-items: center; justify-content: center; gap: clamp(18px, 4vw, 40px); flex-wrap: wrap; padding-bottom: 12px; }
/* the harvested logos mix light and dark art — neutral press-strip chips read consistently on any tone */
.order-logo-img { height: 34px; width: auto; max-width: 120px; object-fit: contain; background: #fff; padding: 5px 12px; border-radius: 6px; box-sizing: border-box; filter: grayscale(1); opacity: .82; }

/* ---- buttons ---- */
.order-btn { font: inherit; font-size: 15px; font-weight: var(--fw-bold); border-radius: 999px; padding: 14px 26px; min-height: 48px; cursor: pointer; border: 1px solid transparent; transition: transform var(--t-fast) var(--ease-spring), box-shadow var(--t-base) var(--ease), background var(--t-base) var(--ease); }
.order-btn.primary { background: var(--order-accent); color: var(--order-on-accent); box-shadow: var(--shadow); }
.order-btn.primary:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
.order-btn.primary:active { transform: none; }
.order-btn.primary:disabled { opacity: .6; cursor: default; transform: none; }
.order-btn.ghost { background: transparent; border-color: var(--o-line-strong); color: var(--o-text); }
.order-btn.ghost:hover { border-color: var(--order-accent); }
.order-btn.xl { font-size: var(--fs-md); padding: 17px 32px; min-height: 56px; }
.order-cta-arrow { display: inline-block; transition: transform var(--t-base) var(--ease); }
.order-btn:hover .order-cta-arrow { transform: translateX(3px); }
.order-back { background: none; border: 0; padding: 6px 0; font: inherit; font-size: 12.5px; font-weight: var(--fw-bold); letter-spacing: .1em; text-transform: uppercase; color: var(--o-muted); cursor: pointer; border-bottom: 1px solid var(--o-line-strong); }
.order-back:hover { color: var(--o-text); border-color: var(--o-text); }
.order-link { background: none; border: 0; padding: 4px 0; font: inherit; font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--order-accent); cursor: pointer; }
.order-hero .order-link { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
.order-link:hover { text-decoration: underline; }
.order-link.quiet { color: var(--o-muted); font-weight: var(--fw-medium); }
.order-hero .order-link.quiet { color: rgba(255, 255, 255, .6); }
.order-nav { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: clamp(28px, 5vh, 44px); }
.order-nav.confirm { margin-top: 24px; }

/* ---- vision cards ---- */
.order-vision-grid { display: grid; gap: 12px; }
.order-vision-card { text-align: left; background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 14px; padding: 22px 24px; cursor: pointer; box-shadow: var(--shadow-sm); display: flex; flex-direction: column; gap: 6px; transition: border-color var(--t-base) var(--ease), transform var(--t-fast) var(--ease-spring), box-shadow var(--t-base) var(--ease); }
.order-vision-card:hover { border-color: var(--order-accent); transform: translateY(-2px); box-shadow: var(--shadow); }
.order-vision-card.sel { border: 1.5px solid var(--order-accent); }
.order-vision-title { font-family: var(--order-serif); font-size: 21px; color: var(--o-text); }
.order-vision-sub { color: var(--o-muted); font-size: 13.5px; line-height: var(--lh-normal); }

/* SPEC315 — the welcome-back bar (returning agent memory link) */
.order-wb { max-width: 1080px; margin: 0 auto; padding-inline: clamp(20px, 4vw, 32px); }
.order-wb-card { display: flex; align-items: center; justify-content: space-between; gap: 18px; flex-wrap: wrap;
  background: var(--o-panel); border: 1px solid color-mix(in srgb, var(--order-accent) 35%, var(--o-line)); border-radius: 14px;
  padding: 18px 22px; box-shadow: var(--shadow); margin-bottom: 26px; }
.order-wb-hi { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.order-wb-name { font-family: var(--order-serif); font-size: clamp(19px, 2.4vw, 24px); letter-spacing: -.2px; color: var(--o-text); }
.order-wb-sub { font-size: var(--fs-caption); color: var(--o-muted); }
.order-wb-row { display: flex; gap: 10px; flex-wrap: wrap; }

/* ---- package ladder (ink chapter — the photos glow) ---- */
.order-pkg-grid { display: grid; gap: 28px; grid-template-columns: 1fr; max-width: 1160px; margin: 0 auto; padding-inline: clamp(20px, 4vw, 32px); }
/* Vincent 2026-07-08 — the two-up grid is RETIRED: every package is a grand
   full-width band, read top-down from the Signature anchor. The desktop
   sideways-band rules live BELOW the base .order-pkg rules (cascade). */
.order-pkg { text-align: left; background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 14px; padding: 0 0 18px; cursor: pointer; overflow: hidden; display: flex; flex-direction: column; gap: 12px; transition: border-color var(--t-base) var(--ease), transform var(--t-fast) var(--ease-spring), box-shadow var(--t-base) var(--ease); }
.order-pkg:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.order-pkg:hover .order-pkg-img { transform: scale(1.03); }
.order-pkg.sel { border: 1.5px solid var(--order-accent); box-shadow: var(--shadow-lg); }
.order-pkg-media { position: relative; aspect-ratio: 3 / 2; overflow: hidden; }
.order-pkg-img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .7s var(--ease); }
.order-pkg-scrim { position: absolute; inset: 0; background: linear-gradient(to top, rgba(10, 10, 12, .78) 0%, rgba(10, 10, 12, .35) 38%, transparent 68%); }
.order-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -62%); width: 54px; height: 54px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, .85); color: #fff; font-size: var(--fs-md); display: flex; align-items: center; justify-content: center; padding-left: 4px; background: rgba(10, 10, 12, .25); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); }   /* SAFPOLISH 2026-08-25 D4 */
.order-pkg-onimg { position: absolute; left: 20px; right: 20px; bottom: 16px; color: #fff; display: flex; flex-direction: column; gap: 4px; }
.order-pkg-badge { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .16em; text-transform: uppercase; color: #fff; border-bottom: 1px solid var(--order-accent); padding-bottom: 3px; align-self: flex-start; text-shadow: 0 1px 12px rgba(0, 0, 0, .4); }
.order-pkg-name { font-family: var(--order-serif); font-size: clamp(24px, 3vw, 30px); letter-spacing: -.3px; text-shadow: 0 1px 20px rgba(0, 0, 0, .35); }
.order-pkg-tag { color: var(--o-muted); font-size: 13.5px; line-height: 1.55; padding: 2px 20px 0; max-width: 56ch; }
.order-pkg-inc { display: flex; flex-direction: column; padding: 4px 20px 0; flex: 1 1 auto; }   /* SPEC289 — absorb the slack so prices align at the bottom across equal-height cards */
.order-pkg-inc-row { font-size: var(--fs-meta); font-weight: var(--fw-semibold); letter-spacing: .05em; text-transform: uppercase; color: var(--o-muted); padding: 7px 0; border-top: 1px solid var(--o-line); }
.order-pkg-inc-row:first-child { border-top: 0; }
.order-pkg-pricefoot { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 10px 20px 0; border-top: 1px solid var(--o-line); margin: 8px 20px 0; padding-inline: 0; }
.order-pkg-selmark { font-family: var(--order-serif); font-size: var(--fs-body); letter-spacing: var(--ls-label); color: var(--o-text); }
.order-pkg-price { display: flex; flex-direction: column; align-items: flex-end; font-family: var(--order-serif); font-variant-numeric: tabular-nums; font-size: 27px; letter-spacing: -.4px; color: var(--o-text); }
.order-price-from { font-family: var(--font); font-size: 10.5px; font-weight: var(--fw-semibold); letter-spacing: .14em; text-transform: uppercase; color: var(--o-muted); }
/* Vincent 2026-07-08 — desktop turns each package band sideways: a tall photo
   on the left, the sell (tagline, best-for, the full include list in two
   columns, price) on the right. Mobile keeps the stacked flex column above. */
@media (min-width: 860px) {
  /* SPEC308 W3 — the fact line (row 3) pushes the include grid to row 4, which is
     the minmax(0,1fr) slack row so prices still settle at the bottom of the band */
  .order-pkg { display: grid; grid-template-columns: minmax(0, 1.08fr) minmax(0, 1fr); grid-template-rows: auto auto auto minmax(0, 1fr) auto auto auto; column-gap: 30px; padding: 0 0 26px; }
  .order-pkg-media { grid-area: 1 / 1 / -1 / 2; aspect-ratio: auto; min-height: 480px; height: 100%; }
  .order-pkg-tag, .order-pkg-bestfor, .order-pkg-facts, .order-pkg-inc, .order-proofchip, .order-pkg-pricefoot, .order-pkg-delta, .order-pkg-actions { grid-column: 2; }
  .order-pkg-facts { padding: 4px 30px 0 0; }
  .order-pkg .order-proofchip { padding: 6px 30px 0 0; }
  .order-pkg-actions { padding: 16px 30px 0 0; }
  .order-pkg-tag { font-size: 15.5px; padding: 28px 30px 0 0; max-width: none; }
  .order-pkg-bestfor { padding: 0 30px 0 0; }
  .order-pkg-inc { display: grid; grid-template-columns: 1fr 1fr; column-gap: 24px; align-content: start; padding: 10px 30px 0 0; }
  .order-pkg-inc-row:nth-child(2) { border-top: 0; }   /* the 2nd column's first row loses its hairline too */
  .order-pkg-pricefoot { margin: 10px 30px 0 0; }
  .order-pkg-delta { padding-right: 30px; }
  .order-pkg-name { font-size: clamp(28px, 3vw, 38px); }
}

/* ---- add-ons: shop-the-story rows ---- */
.order-addon-grid { display: grid; gap: 14px; margin-bottom: 20px; }
.order-addon { display: flex; align-items: center; gap: 16px; background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 14px; padding: 14px 16px; box-shadow: var(--shadow-sm); cursor: pointer; transition: border-color var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease); }   /* whole row opens the details sheet (Vincent 2026-07-08 round 2) */
.order-addon:hover { border-color: var(--o-line-strong); box-shadow: var(--shadow); }
.order-addon.on { border-color: var(--order-accent); }
.order-addon-thumb { position: relative; flex: 0 0 116px; aspect-ratio: 4 / 3; border: 0; padding: 0; border-radius: 10px; overflow: hidden; cursor: zoom-in; background: var(--o-bg); }
.order-addon-thumbimg { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .7s var(--ease); }
.order-addon-thumb:hover .order-addon-thumbimg { transform: scale(1.05); }
.order-addon-see { position: absolute; left: 0; right: 0; bottom: 0; font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: .08em; text-transform: uppercase; color: #fff; background: linear-gradient(to top, rgba(10, 10, 12, .72), transparent); padding: 14px 6px 6px; text-align: center; }
.order-addon-main { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1 1 auto; }
.order-addon-name { font-family: var(--order-serif); font-size: var(--fs-lg); color: var(--o-text); }
.order-addon-why { color: var(--o-muted); font-size: var(--fs-caption); line-height: var(--lh-normal); max-width: 44ch; }
.order-addon-price { font-family: var(--order-serif); font-variant-numeric: tabular-nums; font-size: 17px; color: var(--o-text); margin-top: 4px; }
.order-addon-price-fine { font-family: var(--font); font-size: 11.5px; color: var(--o-faint); }
.order-add-btn { flex: 0 0 auto; font: inherit; font-size: var(--fs-body); font-weight: var(--fw-bold); min-width: 92px; min-height: 44px; border-radius: 999px; border: 1px solid var(--o-line-strong); background: var(--o-panel); color: var(--o-text); cursor: pointer; transition: background var(--t-base) var(--ease), color var(--t-base) var(--ease), border-color var(--t-base) var(--ease), transform var(--t-fast) var(--ease-spring); }
.order-add-btn:hover { border-color: var(--order-accent); }
.order-add-btn.on { background: var(--order-accent); border-color: var(--order-accent); color: var(--order-on-accent); }
.order-add-btn.sm { min-width: 76px; min-height: 38px; font-size: var(--fs-caption); }
.order-covered { color: var(--o-muted); font-size: 14.5px; line-height: 1.6; background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 14px; padding: 18px 20px; }

/* staging qty + tier controls */
.order-staging-controls { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
.order-qty { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--o-line-strong); border-radius: 999px; overflow: hidden; }
.order-qty-btn { width: 40px; height: 40px; border: 0; background: var(--o-panel); font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--o-text); cursor: pointer; }
.order-qty-btn:hover { background: color-mix(in srgb, var(--order-accent) 8%, var(--o-panel)); }
.order-qty-val { min-width: 26px; text-align: center; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.order-qty-unit { color: var(--o-muted); font-size: var(--fs-caption); }
.order-seg-row { display: inline-flex; border: 1px solid var(--o-line-strong); border-radius: 999px; overflow: hidden; }
.order-seg { font: inherit; font-size: 12.5px; font-weight: var(--fw-bold); padding: 9px 14px; border: 0; background: var(--o-panel); color: var(--o-muted); cursor: pointer; }
.order-seg.on { background: var(--order-accent); color: var(--order-on-accent); }

/* included + full collection */
.order-included { margin: 22px 0; }
.order-included-label { font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: .16em; text-transform: uppercase; color: var(--o-muted); margin-bottom: 8px; }
.order-included-rows { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.order-included-item { font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--o-muted); }
.order-more { border-top: 1px solid var(--o-line); padding-top: 14px; }
.order-more-sum { cursor: pointer; font-weight: var(--fw-bold); font-size: var(--fs-caption); letter-spacing: .08em; text-transform: uppercase; color: var(--o-muted); list-style: none; padding: 8px 0; }
.order-more-sum:hover { color: var(--o-text); }
.order-more-sum::-webkit-details-marker { display: none; }
.order-more-sum::after { content: ' ⌄'; }
.order-more[open] .order-more-sum::after { content: ' ⌃'; }
.order-more-list { display: flex; flex-direction: column; }
.order-more-row { display: flex; align-items: center; gap: 14px; padding: 12px 2px; border-bottom: 1px solid var(--o-line); }
.order-more-row:last-child { border-bottom: 0; }
.order-more-thumb { flex: 0 0 64px; aspect-ratio: 4 / 3; border: 0; padding: 0; border-radius: 8px; overflow: hidden; cursor: zoom-in; background: var(--o-bg); }
.order-more-thumbimg { width: 100%; height: 100%; object-fit: cover; display: block; }
.order-more-name { flex: 1 1 auto; font-size: 14.5px; font-weight: var(--fw-semibold); color: var(--o-text); }
.order-more-price { font-variant-numeric: tabular-nums; font-size: var(--fs-body); color: var(--o-muted); }

/* bundle-beat nudge — generosity, not pressure */
.order-nudge { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; background: color-mix(in srgb, var(--order-accent) 7%, var(--o-panel)); border: 1px solid color-mix(in srgb, var(--order-accent) 30%, var(--o-line)); border-radius: 14px; padding: 14px 18px; font-size: 13.5px; line-height: 1.55; color: var(--o-text); margin-bottom: 18px; }

/* ---- contact + the print-mount receipt ---- */
.order-contact-grid { display: grid; gap: 4px; }
.order-hp { position: absolute; left: -9999px; top: -9999px; height: 1px; width: 1px; opacity: 0; pointer-events: none; }
.order-receipt { background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 4px; box-shadow: var(--shadow-lg); padding: 12px 12px 20px; margin-top: 28px; }
.order-receipt-img { display: block; width: 100%; aspect-ratio: 21 / 9; object-fit: cover; margin-bottom: 20px; }
.order-receipt-kicker { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: .18em; text-transform: uppercase; color: var(--o-muted); margin: 0 12px 6px; }
.order-receipt-addr { font-family: var(--order-serif); font-size: var(--fs-h1); color: var(--o-text); line-height: 1.25; margin: 0 12px; }
.order-receipt-meta { font-size: 12.5px; color: var(--o-faint); margin: 4px 12px 16px; }
.order-receipt-line { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; padding: 9px 12px; border-bottom: 1px solid var(--o-line); font-size: var(--fs-body); color: var(--o-text); }
.order-receipt-addontag { font-size: 10px; font-weight: var(--fw-bold); letter-spacing: .08em; text-transform: uppercase; color: var(--o-faint); margin-left: 8px; }
.order-receipt-amt { font-variant-numeric: tabular-nums; font-weight: var(--fw-semibold); white-space: nowrap; }
.order-receipt-total { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; padding: 16px 12px 4px; font-weight: var(--fw-bold); font-size: 15px; color: var(--o-text); }
.order-receipt-totalnum { font-family: var(--order-serif); font-variant-numeric: tabular-nums; font-size: 31px; letter-spacing: -.4px; }
.order-receipt.done { margin: 26px auto 0; text-align: left; max-width: 480px; }

/* ---- frosted running-total ribbon (steps 3–6 only) ---- */
.order-ribbon { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: flex; align-items: center; justify-content: space-between; gap: 16px; max-width: 760px; margin: 0 auto; background: rgba(255, 255, 255, .82); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); border-top: 1px solid var(--border); padding: 12px 22px calc(12px + env(safe-area-inset-bottom)); }
.order[data-tone="ink"] .order-ribbon, html[data-theme="dark"] .order .order-ribbon { background: rgba(20, 20, 22, .78); border-top-color: rgba(255, 255, 255, .13); }
.order-ribbon.hide { display: none; }
.order-ribbon-left { display: flex; flex-direction: column; }
.order-ribbon-label { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .14em; text-transform: uppercase; color: var(--o-muted); }
.order-ribbon-note { font-size: 11.5px; color: var(--o-muted); }
.order-ribbon-total { font-family: var(--order-serif); font-variant-numeric: tabular-nums; font-size: 27px; letter-spacing: -.4px; color: var(--o-text); }

/* one-line affirmations (aria-live) */
.order-affirm { position: fixed; left: 50%; transform: translateX(-50%); bottom: calc(72px + env(safe-area-inset-bottom)); width: max-content; max-width: 92vw; text-align: center; font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--accent-hover); background: var(--panel); border: 1px solid var(--border); border-radius: 999px; padding: 8px 18px; box-shadow: var(--shadow); pointer-events: none; z-index: 39; }
.order-affirm:empty { display: none; }

/* ---- lightbox (native dialog) ---- */
.order-lightbox { border: 0; border-radius: 14px; padding: 0; max-width: min(920px, 92vw); width: 100%; background: var(--panel); color: var(--text); box-shadow: var(--shadow-lg); }
.order-lightbox::backdrop { background: rgba(10, 10, 12, .72); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }   /* SAFPOLISH 2026-08-25 D4 — ::backdrop pseudo, paired for parity, harmless (unpaired anywhere ratchets to 0) */
.order-lightbox-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px 10px; }
.order-lightbox-title { font-family: var(--order-serif); font-size: 21px; }
.order-lightbox-close { font: inherit; border: 0; background: none; font-size: var(--fs-md); cursor: pointer; color: var(--muted); padding: 6px 10px; }
.order-lightbox-body { display: flex; flex-direction: column; gap: 12px; padding: 0 20px 20px; max-height: 76vh; overflow: auto; }
.order-lb-img { width: 100%; border-radius: 10px; display: block; }
.order-lb-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.order-lb-fig { margin: 0; }
.order-lb-fig figcaption { font-size: 11.5px; font-weight: var(--fw-semibold); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-top: 6px; }
.order-lb-tour { width: 100%; aspect-ratio: 16 / 10; border: 0; border-radius: 10px; }
.order-lb-cap { font-size: 12.5px; color: var(--muted); margin: 0; }

/* ---- SPEC291 · benefit sheet (progressive disclosure inside "see the work")
   media column + honest benefit aside; side-by-side on wide, stacked on mobile.
   The stat is the ONE rationed accent (tabular-nums, brief); the source tag word
   carries the meaning — the tint is redundant reinforcement (WCAG 1.4.1). ---- */
.order-lightbox-body:has(.order-benefit) { display: grid; grid-template-columns: 1fr; align-items: start; }
@media (min-width: 720px) {
  .order-lightbox-body:has(.order-benefit) { grid-template-columns: minmax(0, 62fr) minmax(240px, 38fr); gap: 20px; }
}
.order-lb-media { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.order-benefit { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.order-benefit-hook { font-family: var(--order-serif); font-size: 18.5px; line-height: 1.4; color: var(--text); margin: 0; }
.order-benefit-why { font-size: 13.5px; line-height: 1.6; color: var(--muted); margin: 0; }
.order-benefit-stat { display: flex; flex-direction: column; gap: 5px; padding: 13px 15px; border-radius: 12px;
  background: color-mix(in srgb, var(--order-accent) 7%, var(--panel)); border: 1px solid color-mix(in srgb, var(--order-accent) 22%, var(--o-line, var(--line))); }
.order-benefit-num { font-size: 13.5px; line-height: var(--lh-normal); font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; }
.order-benefit-src { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; font-size: var(--fs-meta); line-height: 1.4; color: var(--muted); }
.order-benefit-tag { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .1em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 999px; border: 1px solid currentColor; white-space: nowrap; }
.order-benefit-srcname { font-weight: var(--fw-semibold); }
/* redundant color reinforcement only — the WORD above already states the tier */
.order-benefit-independent .order-benefit-tag { color: color-mix(in srgb, var(--order-accent) 88%, var(--text)); }
.order-benefit-industry .order-benefit-tag,
.order-benefit-vendor .order-benefit-tag { color: var(--muted); }

/* ---- confirmation (ink curtain call) ---- */
.order-step.done { text-align: center; padding-top: clamp(48px, 10vh, 96px); }
.order-step.done .order-hero-title { color: var(--o-text); max-width: none; text-shadow: none; margin-inline: auto; }
.order-step.done .order-hero-sub { color: var(--o-muted); margin-inline: auto; }
.order-done-mark { width: 74px; height: 74px; border-radius: 50%; background: var(--order-accent); color: var(--order-on-accent); font-size: var(--fs-hero); font-weight: var(--fw-black); display: flex; align-items: center; justify-content: center; margin: 0 auto 24px; box-shadow: var(--shadow-lg); animation: order-pop .55s var(--ease-spring) both; }
@keyframes order-pop { from { transform: scale(.5); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.order-payment-note { color: var(--o-muted); font-size: 13.5px; line-height: 1.6; max-width: 44ch; margin: 20px auto 0; }
.order-step.done .order-btn.primary.xl { display: inline-block; margin-top: 22px; text-decoration: none; }
.order-step.done .order-receipt.done { background: var(--panel); color: var(--text); }
.order-step.done .order-receipt.done .order-receipt-addr, .order-step.done .order-receipt.done .order-receipt-line, .order-step.done .order-receipt.done .order-receipt-total { color: var(--text); }

/* ---- desktop refinements + reduced motion ---- */
@media (min-width: 720px) {
  .order-vision-grid { grid-template-columns: repeat(3, 1fr); }
  .order-contact-grid { grid-template-columns: 1fr 1fr; column-gap: 16px; }
  .order-contact-grid .order-field:nth-child(4) { grid-column: 1 / -1; }
}
@media (max-width: 560px) {
  .order-addon { flex-wrap: wrap; }
  .order-addon-thumb { flex-basis: 96px; }
  /* text sits higher on the tall mobile hero — deepen the scrim so it always clears */
  .order-hero-scrim { background:
    linear-gradient(to top, rgba(10, 10, 12, .9) 0%, rgba(10, 10, 12, .58) 52%, rgba(10, 10, 12, .22) 82%),
    linear-gradient(to bottom, rgba(10, 10, 12, .38), transparent 26%); }
}
@media (prefers-reduced-motion: reduce) {
  .order-step, .order-done-mark, .order-hero-img.kenburns { animation: none; }
  .order-pkg-img, .order-addon-thumbimg, .order-cta-arrow, .order-rail-fill, .order-rail-node, .order { transition: none; }
  .order-btn.primary:hover, .order-pkg:hover, .order-vision-card:hover { transform: none; }
}
/* programmatic focus lands on titles for screen readers — no ring needed */
.order-step-title:focus, .order-step-title:focus-visible, .order-hero-title:focus, .order-hero-title:focus-visible { outline: none; box-shadow: none; }

/* SPEC278c — world-class pass additions (identity, policy, timeline, deltas, marks) */
.order-identity { border-top: 1px solid var(--o-line); margin-top: clamp(28px, 5vh, 44px); padding-top: 14px; font-size: var(--fs-meta); color: var(--o-muted); line-height: 1.8; text-align: center; }
.order-identity a { color: var(--o-muted); text-decoration: underline; text-underline-offset: 2px; }
.order-identity-privacy { font-size: 11.5px; color: var(--o-muted); opacity: .85; }
.order-policy { display: flex; flex-direction: column; gap: 4px; margin: 18px 0 4px; padding: 14px 18px; border: 1px solid var(--o-line); border-radius: 12px; background: var(--o-panel); font-size: 12.5px; color: var(--o-muted); line-height: 1.55; }
.order-cred { font-size: var(--fs-micro); color: var(--o-muted); margin-top: 4px; }
.order-change { background: none; border: 0; padding: 0 10px 0 0; font: inherit; font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--o-muted); opacity: .7; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.order-change:hover { opacity: 1; color: var(--o-text); }
.order-receipt-actions { display: inline-flex; align-items: baseline; gap: 2px; white-space: nowrap; }
.order-pkg-delta { font-size: var(--fs-meta); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; color: var(--o-muted); padding: 6px 20px 0; align-self: flex-end; }
.order-okmark { display: none; color: var(--ok); font-weight: var(--fw-black); }
.order-okmark.show { display: inline; }
.order-fries { font-family: var(--order-serif); font-size: 15.5px; color: var(--o-muted); margin: -14px 0 22px; }
.order-fine.tight { margin-top: 6px; }
.order-done-email { color: var(--o-muted); font-size: var(--fs-body); line-height: 1.6; max-width: 52ch; margin: 6px auto 0; }
.order-timeline { list-style: none; padding: 0; margin: 26px auto 0; max-width: 460px; text-align: left; display: flex; flex-direction: column; gap: 0; }
.order-timeline-node { position: relative; padding: 0 0 18px 26px; font-size: 13.5px; color: var(--o-muted); line-height: var(--lh-normal); }
.order-timeline-node::before { content: ''; position: absolute; left: 7px; top: 16px; bottom: 0; width: 1px; background: var(--o-line); }
.order-timeline-node:last-child::before { display: none; }
.order-timeline-dot { position: absolute; left: 2px; top: 4px; width: 11px; height: 11px; border-radius: 50%; border: 1px solid var(--o-line-strong); background: var(--o-bg); }
.order-timeline-node.lit { color: var(--o-text); }
.order-timeline-node.lit .order-timeline-dot { background: var(--order-accent); border-color: var(--order-accent); }
.order-done-offer { max-width: 480px; margin: 18px auto 0; padding: 14px 18px; border: 1px solid var(--o-line); border-radius: 12px; background: var(--o-panel); font-size: var(--fs-caption); color: var(--o-muted); line-height: 1.6; text-align: left; }
.order-done-offer-name { font-family: var(--order-serif); font-size: 15px; color: var(--o-text); }

/* SPEC279 — live slot booking on the schedule step */
.order-cal.hide, .order-request.hide { display: none; }
.order-photog-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px; }
.order-photog { font: inherit; font-size: var(--fs-body); font-weight: var(--fw-semibold); color: var(--o-text); background: var(--o-panel); border: 1px solid var(--o-line-strong); border-radius: 999px; padding: 9px 16px; min-height: 44px; cursor: pointer; transition: border-color var(--t-base) var(--ease), background var(--t-base) var(--ease), color var(--t-base) var(--ease); }
.order-photog:hover { border-color: var(--order-accent); }
.order-photog.on { background: var(--order-accent); border-color: var(--order-accent); color: var(--order-on-accent); }
.order-photog-inner { display: inline-flex; align-items: center; gap: 8px; }
.order-photog-initial { width: 22px; height: 22px; border-radius: 50%; border: 1px solid currentColor; display: inline-flex; align-items: center; justify-content: center; font-family: var(--order-serif); font-size: var(--fs-meta); }
.order-cal-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-bottom: 10px; }
.order-cal-title { font-size: 11.5px; font-weight: var(--fw-bold); letter-spacing: .08em; text-transform: uppercase; color: var(--o-muted); }
.order-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; margin-bottom: 16px; }
.order-cal-pad { min-height: 1px; }
.order-cal-day { display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 2px 7px; background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 10px; cursor: pointer; transition: border-color var(--t-base) var(--ease), transform var(--t-fast) var(--ease-spring); }
.order-cal-day:hover:not(.off) { border-color: var(--order-accent); transform: translateY(-1px); }
.order-cal-day.sel { border: 1.5px solid var(--order-accent); }
.order-cal-day.off { opacity: .38; cursor: default; }
.order-cal-dow { font-size: 9.5px; font-weight: var(--fw-bold); letter-spacing: .08em; text-transform: uppercase; color: var(--o-muted); }
.order-cal-num { font-family: var(--order-serif); font-size: 17px; font-variant-numeric: tabular-nums; color: var(--o-text); }
.order-cal-dot { width: 5px; height: 5px; border-radius: 50%; background: transparent; }
.order-cal-dot.has { background: var(--order-accent); }
.order-slotgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; margin-bottom: 12px; }
.order-slot { font: inherit; font-size: 13.5px; font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; color: var(--o-text); background: var(--o-panel); border: 1px solid var(--o-line-strong); border-radius: 10px; padding: 11px 6px; min-height: 44px; cursor: pointer; transition: border-color var(--t-base) var(--ease), background var(--t-base) var(--ease), color var(--t-base) var(--ease); }
.order-slot:hover { border-color: var(--order-accent); }
.order-slot.sel { background: var(--order-accent); border-color: var(--order-accent); color: var(--order-on-accent); }
.order-slot-summary { font-size: var(--fs-body); color: var(--o-text); margin: 6px 0 4px; min-height: 20px; }
.order-slot-summary-check { color: var(--ok); font-weight: var(--fw-black); }
.order-escape { margin: 10px 0 18px; }

/* ============================================================
   SPEC287 — the goal→package selling engine (RESEARCH-PORTAL-SELL)
   Goal-aware package step: recommended ribbon, benefit + best-for copy,
   per-package "See the work", the always-on upgrade nudge, guided path.
   ============================================================ */
/* each package now sits in a cell so its "See the work" control can live
   OUTSIDE the card <button> (valid HTML — no button-in-button) */
.order-pkg-cell { display: flex; flex-direction: column; gap: 8px; }
.order-pkg-cell .order-pkg { width: 100%; flex: 1 1 auto; }   /* SPEC289 — card fills the cell (minus the See-the-work row) so cards match height */
/* the dynamic "Recommended for {goal}" ribbon — a quiet accent lift */
.order-pkg-badge.rec { border-bottom-color: var(--order-accent); color: #fff; }
.order-pkg.rec { border-color: color-mix(in srgb, var(--order-accent) 45%, var(--o-line)); }
.order-pkg.rec.sel { border-color: var(--order-accent); }
/* the plain self-select line */
.order-pkg-bestfor { font-size: var(--fs-meta); font-weight: var(--fw-semibold); letter-spacing: .02em; color: var(--o-muted); padding: 0 20px; opacity: .85; }
/* quiet until hover/focus — restraint per the luxury brief */
/* Vincent 2026-07-08 round 2 — BOTH actions live INSIDE the card as big
   equal pills: Choose (selects) + See everything you get (depth sheet).
   Nothing important in fine print, ever. */
.order-pkg-actions { display: flex; flex-wrap: wrap; gap: 10px; padding: 16px 20px 0; }
.order-pkg-actions .order-btn { flex: 1 1 auto; min-height: 50px; white-space: nowrap; }
.order-pkg-choose.sel { border-color: var(--order-accent); color: var(--order-accent); }

/* the "Help me choose" entry — a quiet link right under the step header */
.order-help-row { margin: 10px auto 0; max-width: 1080px; padding-inline: clamp(20px, 4vw, 32px); display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }   /* SPEC289 helpers — now real pills (Vincent 2026-07-08 round 2) */
.order-addon-details { align-self: flex-start; margin-top: 6px; }

/* the always-on upgrade nudge — one quiet strip, never a nag */
.order-upgrade { max-width: 1080px; margin: 0 auto; padding: 15px 20px; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 18px; background: color-mix(in srgb, var(--order-accent) 6%, var(--o-panel)); border: 1px solid color-mix(in srgb, var(--order-accent) 26%, var(--o-line)); border-radius: 14px; }
.order-upgrade-body { display: flex; flex-direction: column; gap: 3px; min-width: 220px; flex: 1 1 340px; }
.order-upgrade-kick { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .14em; text-transform: uppercase; color: var(--order-accent); }
.order-upgrade-line { font-size: 13.5px; line-height: var(--lh-normal); color: var(--o-text); }
/* SPEC291 — one honest, independent proof line under the upgrade nudge */
.order-upgrade-proof { font-size: 12.5px; line-height: var(--lh-normal); color: var(--o-muted); font-variant-numeric: tabular-nums; margin-top: 2px; }
.order-upgrade-proofsrc { font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--o-muted); }   /* nowrap removed — it forced 525px on a 375px phone (horizontal page scroll) */
.order-btn.sm { font-size: 13.5px; padding: 10px 18px; min-height: 40px; white-space: nowrap; }

/* the guided "Help me choose" dialog (reuses the .order-lightbox shell) */
.order-guide { max-width: min(560px, 92vw); }
.order-guide-body { gap: 16px; padding-top: 6px; }
.order-guide-dots { display: flex; gap: 7px; }
.order-guide-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--o-line-strong); opacity: .5; }
.order-guide-dot.on { background: var(--order-accent); opacity: 1; }
.order-guide-q { font-family: var(--order-serif); font-size: 20px; line-height: var(--lh-snug); color: var(--o-text); margin: 0; }
.order-guide-opts { display: flex; flex-direction: column; gap: 10px; }
.order-guide-opt { text-align: left; font: inherit; font-size: 15px; font-weight: var(--fw-semibold); color: var(--o-text); background: var(--o-panel); border: 1px solid var(--o-line-strong); border-radius: 12px; padding: 14px 18px; min-height: 48px; cursor: pointer; transition: border-color var(--t-base) var(--ease), background var(--t-base) var(--ease); }
.order-guide-opt:hover { border-color: var(--order-accent); }
.order-guide-opt.on { border-color: var(--order-accent); background: color-mix(in srgb, var(--order-accent) 8%, var(--o-panel)); }
.order-guide-nav { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.order-guide-reco-kick { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .14em; text-transform: uppercase; color: var(--order-accent); margin: 0; }
.order-guide-reco-name { font-family: var(--order-serif); font-size: var(--fs-display); letter-spacing: -.3px; color: var(--o-text); margin: 2px 0 0; }
.order-guide-reco-why { font-size: 14.5px; line-height: 1.6; color: var(--o-text); margin: 0; }
.order-guide-reco-alt { font-size: var(--fs-caption); line-height: 1.55; color: var(--o-muted); margin: 0; }
.order-guide-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 4px; }

/* SPEC288 (§2.3) — the honest side-by-side "Compare all four" (opens in the
   lightbox shell). Each column is one tier's own includes, priced live; the
   row scrolls inside its own container so the page body never scrolls sideways. */
.order-compare-dlg { max-width: min(1000px, 96vw); }
.order-compare-sub { margin: 0 0 14px; }
.order-compare { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 8px; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch; }
.order-compare-col { flex: 0 0 clamp(210px, 44vw, 236px); scroll-snap-align: start; display: flex; flex-direction: column; gap: 7px; padding: 16px 16px 18px; border: 1px solid var(--o-line); border-radius: 14px; background: var(--o-panel); }
.order-compare-col.sel { border-color: var(--order-accent); }
.order-compare-col.rec { border-color: color-mix(in srgb, var(--order-accent) 45%, var(--o-line)); }
.order-compare-rec { min-height: 14px; font-size: 10px; font-weight: var(--fw-black); letter-spacing: .14em; text-transform: uppercase; color: var(--order-accent); }
.order-compare-name { font-family: var(--order-serif); font-size: 21px; letter-spacing: -.2px; color: var(--o-text); }
.order-compare-price { font-size: 17px; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; color: var(--o-text); }
.order-compare-best { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--o-muted); opacity: .9; }
.order-compare-inc { list-style: none; margin: 6px 0 4px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.order-compare-inc li { position: relative; padding-left: 20px; font-size: var(--fs-caption); line-height: 1.4; color: var(--o-text); }
.order-compare-inc li::before { content: "✓"; position: absolute; left: 0; top: 0; color: var(--order-accent); font-weight: var(--fw-bold); }
.order-compare-choose { margin-top: auto; width: 100%; justify-content: center; }
@media (max-width: 560px) { .order-compare-col { flex-basis: 78vw; } }

/* ============================================================
   SPEC290 — the "Your Brand" Mini Branding card + the money-free
   Signature Standard peak-end panel. One card, a pick-one reel-count
   tier picker; a two-column honest ladder (what every tier includes vs
   what the Signature Standard adds). Matches the SPEC287/288 order-layer
   tokens; the equal-height package grid (SPEC289) is untouched. */
.order-brandcard { background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 16px; padding: 18px; margin-bottom: 20px; box-shadow: var(--shadow-sm); display: flex; gap: 18px; align-items: stretch; transition: border-color var(--t-base) var(--ease); }
.order-brandcard.on { border-color: var(--order-accent); }
.order-brandcard.lead { border-color: color-mix(in srgb, var(--order-accent) 42%, var(--o-line)); box-shadow: var(--shadow); }
.order-brandcard-thumb { position: relative; flex: 0 0 148px; align-self: flex-start; aspect-ratio: 4 / 3; border: 0; padding: 0; border-radius: 12px; overflow: hidden; cursor: zoom-in; background: var(--o-bg); }
.order-brandcard-thumbimg { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .7s var(--ease); }
.order-brandcard-thumb:hover .order-brandcard-thumbimg { transform: scale(1.05); }
.order-brandcard-body { display: flex; flex-direction: column; gap: 10px; min-width: 0; flex: 1 1 auto; }
.order-brandcard-eyebrow { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .18em; text-transform: uppercase; color: var(--order-accent); }
.order-brandcard-title { font-family: var(--order-serif); font-weight: 440; font-size: 24px; letter-spacing: -.3px; line-height: 1.12; color: var(--o-text); margin: 0; }
.order-brandcard-lede { color: var(--o-muted); font-size: 13.5px; line-height: 1.55; margin: 0; max-width: 58ch; }
.order-brandtier-row { display: flex; flex-wrap: wrap; gap: 10px; margin: 2px 0 4px; }
.order-brandtier { position: relative; flex: 1 1 130px; min-height: 62px; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 2px; font: inherit; text-align: left; padding: 10px 14px; border-radius: 12px; border: 1px solid var(--o-line-strong); background: var(--o-panel); color: var(--o-text); cursor: pointer; transition: border-color var(--t-base) var(--ease), background var(--t-base) var(--ease); }
.order-brandtier:hover { border-color: var(--order-accent); }
.order-brandtier.on { border-color: var(--order-accent); background: color-mix(in srgb, var(--order-accent) 8%, var(--o-panel)); }
.order-brandtier-reels { font-size: var(--fs-meta); font-weight: var(--fw-bold); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--o-muted); }
.order-brandtier-price { font-family: var(--order-serif); font-variant-numeric: tabular-nums; font-size: var(--fs-h1); color: var(--o-text); }
.order-brandtier-mark { position: absolute; top: 8px; right: 10px; font-size: var(--fs-meta); font-weight: var(--fw-black); color: var(--order-accent); }
.order-brandcard-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 4px; }
.order-brandcard-collabel { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .1em; text-transform: uppercase; color: var(--o-muted); margin-bottom: 6px; }
.order-brandcard-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.order-brandcard-list li { position: relative; padding-left: 19px; font-size: 12.5px; line-height: 1.45; color: var(--o-text); }
.order-brandcard-list.yes li::before { content: "✓"; position: absolute; left: 0; top: 0; color: var(--order-accent); font-weight: var(--fw-bold); }
.order-brandcard-list.add li { color: var(--o-muted); }
.order-brandcard-list.add li::before { content: "+"; position: absolute; left: 0; top: 0; color: var(--o-faint); font-weight: var(--fw-bold); }
.order-brandcard-scent { font-family: var(--order-serif); font-size: var(--fs-body); line-height: var(--lh-normal); color: var(--o-muted); margin: 4px 0 0; }
@media (max-width: 640px) {
  .order-brandcard { flex-direction: column; }
  .order-brandcard-thumb { flex-basis: auto; width: 100%; align-self: stretch; aspect-ratio: 16 / 9; }
  .order-brandcard-cols { grid-template-columns: 1fr; }
}

/* the peak-end Signature Standard learn-more (money-free — NO retainer price) */
.order-ss-panel { max-width: 480px; margin: 26px auto 0; text-align: left; background: color-mix(in srgb, var(--order-accent) 6%, var(--o-panel)); border: 1px solid color-mix(in srgb, var(--order-accent) 28%, var(--o-line)); border-radius: 16px; padding: 20px 22px; }
/* on the ink-toned confirmation, mirror the receipt: a light panel with the
   app's real dark text/muted so nothing goes faint on the dark background. */
.order-step.done .order-ss-panel { background: color-mix(in srgb, var(--order-accent) 7%, var(--panel)); border-color: color-mix(in srgb, var(--order-accent) 26%, var(--border)); }
.order-ss-eyebrow { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .18em; text-transform: uppercase; color: var(--order-accent); }
.order-ss-title { font-family: var(--order-serif); font-weight: 440; font-size: 21px; letter-spacing: -.2px; line-height: 1.15; color: var(--o-text); margin: 6px 0 8px; }
.order-step.done .order-ss-title { color: var(--text); }
.order-ss-body { font-size: 13.5px; line-height: 1.55; color: var(--o-muted); margin: 0 0 14px; }
.order-step.done .order-ss-body { color: var(--muted); }
.order-ss-panel .order-btn.ghost { display: inline-block; text-decoration: none; }
.order-step.done .order-ss-panel .order-btn.ghost { color: var(--text); border-color: var(--border-strong); }
.order-step.done .order-ss-panel .order-btn.ghost:hover { border-color: var(--order-accent); }

/* SPEC312 — order → portal handoff card (confirmation step; mirrors the ss-panel light treatment) */
.order-portal-card { max-width: 480px; margin: 26px auto 0; text-align: left; background: color-mix(in srgb, var(--order-accent) 7%, var(--panel)); border: 1px solid color-mix(in srgb, var(--order-accent) 26%, var(--border)); border-radius: 16px; padding: 20px 22px; }
.order-portal-body { font-size: 13.5px; line-height: 1.55; color: var(--muted); margin: 6px 0 14px; }
.order-portal-card .order-btn.ghost { display: inline-block; text-decoration: none; color: var(--text); border-color: var(--border-strong); }
.order-portal-card .order-btn.ghost:hover { border-color: var(--order-accent); }

/* ── SPEC297 Coaching KB (Playbook cards) ── */
.coaching-kb .kb-card { display: flex; flex-direction: column; }
.coaching-kb .kb-card-title { font-weight: var(--fw-bold); font-size: 14.5px; margin-bottom: 4px; }
.coaching-kb .kb-hook { font-weight: var(--fw-semibold); font-size: 13.5px; margin-bottom: 6px; }
.coaching-kb .kb-why { font-size: var(--fs-caption); line-height: var(--lh-normal); margin-bottom: 8px; }
.coaching-kb .kb-stat { font-size: var(--fs-caption); font-weight: var(--fw-semibold); padding: 8px 10px; border-radius: 8px;
  background: var(--hover); border-left: 3px solid var(--accent); }
.coaching-kb .kb-source { font-size: var(--fs-meta); margin-top: 5px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.coaching-kb .kb-say { font-size: var(--fs-caption); font-style: italic; margin: 6px 0; color: var(--text); }
.coaching-kb .kb-star { color: #f59e0b; }
.coaching-kb .kb-fulltool code { font-size: var(--fs-meta); padding: 1px 5px; border-radius: 4px; background: var(--hover); }

/* ============================================================
   SPEC306 — streamed-boot loading skeleton (quiet, token-based;
   shows only for the ~1s a navigated-to module is still loading).
   Brief-compliant: skeleton (never a spinner), motion is a soft
   opacity pulse, disabled under prefers-reduced-motion.
   ============================================================ */
.stream-skel { max-width: 720px; margin: 32px auto; padding: 0 16px; display: flex; flex-direction: column; gap: 16px; }
.stream-skel-bar { height: 14px; border-radius: 7px; background: var(--hover); animation: skelPulse 1.4s ease-in-out infinite; }
.stream-skel-bar.w40 { width: 40%; height: 22px; border-radius: 8px; }
.stream-skel-bar.w70 { width: 70%; }
.stream-skel-bar.w55 { width: 55%; }
.stream-skel-card { height: 120px; border-radius: var(--radius-lg, 12px); background: var(--hover); border: 1px solid var(--border); animation: skelPulse 1.4s ease-in-out infinite; }
@keyframes skelPulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .stream-skel-bar, .stream-skel-card { animation: none; } }

/* ============================================================
   SPEC307 — client portal default accent: warm bronze, not the
   ops-app indigo (DESIGN-BRIEF §3 Delivery: "warm accent"). The
   agent's brand color still wins — screenReport sets --accent
   inline on the report wrap, which outranks this.
   SPEC-DHCLIENT D2 — the whole portal converges onto the D1 dark-cinematic tokens now
   (design-handoff/README.md + Client-A-Cinematic.dc.html), not just the accent: every
   shared .card/.btn/.input/.chip component the portal already reuses reads
   var(--panel)/var(--border)/var(--text)/var(--muted), so remapping those here — same
   scoped idiom .cstatus (D3) / .site-page (D4) use — recolors the whole page with zero
   other rule changes. --accent becomes the D1 "Blue, link" token (champagne stays a
   sparing highlight, per the README, not a blanket accent). Fixed dark-cinematic
   regardless of the toggle's data-theme state (a client sees this for five minutes; it's
   the studio's fixed brand, not a user preference) — SPEC285's toggle mechanism itself
   (localStorage, matchMedia, the sun/moon icon repaint) stays wired, untouched, it just
   no longer changes this page's LOOK, matching every other client surface in this wave.
   Hub UI is unaffected — #portal-root exists only on portal.html.
   ============================================================ */
#portal-root, [data-theme="dark"] #portal-root {
  --accent: var(--cf-blue); --accent-hover: var(--cf-blue-hover); --accent-soft: rgba(103,179,213,.14);
  --bg: var(--cf-ground); --bg-2: var(--cf-card); --panel: var(--cf-card); --card: var(--cf-card); --subtle: var(--cf-card); --hover: var(--cf-card);
  --text: var(--cf-text); --text-soft: var(--cf-text-3); --muted: var(--cf-text-3); --faint: var(--cf-text-4);
  --border: var(--cf-hairline); --border-strong: var(--cf-hairline-hover);
  --ok: var(--cf-green); --ok-soft: var(--cf-green-bg); --ok-ink: var(--cf-green);
  /* self-paint (not just supply the vars) — belt-and-suspenders with body's own
     background/color in portal.html so there's no gap/flash at the #portal-root
     boundary itself, regardless of what a caller happens to render at the root. */
  background: var(--bg); color: var(--text);
  color-scheme: dark;
}
/* D1/D2 — "Minimum hit target is 44px on any touch surface." The shared .btn base is
   ~34px, sized for the admin app's dense desktop UI; the app-wide
   @media(max-width:640px) .btn-sm rule already covers narrow phones, but the README's
   rule isn't mobile-gated, so this closes the gap at any width without touching the
   shared .btn rule (moved here from portal.html's inline <style> so it lives in
   portal.css like every other #portal-root rule, instead of being invisible to it). */
#portal-root .btn { min-height: 44px; }

/* ============================================================
   SPEC308 — order AOV (RESEARCH-ORDER-AOV-2026-07-08, wave 1)
   Landing package ladder · rail-as-argument · encouragement
   lines · post-confirm add window. Order-page only (all classes
   live under .order on order.html); uses the --o-* tone vars.
   After editing here: node tools/extract-order-css.js
   ============================================================ */

/* — item 1B · the landing package ladder (package-first front door) — */
.order-lpkgs { margin-top: clamp(30px, 5vh, 52px); }
.order-lpkgs-sub { text-align: center; color: var(--o-muted); font-size: var(--fs-caption); line-height: var(--lh-normal); margin: -6px auto 20px; max-width: 52ch; }
.order-lpkg-grid { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 640px) { .order-lpkg-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1080px) { .order-lpkg-grid { grid-template-columns: repeat(4, 1fr); } }
.order-lpkg { text-align: left; font: inherit; background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 14px; padding: 0 0 16px; cursor: pointer; overflow: hidden; display: flex; flex-direction: column; gap: 7px; box-shadow: var(--shadow-sm); transition: border-color var(--t-base) var(--ease), transform var(--t-fast) var(--ease-spring), box-shadow var(--t-base) var(--ease); }
.order-lpkg:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); border-color: var(--order-accent); }
.order-lpkg:hover .order-lpkg-img { transform: scale(1.04); }
/* the Best-Seller card reads architectural: tinted panel + hairline accent border */
.order-lpkg.bestseller { background: color-mix(in srgb, var(--order-accent) 6%, var(--o-panel)); border-color: color-mix(in srgb, var(--order-accent) 45%, var(--o-line)); }
.order-lpkg-media { position: relative; display: block; aspect-ratio: 3 / 2; overflow: hidden; background: var(--o-bg); }
.order-lpkg-img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .7s var(--ease); }
.order-lpkg-badge { position: absolute; left: 12px; bottom: 10px; font-size: 10px; font-weight: var(--fw-black); letter-spacing: .16em; text-transform: uppercase; color: #fff; border-bottom: 1px solid var(--order-accent); padding-bottom: 2px; text-shadow: 0 1px 12px rgba(0, 0, 0, .45); }
.order-lpkg-name { font-family: var(--order-serif); font-size: 21px; letter-spacing: -.2px; color: var(--o-text); padding: 5px 16px 0; }
.order-lpkg-best { color: var(--o-muted); font-size: 12.5px; line-height: var(--lh-normal); padding: 0 16px; flex: 1 1 auto; }
.order-lpkg-price { font-family: var(--order-serif); font-variant-numeric: tabular-nums; font-size: 19px; color: var(--o-text); padding: 0 16px; }
.order-lpkg-cta { font-size: var(--fs-meta); font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--order-accent); padding: 2px 16px 0; }

/* — item 7 · the fact-anchored encouragement line in the step head — */
.order-affirm-note { font-family: var(--order-serif); font-size: 15.5px; font-style: italic; color: var(--o-muted); margin: 0 0 10px; }

/* — X2 · the "matches your goal" chip on the goal's lead package card — */
.order-pkg-goalchip { font-size: 10px; font-weight: var(--fw-black); letter-spacing: .14em; text-transform: uppercase; color: #fff; align-self: flex-start; background: rgba(10, 10, 12, .38); border: 1px solid rgba(255, 255, 255, .55); border-radius: 999px; padding: 3px 10px; backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); text-shadow: 0 1px 8px rgba(0, 0, 0, .4); }   /* SAFPOLISH 2026-08-25 D4 */

/* — item 4 · the ribbon argues quietly: piece count + promise + honest "ahead" — */
.order-ribbon-ahead { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--order-accent); }
.order-ribbon-ahead:empty { display: none; }

/* — item 4 · receipt benefit echoes + the three trust ticks under the total — */
.order-receipt-echo { display: block; font-size: var(--fs-meta); font-weight: 400; color: var(--o-faint); line-height: 1.45; margin-top: 2px; max-width: 46ch; }
.order-trust-ticks { display: flex; flex-wrap: wrap; gap: 4px 16px; padding: 6px 12px 2px; }
.order-trust-tick { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--o-muted); }

/* — item 8 · the post-confirm add-window CTA — */
.order-done-offer-cta { display: inline-block; margin-top: 8px; font-weight: var(--fw-bold); }

/* ============================================================
   SPEC308 — order AOV, wave 2 (education + add-on moments).
   Depth sheets · before/after reveal · own-before-offer chips ·
   quiet contextual moments · equal-dignity skip. Order-page only;
   after editing here: node tools/extract-order-css.js
   ============================================================ */

/* — item 2 · the service depth sheet (inside the existing lightbox) — */
.order-sheet { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.order-sheet-hook { font-family: var(--order-serif); font-size: var(--fs-h1); line-height: 1.3; letter-spacing: -.2px; color: var(--o-text); margin: 2px 0 0; }
.order-sheet-why { color: var(--o-muted); font-size: var(--fs-body); line-height: 1.65; margin: 0; max-width: 62ch; }
.order-sheet-media { display: grid; gap: 10px; }
.order-sheet-img { width: 100%; border-radius: 10px; display: block; }
.order-sheet-foot { position: sticky; bottom: 0; display: flex; align-items: center; justify-content: flex-end; gap: 10px; padding: 12px 0 2px; margin-top: 2px; background: var(--panel); border-top: 1px solid var(--o-line); }
.order-sheet-in { font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--o-text); margin-right: auto; }

/* — package sheet walkthrough (Vincent 2026-07-08): every include, taught — */
.order-sheet-walkhead { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: .18em; text-transform: uppercase; color: var(--o-muted); margin: 16px 0 0; }
.order-sheet-walk { display: flex; flex-direction: column; }
.order-walk-row { display: flex; gap: 16px; align-items: flex-start; padding: 14px 0; border-bottom: 1px solid var(--o-line); }
.order-walk-row:last-child { border-bottom: 0; }
.order-walk-media { flex: 0 0 128px; aspect-ratio: 4 / 3; border-radius: 8px; overflow: hidden; background: var(--o-bg); }
.order-walk-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.order-walk-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.order-walk-name { font-family: var(--order-serif); font-size: 17px; color: var(--o-text); }
.order-walk-hook { font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--o-text); }
.order-walk-why { font-size: 12.5px; line-height: 1.55; color: var(--o-muted); margin: 0; max-width: 60ch; }
@media (max-width: 560px) { .order-walk-media { flex-basis: 96px; } }

/* — item 2 (D2) · the before/after reveal — twilight + staging sheets only.
   Two stacked images; the AFTER layer is clipped by an invisible full-cover
   range input (keyboard + touch for free), a hairline handle rides the value. */
.order-ba { position: relative; aspect-ratio: 3 / 2; border-radius: 10px; overflow: hidden; user-select: none; touch-action: pan-y; background: var(--o-bg); }
.order-ba-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.order-ba-top { will-change: clip-path; }
.order-ba-range { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; opacity: 0; cursor: ew-resize; -webkit-appearance: none; appearance: none; background: transparent; z-index: 2; }
.order-ba-range::-webkit-slider-thumb { -webkit-appearance: none; width: 44px; height: 100%; cursor: ew-resize; }
.order-ba-range::-moz-range-thumb { width: 44px; height: 100%; border: 0; background: transparent; cursor: ew-resize; }
.order-ba-handle { position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px; background: rgba(255, 255, 255, .92); box-shadow: 0 0 0 1px rgba(0, 0, 0, .25); pointer-events: none; }
.order-ba-tag { position: absolute; top: 10px; padding: 3px 9px; border-radius: 999px; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; background: rgba(10, 12, 18, .6); color: #fff; pointer-events: none; }
.order-ba-tag.before { right: 10px; }
.order-ba-tag.after { left: 10px; }
.order-ba-room { position: absolute; left: 10px; bottom: 10px; padding: 4px 10px; border-radius: 6px; font-size: 11.5px; font-weight: var(--fw-semibold); background: rgba(10, 12, 18, .62); color: #fff; pointer-events: none; }

/* — item 2 · the quiet learn-more paired with Add on every add-on card — */
.order-addon-details { align-self: flex-start; font-size: 12.5px; padding: 0; }

/* — item 3a · own-before-offer: the "In your {package}" chip strip — */
.order-own { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; margin: 0 0 18px; }
.order-own-label { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: .12em; text-transform: uppercase; color: var(--o-muted); margin-right: 2px; }
.order-own-chip { font: inherit; font-size: 12.5px; color: var(--o-text); background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 999px; padding: 5px 12px; }
button.order-own-chip { cursor: pointer; transition: border-color var(--t-base) var(--ease); }
button.order-own-chip:hover { border-color: var(--order-accent); }

/* — items 3b/3c · the quiet contextual moment (golden hour / sqft) — */
.order-moment { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; border: 1px solid var(--o-line); border-left: 3px solid var(--order-accent); border-radius: 10px; padding: 12px 14px; background: var(--o-panel); margin: 12px 0 0; }
.order-moment.head { margin: 4px 0 14px; }
.order-moment-line { flex: 1 1 260px; font-size: 13.5px; line-height: 1.55; color: var(--o-text); }
.order-moment-actions { display: flex; gap: 8px; }
.order-moment-btn { font: inherit; font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--o-text); background: transparent; border: 1px solid var(--o-line); border-radius: 999px; padding: 7px 16px; min-height: 36px; cursor: pointer; transition: border-color var(--t-base) var(--ease); }
.order-moment-btn:hover { border-color: var(--order-accent); }

/* — item 10 · the equal-dignity skip under the add-on grid — */
.order-skip { text-align: center; margin: 14px 0 0; }

/* ============================================================
   SPEC308 — order AOV, wave 3 (RESEARCH-ORDER-AOV-2026-07-08 §2 B2/B3/B4/C5/E/F,
   §5 items 5/6/9/11/12). Cumulative includes · one-door comparison matrix ·
   data-true proof chips · package fact lines · honest availability · adjacency
   narration · mobile one-thumb pass. Order-page only; after editing here:
   node tools/extract-order-css.js
   ============================================================ */

/* — item 5 · the "Everything in {below}, plus —" header inside the include grid — */
.order-pkg-inc-plus { grid-column: 1 / -1; font-size: var(--fs-meta); font-weight: var(--fw-bold); letter-spacing: var(--ls-label); color: var(--o-text); padding: 2px 0 5px; }
.order-pkg-inc-plus + .order-pkg-inc-row { border-top: 0; }   /* first row never doubles a hairline under the header */
@media (min-width: 860px) { .order-pkg-inc-plus ~ .order-pkg-inc-row:nth-child(3) { border-top: 0; } }   /* the 2nd column's first row too */

/* — B4 · the per-package fact line (real catalog + duration numbers) — */
.order-pkg-facts { font-size: 11.5px; font-weight: var(--fw-semibold); letter-spacing: .02em; color: var(--o-muted); padding: 0 20px; font-variant-numeric: tabular-nums; }

/* — item 6 · data-true social-proof chip (packages + surfaced add-ons) — */
.order-proofchip { font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--o-muted); }
.order-pkg .order-proofchip { display: block; padding: 6px 20px 0; }
.order-addon .order-proofchip { display: inline-block; }

/* — item 5 · one quiet door to the ✓ / — comparison matrix — */
.order-cmp-linkwrap { text-align: center; margin-top: 14px; }
.order-cmp-link { font-size: var(--fs-caption); }
/* NB: this table lives inside the lightbox <dialog>, which mounts OUTSIDE the
   .order shell — so it uses ROOT theme vars (--text/--muted/--faint/--border,
   light-or-dark correct) for colour; only --order-accent/--order-serif are carried
   in from the shell by openCompareMatrix (see order.js). */
.order-cmp-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.order-cmp-table { border-collapse: collapse; width: 100%; min-width: 520px; font-variant-numeric: tabular-nums; }
.order-cmp-table th, .order-cmp-table td { padding: 10px 12px; text-align: center; border-bottom: 1px solid var(--border); }
.order-cmp-rowhead { text-align: left; font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--text); white-space: nowrap; }
.order-cmp-col { vertical-align: bottom; }
.order-cmp-colname { display: block; font-family: var(--order-serif); font-size: var(--fs-md); color: var(--text); }
.order-cmp-colprice { display: block; font-size: var(--fs-caption); font-weight: var(--fw-bold); color: var(--text); }
.order-cmp-colrec { display: block; font-size: 9.5px; font-weight: var(--fw-black); letter-spacing: .12em; text-transform: uppercase; color: var(--order-accent); margin-top: 2px; }
.order-cmp-col.rec, .order-cmp-cell.rec { background: color-mix(in srgb, var(--order-accent) 8%, transparent); }
.order-cmp-yes { color: var(--order-accent); font-weight: var(--fw-black); }
.order-cmp-dash { color: var(--faint); }
.order-cmp-teaser td { border-bottom: 0; }
.order-cmp-teasercell { text-align: center; font-size: 12.5px; color: var(--muted); padding-top: 14px; }

/* — item 11 · honest availability caption under the slot grid (fact, not alarm) — */
.order-slots-left { font-size: 12.5px; color: var(--o-muted); margin: 10px 0 0; }

/* — item 12 · included-vs-adjacent narration in the browse rows — */
.order-more-namecol { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.order-more-namecol .order-more-name { flex: 0 0 auto; }
.order-adjacent { font-size: 11.5px; line-height: 1.45; color: var(--o-muted); }

/* — item 9 · mobile one-thumb: the ribbon docks as a total + CTA bottom bar — */
.order-ribbon-right { display: flex; align-items: center; gap: 14px; min-width: 0; }
.order-ribbon-cta { display: none; }
@keyframes order-sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
@media (max-width: 640px) {
  .order-ribbon { gap: 12px; padding: 10px 16px calc(10px + env(safe-area-inset-bottom)); }
  .order-ribbon-note { display: none; }   /* the "every price shown" promise lives in the flow copy; the bar stays total + action */
  .order-ribbon-total { font-size: var(--fs-h1); }
  .order-ribbon-cta:not(.empty) { display: inline-flex; align-items: center; justify-content: center; font: inherit; font-size: var(--fs-body); font-weight: var(--fw-bold); min-height: 44px; padding: 0 18px; border: 0; border-radius: 999px; background: var(--order-accent); color: var(--order-on-accent); cursor: pointer; white-space: nowrap; }
  .order-host { padding-bottom: calc(78px + env(safe-area-inset-bottom)); }   /* keep the in-flow Back/Continue reachable above the fixed bar */
  /* depth sheets + comparison matrix dock as slide-up bottom sheets */
  /* SPEC-V2MODALFIT — 92vh -> 92dvh: iOS Safari's address/toolbar chrome
     shrinks and grows mid-scroll, so a plain vh unit here could clip the
     sheet's own footer behind the (currently hidden) toolbar right as it
     reappears. dvh tracks the toolbar instead of assuming it's gone. */
  .order-lightbox { max-width: 100vw; width: 100vw; margin: 0; position: fixed; left: 0; right: 0; bottom: 0; top: auto; border-radius: 16px 16px 0 0; max-height: 92dvh; animation: order-sheet-up .28s var(--ease); }
  .order-lightbox-body { max-height: 78vh; }
}
@media (prefers-reduced-motion: reduce) { .order-lightbox { animation: none; } }

/* SPEC308 landing-reorder (Vincent 2026-07-08) — the package ladder shares the
   first viewport: compact hero (no button), cards pull UP over the hero's bottom
   edge (art-directed overlap; solid --o-panel cards), the guided path demoted to
   a quiet link under the ladder. Landing tone is ink, so the kicker/sub that sit
   on the photo edge get a whisper of shadow for legibility. */
.order-hero.compact { min-height: min(58svh, 560px); }
.order-hero.compact .order-hero-title { font-size: clamp(34px, 6vw, 62px); margin-bottom: 12px; }
.order-hero.compact .order-hero-sub { margin-bottom: 8px; }
.order-hero.compact .order-hero-body { padding-bottom: clamp(72px, 11vh, 118px); }
.order-lpkgs.overlap { margin-top: clamp(-84px, -7vh, -44px); position: relative; z-index: 2; }
.order-lpkgs.overlap .order-logos-kicker, .order-lpkgs.overlap .order-lpkgs-sub { text-shadow: 0 1px 18px rgba(0, 0, 0, .45); }
.order-undecided { display: flex; flex-direction: column; align-items: center; gap: 2px; margin: 26px auto 0; padding-inline: 20px; text-align: center; }
.order-undecided .order-link { color: var(--o-muted); font-weight: var(--fw-medium); text-decoration: underline; text-underline-offset: 3px; min-height: 44px; }
.order-undecided .order-link:hover { color: var(--o-text, inherit); }
.order-undecided .order-fine { margin-top: 4px; text-align: center; }
.order-logos-band { margin-top: clamp(26px, 4.5vh, 44px); }
@media (max-width: 640px) {
  .order-hero.compact { min-height: min(46svh, 430px); }
  .order-hero.compact .order-hero-body { padding-bottom: 64px; }
  .order-lpkgs.overlap { margin-top: -34px; }
}

/* SPEC308 intimacy pass (Vincent 2026-07-08) — less scrolling, always-reachable
   Continue, the agent-brand spotlight leads the add-ons, preview-slots tag. */
/* 1 · property step de-scroll: slim chapter banner + 2-col optional-details grid */
.order-chapter.slim .order-chapter-img { aspect-ratio: auto; height: clamp(88px, 13vh, 140px); }
.order-detgrid { display: grid; gap: 0 22px; }
@media (min-width: 640px) { .order-detgrid { grid-template-columns: 1fr 1fr; } }
/* 2 · the Continue mirror in the fixed ribbon now lives on EVERY viewport —
   the next page is always one tap away at the bottom edge, no scrolling. */
.order-ribbon-cta:not(.empty) { display: inline-flex; align-items: center; justify-content: center; font: inherit; font-size: var(--fs-body); font-weight: var(--fw-bold); min-height: 44px; padding: 0 20px; border: 0; border-radius: 999px; background: var(--order-accent); color: var(--order-on-accent); cursor: pointer; white-space: nowrap; transition: transform var(--t-fast) var(--ease-spring), box-shadow var(--t-base) var(--ease); }
.order-ribbon-cta:not(.empty):hover { transform: translateY(-1px); box-shadow: var(--shadow); }
.order-ribbon-cta:disabled { opacity: .55; cursor: default; transform: none; box-shadow: none; }
/* 3 · the Your-Brand SPOTLIGHT — an editorial ink moment that leads the offers,
   visually distinct from the utility add-on cards below it */
.order-brandcard.spotlight { background: linear-gradient(135deg, #17181d, #232630 82%); color: #f4f1ea; border: 1px solid rgba(255, 255, 255, .14); border-radius: 18px; box-shadow: var(--shadow-lg); }
.order-brandcard.spotlight .order-brandcard-eyebrow { color: color-mix(in srgb, var(--order-accent) 34%, #fff); letter-spacing: .18em; }
.order-brandcard.spotlight .order-brandcard-title { font-family: var(--order-serif); font-weight: 400; font-size: clamp(24px, 3.4vw, 34px); color: #fff; }
.order-brandcard.spotlight .order-brandcard-lede { color: rgba(255, 255, 255, .78); }
.order-brandcard.spotlight .order-brandcard-collabel { color: rgba(255, 255, 255, .55); }
.order-brandcard.spotlight .order-brandcard-list { color: rgba(255, 255, 255, .82); }
.order-brandcard.spotlight .order-brandcard-scent { color: rgba(255, 255, 255, .62); }
.order-brandcard.spotlight .order-link { color: color-mix(in srgb, var(--order-accent) 30%, #fff); }
.order-brandcard.spotlight .order-brandtier { background: rgba(255, 255, 255, .06); border: 1px solid rgba(255, 255, 255, .22); color: #f4f1ea; }
.order-brandcard.spotlight .order-brandtier:hover { border-color: rgba(255, 255, 255, .5); }
.order-brandcard.spotlight .order-brandtier.on { background: var(--order-accent); border-color: var(--order-accent); color: var(--order-on-accent); }
.order-brandcard.spotlight .order-brandtier-price { color: inherit; opacity: .82; }
.order-logos-kicker.home-addons { margin: 30px auto 14px; }
/* 4 · preview-slots — honest, unmistakable sample-data tag */
.order-preview-tag { background: #fef3c7; color: #92400e; border: 1px solid #f3d489; border-radius: 10px; padding: 9px 14px; font-size: 12.5px; line-height: var(--lh-normal); margin: 0 0 14px; }
html[data-theme="dark"] .order-preview-tag { background: #2c2413; color: #f5d489; border-color: #57431c; }

/* SPEC308 identity (Vincent 2026-07-08) — "Ordering for" internal picker, agent
   welcome line. The picker reuses the address .order-sugg dropdown for results. */
.order-forwhom { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 20px; padding: 12px 16px; background: var(--o-panel); border: 1px solid var(--o-line); border-radius: 12px; box-shadow: var(--shadow-sm); }
.order-forwhom-label { font-size: var(--fs-micro); font-weight: var(--fw-black); letter-spacing: .14em; text-transform: uppercase; color: var(--o-muted); white-space: nowrap; }
.order-forwhom-search { position: relative; flex: 1 1 240px; min-width: 200px; }
.order-forwhom-search .order-sugg { top: calc(100% + 4px); }
.order-forwhom-set { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.order-forwhom-chip { display: inline-flex; align-items: baseline; gap: 8px; padding: 5px 12px; border-radius: 999px; background: color-mix(in srgb, var(--order-accent) 10%, var(--o-panel)); border: 1px solid color-mix(in srgb, var(--order-accent) 32%, var(--o-line)); }
.order-forwhom-name { font-weight: 650; color: var(--o-text); }
.order-forwhom-sub { font-size: 12.5px; color: var(--o-muted); }
.order-forwhom-new { font-weight: 650; color: var(--order-accent); border-top: 1px solid var(--o-line); }
.order-hero-greet { color: rgba(255, 255, 255, .92); font-size: clamp(15px, 2vw, 18px); line-height: 1.55; margin: 0 0 10px; font-weight: 550; text-shadow: 0 1px 14px rgba(0, 0, 0, .35); max-width: 46ch; }

/* SPEC309 — "Team on this job" chips (one-tap crew attachment in the job modal) */
.job-team-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 8px; }
.job-team-chip { display: inline-flex; align-items: center; gap: 5px; padding: 3px 6px 3px 10px; border-radius: 999px; background: var(--accent-soft, color-mix(in srgb, var(--accent) 12%, var(--card))); border: 1px solid color-mix(in srgb, var(--accent) 28%, var(--border)); font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--text); }
.job-team-x { display: inline-grid; place-items: center; width: 18px; height: 18px; border: 0; border-radius: 50%; background: transparent; color: var(--muted); font-size: 15px; line-height: 1; cursor: pointer; }
.job-team-x:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); color: var(--text); }
.role-select:disabled { opacity: .7; cursor: default; }

/* ============================================================
   SPEC312 — locked (view-only) delivery: watermark + lock notice
   ============================================================ */
/* the repeating diagonal brand watermark (bg image set inline per-brand) */
.cstatus-wm { position: absolute; inset: 0; pointer-events: none; background-repeat: repeat; z-index: 3; }
/* on the cinematic cover the mark sits over the PHOTO but under the scrim + title/CTAs */
.cstatus-cover .cstatus-wm { z-index: 0; }
.cstatus-gal--locked .cstatus-gal-open, .cstatus-gal--locked .cstatus-gal-item { -webkit-user-select: none; user-select: none; }
.cstatus-gal--locked img { -webkit-user-drag: none; }
.cstatus-gal-item { position: relative; }
.cstatus-gal-open { position: relative; }
/* quiet luxury lock notice under the cover */
.cstatus-locknote { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 14px 18px 0; padding: 12px 16px; border: 1px solid color-mix(in srgb, var(--cover-accent, #d4af37) 35%, var(--border)); border-radius: 12px; background: color-mix(in srgb, var(--cover-accent, #d4af37) 7%, var(--card)); font-size: 13.5px; }
.cstatus-locknote-ic { display: inline-grid; place-items: center; color: color-mix(in srgb, var(--cover-accent, #d4af37) 80%, var(--text)); }
.cstatus-locknote-txt { flex: 1 1 240px; }
.cstatus-locknote .btn { margin-left: auto; }
/* lightbox: watermark stage wrapper (img keeps its own transitions) */
.cstatus-lb-stage { position: relative; display: grid; place-items: center; max-width: 100%; max-height: 100%; }
.cstatus-lb-stage img { display: block; }
.cstatus-lb-stage .cstatus-wm { z-index: 4; }
/* locked video overlay */
.cstatus-lb--video .cstatus-lb-vidwrap { position: relative; width: min(1100px, 94vw); max-height: 86vh; }
.cstatus-lb--video video { width: 100%; max-height: 86vh; display: block; border-radius: 10px; background: #000; }
.cstatus-lb--video .cstatus-wm { border-radius: 10px; }

/* ============================================================
   SPEC314 — Signature Pulse (retainer at-a-glance board)
   ============================================================ */
.tss-pulse { display: flex; flex-direction: column; }
.tss-pulse-row { display: grid; grid-template-columns: 26px minmax(140px, 1.3fr) minmax(150px, 1.2fr) 82px minmax(90px, .9fr) minmax(90px, .9fr) minmax(80px, .8fr) minmax(66px, .65fr); gap: 10px; align-items: center; padding: 9px 8px; border-radius: 8px; cursor: pointer; font-size: var(--fs-caption); }
.tss-pulse-row:not(.tss-pulse-head):hover { background: var(--bg-2); }
.tss-pulse-head { cursor: default; font-size: 10.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .05em; color: var(--muted); border-bottom: 1px solid var(--border); border-radius: 0; padding-bottom: 6px; }
.tss-pulse-rag { text-align: center; font-size: var(--fs-meta); }
.tss-pulse-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tss-pulse-cell { display: inline-flex; align-items: center; gap: 6px; min-width: 0; white-space: nowrap; }
.tss-pulse-cell.overdue { color: var(--danger); font-weight: var(--fw-bold); }
.tss-pulse-quota { position: relative; display: inline-block; width: 74px; height: 16px; border-radius: 8px; background: var(--bg-2); overflow: hidden; }
.tss-pulse-quota-fill { position: absolute; inset: 0 auto 0 0; background: color-mix(in srgb, var(--info) 55%, transparent); border-radius: 8px; }
.tss-pulse-quota-fill.done { background: color-mix(in srgb, var(--ok) 60%, transparent); }
.tss-pulse-quota-n { position: relative; display: block; text-align: center; font-size: 10.5px; font-weight: var(--fw-black); line-height: 16px; }
/* SPEC-V2RETAIN — RAG status pill (label, not just the dot) + the champagne streak
   badge (design's "eleven pixels of shape" board — the ONE decorative champagne
   accent per row, reserved for a real, CURRENT growth streak per the stale-snapshot
   law) + the 4-tile counters row above the board + the action column. */
.tss-pulse-counters { display: flex; gap: 10px; flex-wrap: wrap; margin: 0 0 12px; }
.tss-pulse-counter { flex: 1 1 130px; background: var(--card); border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; }
.tss-pulse-counter-val { font-size: 22px; font-weight: var(--fw-black); letter-spacing: -.5px; font-variant-numeric: tabular-nums; }
.tss-pulse-counter-label { font-size: 11px; color: var(--muted); margin-top: 2px; }
.tss-pulse-rag-pill { display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; border-radius: 999px; font-size: 10.5px; font-weight: var(--fw-bold); white-space: nowrap; }
.tss-pulse-streak { display: inline-flex; align-items: center; gap: 3px; padding: 1px 7px; border-radius: 999px; background: color-mix(in srgb, var(--accent-warm) 16%, transparent); border: 1px solid color-mix(in srgb, var(--accent-warm) 40%, transparent); color: var(--accent-warm); font-size: 10.5px; font-weight: var(--fw-black); }
.tss-pulse-action { font-weight: var(--fw-bold); text-align: right; }
@media (max-width: 860px) { .tss-pulse-row { grid-template-columns: 22px minmax(120px, 1.5fr) minmax(130px, 1.2fr) 70px; } .tss-pulse-row > :nth-child(n+5) { display: none; } }

/* SPEC316 — Jarvis Search jump-to-message flash */
.chat-msg--flash { animation: vvt-msg-flash 2.4s ease-out; border-radius: 8px; }
@keyframes vvt-msg-flash { 0% { background: color-mix(in srgb, var(--accent) 22%, transparent); } 100% { background: transparent; } }

/* SPEC317 — Ask VVT (Jarvis) answer card in the palette */
.qs-askhost:empty { display: none; }
.qs-ask { margin: 10px 14px 4px; padding: 12px 14px; border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border)); border-radius: 12px; background: color-mix(in srgb, var(--accent) 6%, var(--panel)); }
.qs-ask-h { font-weight: var(--fw-black); font-size: 13.5px; margin-bottom: 6px; }
.qs-ask-a { font-size: 13.5px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
.qs-ask-p { font-size: var(--fs-caption); color: var(--muted); margin-bottom: 8px; }
.qs-ask-src { font-size: 10.5px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 10px 0 2px; }

/* SPEC318 — "how the sun hits this home" impact lines */
.cond-impact { display: flex; flex-direction: column; gap: 5px; margin-top: 10px; }
.cond-impact-line { font-size: var(--fs-caption); line-height: var(--lh-normal); }
.cond-impact-line.strong { font-weight: var(--fw-bold); }

/* ============================================================
   SPEC319 — Post Production pipeline board (Timeliner-style)
   ============================================================ */
.pp-batches { display: flex; gap: 10px; overflow-x: auto; padding: 2px 2px 10px; margin-bottom: 6px; }
/* GOLIVE wave2 pick1 — wraps a batch pill + its optional "Deliver the shoot" shortcut */
.pp-batch-wrap { display: flex; flex-direction: column; gap: 5px; flex: 0 0 auto; }
.pp-batch-deliver { border: 1px solid color-mix(in srgb, var(--ok) 45%, var(--border)); background: color-mix(in srgb, var(--ok) 12%, transparent); color: var(--ok); border-radius: 8px; padding: 4px 9px; font-size: var(--fs-micro); font-weight: var(--fw-bold); cursor: pointer; white-space: nowrap; transition: transform .12s, box-shadow .12s; }
.pp-batch-deliver:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
.pp-batch { display: flex; align-items: center; gap: 9px; flex: 0 0 auto; border: 1px solid var(--border); border-radius: 12px; background: var(--panel); padding: 8px 12px; cursor: pointer; transition: transform .12s, box-shadow .12s, border-color .12s; }
.pp-batch:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
.pp-batch.active { border-color: var(--accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent); }
.pp-batch-ring { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; flex: 0 0 auto; }
.pp-batch-ring-n { display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: var(--panel); font-size: 10.5px; font-weight: var(--fw-black); }
.pp-batch-txt { display: flex; flex-direction: column; text-align: left; min-width: 0; }
.pp-batch-addr { font-weight: var(--fw-bold); font-size: 12.5px; white-space: nowrap; max-width: 150px; overflow: hidden; text-overflow: ellipsis; }
.pp-batch-date { font-size: var(--fs-micro); color: var(--muted); }
.pp-board { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(215px, 1fr); gap: 10px; overflow-x: auto; align-items: start; padding-bottom: 8px; }
.pp-col { background: var(--bg-2); border-radius: 12px; padding: 8px; min-height: 120px; }
.pp-col-head { display: flex; align-items: center; gap: 6px; font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .05em; color: var(--muted); padding: 4px 4px 8px; border-bottom: 2px solid; margin-bottom: 8px; }
.pp-col-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.pp-col-n { margin-left: auto; background: var(--panel); border-radius: 999px; padding: 1px 7px; font-size: 10.5px; }
.pp-col-empty { text-align: center; color: var(--faint); padding: 14px 0; }
.pp-card { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 8px; overflow: hidden; cursor: pointer; transition: transform .12s, box-shadow .12s; }
.pp-card:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
.pp-thumb { height: 74px; background-size: cover; background-position: center; background-color: var(--bg-2); }
.pp-card-body { padding: 9px 11px 10px; }
.pp-card-title { font-weight: var(--fw-bold); font-size: 12.5px; line-height: 1.3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-card-svc { font-size: 11.5px; color: var(--accent); font-weight: var(--fw-semibold); margin-top: 1px; }
/* SPEC-V2CONNECT D3 2026-08-28 — the add-on chip row (App.pipeCards' `addons`):
   never a full second card, a quiet pill beside the primary video's own line. */
.pp-card-addons { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 3px; }
.pp-card-addon-chip { font-size: 10.5px; font-weight: var(--fw-semibold); color: var(--muted); background: var(--subtle); border: 1px solid var(--border); border-radius: 5px; padding: 1px 6px; }
.pp-card-meta { font-size: var(--fs-micro); color: var(--muted); margin-top: 4px; }
/* SOPQC-3 — the shooter's brief preview line (job.brief; App.jobBriefFields). Absent
   when the job has no brief, same as .pp-card-meta above it. */
.pp-card-brief { font-size: var(--fs-micro); color: var(--accent); margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-acts { display: flex; gap: 4px; margin-top: 7px; }
.pp-act { border: 1px solid var(--border); background: var(--bg); border-radius: 7px; width: 27px; height: 24px; display: grid; place-items: center; font-size: var(--fs-meta); cursor: pointer; }
.pp-act:hover { border-color: var(--accent); }
.pp-act--ok { color: var(--ok); font-weight: var(--fw-black); }
.pp-act--warn { color: var(--warn); }

/* SPEC321 — review player: general notes + attachments + circle tool */
.review-generaltoggle.on { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.review-tcchip--general { cursor: default; opacity: .85; }
.review-filesrow { display: flex; gap: 6px; flex-wrap: wrap; margin: 4px 0 6px; }
.review-filesrow:empty { display: none; }
.review-filechip { display: inline-flex; align-items: center; gap: 4px; border: 1px solid var(--border); border-radius: 999px; background: var(--bg); padding: 2px 10px; font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--text); text-decoration: none; max-width: 220px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.review-filechip:hover { border-color: var(--accent); }
.review-filechip-x { border: 0; background: none; cursor: pointer; font-size: var(--fs-caption); color: var(--muted); padding: 0 0 0 2px; }

/* SPEC322 — Training Machine: skill matrix + 40-day arc */
.tm-matrix { display: grid; gap: 3px; align-items: center; min-width: 560px; }
.tm-h { font-size: 10px; font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: var(--ls-label); color: var(--muted); text-align: center; }
.tm-h--skill { writing-mode: vertical-rl; transform: rotate(180deg); max-height: 96px; overflow: hidden; text-overflow: ellipsis; justify-self: center; }
.tm-who { font-size: 12.5px; font-weight: var(--fw-bold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; padding-right: 6px; }
.tm-who.me { color: var(--accent); }
.tm-cell { border: 1px solid var(--border); background: var(--bg); border-radius: 8px; height: 30px; display: grid; place-items: center; cursor: pointer; transition: border-color .12s, transform .12s; }
.tm-cell:hover { border-color: var(--accent); transform: scale(1.08); }
.tm-dot { width: 13px; height: 13px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }
.tm-arc { padding: 10px 0; border-bottom: 1px solid var(--border); }
.tm-arc:last-of-type { border-bottom: 0; }
.tm-arc-bar { height: 8px; border-radius: 999px; background: var(--bg-2); overflow: hidden; margin-top: 6px; }
.tm-arc-fill { height: 100%; border-radius: 999px; background: linear-gradient(90deg, #579bfc, #00c875); transition: width .3s; }

/* SPEC323 — Jarvis Daily Brief. D6 — retired the bespoke gradient background + animated
   orb; the card now conforms to .insight (accent border) like every other insight card,
   and the FRIDAY brand mark is the sparkles line icon in the card-title, same as elsewhere. */
.jarvis-brief { margin-bottom: 14px; background: var(--panel); border: 1px solid var(--border); }
.jarvis-brief-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.jarvis-brief-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.jarvis-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--border); background: var(--bg); border-radius: 999px; padding: 6px 13px 6px 9px; font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--text); cursor: pointer; transition: border-color .12s, transform .12s; max-width: 100%; }
.jarvis-chip:hover { border-color: var(--accent); transform: translateY(-1px); }
.jarvis-chip-ico { font-size: var(--fs-body); }
/* INTEGRATOR 07-30: raw hex → semantic tokens, so both borders re-tint in dark
   mode like every other chip. Computed light-mode result is unchanged. */
.jarvis-chip--warn { border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); }
.jarvis-chip--red { border-color: color-mix(in srgb, var(--danger) 55%, var(--border)); }

/* SPEC324 — B-roll library */
.broll-form { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.broll-form .btn { grid-column: 1 / -1; justify-self: start; }
@media (max-width: 640px) { .broll-form { grid-template-columns: 1fr; } }
.broll-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 12px; }
.broll-chips .chip { cursor: pointer; border: 0; background: var(--bg-2); color: var(--muted); }
.broll-chips .chip--on { background: var(--accent-soft, var(--bg-2)); color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.broll-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
.broll-card { border: 1px solid var(--border); border-radius: var(--radius-4); padding: 12px 13px; background: var(--panel); }   /* DHSWEEP W9 — was a literal 12px, exactly --radius-4's value; zero-diff token wire */
.broll-card-title { font-weight: var(--fw-bold); font-size: 13.5px; line-height: var(--lh-snug); }
.broll-card-tags { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0 5px; }
.broll-tag { font-size: 10.5px; font-weight: var(--fw-bold); color: var(--accent); background: var(--accent-soft, var(--bg-2)); border-radius: 999px; padding: 2px 8px; }

/* SPEC326 — Trend Radar */
.trend--scripted { opacity: .78; }

/* SPEC327 — portal brand-intake wizard */
.pt-intake-invite { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; border: 1px solid var(--border); border-radius: 14px; padding: 14px 16px; margin: 4px 0 18px; background: var(--panel); }
.pt-intake-dots { display: flex; gap: 6px; margin: 6px 0 12px; }
.pt-intake-dot { width: 26px; height: 5px; border-radius: 999px; background: var(--border); }
.pt-intake-dot.on { background: var(--accent); }
.pt-intake-dot.done { background: var(--accent); opacity: .45; }
.pt-intake-card { border: 1px solid var(--border); border-radius: 14px; padding: 18px; background: var(--panel); }
.pt-intake-card .input { width: 100%; box-sizing: border-box; margin-bottom: 8px; }

/* SPEC-SNAPPER Phase C — "My listings": orders grouped by PROPERTY, one card per
   home instead of one per visit. Sits above "Your orders" on the client portal. */
.pt-listings { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; margin: 4px 0 22px; }
.pt-listing { border: 1px solid var(--border); border-radius: 14px; background: var(--panel); overflow: hidden; }
.pt-listing-hero { aspect-ratio: 16 / 9; background-size: cover; background-position: center; background-color: var(--bg-2); }
.pt-listing-body { padding: 14px 16px 16px; }
.pt-listing-addr { font-weight: var(--fw-bold); font-size: 15px; line-height: 1.3; }
.pt-listing-meta { color: var(--muted); font-size: 12.5px; margin: 2px 0 10px; }
.pt-listing-acts { display: flex; gap: 8px; flex-wrap: wrap; }

/* SPEC328 — live-call FAB: its own slot LEFT of the ＋ (was stacked exactly on top of it) */
.call-fab {
  position: fixed; right: 84px; bottom: 22px; z-index: 60;
  width: 52px; height: 52px; border-radius: 50%; border: 0; cursor: pointer;
  background: var(--accent); color: #fff; font-size: var(--fs-h1); line-height: 1;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 6px 18px -3px rgba(var(--ring-rgb), .5), 0 2px 6px rgba(16, 24, 40, .2);
  transition: transform .12s ease, background .12s ease;
}
.call-fab:hover { background: var(--accent-hover); transform: translateY(-2px) scale(1.03); }
.call-fab:active { transform: scale(.96); }
@media (max-width: 640px) { .call-fab { right: 84px; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); width: 56px; height: 56px; } }
@media (prefers-reduced-motion: reduce) { .call-fab { transition: none; } }

/* SPEC333 — Lead Setting queue */
.ls-sect { font-size: var(--fs-micro); font-weight: var(--fw-black); text-transform: uppercase; letter-spacing: .05em; color: var(--muted); margin: 12px 0 2px; }

/* SPEC334 — Feature Spotlight chip */
.jarvis-chip--spot { border-style: dashed; border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); max-width: 100%; white-space: normal; text-align: left; }
.jarvis-chip-x { margin-left: 4px; color: var(--muted); font-size: var(--fs-meta); padding: 0 2px; }
.jarvis-chip-x:hover { color: var(--text); }

/* ============ SPEC336 — chat glow-up (Mobbin: Slack/ClickUp patterns) ============ */
/* avatars: rounded-square (Slack), a touch bigger presence */
.chat-av { border-radius: 9px; }
/* message rows: calmer hover, more air between groups */
.chat-msg { padding: 4px 8px; }
.chat-msg:not(.grouped) { margin-top: 2px; }
/* SPEC-V2CHATB item 6 2026-08-28 — the hover rail: prototype anatomy is a top-right
   floating toolbar revealed via a `--act` CSS custom property the ROW sets on its own
   hover/focus (README §Chat: "Reveal uses a --act custom property set by the row's
   own hover, with a .32 fallback where custom properties are unsupported"), replacing
   the below-text ghost row CONSOL-1 (2026-07-11) had moved reacting/reply/pin into.
   That decision predates this fidelity rebuild wave and is superseded by it per
   SPEC-V2CHATB item 6's explicit instruction — the ALREADY-ADDED reaction chips
   (.chat-reactions) and the thread-summary pill stay exactly where CONSOL-1 put them
   (still below the text, still always visible once added); only the CONTROLS for
   taking a new action (react/reply/pin/copy) move into this corner rail. --act lives
   on .chat-msg itself (0 idle, 1 on hover/focus-within) so every child that wants the
   same reveal curve — the rail's own opacity, the gutter timestamp elsewhere — can
   read the same variable instead of each writing its own :hover selector. */
.chat-msg { --act: 0; }
.chat-msg:hover, .chat-msg:focus-within { --act: 1; }
.chat-msg-actions {
  top: 2px; right: 8px; gap: 0; opacity: var(--act, .32); transform: none;
  border: 1px solid var(--border); border-radius: 9px; background: var(--panel);
  box-shadow: 0 6px 18px -6px rgba(16, 24, 40, .28); overflow: hidden;
  transition: opacity .14s ease;
}
.chat-msg-act { border: 0; box-shadow: none; border-radius: 0; padding: 5px 10px; background: var(--panel); color: var(--muted); cursor: pointer; }
.chat-msg-act:hover { background: var(--hover); color: var(--text); }
.chat-msg-act + .chat-msg-act { border-left: 1px solid var(--border); }
/* the reply/pin buttons reuse two pre-existing class names purely as DOM test hooks
   (VISUAL 07-30's pinned-rights count, CHATB3.3's reply-affordance click) — see the
   moved-tests note in the V2CHATB suite region; this rail is their only styling now,
   the older .chat-ghost-row-scoped rules below no longer have a matching ancestor. */
.chat-msg-act--pin-rail.on { color: var(--accent); }
/* @media(hover:none) touch devices get no hover at all — keep the rail legibly
   visible rather than stuck at the .32 idle opacity a finger can never "hover" past. */
@media (max-width: 640px), (hover: none) { .chat-msg-actions { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .chat-msg-actions { transition: none; } }
/* grouped gutter time — appears on row hover */
.chat-av-spacer { display: flex; align-items: center; justify-content: flex-end; }
.chat-gutter-time { font-size: 10px; color: var(--faint); opacity: 0; padding-right: 2px; }
.chat-msg:hover .chat-gutter-time { opacity: 1; }
/* day divider: floating pill on a hairline (Slack) */
.chat-day-divider { display: flex; align-items: center; margin: 14px 0 6px; }
.chat-day-divider::before, .chat-day-divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }
/* SPEC-V2POLISH1 D2 — measured against the prototype's own day-divider text
   (VVT-Hub-Owner.dc.html: "font-size:11.5px;font-weight:600;color:#656d7b" — no
   border/pill there, plain text on the hairline). V2POLISH1 kept the bordered
   floating-pill TREATMENT as a deliberate, documented SPEC336 deviation.
   SPEC-V2POLISH2 D2 2026-08-28 REVERSES that call, by the SAME ruling this
   whole wave runs on ("i liked the stacked cards thing... thats how i
   designed it" — the prototype wins, unqualified, over SPEC336 wherever the
   two conflict): bare text on the hairline, no pill chrome at all. Moved test:
   tests/suite.js "VP8" (V2POLISH1's own lock, which asserted the pill STAYED)
   is updated in place under V2POLISH2 to assert the opposite — chrome absent —
   preserving VP8's original intent (lock the divider's type to the measured
   sheet) while correcting what "matches the sheet" means for the chrome itself. */
.chat-day-divider span {
  font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--faint); background: none;
  border: 0; border-radius: 0; padding: 0; margin: 0; box-shadow: none;
}
/* the red New line (Slack unread rule) */
.chat-new-divider { display: flex; align-items: center; margin: 10px 0 4px; }
.chat-new-divider::before, .chat-new-divider::after { content: ''; flex: 1; height: 1px; background: #e2445c; opacity: .5; }
.chat-new-divider span { font-size: 10px; font-weight: var(--fw-black); letter-spacing: .08em; text-transform: uppercase; color: #e2445c; padding: 0 10px; }
/* bot/system cards: one quiet collapsed line, not a fake teammate */
.chat-bot-card { margin: 6px 0 6px 44px; border: 1px solid transparent; border-radius: 12px; background: none; overflow: hidden; max-width: 620px; flex-shrink: 0; }
/* ^ flex-shrink:0 (COHESION closer 2026-08-11): .chat-list is a column-flex scroller and
   overflow:hidden gives this card an automatic min-size of 0, so it absorbed ALL flex
   shrink and rendered 2px tall whenever the list overflowed (i.e. always on a phone
   with real history). Verified live at 375x812: 2px before, 42px after. */
/* SPEC-DHCHAT D2 2026-08-27 — README's "activity" convention (the only auto-posting
   message type this app actually has today — the morning digest, App.buildChatDigest,
   core.js) is "icon, one line, muted, indented" — this card's COLLAPSED state (the
   default; botCard in chat.js already renders it collapsed) already had the icon,
   line, and 44px indent (matching .chat-av's own width, above), it just carried card
   chrome (border+tinted fill) that read as a boxed object, not a quiet line. Border/
   fill now apply only once actually expanded (.open, chat.js's own toggle class) —
   the same border/background this card always had, kept but deferred. */
.chat-bot-card.open { border-color: var(--border); background: var(--subtle); }
.chat-bot-head {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 12px;
  border: 0; background: none; cursor: pointer; font: inherit; text-align: left; color: var(--text);
}
.chat-bot-head:hover { background: var(--hover); }
.chat-bot-av { width: 24px; height: 24px; border-radius: 7px; background: var(--bg-2); display: grid; place-items: center; font-size: var(--fs-caption); flex: 0 0 auto; }
.chat-bot-title { font-weight: var(--fw-bold); font-size: var(--fs-meta); color: var(--muted); flex: 0 0 auto; }
/* SPEC-DHCHAT D2 2026-08-27 — the activity-line convention is muted top to bottom;
   .chat-bot-title (above) already was, this one carried full --text. */
.chat-bot-sum { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; min-width: 0; }
.chat-bot-caret { color: var(--faint); font-size: var(--fs-micro); flex: 0 0 auto; }
.chat-bot-body { display: none; padding: 4px 14px 12px 44px; font-size: var(--fs-caption); line-height: var(--lh-normal); }
.chat-bot-card.open .chat-bot-body { display: block; }

/* SPEC-V2CONNECT D1/D2 2026-08-28 — the three typed auto-post shapes
   (chat.js: sysCutCard/sysClientNote/sysActivityLine), matching the
   prototype's own Chat-screen renderings (design-handoff/VVT-Hub-Owner.dc.html)
   adapted to this app's dark-first tokens — the mock is light-mode chrome, so
   structure (thumbnail slot + title + version + notes pill + link; champagne
   left border + eyebrow + name + timecode chip + body + link; one muted
   indented line) carries over, literal hex does not. 44px indent matches
   .chat-bot-card's own convention so every system row — human-typed or
   auto-posted — lines up in the same quiet left rail. */
.chat-sys-wrap { margin: 6px 0 6px 44px; max-width: 620px; flex-shrink: 0; }

/* cut card */
.chat-cutcard { display: flex; align-items: center; gap: 12px; border: 1px solid var(--border); border-radius: 11px; background: var(--card); padding: 9px 13px 9px 9px; }
.chat-cutcard--live { cursor: pointer; }
.chat-cutcard--live:hover { border-color: var(--accent); }
.chat-cutcard-thumb { flex: none; width: 60px; height: 38px; border-radius: 6px; background: var(--subtle); border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; color: var(--muted); }
.chat-cutcard-body { flex: 1; min-width: 0; }
.chat-cutcard-title { font-size: var(--fs-caption); font-weight: var(--fw-bold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat-cutcard-meta { display: flex; align-items: center; gap: 8px; margin-top: 4px; font-size: var(--fs-micro); color: var(--muted); font-variant-numeric: tabular-nums; }
.chat-cutcard-notes { font-weight: var(--fw-bold); color: #b54708; background: rgba(181, 71, 8, .12); border: 1px solid rgba(181, 71, 8, .28); border-radius: 5px; padding: 2px 7px; }
.chat-cutcard-time { color: var(--faint); }
.chat-cutcard-open { flex: none; font-size: 12.5px; font-weight: var(--fw-bold); white-space: nowrap; color: var(--accent); }

/* client note — champagne left border is DECORATIVE only (the token's own
   :root rule, "never text-on-fill"); note body stays normal ink. */
.chat-note { border: 1px solid var(--border); border-left: 3px solid var(--accent-warm); border-radius: 0 10px 10px 0; background: var(--card); padding: 11px 14px; }
.chat-note-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.chat-note-eyebrow { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: .06em; text-transform: uppercase; color: var(--accent-warm); }
.chat-note-who { font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--muted); }
.chat-note-tc { font-size: var(--fs-micro); font-weight: var(--fw-bold); color: var(--accent); background: var(--accent-soft); border-radius: 5px; padding: 2px 7px; font-variant-numeric: tabular-nums; }
.chat-note-time { font-size: var(--fs-micro); color: var(--faint); margin-left: auto; }
.chat-note-body { margin-top: 7px; font-size: var(--fs-body); line-height: var(--lh-normal); }
.chat-note-jump { display: inline-block; margin-top: 8px; font-size: 12.5px; font-weight: var(--fw-bold); color: var(--accent); }

/* activity — ONE quiet line, muted, indented; never a card. */
.chat-activity-row { display: flex; align-items: center; gap: 8px; margin: 2px 0 2px 44px; padding: 2px 0; color: var(--muted); font-size: 12.5px; }
.chat-activity-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--faint); flex: none; }
.chat-activity-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chat-activity-time { font-size: var(--fs-micro); color: var(--faint); font-variant-numeric: tabular-nums; flex: none; }

/* composer: SPEC-V2POLISH2 D1 2026-08-28 — the STACKED writing-card anatomy.
   Vincent's ruling (verbatim, 08-28): "i liked the stacked cards thing. makes
   it feel super duper cool. thats how i designed it" — the prototype's
   stacked card REPLACES SPEC336's single-row pill (.chat-composer-row +
   .chat-composer-pillrow, both retired below); SPEC336 loses by this ruling,
   not re-litigated. Measured against design-handoff/VVT-Hub-Owner.dc.html's
   Chat-screen composer — literal inline-style values, read via the
   dc-extract.mjs/dc-render.mjs harness (scratchpad) — see the raw report's
   value sheet for the full transcription. Anatomy: the textarea sits ALONE
   on top of the card; a quiet toolbar sits BENEATH it, inside the SAME
   bordered card (never a second box). This is a pure DOM-position + CSS
   restructure — chat.js's send()/keydown/input/paste listeners on `ta` and
   every attach/emoji/mic/job-pill handler are byte-identical to before this
   wave, only relocated in the DOM (see chat.js's own composer-build comment). */
.chat-composer { padding: 0 22px 20px; }
.chat-composer-card {
  margin-top: 12px; background: var(--panel); border: 1px solid var(--border-strong);
  border-radius: 12px; box-shadow: var(--shadow-card); transition: border-color .12s, box-shadow .12s;
}
.chat-composer-card:focus-within { border-color: var(--accent); box-shadow: var(--focus-ring-soft); }
/* the writing surface — measured 15.5px / line-height 1.6 / min-height 52px /
   padding 15px 18px 4px. Font-size is NOT redeclared here on purpose: the
   base .chat-input rule (styles.css ~2972, SPEC-V2CHATB) already carries the
   desktop 15.5px (an exact match to the prototype's own measured value) with
   the ≤640px Safari anti-zoom floor physically beneath it — redeclaring here
   would just be a second place that trap could silently die from a future
   edit, so this rule only touches what's genuinely different from the base. */
.chat-composer-card .chat-input {
  border: 0; box-shadow: none; background: transparent; width: 100%; box-sizing: border-box;
  min-height: 52px; padding: 15px 18px 4px; line-height: 1.6;
}
.chat-composer-card .chat-input:focus { border: 0; box-shadow: none; }
/* the quiet toolbar beneath — measured gap:3px, padding:6px 12px 10px. Every
   child (bold/italic/link/attach/emoji/mic, the divider, the two attach
   pills, the spacer, the "Enter sends" hint, Send) is a direct flex child of
   THIS row, same as the prototype's one toolbar div — never a second nested
   row, which is what SPEC336's separate .chat-composer-pillrow used to be. */
.chat-composer-toolbar { display: flex; align-items: center; gap: 3px; padding: 6px 12px 10px; flex-wrap: wrap; }
/* bold/italic/link/attach(+)/emoji — uniform 30px quiet icon footprint,
   matching the prototype's 5 composeTools icons exactly (bold, italic, link,
   clip/attach, mic were the ones its own data array carried — see the raw
   report; emoji/GIF stay reachable exactly where they already were: emoji's
   own picker button, GIF inside the existing ＋ menu — "existing pickers",
   nothing new added to the visible row). */
/* :not(.chat-tool--pill) — attachJobPill/attachCutPill carry BOTH .chat-tool
   AND .chat-tool--pill (same shared base class every composer icon uses, per
   chat.js), so without this exclusion this fixed-30×30-square rule's TWO-class
   specificity (0,0,2,0) beat plain `.chat-tool--pill`'s own width:auto rule
   below (0,0,1,0) and squashed both attach pills to unreadable 30px squares —
   caught live in the side-by-side screenshot pass, not a theoretical trap. */
.chat-composer-toolbar .chat-tool:not(.chat-tool--pill), .chat-composer-toolbar .chat-tool--fmt {
  width: 30px; height: 30px; font-size: 14px; border: 0; background: none; color: var(--muted); border-radius: 7px;
}
.chat-composer-toolbar .chat-tool--plus { font-size: 18px; }
.chat-composer-toolbar .chat-tool:not(.chat-tool--pill):hover, .chat-composer-toolbar .chat-tool--fmt:hover { background: var(--hover); color: var(--text); }
/* the vertical hairline the prototype uses to separate the format/attach
   icon cluster from the two named attach pills — measured 1px × 20px. */
.chat-composer-divider { width: 1px; height: 20px; background: var(--border); margin: 0 4px; flex: none; }
.chat-composer-spacer { flex: 1 1 auto; }
/* "Enter sends" — a NEW quiet hint the prototype carries that ours never had
   (measured 11.5px / --faint, nearest token to the prototype's unmapped
   #b8bec7 — the honest literal call is documented in the raw report). Purely
   informational: chat.js's real Enter/Shift-Enter handler is unchanged and
   this never claims otherwise for a channel where it might differ. */
.chat-composer-hint { font-size: 11.5px; color: var(--faint); margin-right: 9px; white-space: nowrap; }
/* Send — measured min-height 36px, padding 0 19px, border-radius 8px,
   font-size 13.5px/600. CAUGHT LIVE (side-by-side pass): the SPEC336-era
   unscoped `.chat-send { height:36px; border-radius:10px }` rule was dropped
   when this block replaced the old .chat-composer-row section, silently
   leaving Send at the base .btn 42px/var(--radius-sm)/13px/"8px 14px" —
   restoring it HERE, scoped + measured, rather than reintroducing the old
   unscoped rule (nothing else in the app uses .chat-send outside a composer
   toolbar/row, so scoping costs nothing and documents the real source).
   margin-left:auto (not just the flex spacer) keeps Send pinned to the right
   edge of WHATEVER line it lands on if the toolbar wraps on a narrow phone
   (D1: "Send as the primary at the right end" — the spacer alone only
   guarantees that on the row it started on, not after a wrap). */
.chat-composer-toolbar .chat-send {
  margin: 0 0 0 auto; min-height: 36px; height: 36px; padding: 0 19px; border-radius: 8px; font-size: 13.5px; font-weight: var(--fw-semibold);
}
/* attach-job / attach-cut pills — measured height 30px, padding 0 11px, gap
   7px, border var(--border) (#e4e7ec exact), background var(--subtle)
   (#fbfbfa exact), label 12.5px/600/--accent-hover (#1e3f66 exact — darker
   than the icon-stroke accent, same fg/bd split V2POLISH1's VP5 already
   documented for the segment tab). */
.chat-tool--pill {
  display: inline-flex; align-items: center; gap: 7px; width: auto; height: 30px; padding: 0 11px;
  border: 1px solid var(--border); border-radius: 7px; background: var(--subtle); font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--accent-hover);
}
.chat-tool--pill:hover { border-color: var(--accent); background: var(--accent-soft); }
@media (max-width: 640px) {
  .chat-composer-toolbar .chat-tool:not(.chat-tool--pill), .chat-composer-toolbar .chat-tool--fmt { width: 44px; height: 44px; }
  /* D1: "targets ≥44px" on mobile — the attach pills and the inline mic are part of
     THIS toolbar now (they weren't a mobile gap this wave introduced; they were
     already 30px before it, but the stacked-card restructure is the natural place to
     close it, scoped to just this composer so no other .chat-tool--pill/.voice-mic
     surface app-wide is affected). */
  .chat-composer-toolbar .chat-tool--pill { height: 44px; }
  .chat-composer-toolbar .voice-mic { width: 44px; height: 44px; }
  .chat-composer-hint { display: none; }   /* a desktop nicety only — the row is tight enough on a phone without it; never load-bearing (Enter/Shift-Enter behavior itself is unaffected) */
  /* CAUGHT LIVE (mobile screenshot pass) — the OLD bare `.chat-send{height:44px}`
     mobile rule (styles.css ~3230) has LOWER specificity than the desktop-only
     `.chat-composer-toolbar .chat-send` rule above, and CSS media queries add
     no specificity of their own — so without this, the 36px desktop value won
     even on a phone (the exact DESIGNA-trap shape this codebase has hit
     before). Placed AFTER the desktop rule in source order, same fix idiom as
     .chat-tool--pill/.voice-mic just above. */
  .chat-composer-toolbar .chat-send { height: 44px; min-height: 44px; }
}
/* quick replies — one-tap starting points ABOVE the card (README §Chat + D1's own
   anatomy line). SPEC-V2POLISH2 D1/D2 2026-08-28 — re-measured against the
   prototype's own quick-reply row (design-handoff/VVT-Hub-Owner.dc.html):
   padding 4px 0 0 (the 22px side inset now comes from .chat-composer itself,
   which also insets the card below it — one shared gutter, not two); chip
   height 30px/pill radius/border --border-strong (#d0d5dd exact)/background
   --panel (#fff exact)/12.5px/600/--muted (#565e6b exact) text; hover border
   --accent (#2b608f exact) + text --accent-hover (#1e3f66 exact) — the same
   border/text fg-split VP5 (V2POLISH1) already measured on the segment tab. */
.chat-quick-replies { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; padding: 4px 0 0; }
.chat-quick-reply {
  height: 30px; display: flex; align-items: center; padding: 0 12px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--panel); font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--muted); cursor: pointer; white-space: nowrap;
}
.chat-quick-reply:hover { border-color: var(--accent); color: var(--accent-hover); }
/* thread pill: hover lift + accent count */
.chat-thread-link { border-radius: 9px; transition: background .12s, border-color .12s; }
.chat-thread-link:hover { border-color: var(--accent); background: var(--accent-soft); }
@media (prefers-reduced-motion: reduce) { .chat-msg-actions, .chat-thread-link, .chat-composer-row { transition: none; } }

/* SPEC336 — on the chat view the FAB cluster lifts ABOVE the composer (floating
   buttons never sit on an input — the SPEC328 rule). The composer has its own
   inline mic, so the voice FAB hides there entirely. */
body[data-view="chat"] .quick-add-fab { bottom: 140px; }
body[data-view="chat"] .call-fab { bottom: 140px; }
body[data-view="chat"] .voice-fab { display: none; }
body[data-view="chat"] .bug-fab { bottom: 206px; }
@media (max-width: 640px) {
  body[data-view="chat"] .quick-add-fab, body[data-view="chat"] .call-fab { bottom: calc(140px + env(safe-area-inset-bottom, 0px)); }
  body[data-view="chat"] .bug-fab { bottom: calc(206px + env(safe-area-inset-bottom, 0px)); }
}

/* SPEC336 reviewer fix — touch devices have no hover: actions stay visible and in-row
   (the floating lift left an INVISIBLE tappable toolbar over the message above) */
@media (max-width: 640px), (hover: none) {
  /* reviewer fixes (SPEC337): the toolbar FLOWS after the message instead of overlaying
     the author/timestamp (6 buttons now), and the composer wraps so the input keeps
     full width instead of collapsing to a 12px sliver next to 5 tool buttons */
  .chat-msg-actions { opacity: 1; transform: none; position: static; justify-content: flex-end; box-shadow: none; border: 0; background: transparent; margin-top: 3px; }
  .chat-msg-act { border: 1px solid var(--border); border-radius: 6px; background: var(--panel); }
  .chat-msg-act + .chat-msg-act { border-left: 1px solid var(--border); margin-left: 2px; }
  .chat-composer-row { flex-wrap: wrap; }
  .chat-composer-row .chat-input { flex: 1 1 100%; order: -1; }
}

/* SPEC337 — quick reactions + prominent threads */
.chat-msg-act--emoji { font-size: 15px; padding: 5px 8px; }
.chat-msg-act--reply { font-weight: var(--fw-bold); font-size: var(--fs-meta); color: var(--accent); white-space: nowrap; }
.chat-msg-act--reply:hover { background: var(--accent-soft); }
.chat-thread-ghost {
  display: none; align-items: center; gap: 5px; margin-top: 4px;
  border: 1px dashed var(--border-strong); border-radius: 9px; background: none;
  padding: 4px 12px; font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--accent); cursor: pointer;
}
.chat-msg:hover .chat-thread-ghost, .chat-msg:focus-within .chat-thread-ghost { display: inline-flex; }   /* keyboard path: focusing the row (tabindex 0) reveals it */
.chat-msg:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
.chat-thread-ghost:hover { background: var(--accent-soft); border-color: var(--accent); border-style: solid; }
@media (max-width: 640px), (hover: none) { .chat-thread-ghost { display: inline-flex; } }
.emoji-pop-edit {
  display: block; width: 100%; margin-top: 4px; padding: 5px 8px; border: 0; border-top: 1px solid var(--border);
  background: none; cursor: pointer; font: inherit; font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--muted); text-align: center;
}
.emoji-pop-edit:hover { color: var(--accent); }

/* SPEC338 — GIF picker */
.chat-tool--gif { font-size: 10.5px; font-weight: var(--fw-black); letter-spacing: var(--ls-label); color: var(--accent); }
.gif-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 6px; max-height: 380px; overflow-y: auto; }
.gif-cell { border: 1px solid var(--border); border-radius: 10px; padding: 0; overflow: hidden; cursor: pointer; background: var(--bg-2); aspect-ratio: 4 / 3; transition: transform .12s, border-color .12s; }
.gif-cell:hover { transform: scale(1.03); border-color: var(--accent); }
.gif-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (prefers-reduced-motion: reduce) { .gif-cell { transition: none; } }

/* SPEC339 — voice messages + screen recordings */
.chat-voice { display: inline-flex; align-items: center; gap: 8px; margin: 5px 0; padding: 6px 12px 6px 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--subtle); }
.chat-voice-ico { font-size: 15px; }
.chat-voice audio { height: 36px; max-width: min(320px, 60vw); }
.chat-screenrec { display: block; margin: 6px 0; max-width: min(420px, 100%); border-radius: 10px; border: 1px solid var(--border); background: #000; }
.chat-rec-pill {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 96px; z-index: 950;
  display: flex; align-items: center; gap: 10px; padding: 10px 14px;
  border: 1px solid var(--border); border-radius: 999px; background: var(--panel);
  box-shadow: 0 10px 30px -6px rgba(16, 24, 40, .35); font-size: var(--fs-caption); font-weight: var(--fw-semibold);
}
.chat-rec-dot { width: 10px; height: 10px; border-radius: 50%; background: #e2445c; animation: recPulse 1.2s ease-in-out infinite; }
@keyframes recPulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .chat-rec-dot { animation: none; } }
.chat-rec-timer { font-variant-numeric: tabular-nums; color: var(--muted); }

/* SPEC340 — ghost row (quick reacts + reply pill), ＋ composer menu, add-cut act */
.chat-ghost-row { display: none; align-items: center; gap: 4px; margin-top: 4px; flex-wrap: wrap; }
.chat-msg:hover .chat-ghost-row, .chat-msg:focus-within .chat-ghost-row { display: inline-flex; }
@media (max-width: 640px), (hover: none) { .chat-ghost-row { display: inline-flex; } }
.chat-ghost-react { border: 1px solid var(--border); border-radius: 999px; background: var(--panel); padding: 3px 9px; font-size: var(--fs-caption); cursor: pointer; line-height: 1; }
.chat-ghost-react:hover { border-color: var(--accent); background: var(--accent-soft); transform: scale(1.1); }

/* CONSOL-5 — reusable anchored popover + the service dropdown + per-service notes thread.
   All tokens (var(--panel/--border/--accent/--hover)) so it inherits the brand restyle. */
.popover { position: fixed; z-index: 3000; background: var(--panel); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 12px 36px -8px rgba(16, 24, 40, .32); padding: 6px; min-width: 210px; max-width: min(340px, 92vw); max-height: 62vh; overflow: auto; }
/* subtle ghost button (no bordered-chip outline — Vincent: "weird outline, looks really bad") */
.svc-menu-trigger { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; max-width: 100%; border: 0; background: transparent; color: var(--text); font: inherit; font-size: 12.5px; font-weight: var(--fw-medium); padding: 3px 7px; border-radius: 8px; white-space: nowrap; }
.svc-menu-trigger:hover, .svc-menu-trigger[aria-expanded="true"] { background: var(--hover); }
.svc-menu-trigger:focus-visible { background: var(--hover); }
/* CONSOL-3 — Big-density expanded services: each service as its own small chip */
.svc-exp { display: flex; flex-wrap: wrap; gap: 5px; }
.svc-exp-chip { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--border); background: var(--panel); border-radius: 999px; padding: 3px 10px; font-size: 12.5px; cursor: pointer; color: var(--text); }
.svc-exp-chip:hover, .svc-exp-chip:focus-visible { border-color: var(--accent); background: var(--accent-soft); }
.svc-exp-n { font-size: var(--fs-micro); color: var(--muted); }
.svc-menu-dots { display: inline-flex; align-items: center; }
.svc-menu-dots .chip-dot { margin-right: -4px; box-shadow: 0 0 0 1.5px var(--panel); }
.svc-menu-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.svc-menu-caret { opacity: .55; font-size: 10px; }
.svc-menu-list { display: flex; flex-direction: column; gap: 2px; }
.svc-menu-row { display: flex; align-items: center; gap: 4px; }
.svc-menu-pick { flex: 1 1 auto; display: flex; align-items: center; gap: 8px; text-align: left; background: transparent; border: 0; border-radius: 8px; padding: 7px 9px; cursor: pointer; font-size: var(--fs-caption); color: var(--text); min-width: 0; }
.svc-menu-pick:hover, .svc-menu-pick:focus-visible { background: var(--hover); }
.svc-menu-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.svc-menu-thread { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 3px; background: transparent; border: 0; border-radius: 8px; padding: 6px 8px; cursor: pointer; color: var(--muted); font-size: var(--fs-meta); }
.svc-menu-thread:hover, .svc-menu-thread:focus-visible { background: var(--hover); color: var(--text); }
.svc-menu-thread.has-notes { color: var(--accent); }
.svc-menu-count { font-weight: var(--fw-bold); }
.svc-thread-list { display: flex; flex-direction: column; gap: 8px; max-height: 46vh; overflow: auto; }
.svc-thread-row { position: relative; border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; }
.svc-thread-meta { display: flex; justify-content: space-between; gap: 8px; font-size: 11.5px; margin-bottom: 3px; }
.svc-thread-text { font-size: var(--fs-caption); line-height: 1.45; white-space: pre-wrap; word-break: break-word; }
.svc-thread-del { position: absolute; top: 4px; right: 4px; }
.chat-tool--plus { font-size: 20px; font-weight: var(--fw-bold); color: var(--accent); }
.chat-plus-menu {
  position: fixed; z-index: 960; min-width: 250px; padding: 6px;
  border: 1px solid var(--border); border-radius: 14px; background: var(--panel);
  box-shadow: 0 14px 40px -8px rgba(16, 24, 40, .35);
}
.chat-plus-item { display: flex; align-items: center; gap: 11px; width: 100%; padding: 9px 10px; border: 0; border-radius: 9px; background: none; cursor: pointer; font: inherit; text-align: left; color: var(--text); }
.chat-plus-item:hover { background: var(--accent-soft); }
.chat-plus-ico { font-size: 17px; width: 24px; text-align: center; flex: 0 0 auto; }
.chat-plus-txt { display: flex; flex-direction: column; min-width: 0; }
.chat-plus-txt > span:first-child { font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.chat-plus-sub { font-size: var(--fs-micro); color: var(--muted); }
.pp-act--add { width: auto; padding: 0 9px; font-size: var(--fs-micro); font-weight: var(--fw-black); color: var(--accent); white-space: nowrap; }

/* SPEC341 — focus layer */
.focus-nudge { display: flex; align-items: center; justify-content: space-between; gap: 10px; border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 10px; background: var(--subtle); padding: 8px 12px; margin-bottom: 12px; }
.focus-bar { height: 10px; border-radius: 999px; background: var(--bg-2); overflow: hidden; }
.focus-bar-fill { height: 100%; border-radius: 999px; background: var(--accent); transition: width .4s; }
.focus-bar-fill.over { background: linear-gradient(90deg, var(--accent), #00c875); }
.focus-stats { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 8px; }
.focus-stat-v { font-size: 20px; font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }
.focus-stat-l { font-size: var(--fs-micro); color: var(--muted); text-transform: uppercase; letter-spacing: var(--ls-label); font-weight: var(--fw-bold); }
.focus-stat-s { font-size: var(--fs-micro); color: var(--faint); }
@media (prefers-reduced-motion: reduce) { .focus-bar-fill { transition: none; } }

/* SPEC342 — My Day timeline */
.myday-tl { position: relative; margin-left: 52px; }
.myday-hour { position: absolute; left: -52px; right: 0; border-top: 1px solid var(--border); font-size: 10px; color: var(--faint); }
.myday-hour span { position: relative; top: -7px; background: var(--panel); padding-right: 6px; }
.myday-busy {
  position: absolute; left: 0; right: 46%; overflow: hidden;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 3px 8px; font-size: var(--fs-micro); color: var(--muted); white-space: nowrap; text-overflow: ellipsis;
}
.myday-block {
  position: absolute; left: 55%; right: 0; overflow: hidden; cursor: pointer;
  background: var(--accent-soft, var(--bg-2)); border: 1px solid var(--accent); border-left-width: 3px; border-radius: 8px;
  padding: 3px 8px; transition: transform .1s;
}
.myday-block:hover { transform: scale(1.01); }
.myday-block.done { opacity: .55; border-color: var(--ok, #00c875); text-decoration: line-through; }
.myday-block.pinned { border-style: solid; box-shadow: 0 2px 8px -2px rgba(16,24,40,.25); }
.myday-block-t { font-size: 11.5px; font-weight: var(--fw-bold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.myday-block-r { font-size: 10px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.myday-now { position: absolute; left: -52px; right: 0; height: 2px; background: #e2445c; z-index: 5; }
.myday-now::before { content: ''; position: absolute; left: 0; top: -3px; width: 8px; height: 8px; border-radius: 50%; background: #e2445c; }
@media (prefers-reduced-motion: reduce) { .myday-block { transition: none; } }

/* SPEC344 — hit/miss + insights */
.myday-block.missed { opacity: .6; border-color: #e2445c; background: color-mix(in srgb, #e2445c 8%, var(--panel)); }
.myday-hitring { font-size: 11.5px; font-weight: var(--fw-bold); color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 3px 11px; }
.myday-hitring.good { color: var(--ok, #00c875); border-color: color-mix(in srgb, #00c875 40%, var(--border)); }
.myday-fun { margin: 8px 0 2px; padding: 8px 12px; border-radius: 10px; background: var(--accent-soft, var(--bg-2)); font-size: var(--fs-caption); }
.myday-tip { display: flex; align-items: flex-start; gap: 9px; padding: 7px 2px; border-bottom: 1px solid var(--border); }
.myday-tip:last-child { border-bottom: 0; }
.myday-tip-ico { font-size: 15px; flex: 0 0 auto; }

/* SPEC346 — pay statement */
.pay-statement { margin: 10px 0 0; padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px; background: var(--subtle); font-size: 12.5px; line-height: 1.6; white-space: pre-wrap; font-variant-numeric: tabular-nums; }

/* SPEC347 — skills & knowledge importer */
.skills-drop { display: block; width: 100%; padding: 22px 14px; margin: 8px 0; border: 2px dashed var(--border-strong); border-radius: 12px; background: var(--subtle); cursor: pointer; font: inherit; font-size: var(--fs-caption); font-weight: var(--fw-semibold); color: var(--muted); text-align: center; transition: border-color .12s, color .12s; }
.skills-drop:hover, .skills-drop.on { border-color: var(--accent); color: var(--accent); }

/* D5 — the ONE insight-card idiom: every "here's what's going on" card (Day-at-a-glance,
   FRIDAY brief, Week-coached, "The bottom line", etc.) shares this accent-border wrapper
   so five renderers stop inventing five looks. Tone defaults to accent; --ok is reserved
   for the daily recap card, --warn/--danger only when the card's lead item warrants it. */
:root { --insight-tone: var(--accent); }
.insight { border-left: 3px solid var(--insight-tone, var(--accent)); }
/* tone modifiers — App.el's style-object merge can't set a custom property reliably
   (no setProperty path), so tone is a class, not an inline style. */
.insight--ok { --insight-tone: var(--ok); }
.insight--warn { --insight-tone: var(--warn); }
.insight--danger { --insight-tone: var(--danger); }

/* FRIDAY-SLIM 2026-08-12 — the opening moment (spec section A/C/D). Class hooks
   only (`fri-*`, the existing .fri-cite precedent) — friday.js applies these,
   no new hexes/fonts/gradients. Tokens only, so dark mode is zero extra work
   (verified once anyway — the champagne hairline swaps under [data-theme="dark"]). */

/* C1 — the hero card: .fri-hero sits alongside `card card-pad insight` (D5's
   idiom, accent left border kept); the champagne top hairline is the one
   "expensive" decorative move the token comment explicitly permits. */
.fri-hero { padding: var(--space-5); border-top: 1px solid var(--accent-warm); }

/* C2 — the headline sentence: Fraunces, the same editorial voice as .view-title. */
.fri-headline { font-family: var(--font-serif); font-size: var(--fs-lg); line-height: var(--lh-snug); font-weight: var(--fw-semibold); letter-spacing: var(--ls-tight); color: var(--text); font-variant-numeric: tabular-nums; }
@media (min-width: 861px) { .fri-headline { font-size: var(--fs-h1); } }

/* C4 — action rows: dot + one-line title + optional trailing meta; whole row
   is the tap target (App.rowActivate wires role=button/keyboard in friday.js). */
.fri-row { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-2) 0; border-top: 1px solid var(--border); min-height: 40px; cursor: pointer; }
.fri-row:hover { background: var(--subtle); }
.fri-row-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.fri-row-title { font-size: var(--fs-caption); font-weight: var(--fw-semibold); flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fri-row-meta { font-size: var(--fs-meta); color: var(--muted); flex: 0 0 auto; font-variant-numeric: tabular-nums; }

/* C5 — disclosure buttons: "N more ▸", "which ones? ▸", the utility link row.
   The ▸/▾ glyph is a literal text swap in friday.js, not a CSS transform —
   cheaper and reduced-motion-proof (pairs with C9). */
.fri-more { background: none; border: 0; padding: var(--space-2) 0; font: inherit; font-size: var(--fs-meta); font-weight: var(--fw-medium); color: var(--muted); cursor: pointer; letter-spacing: var(--ls-label); text-transform: uppercase; }
.fri-more:hover { color: var(--accent); }

/* C6 — composer */
.fri-composer { margin-top: var(--space-4); }
.fri-composer .textarea { font-size: var(--fs-body); box-shadow: var(--shadow-sm); }
.fri-composer .textarea:focus { box-shadow: var(--focus-ring-soft); }

/* C7 — disclosure line, restyled + moved under the composer (full tab only;
   the docked panel keeps its original inline styling, unchanged, in friday.js). */
.fri-disclosure { font-size: var(--fs-micro); color: var(--faint); margin-top: var(--space-2); line-height: 1.4; }

/* C9 — one considered entrance, hero card only, once per arrival (friday.js
   guards the repeat with a 2s App._fridayEnteredAt stamp so App.refresh()'s
   re-render never replays it mid-session). Plain CSS class — never wired
   through startViewTransition (that API defers cross-view renders). */
@keyframes fri-enter { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.fri-enter { animation: fri-enter var(--t-slow) var(--ease-spring) both; }
@media (prefers-reduced-motion: reduce) { .fri-enter { animation: none; } }

/* C8 — thread rail row title (was inline fontSize:'12.5px' in friday.js).
   Active-thread background stays the already-tokened var(--accent-soft)
   inline style in friday.js — no other rail changes. */
.fri-thread-title { font-size: var(--fs-caption); }

/* SPEC348 — Monday coaching digest */
.coach-item { display: flex; align-items: flex-start; gap: 10px; padding: 8px 2px; border-bottom: 1px solid var(--border); }
/* TRUTH-CHECK 2026-08-18 — TAPFLOOR ratchet repair (§I.1 LAW 1, floor 44px at 375×812).
   Home's coach rows are real tap targets and measured 315×41 — three of them were live
   in the ten-screen sweep and pushed the ratchet from 17 to 19 the moment the §A canary
   row ("Only your changes reached the team this week") joined them. A 3px min-height on
   the INTERACTIVE variants only clears all three; the plain <div> rows are untouched. */
button.coach-item, a.coach-item, .coach-item[role="button"] { min-height: 44px; }
.coach-item:last-child { border-bottom: 0; }
.coach-item-ico { font-size: var(--fs-md); flex: 0 0 auto; margin-top: 1px; }

/* RECAP 2026-08-10 — start-of-day / end-of-day recap card (Today, top of view).
   Reuses .coach-item's row layout for each line; conforms to .insight (D5) with tone --ok
   so it stays visually distinguishable from the accent-toned FRIDAY/coach cards. */
.recap-item:hover { background: var(--subtle); border-radius: 6px; }

/* SPEC349 — Setter Desk */
.setter-form .input, .setter-form textarea.input { display: block; width: 100%; box-sizing: border-box; margin-top: 8px; }
.setter-form .select { margin-top: 8px; }
.setter-drop { display: block; width: 100%; padding: 14px; margin: 8px 0 0; border: 2px dashed var(--border-strong); border-radius: 10px; background: var(--subtle); cursor: pointer; font: inherit; font-size: var(--fs-meta); color: var(--muted); text-align: center; transition: border-color .12s, color .12s; }
.setter-drop:hover, .setter-drop.on { border-color: var(--accent); color: var(--accent); }
.setter-variant { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; margin-top: 8px; background: var(--subtle); }
.setter-win { border-bottom: 1px solid var(--border); padding: 8px 2px; }
.setter-temp-hot { background: rgba(220, 80, 60, .14); }
.setter-temp-warm { background: rgba(220, 160, 40, .14); }
.setter-temp-cold { background: rgba(90, 140, 220, .14); }

/* CONSOL — Post & Review merged board: the lens toggle sits above the delegated board.
   Dead since SPEC-V2VIDEO's rebuild (postreview.js no longer renders a mode bar — see
   the .vpr-* block below) but left in place: harmless, and other lens wrappers may
   still reference the shared .lens-modes spacing rule. */
.postreview-modes { margin: 0 0 14px; }

/* CONSOL — generic lens wrapper toggle spacing */
.lens-modes { margin: 0 0 14px; }

/* ============================================================
   SPEC-V2VIDEO 2026-08-28 — "Video, post and review" (postreview.js), fidelity
   rebuild per design-handoff/README.md §4 + VVT-Video-Post.dc.html. Every color
   here is a TOKEN (theme-aware); the five STAGE colors themselves are literal
   hex read live from settings.videoStages (postpipeline.js App.videoStages()),
   composited through App.tintOf()/App.isDarkColor() at render time — never a
   CSS literal — so a team recolour/rename propagates without a CSS change.
   Every clickable control here is either a proven tap-floor class (.btn, .chip,
   .pp-act — already covered by the SPEC202/SPEC-A11Y halo below) or sized to a
   44px minimum directly, per this repo's tap-floor law (LAWA — tests/suite.js). */
.vpr-wrap { display: flex; flex-direction: column; gap: 0; }
.vpr-head { display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; }
.vpr-headline { min-width: 0; }
.vpr-sub { margin: 6px 0 0; max-width: 620px; font-size: var(--fs-meta); line-height: 1.6; color: var(--muted); }
/* SPEC-V2POSTFIT — the title row: h1, the derived "N cuts moving" caption,
   the derived presence chip. */
.vpr-titlerow { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.vpr-countline { font-size: 13px; color: var(--muted); cursor: default; }
.vpr-presence {
  display: inline-flex; align-items: center; gap: 8px; min-height: 28px; padding: 4px 12px;
  border-radius: 999px; background: color-mix(in srgb, var(--accent) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--accent); white-space: nowrap;
}
.vpr-presence-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--ok); flex: none; }
/* SPEC-V2VIDFIT — the design's top-bar red chip ("1 cut past its promise"),
   shown only when something genuinely is (never an empty/zero-count pill —
   RULES §2.6's "a blank is better than a guess" cousin: silence when there's
   nothing to say, not a chip reading "0"). */
.vpr-latebanner {
  display: inline-flex; align-items: center; gap: 7px; min-height: 30px; padding: 0 12px;
  border-radius: 999px; background: color-mix(in srgb, var(--danger) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent);
  font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--danger); white-space: nowrap;
}
.vpr-latebanner-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--danger); flex: none; }
.vpr-filters { display: flex; gap: 7px; flex-wrap: wrap; margin-left: auto; }
.vpr-fchip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 14px;
  border-radius: 999px; border: 1px solid var(--border); background: var(--panel);
  font-size: var(--fs-meta); font-weight: var(--fw-semibold); color: var(--muted); cursor: pointer; white-space: nowrap;
}
.vpr-fchip:hover { border-color: var(--accent); }
.vpr-fchip.on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); color: var(--accent); font-weight: var(--fw-bold); }
.vpr-fchip-n { font-size: var(--fs-micro); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }

.vpr-counters { display: flex; gap: 10px; margin-top: 16px; flex-wrap: wrap; }
.vpr-counter { flex: 1 1 140px; background: var(--panel); border: 1px solid var(--border); border-radius: 11px; padding: 12px 14px; }
.vpr-counter-val { font-size: 22px; font-weight: var(--fw-black); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.vpr-counter-lbl { margin-top: 2px; font-size: var(--fs-meta); color: var(--muted); }

.vpr-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 16px 0 6px; }
.vpr-views { display: flex; gap: 2px; padding: 2px; border: 1px solid var(--border); border-radius: 10px; background: var(--bg-2); }
.vpr-view-btn {
  min-height: 40px; padding: 0 13px; border: none; border-radius: 8px; background: transparent;
  font-family: inherit; font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--muted); cursor: pointer; white-space: nowrap;
}
.vpr-view-btn.on { background: var(--panel); color: var(--text); box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,.1)); }
/* SPEC-V2POSTFIT — the design's own Board/Table/… lens reads as underlined
   tabs on a bare baseline, not a pill toggle. Same buttons, same handlers —
   a visual modifier only, so nothing about the existing view-switch logic
   (or VF-region tests keyed on .vpr-view-btn/.on) changes. */
.vpr-views--lens { gap: 0; padding: 0; border: none; border-radius: 0; background: none; border-bottom: 1px solid var(--border); }
.vpr-views--lens .vpr-view-btn { border-radius: 0; border-bottom: 2px solid transparent; padding: 0 15px 12px; min-height: 36px; }
.vpr-views--lens .vpr-view-btn.on { background: none; box-shadow: none; border-bottom-color: var(--accent); color: var(--text); font-weight: var(--fw-bold); }
.vpr-samejobs { font-size: 12px; color: var(--faint); white-space: nowrap; }
/* SPEC-V2POSTFIT — "Just you": a static personal-scope badge (ground truth —
   the prototype's own span has no onClick), not a filter toggle. */
.vpr-justyou {
  display: inline-flex; align-items: center; min-height: 22px; padding: 3px 9px; border-radius: 999px;
  background: var(--bg-2); font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .02em; color: var(--muted); white-space: nowrap;
}
.vpr-toolbar .btn { margin-left: auto; }

.vpr-content { margin-top: 6px; }

/* ---- Queue view: tinted stage bands, each a group of rows ---- */
.vpr-queue { display: flex; flex-direction: column; gap: 16px; }
.vpr-band-head {
  display: flex; align-items: center; gap: 12px; padding: 9px 16px; border-radius: 9px; border: 1px solid transparent;
}
/* SPEC-V2POSTFIT — NEUTRAL header: the design's subline states colour is
   spent on health only ("Stage is the column, so color stays for health
   only") — the former per-column stage-hue tint is gone from the header
   chrome; the health ring (below) is the only colour left in this row. */
.vpr-band-head--neutral { background: var(--bg-2); border-color: var(--border); }
.vpr-neutral-chip { background: var(--panel); color: var(--muted); border: 1px solid var(--border); }
.vpr-health-ring { flex: none; display: block; }
.vpr-band-step { font-size: var(--fs-micro); font-weight: var(--fw-semibold); opacity: .7; }
/* SPEC-V2VIDFIT — RULES §5.8's right-aligned band caption ("nobody on it yet" /
   "being cut" / ...), sourced from the prototype's own STAGES array (code-owned
   derivation text, not a Stages-editor field — see postreview.js's
   STAGE_NOTE_BY_ID). margin-left:auto pins it to the band's far right edge,
   same convention the row action / filters row already use. */
.vpr-band-note { margin-left: auto; font-size: var(--fs-meta); font-weight: var(--fw-medium); color: var(--muted); white-space: nowrap; }
.vpr-band-body { margin-top: 8px; background: var(--panel); border: 1px solid var(--border); border-radius: 11px; overflow: hidden; }

.vpr-row {
  display: flex; align-items: center; gap: 14px; padding: 10px 14px; min-height: 70px;
  border-bottom: 1px solid var(--border); border-left: 4px solid transparent; cursor: pointer;
}
.vpr-row:last-child { border-bottom: none; }
.vpr-row:hover { background: var(--bg-2); }
.vpr-rowsize-compact { min-height: 48px; padding: 6px 14px; }
.vpr-rowsize-big { min-height: 92px; }
.vpr-rowsize-big .vpr-thumb { width: 124px; height: 70px; }

/* D3 — measured off the running prototype (design-handoff/VVT-Video-Post.dc.html,
   Queue view, 1280x720): the frame reads noticeably bigger than a list icon —
   ~96x54 at normal density, not 80x46. Board cards LEAD with the frame per
   RULES §5.8; this is the queue's equivalent gesture at row scale. */
.vpr-thumb { position: relative; flex: none; width: 96px; height: 54px; border-radius: 6px; overflow: hidden; border: 1px solid var(--border); background: var(--bg-2) center/cover; }
.vpr-thumb-badge { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; background: rgba(16,24,40,.24); color: #fff; }
/* D2 — the "styled empty box" implementation trap №1 names explicitly: never a
   flat grey rect (reads as a broken/loading image), a deliberate diagonal
   hatch so an honestly-empty frame reads as INTENTIONAL. */
.vpr-thumb--empty, .vpr-card-thumb.vpr-thumb--empty {
  background-image: repeating-linear-gradient(135deg, var(--border) 0 1px, transparent 1px 9px) !important;
  background-color: var(--bg-2);
}
/* D3 — measured: the title/address column does NOT grow with available space
   (RULES §5.8: "the title column is fixed width so slack collects at the
   right edge, not between the video and the agent") — flex-grow:0 at a fixed
   basis, not flex:1. The action link's own margin-left:auto (.vpr-row-action)
   is what actually collects the slack. */
.vpr-row-title-wrap { min-width: 0; flex: 0 0 240px; }
.vpr-row-titleline { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.vpr-row-title { font-size: 14.5px; font-weight: var(--fw-bold); letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vpr-row-ver { font-size: var(--fs-micro); font-weight: var(--fw-semibold); color: var(--muted); font-variant-numeric: tabular-nums; }
.vpr-row-flag { padding: 2px 8px; border-radius: 5px; background: color-mix(in srgb, var(--warn) 16%, transparent); border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent); font-size: 10.5px; font-weight: var(--fw-bold); color: var(--warn); white-space: nowrap; }
/* SPEC-V2VIDFIT — the Extra lane folded onto this board as a card tag (RULES
   §5.8 audit: "the prototype shows EXTRA as a card tag, not a separate
   section"). Deliberately NEUTRAL, not a stage/urgency color — Extra carries
   no promise and must never read as an alert. */
.vpr-extra-tag { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 5px; background: var(--bg-2); border: 1px solid var(--border); font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .04em; color: var(--muted); white-space: nowrap; }
.vpr-row-addr { margin-top: 3px; font-size: var(--fs-meta); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* SPEC-WAVE5V 2026-09-02 — the Table lens's per-cut approval-status stepper
   (postreview.js reviewStatusChips, over App.REVIEW_STATUSES). Glance-only —
   see that function's own comment on why it is never clickable. Off-state
   chips stay quiet (bg-2/faint, no color borrowed from the status palette);
   the current step is inline-colored per-status by JS (App.tintOf(s.color)),
   matching how every other status/tone chip on this board already tints. */
.vpr-review-steps { display: flex; align-items: center; gap: 4px; margin-top: 4px; flex-wrap: wrap; }
.vpr-review-step { display: inline-flex; align-items: center; padding: 1px 7px; border-radius: 999px; font-size: 9.5px; font-weight: var(--fw-semibold); letter-spacing: .02em; background: var(--bg-2); color: var(--faint); border: 1px solid transparent; white-space: nowrap; }
.vpr-review-step.on { border-width: 1px; border-style: solid; }

/* SPEC-WAVE5V 2026-09-02 — the "Unlinked cuts" strip (postreview.js
   unlinkedCutsStrip): reviewItems with no jobId, invisible to App.pipeCards'
   job-scoped board otherwise. Reuses .card/.card-pad for the row chrome
   (see the JS) — only the flex wrapper is new here. */
.vpr-unlinked { margin: 2px 0 14px; }
.vpr-unlinked-card:hover { border-color: var(--accent); }

.vpr-field { min-width: 0; flex: 0 1 auto; }
.vpr-field-label { font-size: 10px; font-weight: var(--fw-bold); letter-spacing: .05em; text-transform: uppercase; color: var(--faint); }
.vpr-field-value { margin-top: 1px; font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vpr-field-sub { font-size: var(--fs-micro); color: var(--muted); font-weight: var(--fw-medium); }
/* SPEC-V2POSTFIT — the Table lens's optional "Editor notes" column. */
.vpr-field-note { max-width: 240px; font-weight: var(--fw-medium); white-space: normal; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.vpr-person { display: inline-flex; align-items: center; gap: 6px; }

.vpr-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 5px; background: currentColor; }
.vpr-raws-on { color: var(--ok); }
.vpr-raws-off { color: var(--warn); }

.vpr-due { display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px; font-size: 11px; font-weight: var(--fw-bold); white-space: nowrap; }
/* SPEC-V2VIDFIT — the queue row's second, smaller due line (the actual
   calendar date under the "3d left" / "Due today" pill — RULES §5.8's own
   row list: "days-left ('3d left / Mon Sep 1' ...)"). */
.vpr-due-date { margin-top: 3px; font-size: var(--fs-micro); color: var(--faint); white-space: nowrap; }
.vpr-tone-red { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }
.vpr-tone-amber { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.vpr-tone-green { background: color-mix(in srgb, var(--ok) 14%, transparent); color: var(--ok); }
.vpr-tone-neutral { background: var(--bg-2); color: var(--muted); }
.vpr-row-action { flex: none; margin-left: auto; font-size: 12.5px; font-weight: var(--fw-semibold); color: var(--accent); white-space: nowrap; }

/* ---- Board view: neutral lanes of cards (colour spent on health only) ---- */
.vpr-board { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(256px, 1fr); gap: 14px; overflow-x: auto; align-items: start; padding-bottom: 8px; }
/* WAVE0-A A8 (should-land polish, taken up by Section B in Wave 1) — Post &
   Review's lanes stayed 256px-minimum even as the viewport narrowed, so
   fewer and fewer lanes fit on screen before the board just scrolled wider;
   tighten the floor at two breakpoints so more of the pipeline stays
   visible without a horizontal drag. */
@media (max-width: 1439px) { .vpr-board { grid-auto-columns: minmax(200px, 1fr); } }
@media (max-width: 1179px) { .vpr-board { grid-auto-columns: minmax(160px, 1fr); } }
.vpr-lane { border-radius: 12px; border: 1px solid transparent; overflow: hidden; }
.vpr-lane--neutral { background: transparent; }
.vpr-lane-head { display: flex; align-items: center; gap: 9px; padding: 11px 13px; }
.vpr-lane-head--neutral { background: var(--bg-2); border-bottom: 1px solid var(--border); }
.vpr-lane-body { padding: 10px; display: flex; flex-direction: column; gap: 9px; min-height: 90px; }
.vpr-lane-empty { min-height: 60px; display: flex; align-items: center; justify-content: center; border: 1.5px dashed var(--border); border-radius: 9px; font-size: var(--fs-meta); color: var(--faint); }

/* SPEC-V2POSTFIT — big frame-first card. Border colour is health-only
   (red past promise / amber due today / neutral otherwise — border-color,
   set inline per card); the pre-existing colorBy accent (Personal §6 —
   stage/editor/shooter/urgency) survives as the 3px TOP strip only
   (border-top-color, also inline), so picking "colour by editor" in Fields
   still shows something without re-introducing a second meaning for the
   card's main border. */
.vpr-card { background: var(--panel); border: 1px solid var(--border); border-top: 3px solid transparent; border-radius: 11px; overflow: hidden; cursor: pointer; transition: transform .12s, box-shadow .12s; }
.vpr-card:hover { transform: translateY(-1px); box-shadow: var(--shadow-lg); }
.vpr-card--late { box-shadow: 0 0 0 1px color-mix(in srgb, var(--danger) 18%, transparent); }
.vpr-card-thumb { position: relative; height: 104px; background: var(--bg-2) center/cover; }
/* RULES §3.2's scrim rule, applied here for the same reason it exists on
   client-facing dark surfaces: the v#/timer overlay needs to read over any
   photo, not just a dark one. */
.vpr-card-scrim { position: absolute; inset: auto 0 0 0; height: 52%; background: linear-gradient(to top, rgba(13,13,13,.82), rgba(13,13,13,0)); pointer-events: none; }
.vpr-card-overlay { position: absolute; left: 9px; right: 9px; bottom: 7px; display: flex; align-items: center; gap: 6px; }
.vpr-card-ver { font-size: 11px; font-weight: var(--fw-bold); color: rgba(255,255,255,.92); font-variant-numeric: tabular-nums; letter-spacing: .02em; }
.vpr-thumb-badge--inline { position: static; display: inline-flex; color: rgba(255,255,255,.82); }
.vpr-card-timer {
  display: inline-flex; align-items: center; gap: 4px; padding: 2px 7px; border-radius: 5px;
  background: rgba(18,128,92,.92); font-size: 10.5px; font-weight: var(--fw-bold); color: #fff; font-variant-numeric: tabular-nums; white-space: nowrap;
}
.vpr-card-timer-dot { width: 5px; height: 5px; border-radius: 50%; background: #fff; }
/* EXTRA's own corner placement — top-left, over the frame (was inline next
   to the title). vpr-extra-tag's base look (background/border/colour) is
   unchanged; this only repositions it. */
.vpr-card-extra { position: absolute; top: 8px; left: 9px; }
.vpr-card-firstver {
  position: absolute; top: 8px; right: 9px; width: 19px; height: 19px; display: flex; align-items: center; justify-content: center;
  background: rgba(8,9,11,.66); border: 1px solid rgba(230,210,177,.6); border-radius: 50%; color: #e6d2b1;
}
/* D3 — the amber open-notes flag, on the CARD too (queue already had it via
   .vpr-row-flag). Moved to top-RIGHT (was top-left) so it never collides
   with the EXTRA corner tag, which now owns that corner; stacks under the
   first-version badge when both are present on the same card. */
.vpr-card-flag { position: absolute; top: 7px; right: 8px; padding: 2px 7px; border-radius: 5px; background: #dc6803; color: #fff; font-size: 10px; font-weight: var(--fw-bold); white-space: nowrap; box-shadow: 0 1px 3px rgba(0,0,0,.35); }
.vpr-card-firstver ~ .vpr-card-flag, .vpr-card-flag ~ .vpr-card-firstver { top: 31px; }
.vpr-card-body { padding: 10px 11px 11px; display: flex; flex-direction: column; gap: 6px; }
.vpr-card-titlerow { display: flex; align-items: flex-start; gap: 6px; }
/* SPEC-V2POSTFIT — "address bold · service line" (the design's own card
   hierarchy). .vpr-card-addr/.vpr-card-title keep their PRE-EXISTING
   meaning (several V2VIDEO2 tests select a card by `.vpr-card-addr`'s
   text) — only which one looks bold/primary swaps. */
.vpr-card-addr { flex: 1; font-size: 13.5px; font-weight: var(--fw-bold); line-height: 1.3; letter-spacing: -.01em; }
.vpr-card-prio { flex: none; margin-top: 2px; color: var(--warn); }
.vpr-card-title { font-size: var(--fs-meta); font-weight: 400; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* SPEC-V2POSTFIT — the editorial-note line: the cut's latest real working
   note (latestNoteText), clamped to two lines like the prototype's own. */
.vpr-card-note { font-size: 11.5px; line-height: 1.45; color: var(--muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.vpr-card-foot { display: flex; align-items: center; gap: 7px; margin-top: 3px; }
.vpr-card-avatars { display: inline-flex; }
.vpr-card-av2 { margin-left: -9px; border: 1.5px solid var(--panel); border-radius: 50%; }

/* ---- Display panel (fixed popover; per-user; Stages block = team) ---- */
.vpr-display-backdrop { position: fixed; inset: 0; z-index: 600; background: rgba(16,24,40,.34); }
.vpr-display-panel {
  position: fixed; top: 96px; right: 24px; z-index: 601; width: min(360px, calc(100vw - 32px));
  max-height: calc(100vh - 140px); display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--border); border-radius: 13px;
  box-shadow: 0 24px 56px -16px rgba(16,24,40,.36); overflow: hidden;
}
.vpr-display-head { flex: none; display: flex; align-items: center; gap: 10px; padding: 15px 17px; border-bottom: 1px solid var(--border); }
.vpr-display-title { font-size: 13.5px; font-weight: var(--fw-bold); letter-spacing: -.01em; }
.vpr-display-subtitle { margin-top: 2px; font-size: 11.5px; color: var(--muted); }
.vpr-display-body { flex: 1; min-height: 0; overflow-y: auto; }
.vpr-display-group { padding: 14px 17px 8px; border-bottom: 1px solid var(--border); }
.vpr-display-glabel { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 9px; }
.vpr-display-opts { display: flex; flex-direction: column; gap: 3px; }
.vpr-display-opt {
  display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px; padding: 0 9px;
  border: none; border-radius: 7px; background: transparent; cursor: pointer; text-align: left; font-family: inherit;
}
.vpr-display-opt:hover { background: var(--bg-2); }
.vpr-display-opt.on { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.vpr-display-tick {
  width: 15px; height: 15px; flex: none; border: 1.5px solid var(--border-strong, var(--border)); display: flex; align-items: center; justify-content: center; color: #fff;
}
.vpr-display-opt.on .vpr-display-tick { border-color: var(--accent); background: var(--accent); }
.vpr-display-optlabel { flex: 1; font-size: 13px; font-weight: var(--fw-medium); color: var(--text); }
.vpr-display-opt.on .vpr-display-optlabel { font-weight: var(--fw-bold); color: var(--accent); }
.vpr-display-hint { font-size: 11.5px; color: var(--faint); white-space: nowrap; }
.vpr-display-foot { flex: none; display: flex; align-items: center; gap: 9px; padding: 12px 17px; border-top: 1px solid var(--border); background: var(--bg-2); }
.vpr-display-savedline { flex: 1; font-size: 11.5px; color: var(--muted); }

.vpr-stages-block { padding: 14px 17px 16px; background: var(--bg-2); }
.vpr-stages-head { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
.vpr-team-badge { padding: 1px 7px; border-radius: 4px; background: color-mix(in srgb, var(--warn) 16%, transparent); border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent); font-size: 10px; font-weight: var(--fw-bold); color: var(--warn); }
.vpr-stages-warn { font-size: 11.5px; line-height: 1.45; color: var(--muted); margin-bottom: 10px; }
.vpr-stages-list { display: flex; flex-direction: column; gap: 5px; }
.vpr-stage-row { display: flex; align-items: center; gap: 9px; min-height: 44px; padding: 0 9px 0 7px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); }
/* a 44×44 tap target (house tap-floor law — LAWA, tests/suite.js) painted in
   the stage's own colour — bigger than the prototype's 16px dot by necessity,
   not by choice; a disclosed deviation. */
.vpr-swatch { position: relative; width: 44px; height: 44px; flex: none; border-radius: 8px; border: 1px solid rgba(16,24,40,.15); cursor: pointer; }
.vpr-swatch-input { position: absolute; inset: 0; width: 100%; height: 100%; padding: 0; margin: 0; border: none; opacity: 0; cursor: pointer; }
.vpr-stage-nameinput { flex: 1; min-width: 0; border: none; background: transparent; font: inherit; font-size: 13px; font-weight: var(--fw-semibold); color: var(--text); padding: 8px 0; }
.vpr-stage-nameinput:focus { outline: 1px solid var(--accent); border-radius: 4px; }
.vpr-stage-count { flex: none; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; }

/* ---- Cut view ---- */
.vpr-cut { display: flex; flex-direction: column; min-height: 0; }
.vpr-cut-head { display: flex; align-items: center; gap: 13px; padding: 14px 20px; background: var(--panel); border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.vpr-esc-chip { margin-left: 6px; font-size: 10.5px; font-weight: var(--fw-semibold); color: var(--faint); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; }
.vpr-cut-headline { min-width: 0; }
.vpr-cut-titleline { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.vpr-cut-title { font-size: 16px; font-weight: var(--fw-bold); letter-spacing: -.015em; }
.vpr-cut-sub { margin-top: 3px; font-size: var(--fs-meta); color: var(--muted); }
.vpr-stage-chip { padding: 3px 9px; border-radius: 5px; border: 1px solid; font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .04em; text-transform: uppercase; }
.vpr-cut-actions { display: flex; gap: 7px; flex-wrap: wrap; margin-left: auto; }

.vpr-cut-body { flex: 1; min-height: 0; display: flex; gap: 0; flex-wrap: wrap; }
.vpr-cut-main { flex: 1 1 560px; min-width: 0; padding: 20px 22px 30px; overflow-y: auto; }
.vpr-side { flex: 0 0 340px; max-width: 100%; background: var(--panel); border-left: 1px solid var(--border); padding: 18px 18px 26px; overflow-y: auto; }

.vpr-player { background: #0d0d0d; border-radius: 11px; overflow: hidden; border: 1px solid var(--border); }
.vpr-video { display: block; width: 100%; max-height: 60vh; background: #000; }
/* D4 — the honest "no cut yet" player state (noCutPlayerAndNotes) */
.vpr-player-empty { aspect-ratio: 16/9; display: flex; align-items: center; justify-content: center; }
.vpr-player-empty-box { text-align: center; color: rgba(255,255,255,.6); }
.vpr-player-empty-box svg { opacity: .5; }
.vpr-player-empty-title { margin-top: 10px; font-size: 14px; font-weight: var(--fw-bold); color: rgba(255,255,255,.85); }
.vpr-player-empty-sub { margin-top: 4px; font-size: 12.5px; color: rgba(255,255,255,.5); }
.vpr-transport { display: flex; align-items: center; gap: 12px; padding: 10px 14px; background: #14151a; }
.vpr-play { position: relative; flex: none; width: 34px; height: 34px; border-radius: 50%; border: 1px solid rgba(255,255,255,.4); background: rgba(255,255,255,.08); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; }
.vpr-scrub { position: relative; flex: 1; height: 24px; display: flex; align-items: center; cursor: pointer; }
.vpr-scrub::before { content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 2px; background: rgba(255,255,255,.2); }
.vpr-scrub-fill { position: absolute; left: 0; top: 50%; height: 4px; margin-top: -2px; border-radius: 2px; background: var(--accent); }
.vpr-scrub-pins { position: absolute; inset: 0; }
.vpr-scrub-pin { appearance: none; -webkit-appearance: none; position: absolute; top: 50%; width: 12px; height: 12px; margin: -6px 0 0 -6px; border-radius: 50%; border: 1.5px solid rgba(13,13,13,.75); cursor: pointer; padding: 0; }
.vpr-scrub-pin.client { background: #e6d2b1; }
.vpr-scrub-pin.internal { background: #67b3d5; }
.vpr-tc-wrap { display: flex; align-items: baseline; gap: 4px; color: #fff; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.vpr-tc-total { opacity: .5; }

.vpr-notes-head { display: flex; align-items: center; gap: 10px; margin-top: 20px; flex-wrap: wrap; }
.vpr-notefilters { display: flex; gap: 6px; }
.vpr-notefilter {
  min-height: 32px; padding: 0 11px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel);
  font-size: 12px; font-weight: var(--fw-semibold); color: var(--muted); cursor: pointer;
}
.vpr-notefilter.on { border-color: var(--accent); color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.vpr-notes-list { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.vpr-note { display: flex; gap: 12px; padding: 12px 14px; background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: 10px; }
.vpr-note.client { border-left-color: #b18f52; }
.vpr-note.internal { border-left-color: #2b608f; }
.vpr-note.resolved { opacity: .58; }
.vpr-note-tc { flex: none; width: 52px; font-size: var(--fs-meta); font-weight: var(--fw-bold); color: var(--muted); }
.vpr-note-tcbtn { border: none; background: transparent; color: var(--accent); font: inherit; font-weight: var(--fw-bold); cursor: pointer; padding: 0; }
.vpr-note-body { flex: 1; min-width: 0; }
.vpr-note-headline { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.vpr-note-clientbadge { font-size: 10px; font-weight: var(--fw-bold); color: #7a6234; background: #f0e6d4; border-radius: 5px; padding: 1px 6px; }
.vpr-note-text { margin-top: 4px; font-size: 13.5px; line-height: 1.55; }
.vpr-note-reply { margin-top: 10px; padding-left: 12px; border-left: 2px solid var(--border); }
.vpr-note-replyinput { margin-top: 10px; min-height: 34px; font-size: 13px; }
.vpr-note-actions { flex: none; display: flex; flex-direction: column; gap: 6px; align-items: flex-end; }
.vpr-compose { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.vpr-compose-input { width: 100%; box-sizing: border-box; }

.vpr-side-people { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
.vpr-side-person { padding: 10px 12px; background: var(--bg-2); border: 1px solid var(--border); border-left: 3px solid var(--border); border-radius: 9px; }
.vpr-side-person-role { font-size: 9.5px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.vpr-side-person-name { display: flex; align-items: center; gap: 7px; margin-top: 6px; font-size: 12.5px; font-weight: var(--fw-semibold); }
.vpr-side-raws { display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 12px 14px; background: color-mix(in srgb, var(--accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent); border-radius: 11px; text-decoration: none; color: inherit; }
.vpr-side-raws--empty { color: var(--faint); font-size: var(--fs-meta); font-style: italic; }
.vpr-side-raws-title { font-size: 13.5px; font-weight: var(--fw-bold); color: var(--accent); }
.vpr-side-raws-meta { margin-top: 2px; font-size: 11.5px; color: var(--muted); }
.vpr-side-prefs { margin-top: 16px; background: #f0e6d4; border: 1px solid rgba(177,143,82,.35); border-radius: 11px; padding: 14px 16px; }
.vpr-side-prefs-label { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: #7a6234; margin-bottom: 8px; }
.vpr-side-prefs-text { font-size: 13px; line-height: 1.55; color: #5f4d2c; }
.vpr-side-shooter { margin-top: 16px; background: var(--bg-2); border: 1px solid var(--border); border-radius: 11px; padding: 14px 16px; }
.vpr-side-shooter-label { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 8px; }
.vpr-side-shooter-text { font-size: 13.5px; line-height: 1.6; }
.vpr-side-versions-label { margin-top: 16px; font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.vpr-side-versions { margin-top: 8px; display: flex; flex-direction: column; gap: 7px; }
.vpr-side-version { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 8px 11px; background: var(--bg-2); border: 1px solid var(--border); border-radius: 9px; cursor: pointer; }
.vpr-side-version.on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.vpr-side-version-label { font-size: 12.5px; font-weight: var(--fw-bold); }
.vpr-side-version-meta { flex: 1; font-size: 11px; color: var(--faint); }
.vpr-side-version-state { font-size: 11px; font-weight: var(--fw-semibold); color: var(--muted); }
.vpr-side-hours { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }
.vpr-side-hours-label { font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.vpr-side-hours-value { margin-top: 6px; font-size: 24px; font-weight: var(--fw-bold); letter-spacing: -.02em; font-variant-numeric: tabular-nums; }

@media (max-width: 860px) {
  .vpr-cut-body { flex-direction: column; }
  .vpr-side { flex: none; width: 100%; border-left: none; border-top: 1px solid var(--border); }
  .vpr-board { grid-auto-columns: minmax(180px, 82vw); }
}
@media (max-width: 640px) {
  .vpr-row { flex-wrap: wrap; }
  .vpr-field { flex: 1 1 44%; }
}

/* ============================================================
   golive#13 — mobile bottom tab bar. A one-thumb accelerator over the
   SPEC86 drawer (☰), which stays fully intact — the bar's 5th "More"
   cell just toggles body.nav-open, exactly like the ☰ burger already
   does (index.html). Built + role-filtered in core.js (buildTabbar,
   next to buildQuickAddFab); every id it renders already passed the
   live App.canViewId gate, so this block is pure presentation.
   Mobile-only: #tabbar is display:none outside the ≤860px query (the
   SAME breakpoint the drawer itself switches on, styles.css:757), so
   desktop has zero layout change. Self-contained — delete this block
   + the buildTabbar()/renderTabbarActive() calls in core.js to revert.
   ============================================================ */
#tabbar { display: none; }
@media (max-width: 860px) {
  #tabbar {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 58;
    background: var(--sidebar-bg); border-top: 1px solid var(--sidebar-border);
    padding-bottom: env(safe-area-inset-bottom, 0px);
    box-shadow: 0 -4px 18px rgba(0,0,0,.22);
  }
  #tabbar .tb-row { display: grid; grid-template-columns: repeat(5, 1fr); }
  #tabbar .tb-item {
    position: relative; box-sizing: border-box; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: 3px; min-height: 56px;
    padding: 6px 2px; border: 0; background: none; cursor: pointer; font: inherit;
    color: var(--sidebar-text); font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .01em;
    -webkit-tap-highlight-color: transparent;
  }
  #tabbar .tb-ico svg { width: 21px; height: 21px; }
  #tabbar .tb-lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
  #tabbar .tb-item:active { background: rgba(255,255,255,.05); }
  #tabbar .tb-item.active { color: var(--sidebar-active); }
  #tabbar .tb-item.active::before {
    content: ''; position: absolute; top: 0; left: 22%; right: 22%; height: 3px;
    border-radius: 0 0 4px 4px; background: var(--accent);
  }

  /* clear the fixed bar: the existing bottom padding PLUS its own content height
     (56px item + 1px border ≈ 57px; 62px leaves a hair of breathing room). Safe-area
     is already handled — the bar pads itself, so content only needs the flat height. */
  #view { padding-bottom: calc(50px + 62px); }

  /* the FAB cluster (quick-add ＋ / voice mic / bug report / FRIDAY Listens 📞 call-fab
     — SPEC303/SPEC310/SPEC328) sits bottom-right and would now sit UNDER the bar —
     lift every one of them by its height. These mirror the EXISTING base selectors
     above (same specificity); being appended after them in the file, they win the
     cascade at this breakpoint without touching the desktop-only declarations. */
  .quick-add-fab, .call-fab { bottom: calc(22px + 62px); }
  .voice-fab { bottom: calc(22px + 62px); }
  .voice-fab.stacked { bottom: calc(86px + 62px); }
  .bug-fab { bottom: calc(150px + 62px); }
  body[data-view="chat"] .quick-add-fab, body[data-view="chat"] .call-fab { bottom: calc(140px + 62px); }
  body[data-view="chat"] .bug-fab { bottom: calc(206px + 62px); }
}
@media (max-width: 640px) {
  /* D7 — one FAB to clear, not the old 5-deep stack: 24px gap + safe-area + tab-bar
     clearance (62px) + the dial button's own footprint (72px), not 96px sized for a
     212px-tall cluster. */
  #view { padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px) + 62px + 72px); }
  .quick-add-fab, .call-fab { bottom: calc(16px + env(safe-area-inset-bottom, 0px) + 62px); }
  .voice-fab { bottom: calc(16px + env(safe-area-inset-bottom, 0px) + 62px); }
  .voice-fab.stacked { bottom: calc(80px + env(safe-area-inset-bottom, 0px) + 62px); }
  .bug-fab { bottom: calc(150px + env(safe-area-inset-bottom, 0px) + 62px); }
  body[data-view="chat"] .quick-add-fab, body[data-view="chat"] .call-fab { bottom: calc(140px + env(safe-area-inset-bottom, 0px) + 62px); }
  body[data-view="chat"] .bug-fab { bottom: calc(206px + env(safe-area-inset-bottom, 0px) + 62px); }
}
@media (prefers-reduced-motion: reduce) { #tabbar .tb-item { transition: none; } }
/* golive#13 — the on-screen keyboard toggle (index.html, visualViewport listener next
   to the SPEC86 drawer wiring) hides the bar entirely while typing, so it never sits
   above a focused input. #view/FAB offsets are left as-is: the keyboard already pushes
   everything above it, so there's nothing left at the old tab-bar height to clear. */
body.kb-open #tabbar { display: none; }


/* ==========================================================================
   ==  2026-07-30 SURFACE VISUAL PASS — five builder blocks, merged in one    ==
   ==  integrator pass. Appended at the END on purpose: several blocks are    ==
   ==  deliberate same-specificity overrides of earlier rules and rely on     ==
   ==  source order. Token-only, no !important, no new custom properties.     ==
   ========================================================================== */


/* ##########################################################################
   CHAT — visual pass (chat.js). scratchpad/css-chat.css
   Merged by the INTEGRATOR pass, 2026-07-30. Source: builder scratch file.
   ########################################################################## */
/* ============================================================
   CHAT — visual pass, 2026-07-30
   Owner: chat surface builder. Appends verbatim to styles.css.

   NO new CSS variables. Every value below is an existing design token, so both
   themes come for free (--ok, --panel, --bg, --hover, --accent, --faint and the
   --fs- / --space- / --fw- scales all flip under html[data-theme="dark"]).
   NO raw hex. NO !important. Every selector is scoped to a .chat-* class.

   Three feature blocks (presence dots / read receipts / pinned bar) then the
   depth pass. Sizes quoted in comments are MEASURED off the live DOM before
   this file, not read off the source.
   ============================================================ */

/* ============================================================
   A · PRESENCE DOTS  (Discord/Whop: the dot is CUT OUT of the avatar)
   The ring is drawn in the surface colour behind the avatar so the dot never
   muddies the avatar hue. Only two states exist — filled = active (<=10 min),
   ring = recent (<=2 h). There is deliberately NO "offline" dot: chat.js
   renders no node at all when the app has no evidence for a person.
   Semantic --ok, kept off --accent per the standing rule.
   ============================================================ */
/* align-self is load-bearing: .chat-msg is a flex row with the default
   align-items:stretch, so without it the wrapper grows to the FULL row height and
   the bottom-right dot floats away from the avatar (measured: dot at y=897 on a
   34px avatar sitting at y=744). */
.chat-avwrap { position: relative; display: inline-flex; flex: 0 0 auto; align-self: flex-start; }
.chat-presence {
  position: absolute; right: -1px; bottom: -1px;
  width: 10px; height: 10px; border-radius: 50%;
  box-shadow: 0 0 0 2px var(--bg);          /* the cut-out ring — chat sits on --bg, not --panel */
  pointer-events: none;
}
.chat-presence--active { background: var(--ok); }
.chat-presence--recent { background: var(--bg); box-shadow: 0 0 0 2px var(--bg), inset 0 0 0 2px var(--ok); }
/* the ring tracks the surface actually behind it in the three states that exist */
.chat-msg:hover .chat-presence { box-shadow: 0 0 0 2px var(--subtle); }
.chat-msg:hover .chat-presence--recent { box-shadow: 0 0 0 2px var(--subtle), inset 0 0 0 2px var(--ok); }
.chat-ch:hover .chat-presence { box-shadow: 0 0 0 2px var(--hover); }
.chat-ch:hover .chat-presence--recent { box-shadow: 0 0 0 2px var(--hover), inset 0 0 0 2px var(--ok); }
.chat-ch.active .chat-presence { box-shadow: 0 0 0 2px var(--accent-soft); }
.chat-ch.active .chat-presence--recent { box-shadow: 0 0 0 2px var(--accent-soft), inset 0 0 0 2px var(--ok); }
/* rail avatars are 22px, so the dot steps down to keep Whop's ~1:2.5 proportion */
.chat-ch .chat-presence { width: 8px; height: 8px; }

/* ============================================================
   B · READ RECEIPT. Whop / Instagram / Basecamp all converge on the same
   treatment — faint microtype under the last message, no chip, no icon, no
   avatar stack — and this takes that. It does NOT take their right-alignment:
   MEASURED, a right-aligned receipt landed at x 544-1106 / y 800-816 and the
   app's own quick-add FAB occupies x 1074-1126 / y 784-836, so the count sat
   underneath a floating button. Aligning it to the 52px message column (the
   avatar gutter) puts it directly under the text it annotates, clears every
   FAB at every width, and is what Basecamp's left-aligned "Delivered" does.
   ============================================================ */
.chat-receipt {
  margin: var(--space-1) 0 var(--space-2) 52px;
  width: max-content; max-width: 100%;   /* hug the text so the title tooltip has no dead full-width hit area */
  font-size: var(--fs-micro); font-weight: var(--fw-medium); color: var(--faint);
}

/* ============================================================
   C · PINNED MESSAGE BAR  (Whop / Campsite: one un-carded line separated from
   the feed by a hairline, NOT a floating card. It is a flex sibling of the
   scroller rather than sticky INSIDE it, which makes it permanently visible
   instead of merely sticky — same intent, less coupling to renderList.)
   ============================================================ */
.chat-pinbar {
  display: flex; align-items: center; gap: var(--space-2); flex: 0 0 auto;
  padding: var(--space-2) var(--space-3);
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  box-shadow: inset 3px 0 0 var(--accent);
  font-size: var(--fs-meta);
}
.chat-pinbar-label {
  flex: 0 0 auto; color: var(--accent);
  font-size: var(--fs-micro); font-weight: var(--fw-black);
  letter-spacing: var(--ls-label); text-transform: uppercase;
}
/* the 1/N cycler — a real button, but styled as the label it replaces */
.chat-pinbar-cycle { border: 0; background: none; padding: 0; margin: 0; cursor: pointer; font-family: inherit; }
.chat-pinbar-cycle:hover { color: var(--accent-hover); text-decoration: underline; }
.chat-pinbar-cycle:focus-visible { outline: 2px solid var(--accent); outline-offset: var(--focus-offset); border-radius: var(--radius-sm); }
.chat-pinbar-hit {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: baseline; gap: var(--space-2);
  cursor: pointer; overflow: hidden;
}
.chat-pinbar-text {
  flex: 0 1 auto; min-width: 0; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chat-pinbar-who { flex: 0 0 auto; color: var(--muted); white-space: nowrap; }
.chat-pinbar-hit:hover .chat-pinbar-text { color: var(--accent); }
.chat-pinbar-hit:focus-visible { outline: 2px solid var(--accent); outline-offset: var(--focus-offset); border-radius: var(--radius-sm); }
.chat-pinbar-x {
  flex: 0 0 auto; border: 0; background: none; cursor: pointer;
  color: var(--muted); font-size: var(--fs-caption); line-height: 1;
  padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm);
}
.chat-pinbar-x:hover { background: var(--hover); color: var(--danger); }
.chat-pinbar-x:focus-visible { outline: 2px solid var(--accent); outline-offset: var(--focus-offset); }

/* ============================================================
   D1 · COLLAPSE THE TYPE SCALE.
   Measured inside .chat-layout before this file: 15 distinct font sizes, five of
   them (13.5 / 12.5 / 11.5 / 10.5 / 10px) off the token scale entirely — hand-typed
   one-offs. Message body text was 13.5px/19.575px, BELOW the app's own --fs-body,
   on the one surface where people read paragraphs.
   Everything below moves onto --fs-micro 11 / --fs-meta 12 / --fs-caption 13 /
   --fs-body 14, so the hierarchy reads as steps instead of a half-pixel smear.
   (Emoji GLYPH sizes — .chat-tool 17px, .chat-tool--plus 20px, .chat-voice-ico
   15px — are deliberately left alone: they size a picture, not text.)
   ============================================================ */
/* SPEC-DHCHAT D2 2026-08-27 — README's internal Body spec is literally "Inter 400,
   13.5–14px, 1.6"; the D1 pass above already landed the 14px (--fs-body), only the
   line-height (1.5, --lh-normal) was short of the mockup's 1.6 — no token for 1.6
   exists app-wide, so this uses the same bare literal every other 1.6-line-height
   rule in styles.css already does (.mkit-ta, .order-benefit-why, etc.). */
.chat-text { font-size: var(--fs-body); line-height: 1.6; }
.chat-author { font-size: var(--fs-body); }
.chat-ch { font-size: var(--fs-caption); }
/* SPEC-DHCHAT D1 2026-08-27 — README's Eyebrow spec (internal): "Inter 600–700,
   11–11.5px, .06em, uppercase" — the app's own established eyebrow convention
   (.legend-h, .cscript-label, .order-lb-fig figcaption, .guide-count, ~10 sites)
   already uses this exact font-weight/letter-spacing/color combo; this channel-
   group label gets the same treatment instead of its own one-off .04em/faint. */
.chat-rail-head { font-size: var(--fs-micro); font-weight: var(--fw-bold); letter-spacing: .06em; color: var(--muted); }
.chat-ch-badge { font-size: var(--fs-micro); }
.chat-status { font-size: var(--fs-micro); }
.chat-gutter-time { font-size: var(--fs-micro); }
.chat-jobcard-sub { font-size: var(--fs-micro); }
.chat-file { font-size: var(--fs-meta); }
.chat-thread-link { font-size: var(--fs-meta); }
.chat-bot-sum { font-size: var(--fs-meta); }
.chat-thread-title strong { font-size: var(--fs-md); }
.chat-new-divider span { font-size: var(--fs-micro); }
.chat-thread-avs .chat-av { font-size: var(--fs-micro); }   /* thread-pill avatars rendered their initials at 9px */
.emoji-pop-edit { font-size: var(--fs-micro); }

/* ============================================================
   D2 · MESSAGE GROUPING IS ALIVE AGAIN.
   chat.js now emits the `grouped` class it has always computed, so this rule —
   which used to match EVERY row — finally distinguishes a new speaker from a
   same-speaker continuation. Measured before: inter-row gaps were a uniform
   [4,4,4,51,4,4,4,4,4,4,4,4,4]; the only air in the feed came from day dividers.
   ============================================================ */
.chat-msg:not(.grouped) { margin-top: var(--space-3); }
.chat-msg.grouped { margin-top: 0; }
.chat-list > .chat-msg:first-child, .chat-thread-list > .chat-msg:first-child { margin-top: 0; }

/* ============================================================
   D3 · THE ROLE PILL BECOMES MICROTYPE.
   Measured: 11 filled grey pills marching down a 14-row feed, competing with body
   text that was smaller than they were. Same words, none of the noise.
   ============================================================ */
.chat-role {
  background: none; padding: 0; border-radius: 0;
  color: var(--faint); font-size: var(--fs-micro); font-weight: var(--fw-medium);
}

/* ============================================================
   D4 · THE CONSOL-1 ROW BECOMES ONE REAL FLOATING ACTION BAR.
   Vincent's 2026-07-11 call stands — reactions + reply live in ONE
   hover/focus-revealed row UNDER the message and never also in a corner toolbar.
   This does not move it; it makes it look like one object instead of five loose
   pills (Whop/Basecamp: one container, one elevation, borderless children).
   Buttons go from a measured 36x21 to 30x28 minimum.
   ============================================================ */
.chat-ghost-row {
  gap: 0; margin-top: var(--space-2); padding: 2px;
  flex-wrap: nowrap; width: max-content; max-width: 100%;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--panel); box-shadow: var(--shadow-sm);
}
.chat-ghost-react {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-1);
  border: 0; border-color: transparent; background: none; border-radius: 999px;
  min-width: 30px; height: 28px; padding: 0 var(--space-2);
  font-size: var(--fs-caption); line-height: 1; color: var(--text);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
/* scale(1.1) inside a bordered bar reads as a glitch — the bar is the frame now */
.chat-ghost-react:hover { background: var(--hover); border-color: transparent; transform: none; }
.chat-ghost-react:focus-visible { background: var(--hover); box-shadow: var(--focus-ring); outline: none; }
/* labelled actions sit after a hairline, borderless, same height as the emoji */
.chat-ghost-row .chat-thread-ghost, .chat-ghost-row .chat-ghost-pin {
  border: 0; border-left: 1px solid var(--border); border-radius: 999px;
  height: 28px; margin-top: 0; padding: 0 var(--space-3);
  background: none; font-size: var(--fs-meta); font-weight: var(--fw-semibold);
}
.chat-ghost-row .chat-ghost-pin { color: var(--muted); }
.chat-ghost-row .chat-ghost-pin.on { color: var(--accent); }
.chat-ghost-row .chat-ghost-pin:hover { background: var(--accent-soft); color: var(--accent); }
.chat-ghost-row .chat-thread-ghost:hover { background: var(--accent-soft); border-color: var(--border); border-style: solid; }
@media (prefers-reduced-motion: reduce) { .chat-ghost-react { transition: none; } }

/* ============================================================
   D5 · A COMMITTED REACTION STOPS LOOKING LIKE AN OFFERED ONE (Twist).
   Measured before: .chat-react (46x25, 999px, --panel fill, --border stroke) and
   .chat-ghost-react (36x21, 999px, --panel fill, --border stroke) were the same
   object stacked 4px apart. Now: committed = FILLED, no outline; offered =
   borderless glyph inside an outlined bar. .chat-react.mine keeps its accent
   treatment (its 2-class rule already outranks this one).
   ============================================================ */
.chat-react {
  background: var(--subtle); border-color: transparent;
  font-size: var(--fs-meta); font-variant-numeric: tabular-nums;
  padding: 2px 10px; line-height: 20px;
}
.chat-react:hover { background: var(--hover); }
.chat-react:focus-visible { outline: 2px solid var(--accent); outline-offset: var(--focus-offset); }

/* ============================================================
   D7 · THE MOBILE LAYOUT CORRECTION. One declaration, measured.
   Before, at 375x812: .chat-main was 1847px tall inside a 662px .chat-layout —
   the classic flex min-height:auto floor — so .chat-list never became a scroller
   (clientHeight === scrollHeight), the composer sat at y=1855 (far below an 812px
   viewport, behind the tab bar) and the rail/header scrolled out of reach.
   Everything else on mobile is decoration on a broken box without this.
   ============================================================ */
.chat-main, .chat-list, .chat-thread-list { min-height: 0; }

/* ============================================================
   D9 · RAIL SECTION LABELS SURVIVE A LONG ROSTER.
   ============================================================ */
.chat-rail-head { position: sticky; top: 0; z-index: 1; background: var(--bg); }

/* D8 · MOBILE HEAD BUDGET. Measured before at 375px: rail 43 + head 93 (the channel
   name wrapped to two lines beside "Kudos: on" / "Members" / "Local only") + composer
   111 = 247px of 812 = 30% of the viewport consumed before one message renders.
   The channel name gets its own full-width line and can never be squeezed out — an
   earlier attempt at squeezing all four onto one line measured the title at 0px wide,
   which is worse than the wrap it replaced. The control group keeps every control and
   scrolls sideways inside itself if a future channel adds another one. */
@media (max-width: 760px) {
  .chat-head { flex-wrap: wrap; row-gap: var(--space-1); padding-bottom: var(--space-2); }
  .chat-head-title {
    flex: 1 1 100%; min-width: 0; font-size: var(--fs-body);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .chat-head > .row-line {
    flex: 1 1 100%; min-width: 0; justify-content: flex-start;
    overflow-x: auto; scrollbar-width: none;
  }
  .chat-head > .row-line::-webkit-scrollbar { display: none; }
}

/* ============================================================
   D6 · MOBILE: ONE REACT BUTTON INSTEAD OF FIVE.
   Measured before at 375px: the always-on quick-react bar rendered on 14 of 14
   rows — 463px of ghost chrome inside a 1639px feed, 28.2% of everything you
   scroll — and every button was 36x21, under half the 44px touch minimum.
   NOTHING becomes unreachable: .chat-ghost-more still opens the full picker
   (chat.js emojiPopover), and App.quickReacts / the editor are untouched, so the
   quick reacts move exactly one tap away. Pin is exempt from the hide so a
   channel manager can still pin from a phone.
   ============================================================ */
@media (max-width: 640px), (hover: none) {
  .chat-ghost-react:not(.chat-ghost-more):not(.chat-ghost-pin) { display: none; }
  .chat-ghost-row .chat-ghost-more,
  .chat-ghost-row .chat-ghost-pin,
  .chat-ghost-row .chat-thread-ghost { min-width: 44px; height: 44px; }
}
/* MEASURED HONESTLY: this cuts the BUTTON COUNT on touch from 5 to 1 and lifts every
   target from 36x21 to 44x44, but because 44px is taller than the 21px it replaces,
   the bar's SHARE of the scrollable feed went 28.2% -> 33.9%, not down. The 44px
   minimum is not negotiable, so the only way to actually shrink that share is to stop
   rendering the bar on every row and reveal it on the tapped message instead
   (.chat-msg already carries tabindex=0, so :focus-within would do it with no JS).
   That turns SPEC337's one-tap react into two taps, which is Vincent's call, not mine
   — so it is NOT done here. */


/* ##########################################################################
   JOB TRACKER — table + kanban (tracker.js). scratchpad/css-tracker.css
   Merged by the INTEGRATOR pass, 2026-07-30. Source: builder scratch file.
   ########################################################################## */
/* ============================================================================
   JOB TRACKER (tracker.js) — table + kanban.  07-30.
   Pairs with the tracker.js pass of the same date. Everything here is token-only
   (no raw hex, no new custom properties) and themes itself through the existing
   light/dark token pairs.

   SCOPING — three hooks added by tracker.js in this same pass, because every
   structural class this surface uses is SHARED:
     .jt-view        the Job Tracker's view wrapper   (.toolbar / .view-header are app-wide)
     .jt-table-wrap  its table scrollport             (.table-wrap is used by ~30 panels)
     .jt-board       its kanban board                 (.kanban is also Leads / Tasks /
                                                       Marketing / Pipelines / Video)
   Nothing below changes another surface EXCEPT the two blocks explicitly marked
   "INTENTIONALLY GLOBAL" (§6 .cell-select, §7 urgency ink) — see the notes there.
   Verified live: the Leads, Tasks and Pipelines boards still compute
   max-height:none, position:static heads and the original --bg-2 column fill.

   NO new CSS variables. NO !important.
   ============================================================================ */


/* ─────────────────────────────────────────────────────────────────────────────
   §1  TABLE — a real scrollport, so the header can actually stick.
   ─────────────────────────────────────────────────────────────────────────────
   MEASURED DEFECT: the table is 1713px tall in a 900px viewport and `thead`
   computes `position: static`, so scrolled to the bottom the header row sits at
   top:-5630px on a full board. The status is written in words ONLY in the group
   header, and that scrolls away too — past the fold the table is anonymous.

   Sticky against the PAGE does not work here: `.table-wrap` already declares
   `overflow-x: auto`, which forces overflow-y off `visible` (computed: auto), so
   the wrap — not the page — is the nearest scrollport. Verified live: a sticky
   `th` measured against #main ended at top:-882px. The fix must therefore give
   the wrap a real height to scroll WITHIN. */
.jt-table-wrap {
  overflow: auto;
  /* the floor keeps the table usable if the toolbar ever wraps to four rows;
     320px of chrome is the measured view-header + toolbar stack (327px). */
  max-height: max(340px, calc(100vh - 320px));
  overscroll-behavior: contain;   /* don't chain the page scroll when the table bottoms out */
}
/* sticky rows must be OPAQUE. --subtle is the header fill the table already uses.
   `border-bottom` is unreliable on a sticky cell in a border-collapse table
   (Chrome drops it), so the hairline is redrawn as an inset shadow. */
.jt-table-wrap table.table thead th {
  position: sticky;
  top: 0;
  z-index: 4;
  background: var(--subtle);
  box-shadow: inset 0 -1px 0 var(--border);
}
/* The "＋ add custom column" button is what actually sets the header height (a
   32px .btn-sm inside a 12px cell). Shrinking it makes the header a predictable
   36px, which is what the group-head offset below is pinned to. */
.jt-table-wrap table.table thead th .btn {
  padding: 1px 7px;
  font-size: var(--fs-micro);
  line-height: var(--lh-snug);
}
/* Group headers stack UNDER the column header — the status name in words stays on
   screen for the whole group instead of scrolling out of the board.

   The `top` offsets are the MEASURED header heights per density preset (25.3 /
   33.3 / 49.4px, live in this browser), each rounded DOWN. Rounding down matters:
   a value below the real header height makes the group head slide a hair BEHIND
   the header (z-index 3 < 4) — invisible. A value above it would open a gap that
   table rows scroll through. */
.jt-table-wrap table.table tr.group-head > td {
  position: sticky;
  top: 32px;
  z-index: 3;
  font-size: var(--fs-meta);
}
.jt-table-wrap.density-compact table.table tr.group-head > td { top: 24px; }
.jt-table-wrap.density-big table.table tr.group-head > td { top: 48px; }


/* ─────────────────────────────────────────────────────────────────────────────
   §2  TABLE DENSITY — 136px average row → ~46px.
   ─────────────────────────────────────────────────────────────────────────────
   MEASURED DEFECT: average row 136px (min 94, max 189) = 6.6 rows per 900px
   viewport on a 45-row board; Linear/Asana fit 20–25. Two drivers: a 3-block
   vertical stack in the Address cell, and 12px vertical cell padding.

   Scoped to `.density-comfortable` ONLY. The Compact and Big presets are real
   features with their own (0,2,2) rules in styles.css; a bare `.jt-table-wrap
   table.table td` would tie on specificity, land later in the file, and silently
   overwrite BOTH presets. */
.jt-table-wrap.density-comfortable table.table td { padding: 8px 12px; }
.jt-table-wrap.density-comfortable table.table th { padding: 7px 12px; }

/* The address column needs room for one line of a real LA address (it measured
   190px, which wrapped every address to three lines) — but it must NOT be allowed
   to take as much as it likes. MEASURED: uncapped, `white-space: nowrap` on four
   inline items pushed the column from 172px to 570px and the whole table from
   1471px to 1786px, dropping fully-visible columns from 8 to 5. The shrink
   priorities below decide who gives way: the address never drops below 90px; the
   tags — a label, recoverable on hover or in the modal — yield first.
   ── The two declarations that used to live here were DELETED 2026-09-08 ──
   SLEEK 08-12 (A5) set the cap at 300px (on `.jt-addr`, below) and a 220px floor
   on `th.jt-col-address` (§FROZEN IDENTITY, here). SPEC-TRACKFIT 2026-09-03
   superseded BOTH from ~600 lines further down with identical selectors at the same
   specificity — 200px, and `min-width: 0` — so neither had reached the screen since.
   MEASURED 2026-09-08 at 1280 and 1700: removing them changes nothing rendered. The
   live numbers are in the SPEC-TRACKFIT block; that is the only place to edit them.
   The class hook (SPEC-V2TRACKFIT Q1 2026-08-29, was `:nth-child(2)`) is still how
   every surviving address-width rule selects: the checkbox column is optional, so
   address sits at position 2 with it off and 3 with it on, and a fixed nth-child
   can only ever be right for one of those. */

/* SPEC-V2TRACKFIT Q2 2026-08-29 (owner's morning ruling: design wins) — the
   small thumbnail box the prototype's address cell carries (VVT-Hub-Owner
   .dc.html ~line 307: 30×20, rounded, cover-fit). tracker.js's buildRow now
   renders the .jt-addr-cell as TWO children (this thumb, then the existing
   .jt-addr text/flags flex) — the cell itself needs to lay them out
   side by side. Honest empty state: no job in this data model carries a real
   thumbnail field (see buildRow's own doc comment), so the dashed box is what
   renders for every job today; a real photo drops in the moment one exists. */
.jt-table-wrap .jt-addr-cell { display: flex; align-items: center; gap: var(--space-2); }
/* FABLE-FIX 2026-08-30 (Vincent's bug: "why is this entire row so massive") —
   the plain-text SERVICES cell (FABLE-RULING 08-29) shipped without a wrap
   clamp, so a 7-service job wrapped it 6+ lines and inflated the whole row to
   173px (reproduced at his exact viewport). Design law: rows are ONE line;
   the full list already lives on the cell's hover title. */
.jt-table-wrap td.jt-services-text { max-width: 300px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.jt-addr-thumb {
  flex: none; width: 30px; height: 20px; border-radius: 4px; overflow: hidden;
  border: 1px solid var(--border); background: var(--bg-2);
}
.jt-addr-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.jt-addr-thumb-empty { border-style: dashed; }

/* Address cell — ONE line: address (ellipsised, full value on hover) · flags ·
   tag chips. tracker.js builds the nodes; this lays them out. (SLEEK 08-12 — the
   dim mono orderId left this inventory: directive A1 took the node off the row.) */
.jt-table-wrap .jt-addr {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
}
.jt-table-wrap .jt-addr > * { flex: 0 0 auto; }
.jt-table-wrap .jt-addr-text {
  flex: 1 1 auto;
  min-width: 90px;
  font-weight: var(--fw-semibold);
  font-size: var(--fs-body);   /* SLEEK 08-12 (A4) — the anchor reads one step stronger than the data cells around it, which inherit the table's smaller size. Decided: stays Inter, never Fraunces at 13-14px table scale. */
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* (SLEEK 08-12, A3 — the `.jt-order` dim-identifier rule that lived here was
   removed with the node itself: A1 took the Tonomo order id off the row. The id
   survives on the address hover title, the job modal, and data-side search.) */
/* tags trail the row; they never justify a second line */
.jt-table-wrap .jt-addr .tag-chips { flex: 0 1 auto; flex-wrap: nowrap; overflow: hidden; max-width: 92px; }

/* ── FROZEN IDENTITY COLUMN — RE-TUNED 2026-08-29 (SPEC-V2TRACKFIT Q1) ───────
   Owner's morning ruling: the design's CLIENT column is now the frozen
   identity anchor (not Address), and MEASURED at 375px it is Client ALONE
   that fits — Client+Address both frozen was tried and did not (a
   content-variable second sticky column has no fixed left-offset to pin to,
   and their combined width left well under 100px of clear pane on a phone).
   So: CLIENT is sticky on every viewport; ADDRESS is a normal (non-frozen,
   horizontally-scrolling) column now, same as Services/Stage/Shoot/etc.

   The bulk-select CHECKBOX is Display-picker-optional now (Q5, default OFF —
   the prototype's table has none), so the frozen anchor is `:first-child` in
   the common case and shifts to `:nth-child(2)` only when a user turns the
   checkbox back on (`.jt-table-wrap.jt-has-checkbox`) — selectors below cover
   both configurations explicitly rather than assuming one.

   Every selector below excludes tr.group-head — its single full-width colspan
   <td> IS a `td:first-child`, so an unqualified rule would silently steal its
   z-index and its status tint.

   Z-ORDER (a tie is decided by DOM order — which is wrong here, because the
   LATER, scrolled-away columns would win):
     6  thead corner cell(s) (sticky top AND left)
     4  the rest of thead        (sticky top)
     3  the status group band    (sticky top, spans every column)
     2  the frozen body cell(s)  (sticky left)                                */
/* default configuration (no checkbox): CLIENT is :first-child */
.jt-table-wrap:not(.jt-has-checkbox) table.table thead th:first-child { z-index: 6; }
.jt-table-wrap:not(.jt-has-checkbox) table.table thead th:first-child,
.jt-table-wrap:not(.jt-has-checkbox) table.table tbody tr:not(.group-head) > td:first-child {
  position: sticky; left: 0; z-index: 2; background: var(--panel);   /* sticky cells MUST be opaque */
}
.jt-table-wrap:not(.jt-has-checkbox) table.table tbody tr:not(.group-head):hover > td:first-child { background: var(--hover); }

/* opt-in configuration (Display → Row select turned on): CHECKBOX is
   :first-child (unchanged shape from before this pass) and CLIENT becomes
   :nth-child(2), pinned at the checkbox's own measured per-density width —
   the SAME 34/42/50px this app has always used for it (tracker.js sets an
   inline `width:34px` on that <th>; the density preset's own padding adds
   the rest, and it cannot be derived from CSS alone). */
.jt-table-wrap.jt-has-checkbox table.table thead th:first-child,
.jt-table-wrap.jt-has-checkbox table.table thead th:nth-child(2) { z-index: 6; }
.jt-table-wrap.jt-has-checkbox table.table thead th:first-child,
.jt-table-wrap.jt-has-checkbox table.table tbody tr:not(.group-head) > td:first-child { position: sticky; left: 0; }
.jt-table-wrap.jt-has-checkbox table.table thead th:nth-child(2),
.jt-table-wrap.jt-has-checkbox table.table tbody tr:not(.group-head) > td:nth-child(2) { position: sticky; left: 42px; }
.jt-table-wrap.jt-has-checkbox.density-compact table.table thead th:nth-child(2),
.jt-table-wrap.jt-has-checkbox.density-compact table.table tbody tr:not(.group-head) > td:nth-child(2) { left: 34px; }
.jt-table-wrap.jt-has-checkbox.density-big table.table thead th:nth-child(2),
.jt-table-wrap.jt-has-checkbox.density-big table.table tbody tr:not(.group-head) > td:nth-child(2) { left: 50px; }
.jt-table-wrap.jt-has-checkbox table.table tbody tr:not(.group-head) > td:first-child,
.jt-table-wrap.jt-has-checkbox table.table tbody tr:not(.group-head) > td:nth-child(2) { z-index: 2; background: var(--panel); }
.jt-table-wrap.jt-has-checkbox table.table tbody tr:not(.group-head):hover > td:first-child,
.jt-table-wrap.jt-has-checkbox table.table tbody tr:not(.group-head):hover > td:nth-child(2) { background: var(--hover); }

/* the frozen edge's hairline — class-based (.jt-col-client), so it always
   marks the TRUE frozen column regardless of whether checkbox is showing */
.jt-table-wrap table.table tbody tr:not(.group-head) > td.jt-col-client { box-shadow: inset -1px 0 0 var(--border); }

/* the group band spans every column, so its chip would scroll away too — pin the
   band's own content instead, and it rides along at the left edge. Its z-index is
   restated here because the frozen-column block above sits later in the cascade. */
.jt-table-wrap table.table tbody tr.group-head > td { z-index: 3; }
.jt-table-wrap table.table tr.group-head > td > span { position: sticky; left: var(--space-3); }

/* content-visibility placeholder height MUST track the new density or the
   scrollbar thrashes as off-screen rows resolve (contain-intrinsic-size was
   pinned at 96px against a 42px row).
   Values are the MEASURED post-change row heights: 21.0 / 42.3 / 145.8px. */
.jt-table-wrap table.table tbody tr.cv-row { contain-intrinsic-size: auto 44px; }
.jt-table-wrap.density-compact table.table tbody tr.cv-row { contain-intrinsic-size: auto 21px; }
.jt-table-wrap.density-big table.table tbody tr.cv-row { contain-intrinsic-size: auto 120px; }


/* ─────────────────────────────────────────────────────────────────────────────
   §3  KANBAN — the board gets its own scrollport and the column heads stick.
   ─────────────────────────────────────────────────────────────────────────────
   MEASURED DEFECT: the tallest column is 1431px in a 900px viewport and
   `.kanban-col-head` computes `position: static`, so at the bottom of that column
   its header sits at top:-601px. The card carries NO status cue of its own —
   status is encoded ONLY by which column you are in — so once you scroll, the
   board is anonymous.

   THE SCROLLER IS THE COLUMN, NOT THE BOARD. I built it the other way first —
   board scrolls vertically, heads sticky inside it — and measured the result: at
   scrollTop 400 the two TALL columns pinned their heads at offset 0, but every
   SHORT column had scrolled off the top entirely (heads at -21, -107, -159px),
   taking its cards with it. A sticky element can never outlive its containing
   block. Giving each COLUMN the scrollport instead means the board scrolls only
   horizontally, no column ever leaves the screen, every head is always visible,
   and each column scrolls independently — which is also what Trello / Jira /
   Linear do. `overflow-x: auto` + `overflow-y: hidden` is a legal pair (unlike
   auto + visible, which is what silently created the old phantom scrollport). */
.jt-board {
  overflow-x: auto;
  overflow-y: hidden;
  align-items: flex-start;     /* columns must not stretch — restated, it is load-bearing */
  gap: var(--space-3);
  padding-bottom: var(--space-3);
}
/* the column supplies horizontal padding only; the sticky head supplies the top
   padding itself, so it pins flush at offset 0 with no gap above it */
.jt-board .kanban-col {
  max-height: max(300px, calc(100vh - 300px));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
  padding: 0 var(--space-2) var(--space-2);
  min-height: 0;
}
.jt-board .kanban-col::-webkit-scrollbar { width: 8px; }
.jt-board .kanban-col::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }

/* ── SPEC-STACKBOARD 2026-09-03 · stacked stage BANDS instead of sideways lanes ──
   Vincent: "one thing tonomo does right is it kind of stacks things on top of each
   other so it isn't one infinite scroll to the side."
   MEASURED, and the lane board cannot be rescued by resizing: `.jt-board .kanban-col`
   (styles.css:1048) is clamped to fit "~4 across" by its own comment, while the studio
   runs NINE statuses — so the board measured 2012px inside a 1138px pane at 1280
   (874px over) and 1592/882 at 1024. Nine readable columns do not fit a laptop at any
   column width worth reading, which is exactly why he wants stacking rather than a
   tighter clamp.
   Each stage becomes a FULL-WIDTH BAND with its jobs listed underneath — the
   VVT-Hub-Owner / VVT-Video-Post mockups' own model (a count, the stage name, rows
   beneath) — so the page scrolls DOWN like everything else and nothing is off-screen
   to the right.
   UNCONDITIONAL, and that is a correction of my own first attempt: I initially gated
   this below 1440 on the theory that a big monitor is where lanes were designed to
   work. MEASURED at 1440 with the gate in place: still **732px over**. Because
   `#view { max-width: 1340px }` (styles.css:337) caps the content pane at 1280px on
   EVERY display, nine lanes at a readable width cannot fit at ANY viewport — there is
   no width at which the lane board is the right answer for this studio's nine statuses,
   so there is no width at which to keep it.
   Scoped to `.jt-board`, the Tracker's own hook, so Leads / Tasks / Marketing /
   Pipelines / Video keep their lane boards untouched.
   The nested vertical scrollport is REMOVED when stacked (max-height/overflow-y): a
   scrollport inside a page that already scrolls is the two-scrollbar trap the original
   `.jt-board` comment was written to avoid, and a band has no fixed height to scroll
   within. The head's `position: sticky` goes with it — sticky resolves against that
   now-absent scrollport, and left as-is it pins mid-page while you scroll past. */
.jt-board { display: block; overflow-x: visible; overflow-y: visible; padding-bottom: 0; }
.jt-board .kanban-col {
  flex: none;                    /* beats the 4-across clamp at styles.css:1048 */
  width: auto; min-width: 0; max-width: none;
  max-height: none; overflow-y: visible;
  margin-bottom: var(--space-3);
  border-radius: var(--radius);
}
  /* An empty stage is one line, not a hole: in Active scope "Completed · 0" and
     "Cancelled · 0" are empty BY DEFINITION (tracker.js buildKanban says so), and a
     full-width empty band wastes a whole row each. They stay present — a done column
     is still a legitimate drag target, which is why buildKanban collapses rather than
     removes them — just quiet. */
.jt-board .kanban-col:not(:has(.kanban-card)) { opacity: 0.62; }
.jt-board .kanban-col:not(:has(.kanban-card)) .kanban-col-head { margin-bottom: 0; border-bottom: 0; }
.jt-board .kanban-col-head {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--bg-2);     /* MUST be opaque, and MUST match .kanban-col */
  padding: var(--space-2) 2px var(--space-2);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius) var(--radius) 0 0;
  /* tracker.js paints `inset 0 3px 0 0 <status colour>` here inline, so the
     column's identity survives BOTH axes of scroll (ClickUp / Slack Lists). */
}
/* SPEC-STACKBOARD 2026-09-03 — MUST come after the sticky rule above, not before it.
   Placed earlier (where the rest of the stacking block lives) this lost the cascade and
   the head stayed `position: sticky` — pinning against a scrollport the stacked band no
   longer has, so it floated mid-page as you scrolled. Caught by its own test, not by eye. */
.jt-board .kanban-col-head { position: static; border-radius: var(--radius) var(--radius) 0 0; }
/* the COUNT is what you scan a board for. It was an 11px dim `.badge` floated to
   the far right — the weakest thing in the header; UI-BOARD 07-30 moved it to
   lead, right after the status chip. SPEC-DHTRACK D3 folds the count INTO a
   solid stage-color chip (the shared .stage-band-chip — see the stage-band
   block above the kanban section) instead of a separate plain `.count` span, so
   this rule now sizes that chip for the kanban context (the shared default is
   sized for the table band's roomier row). */
.jt-board .kanban-col-head .stage-band-chip {
  font-size: var(--fs-caption);
}
.jt-board .kanban-col-head .kcol-sum {
  margin-left: auto;
  color: var(--muted);
  font-size: var(--fs-micro);
  font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}


/* ─────────────────────────────────────────────────────────────────────────────
   §4  KANBAN — empty columns: a drop target, and a rail for the ones that are
       empty by definition.
   ─────────────────────────────────────────────────────────────────────────────
   MEASURED DEFECT: in Active scope the board still renders "Completed · 0" and
   "Cancelled · 0" as full 260px columns — 544px of permanently empty board, 20%
   of it — while "Stuck · 7 jobs · $6,417", the one column that needs a PM, starts
   at x≈2176px, entirely off-screen (board scrollWidth 2708 vs 1148 visible).

   They are COLLAPSED, not removed: a done column is a legitimate drag target
   ("deliver this job"), and deleting that gesture is an IA decision, not styling. */
.jt-board .kanban-col-empty {
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--faint);
  font-size: var(--fs-meta);
  text-align: center;
  padding: var(--space-4) var(--space-2);
}
.jt-board .kanban-col.drag-over .kanban-col-empty {
  border-color: var(--accent);
  border-style: solid;
  color: var(--accent);
}

/* the rail: a measured 44px instead of 260px, reclaiming ~430px of board width
   (board scrollWidth 2708 → 2276 on the live board). Still a full drop target,
   still labelled, still counted. Vertical writing mode is the standard
   collapsed-column idiom (Jira / Trello) — it reads as "this column is folded",
   which is exactly what is true. */
.jt-board .kanban-col.is-rail {
  flex: 0 0 48px;
  min-width: 0;
  padding: 0;
  max-height: 240px;
  overflow: hidden;
}
.jt-board .kanban-col.is-rail .kanban-col-head {
  writing-mode: vertical-rl;      /* inline axis runs top→bottom: chip, then count */
  align-items: center;
  justify-content: flex-start;
  gap: var(--space-2);
  height: 100%;
  padding: var(--space-3) var(--space-1);
  margin: 0;
  border-bottom: none;
  border-radius: var(--radius);
}
.jt-board .kanban-col.is-rail .kanban-col-empty { display: none; }
/* dragging over a folded column opens it, so the drop is never a blind gesture */
.jt-board .kanban-col.is-rail.drag-over { flex: 0 0 200px; max-height: max(300px, calc(100vh - 300px)); overflow-y: auto; }
.jt-board .kanban-col.is-rail.drag-over .kanban-col-head {
  writing-mode: horizontal-tb;
  height: auto;
  padding: var(--space-2);
  border-bottom: 1px solid var(--border);
  border-radius: var(--radius) var(--radius) 0 0;
}
.jt-board .kanban-col.is-rail.drag-over .kanban-col-empty { display: block; margin: var(--space-2) var(--space-2) 0; }
@media (prefers-reduced-motion: no-preference) {
  .jt-board .kanban-col { transition: flex-basis var(--t-fast) var(--ease); }
}


/* ─────────────────────────────────────────────────────────────────────────────
   §5  KANBAN CARD — three bands instead of one soup row.
   ─────────────────────────────────────────────────────────────────────────────
   MEASURED DEFECT: title 13px/700 vs meta 12.5px/400 — HALF A PIXEL of size
   separation between the headline and everything supporting it. Card 0 carried
   ten distinct fontSize/weight/colour combinations, all metadata lived in one
   undifferentiated flex-wrap row (so the client who owns the job read exactly
   like "Jul 16"), and card heights ran 104→204px.

   Bands: 1 WHAT (address + flags) · 2 WHO (client + crew) · 3 WHEN (dates,
   priority, price). Title is now var(--fs-body)/600 against var(--fs-meta)/400 —
   a real 2px + 200-weight step. */
.jt-board .kanban-card {
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-2);
  font-size: var(--fs-meta);
  line-height: var(--lh-snug);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);   /* the ONE rhythm between bands */
}
/* one bar + at most one tag row now, so the intrinsic-size guess drops with it */
.jt-board .kanban-card.cv-card { contain-intrinsic-size: auto 104px; }

/* band 1 — WHO/WHERE. SPEC-V2TRACKFIT Q6a 2026-08-29 (owner's morning ruling)
   — re-tuned to the prototype's own card exactly (VVT-Hub-Owner.dc.html
   ~line 357-358, re-click-mapped on :8123): CLIENT is the bold primary line,
   ADDRESS the muted line right under it — a vertical stack, not the old
   title-plus-flags row (the design's card carries no per-row flags on this
   band at all; see kc-flags below for what's left and why). */
.jt-board .kanban-card .kc-head {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.jt-board .kanban-card .kc-client {
  font-size: 13px;
  font-weight: var(--fw-semibold);
  letter-spacing: -.01em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.jt-board .kanban-card .kc-addr {
  font-size: 12px;
  line-height: 1.45;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.jt-board .kanban-card .kc-flags {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-top: 2px;
}
/* the QC-override / delivered-with-open-red EXCEPTION flags (age/growth
   chips were retired from this card — Q6a) must never be mistaken for
   routine content; the design has no equivalent to fold them into, so they
   keep their existing look but stay visually quiet and collapse to nothing
   when there isn't one. */
.jt-board .kanban-card .kc-flags:empty { display: none; }

/* band 2 — CREW. Tier badge + who's shooting/editing — real functionality
   beyond the static mock (the design's data model has no crew-assignment
   concept), kept as its own quiet row rather than dropped. */
.jt-board .kanban-card .kc-who {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  min-height: 22px;   /* the avatar height — keeps band 3 at a constant offset */
}
.jt-board .kanban-card .kc-who:empty { display: none; }
.jt-board .kanban-card .kc-who .avatar-stack { margin-left: auto; flex: 0 0 auto; }

/* band 3 — WHEN + HOW MUCH. Fixed order: shoot · due · priority · price. */
.jt-board .kanban-card .kc-facts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  font-size: var(--fs-meta);
  color: var(--muted);
}
.jt-board .kanban-card .kc-fact { display: inline-flex; align-items: center; white-space: nowrap; }
.jt-board .kanban-card .kc-prio {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}
.jt-board .kanban-card .kc-prio-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; flex: 0 0 auto; }
/* price is the one right-aligned figure, so a column of cards forms a money rail */
.jt-board .kanban-card .kc-price {
  margin-left: auto;
  color: var(--text);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.jt-board .kanban-card .kc-bar,
.jt-board .kanban-card .kc-tags { min-width: 0; }
.jt-board .kanban-card .kc-tags .tag-chips { max-width: 100%; }

/* SPEC-WAVE0-A A4 2026-09-02 — the card foot's stage pill (statusCellSelect,
   buildKanbanCard): the one non-drag way to move a card, so touch/keyboard
   users aren't stuck with HTML5 drag as the only door. */
.jt-board .kanban-card .kc-foot { margin-top: var(--space-2); }
.jt-board .kanban-card .kc-foot .jt-stage-pill { width: 100%; max-width: 100%; }

/* the primary gesture on a card is DRAG. Lifting it 1px on hover and swapping in
   the biggest shadow in the system fights that — the card should look ready to be
   picked up, not already floating. */
.jt-board .kanban-card:hover {
  transform: none;
  box-shadow: var(--shadow);
  border-color: var(--border-strong);
}
.jt-board .kanban-card:focus-visible { box-shadow: var(--focus-ring); }

/* DARK MODE — measured: `.kanban-col` (--bg-2 #16202e) and `.kanban-card`
   (--panel #1a1d27) sit at a luminance ratio of 1.02:1, and the COLUMN is the
   lighter surface — so the light-mode "cards float on a tray" relationship is
   inverted and only the 1px --border holds a card together. Dropping the tray to
   --bg (#101722) puts it BELOW the card again (1.09:1) and restores the direction.
   Light mode is untouched and already reads correctly (#f6f6f4 page / #eef0f4
   tray / #ffffff card). */
html[data-theme="dark"] .jt-board .kanban-col { background: var(--bg); }
html[data-theme="dark"] .jt-board .kanban-col-head { background: var(--bg); }
html[data-theme="dark"] .jt-board .kanban-card { border-color: var(--border-strong); }


/* ─────────────────────────────────────────────────────────────────────────────
   §6  ONE STATUS, ONE COSTUME  —  INTENTIONALLY GLOBAL (2 surfaces).
   ─────────────────────────────────────────────────────────────────────────────
   MEASURED DEFECT: the same datum wore four costumes — an App.chip pill in the
   table group header, a 127×32px tinted native <select> in the row (90 form
   controls on one page at 45 rows), an App.chip pill in the kanban column head,
   and NOTHING on the kanban card. The loudest of the four was the least readable.

   `.cell-select` is created ONLY by tracker.js's statusCellSelect /
   priorityCellSelect (exported as App.statusCellSelect / App.priorityCellSelect),
   so this rule reaches exactly two surfaces: the Job Tracker and the Video
   Tracker. Both should look the same — that is the point of one system.

   It stays a <select>. It is NOT converted to a click-to-edit chip: that would
   route around statusCellSelect's must-fix-QC red gate (tracker.js:188–200),
   App.logActivity and App.deliveredMoment, and it would turn a one-gesture
   dropdown into two. tintSelect() already writes the status colour inline as
   background/border/ink, so the resting state now matches the App.chip in the
   group header above it pixel for pixel — and the row loses ~14px. */
/* SLEEK 08-12 — status becomes the row's single strongest color signal: a
   left-aligned, token-sized pill. This supersedes the 07-30 rationale above
   (which centered the control to match `.chip` pixel-for-pixel) — Fable's
   call is that left alignment scans faster down a column of pills. The tint
   itself (tintSelect's inline background/border/ink) is untouched; do NOT
   edit tintSelect/statusCellSelect/priorityCellSelect JS. Still shared with
   the Video Tracker via this same global .cell-select (see §6 above), which
   is the desired parity. SPECIFICITY NOTE: `select.select, select.input, …
   { padding-right: 30px }` (styles.css ~485, chevron clearance for the OTHER
   selects in the app) is `(0,1,1)` — one class ABOVE this rule's bare
   `.cell-select` `(0,1,0)`, so a same-shorthand `padding` here loses just the
   padding-right longhand regardless of source order, leaving a lopsided
   30px-right pill. Selector bumped to `select.cell-select` `(0,1,1)` to tie
   and win on source order (this rule is later in the file). */
select.cell-select {
  appearance: none;
  -webkit-appearance: none;
  background-image: none;
  border-radius: 999px;
  padding: 3px 12px;
  min-height: 0;
  height: auto;
  min-width: 0;
  width: auto;
  font-size: var(--fs-meta);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  letter-spacing: .1px;
  cursor: pointer;
  text-align: left;
}
/* appearance:none removes the native disclosure arrow — decided: no replacement
   chevron (tintSelect's inline `background` would clobber a background-image).
   The affordance is carried entirely by hover/focus below; it must be
   unmistakable. */
/* DESIGNA 2026-08-19 — the ring tokens alone measured 1.25:1 (hover) / 1.73:1 (focus)
   against the white panel: invisible, on the control whose own comment above demands an
   UNMISTAKABLE affordance. tintSelect writes background/border-color INLINE, so a CSS
   border change can never win here — outline can (nothing writes it inline). --accent on
   --panel = 6.62:1, and the 2px accent outline is this file's own :focus-visible idiom
   (see ~:3921). The soft rings stay underneath for the glow. */
.cell-select:hover { box-shadow: var(--focus-ring-soft); outline: 1px solid var(--accent); outline-offset: 1px; }
.cell-select:focus-visible { box-shadow: var(--focus-ring); outline: 2px solid var(--accent); outline-offset: 2px; }
/* the Big density preset stays roomy (its own font-size rule already wins) */
.jt-table-wrap.density-big .cell-select { padding: 6px 13px; }
/* SLEEK 08-12 — Compact tightens the pill to match the row's shrunken padding */
.jt-table-wrap.density-compact .cell-select { padding: 1px 9px; }
/* LAW-A 2026-08-15 (#24 follow-up, mobile only) — LIVE-VERIFIED at 375×812: this is the
   status control a crew member taps most (every row, both trackers), and it measured
   24px tall via getBoundingClientRect (min-height:0/height:auto above) — the worst
   tap-target gap found this pass, well under half the 44px floor. §I.4 of the plan
   names this control explicitly: stay a native <select> (unchanged — do NOT convert to
   a popover), just make it 44px tall. Scoped to phones only, same as the .btn/select
   fixes above — SLEEK 08-12's deliberate compact-pill density on desktop/tablet (mouse,
   not a finger) is untouched. NOTE (corrected DESIGNJ 2026-08-19): the claim that the
   density-preset overrides above "are also desktop-only in practice" was an ASSUMPTION,
   not an enforcement — see the rule below. */
@media (max-width: 640px) {
  select.cell-select { min-height: 44px; font-size: var(--fs-md); }   /* DESIGNA 2026-08-19 — was 12px (--fs-meta): iOS auto-zoomed on the most-tapped control in both trackers; ≥16px joins the anti-zoom law */
  /* DESIGNJ 2026-08-19 (judge pass on DESIGNA) — the rule above is (0,1,1) and LOSES to
     `.table-wrap.density-compact select` (:633) and `.table-wrap.density-big select` (:642),
     both (0,2,1). The ⚙ Display › Row size picker has NO width gate (tracker.js:4041,
     per-device localStorage), so a phone set to Compact measured 11px / min-height:0 /
     17.75px tall LIVE at 390×844 — iOS auto-zoom returns AND the LAW-A 44px floor collapses
     to under half. TAPFLOOR cannot see this: its sweep reblanks to zero data, so no row
     control exists to measure. (0,3,1) here outranks both regardless of order; scoped to
     .jt-table-wrap because tracker.js:4229 is the ONLY site that emits a density-* class
     and it always emits both, and it matches this file's own idiom at :7789/:7791.
     Phones only — desktop/tablet Compact and Big are byte-identical to before. */
  .jt-table-wrap.density-compact select.cell-select,
  .jt-table-wrap.density-big select.cell-select { min-height: 44px; font-size: var(--fs-md); }
}


/* ─────────────────────────────────────────────────────────────────────────────
   §7  URGENCY COLOUR  —  INTENTIONALLY GLOBAL (accessibility).
   ─────────────────────────────────────────────────────────────────────────────
   FOUR MEASURED AA FAILURES in light mode, all on the signals a PM must not miss:
     .due-today            #dc6803 @ 12.5px/700  →  3.49:1   (needs 4.5)
     .due-soon             #dc6803 @ 12.5px/400  →  3.49:1
     .age-badge.age-warm   #dc6803 on --warn-soft @ 10.5/700 →  3.34:1
     .age-badge.age-hot    #d92d20 on --danger-soft          →  4.44:1
     .due-overdue          4.83:1 on --panel but 4.38:1 on a status-tinted row

   Identical root cause in all five: they use the FILL tokens --warn / --danger as
   text ink. The repo already fixed exactly this for .chip.is-warn / .chip.is-danger
   (styles.css:620–622) by switching to --warn-ink / --danger-ink. This is that same
   one-line swap, nothing new.

   Left GLOBAL on purpose: scoping an AA fix to one view would leave the identical
   failure on every other surface that renders a due date. Dark mode is a no-op —
   --warn-ink / --danger-ink alias --warn / --danger under html[data-theme="dark"],
   and dark already measured 6.03–8.24:1. If the styles.css owner lands the same
   swap this pass, these three lines are a harmless duplicate; drop them then. */
.due-overdue, .overdue { color: var(--danger-ink); }
.due-today { color: var(--warn-ink); }
.due-soon { color: var(--warn-ink); font-weight: var(--fw-semibold); }   /* + a real 3-step ramp: soft → semibold → bold */
.age-badge.age-warm { color: var(--warn-ink); }
.age-badge.age-hot { color: var(--danger-ink); }


/* ══════════════════════════════════════════════════════════════════════════
   TRACKER-SLEEK 2026-08-12 — width fix (progressive column collapse) + look
   directives (Fable's spec, sections B-tiers / C / D). Builder scope: this
   file only. Pairs with the same-date tracker.js/core.js pass (row-level
   orderId removal, Priority+Package demoted out of the default column set —
   see App.getTrackerColumns' `_jtSlimCols0812` migration). Tokens only, no
   new custom properties, no !important.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── SLEEK 08-12 · PROGRESSIVE COLUMN COLLAPSE ────────────────────────────────
   Width-driven, VIEW-ONLY hiding by stable per-key class (D17(c) pattern, see the
   640px block below): the synced trackerColumns setting is never touched, the ⚙
   picker still governs the master set, and every hidden datum stays one row-click
   away in the job modal.
   SPEC-WAVE0-A A2 2026-09-02 (RE-TUNED, supersedes the 08-12/08-28 tiers below —
   NOT deleted from history, see the archived measurements two rulings up in this
   file's own git-blame equivalent, the memory index) — the old ≤1599px tier hid
   Services + Editor + Needs-you, and the old ≤1439px tier hid Photographer + Price,
   which meant on Vincent's OWN 1440 Mac two of the seven DEFAULT job columns
   (Services, Needs you) never rendered at all while the Display picker still showed
   them checked — measured, reports-2026-09/audit-sys/inv-smallscreen.json. Ruling:
   resize, don't delete. Every DEFAULT column (Client · Address · Services · Stage ·
   Shoot · Price · Payment · Needs you) now renders unconditionally at ≥1280 — the
   ONLY width-driven hides left are the ≤1279 tier below (≤1179 until SPEC-BANDFIT
   2026-09-08 corrected it; see that block's own measurement) (Price/Payment/Editor/
   Photographer/links + the "+" cell) and a tighter Address cap, so the previously-
   protected 1280/1024 hard targets keep zero horizontal scroll with the SAME
   default set the picker actually shows checked. A "Show all columns" chip
   (tracker.js render(), localStorage 'vvt-ui-tracker-showall') is the escape hatch
   the old ladder never had: every `:not(.jt-show-all)` hide below is skipped when
   it's on, and `.table-wrap`'s own `overflow-x:auto` (styles.css ~753, always on)
   scrolls instead — Client stays sticky regardless (the FROZEN IDENTITY rule below
   is unconditional), so scrolling never loses row identity. */
/* ── SPEC-TRACKFIT 2026-09-03 · the caps that were tier-gated are now UNCONDITIONAL ──
   The tier below only bound Address BELOW 1440, on the reasoning that a bigger
   viewport has room to spare. It does not: `#view { max-width: 1340px }`
   (styles.css:337) caps the content pane at 1280px on EVERY monitor, so at exactly
   1440 — Vincent's own Mac — no tier applied and Address rendered UNCAPPED at 362px,
   making 1440 the WORST width in the app. MEASURED on this tree, default columns,
   seed data: 122px of horizontal scroll at 1440, 102px at 1280, 194px at 1024 — i.e.
   the ladder's own comment above ("the previously-protected 1280/1024 hard targets
   keep zero horizontal scroll") was false at all three. Since the pane can never
   exceed 1280px there is no width at which a 362px Address column is affordable, so
   the cap belongs outside the media query entirely.
   The cell-level cap (th/td, not just the inner .jt-addr) is the A2 2026-09-02 finding,
   inherited from the <=1439 tier that used to sit below this block: table auto-layout
   sizes the cell off content BEFORE a descendant's max-width applies, so capping only
   .jt-addr does nothing. min-width is reset for the same reason — a min-width always
   beats a conflicting smaller max-width per spec, and §FROZEN IDENTITY set a 220px floor.
   THESE FOUR LINES ARE NOW THE WHOLE LADDER above the <=700px phone tier. Deleted
   2026-09-08, each already overridden by them and each measured to change nothing: that
   <=1439 tier (a byte-for-byte repeat), §FROZEN IDENTITY's 220px floor, SLEEK 08-12's
   300px cap, and STAGE1-FIX's 282px Links give-back. Edit the numbers HERE. */
.jt-table-wrap thead th.jt-col-address,
.jt-table-wrap td.jt-addr-cell { max-width: 200px; }
.jt-table-wrap .jt-addr { max-width: 200px; }
.jt-table-wrap table.table thead th.jt-col-address { min-width: 0; }
/* Services is the other genuinely fat column (measured 300px at every width, pinned
   at its own cap while carrying names as long as "Zillow 3D Tour w/Interactive Floor
   Plan"). It ellipsizes the same way Address does and the full list is one row-click
   away in the job modal, so it gives width back on the same doctrine. */
.jt-table-wrap thead th.jt-col-services,
.jt-table-wrap td.jt-col-services { max-width: 220px; }
/* The last 22px (1280) and 34px (1024). Every column above is already at or near
   its useful floor, so the remaining width comes from PADDING rather than from
   clipping more content: the default density spends 24px of horizontal padding per
   cell (styles.css:9184-9185) across nine columns — 216px of the table. Stepping
   12px to 9px below 1440 returns ~54px, which clears both remaining overflows with
   room to spare and takes nothing off the screen. 1440 keeps the roomier padding,
   because it measured 0 over without this. */
@media (max-width: 1439px) {
  .jt-table-wrap.density-comfortable table.table td { padding: 8px 9px; }
  .jt-table-wrap.density-comfortable table.table th { padding: 7px 9px; }
  /* ── SPEC-BIGFIT 2026-09-08 · the SAME relief for Big, which never got it ──
     The ladder above (and the caps above it) was tuned in Cozy and scoped to
     `.density-comfortable` ONLY — deliberately, so it could not clobber the
     Compact/Big presets' own (0,3,2) rules. The unintended half: Big then paid
     the wide-tier bill with NONE of the relief that funds it. Big spends 32px of
     horizontal padding per cell against Cozy's 18px here, on top of +1.5px font
     and +.16 line-height, and `density-big` is a real user-selectable state
     (Display ▸ Row size) that persists per account via App.pref
     ('vvt-ui-tracker-density').
     MEASURED 2026-09-08, real tracker view, owner, default job columns, 14
     production-shaped jobs (real LA listing addresses, real service names incl.
     "Zillow 3D Tour w/Interactive Floor Plan"), in an iframe driven to each
     width so the media queries evaluate for real, against the app's OWN pane —
     min(1280, vw-142), the 82px rail plus `#view{max-width:1340px;padding:26px
     30px}`. Table width REQUIRED vs pane available, before → after:
       vw 1024 (pane 882):  Big 983 (-101 over) → 891 (-9)   · Cozy 893 (-11) both
       vw 1280 (pane 1138): Big 1248 (-110)     → 1130 (+8)  · Cozy 1126 (+12)
       vw 1440 (pane 1280): Big 1248 (+32)      → 1248 (+32) · unchanged by design
     So Big now lands within 4px of Cozy's own headroom at the 1280 hard target
     instead of 110px past it, and at 1024 it is actually 2px BETTER than Cozy.
     WHY PADDING AND NOT FONT: horizontal padding is the one lever that "takes
     nothing off the screen" (the comment above this block, unchanged doctrine).
     Big's identity is its ROW HEIGHT and its text size — the 16px/15px VERTICAL
     padding and the 14.5px font are deliberately untouched here, so Big still
     reads as Big; only the horizontal gutters follow the same width ladder Cozy
     already walks. Big is never tighter than Cozy: 9px here, 7px below, the
     identical numbers.
     WHY SERVICES ALSO STEPS: padding relief pays on SEVEN of the nine columns —
     Address (200px) and Services (220px) are capped, so shrinking their gutters
     returns the width to the cap, not to the table. Measured, the padding ladder
     alone leaves Big 12px over at 1280; the last 12px has to come from one of the
     two capped columns. It comes from SERVICES, not Address: Address is the row's
     listing identity and keeps its full 200px cap at every density, while the
     service list is the column this file already calls "one row-click away in the
     job modal" and it ellipsizes with the full value on the cell title. Cozy's
     220px is untouched. */
  .jt-table-wrap.density-big table.table td { padding: 16px 9px; }
  .jt-table-wrap.density-big table.table th { padding: 15px 9px; }
  .jt-table-wrap.density-big thead th.jt-col-services,
  .jt-table-wrap.density-big td.jt-col-services { max-width: 200px; }
}
/* The final 10px at 1024. Same lever, one notch further — so the six survivors get
   every pixel where the pane is genuinely tight.
   THIS 1179 IS NOT A TYPO AND IT IS NOT DRIFT (SPEC-BANDFIT 2026-09-08). The column
   tier below moved to ≤1279; this padding notch DELIBERATELY did not follow it. Its
   job is the 1024-class pane, not "wherever columns are hidden": between 1180 and
   1279 the six-column set has room to spare, so spending the extra 2px of gutter
   there would only make the table read tighter for nothing. MEASURED 2026-09-08 with
   the tier at ≤1279 and this notch left at ≤1179 — the six survivors at their roomier
   9px gutters fit every width from 1180 to 1279 in all three densities with zero
   overflow. The two numbers describe two different problems and are meant to differ.
   (The DENSITYFIT sheet walk in tests/suite.js pins this block's 1179 on purpose.) */
@media (max-width: 1179px) {
  .jt-table-wrap.density-comfortable table.table td { padding: 8px 7px; }
  .jt-table-wrap.density-comfortable table.table th { padding: 7px 7px; }
  /* SPEC-BIGFIT 2026-09-08 — Big takes the same second notch (see the block above). */
  .jt-table-wrap.density-big table.table td { padding: 16px 7px; }
  .jt-table-wrap.density-big table.table th { padding: 15px 7px; }
}

/* The <=1439 tier that used to sit here was DELETED 2026-09-08. It repeated the four
   unconditional declarations above byte-for-byte (200px on th, td and .jt-addr;
   min-width:0 on the header), making it a verbatim no-op from the moment SPEC-TRACKFIT
   2026-09-03 lifted those caps out of it — while its comments went on describing the
   pre-09-03 world, in which Address "steps down once more" below 1440. Nothing about
   Address is width-conditional any more above the <=700px phone tier: `#view
   { max-width: 1340px }` (styles.css:337) caps the pane at 1280px on EVERY monitor, so
   "a bigger viewport has room to spare" is never true. The A2 2026-09-02 cell-level
   finding this tier carried is kept in the SPEC-TRACKFIT comment above, where the live
   rules are. MEASURED 2026-09-08 at BOTH a 1280 and a 1700 window: removing this block
   changed nothing rendered. */
/* SPEC-BANDFIT 2026-09-08 — THIS TIER WAS ≤1179px, AND THAT NUMBER WAS THE BUG.
   1179 was picked for the 1024 laptop — the comment it carried said "pane <
   ~886px" — but the pane at a 1179 viewport is 1037px, not 886, and the full
   nine-column set needs ~1112px. So from 1180 (where this tier stops applying)
   up to ~1255 (where the full set finally fits) EVERY column rendered into a
   pane far too narrow for it, and the Job Tracker side-scrolled in ALL THREE
   row sizes — including Cozy, the default. The tier stepped the columns out
   AFTER the pane got too narrow instead of BEFORE.
   MEASURED 2026-09-08 AT THE CALL SITE, not in a harness: the real tracker
   view, owner, default job columns, the real 19-job seed board, driven through
   real viewport widths so these queries evaluate for real. The band ran
   1180→1255 in Cozy (76px over at 1180, 1px at 1255) and 1180→1252 in Big;
   Compact 1180→1193. 1260/1270/1280/1366/1440/1512/1920 all measured zero.
   WHY 1279 AND NOT 1255, the width where the band merely closes: 1255 is a
   knife-edge, and this file has been bitten by exactly that before. Re-measured
   with the same board carrying the widest real name+company pair seed.js ships
   ("Marisol Contreras / Contreras & Associates" — Client is the one uncapped
   content column, so it is where a real roster legitimately grows), a ≤1255
   tier RE-OPENED the defect at 1256–1266. ≤1279 measured ZERO overflow at
   every width in every density across all three fixtures.
   1279 is also the one threshold here that cannot drift out of sync: 1280 is
   already the app's hard target, the pane cap (`#view` above caps the pane at
   1280 on ANY monitor) and the contract the suite's own W0A A2 test states in
   as many words — "at >=1280 every DEFAULT job column renders". Below the
   hard target the low-value glance columns step out; at and above it the full
   set ships. Their real home is the job modal, one row-click away, and the
   "N columns hidden · Show all" chip (tracker.js render()) is the escape
   hatch — it counts the REAL hidden <th>s via getComputedStyle rather than
   duplicating this number, so it followed this change for free. NO JS KNOWS
   THIS BREAKPOINT; it lives here and only here. */
@media (max-width: 1279px) {   /* pane < ~1138px: Price + Payment + Editor + Photographer + links + the "+ add column" cell step out, UNLESS Show all is on. Client · Address · Services · Stage · Shoot · Needs you never hide at any width — those are the row's identity and its urgency. */
  .jt-table-wrap:not(.jt-show-all) .jt-col-price,
  .jt-table-wrap:not(.jt-show-all) .jt-col-payment,
  .jt-table-wrap:not(.jt-show-all) .jt-col-editor,
  .jt-table-wrap:not(.jt-show-all) .jt-col-photographer,
  .jt-table-wrap:not(.jt-show-all) .jt-col-links { display: none; }
  /* STAGE1-FIX 2026-08-14 (checker 2) history — links steps out here alongside
     payment, same reasoning as ever (lowest-value glance column of the survivors,
     real home is the job modal). A1 2026-09-02 — the drawer row's own single
     colspan td is ALSO a tbody td:last-child, so this same rule used to swallow it
     whole (:not(.jt-drawer-row) fixes the actual blocking bug, reports-2026-09/
     audit-sys/inv-smallscreen.json finding 1). */
  .jt-table-wrap:not(.jt-show-all) table.table thead th:last-child,
  .jt-table-wrap:not(.jt-show-all) table.table tbody tr:not(.group-head):not(.jt-drawer-row) > td:last-child { display: none; }
  /* SPEC-TRACKFIT 2026-09-03 — this block used to WIDEN Address back to 280px here,
     on the reasoning that Price/Payment/Editor/Photographer/links stepping out had
     "freed real room". MEASURED on this tree at 1024x768, owner, default columns:
     it had not. With those five hidden AND Address back at 280 the table still ran
     994px in an 880px pane — 114px of horizontal scroll — and the 80px this
     give-back spends is most of it. The narrowest pane is the last place to hand
     width back to the file's own designated shrink-first column, so the
     unconditional 200px cap above simply stands at every width now. Address stays
     far above its 90px text floor and the full string is one row-click away. */
}
/* STAGE1-FIX 2026-08-14 (checker 2) gave Address 18px back here — `.jt-table-wrap
   :has(.jt-col-links) .jt-addr { max-width: 282px }` at >=1180 — so the Links column
   could ship on the 1280 hard target without hiding. DELETED 2026-09-08: INERT since
   SPEC-TRACKFIT 2026-09-03 capped the address CELL at 200px, and a give-back on the
   inner div cannot outgrow the cell that contains it. MEASURED 2026-09-08 with Links
   switched ON at a 1700 window: the div computed 282px and rendered 138px — exactly
   what it renders with the column off, so the give-back had never reached the screen.
   Links costs Address nothing now; `.table-wrap`'s own overflow-x:auto is the escape
   hatch, and the 200px cell cap above is the only number in play. */

/* SLEEK 08-12 closer fix (measured DEFECT, width-independent, not taste): the
   CONSOL-3 column-resize grip is `right: -4px; width: 9px` — it deliberately
   straddles the column boundary. On the LAST visible resizable column those 4px
   poke past the table's right edge and become phantom scrollable overflow: at
   ≤1279 (≤1179 before SPEC-BANDFIT 2026-09-08) the "+ add column" cell (which
   has no grip) is display:none, so a
   resizable column IS last, and every snug pane measured a constant +4px
   (1024: 734 vs 730; 1179: 889 vs 885) even after the table itself fit exactly.
   Scoped to the tracker wrap only: pull the grip fully inside its column and move
   its hover hairline from right:4px to right:0 so the visible line still hugs the
   column edge exactly as before — same 9px grab, same look, zero phantom pixels.
   The generic .col-resize (styles.css ~626) and every other resizable table are
   untouched. */
.jt-table-wrap .col-resize { right: 0; }
.jt-table-wrap .col-resize::after { right: 0; }

/* SLEEK 08-12 — C2 header discipline: quiet uppercase micro-labels, the DATA is
   the ink, the header is the whisper. `.table th` (styles.css ~598) already sets
   text-align:left, font-size 11.5px, text-transform:uppercase, letter-spacing
   .4px and color:var(--muted) — already at/near the spec's target values. The
   one real delta is weight (currently the browser default ~400 via inherited
   normal); add only that. */
.jt-table-wrap table.table th { font-weight: var(--fw-semibold); }
/* SPEC-DHTRACK D2 — the last step to the README's exact "11px/600/uppercase,
   muted" header spec: SLEEK 08-12 above landed the weight; this steps the size
   down from the shared 11.5px to the exact --fs-micro (11px) token, scoped to
   this table only (every other .table th in the app keeps 11.5px). */
.jt-table-wrap table.table th { font-size: var(--fs-micro); }
/* SPEC-DHTRACK D2 — row hover wash: the shared `.table tbody tr:hover` (~line 774)
   uses var(--hover) everywhere else in the app; the Job Tracker's own row hover
   steps to var(--subtle) (#fbfbfa in light) per the README's row spec — scoped so
   no other table's hover state changes, the accent-tinted inset rail is untouched. */
.jt-table-wrap table.table tbody tr:hover { background: var(--subtle); }

/* SLEEK 08-12 — C3 kill vertical rules, keep the scan line: `.table td`/`th`
   (~598-604) already carry only a bottom hairline, no left/right border exists
   to override — this makes that contract explicit for this surface so a future
   generic-table border addition can't silently leak a vertical rule in here.
   The frozen column's own inset box-shadow (FROZEN block, td:nth-child(2)) is a
   separate, load-bearing rule — untouched. */
.jt-table-wrap table.table th, .jt-table-wrap table.table td { border-left: 0; border-right: 0; }


/* ─────────────────────────────────────────────────────────────────────────────
   §8  MOBILE (≤640px) — the board starts above the fold.
   ─────────────────────────────────────────────────────────────────────────────
   MEASURED DEFECT at 375×812: the board's top edge sat at y=601 — 74% of the
   first screen was chrome (view header, Save view, Active/All seg, search, three
   selects, Filters, a 4-up mode seg and New job, each on its own wrapped row)
   before a single job appeared. And there was no responsive rule for `.kanban` or
   `.kanban-col` anywhere in styles.css: the column stayed a hard 260px.

   Every control is KEPT — nothing is hidden on a small screen, the rows just
   become one horizontally-scrollable rail (Notion / Asana iOS pattern). */
@media (max-width: 640px) {
  .jt-view .view-header { margin-bottom: var(--space-3); padding-bottom: var(--space-2); }
  .jt-view .view-title { font-size: var(--fs-h1); }
  .jt-view .view-sub { margin-top: var(--space-1); font-size: var(--fs-meta); }

  .jt-view .toolbar,
  .jt-view .toolbar-more {
    flex-wrap: nowrap;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    margin-bottom: var(--space-2);
    padding-bottom: 2px;
  }
  .jt-view .toolbar::-webkit-scrollbar,
  .jt-view .toolbar-more::-webkit-scrollbar { display: none; }
  .jt-view .toolbar > *,
  .jt-view .toolbar-more > * { flex: 0 0 auto; }
  .jt-view .toolbar .spacer { display: none; }          /* a flex:1 spacer in a scroll rail is dead width */
  .jt-view .toolbar .search-input { min-width: 190px; }

  /* the next column peeks ~16% and the board snaps to it, so the swipe advertises
     itself instead of the column being cut off at an arbitrary pixel */
  .jt-board { scroll-snap-type: x mandatory; }
  .jt-board .kanban-col { flex: 0 0 84vw; scroll-snap-align: start; max-height: max(300px, calc(100vh - 250px)); }
  .jt-board .kanban-col.is-rail { flex: 0 0 48px; }
  .jt-board .kanban-col.is-rail.drag-over { flex: 0 0 84vw; }

  .jt-table-wrap { max-height: max(340px, calc(100vh - 240px)); }

  /* D17 — the table clipped mid-badge with no signal (measured: age/trend chips
     half-cut at the right edge). (a) the scroll-fade above now shows there's more;
     (b) the scroll advertises itself the same way .jt-board already does above;
     (c) un-crowd: the address column gets a floor so it never shrinks to the point
     the badge cluster crowds it out. (SLEEK 08-12, A3 — the order-id hash-chip
     hide that lived here was removed as dead CSS: A1 took the `.jt-order` node off
     the row app-wide, so there is nothing left to hide on a phone.) */
  /* STAGE1-FIX 2026-08-14 (checker 2) — THIS is the "D9 sticky column bleed" the
     design doc lists, and it is a snap bug, not a paint bug. The snapport's start edge
     was the wrap's own left, but the FROZEN gutter cell is pinned over the first 42px
     of it (`left: 0`, and the address is pinned at `left: 42px` — see the FROZEN
     IDENTITY COLUMN block), so `scroll-snap-align: start` parked the board 42px too far
     right and the Client column came to rest UNDERNEATH the frozen address.
     MEASURED at 375x812, Owner, default columns, resting (snapped) scroll position:
     scrollLeft 45.5, address cell [57,217], Client cell [175,297] -> 42px of Client
     hidden behind the address, so "Pamela Vidal" reads "ela Vidal" and the header
     "CLIENT" reads "NT". Note a getBoundingClientRect/hit-test check PASSES here — the
     frozen cell is correctly opaque and correctly on top; only a screenshot (or this
     left-edge arithmetic) shows the half-word. Insetting the snapport by the gutter's
     own 42px re-snaps to scrollLeft 3.5 and Client to [217,339] — 0px hidden, measured,
     with the freeze itself untouched. */
  .jt-table-wrap { scroll-snap-type: x proximity; scroll-padding-left: 42px; }
  /* SPEC-V2TRACKFIT Q1 2026-08-29 — min-width raised 160px → 198px alongside
     the max-width bump just below (same reasoning: the thumbnail box now
     shares this cell with .jt-addr). scroll-padding-left above (42px) matched
     the OLD checkbox-width gutter this rule was written against, when Address
     itself sat in the second FROZEN slot; Address isn't frozen at all now
     (Client is, and it's the true :first-child in the default no-checkbox
     config) — left as-is since it's still a harmless snap-alignment nudge,
     not a correctness-critical pin, and re-deriving it is out of this pass's
     scope. */
  .jt-table-wrap table.table thead th:nth-child(2),
  .jt-table-wrap table.table tbody tr:not(.group-head) > td:nth-child(2) { scroll-snap-align: start; min-width: 198px; }
  /* D17(c) — "collapse services/package columns behind Compact" for THIS app means
     hiding them, view-only, at this breakpoint: App.getTrackerColumns is a synced,
     cross-device user PREFERENCE (App.state.settings.trackerColumns) — writing a
     phone-only default into it would silently change what the same person sees on
     their desktop too. This hides by stable per-key class (jt-col-*, tracker.js
     rth/cell hooks), regardless of the user's own column order, without touching
     that setting. SLEEK 08-12 — `services` is now redundant here: the ≤1599px
     PROGRESSIVE COLLAPSE tier above (see TRACKER-SLEEK 08-12 block) already hides
     it well before this breakpoint, so that line was dropped rather than kept as
     dead CSS. `package` stays: it is out of the default column set (B2 demotion)
     but not locked — a user can re-add it via the ⚙ Display picker, and on a
     phone it should still hide even then. */
  .jt-table-wrap .jt-col-package { display: none; }

  /* ── TRACKER-FROZEN-PHONE 2026-08-13 — the frozen column ate the whole pane ──
     MEASURED DEFECT at 375×812 (headless, focus-emulated, Owner, default
     columns, long-address fixture): the pane is 345px and the two FROZEN cells
     pinned 346px of it. Clear pane: 1px. Scrolled fully right, ALL FOUR data
     columns — Client / Status / Shoot / Due — measured ZERO visible pixels: the
     screenshot at max scroll showed the single header "ADDRESS" and nothing
     else. So on a phone the horizontal scroll the FROZEN IDENTITY COLUMN block
     exists to make cheap bought you literally nothing — you could scroll, but
     the address you were keeping in view was the only thing there was room for.
     Pre-existing 07-30 geometry (the freeze), not caused by the 08-12 sleek
     round; the ≤1179 tier's 280px `.jt-addr` step is already in force here and
     is what makes the cell 304px rather than wider.

     max-width MEETS the min-width the rule above already declares, so the
     frozen address cell is now EXACTLY 160px on a phone at every density
     (measured 160/160/160 for compact/comfortable/big — capping the inner
     `.jt-addr` instead does NOT do this: the div→cell delta is cell padding,
     which is a density preset, so that route measured 160/160/168). The cap is
     on the CELL for that reason, and it lands on 160 rather than a taste value
     because below 160 the min-width above simply wins and nothing changes.
     Clear pane goes 1px → 152/144/136px, which fits the widest data column at
     compact and comfortable. Address text keeps its 90px floor and still
     ellipsises; the full value stays on the cell's title ("address · Order
     <id>") and in the job modal, exactly as the uncapped cell did.

     overflow:hidden is LOAD-BEARING, not tidiness. `.jt-addr > *` is
     `flex: 0 0 auto`, so nothing in the badge cluster can shrink, and
     App.qcStateChip (qc.js) is a whole sentence in a chip — "⚠ shipped past a
     pending review — reason on file", measured 274px. That chip ALREADY
     overflowed the uncapped cell by 137px and, the cell being sticky and
     z-index 2, painted straight over the data columns (confirmed by hit-test,
     not by rect arithmetic — getBoundingClientRect reports layout and cannot
     see clipping). Capping the cell without clipping it would have taken that
     to 289px. Measured after: 10-19 rows still overflow in LAYOUT, zero paint
     over the data at any density. Cost: on a phone the trend/aging chips can be
     clipped where they used to spill — they remain in the job modal, and the
     tags were already the designated yield-first element here.

     KNOWN, ACCEPTED GAP (measured, stated rather than hidden): at `big` density
     the Client cell is 141px against a 136px clear pane, so ~5px of it can sit
     under the frozen edge at the extreme scroll position. 160px cannot be
     tightened to close it — the min-width above is the floor, and lowering that
     would push the address text under its 90px floor and clip the aging chip
     outright. compact and comfortable (the default) have 14-22px of margin.

     SPEC-V2TRACKFIT Q1/Q2 2026-08-29 (owner's morning ruling: design wins) —
     198px, was 160px. Two things changed the math: (1) Address is no longer
     the FROZEN column (Q1 — Client is), so this cap no longer competes
     against the frozen-column mobile budget at all; widening it only grows
     the SCROLLABLE region, never re-opens the "frozen pair ate the pane"
     defect this block was originally about. (2) the cell gained a 30px
     thumbnail box + gap (Q2, ~38px) as a FLEX SIBLING of `.jt-addr`, which
     was otherwise shrinking `.jt-addr`'s own share of the still-160px cell —
     MEASURED: the remaining exception chip (qcOverride, a whole sentence)
     went from whole-or-nothing to a genuine 110-of-179px PARTIAL cut, exactly
     the defect this whole block exists to prevent. +38px restores `.jt-addr`
     its original working width alongside the new thumbnail. */
  .jt-table-wrap table.table thead th:nth-child(2),
  .jt-table-wrap table.table tbody tr:not(.group-head) > td:nth-child(2) { max-width: 198px; overflow: hidden; }

  /* ── TRACKER-PHONE-BADGES 2026-08-13 — the clip above cut chips mid-glyph ───
     The block directly above is right that the clip is load-bearing, and it
     stays. This is the other half of it: a HARD CLIP has no opinion about WHERE
     it lands, so on a phone it landed inside a chip.

     MEASURED BEFORE, 375x812 headless (focus-emulated, Owner, default columns,
     density-comfortable, first-run welcome dismissed), worst-case row = long
     unit-bearing LA address + stale statusSince + growth plays + an open-red
     delivery + qcOverride + 3 tags. `.jt-addr` content 370px in a 136px box:
         .jt-addr-text  90px (its floor)  → 90px drawn, ellipsised   ✓
         aging chip "24d"      34px       → 34px drawn                ✓
         growth chip "GRAPH 6" 37px       → *** 2px drawn ***         ✗
         delivered-open-red   179px       → 0px
         qc override chip     274px       → 0px  (the 274px sentence)
         tag chips              -         → 0px
     That 2px is the defect: a 2px vertical slice of a rounded pill, sitting at
     the frozen edge, reading as damage rather than as truncation. Note what the
     rest of the column says — at 375px the cell has ~38px of slack after the
     address's 90px floor, and the cluster wants 524px. Four of the six items
     were ALREADY invisible before this block; only the boundary item was cut.

     WHY NOT `min-width: 0` + ellipsis (the obvious fix, measured and rejected).
     Flexbox shrinks proportionally to base-size, so handing the cluster 38px of
     space for 524px of content shrinks EVERYTHING to ~7%. Measured, same row:
     the aging chip went 34px -> 14px (i.e. the one chip that reads correctly
     today became a cut chip), growth 37 -> 12, delivered 179 -> 12, qc -> 0.
     Six sub-20px stubs instead of one 2px stub is a worse picture, not a
     better one, and `text-overflow: ellipsis` cannot rescue it: `.chip` and
     `.age-badge` are `display: inline-flex`, and ellipsis does not apply to a
     flex container's content. Ordered shrink (escalating `flex-shrink` down the
     cluster) measured identically — it also has to eat the aging chip before it
     reaches the two chips that actually overflow.

     WHAT THIS DOES INSTEAD — all-or-nothing, by DOM order. `.jt-addr` wraps, and
     the wrapped line is pushed 100px down and clipped away. A flag therefore
     either fits on line 1 and is drawn WHOLE, or it is not drawn at all; there
     is no state in which part of one is on screen. The yield order falls out of
     the existing DOM order (aging, growth, open-red, qc, tags) with no new
     classes and no JS change, and it already agrees with the block above: tags
     yield first. `flex-basis: 90px` on the text is what makes this adaptive —
     flex breaks lines on UNFLEXED sizes, so with the default `auto` basis a
     465px address claims line 1 by itself and every flag wraps away even when
     one would have fitted (measured: that variant lost the aging chip too).
     Pinning the basis to the floor the rule already declares lets the flags
     compete for the real remainder, and the text still GROWS into whatever the
     flags leave (`flex-grow: 1` is untouched).

     `padding-right: 0` is not cosmetic. `overflow: hidden` clips at the cell's
     PADDING box, so content already paints through that 12px and is cut at
     160px — the right padding was never a barrier, it only made flex break
     lines 12px earlier than the clip actually falls. Zeroing it aligns the two,
     which is exactly what buys the aging chip its place at every density.

     MEASURED AFTER, same row: address 100px (was 90 — the flags' dead space
     goes back to the address), aging chip 34px WHOLE, everything else 0px.
     Partially-drawn flags: 0 at compact, comfortable AND big (was 2/2/0). Row
     heights unchanged: 23/41/65 across the three densities.

     THE ONE MAGIC NUMBER, swept rather than guessed. `max-height` has to clear
     line 1 (or the clip cuts a horizontal BAND off the chips — the same defect
     rotated 90 degrees) and stay under the row's content box (or rows grow and
     the UI-BOARD 07-30 density contract breaks). Measured pass windows:
         compact      17-20px   (16 clips 0.8px; 21 grows rows 23 -> 24)
         comfortable  21-24px   (20 clips 1px;   25 grows rows 41 -> 42)
         big          23-33px   (22 clips 0.4px; row has 33px of content box)
     There is no value common to all three, so it is per density, each picked
     two off its floor. The 100px row-gap means the upper end is only ever about
     row height — no value in these windows can leak a sliver of line 2. Both
     bounds are asserted in tests/suite.js (FROZEN-PHONE-BADGES 2026-08-13), so
     a future token change that moves a line box fails loudly instead of
     quietly cutting chips again.

     ACCEPTED COST, stated not hidden: on a phone a row with an open QC override
     or an open-red delivery now looks exactly like a clean row in this cell.
     That is NOT a regression — both chips measured 0 visible px before this
     block too — but it is not a fix either. Both remain one tap away in the job
     modal (tracker.js field('Status', ... App.qcStateChip)), which is where the
     block above already sends them. The invariant that block establishes is
     untouched and re-hit-tested here: nothing from the frozen cell paints over
     the data columns. */
  .jt-table-wrap table.table tbody tr:not(.group-head) > td:nth-child(2) { padding-right: 0; }
  .jt-table-wrap .jt-addr { flex-wrap: wrap; align-content: flex-start; row-gap: 100px; max-height: 23px; overflow: hidden; }
  .jt-table-wrap.density-compact .jt-addr { max-height: 19px; }
  .jt-table-wrap.density-big .jt-addr { max-height: 25px; }
  .jt-table-wrap .jt-addr-text { flex-basis: 90px; }
}


/* ##########################################################################
   TASKS — surface restyle (tasks.js). scratchpad/css-tasks.css
   Merged by the INTEGRATOR pass, 2026-07-30. Source: builder scratch file.
   ########################################################################## */
/* ============================================================================
   TASKS — surface restyle · 07-30
   Ships with the tasks.js changes of the same date. tasks.js references every
   .tk-* class below, so the two MUST land in the same pass — until this block
   is merged the board renders unstyled-but-functional.

   NO new CSS variables. Every value is an existing design token, so both themes
   are covered by the html[data-theme="dark"] overrides already in :root.

   SCOPING NOTE — the Tasks board reuses the app-wide .kanban / .kanban-col /
   .kanban-card / .toolbar classes, which leads.js, tracker.js and
   postpipeline.js also use. Every rule here is therefore gated behind a
   Tasks-only ancestor or companion class (.tk-board, .tk-cards-zone, .tk-zone,
   .tk-card, .tk-toolbar) so NOTHING leaks onto the other boards.

   INTEGRATOR NOTE (out of my file ownership, do not silently skip):
   styles.css:653 `.kanban-card.cv-card { contain-intrinsic-size: auto 140px }`.
   Tasks cards do NOT carry .cv-card (only tracker.js adds it), so this pass does
   not invalidate that estimate — no change needed. Flagged only so the next
   reader does not re-derive it.
   ============================================================================ */

/* ---------------------------------------------------------------------------
   1 · DUE-BUCKET GROUP HEADERS
   The urgency colour lives HERE, on one header, instead of on every row
   (Jobber). Only .is-late is inked; every other bucket is quiet --muted.
   A plain div, not a heading — it must not inject a level into the view's
   h1 -> h2 outline.
   --------------------------------------------------------------------------- */
.tk-bucket-head {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-micro); font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label); text-transform: uppercase;
  color: var(--muted);
  padding: var(--space-3) 2px var(--space-1);
}
.tk-bucket-head:first-child { padding-top: var(--space-1); }
.tk-bucket-label { flex: 1 1 auto; min-width: 0; }
.tk-bucket-count {
  flex: 0 0 auto; letter-spacing: var(--ls-body); font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums; color: var(--faint);
}
.tk-bucket-head.is-late { color: var(--danger-ink); }
.tk-bucket-head.is-late .tk-bucket-count { color: var(--danger-ink); opacity: .85; }

/* Cards mode: the header spans the full grid width above its own sub-grid */
.tk-cards-zone .tk-bucket-head { padding: var(--space-5) 2px var(--space-2); }
.tk-cards-zone .tk-bucket-head:first-child { padding-top: 0; }

/* ---------------------------------------------------------------------------
   2 · COLUMNS — no panel fill; the head rule + bucket headers carry structure
   In dark this also removes the measured 1.02:1 card-vs-column collapse at its
   source (both were near-identical dark greys).
   --------------------------------------------------------------------------- */
.tk-board .kanban-col {
  background: transparent;
  border-radius: 0;
  padding: 0 var(--space-1) 0 0;
  flex: 0 0 300px;   /* was 260px — holds the 14px title on two lines, not three */
}
.tk-board .kanban-col-head {
  border-bottom: 1px solid var(--border);
  padding: 2px 2px var(--space-2);
  margin-bottom: 2px;
}
/* the drop target must keep its resting silhouette back while dragging — a
   transparent column has none, so restate it at higher specificity than the
   transparent rule above (both are two-class selectors; order alone is fragile) */
.tk-board .kanban-col.drag-over {
  background: var(--accent-soft);
  border-radius: var(--radius);
  outline: 2px dashed var(--accent);
  outline-offset: -2px;
  padding: 0 var(--space-1);
}

/* empty column / overflow lines — ONE empty voice on this surface (was three) */
.tk-col-empty {
  font-size: var(--fs-meta); color: var(--faint);
  text-align: center; padding: var(--space-4) 0;
}
.tk-col-more {
  font-size: var(--fs-meta); color: var(--muted);
  text-align: center; padding: var(--space-2) 0 0;
}

/* ---------------------------------------------------------------------------
   3 · THE CARD — flex row: tick + a min-width:0 content column
   (a grid pushed the title below the tick's baseline at narrow widths)
   --------------------------------------------------------------------------- */
.kanban-card.tk-card {
  position: relative;
  border-color: var(--border-strong);   /* dark edge definition 1.23:1 -> 1.76:1 */
  padding: var(--space-3) var(--space-3) 11px;
}
.kanban-card.tk-card:focus-visible {
  outline: 2px solid var(--accent); outline-offset: var(--focus-offset);
}
.tk-row { display: flex; align-items: flex-start; gap: var(--space-3); }
.tk-body { flex: 1 1 auto; min-width: 0; }

/* the title is finally its own type step: +1px over the column head, +1.5px
   over card body, and no longer forced to win on weight alone */
.kanban-card.tk-card .kc-title {
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  color: var(--text);
  margin-bottom: 2px;
  padding-right: 20px;   /* clears the absolutely-positioned bulk checkbox */
}

/* ---------------------------------------------------------------------------
   4 · THE LEADING TICK — completes the task (same meaning as Today's checkbox)
   19px visually, 33px hit area via the ::before overlay so the real target
   clears comfortably without changing the card's layout metrics.
   --------------------------------------------------------------------------- */
.tk-tick {
  position: relative; flex: 0 0 auto;
  width: 19px; height: 19px; margin-top: 1px; padding: 0;
  border: 1.5px solid var(--border-strong); border-radius: 999px;
  background: transparent; color: transparent;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: border-color var(--t-fast) var(--ease),
              background var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.tk-tick::before { content: ''; position: absolute; inset: -7px; border-radius: 999px; }
.tk-tick svg { width: 12px; height: 12px; }
.tk-tick:hover:not(:disabled),
.tk-tick:focus-visible { border-color: var(--ok); color: var(--ok); }
.tk-tick:focus-visible { outline: 2px solid var(--accent); outline-offset: var(--focus-offset); }
.tk-tick:disabled { cursor: default; }

/* ---------------------------------------------------------------------------
   5 · DONE CARDS RECEDE — a FILLED tick instead of a strikethrough.
   Strikethrough + dimming double-encodes and costs legibility; a filled marker
   is what Attio/Linear use. Measured: done title 6.32:1 light / 6.00:1 dark.
   --------------------------------------------------------------------------- */
.kanban-card.tk-card.is-done {
  background: var(--subtle);
  box-shadow: none;
  border-color: var(--border);
}
.kanban-card.tk-card.is-done:hover { box-shadow: var(--shadow-sm); }
.kanban-card.tk-card.is-done .kc-title {
  color: var(--muted); font-weight: var(--fw-medium);
}
/* --on-danger is this design system's ONLY "ink that sits on a saturated fill"
   token, and it flips correctly for this use (#fff on light --ok #079455;
   #2a0a08 on dark --ok #3ccb7f). Named for its first caller, not its scope. */
.kanban-card.tk-card.is-done .tk-tick {
  border-color: var(--ok); background: var(--ok); color: var(--on-danger);
}

/* ---------------------------------------------------------------------------
   6 · META ROW — assignee avatar, monochrome kind glyph, three-tier due
   --------------------------------------------------------------------------- */
.tk-asg {
  display: inline-flex; align-items: center; gap: 5px; min-width: 0;
  font-size: 11.5px; color: var(--muted);
}
.tk-asg .avatar { flex: 0 0 auto; }
.tk-asg.is-unassigned { opacity: .65; }

.tk-kind { display: inline-flex; align-items: center; color: var(--faint); }
.tk-kind svg { width: 13px; height: 13px; }

.tk-due {
  display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: 11.5px; color: var(--muted);
}
/* --danger-ink / --warn-ink are the TEXT reds/ambers per the token comments
   (--danger / --warn stay fill+stroke). Measured on the card in light:
   4.83:1 -> 6.57:1 late, 5.43:1 today. Scoped classes, so the GLOBAL `.overdue`
   rule (shared with .pl-next.overdue and .tss-pulse-cell.overdue) is untouched. */
.tk-due.is-late { color: var(--danger-ink); font-weight: var(--fw-bold); }
.tk-due.is-today { color: var(--warn-ink); font-weight: var(--fw-bold); }

/* ---------------------------------------------------------------------------
   7 · JOB / CLIENT LINK — street only, quiet until hovered
   --------------------------------------------------------------------------- */
.kanban-card.tk-card .task-link {
  display: flex; align-items: center; gap: 5px;
  margin-top: 7px; font-size: 11.5px;
  color: var(--muted); text-decoration: none;
  transition: color var(--t-fast) var(--ease);
}
.kanban-card.tk-card .task-link .tk-link-text {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kanban-card.tk-card .task-link:hover { color: var(--accent); text-decoration: underline; }
.kanban-card.tk-card .task-link:focus-visible {
  outline: 2px solid var(--accent); outline-offset: var(--focus-offset);
  border-radius: var(--radius-sm);
}

/* the one-tap auto-task action, demoted off btn-primary in tasks.js */
.kanban-card.tk-card .tk-action { font-size: var(--fs-micro); padding: 5px 10px; }

/* ---------------------------------------------------------------------------
   8 · BULK SELECT MOVES TO HOVER (Linear) so it stops competing with the tick
   Kept permanently visible on TOUCH (no hover state exists there) and whenever
   a selection is already active — losing bulk-select on mobile would be a
   regression, not a simplification.
   --------------------------------------------------------------------------- */
.kanban-card.tk-card .kc-sel {
  position: absolute; top: var(--space-2); right: var(--space-3);
  float: none; margin: 0; z-index: 1;
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease);
}
.kanban-card.tk-card:hover .kc-sel,
.kanban-card.tk-card:focus-within .kc-sel,
.kanban-card.tk-card .kc-sel:focus-visible,
.kanban-card.tk-card .kc-sel:checked,
.tk-zone.has-sel .kanban-card.tk-card .kc-sel { opacity: 1; }
/* D16 — on a touch device wider than 640px (iPad etc.) there's room for both the
   tick and the select box to read as distinct controls, so the old "always visible
   on touch" behavior stays. ≤640px is the phone case the audit flagged: two
   identical empty squares. There the select box stays hidden until selection mode
   starts (first long-press — tasks.js — or a selection already active), matching
   the desktop hover-to-reveal behavior instead of showing both at rest. */
@media (hover: none) and (min-width: 641px) {
  .kanban-card.tk-card .kc-sel { opacity: 1; }
}

/* ---------------------------------------------------------------------------
   9 · TOOLBAR WIDTH CAPS (moved off inline styles in tasks.js so the mobile
   rule below can actually release them)
   --------------------------------------------------------------------------- */
.toolbar.tk-toolbar .tk-sel { max-width: 190px; }

/* ---------------------------------------------------------------------------
   10 · MOBILE — one-line toolbar, one column per swipe
   Measured on the prototype at 375x812: toolbar 191px -> 52px,
   first-card-top 463px -> 351px.
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
  .toolbar.tk-toolbar {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .toolbar.tk-toolbar::-webkit-scrollbar { display: none; }
  .toolbar.tk-toolbar > * { flex: 0 0 auto; }
  /* the "Assignee / Status / Tag" text labels are redundant with each select's
     own visible value; tasks.js gives every select an aria-label + title so the
     accessible name survives them being hidden */
  .toolbar.tk-toolbar > .tk-fl { display: none; }
  .toolbar.tk-toolbar .spacer { display: none; }
  .toolbar.tk-toolbar .tk-sel { max-width: none; }

  .tk-board { scroll-snap-type: x mandatory; }
  .tk-board .kanban-col { flex: 0 0 88%; scroll-snap-align: start; }
}

/* ---------------------------------------------------------------------------
   11 · MOTION GUARD
   (the existing styles.css:2634 reduced-motion rule already covers .kanban-card
   itself; these are the elements this pass added)
   --------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .tk-tick,
  .kanban-card.tk-card .kc-sel,
  .kanban-card.tk-card .task-link { transition: none; }
}
/* ===================== end TASKS restyle 07-30 ===================== */


/* ##########################################################################
   CALENDAR / SCHEDULE — visual pass (calendar.js). scratchpad/css-calendar.css
   Merged by the INTEGRATOR pass, 2026-07-30. Source: builder scratch file.
   ########################################################################## */
/* ============================================================================
   CALENDAR / SCHEDULE — visual pass, 07-30                 (calendar.js only)
   ----------------------------------------------------------------------------
   NO NEW CSS VARIABLES. Every value below is an existing design token, so this
   block themes itself through the html[data-theme="dark"] overrides already in
   :root. No raw hex, no !important, no new colour system.

   READ THIS FIRST — WHY EVERY SELECTOR STARTS WITH `.calview`:
   `.cal-grid`, `.cal-cell`, `.cal-dow` and `.cal-more` are each declared TWICE
   in styles.css — once in the Calendar block (~1010) and again, LATER and
   therefore winning, in the SPEC223 Tasks-view calendar block (~4299, owned by
   core.js). Measured proof of the collision on the live DOM: this view's
   `.cal-cell` computed to border-radius 8px (the Tasks value) instead of
   var(--radius-sm)=7px, and padding 5px instead of 5px 6px. So a rule written
   into the Calendar block for `.cal-cell` was silently overridden, and any
   change made there bled into the Tasks calendar.
   calendar.js's render() now wraps the whole surface in `.calview`, and every
   rule here is `.calview .cal-*` — specificity (0,2,0) beats the Tasks block's
   (0,1,0) regardless of source order, and nothing leaks the other way.
   Nothing here touches schedule.js's board either: `.sched-off` / `.sched-busy`
   are retuned only INSIDE `.calview`.

   ONE IDIOM WORTH NAMING: the filled "today" date pill uses `var(--bg)` as its
   ink on a `var(--accent)` fill. --chip-ink is exactly backwards for this job
   (light theme = near-black ink on a DARK accent; dark theme = white ink on a
   LIGHT accent — both fail). --bg is guaranteed to sit on the opposite side of
   --accent in both themes: measured 6.12:1 in light (#f6f6f4 on #2b608f) and
   5.36:1 in dark (#101722 on #5f92b4). Both clear AA.
   ========================================================================= */

/* ---- grid + cells --------------------------------------------------------
   The single worst measured defect on this surface lived here: `.cal-cell`
   was max-height:150px / overflow-y:auto while each chip carried an inline
   min-height:44px. On a 6-shoot Thursday that clipped 41px — the operator saw
   3 shoots and the "+3 more" row that exists to announce the other 3 sat at
   top=167px inside a 148px box, i.e. BELOW THE FOLD, and macOS overlay
   scrollbars gave no visual cue at all. 6 of 35 cells overflowed this way.
   The cell no longer scrolls; the chip is one line; four fit. */
.calview .cal-grid { align-items: start; }
.calview .cal-cell {
  min-height: 104px;
  max-height: none;
  overflow: visible;                    /* also stops clipping a chip's focus ring */
  /* REQUIRED, not cosmetic. A grid item's automatic minimum size is min-content, and
     that only stops applying when the item's overflow is NOT visible — so the old
     `overflow-y:auto` was the ONLY thing keeping these columns equal. Restoring
     `overflow:visible` without this made the seven 1fr columns resolve to
     201/151/172/222/173/209/198px and pushed 213px of horizontal overflow onto #main. */
  min-width: 0;
  border-radius: var(--radius-sm);
  padding: var(--space-1) 6px 5px;
  gap: 2px;
}
.calview .cal-dow > div { min-width: 0; }

/* Out-of-month cells: recede via SURFACE, not via opacity on the whole cell.
   The old `opacity:.6` dimmed the day number below AA, and would have compounded
   with the new `.past` chip dim (.6 x .6 = .36). Split so neither stacks. */
.calview .cal-cell.other-month { opacity: 1; background: var(--subtle); }
.calview .cal-cell.other-month .cal-daynum { color: var(--faint); }

/* Let the past recede. Measured before: all 27 week chips carried IDENTICAL
   weight (11px / 600 / 13.3% tint / 3px stripe), so a shoot completed 24 days
   ago read exactly as loudly as one happening tomorrow — nine hues at one alpha
   were carrying 100% of the differentiation. Dim the CHIPS only, never the
   cell, so day numbers stay AA. Future days are deliberately untouched. */
.calview .cal-cell.past .cal-event,
.calview .cal-cell.other-month .cal-event { opacity: .6; }

/* --fw-semibold, not --fw-bold: 34 day numbers at 700 were a third of the whole
   surface's bold count (measured 98 elements at 700 across the calendar subtree —
   when nearly everything is bold, nothing is). Today's number is the one that
   escalates, to --fw-black inside its accent pill below. */
.calview .cal-daynum {
  font-size: var(--fs-micro);
  font-weight: var(--fw-semibold);
  color: var(--muted);
  line-height: var(--lh-tight);
  letter-spacing: 0;
}

/* ---- weekday header ------------------------------------------------------ */
.calview .cal-dow {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  margin-bottom: var(--space-1);
  padding: 0;
}
.calview .cal-dow > div {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 5px;
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  color: var(--faint);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  text-align: center;
  padding: 2px 0 4px;
}
/* the per-day load figure (ClickUp / Fresha). Rendered only when > 0 — a "0" in
   every empty column is noise, and the empty cell below already says it. */
.calview .cal-dowcount {
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  color: var(--muted);
  letter-spacing: 0;
  text-transform: none;
  font-variant-numeric: tabular-nums;
}

/* ---- TODAY ---------------------------------------------------------------
   Measured before: today's cell and a random past cell had IDENTICAL background
   (#fff) and IDENTICAL border colour (#e4e7ec); today's weekday HEADER cell was
   11px/700/--faint — exactly the same as the other six. The only two deltas in
   the whole 35-cell grid were a 2px outline and the day number's colour, and the
   eye landed on a red "Stuck" chip long before it found today. Now: accent
   header + tinted surface + a filled date pill, and the ring gets QUIETER
   because it no longer has to do the work alone. */
.calview .cal-dow > div.today { color: var(--accent); font-weight: var(--fw-black); }
.calview .cal-dow > div.today .cal-dowcount { color: var(--accent); }
.calview .cal-cell.today {
  background: var(--accent-soft);
  outline: 1.5px solid var(--accent);
  outline-offset: -1.5px;
}
/* the pill is a MONTH affordance; in Week the tinted column + accent header
   carry it, and the week cell's day label is a full "Thu Jul 30" agenda header
   that a 20px circle would mangle. */
.calview .cal-cell:not(.cal-cell-week).today .cal-daynum {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--bg);            /* see the contrast note in the banner */
  font-size: var(--fs-micro);
  font-weight: var(--fw-black);
}
/* Craft's phrasing — the day header stays plain and only the word "Today" is
   accented. Cheapest possible today-emphasis for an agenda row. */
.calview .cal-today-tag { color: var(--accent); font-weight: var(--fw-black); }

/* ---- the shoot chip, density 1 of 2: MONTH ------------------------------
   `● 8a  1420 Example Canyon Dr` on one ~18px line. No left edge, no 44px
   floor — the touch target is the cell, which is already role=button via
   App.rowActivate and is >=104px tall. This is what turns 3-visible-of-6 into
   4-visible plus an always-visible "+2 more".
   SPEC-DHCAL D2 2026-08-27 — "Month: shoot chips tinted per shooter": the
   `background: transparent` below is now the graceful NO-JS fallback only —
   calendar.js's eventChip sets an inline App.tintOf(App.crewColor(...), .12)
   fill on every dense chip, which (being inline) always wins over this rule.
   The leading dot stays the STATUS colour (unchanged) — one channel per
   question, "whose shoot" (fill) vs. "what stage" (dot). */
.calview .cal-event--dense {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 0;
  padding: 1px 3px;
  border-left: 0;
  background: transparent;
  border-radius: 4px;
  line-height: var(--lh-snug);
  white-space: nowrap;
}
/* SPEC-DHCAL D2 — was `filter:none; background:var(--hover)` (a transparent chip
   has nothing for brightness() to act on, so hover became a surface swap
   instead). Now that every dense chip carries a real tinted fill, the base
   `.cal-event:hover { filter: brightness(.97) }` rule (styles.css ~1274) has
   something to act on again — this override just deepens it slightly for the
   smaller dense chip, rather than fighting the inline tint with a flat swap. */
.calview .cal-event--dense:hover { filter: brightness(.94); }
.calview .cal-ev-dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  flex: 0 0 auto;
}
.calview .cal-event--dense .cal-ev-time {
  flex: 0 0 auto;
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.calview .cal-event--dense .cal-ev-street {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: var(--fs-micro);
  font-weight: var(--fw-semibold);
  color: var(--text);
}

/* ---- the shoot chip, density 2 of 2: WEEK / DAY COLUMNS ------------------
   Tinted body + solid edge (the Rise posture). SPEC-DHCAL D1 2026-08-27 — the
   accent is now the SHOOTER's colour (App.crewColor via App.tintOf, calendar.js
   eventChip), not the status colour; status stays visible via the explicit chip
   in dayJobRow / the job popup, nothing status-related was removed here. Three
   lines, because one line measurably could not do it: 27 of 27 week chips
   truncated, the widest at 376px of label inside a 102px column, and the
   photographer name the old `withWho` concatenated was NEVER once visible at any
   viewport. Line 1 carries the clock, the weather and the crew AVATAR; line 2
   gets the full street on up to two lines instead of one ellipsed sliver; line 3
   (`.cal-ev-sub`, new this wave) is the agent + services sub-line, only when
   either is on the job. */
.calview .cal-event:not(.cal-event--dense) {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-height: 34px;
  padding: 3px 7px 4px;
  border-radius: 6px;
  white-space: normal;
  text-overflow: clip;
}
.calview .cal-ev-top {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  min-height: 15px;
}
.calview .cal-ev-top .cal-ev-time {
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.calview .cal-ev-av { flex: 0 0 auto; margin-left: auto; }
.calview .cal-event:not(.cal-event--dense) .cal-ev-street {
  font-size: var(--fs-micro);
  font-weight: var(--fw-semibold);
  color: var(--text);
  line-height: var(--lh-snug);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* SPEC-DHCAL D1 — the agent + services sub-line (mockup's block anatomy). Shared
   between eventChip's Week/Day chips and dayJobRow's agenda row — one class, one
   look, in both places it appears. Recedes below the street line (which stays
   the loudest text in the block); a single line, ellipsed rather than wrapped,
   so it never pushes a chip's height around. */
.calview .cal-ev-sub {
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  color: var(--muted);
  line-height: var(--lh-snug);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---- "+N more" -----------------------------------------------------------
   margin-top:auto pins it to the bottom of a cell that no longer scrolls, so the
   one row announcing the hidden shoots is always the last VISIBLE line. */
.calview .cal-more {
  margin-top: auto;
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  color: var(--muted);
  padding: 2px 3px 0;
  letter-spacing: 0;
  background: transparent;
  border-radius: 4px;
}
.calview .cal-more:hover,
.calview .cal-more:focus-visible { color: var(--accent); background: transparent; }

/* ---- weather chip (SPEC21 §C) — token sizes, unchanged behaviour --------- */
.calview .cal-wx {
  margin-left: 0;
  font-size: var(--fs-micro);
  font-weight: var(--fw-bold);
  color: var(--muted);
}
.calview .cal-event .cal-wx { color: var(--muted); opacity: 1; }

/* ---- WEEK ----------------------------------------------------------------
   Measured before: all 7 columns were exactly 370px tall regardless of content,
   because `max-height:420px` + a stretch grid let Wednesday's 7 shoots set the
   row height — Sunday held ONE shoot in 370px, ~300px of it empty white. With
   `align-items:start` on the grid each column is now as tall as its own day. */
.calview .cal-cell-week {
  min-height: 120px;
  max-height: none;
  padding: 6px;
  gap: 3px;
}
/* The header cell one row up already says "MON 27", so this in-cell label was a
   duplicated 17px line x 7. Hidden above 700px EXCEPT on the 1st — the month
   boundary a Jul 27 – Aug 2 week genuinely needs — and shown at every width
   below, where it becomes the stacked agenda's day header. */
.calview .cal-cell-week > .cal-daynum { display: none; }
.calview .cal-cell-week > .cal-daynum.is-first {
  display: block;
  color: var(--text);
  font-weight: var(--fw-black);
}

/* ---- blocked time: DEMOTED below the work it constrains ------------------
   Measured before: `.sched-off` was 11px / 700 / colour --ok on --ok-soft with a
   dashed --ok border, while every actual shoot was 600 at a 13% tint. So
   "Jeffrey off" outranked every real job on the board, and in dark mode it was
   the single brightest element on screen. --ok is the semantic SUCCESS colour; a
   teammate being unavailable is a CONSTRAINT, not a success. Retuned INSIDE
   .calview only — schedule.js's own board keeps its existing treatment. */
.calview .sched-off {
  align-self: flex-start;
  max-width: 100%;
  display: inline-flex;
  align-items: center;
  font-size: var(--fs-micro);
  font-weight: var(--fw-semibold);
  color: var(--muted);
  background: var(--subtle);
  border: 1px dashed var(--border);
  border-radius: 5px;
  padding: 2px 6px;
  margin-bottom: 0;
}
/* `white-space:nowrap` ellipsed "Vincent: 12p–1p Dentist" down to "12p–…" in a
   102px column — the label was 100% chrome and 0% information. It wraps now. */
.calview .sched-busy {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  display: flex;
  align-items: flex-start;
  gap: 3px;
  font-size: var(--fs-micro);
  line-height: var(--lh-snug);
  margin-bottom: 0;
}
.calview .sched-busy svg { flex: 0 0 auto; margin-top: 2px; }

/* ---- DAY view: the time gutter it never had -----------------------------
   The one mode with unlimited width was the only mode showing no clock value at
   all. Fixed 62px right-aligned gutter (Outlook iOS / Binance), then a status
   dot, then address + crew. The em-dash here is the HONEST case: it means the
   job genuinely has neither a shoot time nor an arrival window. */
.calview .cal-dayrow {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 10px 4px;
}
.calview .cal-daytime {
  flex: 0 0 62px;
  text-align: right;
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  color: var(--text);
  line-height: var(--lh-snug);
  font-variant-numeric: tabular-nums;
}
.calview .cal-daytime--none { color: var(--faint); font-weight: var(--fw-semibold); }
.calview .cal-dayedge { width: 8px; height: 8px; }
.calview .cal-dayaddr {
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text);
  line-height: var(--lh-snug);
}
/* the '📷 Vincent  ·  ✂ Ava' emoji sub-line becomes the same avatar the rest of
   the app uses for a teammate (SPEC208 line-icon posture). */
.calview .cal-daywho {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 4px;
  font-size: var(--fs-meta);
  font-weight: var(--fw-medium);
  color: var(--muted);
}
.calview .cal-daywho .avatar { flex: 0 0 auto; }
.calview .cal-daywho .avatar + span { margin-right: 6px; }

/* ---- PER-PERSON lanes (admin Week/Day) -----------------------------------
   These were inline style objects in calendar.js, which is precisely why a lane
   could not respond to the viewport: measured lane heights of 92 / 92 / 103 /
   179 / 197px for 1 / 2 / 4 / 11 / 12 chips, with a hard 180px floor under a
   one-shoot lane. The gutter also showed a colour bar plus a dot in the SAME
   colour — redundant — and carried no load figure. Now: avatar + name + count
   (Fresha / Motion). A COUNT only: no hours, no money, so App.canSeeMoney() is
   not implicated. Only borderLeftColor stays inline — that is per-person DATA. */
.calview .cal-lane {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  margin-bottom: var(--space-2);
}
.calview .cal-lane-head { margin-bottom: var(--space-1); }
.calview .cal-lane-name {
  flex: 0 0 138px;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: 7px var(--space-2);
  border-left: 3px solid var(--muted);
  background: var(--subtle);
  border-radius: var(--radius-sm);
}
.calview .cal-lane-name--ghost {
  background: transparent;
  border-left-color: transparent;
  padding: 0;
}
.calview .cal-lane-id { min-width: 0; }
.calview .cal-lane-who {
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
  color: var(--text);
  line-height: var(--lh-snug);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.calview .cal-lane-load {
  font-size: var(--fs-micro);
  font-weight: var(--fw-semibold);
  color: var(--muted);
  line-height: var(--lh-snug);
  font-variant-numeric: tabular-nums;
}
.calview .cal-lane-strip { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
.calview .cal-cell-lane { min-height: 64px; }

/* per-person DAY: side-by-side columns, scrolling inside their OWN container so
   the page body never scrolls sideways (unchanged behaviour, now in classes). */
.calview .cal-daycols {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  overflow-x: auto;
  padding-bottom: var(--space-1);
}
.calview .cal-daycol {
  flex: 0 0 210px;
  min-width: 190px;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.calview .cal-daycol-title { margin-bottom: var(--space-3); }

/* ---- toolbar ------------------------------------------------------------- */
.calview .cal-count { font-size: var(--fs-meta); }
/* SPEC-DHCAL D2 — Prev/Today/Next joined into one segment/pill group (the
   Owner prototype's own date-nav — design-handoff/VVT-Hub-Owner.dc.html
   ~line 1285), same idea as the mode toggle's `.seg` a border-shared trio
   rather than three loose `.btn.btn-sm` with gaps between them. Radii only
   round the outer corners; the shared inner border comes from dropping the
   left border on every button after the first (no negative-margin overlap
   hack needed — `.btn` borders are already 1px solid var(--border-strong)
   on all four sides, so removing just the left edge leaves one clean line). */
.calview .cal-nav { display: inline-flex; }
.calview .cal-nav .btn { border-radius: 0; }
.calview .cal-nav .btn + .btn { border-left: 0; }
.calview .cal-nav .btn:first-child { border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm); }
.calview .cal-nav .btn:last-child { border-top-right-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm); }

/* ---- <= 700px: stop pretending 7 equal columns fit ----------------------
   Measured before at 375px: a week `.cal-cell` was 44px wide, its chip 32x44px,
   and the whole label rendered as "08:001" — a truncated TIME and nothing else,
   x27 shoots. Month was the same ("08:0(", "16:0").
   Both fixes are CSS-only ON PURPOSE. The alternative — a second JS render path
   gated on matchMedia — would have duplicated dayJobRow, and would have changed
   what the view produces at narrow widths, which the existing permission suite
   asserts on via textContent. Nothing here changes the DOM.
     WEEK  -> the 7-col strip restacks into a day-grouped agenda: the shared
              header row drops out and each cell's own "Thu Jul 30 · Today"
              label (already rendered, hidden above 700px) becomes its header.
     MONTH -> keeps its 7 columns, because it still works as an overview and a
              date picker, but each chip collapses to its dot + time — a 46px
              cell demonstrably cannot hold a street. Tapping the cell opens the
              Day view via App.rowActivate, which is where the detail lives. */
@media (max-width: 700px) {
  .calview .cal-dow-week { display: none; }
  .calview .cal-grid-week { grid-template-columns: 1fr; gap: var(--space-2); }
  .calview .cal-cell-week { min-height: 0; padding: var(--space-2); }
  .calview .cal-cell-week > .cal-daynum {
    display: block;
    font-size: var(--fs-caption);
    font-weight: var(--fw-black);
    color: var(--text);
    text-transform: none;
    letter-spacing: 0;
    padding-bottom: 3px;
  }
  /* full-width rows have room again — restore a 44px touch target */
  .calview .cal-event:not(.cal-event--dense) { min-height: 44px; justify-content: center; }
  .calview .cal-event:not(.cal-event--dense) .cal-ev-street { -webkit-line-clamp: 1; }

  /* MONTH -> density dots (Todoist / Midday). Measured at 375px: a month cell is 44px
     wide, and a chip carrying a half-hour time spills 5–12px past it on 6 of 32 shoots
     ("11:30a" needs ~48px). Ellipsing a clock value would be a LIE about the time, so
     the time comes off entirely and the row of dots becomes the density signal. Each dot
     keeps its own title/aria-label and opens its job; the CELL is the primary target and
     opens the Day view, which is where every detail lives at this width. */
  .calview .cal-cell:not(.cal-cell-week) {
    flex-direction: row;
    flex-wrap: wrap;
    align-content: flex-start;
    align-items: center;
    min-height: 62px;
    padding: 4px 3px;
    gap: 2px;
  }
  .calview .cal-cell:not(.cal-cell-week) > .cal-daynum,
  .calview .cal-cell:not(.cal-cell-week) > .sched-off { flex: 1 0 100%; }
  .calview .cal-cell:not(.cal-cell-week) .cal-event--dense {
    flex: 0 0 auto;
    gap: 0;
    padding: 5px 3px;      /* a bigger hit area than the 7px dot it draws */
  }
  .calview .cal-event--dense .cal-ev-time,
  .calview .cal-event--dense .cal-ev-street { display: none; }
  .calview .cal-cell:not(.cal-cell-week) .cal-ev-dot { width: 7px; height: 7px; }
  .calview .cal-cell:not(.cal-cell-week).today .cal-daynum { min-width: 18px; height: 18px; padding: 0 4px; }
  /* "+3 more" cannot fit a 44px cell on one line at --fs-micro (~42px of text in ~38px of
     interior), so it takes its own full-width line and wraps deliberately rather than
     ellipsing — an ellipsed count would hide the very number it exists to report. */
  .calview .cal-cell:not(.cal-cell-week) .cal-more {
    flex: 1 0 100%;
    margin-top: 3px;
    padding: 0;
    text-align: center;
    line-height: var(--lh-tight);
  }

  /* lanes: the gutter goes full-width above its own strip, and the strip keeps
     7 readable columns inside its OWN horizontal scroller */
  .calview .cal-lane { flex-wrap: wrap; }
  .calview .cal-lane-head { display: none; }
  .calview .cal-lane-name { flex: 1 1 100%; }
  .calview .cal-lane-strip { overflow-x: auto; grid-template-columns: repeat(7, minmax(88px, 1fr)); }
  .calview .cal-cell-lane { min-height: 56px; }
}

/* ==========================================================================
   SPEC-V2CALHOUR 2026-08-28 — the hour grid + sidebar (RULES §5.7, Wave 1)
   Day (default) and Week now render a full-24h hour grid with TIME-POSITIONED
   blocks, per-shooter lanes on a multi-shooter Day, and a right sidebar.
   Measured off the running prototype (design-handoff/VVT-Hub-Owner.dc.html —
   see the wave's report for the click-map + the pixel-by-pixel constants
   sheet). Month is untouched (a deliberate scope call — see the report's
   Questions) and keeps every rule above this block.
     KNOWN GAP (documented, not fixed this wave): the pre-existing "≤700px"
   block just above targeted the OLD week-agenda markup (.cal-dow-week /
   .cal-grid-week / .cal-cell-week) this wave retired for Week's Everyone
   layout — those selectors are now inert here (they still style
   renderWeekByPerson's per-person Week, which is unchanged). The new hour
   grid has NO narrow-width layout of its own yet; Calendar stays a desktop
   surface this wave, same as it effectively already was at Day/Week widths
   below ~900px. Flagged as a Question in the report, not silently shipped. */

.calview .cal-body-row { display: flex; align-items: flex-start; gap: 0; min-width: 0; }
.calview .cal-hourgrid-card {
  flex: 1; min-width: 0; height: 720px;
  display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: var(--shadow), var(--card-hi); overflow: hidden;
}
.calview .cal-hourgrid-topbar {
  flex: none; display: flex; align-items: center; gap: var(--space-3);
  padding: 13px var(--space-4); border-bottom: 1px solid var(--border); background: var(--subtle);
  font-size: var(--fs-caption); font-weight: var(--fw-bold); letter-spacing: -.01em;
}
.calview .cal-hourgrid-topbar .muted-line { font-size: var(--fs-meta); color: var(--faint); font-weight: var(--fw-medium); }
.calview .cal-hourgrid-topbar .spacer { flex: 1; }

/* the hlane header row (multi-shooter Day) sits between the topbar and the scroller,
   its own gutter-width spacer keeping its columns aligned with the grid below. */
.calview .cal-hlane-heads { flex: none; display: flex; border-bottom: 1px solid var(--border); background: var(--panel); }
.calview .cal-hlane-head {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--space-2);
  padding: 10px var(--space-3); border-right: 1px solid var(--border); border-bottom: 3px solid transparent;
}
.calview .cal-hlane-head-id { flex: 1; min-width: 0; }
.calview .cal-hlane-head-name { font-size: var(--fs-caption); font-weight: var(--fw-bold); letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.calview .cal-hlane-head-meta { margin-top: 2px; font-size: var(--fs-micro); color: var(--faint); }
.calview .cal-hlane-head-worth { flex: none; font-size: var(--fs-meta); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }

/* blocked time (🌴/🔒), single-lane Day/Week — sits above the scroller, reusing the
   existing .sched-off/.sched-busy chrome (styled once, above; nothing duplicated). */
.calview .cal-hourgrid-blocked-wrap { flex: none; display: flex; flex-wrap: wrap; gap: var(--space-1); padding: var(--space-2) var(--space-3) 0; }
.calview .cal-hourgrid-blocked-wrap--day { padding-left: calc(58px + var(--space-3)); }
.calview .cal-hourgrid-blocked-wrap-inline { display: flex; }
.calview .cal-hourgrid-blocked { display: flex; }
.calview .cal-hourgrid-blocked-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding: var(--space-2) 4px 0; }

.calview .cal-hourgrid-scroll { flex: 1; min-height: 0; overflow-y: auto; }
.calview .cal-hourgrid-inner { position: relative; display: flex; }

/* one hour = 56px (Day grid) — see calendar.js App.CAL_HOUR_PX. The custom property
   lets Week's 7-column grid (52px, App.CAL_WEEK_HOUR_PX — see the `.cal-dow-week ~`
   override below) share every rule here by overriding ONE value, rather than a
   second ruleset. */
.calview .cal-hour-gutter { --cal-hour-h: 56px; width: 58px; flex: none; border-right: 1px solid var(--border); }
.calview .cal-hour-row { height: var(--cal-hour-h, 56px); position: relative; }
.calview .cal-hour-label {
  position: absolute; top: -7px; right: 9px; font-size: 11px; font-weight: var(--fw-semibold);
  color: var(--faint); font-variant-numeric: tabular-nums;
}
.calview .cal-hour-row.cal-hour-off .cal-hour-label { color: var(--border-strong); }

.calview .cal-hourgrid-col { --cal-hour-h: 56px; flex: 1; min-width: 0; position: relative; border-right: 1px solid var(--border); }
.calview .cal-hour-cell { height: var(--cal-hour-h, 56px); border-bottom: 1px solid var(--border); cursor: pointer; }
.calview .cal-hour-cell:hover { background: var(--hover); }
.calview .cal-hour-cell.cal-hour-off { background: var(--subtle); }
.calview .cal-hourgrid-col.drop-hot { background: color-mix(in srgb, var(--accent) 8%, transparent); outline: 2px dashed var(--accent); outline-offset: -2px; }
.calview .cal-hourgrid-col.today { background: color-mix(in srgb, var(--accent) 4%, transparent); }

/* Week's 7 columns run at the compact 52px hour + 44px block floor (measured —
   App.CAL_WEEK_HOUR_PX/the `short` floor in the prototype's own block() builder). */
.calview .cal-dow-week ~ .cal-hourgrid-scroll .cal-hour-gutter,
.calview .cal-dow-week ~ .cal-hourgrid-scroll .cal-hourgrid-col { --cal-hour-h: 52px; }

.calview .cal-hblock {
  position: absolute; left: 8px; right: 10px; box-sizing: border-box;
  display: flex; flex-direction: column; gap: 3px; padding: 8px 10px;
  background: var(--panel); border: 1px solid var(--border); border-left-width: 4px; border-radius: 9px;
  box-shadow: 0 1px 3px rgba(16, 24, 40, .07); overflow: hidden; cursor: pointer;
  transition: box-shadow var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.calview .cal-hblock:hover, .calview .cal-hblock:focus-visible { box-shadow: 0 6px 16px rgba(16, 24, 40, .15); border-color: var(--accent); }
.calview .cal-hblock--dead { opacity: .55; text-decoration: line-through; }
.calview .cal-hblock-top { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.calview .cal-hblock-av { margin-left: auto; flex: 0 0 auto; }
.calview .cal-hblock-time { font-size: 12px; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; white-space: nowrap; }
.calview .cal-hblock-tag {
  padding: 2px 7px; border-radius: 5px; background: var(--accent-warm-soft); border: 1px solid rgba(177, 143, 82, .35);
  font-size: 9.5px; font-weight: var(--fw-bold); letter-spacing: .06em; color: #7a6234;
}
.calview .cal-hblock-addr { font-size: 13.5px; font-weight: var(--fw-bold); letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.calview .cal-hblock-foot { margin-top: auto; }
.calview .cal-hblock--compact { padding: 5px 7px; gap: 2px; }
.calview .cal-hblock--compact .cal-hblock-addr { font-size: 11.5px; }
.calview .cal-hblock--compact .cal-hblock-time { font-size: 10.5px; }
.calview .cal-hblock--compact .cal-ev-sub { font-size: 10.5px; }

/* ---- the sidebar ---------------------------------------------------------- */
.calview .cal-sidebar { width: 296px; flex: none; padding-left: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); overflow-y: auto; height: 720px; }
.calview .cal-side-card { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 15px 16px; box-shadow: var(--shadow), var(--card-hi); }
.calview .cal-side-head { display: flex; align-items: baseline; gap: var(--space-2); }
.calview .cal-side-label { font-size: 11px; font-weight: var(--fw-bold); letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.calview .cal-side-count { font-size: 11.5px; color: var(--border-strong); font-variant-numeric: tabular-nums; }
.calview .cal-side-hint { margin: 7px 0 0; font-size: var(--fs-meta); line-height: 1.5; color: var(--border-strong); }
.calview .cal-side-rows { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-3); }
.calview .cal-side-stat { display: flex; align-items: baseline; gap: var(--space-2); }
.calview .cal-side-stat-label { flex: 1; font-size: var(--fs-caption); color: var(--faint); }
.calview .cal-side-stat-value { flex: none; font-size: 14px; font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }

.calview .cal-unsched-item {
  display: flex; align-items: center; gap: var(--space-2); padding: 9px 10px;
  background: var(--subtle); border: 1px dashed var(--border-strong); border-radius: 8px; cursor: grab;
}
.calview .cal-unsched-item:hover, .calview .cal-unsched-item.is-armed { border-color: var(--accent); background: var(--hover); }
.calview .cal-unsched-item.is-dragging { opacity: .5; }
.calview .cal-unsched-text { flex: 1; min-width: 0; }
.calview .cal-unsched-addr { font-size: var(--fs-caption); font-weight: var(--fw-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.calview .cal-unsched-sub { font-size: 11.5px; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.calview .cal-due-row { display: flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.calview .cal-due-text { flex: 1; min-width: 0; }
.calview .cal-due-addr { font-size: var(--fs-caption); font-weight: var(--fw-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.calview .cal-due-sub { font-size: 11.5px; color: var(--faint); }
.calview .cal-due-chip { flex: none; font-size: 11px; font-weight: var(--fw-bold); border-radius: 5px; padding: 2px 7px; white-space: nowrap; }

.calview .cal-next-row { margin-top: 11px; display: flex; gap: var(--space-2); }
/* WAVE0-A A9 (should-land polish, taken up by Section B in Wave 1) — the
   ellipsis rules below already existed but never engaged: their flex parent
   (calendar.js's bare `.grow` wrapper — unlike `.list-row .grow`, styles.css
   ~1353, a bare `.grow` carries no `flex`/`min-width` rule of its own) never
   shrank below its content's natural width, so a long address just pushed
   the sidebar wider instead of truncating. Scoping flex:1/min-width:0 here
   is the actual fix; the ellipsis CSS was correct all along. A `title=`
   attribute on the address node (so a hover reveals the full address) is
   calendar.js:1071 — Section C's file this wave, left as a note. */
.calview .cal-next-row .grow { flex: 1; min-width: 0; }
.calview .cal-next-time { font-size: var(--fs-caption); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.calview .cal-next-addr { margin-top: 2px; font-size: var(--fs-caption); font-weight: var(--fw-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.calview .cal-next-sub { margin-top: 2px; font-size: 11.5px; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.calview .cal-ahead-row { display: flex; gap: var(--space-2); }
.calview .cal-ahead-row + .cal-ahead-row { margin-top: var(--space-2); }
.calview .cal-ahead-dow { flex: none; width: 44px; font-size: 10.5px; font-weight: var(--fw-bold); letter-spacing: .04em; text-transform: uppercase; color: var(--accent); }
.calview .cal-ahead-time { font-size: 11.5px; color: var(--accent); font-variant-numeric: tabular-nums; }
.calview .cal-ahead-text { flex: 1; min-width: 0; }


/* ##########################################################################
   HOME ▸ TODAY — hierarchy + rhythm (today.js). scratchpad/css-home.css
   Merged by the INTEGRATOR pass, 2026-07-30. Source: builder scratch file.
   ########################################################################## */
/* ============================================================
   HOME → TODAY — hierarchy + rhythm pass.  2026-07-30
   Surface: Home ▸ Today (today.js) and the FRIDAY brief card that
   sits at the top of it (dailybrief.js).

   INTEGRATOR: append this block at the END of styles.css (it must land
   AFTER the existing ".today-*" block at ~line 1615 and after the SPEC28
   ".card-title { letter-spacing: .35px }" at ~line 1657). Every selector
   here is already more specific than what it overrides, so order is belt-
   and-braces rather than load-bearing.

   Scoped entirely to classes emitted ONLY by today.js + dailybrief.js
   (grep-verified: .today-* and .jarvis-* appear in no other JS file), so
   nothing here can leak to another surface. No bare .card / .list-row /
   .card-title selector exists in this file.

   Tokens only — no raw hex, no new CSS variables, no !important. The
   html[data-theme="dark"] overrides already in styles.css theme all of
   this for free. No new animation, so no new prefers-reduced-motion guard
   is needed (the existing .jarvis-brief-orb guard at ~6069 still stands).
   No focus styles are touched: .today-shoot keeps its :focus-visible
   outline and every demoted button keeps .btn's global ring + 40px target.
   ============================================================ */

/* ---- 1. rung heads: one real type step above row text --------------
   Measured before: the largest thing below the 28px h1 on an 8,146px page
   was 15px, and "Late · 27" was set in the SAME 13px muted uppercase as
   "Listing-iversary" and "Backup reminder". `.card-title` is deliberately
   NOT restyled globally — 19 other cards and the whole app use it; only the
   two Today heading families are promoted. */
.today-section-head h3.card-title,
.today-group > h3.card-title {
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--text);
  text-transform: none;
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-snug);
  /* gap:0 (not the inherited 7px) because the count span carries its OWN " · "
     separator inside the h3's textContent — a flex gap on top of that reads as
     "Late  ·  10". The icon gets its space back explicitly, one line down. */
  gap: 0;
}
.today-section-head h3.card-title { margin: 0; }          /* the head row owns the spacing */
.today-group > h3.card-title { margin: 0 0 var(--space-3); }

/* the icon gets back the space `gap: 0` above took away, and stays secondary to
   the words it labels. App.icon wraps its <svg> in a <span class="icon"> and the
   svg strokes with currentColor — so the colour has to land on that SPAN, not on
   a `> svg` that is never a direct child. */
.today-section-head h3.card-title > .icon,
.today-group > h3.card-title > .icon { margin-right: var(--space-2); color: var(--muted); }

/* the count reads as a NUMBER, not as more label. The literal " · N" lives
   inside this span — tests/suite.js:27110 matches /Due today · (\d+)/ against
   the h3's own textContent, so the separator must stay in the text, not become
   a ::before. */
.today-count {
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  white-space: pre;   /* the leading space in " · N" IS the separator — never collapse it */
}

/* ---- 2. tone — ONLY the two rungs that mean "act now" ---------------
   Jobber's schedule: colour the SECTION HEAD, keep every row visually flat.
   Two tinted rungs on the page (Late = danger, Blocked on you = warn), never
   eight. The title text stays var(--text) so contrast never depends on the
   tone in either theme; only the icon, the count and a 3px rail carry it. */
.today-section--danger .today-count,
.today-section--danger .today-section-head h3.card-title > .icon { color: var(--danger); }
.today-section--warn .today-count,
.today-section--warn .today-section-head h3.card-title > .icon { color: var(--warn); }

.today-section--danger > .card,
.today-section--warn > .card { position: relative; }
.today-section--danger > .card::before,
.today-section--warn > .card::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  border-radius: var(--radius-lg) 0 0 var(--radius-lg);
}
.today-section--danger > .card::before { background: var(--danger); }
.today-section--warn > .card::before { background: var(--warn); }

/* ---- 3. shoot card: a fixed time gutter ----------------------------
   Timepage's day view. The time was absent from the card entirely while the
   brief chip 400px above printed it. `.today-shoot` becomes a 2-column grid;
   today.js moves ALL of the card's former children into .today-shoot-body,
   so nothing can strand itself in the gutter. */
.today-shoot {
  display: grid;
  grid-template-columns: 62px minmax(0, 1fr);
  column-gap: var(--space-3);
  align-items: start;
  /* SPEC-DHCREW D1 — the mockup's per-card left edge in the shooter's own
     crewColor. Same idiom as .floor-card (styles.css:9573): a fixed 3px
     border-left, colored inline per-card from App.crewColor. Defaults to the
     card's own hairline so an edge case with no color set never reads as an
     accidental gap. */
  border-left-width: 3px;
  border-left-style: solid;
  border-left-color: var(--border);
}
.today-shoot-body { min-width: 0; }
/* SPEC-DHCREW D1 — service chips (small pills), reading job.services. */
.today-shoot-svcs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.today-shoot-svc {
  padding: 3px 9px; border-radius: var(--radius-2);
  background: var(--bg-2); border: 1px solid var(--border);
  font-size: 11px; font-weight: var(--fw-semibold); color: var(--muted);
}
/* SPEC-DHCREW D1 — the chevron affordance next to the status pill; decorative
   (the whole card is already the role="button" target), so aria-hidden. */
.today-shoot-chevron { display: inline-flex; color: var(--faint); }
/* SPEC-DHCREW D1 — the drive line, restyled bare (no icon) + centered, same
   text App.fetchDriveForShoot always produced (see fillDrive, today.js). */
.today-shoot-drive { text-align: center; font-size: var(--fs-caption); }
.today-shoot-time {
  font-family: var(--font-serif);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-tight);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-snug);
  color: var(--text);
  padding-top: 1px;
}
.today-shoot-time .today-shoot-ampm {
  display: block;
  font-family: var(--font);
  font-size: var(--fs-micro);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--muted);
}
/* an untimed shoot renders an honest '—', never a fabricated clock time */
.today-shoot-time.is-untimed { color: var(--faint); }

/* the weather line's glyphs now go through App.labelIcon (SVG, currentColor)
   instead of raw OS emoji — each icon+value pair is one atom that wraps
   together rather than the icon orphaning onto the previous line. */
.today-wx { display: inline-flex; align-items: center; gap: var(--space-1); }

/* ---- 4. the FRIDAY brief ------------------------------------------- */
/* its head was the same 13px muted uppercase as a Layer-2 collapse label,
   despite being the first thing on the page. Same promotion as the rungs. */
.jarvis-brief-head .card-title {
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--text);
  text-transform: none;
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-snug);
}
/* a chip that wraps to two lines should read like a SENTENCE, not a centred
   banner. <button> defaults to text-align:center, so four of seven chips
   rendered as centred ragged pills at 375px. Only --spot had this fix. */
.jarvis-chip { text-align: left; justify-content: flex-start; }

/* INTEGRATOR: the builder's two .jarvis-chip--warn / --red overrides are NOT
   appended here. The raw hex they existed to neutralise (styles.css:6074-6075,
   #cab641 / #e2445c) was swapped to var(--warn) / var(--danger) IN PLACE in that
   same block, which is the better fix: identical computed result, one rule not
   two, and no raw hex left in the file. */

/* ---- 5. mobile: the identity column never collapses -----------------
   Measured at 375px in the Late list, `.list-row > .grow` was 211px with no
   action, 53–59px with a Nudge button, and 19px with a chip + Log button —
   the task title rendered as "C…" inside a 317px row while the secondary
   meta and the button kept their full width. Scoped to this surface's two
   list containers; the global `.list-row .grow { flex: 1 }` is untouched. */
@media (max-width: 640px) {
  .today-section .list-row,
  .today-group .list-row {
    flex-wrap: wrap;
    row-gap: var(--space-2);
    align-items: center;
    min-height: 44px;
  }
  /* claim line 1 (next to a leading checkbox); chips / actions / due wrap below */
  .today-section .list-row > .grow,
  .today-group .list-row > .grow { flex: 1 1 calc(100% - 40px); min-width: 0; }
  .today-section .list-row > .nowrap:last-child,
  .today-group .list-row > .nowrap:last-child { margin-left: auto; }

  .today-shoot { grid-template-columns: 54px minmax(0, 1fr); column-gap: var(--space-2); }
}
/* ============================================================ */

/* ============================================================
   SPEC-DHTODAY 2026-08-27 — Wave 2: the Today screens (owner + manager).
   Looks-only, built ON the DHSHELL block just above (same tokens, same
   .today-* namespace — grep-verified today.js is still the only emitter).
   No raw hex, no new colors outside the tone-triple/stage tokens DHSHELL
   already landed; every rule here is additive to what already exists.
   ============================================================ */

/* ---- D1 — four counter cards, top of Owner Today -------------------
   Tabular-nums is already the app-wide numeric convention (styles.css:214/
   229); .today-counter-n just opts a big display number into the same rule
   rather than inventing a second one. */
.today-counters {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
@media (max-width: 720px) { .today-counters { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.today-counter { padding: var(--space-4) var(--space-4); }
.today-counter-n {
  font-family: var(--font-serif);
  font-size: var(--fs-h1);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-tight);
  line-height: var(--lh-tight);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
.today-counter-label { color: var(--muted); font-size: var(--fs-meta); margin-top: var(--space-1); }

/* ---- D1 — Late is a genuinely red-tinted container, not just a 3px rail.
   Additive to DHSHELL's `.today-section--danger > .card::before` rail —
   both a tint AND a rail read as "act now" together. Row text stays
   var(--text) (DHSHELL's own rule, restated): only the card surface tints. */
.today-section--danger > .card { background: var(--tone-red-bg); border-color: var(--tone-red-bd); }

/* ---- D1 — the action column: whatever sits last in a Late/Blocked-on-you
   row (a button, a chip, or nothing) occupies the SAME width, so the column
   of actions aligns down the page the way the design brief's "fixed width"
   rule asks for. Opt-in (today.js passes `.today-action-col` only from
   lateRowNode/blockedRowNode's jobRowNode/taskRowNode calls) — every other
   .list-row on the page (Due today, Waiting, Next 7 days, the Layer-2 cards)
   is byte-unchanged. */
/* overflow:hidden is load-bearing, not decorative: a flex item's AUTOMATIC
   minimum size is its content's intrinsic min-width unless overflow is not
   `visible` (CSS Flexbox §4.5) — without it, one long action label (a Nudge
   button with a long name) would silently widen ONLY that row's column past
   `--today-action-w`, defeating the "aligns down the page" rule this exists
   for. The child rule below caps any button/link/span at the column's own
   width with an ellipsis, so a rare long label degrades gracefully instead
   of blowing out the fixed column. */
.today-action-col { flex: 0 0 var(--today-action-w, 132px); display: flex; justify-content: flex-end; text-align: right; overflow: hidden; }
.today-action-col > .btn,
.today-action-col > a,
.today-action-col > span { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Today's shoots keeps its own bottom actions row (Directions / Field sheet
   — restructuring shootCard into a single inline right-aligned link risked
   the existing 2-column time-gutter grid and its mobile rules just above),
   but is given the same minimum width + right alignment so the three
   sections read as one visual system without changing its DOM. */
.today-shoot-actions { justify-content: flex-end; }

/* ---- D2 — manager Today: "the floor today" -------------------------- */
.today-floor { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-3); }
.floor-card { padding: var(--space-3) var(--space-4); border-left-width: 3px; border-left-style: solid; }
.floor-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); flex-wrap: wrap; }
.floor-name { font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.floor-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
.floor-chip {
  display: inline-flex; align-items: center; gap: var(--space-1);
  padding: 3px 9px; border-radius: var(--radius-pill);
  background: var(--tone-neutral-bg); border: 1px solid var(--tone-neutral-bd);
  font-size: var(--fs-micro); font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums; cursor: pointer;
}
.floor-empty { color: var(--muted); font-size: var(--fs-caption); margin-top: var(--space-2); }

/* ---- D2 — manager Today: "four queues", 2x2 (stacks on mobile) ------ */
.today-queues { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); margin-bottom: var(--space-3); }
@media (max-width: 720px) { .today-queues { grid-template-columns: 1fr; } }
.today-queue--danger { background: var(--tone-red-bg); border-color: var(--tone-red-bd); }
.today-queue-title { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-caption); font-weight: var(--fw-bold); margin: 0 0 var(--space-2); }

/* ---- D2 — Customize popover: a toggle per Today block, hers alone --- */
.today-customize { position: relative; display: inline-block; }
.today-customize-pop {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 40;
  min-width: 220px; background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius-2); box-shadow: var(--shadow-popover); padding: var(--space-3);
}
.today-customize-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-2) 0; font-size: var(--fs-caption); }
/* ============================================================ */

/* ============================================================
   SPEC-DHFIDELITY1 2026-08-28 — the owner's Today, composition-faithful to
   design-handoff/VVT-Hub-Owner.dc.html. Every rule below is scoped under
   `.today-proto` (the wrapper today.js's renderOwnerToday() puts ONLY around
   the greeting/counters/Late/shoots/Blocked region) — crew and manager (ops)
   Today never emit that class, so their existing `.card`/`.card-pad`/
   `.list-row`/`.today-counter-n` rendering is byte-for-byte and pixel-for-
   pixel untouched. Tokens only — no raw hex, no new CSS variables, no
   !important; measured against the prototype's own inline styles at 1440px
   (its card radius/action padding are 10px/13-16px consistently across Late,
   Today's shoots and Blocked on you, vs. this app's global --radius-lg/
   .card-pad which were sized for other surfaces). The "More tools" drawer and
   everything folded inside it are OUTSIDE `.today-proto` on purpose, so a
   folded legacy card keeps rendering exactly as it always has. */
.today-proto .today-counter-n { font-size: 26px; }
.today-proto .card { border-radius: var(--radius-3); }
/* the ladder's list container carries BOTH the shared card-pad inset (18px
   20px, right for every other .card-pad on the app) and each row's own
   9px/4px .list-row padding — the prototype's Late/Blocked-on-you containers
   have no padding of their own at all, just edge-to-edge 13px/16px rows with
   a border between them, so the two paddings never compound. Zeroed here,
   scoped to this one region only. */
.today-proto .today-section > .card { padding: 0; overflow: hidden; }
.today-proto .today-section .list-row { padding: 13px 16px; }
/* ============================================================ */

/* ============================================================
   ROLE LADDER  (ladder-view.js)  —  .lad-* only.
   Tokens only: every color, radius, space and weight below is a var(), so both
   themes reflow off :root / html[data-theme="dark"] with no per-theme override
   needed here. The four state colors use the app's *-ink text tokens (which
   already flip per theme) on *-soft fills — never a raw hex.
   ============================================================ */
.lad-toolbar { margin-bottom: var(--space-4); gap: var(--space-2); flex-wrap: wrap; align-items: center; }
.lad-who { max-width: 210px; }
.lad-who-k, .lad-selfnote { font-size: var(--fs-meta); font-weight: var(--fw-bold); }
.lad-selfname { font-weight: var(--fw-black); font-size: var(--fs-md); }

/* ---- the identity header ---- */
.lad-head { margin-bottom: var(--space-3); }
.lad-head-id { display: flex; align-items: center; gap: var(--space-3); }
.lad-name { font-family: var(--font-serif); font-size: var(--fs-h1); font-weight: var(--fw-semibold); margin: 0; line-height: var(--lh-tight); }
.lad-head-chips { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-1); }
.lad-headline { font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--text); margin: var(--space-3) 0 0; line-height: var(--lh-snug); }
.lad-progress { margin-top: var(--space-3); }
.lad-progress .databar { width: 100%; }
.lad-note { font-size: var(--fs-meta); margin-top: var(--space-2); line-height: var(--lh-normal); }
.lad-blurb { color: var(--muted); font-size: var(--fs-caption); margin: 0 0 var(--space-3); }

.lad-banner { margin-top: var(--space-3); padding: var(--space-2) var(--space-3); border-radius: var(--radius-sm); font-size: var(--fs-meta); line-height: var(--lh-normal); border: 1px solid var(--border); }
.lad-banner-info { background: var(--info-soft); border-color: color-mix(in srgb, var(--info) 30%, transparent); color: var(--text); }
.lad-banner-warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 30%, transparent); color: var(--text); }

/* ---- criterion rows ---- */
.lad-crit-list { display: flex; flex-direction: column; gap: var(--space-2); }
.lad-crit { display: flex; align-items: flex-start; gap: var(--space-3); padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius); background: var(--subtle); }
.lad-crit-main { flex: 1 1 auto; min-width: 0; }
.lad-crit-side { display: flex; flex-direction: column; align-items: flex-end; gap: var(--space-2); flex: 0 0 auto; }
.lad-crit-label { font-weight: var(--fw-semibold); font-size: var(--fs-caption); line-height: var(--lh-snug); }
.lad-crit-meta { font-size: var(--fs-meta); color: var(--muted); margin-top: var(--space-1); line-height: var(--lh-normal); }
.lad-crit-why { font-size: var(--fs-meta); color: var(--faint); margin-top: var(--space-1); line-height: var(--lh-normal); }
.lad-crit-nums { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-1); }
.lad-actual { font-size: var(--fs-lg); font-weight: var(--fw-black); letter-spacing: var(--ls-tight); }
.lad-target { font-size: var(--fs-meta); color: var(--muted); font-weight: var(--fw-semibold); }
.lad-n { font-size: var(--fs-micro); color: var(--faint); }
/* THIN is never a failure: muted ink, no fill, no number. */
.lad-thin { color: var(--muted); font-style: italic; }
.lad-crit.lad-st-thin .lad-crit-label { color: var(--muted); }

.lad-disc { margin-top: var(--space-2); }
.lad-disc > summary { font-size: var(--fs-micro); color: var(--faint); cursor: pointer; }
.lad-disc-body { font-size: var(--fs-micro); color: var(--muted); line-height: var(--lh-normal); margin-top: var(--space-1); }

/* ---- state pills ---- */
.lad-pill { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: var(--fs-micro); font-weight: var(--fw-bold); white-space: nowrap; border: 1px solid transparent; }
.lad-pill.lad-st-met { background: var(--ok-soft); color: var(--ok-ink); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.lad-pill.lad-st-unmet { background: var(--warn-soft); color: var(--warn-ink); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.lad-pill.lad-st-thin { background: var(--hover); color: var(--muted); border-color: var(--border); }
.lad-pill.lad-st-manual { background: var(--info-soft); color: var(--text); border-color: color-mix(in srgb, var(--info) 30%, transparent); }
.lad-crit.lad-st-met { border-left: 3px solid var(--ok); }
.lad-crit.lad-st-unmet { border-left: 3px solid var(--warn); }
.lad-crit.lad-st-thin { border-left: 3px solid var(--border-strong); }
.lad-crit.lad-st-manual { border-left: 3px solid var(--info); }

/* ---- pay band ---- */
.lad-comp { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.lad-comp-cell { flex: 1 1 160px; background: var(--subtle); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-3); }
.lad-comp-k { font-size: var(--fs-micro); color: var(--muted); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: .4px; }
.lad-comp-v { font-size: var(--fs-lg); font-weight: var(--fw-black); margin-top: var(--space-1); }
.lad-comp-note { flex: 1 1 100%; font-size: var(--fs-micro); }
.lad-comp-empty { font-size: var(--fs-meta); }

/* ---- owner controls ---- */
.lad-owner { margin-bottom: var(--space-3); }
.lad-owner-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.lad-owner-k { font-size: var(--fs-meta); font-weight: var(--fw-bold); }
.lad-owner-note { font-size: var(--fs-micro); margin-top: var(--space-2); }

/* ---- the PIP ---- */
.lad-pip { margin-bottom: var(--space-3); border-left: 3px solid var(--info); }
.lad-pip-late { border-left-color: var(--danger); }
.lad-pip-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); flex-wrap: wrap; }
.lad-pip-title { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-md); font-weight: var(--fw-black); margin: 0; }
.lad-pip-dates { font-size: var(--fs-meta); margin-top: var(--space-1); }
.lad-pip-note { margin-top: var(--space-2); font-size: var(--fs-caption); line-height: var(--lh-normal); white-space: pre-wrap; }
.lad-pip-score { margin: var(--space-3) 0 var(--space-2); font-size: var(--fs-caption); }
.lad-pip-actions { margin-top: var(--space-3); display: flex; gap: var(--space-2); flex-wrap: wrap; }
.lad-pip-foot { font-size: var(--fs-micro); margin-top: var(--space-2); line-height: var(--lh-normal); }

.lad-pick { display: flex; flex-direction: column; gap: var(--space-1); margin: var(--space-3) 0; }
.lad-pick-row { display: flex; align-items: flex-start; gap: var(--space-2); padding: var(--space-2); border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; background: var(--subtle); }
.lad-pick-row:hover { background: var(--hover); }
.lad-pick-label { display: block; font-size: var(--fs-caption); font-weight: var(--fw-semibold); line-height: var(--lh-snug); }
.lad-pick-meta { display: block; font-size: var(--fs-micro); margin-top: 2px; }
.lad-form { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.lad-form > .field { flex: 1 1 150px; }
.lad-err { color: var(--danger-ink); font-size: var(--fs-meta); font-weight: var(--fw-semibold); min-height: 18px; margin-top: var(--space-2); }

/* ---- team overview ---- */
.lad-list { display: flex; flex-direction: column; }
.lad-row { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3) var(--space-2); border-bottom: 1px solid var(--border); cursor: pointer; border-radius: var(--radius-sm); }
.lad-row:last-child { border-bottom: none; }
.lad-row:hover { background: var(--hover); }
.lad-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.lad-row-name { font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.lad-row-meta { font-size: var(--fs-meta); margin-top: 2px; }
.lad-row-chips { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-1); }
.lad-row-score { font-size: var(--fs-caption); font-weight: var(--fw-black); color: var(--muted); white-space: nowrap; }
.lad-ready-note { font-size: var(--fs-meta); margin: 0 0 var(--space-2); line-height: var(--lh-normal); }

/* ---- owner setup + the refused record ---- */
.lad-setup, .lad-refused { margin-top: var(--space-3); }
.lad-setup > summary, .lad-refused > summary { font-weight: var(--fw-bold); cursor: pointer; font-size: var(--fs-caption); }
.lad-setup-warn { font-size: var(--fs-micro); }
.lad-setup-track { margin-top: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius); padding: var(--space-3); }
.lad-setup-track > summary { font-weight: var(--fw-bold); cursor: pointer; font-size: var(--fs-caption); }
.lad-setup-level { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px dashed var(--border); }
.lad-setup-lvhead { font-weight: var(--fw-black); font-size: var(--fs-caption); margin-bottom: var(--space-2); }
.lad-setup-grid { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.lad-setup-grid > .field { flex: 1 1 150px; min-width: 0; }
.lad-setup-crit { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: flex-end; margin-top: var(--space-2); padding: var(--space-2); background: var(--subtle); border-radius: var(--radius-sm); }
.lad-setup-crit > .field { flex: 1 1 140px; min-width: 0; }
.lad-setup-crit > .field.grow { flex: 3 1 220px; }
/* participates in the row's wrap (flex 1 1) instead of overflowing the card edge —
   measured at 800px, where an inline-flex label ran past the card border. */
.lad-setup-manual { display: inline-flex; align-items: center; gap: 6px; flex: 1 1 150px; min-width: 0; font-size: var(--fs-meta); font-weight: var(--fw-semibold); }
.lad-setup-engine { flex: 1 1 100%; font-size: var(--fs-micro); line-height: var(--lh-normal); word-break: break-word; }
.lad-setup-foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin-top: var(--space-4); }
.lad-refused-row { padding: var(--space-3) 0; border-bottom: 1px solid var(--border); }
.lad-refused-row:last-child { border-bottom: none; }
.lad-refused-sig { font-weight: var(--fw-bold); font-size: var(--fs-caption); }
.lad-refused-why { font-size: var(--fs-meta); margin-top: var(--space-1); line-height: var(--lh-normal); }
.lad-refused-build { font-size: var(--fs-micro); color: var(--faint); margin-top: var(--space-1); line-height: var(--lh-normal); }

.lad-hold { margin-top: var(--space-3); }
.lad-hold > summary { font-weight: var(--fw-bold); cursor: pointer; font-size: var(--fs-caption); }
.lad-hold .lad-crit-list { margin-top: var(--space-3); }

/* dark theme: the two soft banner fills are the only places a token pair needs a
   nudge — --info-soft/--warn-soft are near-white in light and near-black in dark,
   and the mixed borders can wash out at 30%. Ink stays the theme-flipping token. */
html[data-theme="dark"] .lad-banner-info,
html[data-theme="dark"] .lad-banner-warn { background: color-mix(in srgb, var(--panel) 88%, var(--text)); }
html[data-theme="dark"] .lad-pill.lad-st-met { background: color-mix(in srgb, var(--ok) 18%, transparent); }
html[data-theme="dark"] .lad-pill.lad-st-unmet { background: color-mix(in srgb, var(--warn) 18%, transparent); }
html[data-theme="dark"] .lad-pill.lad-st-manual { background: color-mix(in srgb, var(--info) 18%, transparent); }

/* 375px: the criterion row stacks its pill under the text instead of squeezing
   the label to two characters (the exact failure mode the Today rows had). */
@media (max-width: 640px) {
  .lad-crit { flex-wrap: wrap; }
  .lad-crit-side { flex-direction: row; align-items: center; width: 100%; justify-content: flex-start; }
  .lad-head-id { flex-wrap: wrap; }
  .lad-row { flex-wrap: wrap; row-gap: var(--space-2); }
  .lad-row > .grow { flex: 1 1 calc(100% - 46px); min-width: 0; }
  .lad-row-score { margin-left: auto; }
  .lad-comp-cell { flex: 1 1 100%; }
  .lad-who { max-width: 100%; width: 100%; }
  .lad-setup-crit > .field, .lad-setup-crit > .field.grow, .lad-setup-grid > .field { flex: 1 1 100%; }
}

/* AUDIT 08-01 — a cancelled/archived shoot on the Calendar was visually identical to a live
   one (measured: same bg, colour, opacity, no strikethrough) and only said so in a hover
   title, which does not exist on a phone. Crew read the Calendar to decide where to drive.
   Keep the event (the calendar suites assert it renders and is counted) — but make it
   unmistakable at a glance, in both themes, without relying on colour alone. */
.cal-event--dead { opacity: .55; }
/* SWEEP 08-02 — the strikethrough half of the fix above never rendered: it targeted
   `.cal-event-title` and `.cal-ev-addr`, and NEITHER class exists anywhere in the app.
   calendar.js's eventChip builds the address as `.cal-ev-street` (both the dense month
   chip and the week/day chip). Measured on a cancelled shoot in the live month grid:
   computed text-decoration-line "none" on the chip AND on its address element. Only
   opacity + the ::after badge were doing any work, so the one marker that survives a
   phone-width month cell — where `.calview .cal-event--dense .cal-ev-street` is
   display:none — was the badge alone. Point it at the real class. */
.cal-event--dead .cal-ev-street { text-decoration: line-through; }
.cal-event--dead::after {
  content: 'cancelled';
  margin-left: 6px; font-size: var(--fs-micro); font-weight: var(--fw-bold);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--danger);
}

/* AUDIT 08-01 #7 — the Tracker's twin of the above. A cancelled/archived job's Due date
   was painted in `overdue due-overdue` danger red, identical to a genuinely late live
   job, so the board's late total disagreed with Today's and a dead shoot read as the
   most urgent row on the screen. Same posture as the Calendar: MARK it dead (dim +
   strike, reason on hover) rather than hide it or shout. Colour is not the only cue. */
.jt-due-dead { color: var(--muted); text-decoration: line-through; font-weight: 400; }
.kanban-card .kc-fact.jt-due-dead { opacity: .7; }

/* ============================================================
   @media print  —  AUDIT-GAPS-2026-08-04 C1 (client-facing) + ST1 (structural)
   ------------------------------------------------------------
   Until 2026-08-04 this file had ZERO print rules and it is the only stylesheet
   index.html loads, so every in-app print path inherited the whole screen chrome.
   The repo's one other @media print block lives in marketing.css:1008, which the
   app never loads.

   MEASURED on the real artifact — Retainers → Sawyer Vance → 📊 Client report →
   🖨 Print / PDF, printed to Letter at 0.4in margins (= 739px CSS content width),
   pdftotext -layout, verbatim from page 1:
       1 / VVT Hub
       Directory  Top Agents  Leads  Pipelines  Retainers  Consult  Coaching
       Coaching KB  Opportunities  Sales Plays  Call Scores
       ← Back                                    Send to client   🖨 Print / PDF
   and where the milestones belong:
       YOUR MILESTONES
       🏆Plan                        🏅 Best month yet for reach
       1  Skip to content
       🚀VVT / 1 / Hub   Doubled their monthly reach
   The agent's own progress report arrived with our internal sales sub-navigation
   stamped across the top — Leads, Pipelines, Opportunities, Call Scores — and the
   mobile tab bar painted through the milestone block on both pages.

   THE TRAP, and why a naive rule misses it: at paper width the app is already past
   its own ≤860px breakpoint, so #sidebar is off-canvas at left:-275px and does NOT
   land on the page — the MOBILE chrome does. And .subtabs (711×90, the sales
   sub-nav) is position:static, in NORMAL FLOW. A rule that only hides fixed-position
   chrome leaves the worst offender untouched. Both classes are handled below.

   SCOPE: hide app chrome, keep the report. retainers.js:2281 is the ONLY
   window.print() that prints the live SPA; core.js:16217 and :16501
   (printShootBrief / printRetainerRecap) window.open a standalone document that
   carries its own @media print and is unaffected by anything here.

   Anything added later that is screen furniture — a dock, a banner, a coach-mark —
   belongs in the hide list below or should carry the .no-print utility class.
   ============================================================ */
@media print {

  /* ---- 0. the PAPER itself. Round 2 set `background:#fff` on html/body and stopped
     there; MEASURED 2026-08-05 (headless Chrome --print-to-pdf, Letter, control page
     alongside) that is not enough — a printed page of the client report carried a solid
     #121212 band on all four margins, both pages.
     Why `background:#fff` cannot fix it: Chrome's print dialog defaults "Background
     graphics" to OFF, so element backgrounds are not painted at all and the UA's own
     page CANVAS shows through — and the canvas colour is chosen by `color-scheme`,
     which :root sets to `dark` in the dark theme (styles.css:147). The app boots dark
     whenever the OS is in Dark Mode (index.html pre-paint script) or the person
     printing has used the theme toggle, so Erika printing from a dark-mode Mac hands
     the client an artifact framed in near-black ink on every sheet.
     Forcing the light color-scheme for print makes the canvas white with no dependency
     on the background-graphics checkbox. The report's own palette is already pinned
     separately at .tss-report below. */
  html { color-scheme: light !important; }

  /* ---- 0b. PAPER PALETTE, app-wide.  ADDED 2026-08-05 (round 4).
     ------------------------------------------------------------------
     0a above forces the page canvas WHITE for every printed screen. But until now
     the matching LIGHT ink was pinned only inside `.tss-report` (section 4 below),
     so on every OTHER screen the dark theme kept `--text: #e7e9ee` (styles.css:114)
     while the paper underneath it was white.
     MEASURED 2026-08-05, headless Chrome, Emulation.setEmulatedMedia media=print,
     html[data-theme="dark"], view=dashboard:
         body  color = rgb(231,233,238)  background = rgb(255,255,255)  → 1.19:1
     That is Erika's job sheet printed from a dark-mode Mac. Chrome's own ink-economy
     pass drags the glyphs partway back toward grey, which is why the sheet reads as
     washed-out rather than literally empty — but 1.19:1 is what the page ASKS the
     printer for, it is below every legibility floor, and it is not something to
     rely on a UA heuristic to rescue.

     Fix: re-pin the LIGHT theme's own :root values for print. Paper is a light
     surface, so the light palette is the correct one on every screen, not just the
     client report — which is what anyone pressing Print expects from a printer.

     WHY `!important`, and why `:root` is not enough on its own: the dark palette is
     declared at `html[data-theme="dark"]` — specificity (0,1,1) — which outranks
     `:root` (0,1,0) no matter how far down the file this block sits. `!important`
     wins on the root element regardless of selector, so this stays correct if the
     theme selector is ever changed or another one is added.

     WHY THIS DOES NOT DISTURB `.tss-report`: the cascade runs per element. These
     declarations land on the ROOT element; `.tss-report`'s own local custom
     properties (section 4) are declarations on the REPORT element, and a
     declaration on an element always beats an inherited value — importance does
     not cross the inheritance boundary. The report keeps its pinned palette and
     additionally stops inheriting dark values for the handful of tokens it does
     not pin itself (--subtle, --warn, --ok-soft, --danger-soft, --info …), which
     is the same direction it was already pinned in.

     Every token `html[data-theme="dark"]` overrides is re-pinned below, in the same
     order, to the value the light `:root` block gives it. Keep the two in step: if
     a token is added to the dark block it belongs here too. */
  :root {
    --bg: #f6f6f4 !important;
    --bg-2: #eef0f4 !important;
    --panel: #ffffff !important;
    --card: #ffffff !important;
    --text-soft: #565e6b !important;
    --subtle: #fbfbfa !important;
    --hover: #f2f4f7 !important;
    --chip-ink: #0b0d13 !important;
    --border: #e4e7ec !important;
    --border-strong: #d0d5dd !important;
    --text: #101828 !important;
    --muted: #565e6b !important;
    --faint: #656d7b !important;
    --accent: #2b608f !important;
    --accent-hover: #1e3f66 !important;
    --accent-soft: #eaf1f7 !important;
    --accent-warm: #b18f52 !important;
    --accent-warm-soft: #f0e6d4 !important;
    --gold: #c19a4e !important;
    --danger: #d92d20 !important;
    --danger-soft: #fef3f2 !important;
    --danger-ink: #b42318 !important;
    --on-danger: #ffffff !important;
    --ok: #079455 !important;
    --ok-soft: #ecfdf3 !important;
    --warn: #dc6803 !important;
    --warn-soft: #fffaeb !important;
    --ok-ink: #067647 !important;
    --warn-ink: #b54708 !important;
    /* LAW-A 2026-08-15 (closer) — the new --info-ink (styles.css:50 light / :139 dark)
       needs its print pin like every other token the dark theme overrides. Without it
       the suite's own r4/A4 guard goes red ("dark-theme tokens with no print pin:
       --info-ink") and a page printed from a dark-mode Mac would keep the DARK ink
       #53b1fd on white paper — measured 2.31:1 on #ffffff, below every legibility
       floor. Pinned to the LIGHT value (r4/A5 requires exactly that): #175cd3 measures
       5.99:1 on white paper. */
    --info-ink: #175cd3 !important;
    --info: #2e90fa !important;
    --info-soft: #eff8ff !important;
    --code-bg: #f6f8fa !important;
    --code-text: #1a1a1a !important;
    --ring-rgb: 43, 96, 143 !important;
    --shadow-sm: 0 1px 2px rgba(16,24,40,.04), 0 1px 1px rgba(16,24,40,.03) !important;
    --shadow: 0 1px 2px rgba(16,24,40,.05), 0 2px 6px -1px rgba(16,24,40,.08) !important;
    --shadow-lg: 0 18px 40px -12px rgba(16,24,40,.18), 0 6px 14px -6px rgba(16,24,40,.08) !important;
    --card-hi: inset 0 1px 0 rgba(255,255,255,.5) !important;
    /* sidebar rail tokens: the rail is dark navy in BOTH themes and every element
       that uses them is display:none below, so these change nothing on paper —
       they are here so "every dark-theme token is re-pinned" stays literally true
       and a test can check it. */
    --sidebar-bg: #1e3f66 !important;   /* SPEC-DHSHELL D2 — tracks the new light :root rail navy (was #16202e) */
    --sidebar-border: #232639 !important;
    --sidebar-text: #aab1c6 !important;   /* SPEC-DHSHELL D2 — tracks the new light :root value (was #9aa1b9) */
    --sidebar-active: #ffffff !important;
    --sidebar-active-bg: color-mix(in srgb, var(--accent) 16%, transparent) !important;
    /* D2 (2026-08-11 design pass) added --ghost-border to the dark block; per this
       block's own rule above ("if a token is added to the dark block it belongs
       here too") it is re-pinned to its LIGHT :root value — same pattern, no other
       token here touched. Verified against r4/A4 (every dark token has a print pin)
       and r4/A5 (the print value matches light). */
    --ghost-border: transparent !important;
    /* SPEC-DHSHELL D1 — the 7 tokens that got genuine dark-block entries above (:174-183)
       need their print pin too, same rule the comment above states. */
    --tone-red-bd: rgba(217,45,32,.24) !important;
    --tone-amber-bd: rgba(220,104,3,.28) !important;
    --tone-green-bd: rgba(7,148,85,.26) !important;
    --tone-neutral-bg: #f2f4f7 !important;
    --shadow-card: 0 1px 2px rgba(16,24,40,.04) !important;
    --shadow-popover: 0 20px 44px -10px rgba(16,24,40,.28) !important;
    --shadow-modal: 0 24px 60px -12px rgba(16,24,40,.35) !important;
  }

  /* ---- 1. the page box: undo the fixed-height, internally-scrolling app shell.
     html/body are height:100%, #app is height:100vh and #main is overflow-y:auto —
     an internally scrolling box is exactly what clips a long document to one page. */
  html, body { height: auto !important; overflow: visible !important; background: #fff !important; }
  #app { display: block !important; height: auto !important; overflow: visible !important; }
  #main {
    flex: none !important; overflow: visible !important;
    height: auto !important; max-height: none !important;
    /* the ≤860px block adds padding-top:52px to clear the mobile topbar we just hid */
    padding-top: 0 !important; padding-bottom: 0 !important;
    margin-left: 0 !important;
  }
  #view {
    overflow: visible !important; height: auto !important; max-height: none !important;
    max-width: none !important; margin: 0 !important; padding: 0 !important;
  }

  /* ---- 2. FIXED chrome. #sidebar is listed even though it is off-canvas at Letter
     width: A4 LANDSCAPE at 0.4in margins is 1122px, which is back above the 860px
     breakpoint and would put the whole nav — every client-facing viewer's internal
     menu — on the paper. !important beats the JS-set inline styles on the FABs. */
  .skip-link,
  #mobile-topbar,
  #sidebar,
  #nav-backdrop,
  #tabbar,
  #quick-add-fab,
  #voice-fab,
  #bug-fab,
  #callFab,
  #demo-ribbon,
  #overlays,
  #toast-zone,
  #nologin-banner,
  #storage-banner,
  .no-print { display: none !important; }

  /* ---- 3. IN-FLOW chrome — the part a fixed-position-only rule misses.
     .subtabs is the internal sales sub-nav (Leads / Pipelines / Opportunities /
     Call Scores). It is 711×90 and position:static, so it prints in normal flow. */
  .subtabs { display: none !important; }

  /* ...but the client report REUSES .subtabs for its own growth-chart metric picker
     (retainers.js:2313 — Followers / Reach / Avg views). That is report content, not
     app chrome: it is the only thing on the page that says WHICH series the chart is
     showing. Keep the selected one, as a plain label rather than a dead button. */
  .tss-report .subtabs { display: block !important; margin: 0 0 6px !important; }
  .tss-report .subtabs .subtab { display: none !important; }
  .tss-report .subtabs .subtab.active {
    display: inline !important;
    background: none !important; border: 0 !important; box-shadow: none !important;
    padding: 0 !important; margin: 0 !important;
    font-weight: 700; color: inherit !important;
  }

  /* .view-header is the app-wide screen-controls row (← Back / Send to client /
     🖨 Print / PDF). Hide the CONTROLS, not the header — a view that puts a real
     heading in there keeps it on the paper. Then collapse the empty rule/space. */
  .view-header { margin: 0 !important; padding: 0 !important; border-bottom: 0 !important; gap: 0 !important; }
  .view-header button,
  .view-header .btn,
  .view-header input,
  .view-header select { display: none !important; }

  /* ---- 4. give the report the whole page ---- */
  .tss-report {
    max-width: none !important; margin: 0 !important;

    /* Chrome's print dialog defaults "Background graphics" to OFF, which strips the
       hero gradient and the stat-card fills and leaves a luxury client artifact
       looking like a fax. print-color-adjust INHERITS, so declaring it once here
       forces colour on the whole report and on nothing else in the app. */
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;

    /* PAPER PALETTE — pinned so the artifact the agent receives is identical no
       matter which theme the person printing happens to be using. MEASURED: with
       html[data-theme="dark"] the same report printed as a near-solid black page
       (stat cards #1a1d27, section cards black, sign-off line at 2.59:1 contrast on
       the white page margin) — legible, ink-drenched, and not what a client should
       get. These are the LIGHT theme's own :root values (styles.css :root block),
       set as local custom properties so every descendant inherits them; nothing
       outside .tss-report is touched. Keep in step with :root if the palette moves. */
    --bg: #f6f6f4; --bg-2: #eef0f4; --panel: #ffffff; --card: #ffffff;
    --border: #e4e7ec; --border-strong: #d0d5dd;
    --text: #101828; --muted: #565e6b; --faint: #656d7b;
    --accent: #2b608f; --accent-hover: #1e3f66; --accent-soft: #eaf1f7;
    --ok: #079455; --danger: #d92d20;
    background: #ffffff !important;
    color: var(--text) !important;
  }

  /* Screen depth cues do not survive the trip to paper: with colour forced on (above)
     the hero's shadow-lg prints as a grey smudge across the top of page 1, right where
     the stat cards sit. MEASURED at 739px: a ~160×20px grey band under .tss-hero.
     Shadows carry no information here — drop them for the printed report. */
  .tss-report, .tss-report * { box-shadow: none !important; text-shadow: none !important; }

  /* ---- 5. page breaks: never split a stat card, a milestone chip or a
     before/after column across two sheets, and never strand a section heading
     at the foot of a page. ---- */
  .tss-stat, .tss-badge, .tss-ba-col, .tss-hero { break-inside: avoid; page-break-inside: avoid; }
  .card-title { break-after: avoid; page-break-after: avoid; }
  /* whole sections stay whole. Safe here because the report's tallest card is the
     growth chart at ~230px — nothing in it can outgrow a sheet and strand a blank page. */
  .tss-report .card { break-inside: avoid; page-break-inside: avoid; }
}

/* MOBILE BELL 2026-08-10 — the top-bar alert button. Push is off, so the in-app bell is the
   team's only alert channel; on a phone it used to sit at the bottom of the hamburger drawer.
   Sized to the same 44px tap target as the burger/search either side of it. Its unread count
   reuses .notif-bell-badge, positioned relative to this button. */
#mobile-topbar .mt-bell {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: 0 0 auto;
  background: transparent; border: 0; border-radius: 12px; cursor: pointer;
  /* NOT `inherit` — that resolved to the page text colour (near-black #101828) on a
     near-black bar (#16202e), so the bell was effectively invisible while the burger
     beside it was white. Measured, not guessed. Match the burger explicitly. */
  color: #fff; padding: 0;
}
#mobile-topbar .mt-bell:active { background: rgba(255,255,255,.12); }
#mobile-topbar .mt-bell .notif-bell-badge {
  position: absolute; top: 4px; right: 4px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 700; line-height: 1;
  color: #fff; background: var(--danger, #dc2626);
  border-radius: 999px; box-shadow: 0 0 0 2px var(--topbar-bg, #101828);
}

/* ============================================================
   MOBILE MODAL FOOTER FIX 2026-08-10 (P0) — .modal-foot (~line 691) is
   `display:flex; justify-content:flex-end` with no wrap, ever. Every .btn is
   `white-space:nowrap` (~line 348) by design — buttons never shrink below their
   text width — so on a narrow phone any footer with THREE children (a leading
   caption/marker + two-plus buttons) has nowhere for the excess width to go but
   the caption: it gets squeezed toward zero and its wrapped text visually runs
   under/over the buttons beside it. Worst offender: the welcome modal's "Closing
   this won't show it again" caption (App.showWelcome, core.js) crushed to ~41px
   next to "Keyboard shortcuts" + "Got it — let's go" — Erika's first screen.
   Same shape hits any modal whose footer uses the .left marker on a Delete
   button (callcoach.js/calls.js/pipelines.js/dashboards.js/social.js/
   timesheets.js/snippets.js/examples.js, clients.js's plain '.left') next to
   Cancel/Save.
   Fix, mobile-only (this file is append-only for this task; the base rule at
   ~691 is left untouched so every screen above 480px is byte-identical to
   before): let the footer wrap, and give the caption/left-marked element its
   own full-width line ABOVE the buttons instead of fighting them for space in
   one nowrap row. Buttons keep their native nowrap sizing and simply wrap to a
   second row themselves if two don't fit side by side. */
@media (max-width: 480px) {
  .modal-foot { flex-wrap: wrap; row-gap: 8px; }
  .modal-foot > .faint,
  .modal-foot > .left {
    flex: 1 1 100%;
    order: -1;
    white-space: normal;
    margin-right: 0;
  }
}

/* ============================================================
   STAGECARD 2026-08-19 — the job modal's identity band + stage face
   (PLAN-STAGECARD §8). The band is always visible; the face is the
   stage's own open middle state (the modal used to be binary —
   553px or 3,847px). Every new control is a full-width, text-
   labelled row ≥44px (TAPFLOOR/ICONGRAM-safe: no icon-only glyphs).
   ============================================================ */
/* SPEC-DHTRACK D3 — job popup hierarchy: the modal's own <h3> already computes
   Fraunces (.modal-head h3, ~line 849) and openJobModal now passes the job's
   ADDRESS as the title string, so that requirement needs no new markup. This is
   the row immediately beneath it: a stage pill in the live status color (ink
   flips per the SAME App.isDarkColor read the stage band uses — the pill's
   background is the literal solid color, same contrast case as the band chip),
   plus the tier badge when the client carries one (unchanged from before). */
.jm-hero-row { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.stage-pill {
  display: inline-flex; align-items: center; padding: 3px 12px; border-radius: var(--radius-pill);
  font-size: 11.5px; font-weight: var(--fw-bold); letter-spacing: .02em;
}
/* SPEC-DHTRACK D3 — champagne panel for the one existing agent-preference-type
   field (job.clientNotes, placeholder "Visible preferences, requests…" — the
   Client notes textarea). Decorative-only per the champagne token's own rule
   (styles.css :root — "never text-on-fill"); text stays the normal ink, only
   the panel surface/edge use the champagne tokens. No new field — restyles the
   one that already renders. */
.jm-champagne {
  background: var(--accent-warm-soft);
  border: 1px solid color-mix(in srgb, var(--accent-warm) 35%, transparent);
  border-radius: var(--radius); padding: 10px 12px;
}
.jm-band { margin-bottom: 10px; }
.jm-onsite {
  display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center;
  margin-top: 10px; padding: 9px 12px; min-height: 44px;
  background: var(--accent-soft); border: 1px solid var(--border);
  border-radius: 10px; font-size: 12.5px; font-weight: 600;
}
.jm-onsite-tag {
  /* DHSWEEP W9 — was letter-spacing: .04em, its own one-off; the app's established
     eyebrow convention (SPEC-DHCHAT D1's .chat-rail-head fix, README: "Inter 600–700,
     11–11.5px, .06em, uppercase") tracks out at .06em — converged, not redesigned
     (size/weight untouched, this view's own choice). */
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--accent); flex: 0 0 auto;
}
.jm-onsite-bit { display: inline-flex; align-items: center; gap: 4px; min-width: 0; }
.jm-face {
  border: 1px solid var(--border); border-radius: 12px;
  padding: 12px; margin-bottom: 12px; background: var(--bg-2);
}
.jm-face-head {
  font-size: 11px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin-bottom: 8px;
}
.jm-stuck {
  display: flex; align-items: center; gap: 6px; min-height: 44px;
  padding: 8px 12px; margin-bottom: 10px; border-radius: 10px;
  background: color-mix(in srgb, var(--danger, #e2445c) 12%, transparent);
  color: var(--danger, #e2445c); font-weight: 700; font-size: 12.5px;
}
.jm-pay-headline { font-size: 14px; font-weight: 700; margin-top: 6px; }
.jm-strip { display: flex; flex-direction: column; gap: 6px; margin: 10px 0 4px; }
.jm-strip-seg {
  display: flex; align-items: center; gap: 6px; width: 100%; min-height: 44px;
  padding: 8px 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg); color: var(--text); font-size: 13px; cursor: pointer; text-align: left;
}
.jm-strip-seg:hover { border-color: var(--border-strong); }
.jm-strip-name { font-weight: 700; flex: 0 0 auto; }
.jm-strip-sub { color: var(--muted); min-width: 0; }
.jm-strip-seg.jm-late { border-color: var(--danger, #e2445c); }
.jm-strip-seg.jm-late .jm-strip-sub { color: var(--danger, #e2445c); font-weight: 700; }
.jm-pop {
  display: flex; align-items: center; gap: 6px; width: 100%; min-height: 44px;
  padding: 9px 12px; border-radius: 10px; font-size: 13px; text-align: left;
  border: 1px solid var(--border); background: var(--bg);
}
button.jm-pop { cursor: pointer; color: var(--text); }
.jm-pop-act { border-color: var(--accent); color: var(--accent); font-weight: 700; }
.jm-pop-ok { color: var(--ok, #00c875); font-weight: 600; }
.jm-pop-warn { border-color: var(--warn, #f5a623); color: var(--warn, #b97900); font-weight: 600; }
.jm-pop-due { border-color: var(--danger, #e2445c); color: var(--danger, #e2445c); font-weight: 700; }
.jm-face-links { margin-top: 8px; }
.jm-face-prog { margin-top: 4px; }
.jm-count { font-weight: 400; color: var(--muted); font-size: 12px; }
.jm-wxline { margin-top: 6px; }
.jm-aftercare .jm-care { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-size: 12.5px; cursor: pointer; }
.jt-board .kanban-card .kc-owed { color: var(--danger, #e2445c); font-weight: 700; }

/* STAGECARD 2026-08-19 — phone-density pass, scoped to the stage-card EDIT modal
   only (.jm-modal is set by openJobModal on edit jobs; every other modal and the
   whole desktop layout are byte-identical). The 512px visible phone body must fit
   the band + the DONE face's Record-payment row on screen 1 (0 scroll), and the
   working view must land ≤6.0 screens: the app-wide .field margin-bottom (12px)
   is redundant INSIDE a 12px-gap grid, and 4 stacked textareas at full height
   were ~25% of the fold. */
@media (max-width: 640px) {
  .jm-modal .form-grid { gap: 8px; }
  .jm-modal .form-grid > .field { margin-bottom: 0; }
  .jm-modal .textarea { min-height: 56px; }
  .jm-modal .jm-face { padding: 8px; }
  .jm-modal .jm-band { margin-bottom: 6px; }
  .jm-modal .jm-onsite { padding: 7px 10px; }
  /* the un-buried Service progress block carries its old in-fold divider chrome
     (border-top + 10px pad + 4px margin as inline styles) — redundant inside the
     face's own bordered card on a phone */
  .jm-modal .jm-face-prog > .full { border-top: 0 !important; padding-top: 0 !important; margin-top: 0 !important; }
  .jm-modal .jm-strip { margin: 6px 0 2px; }
  .jm-modal .jm-face-links { margin-top: 4px; }
}

/* ============================================================
   SPEC-V2FRIDAY 2026-08-28 — FRIDAY, the ONE dark surface in the light Hub.
   Scoped entirely under .friday-dark (the 'friday' view's own wrapper) —
   nothing here leaks into any other view, and nothing outside this block
   changes. Colors measured live off design-handoff/VVT-FRIDAY.dc.html via
   CDP (README §3 + the .dc.html prototype is the composition source of
   truth): bg #0d0d0d, card #1e1e21/#16161a, deep #08090b, champagne #e6d2b1,
   blush #e3bcab, sky #8fc9e0→#387e9f, coral #e0796d, green #5fbf9b.
   DEVIATION (hard-constraint, not an oversight): the prototype's serif-
   italic voice uses the Cormorant Garamond WEB font via a Google Fonts
   CDN <link>. CLAUDE.md's $0/no-CDN law forbids adding any CDN — so the
   serif-italic voice here is Georgia/Times New Roman (system serif),
   never a fetched font file. Everything else — palette, spacing, the
   404px compact voice widget, the beats timeline, the six-segment nav —
   is measured from the real prototype markup, not approximated. */
/* Reuses the app's OWN dark-theme variable system (html[data-theme="dark"],
   above) rather than inventing a parallel one — scoping the SAME custom
   properties under .friday-dark means every existing component (.card,
   .btn, .btn-primary, .chip-btn, .textarea, .input, .muted, .faint —
   everything renderAskInto/messageBubble already render with) reflows dark
   automatically, with ZERO changes to that shared engine. This is what lets
   the compact docked panel (light, unchanged) and the full FRIDAY tab
   (wrapped in .friday-dark) stay the literal SAME renderer — "one engine
   behind both, never two brains," per this file's own architecture note. */
/* .fd-theme carries ONLY the variable overrides (no layout) so it can be
   reused on the voice widget too — the compact/fullscreen voice modes float
   OUTSIDE .friday-dark (over the light Hub, per README §3), but their
   existing-component internals (routeAction's .btn/.input/.textarea/
   .ai-gen-text cards — reused verbatim, never forked) still need to render
   dark-on-dark instead of inheriting the page's light theme. */
.fd-theme {
  --bg: #0d0d0d; --bg-2: #08090b; --panel: #1e1e21; --card: #1e1e21;
  --subtle: #16161a; --hover: #232327; --border: rgba(255,255,255,.10);
  --border-strong: rgba(255,255,255,.22); --text: #fff; --text-soft: rgba(255,255,255,.7);
  --muted: rgba(255,255,255,.5); --faint: rgba(255,255,255,.34);
  --accent: #4492b6; --accent-hover: #387e9f; --accent-soft: rgba(67,146,182,.16);
  --accent-warm: #e6d2b1; --accent-warm-soft: rgba(230,210,177,.12);
  --danger: #e0796d; --ok: #5fbf9b; --warn: #d99a52;
  color: var(--text); font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.friday-dark {
  position: relative; background: var(--bg); font-size: 15px;
  margin: -20px -24px; padding: 0; overflow: hidden; min-height: calc(100vh - 52px);
}
.friday-dark::before {
  content: ''; position: absolute; top: -180px; left: 50%; width: 1100px; height: 620px;
  margin-left: -550px; pointer-events: none;
  background: radial-gradient(ellipse at center, rgba(67,146,182,.13), rgba(13,13,13,0) 68%);
}
.fd-serif { font-family: Georgia, 'Times New Roman', serif; font-style: italic; font-weight: 400; }
.fd-header { position: relative; display: flex; align-items: center; gap: 16px; padding: 20px 34px; border-bottom: 1px solid rgba(255,255,255,.10); flex-wrap: wrap; }
.fd-logo { font-size: 24px; color: #fff; letter-spacing: .01em; }
.fd-status { font-size: 13px; color: rgba(255,255,255,.46); }
.fd-header .spacer { flex: 1; }
.fd-askanywhere { display: inline-flex; align-items: center; gap: 9px; height: 34px; padding: 0 12px; border: 1px solid rgba(255,255,255,.14); border-radius: 9px; background: rgba(255,255,255,.03); color: rgba(255,255,255,.5); font-size: 13px; cursor: pointer; }
.fd-askanywhere kbd { font-size: 11px; font-weight: 600; color: rgba(255,255,255,.62); border: 1px solid rgba(255,255,255,.16); border-radius: 4px; padding: 1px 5px; }
.fd-talkbtn { display: inline-flex; align-items: center; gap: 9px; min-height: 40px; padding: 0 17px; border: 1px solid rgba(230,210,177,.42); border-radius: 10px; background: rgba(230,210,177,.10); font-family: 'Plus Jakarta Sans', system-ui, sans-serif; font-size: 13.5px; font-weight: 700; color: #e6d2b1; cursor: pointer; }
.fd-talkbtn:hover { background: rgba(230,210,177,.18); }

.fd-segnav { position: relative; display: flex; gap: 6px; padding: 14px 34px 0; border-bottom: 1px solid rgba(255,255,255,.10); flex-wrap: wrap; }
.fd-seg-pill { display: flex; align-items: center; gap: 7px; height: 34px; padding: 0 14px; margin-bottom: 14px; border: 1px solid rgba(255,255,255,.14); border-radius: 999px; background: transparent; font-size: 13px; font-weight: 600; color: rgba(255,255,255,.62); cursor: pointer; white-space: nowrap; }
.fd-seg-pill:hover { border-color: rgba(255,255,255,.34); }
.fd-seg-pill.active { background: rgba(230,210,177,.10); border-color: rgba(230,210,177,.42); color: #e6d2b1; font-weight: 700; }
.fd-seg-count { min-width: 16px; height: 16px; padding: 0 5px; border-radius: 999px; background: rgba(255,255,255,.10); font-size: 10.5px; font-weight: 700; color: rgba(255,255,255,.7); display: inline-flex; align-items: center; justify-content: center; font-variant-numeric: tabular-nums; }
.fd-seg-pill.active .fd-seg-count { background: rgba(230,210,177,.28); color: #e6d2b1; }

.fd-body { position: relative; padding: 30px 34px 44px; max-width: 1060px; }
/* flex-wrap (not a @media query) is what actually reflows a NARROW CONTAINER —
   the test harness's mountView(375) fixes a container width without touching
   window.innerWidth, exactly the mechanism the OLD rail already depended on
   (its own inline flex-wrap:'wrap', friday.js's paintRailLayout precedent).
   The @media block below is additional real-viewport-width coverage on top,
   not a replacement for this. */
.fd-body.fd-wide { display: flex; gap: 0; max-width: none; padding: 0; align-items: flex-start; flex-wrap: wrap; }
.fd-body.fd-wide .fd-thread { flex: 1 1 260px; }
.fd-body.fd-wide .fd-sidebar { flex: 0 1 320px; }
.fd-h1 { margin: 0; font-size: 28px; font-weight: 800; letter-spacing: -.025em; line-height: 1.15; color: #fff; }
.fd-sub { margin: 9px 0 0; max-width: 620px; font-size: 14px; line-height: 1.6; color: rgba(255,255,255,.6); }

.fd-card { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 12px; }
.fd-thread { flex: 1; min-width: 0; padding: 30px 34px 40px; max-width: 900px; }
.fd-msg-out { display: flex; justify-content: flex-end; margin-bottom: 20px; }
.fd-msg-out-bubble { max-width: 560px; padding: 13px 17px; border-radius: 12px 12px 4px 12px; background: rgba(67,146,182,.16); border: 1px solid rgba(103,179,213,.24); font-size: 15px; line-height: 1.6; color: #fff; }
.fd-answer { margin-bottom: 28px; }
.fd-answer-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.fd-answer-name { color: #e3bcab; font-size: 17px; }
.fd-badge { padding: 2px 8px; border-radius: 5px; font-size: 10.5px; font-weight: 700; border: 1px solid; }
.fd-badge-data { background: rgba(95,191,155,.12); border-color: rgba(95,191,155,.36); color: #5fbf9b; }
.fd-badge-ai { background: rgba(230,210,177,.12); border-color: rgba(230,210,177,.36); color: #e6d2b1; }
.fd-badge-miss { background: rgba(224,121,109,.12); border-color: rgba(224,121,109,.36); color: #e0796d; }
.fd-answer-text { font-size: 22px; line-height: 1.5; letter-spacing: .005em; color: rgba(255,255,255,.94); max-width: 660px; }
.fd-nums { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.fd-numchip { display: flex; align-items: baseline; gap: 7px; padding: 6px 12px; border-radius: 8px; background: #16161a; border: 1px solid rgba(255,255,255,.10); }
.fd-numchip b { font-size: 15px; font-weight: 700; letter-spacing: -.01em; color: #fff; }
.fd-numchip span { font-size: 11.5px; color: rgba(255,255,255,.5); }
/* SPEC-V2FRIDAYFILE 2026-08-29 — tone variants, click-mapped off the SAME
   prototype's own three-tone number chips (urgent #e0796d / warn #d99a52 /
   neutral, the default #fff above) — an EXACT match to this file's own
   .fd-theme --danger/--warn tokens, so this reuses them rather than
   re-declaring the hexes. */
.fd-numchip b.tone-urgent { color: var(--danger); }
.fd-numchip b.tone-warn { color: var(--warn); }
.fd-speakbtn { width: 30px; height: 30px; flex: none; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,.14); border-radius: 8px; background: transparent; color: rgba(255,255,255,.6); cursor: pointer; }
.fd-speakbtn:hover { border-color: rgba(227,188,171,.5); color: #e3bcab; }

.fd-action { margin-top: 16px; max-width: 600px; background: #16161a; border: 1px solid rgba(230,210,177,.26); border-radius: 12px; overflow: hidden; }
.fd-action-head { display: flex; align-items: center; gap: 9px; padding: 12px 16px; border-bottom: 1px solid rgba(255,255,255,.08); }
.fd-action-kind { font-size: 10.5px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: #e6d2b1; }
.fd-action-note { font-size: 11.5px; color: rgba(255,255,255,.42); }
.fd-action-rows { padding: 14px 16px; }
.fd-action-row { display: flex; align-items: baseline; gap: 14px; padding: 6px 0; }
.fd-action-row .k { flex: none; width: 96px; font-size: 11.5px; color: rgba(255,255,255,.42); }
.fd-action-row .v { flex: 1; font-size: 14px; font-weight: 600; color: #fff; }
.fd-action-btns { display: flex; align-items: center; gap: 9px; padding: 0 16px 14px; }
.fd-btn-primary { display: inline-flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 17px; border: 0; border-radius: 9px; background: #4492b6; font-family: 'Plus Jakarta Sans', system-ui, sans-serif; font-size: 13.5px; font-weight: 700; color: #fff; cursor: pointer; }
.fd-btn-primary:hover { background: #387e9f; }
.fd-btn-ghost { min-height: 40px; padding: 0 15px; border: 1px solid rgba(255,255,255,.16); border-radius: 9px; background: transparent; font-family: 'Plus Jakarta Sans', system-ui, sans-serif; font-size: 13.5px; font-weight: 600; color: rgba(255,255,255,.72); cursor: pointer; }
.fd-btn-ghost:hover { border-color: rgba(255,255,255,.36); color: #fff; }

.fd-cites { margin-top: 16px; padding-top: 14px; border-top: 1px solid rgba(255,255,255,.08); max-width: 680px; }
.fd-cites-label { font-size: 10.5px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: rgba(255,255,255,.34); margin-bottom: 9px; }
.fd-cite-row { display: flex; align-items: baseline; gap: 10px; padding: 3px 0; }
.fd-cite-kind { flex: none; font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #67b3d5; width: 52px; }
/* SPEC-V2FRIDAYFILE 2026-08-29 — the prototype's SOP row used champagne
   (rgb(230,210,177) — this file's own --accent-warm, an exact match) where
   its ENGINE row used the blue above; a brain-doc citation gets the same
   treatment here, an engine citation keeps the default blue. */
.fd-cite-kind.k-sop { color: var(--accent-warm); }
.fd-cite-title { font-size: 13.5px; font-weight: 600; color: #fff; }
.fd-cite-stamp { font-size: 12.5px; color: rgba(255,255,255,.38); }

.fd-miss { margin-top: 16px; max-width: 600px; background: rgba(230,210,177,.07); border: 1px solid rgba(230,210,177,.26); border-radius: 12px; padding: 15px 17px; }
.fd-miss-line { font-size: 13.5px; line-height: 1.6; color: rgba(255,255,255,.78); }
.fd-miss-actions { display: flex; align-items: center; gap: 9px; margin-top: 13px; flex-wrap: wrap; }
.fd-btn-champagne { min-height: 38px; padding: 0 15px; border: 1px solid rgba(230,210,177,.4); border-radius: 9px; background: transparent; font-size: 13px; font-weight: 700; color: #e6d2b1; cursor: pointer; }
.fd-btn-champagne:hover { background: rgba(230,210,177,.12); }

.fd-follow { display: flex; gap: 8px; margin-top: 15px; flex-wrap: wrap; }
/* SPEC-V2FRIDAYFILE 2026-08-29 — first real use: an accessible <button>
   now carries this class (the prototype used a plain clickable <div>, but
   a keyboard/screen-reader affordance is worth more than a byte-identical
   tag) — font-family/text-align/appearance reset so the button doesn't
   pick up the browser's own control font instead of this file's. */
.fd-follow-chip { font-family: inherit; text-align: left; appearance: none; padding: 7px 13px; border-radius: 999px; border: 1px solid rgba(255,255,255,.14); background: rgba(255,255,255,.03); font-size: 13px; color: rgba(255,255,255,.72); cursor: pointer; }
.fd-follow-chip:hover { border-color: rgba(103,179,213,.5); color: #fff; }

.fd-composer { margin-top: 8px; display: flex; align-items: flex-end; gap: 11px; padding: 14px 16px; background: #1e1e21; border: 1px solid rgba(255,255,255,.14); border-radius: 13px; }
.fd-composer textarea { flex: 1; background: transparent; border: 0; color: #fff; font-size: 15px; line-height: 1.5; resize: none; font-family: inherit; }
.fd-composer textarea::placeholder { color: rgba(255,255,255,.4); }

.fd-sidebar { width: 320px; flex: none; padding: 30px 34px 40px 0; }
.fd-sidebar-label { font-size: 10.5px; font-weight: 700; letter-spacing: .22em; text-transform: uppercase; color: rgba(255,255,255,.42); margin-bottom: 13px; }
.fd-sidebar-row { display: flex; align-items: center; gap: 10px; padding: 11px 14px; border-bottom: 1px solid rgba(255,255,255,.06); }
.fd-sidebar-dot { width: 6px; height: 6px; flex: none; border-radius: 50%; background: #67b3d5; }
.fd-sidebar-dot.money { background: rgba(255,255,255,.2); }
.fd-sidebar-txt { flex: 1; font-size: 13px; color: rgba(255,255,255,.76); }
.fd-sidebar-meta { flex: none; font-size: 12px; color: rgba(255,255,255,.38); font-variant-numeric: tabular-nums; }
.fd-sidebar-note { padding: 12px 14px; font-size: 11.5px; line-height: 1.55; color: rgba(255,255,255,.34); }
.fd-gaps { background: rgba(255,255,255,.02); border: 1px solid rgba(255,255,255,.08); border-radius: 12px; padding: 13px 14px; }
.fd-gap-row { display: flex; gap: 9px; padding: 5px 0; }
.fd-gap-mark { flex: none; color: rgba(230,210,177,.6); font-size: 12px; }
.fd-gap-txt { flex: 1; font-size: 12.5px; line-height: 1.5; color: rgba(255,255,255,.56); }

/* ---------------- Findings ---------------- */
.fd-finding-row { display: flex; align-items: flex-start; gap: 15px; background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 12px; padding: 17px 19px; margin-bottom: 11px; }
.fd-finding-row:hover { border-color: rgba(255,255,255,.22); }
.fd-finding-dot { flex: none; width: 8px; height: 8px; margin-top: 7px; border-radius: 50%; }
.fd-finding-title { font-size: 15.5px; font-weight: 700; letter-spacing: -.01em; color: #fff; }
.fd-finding-tone { font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.fd-finding-body { margin-top: 6px; font-size: 14px; line-height: 1.6; color: rgba(255,255,255,.66); }
.fd-finding-nums { display: flex; gap: 7px; margin-top: 11px; flex-wrap: wrap; }
.fd-finding-num { padding: 4px 10px; border-radius: 6px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.09); font-size: 12px; font-weight: 600; color: rgba(255,255,255,.72); }

/* ---------------- Listens ---------------- */
.fd-listens { display: flex; min-height: 500px; }
.fd-calls-rail { width: 280px; flex: none; border-right: 1px solid rgba(255,255,255,.10); padding: 24px 0 40px; }
.fd-call-row { padding: 12px 22px; border-left: 2px solid transparent; cursor: pointer; }
.fd-call-row:hover { background: rgba(255,255,255,.04); }
.fd-call-row.active { border-left-color: #e6d2b1; background: rgba(230,210,177,.06); }
.fd-consent { display: flex; align-items: center; gap: 11px; padding: 11px 15px; background: rgba(18,128,92,.09); border: 1px solid rgba(18,128,92,.26); border-radius: 10px; }
.fd-consent.fd-consent-no { background: rgba(224,121,109,.09); border-color: rgba(224,121,109,.26); }
.fd-scorecard { display: flex; gap: 9px; margin-top: 20px; flex-wrap: wrap; }
.fd-score-tile { padding: 11px 15px; border-radius: 10px; background: #1e1e21; border: 1px solid rgba(255,255,255,.10); min-width: 104px; }
.fd-score-val { font-size: 19px; font-weight: 700; letter-spacing: -.02em; }
.fd-score-label { margin-top: 3px; font-size: 11.5px; color: rgba(255,255,255,.46); }
.fd-beat { display: flex; gap: 15px; padding: 14px 0; border-bottom: 1px solid rgba(255,255,255,.07); }
.fd-beat:hover { background: rgba(255,255,255,.02); }
.fd-beat-t { flex: none; width: 44px; padding-top: 2px; font-size: 12px; color: rgba(255,255,255,.34); font-variant-numeric: tabular-nums; }
.fd-beat-glyph { flex: none; width: 14px; padding-top: 1px; font-size: 13px; text-align: center; }
.fd-beat-kind { font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.fd-beat-quote { margin-top: 5px; font-size: 14px; line-height: 1.55; color: #fff; }
.fd-tone-objection { color: #e0796d; }
.fd-tone-question { color: rgba(255,255,255,.5); }
.fd-tone-commitment { color: #5fbf9b; }
.fd-tone-moment { color: #e6d2b1; }

/* ---------------- The Brain ---------------- */
.fd-brain-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 12px; margin-top: 26px; }
.fd-brain-card { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 12px; padding: 17px 18px; }
.fd-brain-count { font-size: 26px; font-weight: 800; letter-spacing: -.03em; }
.fd-brain-label { margin-top: 9px; font-size: 14.5px; font-weight: 700; }
.fd-brain-note { margin-top: 4px; font-size: 12.5px; line-height: 1.5; color: rgba(255,255,255,.5); }
.fd-brain-pending { display: flex; align-items: center; gap: 8px; margin-top: 12px; padding-top: 11px; border-top: 1px solid rgba(255,255,255,.07); font-size: 12px; }

/* ---------------- What FRIDAY learned / Waiting on you ---------------- */
.fd-miss-row { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 12px; padding: 16px 19px; margin-bottom: 10px; }
.fd-queue-row { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 12px; padding: 17px 19px; margin-bottom: 11px; }

/* ---------------- Voice: compact widget (404px, fixed bottom-right, DEFAULT) ---------------- */
.fd-voice-scrim { position: fixed; inset: 0; z-index: 9500; background: rgba(8,9,11,.38); -webkit-backdrop-filter: blur(1.5px); backdrop-filter: blur(1.5px); }
.fd-voice-mini { position: fixed; right: 28px; bottom: 28px; width: 404px; max-width: calc(100vw - 24px); background: #16161a; border: 1px solid rgba(230,210,177,.26); border-radius: 15px; box-shadow: 0 24px 56px -14px rgba(0,0,0,.8); overflow: hidden; z-index: 9501; color: #fff; font-family: 'Plus Jakarta Sans', system-ui, sans-serif; }
.fd-voice-mini-head { display: flex; align-items: center; gap: 10px; padding: 13px 15px; border-bottom: 1px solid rgba(255,255,255,.08); }
.fd-voice-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #5fbf9b; }
.fd-voice-state { flex: 1; color: #e3bcab; font-size: 16px; cursor: pointer; }
.fd-voice-iconbtn { flex: none; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,.14); border-radius: 7px; background: transparent; color: rgba(255,255,255,.6); cursor: pointer; }
.fd-voice-iconbtn:hover { border-color: rgba(255,255,255,.34); color: #fff; }
.fd-voice-caption { padding: 14px 15px 0; font-size: 15px; line-height: 1.5; color: rgba(255,255,255,.88); }
.fd-wave-mini { display: flex; align-items: center; justify-content: center; gap: 3px; height: 38px; margin-top: 12px; }
.fd-wave-mini span, .fd-wave-full span { display: block; width: 2px; border-radius: 999px; background: #e3bcab; transform-origin: center; transition: transform 260ms cubic-bezier(.16,1,.3,1); }
.fd-wave-mini span { height: 30px; transform: scaleY(.16); }
.fd-wave-full span { height: 80px; transform: scaleY(.16); }
.fd-wave-mini.talking span, .fd-wave-full.talking span { animation: fdwave 1.1s ease-in-out infinite; }
@keyframes fdwave { 0%, 100% { transform: scaleY(.16); } 50% { transform: scaleY(1); } }
.fd-voice-reply { padding: 10px 15px 0; font-size: 20px; line-height: 1.45; color: rgba(255,255,255,.95); }
.fd-voice-actions { display: flex; gap: 8px; padding: 13px 15px 15px; }
.fd-voice-actions .fd-btn-primary { flex: 1; justify-content: center; }

/* ---------------- Voice: fullscreen (expand-arrow ONLY, never default) ---------------- */
.fd-voice-full { position: fixed; inset: 0; z-index: 9600; background: #08090b; display: flex; flex-direction: column; color: #fff; font-family: 'Plus Jakarta Sans', system-ui, sans-serif; }
.fd-voice-full-head { position: relative; flex: none; display: flex; align-items: center; gap: 13px; padding: 22px 34px; }
.fd-voice-full-body { position: relative; flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; align-items: center; padding: 0 40px; }
.fd-voice-full-caption { flex: none; margin-top: auto; max-width: 760px; text-align: center; font-size: 24px; font-weight: 600; color: rgba(255,255,255,.9); }
.fd-voice-full-reply { flex: none; max-width: 720px; margin-top: 36px; text-align: center; font-size: 26px; line-height: 1.46; color: rgba(255,255,255,.95); }
.fd-voice-full-action { flex: none; width: 560px; max-width: 90vw; margin: 28px 0; background: #1e1e21; border: 1px solid rgba(230,210,177,.28); border-radius: 13px; overflow: hidden; }

/* ---------------- Not in the brain wording lives inline; voice mic button ---------------- */
.fd-mic-btn { flex: none; width: 38px; height: 38px; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,.16); border-radius: 9px; background: transparent; color: rgba(255,255,255,.7); cursor: pointer; }
.fd-mic-btn:hover { border-color: rgba(230,210,177,.5); color: #e6d2b1; }

/* ---------------- ⌘K FRIDAY extension — appended INSIDE the existing #cmdk-modal, never a second overlay ---------------- */
.fd-cmdk-friday { border-top: 1px solid var(--border); margin-top: 8px; padding-top: 8px; }
.fd-cmdk-label { padding: 6px 14px 4px; font-size: 10.5px; font-weight: 700; letter-spacing: .18em; text-transform: uppercase; color: var(--muted); }
.fd-cmdk-row { display: flex; align-items: center; gap: 12px; padding: 9px 14px; cursor: pointer; border-radius: 8px; }
.fd-cmdk-row:hover { background: var(--hover-bg, rgba(0,0,0,.04)); }
.fd-cmdk-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; }
@media (max-width: 640px) {
  .fd-body.fd-wide { flex-direction: column; }
  .fd-sidebar { width: 100%; padding: 16px 20px 24px; }
  .fd-thread { padding: 16px 20px 24px; max-width: none; }
  .fd-voice-mini { right: 12px; bottom: 12px; left: 12px; width: auto; }
}

/* ============================================================
   SPEC-V2RAIL D2 — Inbox. Light Deep Harbor (internal), same tokens as
   every other admin surface — no new colour system, per RULES.md §3.1.
   ============================================================ */
.inbox-view { max-width: 860px; margin: 0 auto; padding: 24px 20px 60px; }
.inbox-head-wrap { position: relative; margin-bottom: 14px; }
.inbox-head { font-family: var(--font-display, inherit); font-weight: 600; font-size: 26px; letter-spacing: -.02em; margin: 0 0 6px; color: var(--text); }
.inbox-sub { margin: 0; font-size: 13.5px; color: var(--muted); max-width: 640px; }
.inbox-cleared-today { position: absolute; top: 4px; right: 0; font-size: 12px; color: var(--muted); }
.inbox-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0 20px; }
.inbox-chip { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--border); background: var(--panel); color: var(--text); border-radius: 999px; padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer; }
.inbox-chip:hover { border-color: var(--border-strong); }
.inbox-chip--active { border-color: var(--accent); color: var(--accent); background: var(--accent-soft, rgba(43,96,143,.08)); }
.inbox-chip-count { color: var(--muted); font-weight: 500; }
.inbox-chip--active .inbox-chip-count { color: var(--accent); }
.inbox-group-head { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin: 22px 0 8px; }
.inbox-group-head:first-child { margin-top: 0; }
.inbox-group-count { background: var(--bg-2); border-radius: 999px; padding: 1px 7px; font-size: 11px; }
.inbox-row { display: flex; gap: 12px; align-items: flex-start; border: 1px solid var(--border); border-left: 3px solid var(--border); background: var(--panel); border-radius: var(--radius); padding: 12px 14px; margin-bottom: 8px; }
.inbox-row-icon { flex: none; width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; background: var(--bg-2); color: var(--muted); }
.inbox-row-icon--clientnote, .inbox-row-icon--mention { background: var(--accent-soft, rgba(43,96,143,.1)); color: var(--accent); }
.inbox-row-icon--money { background: var(--danger-soft); color: var(--danger-ink); }
.inbox-row-icon--jobactivity { background: var(--bg-2); color: var(--muted); }
.inbox-row-main { flex: 1 1 auto; min-width: 0; }
.inbox-row-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.inbox-row-title { font-weight: 700; font-size: 14px; color: var(--text); }
.inbox-row-source { font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.inbox-row-body { font-size: 13.5px; color: var(--muted); margin-top: 3px; line-height: 1.5; }
.inbox-row-meta { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
.inbox-row-job { font-size: 12.5px; color: var(--accent); text-decoration: none; }
.inbox-row-job:hover { text-decoration: underline; }
.inbox-row-flag { font-size: 11px; font-weight: 700; border-radius: 999px; padding: 2px 9px; }
.inbox-row-flag--red { background: var(--danger-soft); color: var(--danger-ink); border: 1px solid rgba(217,45,32,.24); }
.inbox-row-side { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.inbox-row-time { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.inbox-row-actions { display: flex; align-items: center; gap: 6px; }
.inbox-check { width: 30px; height: 30px; flex: none; border-radius: 8px; border: 1px solid var(--border); background: var(--panel); color: var(--muted); display: flex; align-items: center; justify-content: center; cursor: pointer; }
.inbox-check:hover { border-color: #079455; color: #079455; background: var(--danger-soft, transparent); background: rgba(7,148,85,.08); }
.inbox-empty { text-align: center; padding: 56px 20px; border: 1px solid rgba(177,143,82,.35); background: var(--accent-warm-soft); border-radius: var(--radius-lg); }
.inbox-empty-icon { color: var(--accent-warm); margin-bottom: 10px; display: flex; justify-content: center; }
.inbox-empty-title { font-family: var(--font-display, inherit); font-size: 20px; font-weight: 600; margin: 0 0 6px; color: var(--text); }
.inbox-empty-sub { margin: 0; font-size: 13.5px; color: var(--muted); }
@media (max-width: 640px) {
  .inbox-view { padding: 16px 14px 40px; }
  .inbox-row { flex-wrap: wrap; }
  .inbox-row-side { flex-direction: row; align-items: center; width: 100%; justify-content: space-between; margin-top: 6px; }
  .inbox-cleared-today { position: static; display: block; margin-top: 6px; }
}

/* ============================================================
   SPEC-V2BOOKING 2026-08-29 — book.html / book-public.js ONLY.
   Dark cinematic per design-handoff/RULES.md §3.2. Sliced into book.css
   by tools/extract-book-css.js — never hand-edit book.css, edit here and
   regenerate:  node tools/extract-book-css.js
   PARTIAL BUILD — see book-public.js's own header for what screens this
   covers. Namespaced bk- so it can never collide with order.css's own
   .order-* rules or the admin Hub's classes.
   ============================================================ */
.bk-shell { min-height: 100vh; background: #0d0d0d; color: #fff; font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif; font-size: 14px; line-height: 1.6; display: flex; flex-direction: column; }
.bk-rail { display: flex; align-items: center; gap: 18px; padding: 14px 20px; background: #08090b; border-bottom: 1px solid rgba(255,255,255,.10); flex-wrap: wrap; position: sticky; top: 0; z-index: 5; }
.bk-mark { font-weight: 800; letter-spacing: .14em; font-size: 12px; color: #e6d2b1; }
.bk-rail-steps { display: flex; gap: 14px; flex-wrap: wrap; }
.bk-rail-step { font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: rgba(255,255,255,.34); }
.bk-rail-step.is-reached { color: rgba(255,255,255,.68); }
.bk-rail-step.is-current { color: #e6d2b1; }
.bk-panel { max-width: 720px; margin: 0 auto; width: 100%; padding: 28px 20px 80px; box-sizing: border-box; }
.bk-step-head h1 { font-family: 'Fraunces', Georgia, 'Times New Roman', serif; font-size: 28px; letter-spacing: -.02em; margin: 0 0 6px; }
/* FIX-V2PAINT §4 — the affirm note (order.js's .order-affirm-note, ported) */
.bk-affirm-note { font-family: 'Fraunces', Georgia, serif; font-style: italic; font-size: 14.5px; color: #e6d2b1; margin: 0 0 8px; }
.bk-step-head .bk-sub { color: rgba(255,255,255,.68); margin: 0 0 20px; font-size: 13.5px; }
.bk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }
.bk-field { display: flex; flex-direction: column; gap: 6px; }
.bk-field-label { font-size: 11.5px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: rgba(255,255,255,.5); }
.bk-input { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); color: #fff; border-radius: 8px; padding: 10px 12px; font-size: 14px; font-family: inherit; min-height: 44px; box-sizing: border-box; }
.bk-input:focus { outline: none; border-color: rgba(255,255,255,.24); box-shadow: 0 0 0 3px rgba(230,210,177,.18); }
.bk-panel-info { background: #16161a; border: 1px solid rgba(255,255,255,.10); border-radius: 10px; padding: 12px 14px; font-size: 13px; color: rgba(255,255,255,.68); margin-bottom: 16px; }
.bk-panel-warn { border-color: rgba(220,104,3,.4); color: #f0b98a; }
.bk-panel-note { border-style: dashed; color: rgba(255,255,255,.5); }
.bk-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; margin-bottom: 20px; }
.bk-card { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 12px; padding: 16px; display: flex; flex-direction: column; gap: 6px; }
.bk-card.is-picked { border-color: #e6d2b1; box-shadow: 0 0 0 1px #e6d2b1 inset; }
.bk-card-name { font-weight: 700; font-size: 15px; }
.bk-card-price { font-family: 'Fraunces', Georgia, serif; font-size: 22px; }
.bk-card-mins { color: rgba(255,255,255,.5); font-size: 12.5px; margin-bottom: 8px; }
.bk-contact-block { border: 1px solid #e6d2b1; border-radius: 10px; padding: 14px; margin-bottom: 16px; display: grid; gap: 12px; }
.bk-lines { list-style: none; padding: 0; margin: 0 0 12px; display: flex; flex-direction: column; gap: 8px; }
.bk-lines li { display: flex; justify-content: space-between; border-bottom: 1px solid rgba(255,255,255,.10); padding-bottom: 8px; font-size: 13.5px; }
.bk-total { display: flex; justify-content: space-between; font-family: 'Fraunces', Georgia, serif; font-size: 20px; padding: 10px 0; border-top: 1px solid rgba(255,255,255,.24); margin-bottom: 12px; }
.bk-terms-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.bk-terms-row input[type="checkbox"] { width: 20px; height: 20px; }
.bk-link-btn { background: none; border: 0; color: #67b3d5; text-decoration: underline; cursor: pointer; font: inherit; padding: 0; }
.bk-link-btn:hover { color: #8fc9e0; }
.bk-terms-disclosure { background: #16161a; border: 1px solid rgba(255,255,255,.10); border-radius: 8px; padding: 12px; font-size: 12.5px; color: rgba(255,255,255,.68); margin-bottom: 16px; }
.bk-nav { display: flex; justify-content: space-between; align-items: center; margin-top: 24px; gap: 12px; }
.bk-btn { font: inherit; font-weight: 700; border-radius: 999px; padding: 12px 22px; min-height: 44px; cursor: pointer; border: 1px solid transparent; }
.bk-btn-primary { background: var(--bk-accent, #e6d2b1); color: #0d0d0d; }
.bk-btn-primary:disabled { opacity: .4; cursor: not-allowed; }
.bk-btn-ghost { background: transparent; color: rgba(255,255,255,.68); border-color: rgba(255,255,255,.24); }
@media (max-width: 640px) {
  .bk-grid { grid-template-columns: 1fr; }
  .bk-panel { padding: 20px 14px 72px; }
}

/* ============================================================
   SPEC-V2BOOKING wave 2 (2026-08-29) — sign-in, tabs, build-your-own,
   the sheet, extras, calendar, chips, choice rows. Same rules as the
   wave-1 block above: namespaced bk-, sliced into book.css by
   tools/extract-book-css.js, order.css untouched.
   ============================================================ */
/* FIX-V2PAINT §4 — branded link headshot/logo (order.js's .order-mast-headshot
   /.order-mast-logo, ported into the rail). */
.bk-rail-agent { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: rgba(255,255,255,.68); }
.bk-rail-headshot { width: 22px; height: 22px; border-radius: 50%; object-fit: cover; border: 1px solid rgba(255,255,255,.24); }
.bk-rail-logo { max-height: 18px; max-width: 64px; object-fit: contain; }
.bk-rail-agent-name { font-weight: 600; }
.bk-rail-mode { font-size: 11px; color: rgba(255,255,255,.5); margin-right: auto; }
.bk-rail-step[onclick] { cursor: pointer; }
.bk-signin-row { display: flex; gap: 10px; align-items: flex-end; margin-bottom: 14px; flex-wrap: wrap; }
.bk-signin-row .bk-field { flex: 1; min-width: 220px; }
.bk-guest-btn { width: 100%; margin-bottom: 16px; }
.bk-tabs { display: flex; gap: 6px; margin-bottom: 18px; background: #1e1e21; border-radius: 999px; padding: 4px; width: max-content; }
.bk-tab { background: none; border: 0; color: rgba(255,255,255,.5); font: inherit; font-weight: 700; padding: 8px 18px; border-radius: 999px; cursor: pointer; }
.bk-tab.is-on { background: #e6d2b1; color: #0d0d0d; }
.bk-card { position: relative; }
.bk-card-see { position: absolute; top: 12px; right: 12px; font-size: 11.5px; }
.bk-card-includes { list-style: none; padding: 0; margin: 0 0 10px; font-size: 12.5px; color: rgba(255,255,255,.5); display: flex; flex-direction: column; gap: 3px; }
.bk-cards-compact .bk-card { padding: 12px; }
.bk-byo-group-title { font-weight: 700; font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: rgba(255,255,255,.5); margin: 18px 0 8px; }
.bk-byo-group-title:first-child { margin-top: 0; }
.bk-byo-sticky { position: sticky; bottom: 0; background: #0d0d0d; padding-top: 10px; }
.bk-owned-list { list-style: none; padding: 0; margin: 0 0 20px; display: flex; flex-direction: column; gap: 6px; color: #5fbf9b; font-size: 13.5px; }
.bk-section-title { font-weight: 700; font-size: 13px; letter-spacing: .04em; margin-top: 10px; }
.bk-best-fit-chip { position: absolute; top: 12px; left: 12px; background: #e6d2b1; color: #0d0d0d; font-size: 10px; font-weight: 800; letter-spacing: .08em; border-radius: 999px; padding: 3px 9px; }
/* the sheet — pairs backdrop-filter with -webkit-backdrop-filter throughout,
   per the order.css intent law (Safari support), not just backdrop-filter alone */
/* FIX-V2PAINT — the sheet appends to document.body (openSheet(), outside
   #book-root/.bk-shell), so it never inherited .bk-shell's `color: #fff` and
   fell back to body{color:var(--text)} — the INTERNAL light Deep Harbor
   theme's near-black #101828, on the sheet's #16161a surface. Found live
   while verifying §3's new example media (the frame drew the eye to the
   near-invisible text below it). Set explicitly rather than depending on
   inherited context, since sheets aren't the only thing that could someday
   append to body. */
.bk-sheet-backdrop { position: fixed; inset: 0; background: rgba(10,10,12,.72); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); display: flex; align-items: center; justify-content: center; z-index: 200; padding: 20px; color: #fff; font-family: Inter, system-ui, -apple-system, 'Segoe UI', sans-serif; font-size: 14px; line-height: 1.6; }
.bk-sheet { background: #16161a; border: 1px solid rgba(255,255,255,.10); border-radius: 14px; padding: 24px; max-width: 460px; width: 100%; max-height: 86vh; overflow-y: auto; position: relative; }
.bk-sheet-close { position: absolute; top: 10px; right: 14px; background: none; border: 0; color: rgba(255,255,255,.5); font-size: 22px; cursor: pointer; }
.bk-sheet-name { font-family: 'Fraunces', Georgia, serif; font-size: 22px; margin-top: 4px; }
.bk-sheet-price { font-family: 'Fraunces', Georgia, serif; font-size: 18px; color: #e6d2b1; margin-bottom: 10px; }
.bk-sheet-blurb { color: rgba(255,255,255,.68); font-size: 13.5px; }
.bk-sheet-actions { display: flex; gap: 10px; margin-top: 16px; }
.bk-offer { background: rgba(230,210,177,.08); border: 1px solid rgba(230,210,177,.3); border-radius: 10px; padding: 14px; margin: 14px 0; }
.bk-offer-badge { font-size: 10.5px; font-weight: 800; letter-spacing: .1em; color: #e6d2b1; margin-bottom: 6px; }
.bk-offer-actions { display: flex; gap: 10px; align-items: center; margin-top: 8px; }
.bk-choice-row { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0; }
.bk-chip { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); color: rgba(255,255,255,.68); border-radius: 999px; padding: 8px 14px; font-size: 13px; cursor: pointer; min-height: 36px; }
.bk-chip.is-on { background: #e6d2b1; color: #0d0d0d; border-color: #e6d2b1; }
.bk-required-chip { background: rgba(220,104,3,.18); color: #f0b98a; font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; border-radius: 999px; padding: 3px 8px; margin-left: 8px; }
.bk-contact-head { display: flex; align-items: center; margin-bottom: 4px; font-weight: 700; font-size: 13px; }
.bk-contact-block.is-known { border-color: rgba(255,255,255,.24); }
/* calendar */
.bk-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; font-weight: 700; }
.bk-cal-dow { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; font-size: 10.5px; color: rgba(255,255,255,.34); text-align: center; margin-bottom: 4px; }
.bk-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin-bottom: 12px; }
.bk-cal-cell { position: relative; aspect-ratio: 1; background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 8px; color: #fff; font: inherit; cursor: pointer; display: flex; align-items: flex-start; justify-content: flex-start; padding: 6px; font-size: 12px; }
.bk-cal-cell.is-picked { border-color: #e6d2b1; box-shadow: 0 0 0 1px #e6d2b1 inset; }
.bk-cal-cell.is-past { opacity: .3; cursor: not-allowed; text-decoration: line-through; }
.bk-cal-cell.is-full { opacity: .45; }   /* CALGATE — live mode: a day with nothing open stays clickable (it explains itself) but reads quiet */
.bk-cal-blank { visibility: hidden; }
.bk-cal-dot { position: absolute; bottom: 6px; right: 6px; width: 7px; height: 7px; border-radius: 50%; }
.bk-cal-dot-green { background: #5fbf9b; }
.bk-cal-dot-champagne { background: #e6d2b1; }
.bk-cal-dot-grey { background: rgba(255,255,255,.24); }
.bk-cal-legend { display: flex; gap: 16px; font-size: 12px; color: rgba(255,255,255,.5); margin-bottom: 18px; }
.bk-cal-legend span { display: inline-flex; align-items: center; gap: 5px; }
.bk-cal-legend .bk-cal-dot { position: static; }
.bk-day-heading { font-size: 13px; font-weight: 700; letter-spacing: .04em; margin: 4px 0 10px; }
.bk-times { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; margin-bottom: 8px; }
.bk-time { background: #1e1e21; border: 1px solid rgba(255,255,255,.10); color: #fff; border-radius: 8px; padding: 10px 6px; font: inherit; font-size: 13px; cursor: pointer; min-height: 44px; }
.bk-time.is-closed { opacity: .3; text-decoration: line-through; cursor: not-allowed; }
.bk-time.is-picked { border-color: #e6d2b1; background: rgba(230,210,177,.15); }
.bk-crew-row { display: flex; align-items: center; gap: 10px; margin: 14px 0; font-size: 13px; }
.bk-crew-bubbles { display: flex; gap: -6px; }
.bk-bubble { width: 30px; height: 30px; border-radius: 50%; background: #4492b6; color: #fff; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: 700; margin-left: -6px; border: 2px solid #0d0d0d; }
.bk-bubble:first-child { margin-left: 0; }
.bk-twilight-block { border-color: rgba(230,210,177,.4); }
/* FIX-V2PAINT §2 — address suggester (o-sugg ported), dark-cinematic palette
   per RULES.md §3.2 (surface #1e1e21, overlay #16161a, hairline border,
   champagne accent) instead of reusing order.css's light-theme --o-* tokens,
   which book.html never loads. */
.bk-field.addr { position: relative; }
.bk-ok-mark { display: none; color: #5fbf9b; font-weight: 700; }
.bk-ok-mark.show { display: inline; }
.bk-sugg { position: absolute; left: 0; right: 0; top: 100%; margin-top: 4px; z-index: 30; display: none; flex-direction: column; background: #1e1e21; border: 1px solid rgba(255,255,255,.10); border-radius: 12px; overflow: hidden; box-shadow: 0 24px 56px -16px rgba(0,0,0,.5); }
.bk-sugg.show { display: flex; }
.bk-sugg-item { font: inherit; font-size: 14px; text-align: left; padding: 12px 14px; background: none; border: 0; border-bottom: 1px solid rgba(255,255,255,.10); color: #fff; cursor: pointer; }
.bk-sugg-item:last-child { border-bottom: 0; }
.bk-sugg-item:hover, .bk-sugg-item:focus-visible { background: rgba(230,210,177,.08); outline: none; }
.bk-prefill { font-size: 12px; color: rgba(255,255,255,.5); margin-top: 6px; }
.bk-prefill:empty { display: none; }
/* FIX-V2PAINT §3 — the sheet's real example media (order.js's MEDIA table,
   reused verbatim via OE.resolveMedia()/OE.serviceMedia()). */
.bk-sheet-media { margin: -24px -24px 14px; }
.bk-sheet-img { width: 100%; display: block; aspect-ratio: 3 / 2; object-fit: cover; }
.bk-sheet-frames { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.bk-sheet-frames .bk-sheet-img { aspect-ratio: 3 / 2; }
.bk-sheet-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.bk-sheet-pair-label { font-size: 10.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: rgba(255,255,255,.5); padding: 6px 10px 0; }
.bk-sheet-media-none { margin: 0 0 14px; padding: 12px 14px; background: #16161a; border: 1px dashed rgba(255,255,255,.24); border-radius: 10px; font-size: 12.5px; color: rgba(255,255,255,.5); }
.bk-sheet:has(.bk-sheet-media) .bk-sheet-close { background: rgba(13,13,13,.55); border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; }
@media (max-width: 640px) {
  .bk-cal-cell { font-size: 11px; padding: 4px; }
  .bk-times { grid-template-columns: repeat(3, 1fr); }
  /* SPEC-V2MODALFIT — same phone bottom-sheet posture as .order-lightbox
     (styles.css ~7298): was a centred floating card at any width, which on
     a phone reads as "doesn't quite fill the screen but doesn't feel like
     a sheet either." dvh (not vh) for the same iOS-toolbar reason. */
  .bk-sheet-backdrop { align-items: flex-end; padding: 0; }
  .bk-sheet { max-width: 100vw; width: 100%; max-height: 92dvh; border-radius: 16px 16px 0 0; margin: 0; }
}

/* ============================================================
   SPEC-V2BOOKING §4/§6 — bookstaff.js / book-settings.js (INTERNAL Hub
   views, Light Deep Harbor — the Hub's own existing theme, not the
   client-facing bk- namespace above). Kept out of book.css entirely
   (extract-book-css.js only scans book-public.js/book.html tokens).
   ============================================================ */
.bookstaff-cols { display: grid; grid-template-columns: 1.3fr 1fr; gap: 16px; align-items: start; }
.bookstaff-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.bookstaff-phone-frame { width: 220px; height: 440px; border: 8px solid var(--border-strong); border-radius: 24px; overflow: hidden; margin: 0 auto; background: #000; }
.bookstaff-phone-frame iframe { width: 100%; height: 100%; border: 0; }
.bookstaff-intel > div { padding: 6px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.bookstaff-override-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--border); }
.bookstaff-override-row .input { flex: 1; }
.bookstaff-override-banner { background: color-mix(in srgb, var(--warn) 12%, var(--card)); border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent); border-radius: 8px; padding: 8px 12px; margin-top: 10px; }
.bookstaff-override-line { font-size: 13px; padding: 4px 0; }
.bookstaff-row { display: flex; gap: 8px; align-items: center; }
.bookstaff-pre { background: var(--subtle); border: 1px solid var(--border); border-radius: 8px; padding: 10px; font-size: 11.5px; overflow-x: auto; max-height: 220px; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
@media (max-width: 860px) {
  .bookstaff-cols { grid-template-columns: 1fr; }
  .bookstaff-grid { grid-template-columns: 1fr; }
}

/* ---- Tonomo row alarm chip (tracker.js tonomoRowChip) — COORDINATOR 2026-09-03 ----
   MEASURED live against this stylesheet at 1180/1280/1380/1439/1440/1600px: the full
   chip is 82px and the address cell only has room for it from 1440px up. Below that the
   cell and its td both compute overflow:visible, so an 82px chip does not get clipped —
   it paints over the Services column. Shrinking it instead produces the illegible ~38px
   "⚠ C" stub the TRACKER-PHONE-BADGES note above already measured and rejected.
   So: full word where it fits, and below 1440 the word is dropped and the red ⚠️ carries
   the alarm on its own. The status word stays in the chip's title attribute and on the
   job popup's own chip, which has real room. Never a stub, never an overflow. */
.jt-addr .jt-tonomo-chip {
  background: var(--danger-soft);
  color: var(--danger-ink);
  font-weight: 700;
  font-size: 10.5px;
  padding: 0 6px;
  flex: 0 0 auto;          /* matches every other .jt-addr > * — never the one element that absorbs the deficit */
  min-width: 80px;
  max-width: 96px;
  overflow: hidden;
}
.jt-addr .jt-tonomo-chip .jt-tonomo-word {
  display: inline-block;
  min-width: 48px;
  max-width: 64px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}
@media (max-width: 1439px) {
  /* no room for the word — keep the alarm, drop the text, stay inside the cell */
  .jt-addr .jt-tonomo-chip { min-width: 0; max-width: none; padding: 0 5px; }
  .jt-addr .jt-tonomo-chip .jt-tonomo-word { display: none; }
}

/* ---- Pipelines strip: the two chips that are not pipelines (COORDINATOR 2026-09-03) ----
   "＋ Pipeline" and "⚙ Manage" sit in the same strip as the real tabs, so they must not
   read as one. Both stay full-size touch targets; they just carry no active state and are
   visually quieter than a board you can be standing on. */
.pl-tab.pipehome-tab-add,
.pl-tab.pipehome-tab-manage {
  color: var(--muted);
  font-weight: var(--fw-semibold);
}
.pl-tab.pipehome-tab-add {
  border: 1px dashed var(--border-strong);
  background: transparent;
}
.pl-tab.pipehome-tab-add:hover,
.pl-tab.pipehome-tab-manage:hover { color: var(--text); background: var(--hover); }
.pl-tab.pipehome-tab-manage { background: transparent; }

/* ═════════════════════════════════════════════════════════════════════════════════════
   SPEC-PHONEFIT 2026-09-05 — the phone density pass (Vincent, iPhone 16 Pro Max = 440×956
   CSS px, 09-05 morning: "everything is way too big even for a bigger iphone … the names
   take half the screen and then the address takes the other half").
   MEASURED BEFORE, live build at 440×956: the Tracker's header stack (crumb + subtabs +
   title + lesson sentence + view seg + New buttons + toolbar) put the FIRST job row at
   y=358 of 956; the table itself was 872px wide in a 410px pane (two screens of sideways
   scroll, the frozen Client column pinning 112px of it); a chat message's action rail
   rendered as four 44px-tall boxes STRETCHED to the message height beside a one-line
   message; the composer toolbar wrapped to three rows (107px).
   Breakpoint = App.PHONE_MQ (tracker.js, 700px) so JS and CSS agree on what a phone is.
   Placed LAST in the file on purpose: several of the rules below tie on specificity with
   the ones they override and win only by source order. Tests: PHONEFIT region.
   ═════════════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 700px) {
  /* ── 1. chrome: one size down, and no duplicate breadcrumb under a top bar that
         already reads "Jobs › Job Tracker" ── */
  :root { --fs-page-title: 24px; --fs-hero: 28px; --fs-display: 24px; }
  #view { padding-top: 10px; padding-left: 12px; padding-right: 12px; }
  .crumb { display: none; }
  .subtabs { margin-bottom: 10px; gap: 18px; }
  .view-header { margin-bottom: var(--space-3); padding-bottom: var(--space-2); gap: 8px 12px; }
  .view-sub { font-size: 12px; margin-top: 2px; }
  .jt-sub-lesson { display: none; }
  .toolbar { margin-bottom: 10px; }
  .card-pad { padding: 12px 14px; }
  .table th { padding: 8px 10px; }
  .table td { padding: 9px 10px; }
  .table th:first-child, .table td:first-child { padding-left: 12px; }
  .btn { padding: 6px 12px; }
  /* app-wide, second pass (Vincent 09-05: "its messy everywhere"): every view's subtitle is a
     one- to three-line lesson paragraph on a phone — one line, ellipsised, full text on desktop
     and on the title's tooltip-free hover; toolbars stop right-aligning orphans (the flex:1
     spacer that pushes "New / Fields" to the far edge is dead width once the bar wraps). */
  .view-header > * { min-width: 0; max-width: 100%; }   /* a nowrap child of a flex row otherwise grows to its text width — measured 551/686/587px pages */
  .view-sub { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
  .toolbar .spacer, .view-header .spacer { display: none; }
  .toolbar { justify-content: flex-start; row-gap: 6px; }
  /* KPI tiles: 109px each, two per row, on every dashboard-ish view (Snapshot, Money,
     Clients, Retainers, Outreach) — 15/17 padding → 10/12, value 22 → 20, gap 12 → 8. */
  .kpi-card { padding: 10px 12px; }
  .kpi-row { gap: 8px; }
  .kpi-value { font-size: 20px; margin-top: 2px; }
  /* Calendar: the day/week body was a two-pane ROW at 440px (a 260px hour grid beside a
     156px sidebar, the "1 shoot · 5.3h on site" caption wrapping one word per line) —
     the sidebar cards drop under the grid on a phone. */
  .cal-body-row { flex-direction: column; }
  .cal-body-row > * { width: 100%; max-width: none; flex: 0 0 auto; min-width: 0; }
  /* two more wrapped toolbars whose "push right" (space-between / margin-left:auto) left
     orphan buttons floating at the far edge once the row wrapped: Pipelines' tab head and
     Post & Review's toolbar. On a phone they simply flow. */
  #view .pipehome-tab-head, #view .pl-toolbar, #view .vpr-toolbar { justify-content: flex-start; gap: 8px; }
  #view .pipehome-tab-head > *, #view .pipehome-tab-head .pipehome-viewswitch, #view .pl-toolbar > *, #view .vpr-toolbar > * { margin-left: 0; }

  /* ── 2. Job Tracker, Table mode → a CARD LIST. Same DOM, same column registry, same
         test hooks (.jt-col-*, .jt-addr-cell, .group-head, .cv-row) — only the
         presentation changes. Each job is two lines: Client + Stage pill, then
         Address · Shoot · Price; a Needs-you chip takes a third line only when it exists.
         Hidden here: services, payment, package, priority, due, editor, creative,
         age, growth, links, the checkbox column and the ＋ add-column cell — all still
         one tap away in the row drawer and the job popup, and untouched on desktop.
         The wrap stops being its own scrollport: nested scroll on a phone is a trap,
         the page scrolls and the stage bands stick under the top bar instead. */
  .jt-table-wrap.jt-table-wrap { overflow: visible; max-height: none; scroll-snap-type: none; scroll-padding-left: 0; }
  .jt-table-wrap table.table, .jt-table-wrap table.table tbody { display: block; width: 100%; }
  .jt-table-wrap table.table thead { display: none; }
  /* the ROW is the sticky element here, not the cell (a sticky <td> can only travel within
     its own <tr>, which in block layout is exactly its own height — measured: 0px of stick).
     Bands stack: the next band slides over the last one under the fixed top bar. */
  .jt-table-wrap table.table tbody tr.group-head { display: block; position: sticky; top: calc(52px + env(safe-area-inset-top, 0px)); z-index: 3; background: var(--panel); }   /* opaque: the band tint is translucent and a stuck band showed the card under it */
  .jt-table-wrap.jt-table-wrap table.table tbody tr.group-head > td { display: block; position: static; top: auto; padding: 7px 10px; }
  /* the toolbar was a sideways scroll rail with Stage / People / By-date parked off-screen —
     it wraps to two rows now (search keeps ≥160px) and the saved-views chip row, a desktop
     power feature, steps out. The "N columns hidden · Show all" note is meaningless in card
     mode (the cards choose their own fields), so it goes too. */
  .jt-view .toolbar { flex-wrap: wrap; overflow: visible; gap: 6px; }
  /* two deliberate rows (measured at 440: 325px + 395px of a 416px toolbar), not a wrap
     that lands wherever: ① Active/All · Stage · People  ② Video only · By stage/date · search */
  .jt-view .toolbar .seg { order: 3; }
  .jt-view .toolbar .seg:first-child { order: 0; }
  .jt-view .toolbar .select { order: 1; flex: 1 1 0; min-width: 0; }   /* basis 0: the two selects SHARE row ① with Active/All instead of asking for their label width */
  .jt-view .toolbar .btn-sm { order: 2; }
  .jt-view .toolbar .search-input { order: 4; flex: 1 1 140px; min-width: 140px; width: auto; }
  .jt-view .toolbar .views-bar { display: none; }
  .jt-view .jt-showall-chip { display: none; }
  /* Vincent 09-05: "yes hide them please" — the New job / New video job row on phones; the ＋
     button (fab-dial → Quick add) is the phone's add path, and it is thumb-reachable. */
  .jt-header-btns > .btn-primary { display: none; }
  .jt-table-wrap .jt-addr { max-width: none; }
  /* #main is overflow-y:auto for the desktop split pane; below 1023px #app is display:block
     and the DOCUMENT scrolls, so that overflow only serves to trap sticky descendants inside
     a box that scrolls away with the page (measured: the band "stuck" at 4px, off the bar). */
  #main { overflow: visible; }
  .jt-table-wrap.jt-table-wrap table.table tbody tr.cv-row {
    display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto;
    grid-template-areas: "client client client stage" "addr shoot price stage" "needs needs needs needs";
    column-gap: 8px; row-gap: 3px; align-items: center;
    padding: 9px 10px 9px 12px; border-bottom: 1px solid var(--border);
    contain-intrinsic-size: auto 64px;
  }
  .jt-table-wrap.jt-table-wrap.jt-table-wrap table.table tbody tr.cv-row > td {
    display: block; padding: 0; border: 0; box-shadow: none; position: static; left: auto;
    min-width: 0; max-width: none; width: auto; background: transparent; overflow: visible;
  }
  .jt-table-wrap table.table tbody tr.cv-row > td.jt-col-client { grid-area: client; font-weight: var(--fw-semibold); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .jt-table-wrap table.table tbody tr.cv-row > td.jt-addr-cell { grid-area: addr; }
  .jt-table-wrap table.table tbody tr.cv-row > td.jt-col-status { grid-area: stage; justify-self: end; }
  .jt-table-wrap table.table tbody tr.cv-row > td.jt-col-shoot { grid-area: shoot; font-size: var(--fs-meta); color: var(--muted); white-space: nowrap; }
  .jt-table-wrap table.table tbody tr.cv-row > td.jt-col-price { grid-area: price; font-size: var(--fs-meta); white-space: nowrap; }
  .jt-table-wrap table.table tbody tr.cv-row > td.jt-needsyou-cell { grid-area: needs; }
  .jt-table-wrap table.table tbody tr.cv-row > td.jt-needsyou-cell:empty { display: none; }
  .jt-table-wrap table.table tbody tr.cv-row > td:not(.jt-col-client):not(.jt-addr-cell):not(.jt-col-status):not(.jt-col-shoot):not(.jt-col-price):not(.jt-needsyou-cell) { display: none; }
  .jt-table-wrap .jt-addr-thumb { display: none; }
  .jt-table-wrap .jt-addr-text { font-weight: var(--fw-medium); font-size: var(--fs-caption); }
  .jt-table-wrap .jt-cell-pencil { display: none; }
  .jt-table-wrap table.table tbody tr.jt-drawer-row { display: block; }
  .jt-table-wrap.jt-table-wrap table.table tbody tr.jt-drawer-row > td { display: block; padding: 0 0 10px; }

  /* ── 3. Chat: the per-message action rail hides until the message is TAPPED (the row
         is tabindex=0 — CONSOL-1 a11y — so a tap focuses it and :focus-within reveals the
         rail) and, when shown, sits on its own line under the text instead of stretched
         beside it. The composer's two attach pills go icon-only (their text stays for
         screen readers) and the "Enter sends" hint is dropped, so the toolbar is ONE row. */
  .chat-msg { flex-wrap: wrap; }
  .chat-msg .chat-msg-actions {
    display: none; flex: 0 0 100%; justify-content: flex-end; gap: 4px; margin: 2px 0 0; padding: 0;
    position: static; border: 0; box-shadow: none; background: transparent; opacity: 1; transform: none;
  }
  .chat-msg:focus-within .chat-msg-actions { display: flex; }
  .chat-msg .chat-msg-act { border: 1px solid var(--border); border-radius: 8px; background: var(--panel); padding: 0 12px; min-height: 40px; font-size: var(--fs-caption); }
  .chat-composer { padding: 0 8px 8px; }
  .chat-composer-toolbar { padding: 4px 5px 8px; gap: 1px; }
  .chat-composer-divider, .chat-composer-spacer { display: none; }
  .chat-composer-toolbar .chat-send { margin-left: auto; padding-left: 14px; padding-right: 14px; min-width: 0; }
  .chat-composer-toolbar .chat-tool--pill { width: 44px; min-width: 44px; padding: 0; justify-content: center; }
  .chat-composer-toolbar .chat-tool--pill > span:not([class]) { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }   /* the bare span is the label; the icon span carries a class and stays */
  .chat-composer-hint { display: none; }
}

/* ============================================================
   SPEC-ACTAS 2026-09-06 — PLAN-ROLEGATE-2026-09-05.md §11.2: "act as" a teammate
   (owner only). The "Acting as X" banner (#actas-banner, refreshActAsBanner in
   core.js), the shared account-menu popover (.acct-menu, openAccountMenu), and the
   button reset needed to turn the rail avatar / mobile top-bar avatar into real
   <button>s. All theme tokens (--warn/--warn-soft/--warn-ink/--text/--muted/--border/
   --hover/--panel) already exist (styles.css:41-54 + dark-mode block) — no new colors
   introduced.
   ============================================================ */
#actas-banner.actas-banner {
  display: flex; align-items: center; gap: 10px; padding: 8px 16px;
  background: var(--warn-soft); color: var(--text); border-bottom: 1px solid var(--warn);
  font-size: var(--fs-caption, 13px); line-height: 1.4;
}
.actas-banner-msg { flex: 1 1 auto; min-width: 0; }
.actas-banner-name { color: var(--warn-ink); }
.actas-banner-back { flex: 0 0 auto; }

/* the shared account-menu popover (rides inside App.popover's own .popover shell —
   styles.css:8173 — this only styles the CONTENT, not the positioning/shadow/border,
   which .popover already provides). */
.acct-menu { display: flex; flex-direction: column; gap: 2px; min-width: 200px; }
.acct-menu-row { padding: 6px 10px; }
.acct-menu-me { font-size: 12.5px; color: var(--muted); border-bottom: 1px solid var(--border); margin-bottom: 4px; padding-bottom: 8px; }
.acct-menu-actas { display: flex; flex-direction: column; gap: 4px; }
.acct-menu-label { font-size: 12px; color: var(--muted); font-weight: 600; }
.acct-menu-select { width: 100%; }
.acct-menu-item {
  display: block; width: 100%; text-align: left; padding: 8px 10px; border: none;
  background: transparent; border-radius: 8px; cursor: pointer; font-size: var(--fs-caption, 13px);
  color: var(--text); font: inherit;
}
.acct-menu-item:hover, .acct-menu-item:focus-visible { background: var(--hover); }

/* the rail avatar / mobile top-bar avatar are now real <button>s (were a plain <div>/
   nothing before) — reset the native button chrome so they paint exactly as before,
   just clickable. Higher-specificity id+class selector so this wins over the plain
   `.rail-avatar` layout rule earlier in this file without editing that rule in place. */
#rail-avatar.rail-avatar { background: transparent; border: none; width: 100%; cursor: pointer; font: inherit; }
#mobile-topbar .mt-avatar {
  width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; padding: 0; cursor: pointer; flex: 0 0 auto;
}

@media (max-width: 700px) {
  /* SPEC-ACTAS phone rules — the banner must fit ONE line at 440px (plan §11.2): the
     name ellipsises before "Back to me" ever gets pushed off-screen or wraps. */
  #actas-banner.actas-banner { flex-wrap: nowrap; padding: 8px 12px; gap: 8px; font-size: 12px; }
  .actas-banner-msg { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
  .actas-banner-back { white-space: nowrap; padding-left: 10px; padding-right: 10px; }
}

/* SPEC-INVITELINK 2026-09-06 (PLAN-ROLEGATE-2026-09-05.md §12) — App.showInviteLinkDialog
   (core.js), the small "Invite" dialog opened from the People card (team.js) and Settings
   → Team & roles. Built entirely from App.modal + .field/.input, so almost nothing new is
   needed — just the two spots that read better in a fixed-width font (a link and a
   ready-to-paste message are both things people compare character-for-character) and the
   small reassurance line underneath. */
.invite-link-message { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.5; }
.invite-link-note { font-size: 11.5px; }

/* ============================================================
   SPEC-PHONEFIT-2 2026-09-06 — PLAN-ACCOUNTSYNC-2026-09-06.md §A. Vincent: "chat feels a
   little messy or a little too much space between talking and the lower rail" and "it should
   work on a regular sized iphone (15)". MEASURED on the deploy-shaped copy at 440×956 (#general):
   .chat-layout = calc(100dvh - 150px) ends 31px above the tab bar, the composer adds 20px of
   padding under the card, the chips row (34) + the two-row card (input 52 + toolbar 56) stack
   to 170px, and the page still scrolls 70px (#view's tab-bar padding-bottom is double-counted
   under a fixed-height chat layout) so the whole box slides. Two iPhone-size sweeps (393×852,
   375×812, 51 views, scratchpad/sweep-393|375) found the page never pans sideways; the real
   hits were the 26px counters, the retainer pulse grid (fixed px columns) and the mobile top
   bar clipping its search button behind the new account avatar on a long "Section › View".
   Same @media the rest of PHONEFIT uses (= App.PHONE_MQ). CSS only — chat.js is another
   session's file and is untouched. */
@media (max-width: 700px) {
  /* chat: the conversation box fills exactly the space between the top bar and the tab bar,
     with an 8px breath above the tab bar — no slack under the composer, nothing to scroll. */
  body[data-view="chat"] #view { padding-bottom: 0; }
  body[data-view="chat"] .chat-layout {
    height: calc(100vh - 52px - env(safe-area-inset-top, 0px) - 10px - 58px - env(safe-area-inset-bottom, 0px) - 8px);
    height: calc(100dvh - 52px - env(safe-area-inset-top, 0px) - 10px - 58px - env(safe-area-inset-bottom, 0px) - 8px);
    min-height: 0;
  }
  body.kb-open[data-view="chat"] .chat-layout {
    height: calc(100dvh - 52px - env(safe-area-inset-top, 0px) - 10px - 8px);
  }
  /* one tight composer: chips become a single scrolling line, the card loses its air, the
     B · i · link pills fold away (markdown still types), the pills row stays one line. */
  .chat-composer { padding: 0 6px 6px; }
  .chat-composer-card { margin-top: 6px; }
  .chat-composer-card .chat-input { min-height: 44px; padding: 10px 14px 2px; }
  .chat-composer-toolbar { padding: 2px 6px 6px; flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  .chat-composer-toolbar::-webkit-scrollbar { display: none; }
  .chat-composer-toolbar .chat-tool--fmt { display: none; }
  .chat-composer-toolbar .chat-tool--pill { width: 40px; min-width: 40px; }
  .chat-quick-replies { display: flex; flex-wrap: nowrap; overflow-x: auto; gap: 6px; padding: 2px 0 0; scrollbar-width: none; }
  .chat-quick-replies::-webkit-scrollbar { display: none; }
  .chat-quick-replies .chat-quick-reply { flex: none; min-height: 30px; padding: 0 10px; font-size: 12.5px; }
  /* the two floating buttons sit above the composer, never over it: card ≈ 108px + chips 32
     + the 8px breath, re-derived from the NEW composer height (was 152+62+134 for the old stack). */
  body[data-view="chat"] .fab-dial-btn { bottom: calc(58px + env(safe-area-inset-bottom, 0px) + 8px + 160px); }
  body[data-view="chat"] .fab-dial-menu { bottom: calc(58px + env(safe-area-inset-bottom, 0px) + 8px + 160px + 68px); }
  /* voice.js's buildFridayBubble writes right/bottom INLINE (see the .fd-bubble comment near
     line 2460), so only !important can move it off the composer here — measured at 393×852:
     the inline 146px put the bubble across the message box's right edge. */
  body[data-view="chat"] .fd-bubble { bottom: calc(58px + env(safe-area-inset-bottom, 0px) + 8px + 160px + 68px) !important; }

  /* a phone widens its layout viewport to the widest box and zooms out to fit — Snapshot and
     People measured 437px of scrollable width at 393 (the preset strip's scroll extent leaking
     through a wrapper) with nothing actually reachable by panning. `clip` stops the leak without
     making #view a scroll container (sticky bands in PHONEFIT keep working). */
  #view { overflow-x: clip; }

  /* regular iPhone: the top bar has six things in 393px — the title yields, the pill and
     the search shrink, the gap tightens; nothing clips. */
  #mobile-topbar { gap: 6px; padding-left: 8px; padding-right: 8px; }
  #mobile-topbar .mt-title { flex: 1 1 0; min-width: 24px; }
  #mobile-topbar .mt-burger, #mobile-topbar .mt-search { width: 36px; height: 36px; flex: none; }
  #mobile-topbar .mt-bell, #mobile-topbar .mt-avatar { flex: none; }
  #mobile-topbar .sync-chip { flex: none; max-width: 64px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

  /* the 26px counters (the only ≥26px text the 393/375 sweeps found) */
  .today-proto .today-counter-n, .sa-big, .dw-big, .cstatus-impact-n { font-size: 22px; }

  /* Retainers › Signature pulse: fluid columns instead of 22+120+130+70 fixed px */
  .tss-pulse-row { grid-template-columns: 22px minmax(0, 1.4fr) minmax(0, 1.2fr) 56px; gap: 6px; }
  .tss-pulse-row > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
}

/* FRIDAY companion · 2026-09-07. Same footprint; illustrated, never a staff photo. */
.fd-companion-avatar {
  position: relative; display: inline-flex; flex: none; align-items: center; justify-content: center;
  overflow: hidden; border-radius: 50%; background: #262329; color: #e3bcab;
  box-shadow: inset 0 0 0 1px rgba(227,188,171,.4);
}
.fd-companion-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.fd-bubble { padding: 2px; }
.fd-companion-head { gap: 7px; flex: none; }
.fd-companion-name { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.fd-companion-name small { color: #a7a3aa; font-size: 10px; white-space: nowrap; }
.fd-companion-head .fd-voice-iconbtn { min-width: 44px; min-height: 44px; }
.fd-voice-mini .fri-composer .textarea { width: 100%; flex: 1 0 100%; min-height: 88px; }
.fd-voice-mini .fri-composer > .btn { min-height: 44px; margin-left: auto; }
.fd-companion-talk { background: none; border: 0; padding: 0; text-align: left; min-height: 44px; font: inherit; color: inherit; }
/* vmWaveBars returns a wrapper. Without a flex row here its 26 bars stack
   vertically through the header instead of forming a bounded waveform. */
.fd-wave-mini > div, .fd-wave-full > div { display: flex; align-items: center; justify-content: center; gap: 3px; }
.fd-companion-tools { margin-top: 12px; border-top: 1px solid rgba(227,188,171,.15); padding-top: 4px; }
.fd-companion-tools summary { cursor: pointer; color: #e3bcab; font-size: 12px; padding: 14px 0; }
.fd-companion-tool-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; padding-bottom: 4px; }
.fd-companion-tool-grid .btn { min-height: 44px; white-space: normal; text-align: left; justify-content: flex-start; }
.fd-companion-head button:focus-visible, .fd-companion-tools summary:focus-visible { outline: 2px solid #e3bcab; outline-offset: 2px; }
@media (max-width: 440px) {
  .fd-companion-head { padding-left: 10px; padding-right: 10px; gap: 5px; }
  .fd-companion-head .chip-btn { display: none; } /* Full Friday keeps the review queue. */
}
@media (prefers-reduced-motion: reduce) {
  .fd-wave-mini span, .fd-wave-full span { animation: none !important; transition: none; }
}
.fd-companion-avatar.is-speaking { animation: eve-speaking 1.8s ease-in-out infinite; }
@keyframes eve-speaking { 0%, 100% { transform: translateY(0); box-shadow: 0 0 0 2px rgba(227,188,171,.2); } 50% { transform: translateY(-1px); box-shadow: 0 0 0 3px rgba(227,188,171,.45); } }
.eve-voice-fields { display: grid; gap: 10px; padding: 4px 0; }
.eve-voice-fields label { display: grid; gap: 5px; font-size: 12px; color: #c9c3c5; }
.eve-voice-fields select, .eve-voice-fields button { min-height: 44px; width: 100%; }
@media (prefers-reduced-motion: reduce) { .fd-companion-avatar.is-speaking { animation: none; box-shadow: 0 0 0 2px #e3bcab; } }

/* SPEC-PROPWS 2026-09-09 — Property Workspace (property-workspace.js). Big tab
   buttons (one primary action per tab, per the north star), a simple thumb grid
   for Media. Reuses .card/.chip/.btn tokens throughout — nothing new below is
   colored on its own; only layout. */
.pws-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin: 4px 0 14px; }
.pws-tab { min-height: 44px; padding: 8px 16px; border-radius: 10px; border: 1px solid var(--border); background: var(--card); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; }
.pws-tab--active { background: var(--accent); color: #fff; border-color: var(--accent); }
.pws-body { min-height: 120px; }
.pws-media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 6px; }
.pws-media-thumb { position: relative; aspect-ratio: 1; border: 0; padding: 0; border-radius: 8px; cursor: pointer; background-size: cover; background-position: center; background-color: var(--hover); }
.pws-media-thumb--sel { outline: 3px solid var(--accent); outline-offset: -3px; }
.pws-media-badge { position: absolute; top: 6px; left: 6px; background: rgba(0,0,0,.55); color: #fff; font-size: 10.5px; font-weight: 700; padding: 2px 6px; border-radius: 999px; }
.pws-media-badge--cover { left: auto; right: 6px; background: var(--accent); }
.pws-selbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
@media (max-width: 520px) {
  .pws-tab { flex: 1 1 auto; text-align: center; padding: 8px 6px; font-size: 12.5px; }
  .pws-media-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
}

/* PLAN-ONEJOB Phase 2D (2026-09-10) — media.js "All media" thumbnail browser.
   Real <img> tiles (not a CSS backgroundImage, unlike .pws-media-thumb) so
   loading="lazy" and empty alt text actually apply; 1:1, ≥96px tap targets,
   a bottom-gradient caption strip for the job address. */
.media-browse-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(112px, 1fr)); gap: 8px; }
.media-tile { position: relative; aspect-ratio: 1; min-width: 96px; min-height: 96px; border: 1px solid var(--border); padding: 0; border-radius: 10px; overflow: hidden; cursor: pointer; background: var(--hover); }
.media-tile img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.media-tile--sel { outline: 3px solid var(--accent); outline-offset: -3px; }
.media-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.media-tile-fallback { width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; background: #1b2433; color: #fff; }
.media-tile-play { font-size: 20px; }
.media-tile-title { font-size: 10px; padding: 0 6px; max-width: 100%; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.media-tile-badge { position: absolute; top: 6px; right: 6px; background: var(--accent); color: #fff; font-size: 10.5px; font-weight: 700; padding: 2px 6px; border-radius: 999px; }
.media-tile-caption { position: absolute; left: 0; right: 0; bottom: 0; background: linear-gradient(transparent, rgba(0,0,0,.68)); color: #fff; font-size: 10.5px; padding: 12px 6px 4px; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 520px) {
  .media-browse-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
}

/* SPEC-GALCHECK 2026-09-10 — PLAN-SNAPPER Phase I photo-check strip (tracker.js
   gallery manager + property-workspace.js Media tab). Outline only — never a
   background/border-radius change to the thumb itself, so a highlighted photo
   still reads at a glance next to an un-highlighted one. */
.gal-hl { outline: 2px solid var(--accent); outline-offset: 2px; }
.gal-check-chip--on { background: var(--accent); color: #fff; }
/* JOBAUDIT 2026-09-10 — kanban card cover thumb (same source as the popup hero) */
.kc-headrow { display: flex; gap: 10px; align-items: flex-start; min-width: 0; }
.kc-headrow > :last-child { min-width: 0; flex: 1 1 auto; }
.kc-thumb { flex: 0 0 48px; width: 48px; height: 48px; border-radius: 8px; background-size: cover; background-position: center; background-color: var(--hover); }

/* SPEC-ONEJOB 2026-09-10 — the ONE JOB page (job-page.js). Mobile-first single
   column, capped at 1100px like the other big-page views; big 44px tab
   targets and primary buttons (dummy-proof, per the plan); focus-visible
   outlines everywhere a keyboard user can land. Reuses .card/.chip/.btn
   tokens throughout — nothing here recolors anything, only layout. */
.jp { max-width: 1100px; margin: 0 auto; }
.jp-back { margin-bottom: 8px; }
.jp-header { margin-bottom: 14px; }
.jp-hero { width: 100%; aspect-ratio: 16 / 9; max-height: 320px; border-radius: 12px; background-color: var(--subtle); background-size: cover; background-position: center; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; margin-bottom: 10px; }
.jp-address { font-size: 22px; margin: 0 0 8px; }
.jp-header-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.jp-status-pill { min-height: 32px; border-radius: 999px; padding: 4px 14px; font-weight: 700; font-size: 12.5px; }
.jp-status-pill:disabled { cursor: default; opacity: .85; }
.jp-crew { display: inline-flex; gap: 4px; flex-wrap: wrap; }
.jp-crew-avatar { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 10.5px; font-weight: 700; }
.jp-tabs { display: flex; gap: 6px; flex-wrap: wrap; margin: 4px 0 14px; }
.jp-tab { min-height: 44px; padding: 8px 16px; border-radius: 10px; border: 1px solid var(--border); background: var(--card); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; }
.jp-tab--active { background: var(--accent); color: #fff; border-color: var(--accent); }
.jp-panel { min-height: 160px; }
.jp-subtitle { font-size: 11.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); margin: 0 0 8px; }
.jp-deliver-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 8px 0; border-top: 1px solid var(--border); }
.jp-svc-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.jp-actions .btn-lg, .jp-film-body .btn, .jp-actions button { min-height: 44px; }
/* JOBCONTROLS-FIX1 2026-09-14 (JC-UX-ROW-JUMP) — align-items:flex-start (not the
   flex default of stretch) so an opened film-body More drawer growing taller
   doesn't stretch/re-center the poster beside it. */
.jp-film-card { display: flex; gap: 10px; padding: 10px; border: 1px solid var(--border); border-radius: 10px; margin-bottom: 10px; background: var(--card); align-items: flex-start; }
.jp-film-card--empty { border-style: dashed; }
.jp-film-poster { flex: 0 0 96px; width: 96px; height: 72px; border-radius: 8px; background-size: cover; background-position: center; background-color: var(--hover); }
.jp-film-body { flex: 1 1 auto; min-width: 0; }
/* JOBCONTROLS 2026-09-14 — replaces PM-CONSOLIDATE-0914's .jp-action-* block.
   The disclosure now reads 'More' with the app's own rotating-›  marker (see
   .today-collapse for the same pattern) instead of the browser's own
   ▶/▼ triangle, and there is no [open] accent border (it used to stack on
   the focus outline). .jp-action-group is gone — App.qcGateFoot no longer
   renders on this page, so nothing builds that wrapper any more. */
.jp-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-start; padding: 12px; margin-bottom: 18px; border: 1px solid var(--border); border-radius: 12px; background: var(--card); }
.jp-action-disclosure { min-width: 0; max-width: 100%; }
.jp-action-summary { list-style: none; display: flex; align-items: center; gap: 8px; box-sizing: border-box; min-height: 44px; padding: 12px 14px; border: 1px solid var(--border); border-radius: 10px; background: var(--card); color: var(--text); font-size: 13px; font-weight: 600; cursor: pointer; overflow-wrap: anywhere; }
.jp-action-summary::-webkit-details-marker { display: none; }
.jp-action-summary::after { content: '›'; margin-left: auto; color: var(--muted); transition: transform .15s; }
.jp-action-disclosure[open] > .jp-action-summary::after { transform: rotate(90deg); }
.jp-action-options { display: flex; flex-wrap: wrap; gap: 8px; padding-top: 10px; }
/* JOBCONTROLS 2026-09-14 — every button a film's controls can place directly
   (preflight's primary, or one inside a More drawer) stays a ≥44px tap
   target, matching the app's own big-targets north star, even though
   filmControls builds them as btn-sm. */
.jp-action-options > .btn, .jp-film-actions > .btn, .jp-deliver-row > .btn, .jp-deliver-sub > .btn { min-height: 44px; white-space: normal; }
/* JOBCONTROLS-FIX2 2026-09-14 (finding 2) — explicit flex-start (the initial
   value already behaves this way, but the click-hazard finding asked for it
   spelled out): nothing in this row grows, so an opening More never slides
   the primary — see the .jp-deliver-row comment below for the row that DID
   have that bug. */
.jp-film-actions { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: flex-start; gap: 8px; }
.jp-film-actions > .qc-preflight, .jp-deliver-row > .qc-preflight, .jp-deliver-sub > .qc-preflight { flex-basis: 100%; }
/* JOBCONTROLS-FIX1 2026-09-14 (JC-UX-ROW-JUMP) — .jp-deliver-row/-sub keep
   align-items:center for the collapsed line, but an OPENED More disclosure's
   2-3 button drawer made that whole row's vertical center jump on every
   open/close. Drop the opened drawer to its own full-width line instead so
   the dot/name/chip/link/primary stay put. */
.jp-deliver-row > .jp-action-disclosure[open], .jp-deliver-sub > .jp-action-disclosure[open] { flex-basis: 100%; }
/* JOBCONTROLS 2026-09-14 (PMC-06) — the 2nd/3rd video on a multi-video
   service, indented under its service row on Overview. */
.jp-deliver-sub { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 6px 0 6px 20px; border-top: 1px dashed var(--border); }
.jp-film-poster[type="button"] { border: 1px solid var(--border); padding: 0; cursor: pointer; }
.jp summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.jp-sheet-choice { border-radius: 10px; }
.jp button:focus-visible, .jp a:focus-visible, .jp [role="tab"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 520px) {
  .jp-tab { flex: 1 1 auto; text-align: center; padding: 8px 6px; font-size: 12px; }
  /* JOBCONTROLS 2026-09-14 — was flex-direction:column; flex-wrap lets the
     poster (already width:100% below) and body wrap onto their own rows
     without forcing every .jp-film-card child (incl. a future one) vertical
     by fiat. Folded the old 700px rules in here too — one phone breakpoint
     for this page, not two overlapping ones. */
  .jp-film-card { flex-wrap: wrap; }
  /* JOBCONTROLS-FIX1 2026-09-14 (JC-CSS-PHONE-POSTER) — width alone does nothing
     in a row flex container once a non-auto flex-basis (96px, set above) has
     already fixed the main size; the poster stayed a 96px-wide sliver at
     160px tall (a portrait crop of a 16:9 cover frame) instead of full width.
     flex-basis:100% here overrides that main size. Button reset for
     .jp-film-poster[type="button"] (outside this media query) is unaffected. */
  .jp-film-poster { flex: 0 0 100%; width: 100%; height: 160px; }
  .jp-film-actions { flex: 1 1 100%; max-width: 100%; }
  .jp-action-disclosure { flex-basis: 100%; }
}

/* ===== SPEC-CALFIT 2026-09-14 =====
   Calendar Day/Week: hide quiet hours + fit the day to the screen (calendar.js).
   Appended-only block, per the build spec — nothing above this line is touched.
   `.is-fit` is added to `.calview` by calendar.js's fitHourGrid only on a real,
   laid-out desktop shell (≤860px never gets it — #app itself is display:block
   there per the existing rule at ~1468, and the page scrolls instead). */

/* the fitted card no longer needs #main/#view's own bottom clearance (the FAB
   dial's 88px reservation at ~2526, #view's own 60px at ~337) — its own inline
   height already accounts for exactly the room available.
   JUDGE FIX 09-14 (false-comments) — corrected specificity: :has() takes its
   most specific argument (Selectors 4), so `#main:has(.calview.is-fit)` and
   `#view:has(.calview.is-fit)` are both #id (1,0,0) + `.calview.is-fit` (0,2,0)
   = (1,2,0), not (1,1,0) as this comment used to say. That beats
   `body[data-view="jobs"] main` — tag(0,0,1) + attr(0,1,0) + tag(0,0,1) =
   (0,1,2) — and beats plain `#view` (1,0,0) the same way. Both rules still win
   in practice (measured: #main padding-bottom 0px with .is-fit, 88px again on
   Tracker) — this was a wrong number in the comment, never a behavior bug.
   Stops matching the instant Calendar leaves the DOM (nothing inline is left
   on the persistent #main/#view shell nodes). */
#main:has(.calview.is-fit) { padding-bottom: 0; }
#view:has(.calview.is-fit) { padding-bottom: 16px; }
/* the sidebar's last card can still scroll clear of the fixed FAB dial once the
   sidebar itself is a fitted, scrolling column rather than the page. */
.calview.is-fit .cal-sidebar::after { content: ''; flex: 0 0 64px; }

/* R2/R6 — the FIRST visible hour's label used to always be hour 0 (blank by
   design, ~line 940 `h === 0 ? '' : …`); now the first row can be 7am (or
   whatever the range starts at) and `.cal-hour-label { top: -7px }` clipped it
   half off the top of the scroller. A 1px hairline replaces the clipped-away
   top border the old always-24-rows grid effectively had at row 0. */
.calview .cal-hourgrid-scroll .cal-hour-gutter .cal-hour-row:first-child .cal-hour-label { top: 1px; }
.calview .cal-hourgrid-scroll .cal-hour-gutter .cal-hour-row:first-child,
.calview .cal-hourgrid-col > .cal-hour-cell:first-child { box-shadow: inset 0 1px 0 var(--border); }

/* the Day topbar's own hint text ("Click an empty hour…") is long enough that a
   fitted, shorter card could wrap it onto a 2nd line and grow the "chrome"
   fitHourGrid measured a moment earlier — never at odds with its own math.
   JUDGE FIX 09-14 (phone-topbar-nowrap-clip, major, verified/corrected) — these
   two rules used to apply at EVERY width, unscoped. Below the ~940px point
   where the fit still doesn't have room to avoid wrapping — every phone,
   every tablet, and desktop widths just over the 861px fit threshold — nowrap
   with no ellipsis hard-clipped the shoot-count span mid-word and shrank the
   hint span to 0px, with no wrap fallback (measured: 390px phone scrollWidth
   452 vs clientWidth 364, hint span 0px wide). A bare `@media (min-width:861px)`
   gate (the first fix tried) still clipped ~11px at 861-935px, because that is
   exactly the width range where `.is-fit` is already true but the card is
   still narrow — scope by `.is-fit` (what the fit chrome-measurement actually
   depends on), not by a viewport guess, and let EVERY muted span (not just
   the last one) ellipsize instead of hard-clip. Below `.is-fit` (phones,
   tablets, any bail) the topbar keeps its pre-CALFIT wrapping — nothing lost
   there, matching the byte-identical topbar markup. */
.calview.is-fit .cal-hourgrid-topbar > span { white-space: nowrap; }
.calview.is-fit .cal-hourgrid-topbar > .muted-line { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* Week's 7-day header (.cal-dow-week, a `display:grid` row with its own 6px
   gaps) sits ABOVE `.cal-hourgrid-inner` (a flex row: a 58px gutter then 7
   zero-gap columns) — without a matching left offset AND a matching zero gap,
   the header's own columns drift out of register with the grid below by a few
   px per column, worse toward the right edge. `column-gap: 0` removes the
   header's gap so both rows divide the SAME (width − 58px) into 7 equal
   shares. Live-verified against `.cal-hourgrid-col` centers (see the build
   report). */
.calview .cal-dow-week { padding-left: 58px; column-gap: 0; }

/* ≤860px never reaches fitHourGrid (see the .is-fit gate above), so the
   pre-fit card/sidebar stay at their historical fixed 720px UNLESS this rule
   lets them size to content — the same "≤700px" block above this one targeted
   the now-retired week-agenda markup, not this hour grid, and never covered
   this case (a documented pre-existing gap, not introduced here). */
@media (max-width: 860px) {
  .calview:not(.is-fit) .cal-hourgrid-card, .calview:not(.is-fit) .cal-sidebar { height: auto; }
}
/* R6 — LAWA's own 44px tap-floor ratchet for the new toggle button on narrow
   widths (the desktop .btn.btn-sm floor is shorter). */
@media (max-width: 700px) {
  .calview .cal-hours-toggle { min-height: 44px; }
}

/* JUDGE FIX 09-14 (fit-blocks-lose-address, major, verified/corrected) — the
   scaled block floor (App.calendarBlockFloorPx) shrinks a Day block enough
   that flex-shrink quietly collapsed .cal-hblock-addr/.cal-ev-sub to a few px
   of unreadable sliver instead of clipping whole lines (measured: address
   3-13px tall at every fitted desktop size, 1/5 to 1/7 blocks readable). The
   verifier's SMALLER fix (not the first draft's `display:none` on the status
   chip, which would have hit every Week block too — Week is ALWAYS compact,
   and Week's status chip is untouched by this bug): keep the chip, just
   shrink it, and stop the address/sub boxes from collapsing under 0. */
.calview .cal-hblock-addr, .calview .cal-hblock .cal-ev-sub { flex-shrink: 0; }
.calview .cal-hblock--compact .cal-hblock-top > .chip { padding: 0 7px; font-size: 10px; line-height: 16px; }
/* Compact Twilight badges must fit the same 16px header as status/avatar.
   Their normal 20.25px box pushed the address/sub-line below short cards. */
.calview .cal-hblock--compact .cal-hblock-tag { padding: 0 7px; line-height: 14px; }
/* inline 20px avatar size (hourBlock's own `size: opts.compact ? 14 : 20`) is
   set on the element itself, so only !important can shrink a DAY block's
   avatar further without a second render — fitHourGrid toggles --compact on
   an EXISTING DOM node, it never rebuilds the avatar. */
.calview .cal-hblock--compact .cal-hblock-av { width: 16px !important; height: 16px !important; }
/* flex-shrink:0 stops the ADDRESS from collapsing, but a block too short to
   also hold the agent/services sub-line still clips it against the block's
   own overflow:hidden — grey, cut-off text is worse than no sub-line at all.
   fitHourGrid (and hourBlock's own initial render) add this class when the
   block's applied height can't hold top row + address + sub + padding — see
   the JS for the measured threshold; this rule is the visual half. */
.calview .cal-hblock--short .cal-ev-sub { display: none; }

/* JUDGE FIX 09-14 (lane-head-chips-change-chrome / lane-head-chips-stack,
   minor) — each 🌴/🔒 chip in a lane head is a block-level, full-width
   element (.sched-off/.sched-busy, same chrome as the single-lane strips),
   so every chip added a WHOLE line to the head row — chrome fitHourGrid
   measures — and 2-3 busy events could shrink rows all the way to the floor
   and bring back an inner scrollbar on an otherwise-fitting day. Chips now
   share one line; several short chips add at most ~21px total instead of
   ~21px EACH. Deliberately keeps the '❓ Not synced yet' row (WAVE4C FINDING
   2, calendar.js ~245) — dropping it would let a genuinely-unknown day read
   as confirmed-clear, the exact risk that chip exists to prevent. */
.calview .cal-hlane-head-id > .sched-off, .calview .cal-hlane-head-id > .sched-busy { display: inline-flex; margin: 2px 4px 0 0; vertical-align: middle; }
/* ===== SPEC-CALFIT END ===== */
/* EVE LIVE 09-16: no-face identity; input bars reflect measured microphone level.
   Speaking remains a lifecycle indicator, not a claimed output-audio waveform. */
.fd-companion-avatar[data-eve-avatar="orb"] { background: radial-gradient(circle at 32% 28%, #fff3d9 0, #dcbba5 18%, #897aa7 48%, #24223e 78%); box-shadow: inset 0 0 9px #ffffff55, 0 0 14px #dcbba522; }
.fd-companion-avatar[data-eve-avatar="orb"] > svg { opacity: .35; }
.fd-companion-avatar[data-eve-avatar="spark"] { background: #252338; color: #ead5bf; }
.fd-wave-mini.listening span, .fd-wave-full.listening span { animation: none; transform: scaleY(var(--mic-level, .08)); background: #80d7bb; }
.fd-wave-mini.listening span:nth-child(3n), .fd-wave-full.listening span:nth-child(3n) { opacity: .65; }
.eve-voice-fields input[type="checkbox"] { width: 20px; height: 20px; accent-color: #dcbba5; }
.fd-voice-full-head { flex-wrap: wrap; }
.fd-voice-full-head .fd-companion-talk { min-width: 0; flex: 1 1 150px; }
.fd-voice-full-head .fd-btn-ghost { flex: 0 0 auto; min-height: 44px; }
@media (max-width: 640px) {
  .fd-voice-full-head { padding: 12px 10px; gap: 8px; }
  .fd-voice-full-head > .faint { flex: 1 1 100%; order: 2; }
}

/* ============================================================
   ORDERV2 — SPEC-ORDERV2-O1O2 2026-09-22. order.html?v=2's own dark-
   cinematic layer (order-flow.js). Every token lives on .of-root, NEVER on
   :root, so it can never leak onto the admin Hub or the v1 order page.
   Regenerate order.css after any class change here or in order-flow.js:
     node tools/extract-order-css.js
   ============================================================ */
/* JUDGE 2026-09-23 (screenshot walk) item 5 — every order-flow.js dialog
   (<dialog>, showModal()) is appended straight to document.body (native
   modal top-layer requirement), so it is a SIBLING of #of-root in the DOM,
   never a descendant — CSS custom properties only cascade to descendants,
   so var(--of-*) resolved to nothing for every dialog button/chip/input,
   rendering them as unstyled plain text. .of-root-scope is a CLASS-based
   twin of the same token block (order-flow.js adds it to every dialog
   element) so tokens resolve regardless of DOM position, with none of
   .of-root's own full-page layout (display/min-height/padding) that a
   dialog must never inherit. Never move these onto :root — see this
   block's own header, one line below: this must never leak dark-cinematic
   tokens onto the admin Hub or the v1 order page. */
.of-root, .of-root-scope {
  --of-bg: #0d0d0d; --of-surface: #161616; --of-line: rgba(255,255,255,.08);
  --of-ink: #f2efe9; --of-ink-2: rgba(242,239,233,.62); --of-accent: #e6d2b1; --of-radius: 14px;
  /* ORDERV5 R0 2026-09-26 -- V3: named so .of-steparea's own clearance and
     every flagged control's scroll-margin-bottom stay tied to the REAL bar
     height instead of a second magic number that can drift from it again
     (measured bar height 81px at both 1440 and 390 -- see build report). */
  --of-bar-h: 88px;
  color: var(--of-ink);
  font-family: 'Plus Jakarta Sans', 'Inter var', Inter, system-ui, -apple-system, sans-serif;
}
.of-root { display: block; min-height: 100vh; background: var(--of-bg); padding: 16px 16px 96px; box-sizing: border-box; }
#of-root[data-cooldown] .of-steparea { pointer-events: none; }   /* r2_6a fix -- scoped to .of-steparea (was the whole #of-root, incl. .of-bar): Back/Continue sit at an identical position every screen and can never be "a different control painted underneath", so blocking them too just ate a real Back-then-Continue tap. ORDERV5 R0 2026-09-26 -- rule 7: 350ms after any screen change, real taps (not test .click() calls) are swallowed on the repainted step content -- fixes the measured Q1->Q2 double-tap ghost answer */
.of-root *, .of-root-scope * { box-sizing: border-box; }
.of-root button, .of-root input, .of-root select, .of-root textarea,
.of-root-scope button, .of-root-scope input, .of-root-scope select, .of-root-scope textarea { font-family: inherit; font-size: 16px; color: var(--of-ink); }
.of-h1 { font-size: 22px; font-weight: 700; line-height: 1.25; margin: 4px 0 14px; letter-spacing: -0.01em; }
/* ORDERV5 R0 2026-09-26 -- V3: on arrival (no scroll), a required control could sit under the fixed bar -- measured -58px (desktop Review terms checkbox) and -54px (mobile property 'Size is right'), both now clear by ~55-60px. Stacks with .of-root's own 96px bottom padding on purpose (that padding is generic breathing room, not sized to the bar). */
.of-steparea { display: block; }   /* judge80 P2#5 fix -- the +120px never actually cleared the bar on arrival (measured identical to base79) and made every path 1.2-2.2 screens longer; reveal-scroll deferred, see build notes */
.of-step { max-width: 640px; margin: 0 auto; display: flex; flex-direction: column; gap: 14px; }
.of-muted { color: var(--of-ink-2); font-size: 14px; line-height: 1.5; margin: 0; }
.of-fine { color: var(--of-ink-2); font-size: 12.5px; line-height: 1.5; margin: 0; }
.of-price { font-weight: 700; font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; }
.of-err { color: #f2a5a5; font-size: 13px; min-height: 16px; margin: 0; }
.of-tip { color: var(--of-accent); font-size: 12.5px; margin: 0; }
.of-benefit { font-size: 12.5px; color: var(--of-accent); margin: 0; }
.of-pkg-savings { font-size: 12.5px; color: var(--of-accent); margin: 0; }   /* ORDERV5 S2 2026-09-25 */
.of-staging-nudge { margin-top: 4px; }   /* ORDERV5 S2 2026-09-25 */
.of-nudge { font-size: 12.5px; color: var(--of-ink-2); background: var(--of-surface); border: 1px solid var(--of-line); border-radius: 10px; padding: 8px 10px; }

.of-rail { display: flex; flex-wrap: nowrap; overflow: hidden; gap: 6px; max-width: 640px; margin: 0 auto 18px; padding-top: 4px; }
.of-dot { width: 22px; height: 22px; border-radius: 999px; border: 1px solid var(--of-line); display: inline-flex; align-items: center; justify-content: center; font-size: 11px; color: var(--of-ink-2); }
.of-dot.on { background: var(--of-accent); color: #14110a; border-color: var(--of-accent); font-weight: 700; }
.of-dot.done { border-color: var(--of-accent); color: var(--of-accent); }

.of-payband { display: flex; align-items: center; justify-content: space-between; gap: 10px; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: var(--of-radius); padding: 12px 14px; font-size: 13.5px; }
.of-link { background: none; border: 0; color: var(--of-accent); font-weight: 600; font-size: 13.5px; padding: 8px 4px; min-height: 44px; cursor: pointer; text-decoration: underline; }

.of-label { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--of-ink-2); }
.of-label-text { font-size: 13px; color: var(--of-ink-2); margin: 0; }
.of-row { display: flex; gap: 10px; }
.of-half { flex: 1 1 0; }
.of-input { background: var(--of-surface); border: 1px solid var(--of-line); border-radius: 10px; padding: 12px 12px; min-height: 44px; width: 100%; }
.of-input:focus { outline: 2px solid var(--of-accent); outline-offset: 1px; }
.of-check { display: flex; align-items: flex-start; gap: 10px; font-size: 13.5px; color: var(--of-ink-2); min-height: 44px; scroll-margin-bottom: calc(var(--of-bar-h) + 16px); }   /* ORDERV5 R0 2026-09-26 -- V3: 'Size is right' + the terms checkbox */
.of-check input { width: 20px; height: 20px; margin-top: 2px; accent-color: var(--of-accent); }
.of-honeypot { position: absolute !important; left: -9999px !important; width: 1px; height: 1px; opacity: 0; }

/* JUDGE PASS 2b item 7 (LOW) — display was unset, so a <button class=of-btn>
   (a block-ish replaced element, text auto-centers) looked fine while an
   <a class=of-btn> (plain `inline`, no vertical centering at all) sat its
   text high/left inside the 44px pill — mismatched against the done
   screen's own buttons. inline-flex centers BOTH element types identically,
   and text-decoration:none stops an anchor from underlining its label. */
.of-btn { display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 16px; border-radius: 10px; border: 1px solid var(--of-line); background: transparent; color: var(--of-ink); font-weight: 600; font-size: 13.5px; cursor: pointer; text-decoration: none; box-sizing: border-box; }
/* JUDGE round 2 (design pass) — ".of-root button{color:var(--of-ink)}" above is
   (class, element) specificity, which beat plain ".of-btn-primary{color:#14110a}"
   (class-only) regardless of source order — every primary button rendered
   near-white text on champagne, unreadable (the punch list's "low contrast" and
   part of the same family as the pinned-bar visibility bug). ".of-root
   .of-btn-primary" (class, class) now outranks it. */
.of-root .of-btn-primary, .of-root-scope .of-btn-primary { background: var(--of-accent); color: #14110a; border-color: var(--of-accent); }
.of-btn-primary:disabled { opacity: .45; cursor: not-allowed; }
.of-cardbtns { display: flex; gap: 8px; flex-wrap: wrap; }

.of-cardrow { display: grid; grid-template-columns: 1fr; gap: 10px; }
.of-card { display: flex; flex-direction: column; gap: 4px; text-align: left; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: var(--of-radius); padding: 14px; min-height: 64px; cursor: pointer; }
.of-card.on { border-color: var(--of-accent); box-shadow: 0 0 0 1px var(--of-accent) inset; }
.of-card strong { font-size: 15px; }
.of-card span { color: var(--of-ink-2); font-size: 13px; }

.of-pkglist { display: grid; grid-template-columns: 1fr; gap: 12px; }
.of-pkgcard { position: relative; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: var(--of-radius); padding: 16px; display: flex; flex-direction: column; gap: 8px; }
.of-pkgcard.on { border-color: var(--of-accent); }
.of-flag { align-self: flex-start; font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; background: var(--of-accent); color: #14110a; border-radius: 999px; padding: 3px 9px; }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor */
.of-flag-goal { background: transparent; border: 1px solid var(--of-accent); color: var(--of-accent); }
.of-includes summary { cursor: pointer; color: var(--of-accent); font-size: 13px; min-height: 44px; display: flex; align-items: center; }
.of-includes ul { margin: 6px 0 0; padding-left: 18px; font-size: 13px; color: var(--of-ink-2); }

.of-pairbar { display: flex; flex-direction: column; gap: 8px; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: var(--of-radius); padding: 12px 14px; }
.of-chiprow { display: flex; flex-wrap: wrap; gap: 8px; }
.of-chip { min-height: 44px; padding: 0 14px; border-radius: 999px; border: 1px solid var(--of-line); background: transparent; color: var(--of-ink); font-size: 13px; font-weight: 600; cursor: pointer; }
.of-chip.on { background: var(--of-accent); color: #14110a; border-color: var(--of-accent); }
.of-chip.off { opacity: .35; cursor: not-allowed; }

.of-addonlist { display: grid; grid-template-columns: 1fr; gap: 12px; }
.of-addoncard { background: var(--of-surface); border: 1px solid var(--of-line); border-radius: var(--of-radius); padding: 14px; display: flex; flex-direction: column; gap: 6px; }
.of-addoncard.on { border-color: var(--of-accent); }

/* ORDERV5 VTWI 2026-09-25 -- shared -/+ qty control, used by BOTH the
   Virtual Twilight add-on card (Add-ons step, state.addons qty=extras) and
   the Virtual Staging picker (Add-ons step + Your style step, SAME shared
   function, state.photoEdit.staging.qty=rooms) -- one control, one look,
   44px targets per the mobile tap-target law. */
/* ORDERV5 VTWI 2026-09-25 -- FIX (live-verify, phone width): wrap instead
   of overflowing the card at the narrow 2-up mobile grid; the two 44px
   buttons never shrink below their tap-target size, only the qty/unit text
   between them does. */
.of-qtyrow { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 10px; margin-top: 4px; }
.of-qty-btn { width: 44px; height: 44px; min-width: 44px; flex: 0 0 44px; border-radius: 999px; border: 1px solid var(--of-line); background: var(--of-surface); color: var(--of-ink); font-size: 20px; font-weight: 700; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.of-qty-btn:hover { background: color-mix(in srgb, var(--of-accent) 10%, var(--of-surface)); }
.of-qty-btn:disabled { opacity: .35; cursor: not-allowed; }
.of-qty-val { min-width: 26px; text-align: center; font-weight: 700; font-size: 15px; color: var(--of-ink); }
.of-qty-unit { font-size: 12px; color: var(--of-ink-2); flex: 1 1 0; min-width: 0; line-height: 1.2; }
.of-qtyrow { flex-wrap: nowrap; }   /* ORDERV5 judge75 — the + dropped to a second line on a 174px phone card */
.of-qty-nudge { color: var(--of-accent); font-weight: 600; }

/* ORDERV4 PASS2 (E) — the Add-ons step's category rows + compact cards.
   Scoped to `.of-addon-rows` so the OTHER `.of-addonlist` use (the "Photo
   finishing touches" checkbox list on Your style) keeps its own 1/2-column
   layout untouched. 2 cols base (<=640px), 3 cols 641-1099px, 4 cols
   >=1100px, per spec. `of-step-wide` on the step wrapper (order-flow.js
   stepAddons) is what makes the 1280px container available in the first
   place — JUDGE round 5 item 1: the step used to be plain `of-step`
   (640px), which squeezed this whole grid into a narrow column regardless
   of the column-count rules below. */
.of-addon-rows { display: flex; flex-direction: column; gap: 22px; }
.of-addon-rowhead { margin: 0 0 10px; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--of-ink-2); }
.of-addon-rows .of-addonlist { grid-template-columns: repeat(2, 1fr); gap: 10px; }
.of-addon-rows .of-addoncard { padding: 12px; gap: 6px; }
/* JUDGE round 5 item 2 — every text node here is margin-reset: <h4>/<p>'s
   own UA default block margins (~1em top+bottom each) used to stack ON TOP
   OF the card's flex `gap`, not replace it — that doubled spacing is what
   read as "~40px of dead space" around the price on a narrow card. Only
   `gap` controls spacing now. The Add/Added button is pushed to the card's
   own bottom edge (margin-top:auto) so cards of different content heights
   in the same grid row still end on the same line. */
.of-addon-rows .of-addoncard h4, .of-addon-rows .of-addoncard > p { margin: 0; }
.of-addon-titlerow { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.of-addon-titlerow h4 { font-size: 14.5px; line-height: 1.3; }
.of-addon-titlerow .of-price { font-size: 13px; white-space: nowrap; }
.of-addon-titlerow { flex-wrap: wrap; row-gap: 2px; }   /* ORDERV5 S2 polish — "+$200 upgrade" was cut off on narrow phone cards */
.of-addon-titlerow h4 { min-width: 0; }
.of-addon-rows .of-addon-photo { aspect-ratio: 4 / 3; height: auto; }
.of-addon-reason { font-size: 12px; line-height: 1.4; color: var(--of-ink-2); margin: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.of-addon-toggle { width: 100%; margin-top: auto; }
/* JUDGE round 5 item 3 (optional) — the Your Brand card spans 2 grid
   columns at >=641px, so its horizontal stepper has room to breathe.
   JUDGE round 6 item 3 fix — round 5's own comment ("a span-2 card is
   already a natural full-width row" at the 2-col phone grid) was WRONG: a
   grid item with no explicit span defaults to ONE column, so at <=640px
   the brand card was HALF width and its 4 segments measured 37x44 —
   under the 44px tap-target floor. <=640px now spans the FULL row
   explicitly (`1 / -1`, both grid tracks) so each segment gets a real
   >=44px-wide share of the whole viewport width. */
@media (min-width: 641px) {
  .of-addoncard-brand { grid-column: span 2; }
}
@media (max-width: 640px) {
  .of-addoncard-brand { grid-column: 1 / -1; }
}
.of-brand-steplabel { display: block; margin-top: 2px; }
/* JUDGE round 5 item 3 — a real horizontal segmented control (4 equal
   columns: None | 2 | 3 | 4), never the vertical .of-chip stack the first
   pass shipped. Every segment is >=44px tall and the whole control fits on
   one line even at 375px (4 equal fractions of the card's own width). */
.of-brand-stepper { display: grid; grid-template-columns: repeat(4, 1fr); border: 1px solid var(--of-line); border-radius: 10px; overflow: hidden; margin-top: 4px; }
.of-brand-seg { min-height: 44px; border: 0; border-right: 1px solid var(--of-line); background: transparent; color: var(--of-ink); font-size: 13px; font-weight: 700; cursor: pointer; }
.of-brand-seg:last-child { border-right: 0; }
.of-brand-seg.on { background: var(--of-accent); color: #14110a; }
@media (min-width: 641px) and (max-width: 1099px) {
  .of-addon-rows .of-addonlist { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1100px) {
  .of-addon-rows .of-addonlist { grid-template-columns: repeat(4, 1fr); }
}

.of-slots { display: flex; flex-direction: column; gap: 10px; }
.of-schedule-stale { color: var(--of-accent); margin-bottom: 8px; }   /* JUDGE round 6 item 8 */
.of-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.of-daychip { min-height: 44px; border-radius: 8px; border: 1px solid var(--of-line); background: var(--of-surface); color: var(--of-ink); font-size: 12px; cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 4px 2px; }
.of-daychip-dow { font-size: 12px; color: var(--of-ink-2); text-transform: uppercase; }   /* JUDGE round 6 item 9 — was 10px */
.of-daychip-date { font-size: 12px; font-weight: 600; }
.of-daychip.on { border-color: var(--of-accent); background: var(--of-accent); color: #14110a; }
/* SPEC-ORDERV4-2026-09-23 (small visual fix) — .of-daychip-dow hardcodes
   color: var(--of-ink-2) (a translucent light gray tuned for the DARK
   --of-surface background) and .on never overrode it, only the parent's
   `color`, which child text nodes with their OWN explicit color ignore. On
   the selected chip's champagne (--of-accent #e6d2b1) background this read
   as near-invisible ("FRI" unreadable). Matches .of-daychip.on's own #14110a
   (measured contrast 12.75:1 against #e6d2b1 — WCAG AA needs 4.5:1 for this
   12px text; AAA needs 7:1). */
.of-daychip.on .of-daychip-dow { color: #14110a; }
.of-daychip.off { opacity: .3; cursor: not-allowed; }

.of-review-sec { border-top: 1px solid var(--of-line); padding-top: 12px; display: flex; flex-direction: column; gap: 6px; }
.of-review-head { display: flex; align-items: center; justify-content: space-between; }
.of-review-head h3 { margin: 0; font-size: 14px; }
.of-offer { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; background: var(--of-surface); border: 1px dashed var(--of-accent); border-radius: var(--of-radius); padding: 12px 14px; }
.of-done { align-items: center; text-align: center; padding-top: 40px; }
/* SPEC-ORDERV4-2026-09-23 (J) — the confirmation screen's summary card +
   prep-guide card. */
.of-done-lead { color: var(--of-ink-2); max-width: 44ch; }
.of-done-summary { width: 100%; max-width: 420px; display: flex; flex-direction: column; gap: 6px; text-align: left; background: var(--of-surface); border-radius: var(--of-radius); padding: 14px 16px; margin: 4px 0; }
.of-done-row { display: flex; justify-content: space-between; gap: 12px; font-size: 13.5px; }
.of-done-label { color: var(--of-ink-2); }
.of-prep-card { width: 100%; max-width: 420px; text-align: left; background: var(--of-surface); border-radius: var(--of-radius); padding: 16px; margin-top: 18px; display: flex; flex-direction: column; gap: 10px; }
.of-prep-title { margin: 0; font-size: 15px; }
.of-prep-list { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 4px; font-size: 13.5px; color: var(--of-ink); }
.of-prep-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.of-prep-actions .of-btn { min-height: 44px; flex: 1 1 auto; }
.of-prep-copystatus { font-size: 12px; color: var(--of-accent); min-height: 14px; }
.of-prep-linkfallback { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.of-prep-linkfallback.is-shown { position: static; width: auto; height: auto; opacity: 1; min-height: 44px; padding: 8px 10px; border-radius: 8px; border: 1px solid var(--of-line); background: var(--of-bg); color: var(--of-ink); font-size: 13px; }

.of-bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 500; background: rgba(13,13,13,.92); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border-top: 1px solid var(--of-line, rgba(255,255,255,.08)); padding: 10px max(16px, env(safe-area-inset-left)) calc(10px + env(safe-area-inset-bottom)); }
.of-bar-inner { max-width: 640px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; gap: 12px; }   /* ORDERV5 R0 2026-09-26 -- V4: dropped flex-wrap -- a wrapped bar-inner is what grew the bar 81px -> 95px and shifted Continue sideways */
.of-bar-left { display: flex; flex-direction: column; gap: 2px; font-size: 11.5px; color: rgba(242,239,233,.62); flex: 1 1 auto; min-width: 0; }   /* ORDERV5 R0 2026-09-26 -- V4: shrinks first, so the right side (Back/Continue) never moves */
.of-bar-band { font-size: 11.5px; }
.of-bar-right { display: flex; align-items: center; gap: 12px; flex: 0 0 auto; }   /* ORDERV5 R0 2026-09-26 -- V4: never shrinks -- Back/Continue keep their own width regardless of the left label's length */
.of-bar-amt { font-weight: 700; font-variant-numeric: tabular-nums; color: #e6d2b1; }

.of-dialog { border: 1px solid rgba(255,255,255,.08); border-radius: 14px; background: #161616; color: #f2efe9; padding: 0; max-width: 480px; width: calc(100vw - 32px); }
.of-dialog::backdrop { background: rgba(0,0,0,.6); }
.of-dialog-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 1px solid rgba(255,255,255,.08); }
.of-dialog-title { font-weight: 700; font-size: 15px; }
.of-dialog-close { min-height: 44px; min-width: 44px; background: none; border: 0; color: #f2efe9; font-size: 16px; cursor: pointer; }
.of-dialog-body { padding: 14px 16px 18px; display: flex; flex-direction: column; gap: 10px; font-size: 13.5px; line-height: 1.55; color: rgba(242,239,233,.85); }

@media (min-width: 640px) {
  .of-pkglist { grid-template-columns: 1fr 1fr; }
  .of-addonlist { grid-template-columns: 1fr 1fr; }
  .of-cardrow { grid-template-columns: repeat(4, 1fr); }
}

/* ============================================================
   ORDERV2 round 2 — JUDGE + orchestrator design pass, 2026-09-23. Wider
   desktop layout, step head (eyebrow/headline/sub), labeled step rail,
   package/add-on/vision photos, the Landing screen, and the Review two-
   column layout. Still every token on .of-root, still `of-` prefixed.
   ============================================================ */
.of-root { max-width: 1280px; margin: 0 auto; }
.of-stephead { display: flex; flex-direction: column; gap: 6px; margin-bottom: 4px; }
.of-eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--of-accent); margin: 0; }
.of-sub { font-size: 17px; color: var(--of-ink-2); margin: 0; line-height: 1.4; }
.of-stephead .of-h1 { margin: 0; }

.of-dot { width: auto; height: auto; padding: 0; border: 0; display: flex; align-items: center; gap: 8px; flex: 1 1 0; min-width: 0; }
.of-dot-num { width: 22px; height: 22px; flex: none; border-radius: 999px; border: 1px solid var(--of-line); display: inline-flex; align-items: center; justify-content: center; font-size: 11px; color: var(--of-ink-2); }
.of-dot.done .of-dot-num { border-color: var(--of-accent); color: var(--of-accent); }
.of-dot-label { display: none; font-size: 11px; color: var(--of-ink-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: .02em; min-width: 0; }
/* round 3 items 1/2 — the CURRENT step is a real champagne PILL (background
   wraps num+label together, both texts #0d0d0d) — not just a colored circle
   with unstyled (near-white, on the page's own dark bg — never actually
   "on champagne" but read that way against the pill shape) text beside it.
   The pill's own label always shows, even below the 640px breakpoint that
   hides every OTHER step's label (item 1's "narrow widths: dots + current
   label only"), and shrinks/truncates rather than push the rail wider than
   its own flex item at 800px. */
/* the current pill does NOT shrink/ellipsis — flex:0 0 auto sizes it to its
   own content, so the connector line (flex:1 1 auto below) and the OTHER
   (shrinkable, min-width:0) dots give way to it instead. Children of a flex
   container are "blockified" per spec regardless of their own `display`
   value, which is why the label needs overflow:visible here — `hidden` +
   an inherited min-width:0 on a flex-blockified span collapsed it to 0
   width with nothing visibly wrong in the rule itself. */
.of-dot.on { background: #e6d2b1; border-radius: 999px; padding: 3px 12px 3px 3px; flex: 0 0 auto; }
.of-dot.on .of-dot-num { background: transparent; border-color: transparent; color: #0d0d0d; font-weight: 700; }
.of-dot.on .of-dot-label { display: inline; color: #0d0d0d; font-weight: 600; overflow: visible; text-overflow: clip; min-width: 0; flex: 0 0 auto; }
.of-dot::after { content: ''; flex: 1 1 auto; height: 1px; background: var(--of-line); min-width: 6px; }
.of-dot:last-child::after { display: none; }

.of-bar-steplabel { font-weight: 700; font-size: 13.5px; display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }   /* ORDERV5 R0 2026-09-26 -- V4: one line, ellipsized ('Prices start at / $400' wrapping to 2 lines is what grew the bar */

.of-pkg-photowrap { position: relative; border-radius: calc(var(--of-radius) - 4px); overflow: hidden; margin: -16px -16px 12px; }
.of-pkg-photo, .of-vision-photo, .of-addon-photo, .of-landing-pkg-photo { display: block; width: 100%; height: 180px; object-fit: cover; }
.of-vision-photo { height: 260px; border-radius: var(--of-radius); margin-bottom: 10px; }
.of-includes-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--of-ink-2); }
.of-includes-list li::before { content: '✓ '; color: var(--of-accent); }
.of-includes-more { color: var(--of-accent) !important; }
.of-includes-more::before { content: '' !important; }
.of-itemized { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; font-size: 13.5px; }
.of-itemized li { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.of-itemized li span:last-child { font-variant-numeric: tabular-nums; color: var(--of-ink-2); }

.of-addon-photowrap { position: relative; border: 0; padding: 0; background: none; cursor: pointer; margin: -14px -14px 10px; display: block; width: calc(100% + 28px); }
.of-photochip { position: absolute; right: 8px; bottom: 8px; background: rgba(13,13,13,.72); color: #fff; font-size: 12px; padding: 4px 9px; border-radius: 999px; }   /* JUDGE round 6 item 9 — was 11px */
.of-tip { text-transform: uppercase; letter-spacing: .06em; font-size: 12px; }   /* JUDGE round 6 item 9 — was 11px; this rule (cascade order) wins over the 12.5px .of-tip above */

.of-review { max-width: none; }
.of-review-grid { display: grid; grid-template-columns: 1fr; gap: 20px; align-items: start; }
.of-review-main { display: flex; flex-direction: column; gap: 0; }
.of-review-aside { background: var(--of-surface); border: 1px solid var(--of-line); border-radius: var(--of-radius); padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.of-review-total { font-size: 20px; }
.of-paycards { display: flex; flex-direction: column; gap: 8px; }
.of-paycard { text-align: left; background: transparent; border: 1px solid var(--of-line); border-radius: 10px; padding: 12px; min-height: 44px; color: var(--of-ink); display: flex; flex-direction: column; gap: 2px; cursor: pointer; }
.of-paycard strong { font-size: 14px; }
.of-paycard span { font-size: 12.5px; color: var(--of-ink-2); }
.of-paycard.on { border-color: var(--of-accent); box-shadow: 0 0 0 1px var(--of-accent) inset; }

.of-landing { display: flex; flex-direction: column; gap: 20px; }
.of-landing-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding-top: 4px; }
.of-wordmark { font-size: 13px; font-weight: 800; letter-spacing: .12em; }
.of-landing-meta { font-size: 12px; color: var(--of-ink-2); }
.of-landing-hero { position: relative; border-radius: var(--of-radius); overflow: hidden; background: var(--of-surface); }
.of-landing-hero-img { display: block; width: 100%; height: 320px; object-fit: cover; }
.of-landing-hero-copy { position: absolute; left: 0; right: 0; bottom: 0; padding: 20px; display: flex; flex-direction: column; gap: 12px; background: linear-gradient(to top, rgba(13,13,13,.88), transparent); }
.of-landing-hero-copy h1 { font-size: 30px; font-weight: 800; color: #fff; margin: 0; }
.of-landing-subhead { font-size: 18px; font-weight: 700; margin: 0; }
.of-landing-pkgs { display: grid; grid-template-columns: 1fr; gap: 12px; }
.of-landing-pkgcard { text-align: left; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: var(--of-radius); padding: 14px; display: flex; flex-direction: column; gap: 4px; cursor: pointer; }
.of-landing-pkgcard .of-link { padding: 0; min-height: 0; }
.of-landing-unsure { align-self: center; }

/* ORDERV5 CONSULT-5b 2026-09-25 — pre-flow screens: Start (resume + "Help
   me choose" / "I know what I want"), Ask (one question per screen, big
   tap targets), Rec (the recommended package). Reuses of-landing-header/
   of-wordmark/of-landing-meta/of-h1/of-sub/of-btn/of-link/of-price so the
   look matches the existing cinematic landing exactly. */
.of-start { display: flex; flex-direction: column; gap: 20px; }
.of-start-hero { position: relative; border-radius: var(--of-radius); overflow: hidden; background: var(--of-surface); }
.of-start-hero-img { display: block; width: 100%; height: 320px; object-fit: cover; }
.of-start-hero-copy { position: absolute; left: 0; right: 0; bottom: 0; padding: 20px; display: flex; flex-direction: column; gap: 10px; background: linear-gradient(to top, rgba(13,13,13,.88), transparent); }
.of-start-hero-copy .of-h1 { color: #fff; margin: 0; }
.of-start-hero-copy .of-sub { color: rgba(255,255,255,.86); margin: 0; }
.of-start-actions { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 6px 0 14px; }
.of-start-cta { min-height: 52px; padding: 0 28px; font-size: 15px; align-self: stretch; }
.of-start-skip { align-self: center; }

.of-ask { display: flex; flex-direction: column; gap: 22px; padding-top: 4px; }
.of-ask-block { display: flex; flex-direction: column; gap: 14px; }
.of-ask-title { margin: 0; }
.of-ask-tiles { display: grid; grid-template-columns: 1fr; gap: 10px; }
.of-ask-tile { min-height: 44px; padding: 14px 16px; border-radius: var(--of-radius); border: 1px solid var(--of-line); background: var(--of-surface); color: var(--of-ink); font-size: 15px; font-weight: 600; text-align: left; cursor: pointer; }
.of-ask-tile.on { border-color: var(--of-accent); background: var(--of-accent); color: #14110a; }
.of-ask-actions { display: flex; justify-content: flex-start; }

.of-rec { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; padding-top: 4px; }
.of-rec-photo { display: block; width: 100%; height: 220px; object-fit: cover; border-radius: var(--of-radius); }
.of-rec-price { font-size: 20px; }
.of-rec-why { margin: 4px 0 6px; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; color: var(--of-ink-2); font-size: 14.5px; }
.of-rec-start { min-height: 52px; padding: 0 28px; font-size: 15px; align-self: stretch; }
.of-rec-links { display: flex; flex-wrap: wrap; gap: 14px; align-self: center; }

@media (min-width: 640px) {
  .of-ask-tiles { grid-template-columns: 1fr 1fr; }
  .of-start-hero-img, .of-rec-photo { height: 360px; }
}


@media (min-width: 640px) {
  .of-dot-label { display: inline; }
  .of-landing-pkgs { grid-template-columns: 1fr 1fr; }
}
@media (min-width: 900px) {
  .of-review-grid { grid-template-columns: 1.6fr 1fr; }
  .of-review-aside { position: sticky; top: 16px; }
  .of-landing-pkgs { grid-template-columns: repeat(3, 1fr); }   /* ORDERV3 §3i — 3 listing cards; the photos-only row has its own rule below */
  .of-landing-hero-img { height: 420px; }
  .of-landing-hero-copy h1 { font-size: 44px; }
}
@media (max-width: 640px) {
  .of-bar-inner { flex-wrap: nowrap; }
  .of-bar-left { display: none; }   /* phone: compact one row, total + Continue only */
}

/* ============================================================
   ORDERV2 v3 — SPEC-ORDERV3-2026-09-23 PASS 1. Persistent header, the
   address combobox, vision tiles, package option C (grouped + tierDiff +
   examples dialog + goal sheet + build-your-own), the Review "Your details"
   block, and the Landing screen's listing/photos-only split. Every token
   still lives on .of-root — `of-` prefixed classes only.
   node tools/extract-order-css.js after any change here or in order-flow.js.
   ============================================================ */
.of-header { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 4px 0 14px; max-width: 1280px; margin: 0 auto; }
.of-header-pay { display: flex; align-items: center; gap: 10px; font-size: 12.5px; color: var(--of-ink-2); }

.of-step-wide { max-width: 1280px; }
.of-step-home { max-width: 1280px; }

/* ---------- step 1: the address combobox + property grid ---------- */
.of-property-grid { display: grid; grid-template-columns: 1fr; gap: 24px; align-items: start; }
.of-property-main { display: flex; flex-direction: column; gap: 16px; }
.of-property-aside { display: flex; flex-direction: column; gap: 12px; }
.of-property-hero { display: block; width: 100%; height: 220px; object-fit: cover; border-radius: var(--of-radius); }
.of-combobox { position: relative; }
.of-addr-input { position: relative; z-index: 1; }
.of-addr-list { position: absolute; z-index: 40; left: 0; right: 0; margin: 4px 0 0; padding: 6px; list-style: none; background: #1e1e21; border: 1px solid var(--of-line); border-radius: 12px; box-shadow: 0 16px 40px rgba(0,0,0,.5); max-height: 280px; overflow-y: auto; }
.of-addr-opt { display: flex; gap: 10px; align-items: baseline; padding: 12px 10px; border-radius: 8px; font-size: 14.5px; cursor: pointer; color: var(--of-ink); }
.of-addr-opt strong { font-weight: 700; }
.of-addr-opt span { color: var(--of-ink-2); font-size: 13px; }
.of-addr-opt.on, .of-addr-opt:hover { background: rgba(230,210,177,.10); }
.of-row3 { display: grid; grid-template-columns: 1fr; gap: 10px; }
.of-countynote { display: flex; flex-direction: column; gap: 10px; padding: 12px 14px; background: rgba(230,210,177,.08); border: 1px solid rgba(230,210,177,.28); border-radius: var(--of-radius); }
.of-countynote span { font-size: 13px; color: var(--of-accent); }
.of-side-pricebox { display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: 14px; }
.of-side-pricebox-label { font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--of-ink-2); }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor */
.of-side-priceline { display: flex; justify-content: space-between; font-size: 14px; }

/* ---------- step 2: vision tiles ---------- */
.of-vision-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
.of-vision-tile { position: relative; display: block; height: 240px; padding: 0; overflow: hidden; text-align: left; cursor: pointer; border-radius: 18px; background: var(--of-surface); border: 2px solid transparent; }
.of-vision-tile.on { border-color: var(--of-accent); box-shadow: 0 0 0 4px rgba(230,210,177,.14); }
.of-vision-tile-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.of-vision-gradient { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(13,13,13,0) 35%, rgba(13,13,13,.92) 100%); }
.of-vision-selected { position: absolute; top: 12px; right: 12px; font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; padding: 5px 10px; border-radius: 999px; background: var(--of-accent); color: #14110a; }   /* judge80 P3 fix -- V11 12px floor */
.of-vision-copy { position: absolute; left: 16px; right: 16px; bottom: 16px; display: flex; flex-direction: column; gap: 4px; }
.of-vision-copy strong { font-size: 19px; font-weight: 800; color: #fff; letter-spacing: -.01em; }
.of-vision-copy span { font-size: 13.5px; color: #d2d4d8; }
.of-vision-unsure { align-self: center; }

/* ---------- step 3: package option C ---------- */
.of-pkg-grouphead { display: flex; align-items: center; gap: 12px; margin-top: 6px; }
.of-pkg-grouphead > span:first-child { font-size: 12px; font-weight: 800; letter-spacing: .12em; text-transform: uppercase; }
.of-pkg-grouphead .of-hr { flex-grow: 1; height: 1px; background: var(--of-line); }
/* ORDERV4 PASS2 (A) — package rows/cards are a real CSS grid with
   grid-template-rows:subgrid, so every card's 9 sections (ribbon, photo,
   title/price, lead, stat, rule, head, list, buttons) line up across the
   row at every breakpoint — not flex + eyeballed spacing. `.of-pkgcard-body`
   is `display:contents` so its children become direct grid items of
   `.of-pkgcard-full`'s subgrid (title/price..buttons = the 7 tracks after
   ribbon+photo). SPEC A said to delete the old recommended-card lift-up
   transform entirely — `.rec` reads as recommended via its border/glow/
   ribbon alone now, never a Y-axis shift. */
.of-pkgrow { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(7, auto) 1fr auto; gap: 14px; padding-top: 10px; }
.of-pkgcard-full { position: relative; grid-row: span 9; display: grid; grid-template-rows: subgrid; overflow: hidden; border-radius: 18px; background: var(--of-surface); border: 2px solid var(--of-line); }
.of-pkgcard-full.on { border-color: var(--of-accent); }
.of-pkgcard-full.rec { background: #1c1a15; border-color: rgba(230,210,177,.55); box-shadow: 0 0 0 4px rgba(230,210,177,.10); }
.of-pkgcard-full.rec.on { border-color: var(--of-accent); }
.of-pkg-ribbon { display: flex; align-items: center; justify-content: center; min-height: 32px; background: var(--of-accent); color: #0d0d0d; font-size: 12px; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; text-align: center; padding: 4px 8px; }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor */
.of-pkg-ribbon.is-empty { background: transparent; color: transparent; }
.of-pkg-ribbon-fin { background: #1e1e21; color: var(--of-accent); }
.of-pkgcard-full .of-pkg-photo { aspect-ratio: 16 / 10; width: 100%; height: auto; object-fit: cover; }
.of-pkgcard-body { display: contents; }
.of-pkg-titlerow { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; padding: 0 16px; margin-top: 12px; }
.of-pkg-titlerow h3, .of-pkg-titlerow h4 { margin: 0; font-size: 18px; font-weight: 800; }
.of-pkg-lead { font-size: 14px; font-weight: 600; margin: 0; padding: 0 16px; }
.of-pkgcard-full .of-benefit { padding: 0 16px; margin: 0; min-height: 1em; }
.of-pkgcard-full .of-pkg-savings { padding: 0 16px; margin: 0; min-height: 1em; }   /* ORDERV5 S2 2026-09-25 */
.of-pkg-rule { border: 0; height: 1px; background: var(--of-line); margin: 2px 16px; }
.of-pkg-plusline { font-size: 12px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--of-ink-2); margin: 0; padding: 0 16px; }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor */
.of-diff-list { list-style: none; margin: 0; padding: 0 16px; display: flex; flex-direction: column; gap: 3px; font-size: 13px; color: #d2d4d8; }
.of-diff-drop { color: var(--of-ink-2); text-decoration: line-through; text-decoration-color: rgba(255,255,255,.25); }
.of-cardbtns-full { display: grid; grid-template-columns: 1fr 1.4fr; gap: 8px; padding: 0 16px 18px; align-self: end; }

.of-pkgrow-photo { display: grid; grid-template-columns: 1fr; gap: 14px; padding-top: 10px; }
.of-pkgcard-h { display: grid; grid-template-columns: 1fr; overflow: hidden; border-radius: 16px; background: var(--of-surface); border: 2px solid var(--of-line); }
.of-pkgcard-h.on { border-color: var(--of-accent); }
.of-pkg-photo-h { display: block; width: 100%; height: 170px; object-fit: cover; }
.of-pkgcard-h-body { display: flex; flex-direction: column; gap: 8px; padding: 16px 18px 18px; }
.of-inc-chip { font-size: 12px; padding: 4px 9px; border-radius: 999px; background: #1e1e21; color: var(--of-ink-2); }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor */
.of-alc-linkwrap { display: flex; justify-content: center; margin-top: 6px; }
.of-link-quiet { color: var(--of-ink-2); text-decoration-color: var(--of-ink-2); }

/* examples dialog + goal sheet + build-your-own share .of-dialog; widen them */
.of-examples-dialog, .of-sheet { max-width: 640px; }
.of-example-photo { display: block; width: 100%; max-height: 320px; object-fit: cover; border-radius: 10px; margin-bottom: 8px; }

/* ORDERV4 PASS2 (F) — the examples carousel (openExamples + add-on/Your
   Brand "See example"). A scroll-snap track (one slide per view), prev/next
   arrows, dots, and the ArrowLeft/Right keys on the focusable wrap. */
.of-carousel { position: relative; display: flex; flex-direction: column; gap: 10px; outline: none; }
.of-carousel-track { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; gap: 0; border-radius: 12px; scrollbar-width: none; }
.of-carousel-track::-webkit-scrollbar { display: none; }
.of-carousel-slide { flex: 0 0 100%; scroll-snap-align: start; display: flex; flex-direction: column; gap: 6px; }
.of-carousel-media { position: relative; width: 100%; aspect-ratio: 4 / 3; border-radius: 12px; overflow: hidden; background: #1e1e21; }
.of-carousel-media.is-vertical { aspect-ratio: 9 / 16; max-height: 70vh; margin: 0 auto; max-width: 280px; }
.of-carousel-media img, .of-carousel-media video, .of-carousel-media iframe { display: block; width: 100%; height: 100%; object-fit: cover; border: 0; }
/* JUDGE PASS 2b item 7 (LOW) — order-flow.js stamps every carousel <img>
   with this class (exampleImg()/carouselSlideMedia()), but the only rule
   that used to style it was the parent-descendant selector above; a class
   with no own rule reads as dead CSS. Same declarations, self-sufficient. */
.of-carousel-img { display: block; width: 100%; height: 100%; object-fit: cover; border: 0; }
.of-carousel-video .of-carousel-play { position: absolute; inset: 0; width: 100%; height: 100%; min-height: 44px; background: rgba(13,13,13,.38); color: #fff; border: 0; font-size: 14px; font-weight: 600; cursor: pointer; }
.of-carousel-chip { align-self: flex-start; font-size: 12px; padding: 4px 9px; border-radius: 999px; background: #1e1e21; color: var(--of-ink-2); }   /* r2_v11a fix -- was 11.5px, under the spec's 12px floor (V11) */
.of-carousel-benefit { margin: 0; font-size: 13px; color: var(--of-ink-2); }
.of-carousel-caption { margin: 0; font-size: 12px; color: var(--of-ink-2); }
.of-carousel-arrow { position: absolute; top: calc(50% - 22px); min-width: 44px; min-height: 44px; border-radius: 999px; border: 0; background: rgba(13,13,13,.62); color: #fff; font-size: 20px; cursor: pointer; }
.of-carousel-prev { left: 4px; }
.of-carousel-next { right: 4px; }
.of-carousel-dots { display: flex; justify-content: center; align-items: center; }
/* JUDGE PASS 2b item 7 (LOW) — the visible dot stays 8px (a crowded row of
   real 44px circles would look absurd), but the TAP TARGET is a real
   44x44px button; the small circle is drawn with ::after so no JS change
   was needed to add a wrapper span. */
.of-carousel-dot { width: 44px; height: 44px; min-width: 44px; min-height: 44px; padding: 0; margin: 0; border-radius: 999px; border: 0; background: transparent; cursor: pointer; position: relative; }
.of-carousel-dot::after { content: ''; position: absolute; top: 50%; left: 50%; width: 8px; height: 8px; margin: -4px 0 0 -4px; border-radius: 999px; background: var(--of-line); }
.of-carousel-dot.on::after { background: var(--of-accent); }
.of-sheet-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }
.of-sheet-tile { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; padding: 12px; background: #1e1e21; border-radius: 12px; }
.of-sheet-tile img { width: 100%; height: 120px; object-fit: cover; border-radius: 8px; }
.of-sheet-continue { width: 100%; margin-top: 4px; }

/* ---------- Review — "Your details" (§3g minimal) ---------- */
.of-details-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }

/* ---------- Landing — photos-only row (§3i) ---------- */
.of-landing-subhead-sm { font-size: 15px; margin-top: 4px; }
.of-landing-pkgs-photo { grid-template-columns: 1fr 1fr; }
.of-landing-pkgcard-sm .of-landing-pkg-photo { height: 130px; }

@media (min-width: 640px) {
  .of-property-grid { grid-template-columns: minmax(0, 1fr) 340px; }
  .of-row3 { grid-template-columns: 1.2fr 1fr 1fr; }
  .of-vision-grid { grid-template-columns: repeat(3, 1fr); }
  .of-vision-tile { height: 520px; }
  .of-pkgrow-photo { grid-template-columns: 1fr 1fr; }
  .of-pkgcard-h { grid-template-columns: 200px minmax(0, 1fr); }
  .of-pkg-photo-h { height: 100%; }
  .of-details-grid { grid-template-columns: 1fr 1fr 1fr; }
}

/* phone: package row becomes a peeking scroll-snap carousel — still a real
   grid (grid-auto-flow:column), never flex, so grid-template-rows:subgrid
   on each `.of-pkgcard-full` keeps working (subgrid needs a grid parent). */
@media (max-width: 640px) {
  .of-pkgrow { grid-auto-flow: column; grid-auto-columns: 86vw; grid-template-columns: none; overflow-x: auto; scroll-snap-type: x mandatory; margin: 0 -16px; padding-left: 16px; padding-right: 16px; -webkit-overflow-scrolling: touch; }
  .of-pkgcard-full { scroll-snap-align: center; }
  .of-pkgrow-photo { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; gap: 12px; padding-bottom: 4px; margin: 0 -16px; padding-left: 16px; padding-right: 16px; -webkit-overflow-scrolling: touch; }
  .of-pkgrow-photo > .of-pkgcard-h { flex: 0 0 86vw; scroll-snap-align: center; grid-template-columns: 1fr; }
}

/* ============================================================
   ORDERV2 v3 PASS 2 — SPEC-ORDERV3-2026-09-23 PASS 2. Your style's video
   where/feel tiles + "why a campaign?" expander + finishing-touches
   expander, Schedule's two-column layout + lockbox reveal + "Tell us about
   the home", and Review's top chips + ONE order summary. `of-` prefixed
   classes only, tokens still live on .of-root.
   node tools/extract-order-css.js after any change here or in order-flow.js.
   ============================================================ */
/* ---------- step 5: Your style — video where/feel + expanders ---------- */
.of-style-sec { display: flex; flex-direction: column; gap: 14px; margin-top: 18px; }
.of-sec-headrow { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.of-sec-headrow h2, .of-sec-headrow h3 { margin: 0; }
.of-btn-pill { min-height: 44px; padding: 0 14px; border-radius: 999px; border: 1px solid rgba(230,210,177,.5); background: transparent; color: var(--of-accent); font-size: 13px; font-weight: 600; cursor: pointer; }   /* ORDERV5 R0 2026-09-26 -- V12 44px floor */
.of-why-box { display: grid; grid-template-columns: 1fr; gap: 12px; padding: 16px; background: var(--of-surface); border: 1px solid rgba(230,210,177,.3); border-radius: 14px; }
.of-why-item { display: flex; flex-direction: column; gap: 4px; }
.of-why-item strong { font-size: 14px; font-weight: 700; }
.of-why-item span { font-size: 13px; line-height: 1.5; color: var(--of-ink-2); }
.of-tilegrid { display: grid; grid-template-columns: 1fr; gap: 14px; }
.of-videotile { position: relative; display: flex; flex-direction: column; gap: 4px; align-items: flex-start; text-align: left; padding: 16px 18px; min-height: 90px; border-radius: 14px; cursor: pointer; background: var(--of-surface); border: 2px solid var(--of-line); }
.of-videotile.on { border-color: var(--of-accent); background: rgba(230,210,177,.08); }
.of-videotile strong { font-size: 17px; font-weight: 800; color: #fff; }
.of-videotile span:not(.of-flag) { font-size: 13px; line-height: 1.5; color: var(--of-ink-2); }
.of-pill-tile { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; text-align: left; padding: 16px 18px; min-height: 76px; border-radius: 14px; cursor: pointer; background: var(--of-surface); border: 2px solid var(--of-line); }
.of-pill-tile.on { border-color: var(--of-accent); background: rgba(230,210,177,.08); }
.of-pill-tile strong { font-size: 16px; font-weight: 800; color: #fff; }
.of-pill-tile span { font-size: 13px; color: var(--of-ink-2); }
.of-touches-panel { display: flex; flex-direction: column; gap: 14px; margin-top: 6px; padding: 16px; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: 14px; }

/* ---------- ORDERV4 PASS2 (G) — twilight look picker ---------- */
.of-twilight-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
.of-twilight-card { position: relative; display: flex; flex-direction: column; overflow: hidden; padding: 0; border-radius: 14px; cursor: pointer; background: var(--of-surface); border: 2px solid var(--of-line); }
.of-twilight-card.on { border-color: var(--of-accent); }
.of-twilight-img { display: block; width: 100%; height: 120px; object-fit: cover; }
/* JUDGE round 5 item 5 — real examples land in a LATER pass (F, examples.json);
   until then every card is text-only against the fixture menu (no
   lookExamples), which read as an empty box. A per-look CSS gradient
   swatch — actual blue/pink/ember hues named after the look — fills the
   same top area an image would, so a text-only card still reads as
   intentional design. "Let us pick" (no id, TWILIGHT_SWATCH has no entry)
   gets the neutral/default champagne-on-charcoal gradient. */
.of-twilight-swatch { display: block; width: 100%; height: 120px; }
.of-twilight-swatch-natural { background: linear-gradient(160deg, #1a3a5c 0%, #2f5f8a 55%, #7fb3d9 100%); }
.of-twilight-swatch-pink { background: linear-gradient(160deg, #3a1f3d 0%, #a44b7a 55%, #f2a7c3 100%); }
.of-twilight-swatch-ember { background: linear-gradient(160deg, #3a2410 0%, #b0631f 55%, #f5b25c 100%); }
.of-twilight-swatch-default { background: linear-gradient(160deg, #1e1e21 0%, #2b2924 60%, rgba(230,210,177,.35) 100%); }
.of-twilight-label { padding: 10px 12px; font-size: 13.5px; font-weight: 700; }
@media (min-width: 480px) {
  .of-twilight-grid { grid-template-columns: repeat(4, 1fr); }
}

/* ---------- ORDERV5 R2 2026-09-26 -- §4 editorial look picker: 2x2 at 390, 4-across at 1440 (same breakpoint the twilight picker above uses) ---------- */
.of-look-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
@media (min-width: 480px) {
  .of-look-grid { grid-template-columns: repeat(4, 1fr); }
}
.of-look-card { position: relative; display: flex; flex-direction: column; overflow: hidden; padding: 0; text-align: left; border-radius: 14px; cursor: pointer; background: var(--of-surface); border: 2px solid var(--of-line); }
.of-look-card.on { border-color: var(--of-accent); }
.of-look-card.on::after { content: '✓'; position: absolute; top: 8px; left: 8px; z-index: 2; width: 24px; height: 24px; border-radius: 999px; background: var(--of-accent); color: #0d0d0d; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 800; }
.of-look-media { position: relative; }
.of-look-img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.of-look-swatch { display: flex; align-items: center; justify-content: center; width: 100%; aspect-ratio: 4 / 3; padding: 0 10px; }
.of-look-swatch-label { display: flex; flex-direction: column; align-items: center; gap: 2px; background: rgba(13,13,13,.55); border-radius: 10px; padding: 8px 12px; text-align: center; }
.of-look-swatch-label strong { font-size: 13px; font-weight: 800; color: #fff; }
.of-look-swatch-label span { font-size: 12px; color: rgba(255,255,255,.72); }
.of-look-swatch-bright { background: linear-gradient(160deg, #fdfbf6 0%, #efe8d8 55%, #d9cdb0 100%); }
.of-look-swatch-natural { background: linear-gradient(160deg, #efe6d5 0%, #cdbb98 55%, #8a6f4d 100%); }
.of-look-swatch-moody { background: linear-gradient(160deg, #1b1815 0%, #332821 55%, #5c4331 100%); }
.of-look-swatch-flash { background: linear-gradient(160deg, #ffffff 0%, #e9e9ec 45%, #b9bcc4 100%); }
.of-look-pill { position: absolute; top: 8px; right: 8px; z-index: 2; background: rgba(13,13,13,.72); color: #fff; font-size: 12px; font-weight: 700; padding: 3px 8px; border-radius: 999px; border: 0; cursor: pointer; min-width: 44px; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }   /* judge80 P3 fix -- was 40x27px, under the 44px tap-target floor */
.of-look-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 4px; }
.of-look-namerow { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; flex-wrap: wrap; }   /* j82 r8_02 P3 fix -- was wrap-on-320px-only (r7_13); the row also clips ~321-345px (namerow.mjs, 340px measured) -- wrap unconditionally, a no-op wherever it already fits */
.of-look-namerow strong { font-size: 15px; font-weight: 800; }
.of-look-price { font-size: 12px; font-weight: 700; color: var(--of-accent); white-space: nowrap; }
.of-look-copy { font-size: 13px; color: var(--of-ink-2); margin: 0; }
.of-look-sig { grid-column: 1 / -1; display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 8px 4px; margin-bottom: 4px; background: none; border: 0; font-size: 13.5px; color: var(--of-ink-2); cursor: pointer; text-align: left; }   /* judge80 P3 fix -- now a child of .of-look-grid (radiogroup accessibility fix), so it must span every column instead of collapsing into one cell */
.of-look-sig-dot { width: 16px; height: 16px; border-radius: 999px; border: 1.5px solid var(--of-line); flex: none; }
.of-look-sig.on { color: var(--of-ink); }
.of-look-sig.on .of-look-sig-dot { border-color: var(--of-accent); background: radial-gradient(circle, var(--of-accent) 0 40%, transparent 42%); }

/* ---------- step 6: Schedule — two columns, lockbox reveal, home notes ---------- */
.of-schedule-grid { display: grid; grid-template-columns: 1fr; gap: 28px; align-items: start; }
.of-schedule-main { display: flex; flex-direction: column; gap: 20px; }
.of-schedule-aside { display: flex; flex-direction: column; gap: 10px; }
.of-lockbox-panel { display: flex; flex-direction: column; gap: 10px; padding: 16px 18px; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: 14px; }
.of-homenotes { display: flex; flex-direction: column; gap: 16px; padding: 20px; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: 16px; }
.of-row2 { display: grid; grid-template-columns: 1fr; gap: 12px; }

/* ---------- step 7: Review & book — top chips + ONE order summary ---------- */
.of-review-chips { display: grid; grid-template-columns: 1fr; gap: 12px; }
.of-review-chip { display: flex; flex-direction: column; gap: 4px; padding: 16px 18px; background: var(--of-surface); border: 1px solid var(--of-line); border-radius: 14px; }
.of-review-chip-head { display: flex; justify-content: space-between; align-items: baseline; }
.of-review-chip-head > span:first-child { font-size: 12px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--of-ink-2); }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor */
.of-review-chip-val { font-size: 15px; font-weight: 700; }
/* SPEC-SERVICES-SETTINGS-2026-09-23 D1b §3 — the Review/pre-submit "prices
   changed" notice (order-flow.js's stepReview()/catalogNoticeVisible). Same
   accent-bordered-card language as .of-review-offer, not an error color
   (.of-err's #f2a5a5) — a price refresh is expected behavior, not a fault. */
.of-catalog-banner { padding: 12px 16px; margin-bottom: 12px; background: var(--of-surface); border: 1px solid var(--of-accent); border-radius: 12px; color: var(--of-accent); font-size: 13.5px; font-weight: 600; }
/* r9_03/r9_04 P1 fix -- order-flow.js stepReview()'s "We couldn't load
   photo prices" notice + Try again button (photoEditMenuBlocksSend) -- same
   card shape as .of-catalog-banner above, error-bordered since this one
   actually blocks Send (a price refresh is expected; a missing price is a
   fault). */
.of-menu-err { padding: 12px 16px; margin-bottom: 12px; background: var(--of-surface); border: 1px solid #f2a5a5; border-radius: 12px; }
.of-menu-err > .of-err { margin-bottom: 8px; }
.of-review-offer { display: flex; flex-direction: column; gap: 12px; padding: 14px; background: var(--of-surface); border: 1px solid rgba(230,210,177,.45); border-radius: 16px; }
.of-offer-photo { width: 100%; max-height: 140px; object-fit: cover; border-radius: 10px; }
.of-review-offer-body { display: flex; flex-direction: column; gap: 3px; }
.of-offer-eyebrow { font-size: 12px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; color: var(--of-accent); }   /* judge80 P3 fix -- V11 12px floor */
.of-review-offer-body strong { font-size: 17px; font-weight: 800; }
.of-eyebrow-sm { font-size: 12px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--of-ink-2); }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor */
.of-sumline { display: flex; justify-content: space-between; gap: 12px; font-size: 15px; }
.of-sumamt { font-weight: 700; font-variant-numeric: tabular-nums; }
.of-sumline-included .of-sumamt { color: var(--of-accent); }
.of-sumline-total { align-items: baseline; }
.of-sumline-total .of-sumamt { font-size: 26px; font-weight: 800; }
.of-divider { height: 1px; background: var(--of-line); }
.of-review-main { gap: 22px; }   /* PASS 2 — chips/details/offer/notes need real spacing (pass 1's own .of-review-main was gap:0, itemized-section-border-driven) */

@media (min-width: 900px) {
  .of-tilegrid { grid-template-columns: repeat(3, 1fr); }
  .of-schedule-grid { grid-template-columns: minmax(0, 1fr) 300px; }
  .of-review-chips { grid-template-columns: 1fr 1fr; }
  .of-review-offer { flex-direction: row; align-items: center; }
  .of-offer-photo { width: 150px; height: 100px; max-height: none; flex: 0 0 auto; }
  .of-review-offer-body { flex-grow: 1; }
  .of-row2 { grid-template-columns: 1fr 1fr; }
}

/* JUDGE 2026-09-23 (screenshot walk) item 3 — the rail's own base rule
   (".of-rail{max-width:640px}", pass 1) squeezed all 7 short labels into a
   640px row even at 1440px wide, so every non-active label ellipsis-
   truncated. At >=1100px there is real room: widen the rail to match the
   wide-step container (1280px) and let every label render its full text,
   not just the current step's pill. Below 1100px, unchanged (dots + the
   current step's pill label only — .of-dot-label stays display:none until
   the existing 640px breakpoint, same as before). */
@media (min-width: 1100px) {
  .of-rail { max-width: 1280px; gap: 10px; }
  .of-dot-label { display: inline; overflow: visible; text-overflow: clip; white-space: nowrap; }
  .of-dot.on .of-dot-label { padding-right: 2px; }
}

/* JUDGE 2026-09-23 (screenshot walk) item 6 — the pairing sheet (openGoalSheet,
   .of-sheet) becomes a real bottom sheet at every width (V3Package/OrderExperience:
   full-width, anchored to the viewport bottom, rounded top corners only) instead
   of the native <dialog> UA default of a floating, vertically-centered box. Image
   tiles run in a ROW at >=900px (desktop); phone keeps the single-column stack
   (.of-sheet-grid's own base rule, unchanged). */
.of-sheet { position: fixed; inset: auto 0 0 0; margin: 0; width: 100%; max-width: none; border-radius: 18px 18px 0 0; max-height: 86vh; overflow-y: auto; }
@media (min-width: 900px) {
  .of-sheet { left: 50%; right: auto; transform: translateX(-50%); width: 100%; max-width: 720px; }
  .of-sheet-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ORDER-TOUCHUP 2026-09-24 BEGIN — Vincent approved the "Order page touch-up"
   canvas (design review 2026-09-24): "make it look exactly
   like that all across the board". CSS-only pass; order-flow.js is under the
   Stripe session's file lock, so markup/copy changes (Almost booked., package
   one-liners on landing cards, whole-card select) follow after that release.
   Measured causes it fixes: .of-h1 was 22px (VVT DS headline = 800 weight,
   tight tracking); .of-bar-inner was capped at 640px on a 1280px flow; the
   rail printed each step number twice ("1  01 The home"); every card carried
   a filled champagne button so no screen had one primary action; add-ons ran
   one 300px card per row on desktop; the terms checkbox wrapped. Everything
   here is scoped to .of-* so the admin Hub and the v1 order page are untouched. */

/* -- Type: the design-system headline scale -- */
.of-h1 { font-size: 34px; font-weight: 800; line-height: 1.04; letter-spacing: -0.03em; }
.of-sub { font-size: 16px; line-height: 1.5; color: #b3b6bc; }
.of-stephead { gap: 10px; margin-bottom: 14px; }
@media (min-width: 900px) {
  .of-h1 { font-size: 48px; }
  .of-sub { font-size: 18px; }
  /* The rail already says which step visually; phone keeps "Step 3 of 7".
     Visually hidden, NOT display:none — the rail is a progressbar with no
     aria-valuenow, so this line is the only "Step X of Y" a screen reader gets. */
  .of-stephead .of-eyebrow { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
}

/* -- Rail: thin progress bars with one label each (the original V3 boards) -- */
.of-rail { max-width: 1280px; gap: 8px; margin: 6px auto 10px; align-items: flex-start; }
.of-dot, .of-dot.on { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: stretch; gap: 10px; padding: 0; background: none; border: 0; border-radius: 0; }
.of-dot::before { content: ''; display: block; height: 3px; border-radius: 2px; background: rgba(255,255,255,.12); }
.of-dot.done::before, .of-dot.on::before { background: var(--of-accent); }
.of-dot::after { display: none; }
.of-dot-num { display: none; }   /* the label already carries "01"; the circle doubled it */
/* Phone: only the CURRENT step's label shows (renderRail's contract: the
   current label is visible at any width). It is max-content wide and
   aligned under its own bar — left for the first two steps, right for the
   last two, centered otherwise — so it never runs off either screen edge. */
.of-dot-label { display: none; }
.of-rail { overflow: visible; }
.of-dot.on .of-dot-label { display: block; width: max-content; max-width: none; align-self: center; padding: 0; font-size: 12px; font-weight: 700; color: #fff; white-space: nowrap; overflow: visible; }
.of-dot.on:nth-child(-n+2) .of-dot-label { align-self: flex-start; }
.of-dot.on:nth-last-child(-n+2) .of-dot-label { align-self: flex-end; }
@media (min-width: 640px) {
  /* Tablet: every label, wrapping to two lines instead of an ellipsis
     ("07 Review & book" was cut at 640–880px). */
  .of-dot-label, .of-dot.on .of-dot-label { display: block; width: auto; align-self: stretch; padding: 0; font-size: 13px; font-weight: 500; line-height: 1.3; color: var(--of-ink-2); white-space: normal; overflow: visible; }
  .of-dot.done .of-dot-label { color: #d2d4d8; }
  .of-dot.on .of-dot-label { color: #fff; font-weight: 700; }
}
@media (min-width: 1100px) {
  .of-dot-label, .of-dot.on .of-dot-label { white-space: nowrap; }   /* the older ≥1100 "never truncate" rule, kept */
}
.of-header { padding-bottom: 18px; }

/* -- Bottom bar: full content width, big total, one filled button -- */
.of-bar { padding: 14px max(16px, env(safe-area-inset-left)) calc(14px + env(safe-area-inset-bottom)); border-top: 1px solid rgba(255,255,255,.10); }
.of-bar-inner { max-width: 1248px; gap: 16px; }
.of-bar-steplabel { font-size: 12px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: #b3b6bc; }
.of-bar-right { gap: 12px; }
.of-bar-amt { font-size: 24px; font-weight: 800; letter-spacing: -0.02em; color: #fff; margin-right: 6px; }
.of-root:has(.of-step-home) .of-bar-amt, .of-root:has(.of-vision-grid) .of-bar-amt { font-size: 15px; font-weight: 700; color: var(--of-accent); letter-spacing: 0; }   /* "Prices start at $400" is a sentence, not a total */
.of-bar .of-btn { min-height: 52px; padding: 0 22px; border-radius: 8px; border-color: rgba(255,255,255,.18); font-size: 15px; min-width: 88px; }   /* ORDERV5 R0 2026-09-26 -- V4: Back stays a fixed 88px */
.of-root .of-bar .of-btn-primary { padding: 0 30px; font-size: 16px; font-weight: 700; min-width: 132px; }   /* ORDERV5 R0 2026-09-26 -- V4: a fixed floor so Continue never narrows/shifts between steps */
@media (min-width: 900px) {
  .of-bar-amt { font-size: 30px; }
}

/* -- Shared controls -- */
.of-btn { border-radius: 8px; }
.of-root .of-btn-primary, .of-root-scope .of-btn-primary { font-weight: 700; }
.of-input { min-height: 52px; border-radius: 8px; border-color: rgba(255,255,255,.18); padding: 12px 16px; }
.of-err { line-height: 1.5; }   /* holds multi-line Stripe recovery messages + link buttons */

/* -- Landing: cinematic hero, pay-at-closing said once, quiet cards -- */
.of-landing { gap: 28px; }
.of-landing-hero { border-radius: 16px; }
.of-landing-hero-img { height: 440px; }
.of-landing-hero-copy { padding: 24px 20px; gap: 16px; background: linear-gradient(180deg, rgba(13,13,13,0) 0%, rgba(13,13,13,.55) 45%, rgba(13,13,13,.95) 100%); }
.of-landing-hero-copy h1 { font-size: 42px; line-height: 1.0; letter-spacing: -0.035em; }
.of-landing-hero-copy .of-payband { background: rgba(13,13,13,.55); border-color: rgba(230,210,177,.28); }
.of-landing-subhead { font-size: 24px; font-weight: 700; letter-spacing: -0.015em; margin-top: 12px; }
.of-landing-subhead.of-landing-subhead-sm { font-size: 12px; font-weight: 600; letter-spacing: .26em; text-transform: uppercase; color: #b3b6bc; margin-top: 8px; }
.of-landing-pkgs { gap: 14px; }
.of-landing-pkgcard { display: grid; grid-template-columns: 96px minmax(0, 1fr) auto; grid-template-rows: auto auto; column-gap: 14px; row-gap: 4px; align-items: center; padding: 10px; gap: 4px 14px; border-radius: 14px; background: #151517; border: 1px solid rgba(255,255,255,.10); color: var(--of-ink); transition: border-color .26s, transform .26s; }
.of-landing-pkgcard:hover { border-color: rgba(255,255,255,.18); transform: translateY(-3px); }
.of-landing-pkgcard .of-landing-pkg-photo { grid-column: 1; grid-row: 1 / span 2; width: 96px; height: 96px; border-radius: 10px; }
.of-landing-pkgcard h4 { grid-column: 2; grid-row: 1; align-self: end; margin: 0; font-size: 18px; font-weight: 700; letter-spacing: -0.015em; }
.of-landing-pkgcard .of-price { grid-column: 2; grid-row: 2; align-self: start; margin: 0; font-size: 15px; }
.of-landing-pkgcard .of-muted { display: none; }   /* "Or $0 today…" was on all 5 cards; the hero says it once */
.of-landing-pkgcard .of-link { grid-column: 3; grid-row: 1 / span 2; width: 40px; height: 40px; border-radius: 999px; border: 1px solid rgba(255,255,255,.18); font-size: 0; text-decoration: none; display: flex; align-items: center; justify-content: center; }
.of-landing-pkgcard .of-link::after { content: '→'; font-size: 18px; color: var(--of-ink); }
@media (min-width: 900px) {
  .of-landing-hero-img { height: 600px; }
  .of-landing-hero-copy { padding: 40px; }
  .of-landing-hero-copy h1 { font-size: 64px; line-height: .98; }
  .of-landing-subhead { font-size: 32px; margin-top: 44px; }
  .of-landing-pkgs { gap: 24px; }
  .of-landing-pkgcard:not(.of-landing-pkgcard-sm) { grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto auto auto; padding: 0 0 24px; overflow: hidden; row-gap: 0; }
  .of-landing-pkgcard:not(.of-landing-pkgcard-sm) .of-landing-pkg-photo { grid-column: 1 / -1; grid-row: 1; width: 100%; height: 260px; border-radius: 0; margin-bottom: 22px; }
  .of-landing-pkgcard:not(.of-landing-pkgcard-sm) h4 { grid-row: 2; grid-column: 1; padding: 0 24px; font-size: 24px; }
  .of-landing-pkgcard:not(.of-landing-pkgcard-sm) .of-price { grid-row: 3; grid-column: 1; padding: 8px 24px 0; font-size: 17px; }
  .of-landing-pkgcard:not(.of-landing-pkgcard-sm) .of-link { grid-row: 2 / span 2; grid-column: 2; margin-right: 24px; }
  .of-landing-pkgcard-sm { grid-template-columns: 220px minmax(0, 1fr) auto; padding: 0; overflow: hidden; column-gap: 24px; }
  .of-landing-pkgcard-sm .of-landing-pkg-photo { width: 220px; height: 150px; border-radius: 0; }
  .of-landing-pkgcard-sm h4 { font-size: 20px; }
  .of-landing-pkgcard-sm .of-price { font-size: 16px; }
  .of-landing-pkgcard-sm .of-link { margin-right: 24px; }
}

/* -- Package step: one quiet button style, price-forward cards, chosen = ring + check -- */
/* ORDERV5 R0 2026-09-26 -- V1 fix: the ribbon floats over the photo
   (out of flow, position:absolute -- confirmed live: getComputedStyle
   gives position:absolute and gridRowStart:auto, so it never consumes a
   subgrid track), so each card has 9 in-flow items -- photo, titlerow,
   lead, savings (ORDERV5 S2 added this one after this rule was tuned),
   benefit, rule, plusline, diff-list, cardbtns -- not 8. The old 8-track
   template left diff-list and cardbtns sharing the last line (measured:
   .of-diff-list top 909/h 187 and .of-cardbtns top 1018/h 78 both ending
   at y=1096). Counted from the real DOM (see measure-subgrid2.mjs in the
   build report), not from this comment. */
.of-pkgrow { gap: 24px; grid-template-rows: repeat(8, auto) 1fr; }
.of-pkgcard-full { grid-row: span 9; border-radius: 16px; border: 1px solid rgba(255,255,255,.10); background: #151517; }
.of-pkgcard-full.rec { border-color: rgba(230,210,177,.35); }
.of-pkgcard-full.on, .of-pkgcard-full.rec.on { border: 1.5px solid var(--of-accent); background: #1a1814; box-shadow: 0 0 0 4px rgba(230,210,177,.10), 0 24px 60px rgba(0,0,0,.45); }
.of-pkgcard-full.on::after { content: '✓'; position: absolute; top: 16px; right: 16px; z-index: 2; width: 32px; height: 32px; border-radius: 999px; background: var(--of-accent); color: #0d0d0d; display: flex; align-items: center; justify-content: center; font-size: 16px; font-weight: 800; }
.of-pkg-ribbon, .of-pkg-ribbon-fin { position: absolute; top: 16px; left: 16px; z-index: 2; min-height: 0; max-width: calc(100% - 88px); padding: 7px 12px; border-radius: 999px; background: rgba(13,13,13,.72); border: 1px solid rgba(230,210,177,.4); color: var(--of-accent); font-size: 12px; font-weight: 700; letter-spacing: .14em; line-height: 1.3; text-align: left; }   /* ORDERV5 R0 2026-09-26 -- V11 12px floor (this is the rule that actually wins the cascade) */
.of-pkg-ribbon.is-empty { display: none; }
.of-pkg-titlerow { padding: 0 24px; margin-top: 10px; }
.of-pkg-titlerow h3 { font-size: 24px; font-weight: 700; letter-spacing: -0.015em; }
.of-pkgcard-full .of-pkg-titlerow .of-price { font-size: 28px; font-weight: 800; letter-spacing: -0.02em; }
.of-pkg-lead { padding: 0 24px; font-size: 15px; font-weight: 400; line-height: 1.5; color: #fff; }
.of-pkgcard-full .of-benefit { padding: 0 24px; font-size: 13px; line-height: 1.5; color: #d8bd92; }
.of-pkgcard-full .of-pkg-savings { padding: 0 24px; font-size: 13px; line-height: 1.5; color: #d8bd92; }   /* ORDERV5 S2 2026-09-25 */
.of-pkg-rule { margin: 4px 24px; }
.of-pkg-plusline { padding: 0 24px; letter-spacing: .2em; color: #b3b6bc; }
.of-byo-cathead { font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--of-ink-2); margin: 10px 0 2px; }   /* ORDERV5 R0 2026-09-26 -- V11: had NO rule at all, fell back to the browser's default h5 size (measured 11.2px) */
.of-diff-list { padding: 0 24px; gap: 8px; font-size: 14px; }
.of-cardbtns-full { padding: 6px 24px 24px; }
.of-pkgcard-full .of-btn, .of-pkgcard-h .of-btn { min-height: 48px; font-size: 15px; border-color: rgba(255,255,255,.18); }
.of-root .of-pkgcard-full .of-btn-primary, .of-root .of-pkgcard-h .of-btn-primary { background: transparent; color: #fff; border-color: rgba(255,255,255,.18); }
.of-root .of-pkgcard-full.on .of-btn-primary, .of-root .of-pkgcard-h.on .of-btn-primary { color: var(--of-accent); border-color: var(--of-accent); }
.of-pkgcard-h { border-radius: 14px; border: 1px solid rgba(255,255,255,.10); background: #151517; }
.of-pkgcard-h.on { border: 1.5px solid var(--of-accent); background: #1a1814; }
.of-pkg-grouphead > span:first-child { font-weight: 600; letter-spacing: .26em; color: #b3b6bc; }
.of-pkg-grouphead { margin-top: 18px; }

/* -- Vision / style / schedule: same ring language for a chosen tile -- */
.of-vision-grid { gap: 24px; }
.of-vision-tile { border-radius: 16px; }
/* The 3 vision tiles were a fixed 520px, so the step overflowed any window
   under ~920px tall (measured: 921px page on a 900px viewport, and far more
   on a laptop). The step's own chrome measures ~401px at 900px tall, so the
   tiles take what is left, between 260px and the old 520px. */
@media (min-width: 640px) {
  .of-vision-tile { height: clamp(260px, calc(100vh - 420px), 520px); height: clamp(260px, calc(100svh - 420px), 520px); }
}

/* -- Add-ons: a real grid on desktop; Your Brand is one wide spotlight -- */
.of-addon-rowhead { font-weight: 600; letter-spacing: .2em; color: #b3b6bc; margin-bottom: 12px; }
.of-addon-rows .of-addoncard { padding: 0 0 22px; gap: 8px; overflow: hidden; border-radius: 14px; background: #151517; border: 1px solid rgba(255,255,255,.10); }
.of-addon-rows .of-addoncard.on { border: 1.5px solid var(--of-accent); background: #1a1814; }
.of-addon-rows .of-addoncard > :not(.of-addon-photowrap) { margin-left: 18px; margin-right: 18px; }
.of-addon-rows .of-addoncard > .of-addon-toggle { width: auto; }
.of-addon-rows .of-addon-photowrap { margin: 0 0 8px; width: 100%; }
.of-addon-rows .of-photochip { left: 12px; right: auto; bottom: 12px; padding: 6px 11px; font-weight: 600; }
.of-addon-titlerow h4 { font-size: 16px; font-weight: 700; }
.of-addon-titlerow .of-price { font-size: 15px; }
.of-addon-reason { font-size: 13.5px; line-height: 1.55; color: #b3b6bc; -webkit-line-clamp: 3; }
.of-addon-rows .of-tip { text-transform: none; letter-spacing: 0; font-weight: 600; color: #d8bd92; }
.of-root .of-addon-toggle.of-btn-primary { min-height: 46px; background: transparent; color: #fff; border-color: rgba(255,255,255,.18); }
.of-root .of-addoncard.on .of-addon-toggle.of-btn-primary { color: var(--of-accent); border-color: var(--of-accent); }
.of-addon-rows ~ .of-check { align-items: center; padding: 16px 20px; border-radius: 14px; background: #151517; border: 1px solid rgba(255,255,255,.10); font-size: 15px; color: #fff; }
.of-addon-rows ~ .of-check input { margin-top: 0; }
@media (min-width: 900px) {
  .of-addon-rows { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 28px 24px; align-items: stretch; }
  .of-addon-row { display: flex; flex-direction: column; min-width: 0; }
  .of-addon-rows .of-addon-row .of-addonlist { flex-grow: 1; grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .of-addon-row:has(.of-addoncard + .of-addoncard), .of-addon-row:has(.of-addoncard-brand) { grid-column: 1 / -1; }
  .of-addon-rows .of-addon-row:has(.of-addoncard + .of-addoncard) .of-addonlist { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .of-addon-titlerow h4 { font-size: 19px; }
  .of-addon-titlerow .of-price { font-size: 17px; }
  .of-addon-rows .of-addon-photo { aspect-ratio: auto; height: 200px; }
  .of-addon-rows .of-addoncard > :not(.of-addon-photowrap) { margin-left: 22px; margin-right: 22px; }
  /* Your Brand: portrait left, copy + reel picker right (canvas "Put you in front of buyers") */
  .of-addon-rows .of-addoncard-brand { grid-column: 1 / -1; display: grid; grid-template-columns: 380px minmax(0, 1fr); grid-template-rows: 1fr auto auto auto auto 1fr; column-gap: 48px; row-gap: 14px; padding: 0; border-color: rgba(230,210,177,.28); }
  .of-addon-rows .of-addoncard-brand > .of-addon-photowrap { grid-column: 1; grid-row: 1 / -1; margin: 0; height: 100%; }
  .of-addon-rows .of-addoncard-brand .of-addon-photo { height: 100%; min-height: 380px; aspect-ratio: auto; object-position: center 25%; }
  .of-addon-rows .of-addoncard-brand > :not(.of-addon-photowrap) { grid-column: 2; margin: 0 48px 0 0; }
  .of-addon-rows .of-addoncard-brand > .of-addon-titlerow { grid-row: 2; }
  .of-addon-rows .of-addoncard-brand > .of-addon-reason { grid-row: 3; font-size: 16px; -webkit-line-clamp: unset; }
  .of-addon-rows .of-addoncard-brand > .of-brand-steplabel { grid-row: 4; }
  .of-addon-rows .of-addoncard-brand > .of-brand-stepper { grid-row: 5; max-width: 520px; }
  .of-addon-rows .of-addoncard-brand .of-addon-titlerow h4 { font-size: 36px; font-weight: 800; letter-spacing: -0.03em; }
}
@media (min-width: 641px) and (max-width: 899px) {   /* tablet: 2-up, so a lone add-on isn't a third-width card beside empty space */
  .of-addon-rows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; align-items: stretch; }
  .of-addon-row { display: flex; flex-direction: column; min-width: 0; }
  .of-addon-rows .of-addon-row .of-addonlist { flex-grow: 1; grid-template-columns: minmax(0, 1fr); }
  .of-addon-row:has(.of-addoncard + .of-addoncard), .of-addon-row:has(.of-addoncard-brand) { grid-column: 1 / -1; }
  .of-addon-rows .of-addon-row:has(.of-addoncard + .of-addoncard) .of-addonlist { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .of-addon-rows .of-addoncard-brand { grid-column: 1 / -1; }
}
@media (max-width: 899px) {
  .of-addon-rows .of-addoncard-brand .of-addon-photo { max-height: 300px; object-position: center 25%; }
}
.of-brand-seg { min-height: 52px; font-size: 15px; font-weight: 600; }

/* -- Review: summary on the right, big total, terms on one line -- */
.of-review-grid { gap: 32px; }
.of-review-aside { padding: 26px; border-radius: 16px; gap: 14px; }
.of-review-aside .of-stripe-element { width: 100%; }
.of-review-total { font-size: 36px; font-weight: 800; letter-spacing: -0.02em; }
.of-paycard { padding: 16px 18px; gap: 4px; }
.of-paycard strong { font-size: 15px; }
.of-paycard span { font-size: 13px; line-height: 1.5; }
.of-paycard.on { border: 1.5px solid var(--of-accent); background: #1a1814; box-shadow: none; }
.of-review-aside .of-check { align-items: center; flex-wrap: wrap; gap: 0 6px; font-size: 14px; color: #d2d4d8; }
.of-review-aside .of-check-consent { flex-wrap: nowrap; align-items: flex-start; }   /* j82 r8_01 P2 fix -- the consent row's text is one long sentence; nowrap keeps checkbox+text on one row, text wraps INSIDE itself below */
.of-review-aside .of-check-consent > span { flex: 1 1 0; min-width: 0; }
.of-review-aside .of-check input { margin: 0 6px 0 0; }
.of-review-aside .of-check .of-link { display: inline-flex; align-items: center; padding: 0; min-height: 44px; font-size: 14px; }   /* 44px: a near-miss must not tick "I agree" instead */
.of-review-aside > .of-link { align-self: flex-start; padding: 0; min-height: 44px; }
.of-review-sec { margin-top: 20px; padding: 22px 20px; gap: 12px; border: 1px solid rgba(255,255,255,.10); border-radius: 14px; background: #151517; }
.of-review-sec .of-input { background: #0d0d0d; }
.of-review-head h3 { font-size: 20px; font-weight: 700; margin: 0; }
.of-review-main > .of-label { margin-top: 20px; }
@media (min-width: 900px) {
  .of-review-grid { grid-template-columns: minmax(0, 1fr) 440px; }
  .of-review-sec { padding: 26px; }
}

/* -- Phone fixes -- */
@media (max-width: 640px) {   /* 640, not 639: the tablet rule starts at 641, so exactly 640 matched neither */
  .of-landing-pkgs-photo { grid-template-columns: 1fr; }   /* 2-up squeezed the name under the arrow */
  .of-addon-rows .of-addonlist { grid-template-columns: 1fr; }   /* one card per row was half-width */
  .of-addon-rows .of-addonlist:has(.of-addoncard + .of-addoncard) { grid-template-columns: repeat(2, 1fr); }
  .of-addon-rows .of-addoncard-brand { grid-column: 1 / -1; }
  /* r2_4a fix -- .of-bar-amt's old min-width:96px was a FLOOR, not a fixed
     box: past that floor .of-bar-right (flex:0 0 auto) just grew with the
     label, and since it's the ONLY visible child of .of-bar-inner's
     space-between on phone, growing it shifted Back/Continue sideways
     screen to screen (and off the right edge past ~390px). .of-bar-right
     now fills the bar and does the positioning; .of-bar-amt is the one
     child that shrinks/ellipsizes inside it, so Back and Continue keep an
     identical fixed width and X position on every screen. */
  .of-bar-right { flex: 1 1 auto; gap: 8px; min-width: 0; }
  .of-bar-amt { flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 22px; margin-right: 0; text-align: right; }
  /* j82 r7_05 P2 fix -- the ellipsis/nowrap rule right above is right for
     the numeric order TOTAL ("$1,150"), but .of-bar-amt also carries the
     longer sentence "Prices start at $400" on the home and vision steps (the
     only steps before a package is picked) -- at 390/360/320px that
     truncated to "Prices start at ..." with the price itself cut off, the
     one price either of those two screens shows at all. Lets it wrap
     instead, on those two steps only (same .of-step-home/.of-vision-grid
     selector pair the font-size override above already keys on) -- 3
     classes/pseudo-classes here beats the bare .of-bar-amt above regardless
     of source order, so the numeric-total case on every OTHER step is
     untouched. */
  .of-root:has(.of-step-home) .of-bar-amt, .of-root:has(.of-vision-grid) .of-bar-amt { white-space: normal; overflow: visible; text-overflow: initial; line-height: 1.2; }
  .of-bar .of-btn { padding: 0 14px; flex: 0 0 auto; }
  /* No nowrap on the PRIMARY specifically: "Pay $2,487.50 & book" ran
     off-screen at 375px when it couldn't wrap. It keeps a fixed 132px box
     (matching the 132px floor set for wider widths up above) and wraps to
     two lines inside that box instead of pushing past the viewport edge. */
  .of-root .of-bar .of-btn-primary { padding: 0 16px; flex: 0 0 132px; white-space: normal; line-height: 1.2; text-align: center; }
  .of-row3 .of-input { padding-left: 10px; padding-right: 10px; }   /* Beds/Baths at 640px had 0px left for the number */
}
@media (max-width: 380px) {
  .of-bar-amt { font-size: 18px; }
  .of-bar .of-btn { padding: 0 10px; font-size: 14px; }
}
@media (max-width: 320px) {
  /* j82 r7_13 P3 fix -- "Magazine" + its price/"Included" label clipped to
     "Magazine Include" at 320px (p3_style.mjs). j82 r8_02 P3 fix -- moved to
     the unconditional base rule above (also clipped ~321-345px); this
     320px-scoped copy is now redundant and dropped. */
  /* judgefix80 r6_06 P3 fix -- live-measured (p6_bar.mjs): at exactly 320px
     .of-bar-amt is 8px short of fitting a 5-digit total ("$3,850",
     scrollWidth 60 vs clientWidth 52); 330px and up are already clean at
     the 132px floor above. Shrinks ONLY the primary button, ONLY this
     narrow, so the 132px-on-every-screen property the walk measures at
     390/1440 is untouched.
     judgefix80 r6_06b fix -- flex-basis alone was a no-op: the base rule's
     min-width:132px ("V4: a fixed floor") still won at every width (a flex
     item's rendered width can never go below its own min-width, regardless
     of flex-basis) -- min-width must be overridden here too. */
  .of-root .of-bar .of-btn-primary { flex: 0 0 116px; min-width: 116px; padding: 0 10px; }
}
/* The hero's side-by-side layout needs ≥1100px: at 900–1045px the $0 card was
   clipped ("…Pay wher"), and it is now the landing's only $0-today line. */
@media (min-width: 1100px) {
  .of-landing-hero-copy { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: 40px; padding: 56px; }
  .of-landing-hero-copy h1 { font-size: 80px; max-width: 11ch; min-width: 0; }
  .of-landing-hero-copy .of-payband { flex: 0 1 320px; min-width: 0; flex-direction: column; align-items: flex-start; gap: 4px; padding: 18px 20px; border-radius: 14px; font-size: 15px; line-height: 1.5; }
  .of-landing-hero-copy .of-payband .of-link { padding: 0; min-height: 44px; display: inline-flex; align-items: center; }
}

/* ORDERFIX 2026-09-25 — the "Pick up where you left off" card on the landing
   (an unfinished booking is offered, never forced) and the campaign tile's
   Upgrade treatment on Your style. */
.of-resume-card { display: flex; flex-direction: column; gap: 12px; padding: 16px 18px; border-radius: 14px; border: 1px solid rgba(230,210,177,.28); background: #151517; }
.of-resume-copy { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.of-resume-kicker { font-size: 12px; font-weight: 600; letter-spacing: .2em; text-transform: uppercase; color: #b3b6bc; }   /* r2_v11a fix -- was 11px, under the spec's 12px floor (V11) */
.of-resume-what { font-size: 17px; font-weight: 700; color: #fff; overflow-wrap: anywhere; }
.of-resume-btns { display: flex; gap: 10px; }
.of-resume-btns .of-btn { flex: 1 1 0; min-height: 44px; font-size: 15px; border-color: rgba(255,255,255,.18); }
.of-root .of-resume-btns .of-resume-go { color: var(--of-accent); border-color: var(--of-accent); font-weight: 700; }
@media (min-width: 640px) {
  .of-resume-card { flex-direction: row; align-items: center; justify-content: space-between; gap: 24px; padding: 18px 22px; }
  .of-resume-btns .of-btn { flex: 0 0 auto; padding: 0 22px; }
}
/* ORDERFIX 2026-09-25 — at 641–899px (a ~720px window, or 200% zoom) the three
   listing cards stayed side by side at 213px each and cut their prices off
   ("$2,10"). One column there; phone keeps its carousel, desktop its row. */
@media (min-width: 641px) and (max-width: 899px) {
  .of-pkgrow { grid-template-columns: minmax(0, 1fr); }
}
.of-root .of-h1:focus, .of-root .of-h1:focus-visible { outline: none; box-shadow: none; }   /* focus is moved here for screen readers on every step change */
.of-videotile-upgrade { border-color: rgba(230,210,177,.35); }
.of-videotile-upgrade .of-flag-goal { background: transparent; border: 1px solid var(--of-accent); color: var(--of-accent); }
/* ORDERV5 STAFF-4c 2026-09-25 -- the staff strip (order-flow.js's
   staffStrip/staffClientPicker/staffAnyTimePanel/staffNoteField). Never
   rendered on the public page -- state.staff is null unless hubStaffParent
   found a real signed-in admin session. */
.of-staffstrip { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 10px 16px; margin: 0 0 12px; border-radius: 12px; background: rgba(230,210,177,.12); border: 1px solid rgba(230,210,177,.3); max-width: 1280px; margin-left: auto; margin-right: auto; }
.of-staffstrip-tag { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: #e6d2b1; white-space: nowrap; }
.of-staffstrip-by { font-size: 12.5px; color: rgba(242,239,233,.7); margin-left: auto; white-space: nowrap; }
.of-staff-picker { position: relative; min-width: 220px; flex: 1 1 260px; }
.of-staff-picker-chip { display: inline-flex; align-items: center; gap: 6px; font-size: 13.5px; color: #fff; }
.of-staff-picker-drop { display: none; position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; right: 0; background: #1a1a1c; border: 1px solid rgba(255,255,255,.14); border-radius: 10px; padding: 6px; max-height: 240px; overflow: auto; box-shadow: 0 8px 24px rgba(0,0,0,.4); }
.of-staff-picker-drop.show { display: block; }
.of-staff-picker-item { display: flex; align-items: center; gap: 4px; width: 100%; min-height: 44px; padding: 8px 10px; background: none; border: none; border-radius: 8px; color: #fff; font-size: 13.5px; text-align: left; cursor: pointer; }
.of-staff-picker-item:hover, .of-staff-picker-item:focus-visible { background: rgba(255,255,255,.08); }
.of-staff-picker-new { color: var(--of-accent); }
.of-staff-anytime { display: flex; flex-direction: column; gap: 10px; margin: 0 0 16px; }
.of-staff-anytime-toggle { align-self: flex-start; }
.of-staff-anytime-inputs { align-items: flex-end; }
.of-staff-note textarea { min-height: 72px; }
.of-pkg-deeplink { align-self: stretch; }

/* ORDER-TOUCHUP 2026-09-24 END */

/* ============================================================
   SPEC-SERVICES-SETTINGS-2026-09-23 D2 — Settings › "Packages, services and
   time" (settings-services.js). Matches the board (SettingsServicesV4) using
   Hub tokens: --panel cards, 18px radius, --accent selection, --ok-ink
   savings, --accent-warm-soft badge. Plus Jakarta Sans is already the Hub's
   --font-alt-ish system font-face (see file top) — no new font load.
   ============================================================ */
.svc-root { display: flex; flex-direction: column; gap: 18px; font-family: 'Plus Jakarta Sans', var(--font), ui-sans-serif, system-ui, sans-serif; }
.svc-header { display: flex; justify-content: space-between; align-items: flex-end; gap: 20px; flex-wrap: wrap; }
.svc-eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .2em; text-transform: uppercase; color: var(--muted); }
.svc-title { margin: 2px 0 4px; font-size: 24px; font-weight: 800; letter-spacing: -.02em; color: var(--ink); }
.svc-sub { margin: 0; font-size: 13.5px; color: var(--muted); max-width: 640px; }
.svc-status { margin-top: 6px; font-size: 11.5px; }
.svc-header-actions { display: flex; gap: 8px; }

.svc-tabs { display: flex; gap: 8px; flex-wrap: nowrap; overflow-x: auto; padding-bottom: 2px; }
.svc-tab { min-height: 40px; padding: 0 16px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel); color: var(--ink); font-size: 13.5px; font-weight: 700; cursor: pointer; white-space: nowrap; display: inline-flex; align-items: center; gap: 6px; }
.svc-tab.active { background: var(--ink); color: var(--panel-inverse, #fff); border-color: var(--ink); }
.svc-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); display: inline-block; }

.svc-body { min-height: 200px; }
.svc-split { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: 20px; align-items: start; }
.svc-panel { display: flex; flex-direction: column; gap: 14px; padding: 20px; background: var(--panel); border: 1px solid var(--border); border-radius: 18px; }
.svc-panel-wide { grid-column: 1 / -1; }
.svc-panel-title { margin: 0; font-size: 19px; font-weight: 800; color: var(--ink); }
.svc-panel-copy { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--muted); }
.svc-editor-host { position: sticky; top: 16px; }
.svc-readonly-note { font-size: 12px; font-weight: 700; color: var(--warn-ink, #b54708); background: var(--warn-soft); border-radius: 8px; padding: 6px 10px; display: inline-block; }
.svc-label { font-size: 12.5px; font-weight: 700; color: var(--ink); margin-bottom: 4px; }

.svc-pkg-list { display: flex; flex-direction: column; gap: 6px; }
.svc-pkg-row { display: flex; flex-direction: column; gap: 6px; padding: 14px 16px; border-radius: 14px; cursor: pointer; text-align: left; background: transparent; border: 1px solid var(--border); }
.svc-pkg-row.active { background: var(--panel); border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
.svc-pkg-row-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.svc-pkg-name { font-size: 15.5px; font-weight: 800; color: var(--ink); }
.svc-pkg-row-bottom { display: flex; gap: 14px; font-size: 13.5px; color: var(--muted); }
.svc-badge { font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; padding: 3px 8px; border-radius: 999px; background: var(--accent-warm-soft); color: var(--warn-ink, #6b5226); }
.svc-save-ink { color: var(--ok-ink); font-weight: 700; }
.svc-add-dashed { min-height: 48px; border-radius: 14px; border: 1.5px dashed var(--border); background: transparent; font-size: 13.5px; font-weight: 700; cursor: pointer; }
.svc-add-pill { align-self: flex-start; min-height: 44px; padding: 0 18px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel); font-size: 13.5px; font-weight: 700; cursor: pointer; }

.svc-field-grid-4 { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 12px; }
.svc-field-grid-4 > .field { min-width: 0; }   /* a grid item's default min-width:auto uses the <input>'s intrinsic content width, which can exceed a narrow track (esp. the mobile sheet) and force horizontal overflow — this lets it shrink to the track */
.svc-field-grid-4 > .field > .input { width: 100%; box-sizing: border-box; }

/* ORDERV5 VTWI 2026-09-25 -- the per-photo tier-table editor (Settings >
   Packages, services and time), today only shown for virtual-twilight. */
.svc-tiers-editor { display: flex; flex-direction: column; gap: 8px; }
.svc-tiers-rows { display: flex; flex-direction: column; gap: 8px; }
.svc-tier-row { display: grid; grid-template-columns: 1fr 1fr auto; gap: 10px; align-items: end; }
.svc-tier-row .field { min-width: 0; }
.svc-tier-remove { min-height: 44px; min-width: 44px; }
.svc-tiers-error { font-size: 12.5px; font-weight: 700; color: var(--warn-ink, #b54708); }
.svc-default-hint { font-size: 11px; color: var(--muted); margin-top: 3px; }

.svc-chip-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; }
.svc-chip { display: flex; justify-content: space-between; align-items: center; gap: 8px; min-height: 44px; padding: 0 14px; border-radius: 10px; cursor: pointer; font-size: 13.5px; font-weight: 600; color: var(--muted); background: var(--panel); border: 1px solid var(--border); }
.svc-chip.on { color: var(--accent); background: var(--accent-soft); border-color: var(--accent); }
.svc-chip-price { font-weight: 500; }

.svc-tiles-block { display: flex; flex-direction: column; gap: 8px; }
.svc-tiles-heading { font-size: 12.5px; font-weight: 700; color: var(--ink); }
.svc-tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.svc-tile { display: flex; flex-direction: column; gap: 4px; padding: 14px; border-radius: 12px; background: var(--subtle); }
.svc-tile-ok { background: var(--ok-soft); }
.svc-tile-label { font-size: 11.5px; color: var(--muted); }
.svc-tile-num { font-size: 20px; font-weight: 800; color: var(--ink); }
.svc-tile-strike { text-decoration: line-through; color: var(--muted); }

.svc-switch-row { display: flex; gap: 10px; align-items: center; min-height: 40px; font-size: 13.5px; font-weight: 600; }
.svc-stepper { display: flex; align-items: center; gap: 10px; }
.svc-stepper button { min-width: 44px; min-height: 44px; }   /* fix #11 — 44px targets */
.svc-panel input[type="checkbox"] { width: 20px; height: 20px; min-width: 20px; }
.svc-switch-row, .svc-photo-row input[type="checkbox"] { min-height: 44px; }   /* fix #11 — the whole row/label is the 44px target, not the tiny box itself */
/* fix #11 — AA contrast: white-on-accent measures ~3.5:1 in dark mode (fails
   AA); reuse the exact darker fill .btn-primary already established for
   dark mode (styles.css ~561) rather than inventing a new pair. */
html[data-theme="dark"] .svc-tri-included,
html[data-theme="dark"] .svc-mini-included,
html[data-theme="dark"] .svc-skill-chip.on { background: #1e3f66; border-color: #1e3f66; }

/* ORCHESTRATOR REVIEW fix #2 — tightened row height (~8 rows fit a 900px panel)
   and the "Where it shows" column is now a 1-line summary + small chips, never
   the old multi-line sentence wall. */
.svc-row-head, .svc-row-inner { display: grid; grid-template-columns: minmax(0, 1.5fr) 74px 74px minmax(0, 1.5fr) 72px; gap: 8px; align-items: center; padding: 0 10px; }
.svc-row-head { font-size: 10px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: 3px; }
/* the layout lives on .svc-row-inner (a plain div); .svc-row (the <button>
   itself) stays a bare block box — see settings-services.js's own comment on
   why a flex/grid <button> is the thing that was losing height. */
.svc-row { display: block; width: 100%; min-height: 40px; border-radius: 10px; cursor: pointer; text-align: left; background: transparent; border: 1px solid transparent; font-size: 12.5px; padding: 0; }
.svc-row.active { background: var(--panel); border-color: var(--accent); }
.svc-row-inner { min-height: 40px; padding: 8px 10px; }
/* min-height on both: CSS Grid gives an item whose overflow is anything but
   "visible" an AUTOMATIC MINIMUM SIZE of 0 (spec, not a bug) — with a single
   mobile column, each row IS its one item's track, so that 0-min collapsed
   the track to ~1px and the real text painted straight through into the row
   below it (measured: two "rows" landing 0.6px apart). An explicit min-height
   restores a real floor regardless of the overflow rule. */
.svc-row-name { font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-height: 1.3em; }
/* no overflow:hidden here — its content (a 1-line summary + a wrapping chip
   row) is short by construction and never needs clipping, and removing it
   sidesteps the grid automatic-minimum-size:0 rule for good instead of
   fighting it with a guessed min-height. */
.svc-row-rule { display: flex; flex-direction: column; gap: 2px; color: var(--muted); font-size: 11px; line-height: 1.3; }
.svc-row-group { font-weight: 700; color: var(--accent); font-size: 10.5px; min-height: 1.2em; }
/* THE actual root cause of the row-overlap bug (not the button, not the
   grid track sizing — both real findings above, but neither was IT): a flex
   column with max-height+overflow:auto still SHRINKS its children first
   (default flex-shrink:1) before ever engaging the scrollbar, so every row
   was silently squashed below its own content height by the same ratio
   (measured: identical squashed height on every row, before AND after the
   button/grid rewrite — the tell that it was the LIST, not the row). flex-
   shrink:0 on each row makes the list actually scroll instead of squashing. */
.svc-row-list { display: flex; flex-direction: column; gap: 3px; max-height: 620px; overflow-y: auto; }
.svc-row-list > .svc-row { flex-shrink: 0; }
.svc-row-chips { display: flex; gap: 3px; flex-wrap: wrap; }
.svc-mini-chip { display: inline-flex; align-items: center; justify-content: center; min-width: 22px; height: 18px; padding: 0 3px; border-radius: 4px; font-size: 11px; font-weight: 800; line-height: 1; }   /* fix #11 — was 9px */
.svc-mini-included { background: var(--accent); color: #fff; }
.svc-mini-addon { background: var(--panel); color: var(--warn-ink, #6b5226); border: 1px solid #d8bd92; }
.svc-mini-hidden { background: var(--subtle); color: var(--muted); border: 1px solid var(--border); }

/* fix #3 — Included = filled accent + white + check; Add-on = warm OUTLINE + "+"
   + live price; Hidden = muted gray + strike icon. All three text/bg pairs are
   AA (measured: white/--accent ≈ 6.9:1; --warn-ink/--panel ≈ existing badge
   pair already used app-wide; --muted/--subtle ≈ existing tile-label pair). */
.svc-tri-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 6px; }
.svc-tri { display: flex; flex-direction: column; gap: 3px; align-items: center; text-align: center; padding: 8px 6px; border-radius: 10px; font-size: 12px; font-weight: 700; cursor: pointer; min-height: 48px; }
.svc-tri-name { font-size: 12.5px; font-weight: 800; }
.svc-tri-state { font-size: 11px; font-weight: 700; }
.svc-tri-included { background: var(--accent); color: #fff; border: 1.5px solid var(--accent); }
.svc-tri-addon { background: var(--panel); color: var(--warn-ink, #6b5226); border: 1.5px solid #d8bd92; }
.svc-tri-hidden { background: var(--subtle); color: var(--muted); border: 1.5px solid var(--border); }
.svc-chip-static { font-size: 12.5px; color: var(--muted); }

/* fix #4 — package-chip groups (Photos / Video / 3D & plans / Branding) + the
   collapsed staff-only disclosure. */
.svc-chip-groups { display: flex; flex-direction: column; gap: 12px; }
.svc-chip-group-h { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 5px; }
.svc-staff-disclosure { border-top: 1px dashed var(--border); padding-top: 10px; }
.svc-staff-disclosure summary { cursor: pointer; font-size: 12.5px; font-weight: 700; color: var(--muted); list-style: none; }
.svc-staff-disclosure summary::-webkit-details-marker { display: none; }
.svc-staff-disclosure summary::before { content: '▸ '; }
.svc-staff-disclosure[open] summary::before { content: '▾ '; }
.svc-staff-disclosure .svc-chip-grid { margin-top: 8px; }

/* fix #5 — short placeholder + a visible unit suffix beside the field, plus an
   optional hint line below for a compound rate. */
.svc-input-suffix { display: flex; align-items: center; gap: 6px; }
.svc-input-suffix .input { flex: 1 1 auto; min-width: 0; }
.svc-suffix { flex: 0 0 auto; font-size: 12px; font-weight: 700; color: var(--muted); }
.svc-hint { font-size: 11px; color: var(--muted); margin-top: 2px; }

/* fix #7 — editable "Can shoot" skill chips */
.svc-skill-chips { display: flex; gap: 4px; flex-wrap: wrap; }
.svc-skill-chip { min-height: 26px; min-width: 44px; padding: 0 8px; border-radius: 999px; border: 1px solid var(--border); background: var(--panel); color: var(--muted); font-size: 11px; font-weight: 700; cursor: pointer; box-sizing: border-box; text-align: center; }
.svc-skill-chip.on { background: var(--accent); color: #fff; border-color: var(--accent); }
/* JUDGE-2026-09-24 (D2 RE-FIX #6) — the short "3D"/"All" labels were only
   ~28-32px wide at min-height:26px; min-width:44 above covers width, and
   the label's own reachable target still needs 44px of HEIGHT on touch —
   handled by the mobile min-height:44 rule further down (already existed
   for .svc-skill-chip), so together the tappable box is >=44x44 on mobile. */
/* JUDGE-2026-09-24 (D2 RE-FIX #6) — Active checkbox: a real 44px label hit
   area, not a bare unlabeled checkbox. */
.svc-active-label { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; min-width: 44px; padding: 0 4px; cursor: pointer; }
.svc-active-label input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; }
.svc-active-text { font-size: 11.5px; color: var(--muted); }

/* fix #8 — the sticky "Unsaved changes" bar, scoped to the PANEL (or the mobile
   sheet) it sits in — never the viewport, so it never fights the sheet overlay's
   z-index. Hidden by default; markDirty/markClean toggle it per tab. */
.svc-savebar { display: none; position: sticky; bottom: -20px; left: 0; right: 0; margin: 8px -20px -20px; padding: 12px 20px; background: var(--panel); border-top: 1px solid var(--border); align-items: center; justify-content: space-between; gap: 12px; border-radius: 0 0 18px 18px; box-shadow: 0 -6px 16px -10px rgba(0,0,0,.25); }
.svc-savebar-msg { font-size: 12.5px; font-weight: 700; color: var(--warn-ink, #8a6d3b); }
.svc-savebar-actions { display: flex; gap: 8px; }
.svc-sheet-body .svc-savebar { margin: 8px -16px calc(-16px - env(safe-area-inset-bottom)); padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); bottom: calc(-16px - env(safe-area-inset-bottom)); }
/* the Scheduling rules tab's bar sits BELOW the grid of panels, not inside one —
   no -20px panel padding to cancel, so it gets its own flat variant. */
.svc-sched-outer { display: flex; flex-direction: column; gap: 12px; }
.svc-sched-outer .svc-savebar { position: sticky; bottom: 0; margin: 0; border-radius: 14px; border: 1px solid var(--border); box-shadow: var(--shadow); }

.svc-media-grid { display: grid; grid-template-columns: repeat(3, 72px) minmax(0, 1fr); gap: 8px; }
.svc-media-thumb { width: 72px; height: 54px; object-fit: cover; border-radius: 8px; background: var(--subtle); }
.svc-media-video { display: flex; align-items: center; justify-content: center; }
.svc-media-upload-tile { min-height: 54px; border-radius: 8px; border: 1.5px dashed var(--border); background: var(--subtle); font-size: 12px; font-weight: 600; color: var(--muted); display: flex; align-items: center; justify-content: center; text-align: center; padding: 4px; cursor: pointer; }

/* SPEC-SERVICES-SETTINGS §8 — the media UPLOAD/pick/reorder pass (settings-services.js
   buildMediaSection / openPickFromSite). Each example is its own card: thumbnail on top,
   the required alt text below it, then ↑ / ↓ / Remove. The upload and pick-from-site
   tiles are cards of the same size so they sit in the grid, never below it. */
.svc-media-section { display: flex; flex-direction: column; gap: 8px; margin-bottom: 4px; }
.svc-media-grid2 { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; }
.svc-media-cell { display: flex; flex-direction: column; gap: 6px; padding: 8px; border-radius: 10px; background: var(--subtle); }
.svc-media-cell .svc-media-thumb { width: 100%; height: 84px; }
.svc-media-thumb-wrap { position: relative; }
.svc-media-video-badge { position: absolute; bottom: 4px; right: 4px; padding: 1px 6px; border-radius: 999px; background: rgba(0,0,0,.65); color: #fff; font-size: 10px; font-weight: 700; }
.svc-media-alt { font-size: 12px; padding: 6px 8px; min-height: 32px; }
.svc-media-cell-actions { display: flex; gap: 4px; justify-content: flex-end; flex-wrap: wrap; }
/* LOW (2026-09-24 ruling round) — 44x44 minimum tap target for ↑/↓/Remove (a manager now
   taps these too, not just the owner at a desk) */
.svc-media-cell-actions .btn { min-width: 44px; min-height: 44px; padding: 0 10px; font-size: 12px; }
.svc-media-remove { color: var(--danger-ink, #b42318); }
.svc-media-upload-wrap { display: contents; }
.svc-media-upload-tile { height: 84px; }
.svc-media-pick-tile { }
.svc-media-error { font-size: 12px; font-weight: 600; color: var(--danger-ink, #b42318); background: var(--danger-soft, rgba(180,35,24,.08)); border-radius: 8px; padding: 6px 10px; }
.svc-upload-status { min-width: 70px; }

/* "Pick from our site" modal grid */
.svc-pick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); gap: 10px; max-height: 60vh; overflow-y: auto; }
.svc-pick-tile { position: relative; padding: 0; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--subtle); cursor: pointer; }
.svc-pick-thumb { width: 100%; height: 84px; object-fit: cover; display: block; }
.svc-pick-badge { position: absolute; bottom: 4px; right: 4px; padding: 1px 6px; border-radius: 999px; background: rgba(0,0,0,.65); color: #fff; font-size: 10px; font-weight: 700; }
@media (max-width: 760px) {
  .svc-media-grid2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .svc-pick-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .svc-media-alt { font-size: 16px; }   /* inputmode/keyboard-zoom guard, matches the rest of this file's 760px pass */
}

.svc-light-table { display: flex; flex-direction: column; gap: 6px; }
.svc-light-head, .svc-light-row { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1.6fr) 130px 130px; gap: 10px; align-items: center; }
.svc-light-head { font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); padding: 0 12px; }
.svc-light-row { min-height: 52px; padding: 0 12px; border-radius: 12px; background: var(--subtle); font-size: 13.5px; }
.svc-sun-line { font-size: 12px; font-weight: 700; color: var(--warn-ink, #8a6d3b); }

.svc-example-box { padding: 14px; border-radius: 12px; background: var(--subtle); font-size: 13.5px; line-height: 1.6; }
.svc-sched-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
.svc-zone-rows { display: flex; flex-direction: column; gap: 8px; font-size: 13.5px; }
.svc-zone-row { display: flex; justify-content: space-between; padding: 10px 12px; border-radius: 10px; background: var(--subtle); }
.svc-photo-rows { display: flex; flex-direction: column; gap: 6px; }
.svc-photo-head, .svc-photo-row { display: grid; grid-template-columns: 64px minmax(0, 1fr) 120px 140px 140px 70px; gap: 10px; align-items: center; }
.svc-photo-head { font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); padding: 0 10px; }
.svc-photo-row { min-height: 48px; padding: 0 10px; border-radius: 10px; background: var(--subtle); font-size: 13.5px; }
.svc-photo-order { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); }

/* D2 FIX-FIRST additions — pending-create state, delete/reset-all, the
   copy-refresh suggestion, the tri-state stale-copy warning, and the
   "Coming with smart scheduling" badge (fixes #1/#2/#3/#6/#8). */
.svc-panel-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.svc-soon-badge { font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; padding: 3px 8px; border-radius: 999px; background: var(--subtle); color: var(--muted); }
.svc-badge-pending { background: var(--warn-soft); color: var(--warn-ink, #8a6d3b); }
.svc-pending-note { color: var(--warn-ink, #8a6d3b); background: var(--warn-soft); }
.svc-panel-footrow { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 4px; }
.svc-delete-btn { color: var(--danger-ink, #b42318); border-color: var(--danger-ink, #b42318); }
.svc-refresh-copy { align-self: flex-start; margin-top: 6px; min-height: 36px; padding: 0 12px; border-radius: 8px; border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent); font-size: 12.5px; font-weight: 700; cursor: pointer; display: inline-flex; }
.svc-stale-warn { display: none; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; padding: 10px 12px; border-radius: 10px; background: var(--warn-soft); color: var(--warn-ink, #8a6d3b); font-size: 12.5px; }

/* JUDGE-2026-09-24 (D2 RE-FIX #4) — "Update the package wording" preview modal. */
.svc-copy-preview-pkg { margin: 0 0 14px; }
.svc-copy-preview-pkg:last-child { margin-bottom: 0; }
.svc-copy-preview-list { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.svc-copy-line-removed { color: var(--danger, #b3261e); text-decoration: line-through; }
.svc-copy-line-added { color: var(--accent); font-weight: 600; }
.svc-copy-line-unchanged { color: var(--muted); font-style: italic; }

/* mobile bottom sheet */
.svc-sheet-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 1200; display: flex; align-items: flex-end; }
.svc-sheet { width: 100%; max-height: 88vh; overflow-x: hidden; overflow-y: auto; background: var(--panel); border-radius: 18px 18px 0 0; padding: 18px 16px calc(16px + env(safe-area-inset-bottom)); position: relative; box-sizing: border-box; }
.svc-sheet-bar { width: 40px; height: 4px; border-radius: 2px; background: var(--border); margin: 0 auto 10px; }
/* JUDGE-2026-09-24 (D2 RE-FIX #6) — 32x32 close button raised to a 44x44
   touch target (the circle itself can stay visually smaller via padding,
   but the tappable box must hit 44). */
.svc-sheet-close { position: absolute; top: 8px; right: 10px; min-width: 44px; min-height: 44px; border-radius: 50%; border: none; background: var(--subtle); font-size: 14px; display: flex; align-items: center; justify-content: center; }
.svc-sheet-body .svc-panel { border: none; padding: 0; max-width: 100%; }
.svc-sheet-body input, .svc-sheet-body textarea { max-width: 100%; box-sizing: border-box; }

@media (max-width: 760px) {
  .svc-split { grid-template-columns: 1fr; }
  .svc-editor-host { display: none; }   /* mobile: the panel opens as a bottom sheet instead (see openMobileSheet in settings-services.js) */
  .svc-chip-grid { grid-template-columns: 1fr; }
  .svc-field-grid-4 { grid-template-columns: 1fr 1fr; }
  .svc-tri-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }   /* ORCHESTRATOR REVIEW fix #3 — 2-up on mobile, not 5 stacked full-width bars */
  .svc-media-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .svc-row-head { display: none; }
  .svc-row-inner { display: flex; flex-direction: column; align-items: stretch; gap: 2px; padding: 10px 12px; min-height: 0; }
  .svc-row-rule, .svc-row-group { font-size: 11.5px; }
  .svc-sched-grid { grid-template-columns: 1fr; }
  /* fix #10 — the old 3-column grid hid the address AND the skill chips
     outright (nth-child(4)/(5) display:none) and squeezed the ↑/↓ buttons +
     row number into a 40px column where they visibly overlapped. A flex
     column stacks everything instead — nothing hidden, nothing overlapping. */
  .svc-photo-head { display: none; }
  .svc-photo-row { display: flex; flex-direction: column; align-items: stretch; gap: 6px; padding: 10px 12px; }
  .svc-photo-order { justify-content: flex-start; gap: 8px; }
  /* JUDGE-2026-09-24 (D2 RE-FIX #6) — 36x36 raised to the 44x44 touch-target floor. */
  .svc-photo-order button { min-width: 44px; min-height: 44px; }
  .svc-tabs { -webkit-overflow-scrolling: touch; }
  .svc-tab, .svc-chip, .svc-tri, .svc-add-pill, .svc-skill-chip { min-height: 44px; }
}
/* SPEC-SERVICES-SETTINGS-2026-09-23 D2 END */

/* STUDIO-OS P0 BEGIN — SPEC-STUDIO-OS-P0-2026-09-16 §3. One appended block,
   tokens only (never a literal gray) so dark mode (html[data-theme="dark"]
   below) works for free. Must stay the LAST thing in this file — a suite
   test asserts exactly one BEGIN/END pair and that the block is final. */

/* -------- .seg — global segmented-control restyle (view switchers + density) -------- */
.seg { padding: 3px; gap: 2px; border-radius: 9px; border-color: var(--border); background: var(--panel); }
.seg button { border-radius: 6px; height: 28px; padding: 0 12px; font-size: 13px; font-weight: 500; color: var(--muted); }
.seg button.active { background: var(--accent-soft); color: var(--accent-hover); font-weight: 600; }
.seg button svg { width: 15px; height: 15px; }
@media (max-width: 640px) { .seg button { min-height: 44px; } }

/* -------- page header: crumbs/right-slot (h1.view-title / p.view-sub keep
   their EXISTING styles.css rules byte-unchanged — measured: they already
   render Fraunces 600 at 30-34px desktop / 24px phone (DHSHELL D4, ~line 511
   + the phone-fit block ~line 13344), so a literal-pixel override here would
   only fight an already-correct, already-tested value. See the report's
   stated deviation.) -------- */
.so-pagehead { display: flex; align-items: flex-end; gap: 16px; }
.so-pagehead-right { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.so-crumbs { font-size: 13px; font-weight: 500; color: var(--muted); display: flex; gap: 6px; align-items: center; margin-bottom: 2px; }
.so-crumb-sep { color: var(--border-strong); }

/* -------- .btn — sizing pass (colors/gradient untouched, incl. .btn-primary's) -------- */
.btn { height: 34px; border-radius: 9px; font-size: 13px; font-weight: 500; box-shadow: var(--shadow-sm); }
.btn-sm { height: 28px; font-size: 12px; border-radius: 7px; }

/* -------- stage pill (App.stagePill) -------- */
.so-stage { display: inline-flex; align-items: center; gap: 6px; height: 24px; padding: 0 9px; border-radius: 999px; font-size: 11.5px; font-weight: 600; background: color-mix(in srgb, var(--stc) 14%, var(--panel)); color: color-mix(in srgb, var(--stc) 80%, var(--text)); }
.so-stage::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: var(--stc); flex: 0 0 auto; }
.so-stage--sm { height: 20px; font-size: 11px; padding: 0 7px; gap: 4px; }
.so-stage--sm::before { width: 6px; height: 6px; }

/* -------- the one deliverable card (App.deliverableCard) -------- */
.so-card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 10px; display: flex; flex-direction: column; gap: 9px; position: relative; cursor: pointer; }
.so-card:hover { border-color: var(--border-strong); transform: translateY(-1px); transition: transform var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease); }
.so-card:focus-visible { box-shadow: var(--focus-ring); outline: none; }
.so-card--late { border-top: 2px solid var(--danger); }
.so-poster { height: 118px; border-radius: 9px; background-color: var(--hover); background-size: cover; background-position: center; position: relative; overflow: hidden; display: flex; align-items: flex-end; padding: 8px; flex-shrink: 0; }
.so-poster--empty { border: 1px dashed var(--border-strong); background-image: repeating-linear-gradient(45deg, var(--subtle) 0 8px, var(--hover) 8px 16px); }
.so-tag { position: absolute; top: 8px; left: 8px; background: rgba(16,24,40,.72); color: #fff; font-size: 10.5px; font-weight: 600; padding: 2px 7px; border-radius: 999px; letter-spacing: .02em; }
.so-ver { position: absolute; top: 8px; right: 8px; background: #fff; color: #101828; font-size: 10.5px; font-weight: 700; padding: 2px 7px; border-radius: 999px; }
.so-title { font-size: 13.5px; font-weight: 600; line-height: 1.3; margin: 0; }
.so-meta { font-size: 12px; color: var(--muted); display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.so-client { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 500; color: var(--text); }
.so-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 8px 10px; padding-top: 8px; border-top: 1px solid var(--border); }
.so-f label { display: block; font-size: 9.5px; font-weight: 700; letter-spacing: .08em; color: var(--faint); text-transform: uppercase; margin-bottom: 3px; }
.so-f .val { font-size: 12px; font-weight: 500; display: inline-flex; align-items: center; gap: 5px; }
.so-more { font-size: 11.5px; color: var(--muted); display: inline-flex; align-items: center; gap: 4px; border: 1px solid var(--border); border-radius: 999px; padding: 2px 9px; background: var(--subtle); align-self: flex-start; }
.so-next { height: 30px; border-radius: 8px; border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent-hover); font-weight: 600; font-size: 12.5px; display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.so-next--go { background: linear-gradient(180deg, var(--accent), var(--accent-hover)); color: #fff; border-color: var(--accent-hover); }
@media (max-width: 640px) { .so-next { min-height: 44px; } }
/* judge additions 2026-09-16 — the optional slots (see App.deliverableCard) and the
   Post & Review board's legacy chrome, which now rides on THIS card: the poster keeps
   the .vpr-card-thumb hook, so the old absolutely-positioned flag/timer/extra/firstver
   badges are re-flowed into the poster's bottom row (the top corners belong to the
   service tag and the version pill). */
.so-titlerow { display: flex; align-items: flex-start; gap: 6px; }
.so-titlerow .so-title { flex: 1; min-width: 0; }
.so-titlerow .vpr-card-prio { margin-top: 1px; }
.so-note { font-size: 11.5px; line-height: 1.45; color: var(--muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.so-trailing { margin-left: auto; display: inline-flex; align-items: center; }
.so-card--today { border-top: 2px solid var(--warn); }
.so-card.vpr-card { border-radius: var(--radius-lg); overflow: visible; }
.so-card.vpr-card:hover { box-shadow: var(--shadow); }
.so-poster { gap: 6px; flex-wrap: wrap; }
.so-poster .vpr-card-flag, .so-poster .vpr-card-timer, .so-poster .vpr-card-extra, .so-poster .vpr-card-firstver, .so-poster .vpr-with-client { position: static; top: auto; right: auto; left: auto; bottom: auto; }
.so-poster .vpr-card-firstver { width: 18px; height: 18px; }
.so-poster .vpr-card-timer { display: inline-flex; align-items: center; gap: 5px; }

/* -------- density (body[data-density]) -------- */
body[data-density="compact"] .so-poster { height: 84px; }
body[data-density="compact"] .so-card { gap: 6px; padding: 8px; }
body[data-density="compact"] .table td { padding-block: 6px; }
body[data-density="big"] .so-poster { height: 150px; }

/* -------- tracker kanban card: a poster strip via layout only (kc-thumb/
   kc-headrow already exist, tracker.js unchanged — CSS-only "look" pass,
   scoped to .jt-board so Leads/Tasks/Marketing boards sharing .kanban-card
   are untouched) -------- */
.jt-board .kanban-card { border-radius: var(--radius-lg); padding: 10px; }
.jt-board .kc-headrow { flex-direction: column; gap: 8px; }
.jt-board .kc-thumb { width: 100%; height: 64px; flex: 0 0 64px; border-radius: var(--radius); }

/* -------- rail: radius only (active-tab treatment stays on its existing
   theme-aware token — see the report's stated deviation) -------- */
#sidebar .nav-item { border-radius: 10px; }

/* -------- top header: search field radius/bg + kbd radius only (its 54px
   height is a deliberately re-measured, documented value — see the report's
   stated deviation; NOT reverted to the mockup's 52px) -------- */
#topheader .sidebar-search { border-radius: 9px; background: var(--subtle); }
#topheader .sidebar-search .ss-kbd { border-radius: 5px; }

/* -------- dark theme overrides -------- */
html[data-theme="dark"] .so-tag { background: rgba(0,0,0,.55); }
html[data-theme="dark"] .so-ver { background: #1c2330; color: #fff; }
html[data-theme="dark"] .so-next { background: color-mix(in srgb, var(--accent) 22%, var(--panel)); }
html[data-theme="dark"] .so-next--go { background: linear-gradient(180deg, var(--accent), var(--accent-hover)); color: #fff; }
html[data-theme="dark"] .so-stage { background: color-mix(in srgb, var(--stc) 26%, var(--panel)); color: color-mix(in srgb, var(--stc) 70%, #fff); }
/* STUDIO-OS P0 END */
/* STUDIO-OS A BEGIN — SPEC-STUDIO-OS-A-2026-09-16. One appended block, tokens
   only, immediately after the P0 block (a suite test asserts this exact
   adjacency and that THIS block is the final thing in the file). Covers:
   §2 the tracker Board column-head swatch, §3 the Post & Review lane-head
   swatch, and §5 the new Workload grid — all reuse the SAME --stc custom
   property idiom App.stagePill/App.densitySwitcher already established. */

/* -------- §2 tracker Board column head: 8px swatch (CSS ::before, never a
   DOM child — a suite test indexes .kanban-col-head's children by position)
   + 2px bottom border, both in the stage color. Scoped to .jt-board so the
   Leads/Tasks/Marketing/Pipelines boards sharing .kanban-col-head are
   untouched. -------- */
.jt-board .kanban-col-head { border-bottom: 2px solid var(--stc, var(--border)); }
.jt-board .kanban-col-head::before { content: ''; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--stc, var(--border)); flex: 0 0 auto; order: -1; }

/* -------- §3 Post & Review Board lane head: same swatch + bottom-border
   treatment, in the MAPPED deliverable-stage color (postreview.js's
   mapLane) — the pre-existing .vpr-lane-head--neutral modifier stays
   (health still lives on the ring only; this is the stage's own signal,
   "one meaning per element"). -------- */
.vpr-lane-head { border-bottom: 2px solid var(--stc, var(--border)); }
.vpr-lane-head::before { content: ''; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--stc, var(--border)); flex: 0 0 auto; order: -1; }

/* -------- §5 Workload -------- */
.wk-toolbar { flex-wrap: wrap; row-gap: 8px; }
.wk-pill-over { color: var(--danger-ink); background: var(--danger-soft); border-color: var(--danger-soft); }
.wk-pill-at { color: var(--warn-ink); background: var(--warn-soft); border-color: var(--warn-soft); }
.wk-pill-room { color: var(--ok-ink); background: var(--ok-soft); border-color: var(--ok-soft); }
.wk-body { display: flex; gap: 14px; flex: 1; min-height: 0; }
.wk-backlog { width: 260px; flex-shrink: 0; display: flex; flex-direction: column; overflow: hidden; }
.wk-backlog-head { padding: 12px 12px 8px; display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 13.5px; }
.wk-backlog-head .count { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--subtle); color: var(--muted); font-size: 11px; font-weight: 700; display: inline-flex; align-items: center; justify-content: center; }
.wk-backlog-list { padding: 0 12px 12px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto; }
.wk-backlog-foot { margin-top: auto; padding: 12px; border-top: 1px solid var(--border); font-size: 12px; color: var(--muted); }
.wk-backlog-card { border-radius: var(--radius); padding: 8px 9px; border: 1px solid var(--border); border-left: 3px solid var(--stc, var(--border)); background: var(--panel); box-shadow: var(--shadow-sm); cursor: grab; }
.wk-backlog-card.dragging { opacity: .45; }
.wk-backlog-title { font-size: 12.5px; font-weight: 600; }
.wk-backlog-sub { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.wk-gridpanel { flex: 1; min-width: 0; overflow: auto; }
.wk-grid { display: grid; grid-template-columns: 220px repeat(5, minmax(0, 1fr)); border-top: 1px solid var(--border); }
.wk-grid--weeks { grid-template-columns: 220px repeat(6, minmax(0, 1fr)); }
.wk-dayhead { min-height: 0; padding: 8px 10px; font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); background: var(--subtle); border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); display: flex; align-items: center; gap: 6px; }
.wk-dayhead b { display: block; font-size: 14px; font-weight: 600; color: var(--text); letter-spacing: 0; text-transform: none; }
.wk-dayhead--today { box-shadow: inset 0 -2px 0 var(--accent); }
.wk-dayhead--today b { color: var(--accent-hover); }
.wk-row-head { border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); padding: 10px; display: flex; flex-direction: column; justify-content: center; gap: 6px; min-width: 0; }
.wk-who { display: flex; gap: 10px; align-items: flex-start; min-width: 0; }
.wk-who-name { font-weight: 600; font-size: 13px; }
.wk-who-cap { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.wk-bar { height: 6px; border-radius: 3px; background: var(--hover); overflow: hidden; }
.wk-bar i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.wk-bar i.over { background: var(--danger); }
.wk-cell { border-bottom: 1px solid var(--border); border-right: 1px solid var(--border); padding: 8px; min-height: 100px; display: flex; flex-direction: column; gap: 6px; }
.wk-cell--today { background: color-mix(in srgb, var(--accent) 4%, var(--panel)); }
.wk-cell--off { background: repeating-linear-gradient(45deg, var(--bg) 0 6px, var(--hover) 6px 12px); }
.wk-cell--dragover { outline: 2px dashed var(--accent); outline-offset: -2px; }
.wk-cell--week { align-items: center; justify-content: center; }
.wk-cell-hrs { font-size: 11px; color: var(--muted); text-align: right; margin-top: auto; }
.wk-cell-hrs--over { color: var(--danger-ink); font-weight: 700; }
.wk-blk { border-radius: var(--radius-sm); padding: 6px 8px; font-size: 12px; font-weight: 500; border: 1px solid var(--border); background: var(--panel); display: flex; flex-direction: column; gap: 2px; box-shadow: var(--shadow-sm); border-left: 3px solid var(--stc, var(--border)); }
.wk-blk-title { line-height: 1.25; }
.wk-blk-hint { font-size: 11px; color: var(--muted); font-weight: 500; }
.wk-blk--shoot { background: var(--accent-soft); border-color: var(--accent-soft); border-left-color: var(--accent); }
.wk-blk--edit[draggable="true"] { cursor: grab; }
.wk-blk--off { background: repeating-linear-gradient(45deg, var(--bg) 0 6px, var(--hover) 6px 12px); color: var(--muted); text-align: center; border-left-color: var(--border-strong); }
@media (max-width: 640px) {
  .wk-grid, .wk-grid--weeks { grid-template-columns: 160px repeat(5, minmax(120px, 1fr)); overflow-x: auto; }
  .wk-body { flex-direction: column; }
  .wk-backlog { width: auto; }
}
/* STUDIO-OS A END */
/* STUDIO-OS B BEGIN — SPEC-STUDIO-OS-B. Job page 2.0: header chips, the Now
   strip, the Deliverables table, and the docked thread panel. Appended once,
   at the very end of the file (P0 §0's "block is last" rule, extended for
   B — see tests/suite.js's updated STUDIOOS_P0 stylesheet-shape test: this is
   a STUDIO-OS-marked block following P0 END, the one shape that test now
   accepts). Tokens are the SAME live :root custom properties every other
   block in this file already reads (var(--accent)/--muted/--border/etc) —
   never the mockup's own studio.css names (--r-lg, --sh, …), which are a
   design-reference file only, not shipped. */

/* -------- header chips row (§1) -------- */
.jp-chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px; }
.jp-client-chip, .jp-due-chip, .jp-paid-chip { height: 28px; box-sizing: border-box; }
.jp-due-chip[data-tone="red"] { background: var(--danger-soft); color: var(--danger-ink); }
.jp-due-chip[data-tone="amber"] { background: var(--warn-soft); color: var(--warn-ink); }
.jp-due-chip[data-tone="green"] { background: var(--ok-soft); color: var(--ok-ink); }
.jp-due-chip[data-tone="neutral"] { background: var(--hover); color: var(--muted); }
.jp-paid-chip { background: var(--ok-soft); color: var(--ok-ink); }

/* -------- Now strip (§2) -------- */
.jp-now { display: flex; align-items: center; gap: 14px; padding: 12px 14px; margin-bottom: 16px; border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--radius-lg); background: var(--panel); box-shadow: var(--shadow-sm); flex-wrap: wrap; }
.jp-now-icon { width: 34px; height: 34px; flex: 0 0 auto; border-radius: 9px; background: var(--accent-soft); color: var(--accent-hover); display: flex; align-items: center; justify-content: center; }
.jp-now-body { flex: 1 1 220px; min-width: 0; }
.jp-now-label { font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--accent-hover); }
.jp-now-line1 { font-size: 14px; font-weight: 600; margin-top: 2px; overflow-wrap: anywhere; }
.jp-now-line2 { font-size: 12.5px; color: var(--muted); margin-top: 1px; overflow-wrap: anywhere; }
.jp-now .btn, .jp-now .qc-preflight { flex: 0 0 auto; }
.jp-now .qc-preflight { flex-basis: 100%; }
@media (max-width: 640px) { .jp-now { flex-direction: column; align-items: stretch; } .jp-now .btn { min-height: 44px; width: 100%; } }

/* -------- Deliverables table (§3) -------- */
.so-table-card { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--panel); box-shadow: var(--shadow-sm); overflow: hidden; margin-bottom: 16px; }
.so-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.so-table th { text-align: left; font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--faint); padding: 8px 10px; border-bottom: 1px solid var(--border); background: var(--subtle); }
.so-table td { padding: 10px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.so-table tr:last-child td { border-bottom: 0; }
.so-table tr.variant td { padding-left: 34px; background: var(--subtle); font-size: 12.5px; color: var(--muted); }
.so-table tr.variant td:first-child { position: relative; }
.so-table tr.variant td:first-child::before { content: ''; position: absolute; left: 18px; top: 0; bottom: 50%; width: 9px; border-left: 1.5px solid var(--border-strong); border-bottom: 1.5px solid var(--border-strong); border-radius: 0 0 0 6px; }
.so-table-thumb { display: inline-block; width: 44px; height: 30px; border-radius: 5px; background: var(--hover); background-size: cover; background-position: center; flex-shrink: 0; vertical-align: middle; margin-right: 8px; }
.so-table td > div:has(.so-table-thumb) { display: flex; align-items: center; }
.so-table-footer { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--border); flex-wrap: wrap; }
.so-table [data-service] .btn { min-height: 32px; }
@media (max-width: 760px) {
  .so-table thead { display: none; }
  .so-table, .so-table tbody, .so-table tr, .so-table td { display: block; width: 100%; }
  .so-table tr { border-bottom: 1px solid var(--border); padding: 8px 10px; }
  .so-table td { border-bottom: 0; padding: 3px 0; }
  .so-table tr.variant td:first-child::before { display: none; }
}

/* -------- folded Job details (§3) -------- */
.jp-details { margin-top: 4px; }
.jp-details > summary { list-style: none; cursor: pointer; min-height: 44px; display: flex; align-items: center; padding: 10px 4px; font-size: 13px; font-weight: 600; color: var(--text); border-top: 1px solid var(--border); }
.jp-details > summary::-webkit-details-marker { display: none; }
.jp-details > summary::after { content: '›'; margin-left: 8px; color: var(--muted); transition: transform .15s; }
.jp-details[open] > summary::after { transform: rotate(90deg); }
.jp-details-body { padding-top: 6px; }

/* -------- page layout: header full width, body row = main column + thread (§4) -------- */
.jp-body-row-outer { display: flex; flex-direction: column; }
.jp-body-row { display: flex; align-items: flex-start; gap: 18px; }
.jp-main-col { flex: 1 1 auto; min-width: 0; }
.jp-thread-host { flex: 0 0 auto; min-width: 0; }
.jp-body-row.jp-dragover { outline: 2px dashed var(--accent); outline-offset: 4px; border-radius: var(--radius-lg); }

/* -------- docked thread panel (§4) -------- */
.jp-thread { width: 360px; flex: 0 0 360px; display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--panel); box-shadow: var(--shadow-sm); max-height: 640px; overflow: hidden; }
.jt-head { display: flex; align-items: center; gap: 8px; height: 48px; padding: 0 12px; border-bottom: 1px solid var(--border); font-weight: 600; font-size: 13.5px; flex-shrink: 0; min-width: 0; }
/* JOBCONTROLS/STUDIO-OS-B — min-width:0 is load-bearing: a flex item's default
   min-width:auto ignores text-overflow:ellipsis entirely and instead grows to
   fit its content, which is exactly what pushed the whole thread panel (and
   the page) past a 320px viewport (measured: 356px vs 320px clientWidth). */
.jt-head-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1 1 auto; }
.jt-head > svg, .jt-head-title, .jt-count, .jt-avs, .jt-openchat { flex-shrink: 0; }
.jt-head-title { flex-shrink: 1; }
.jt-count { min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--accent); color: #fff; font-size: 11px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; }
.jt-avs { display: inline-flex; }
.jt-avs .jt-avs-item, .jt-avs .av { margin-left: -6px; border: 2px solid var(--panel); border-radius: 50%; }
.jt-avs .jt-avs-item:first-child { margin-left: 0; }
.jt-openchat { width: 28px; height: 28px; border-radius: 8px; border: 1px solid transparent; background: transparent; color: var(--muted); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
.jt-openchat:hover { background: var(--hover); border-color: var(--border); }
.jt-body { flex: 1 1 auto; overflow-y: auto; padding-top: 6px; min-height: 120px; }
.jt-msg { display: flex; gap: 10px; padding: 8px 12px; }
.jt-msg-avspace { width: 26px; flex-shrink: 0; }
.jt-msg-b { flex: 1; min-width: 0; }
.jt-msg-who { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; flex-wrap: wrap; }
.jt-msg-who span { color: var(--faint); font-size: 11px; }
.jt-msg p { font-size: 13px; line-height: 1.45; margin: 2px 0 0; overflow-wrap: anywhere; }
.jt-av { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 10.5px; font-weight: 700; flex-shrink: 0; }
.jt-av--client { background: var(--accent-warm); }
.jt-att { margin-top: 6px; border: 1px solid var(--border); border-radius: 9px; padding: 6px 9px; display: flex; align-items: center; gap: 8px; font-size: 12px; background: var(--subtle); }
.jt-aud { display: inline-flex; align-items: center; font-size: 10.5px; font-weight: 600; color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 1px 7px; }
.jt-sys { padding: 4px 12px; }
.jt-sys span:first-child { font-size: 11.5px; color: var(--faint); }
.jt-sys-time { margin-left: 6px; color: var(--faint); font-size: 10.5px; }
.jt-msg--client .jt-msg-b { background: var(--accent-warm-soft); border-radius: 10px; padding: 6px 10px; }
.jt-empty { padding: 24px 16px; text-align: center; color: var(--muted); font-size: 12.5px; }
.jt-sendstate { margin-top: 4px; border: 0; background: transparent; padding: 0; font-size: 11px; color: var(--muted); cursor: default; }
.jt-sendstate--failed { color: var(--danger-ink); cursor: pointer; text-decoration: underline; }
.jt-composer-host { border-top: 1px solid var(--border); padding: 10px 12px 12px; flex-shrink: 0; }
.jt-composer { border: 1px solid var(--border); border-radius: 12px; padding: 8px 10px 6px; }
.jt-composer:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent); }
.jt-ta { width: 100%; border: 0; outline: 0; resize: none; background: transparent; color: var(--text); font: inherit; font-size: 14px; min-height: 22px; max-height: 120px; padding: 0; }
.jt-tools { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.jt-aud-fixed { font-size: 11px; font-weight: 600; color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 2px 8px; }
.jt-send { margin-left: auto; width: 32px; height: 32px; border-radius: 9px; background: var(--accent); color: #fff; border: 0; display: flex; align-items: center; justify-content: center; }
.jt-send:hover { background: var(--accent-hover); }
.jt-barbtn { display: none; }

/* -------- iOS anti-zoom: every new focusable control under 640px joins the
   ≥16px list (PLAN-STUDIO-OS §8 trap) -------- */
@media (max-width: 640px) {
  .jt-ta { font-size: 16px; }
}

/* -------- phone: thread collapses to a bar below the content, expands on
   tap (§4) -------- */
@media (max-width: 1099px) {
  .jp-body-row { flex-direction: column; }
  .jp-thread { width: 100%; flex: 1 1 auto; max-height: none; order: 2; min-width: 0; }
  .jp-thread-host { width: 100%; }
  .jp-thread .jt-barbtn { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 44px; border: 0; border-top: 1px solid var(--border); background: var(--subtle); color: var(--text); font-weight: 600; font-size: 13px; order: 1; }
  .jp-thread .jt-body, .jp-thread .jt-composer-host { display: none; }
  .jp-thread.jt-expanded .jt-body { display: block; max-height: 320px; }
  .jp-thread.jt-expanded .jt-composer-host { display: block; }
  .jp-thread .jt-head { order: 0; }
}
@media (min-width: 1100px) {
  .jp-thread .jt-barbtn { display: none; }
}

/* -------- dark theme -------- */
html[data-theme="dark"] .jp-now { background: var(--panel); }
html[data-theme="dark"] .so-table th { background: var(--subtle); }
html[data-theme="dark"] .jt-msg--client .jt-msg-b { background: color-mix(in srgb, var(--accent-warm) 20%, var(--panel)); }
/* STUDIO-OS B END */

/* STUDIO-OS C BEGIN — SPEC-STUDIO-OS-C-2026-09-16. Review 2.0 chrome (§1),
   the upload tray (§3) and the "+N more fields" expando (§5). Tokens only —
   no new colors — matching the player's existing dark-theater look and the
   app's light/dark pair everywhere else. */

/* -------- Review 2.0: stage pill on the title, version tabs, Compare -------- */
.review-title .so-stage { font-size: 10.5px; }
.review-vertabs.seg { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.14); }
.review-vertabs.seg button, .review-railtabs.seg button { color: rgba(255,255,255,.65); }
.review-vertabs .seg-btn.on, .review-railtabs .seg-btn.on { background: var(--accent); color: #fff; }
.review-vers .btn.on { background: var(--accent); color: #fff; border-color: var(--accent); }
.review-stage--comparing { display: flex; gap: 8px; align-items: stretch; }
.review-stage--comparing .review-video { flex: 1 1 50%; max-width: 50%; }
.review-stage--comparing .review-canvas { max-width: 50%; }
.review-compare { position: relative; flex: 1 1 50%; min-width: 0; display: flex; flex-direction: column; background: #000; border-radius: 8px; overflow: hidden; }
.review-compare[hidden] { display: none; }
.review-stage--swapped.review-stage--comparing { flex-direction: row-reverse; }
.review-compare-empty { color: rgba(255,255,255,.6); font-size: 12.5px; text-align: center; padding: 24px 12px; }
.review-video--compare { width: 100%; flex: 1 1 auto; min-height: 0; object-fit: contain; background: #000; }
.review-compare-pick { position: absolute; top: 6px; left: 6px; z-index: 2; }
.review-compare-swap { position: absolute; top: 6px; right: 6px; z-index: 2; }
@media (max-width: 1099px) {
  .review-stage--comparing { flex-direction: column; }
  .review-stage--comparing .review-video { max-width: 100%; flex: 1 1 auto; }
}

/* -------- Review 2.0: comment card segmented groupings (visual only — the
   underlying .review-generaltoggle / .review-audiencetoggle stay the SAME
   single toggle elements the suite pins; this just makes each read as a
   two-state segment, per board 3) -------- */
.review-generaltoggle { border-radius: 999px 0 0 999px; }
.review-audiencetoggle { border-radius: 999px; }

/* -------- Review 2.0: right-rail tabs (Review · Versions · Activity) -------- */
.review-rail-versions[hidden], .review-rail-activity[hidden] { display: none; }
.review-rail-versions, .review-rail-activity { padding: 4px 16px; overflow-y: auto; flex: 1 1 auto; min-height: 0; }
.review-rail-verrow:last-child { border-bottom: none !important; }
.review-rail-actrow { border-bottom: 1px solid var(--border); padding-bottom: 6px !important; margin-bottom: 2px; }
.review-rail-actrow:last-child { border-bottom: none; }

/* -------- Review 2.0: footer QC line + caption (additive siblings inside
   the EXISTING .review-statusbtns — the 3 buttons themselves are untouched) -------- */
.review-statuscap { line-height: 1.4; }

/* -------- §3 the upload tray -------- */
.so-tray { position: fixed; right: 16px; bottom: 16px; z-index: 1200; display: flex; align-items: center; gap: 10px; background: var(--panel); border: 1px solid var(--border-strong); border-radius: 12px; box-shadow: var(--shadow-lg, 0 8px 24px rgba(0,0,0,.18)); padding: 10px 12px; max-width: min(420px, calc(100vw - 32px)); animation: so-tray-in .18s var(--ease, ease-out); }
@keyframes so-tray-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.so-tray-body { min-width: 0; }
.so-tray-title { font-weight: var(--fw-bold); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px; }
.so-tray-sub { font-size: 11.5px; }
.so-tray-x { border: 0; background: transparent; color: var(--muted); font-size: 18px; line-height: 1; cursor: pointer; width: 28px; height: 28px; border-radius: 999px; flex-shrink: 0; }
.so-tray-x:hover { background: var(--hover); color: var(--text); }
@media (max-width: 640px) {
  .so-tray { left: 16px; right: 16px; max-width: none; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }
  .so-tray button { min-height: 44px; }
  .so-tray-x { min-width: 44px; }
}

/* -------- §5 the card's real "+N more fields" -------- */
.so-more { border: 0; background: transparent; color: var(--accent); font-size: 11px; font-weight: var(--fw-bold); cursor: pointer; padding: 2px 0; text-align: left; }
.so-more:hover { text-decoration: underline; }
.so-fields--more { margin-top: 4px; padding-top: 6px; border-top: 1px dashed var(--border); }
@media (max-width: 640px) {
  .so-more { min-height: 44px; display: flex; align-items: center; }
}

/* -------- dark theme -------- */
html[data-theme="dark"] .review-compare { background: #000; }
html[data-theme="dark"] .so-tray { background: var(--panel); border-color: var(--border-strong); }
/* STUDIO-OS C END */
/* STUDIO-OS D BEGIN — SPEC-STUDIO-OS-D-2026-09-16: One Jobs (scope chips, the
   New job picker), Workload's Settings estimates table, and the Month
   calendar chip's status-color edge. One appended block, per §0. */

/* §1.2 — the Jobs scope chips row (.chips), and workload.js's own pre-existing
   Everyone/Editors/Shooters filter row (SAME class, never styled until now —
   `/usr/bin/grep -n "\.chips" styles.css` returned nothing before this wave). */
.chips { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 10px 0; }
.chips .chip { cursor: pointer; border: 1px solid var(--border); background: var(--panel); color: var(--text); font: inherit; }
.chips .chip:hover { background: var(--subtle); }
/* two pre-existing "selected chip" conventions in this codebase (viewSwitcher/
   scopeSeg's own .active, postreview.js's/workload.js's own .on) — both
   supported so this ONE rule covers every .chips consumer, old and new. */
.chips .chip.active, .chips .chip.on {
  background: var(--accent-soft); color: var(--accent-hover);
  border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent);
}
.so-jobs-scope { margin-top: 4px; }

/* §1.3 — the New job picker sheet (App.modal body) */
.so-newjob-picker { display: flex; flex-direction: column; gap: 10px; }
.so-newjob-opt {
  display: block; width: 100%; text-align: left; padding: 12px 14px; border-radius: 10px;
  border: 1px solid var(--border); background: var(--panel); color: var(--text); cursor: pointer; font: inherit;
}
.so-newjob-opt:hover, .so-newjob-opt:focus-visible { background: var(--subtle); border-color: var(--accent); }
.so-newjob-opt-title { font-weight: var(--fw-semibold); font-size: 14px; }
.so-newjob-opt-sub { color: var(--muted); font-size: 12.5px; margin-top: 2px; }

/* §3 board ruling — Month's dense shoot chip gets a status-color left edge
   (calendar.js sets --cal-status-color via style.setProperty; see that file's
   own comment for why the Week/Day .cal-hblock block and the "By person"
   lane's non-dense .cal-event are deliberately NOT touched — both are pinned
   to the SHOOTER'S color on this exact edge). */
.cal-event--dense { border-left: 3px solid var(--cal-status-color, transparent); }

/* §2.2 — Workload's "Edit estimates per deliverable" Settings card */
.wk-estimates-table { max-height: 320px; overflow-y: auto; margin-top: 4px; }
.wk-estimates-table input[type="number"] { text-align: right; }

/* -------- iOS anti-zoom: every new focusable control under 640px joins the
   ≥16px list (PLAN-STUDIO-OS §8 trap) -------- */
@media (max-width: 640px) {
  .wk-estimates-table input[type="number"] { font-size: 16px; }
}
/* STUDIO-OS D END */

/* STUDIO-OS E BEGIN — SPEC-STUDIO-OS-E-2026-09-16. Plan › Content (board 5),
   Docs as deliverables, and the Reports Production section. ONE appended
   block, per the spec's §0 file-scope rule. iOS anti-zoom: every new
   focusable control under 640px is in the media query at the bottom. */

/* ---------------- Plan → Content ---------------- */
.plan-view { display: flex; flex-direction: column; gap: 12px; }
.plan-toolbar { display: flex; flex-direction: column; gap: 8px; }
.plan-brand-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.plan-brand-chips .chip, .plan-toolbar-row2 .seg + .btn, .reports-range-chips .chip {
  background: var(--subtle); border: 1px solid var(--border); cursor: pointer; color: var(--text);
}
.plan-brand-chips .chip.chip--on, .reports-range-chips .chip.chip--on {
  background: var(--accent-soft, var(--subtle)); border-color: var(--accent); color: var(--accent);
}
.plan-toolbar-row2 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.plan-toolbar-row2 .right, .plan-counts { color: var(--muted); font-size: 12.5px; }
.plan-nav { display: inline-flex; gap: 4px; }
.plan-body { display: grid; grid-template-columns: minmax(0,1fr) 220px; gap: 14px; }
.plan-grid-host { grid-column: 1; }
.plan-unplanned-rail { grid-column: 2; grid-row: 1 / span 2; border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); padding: 10px; max-height: 640px; overflow-y: auto; }
.plan-composer-host { grid-column: 1; border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); padding: 14px; }
.plan-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.plan-grid-dow { background: var(--subtle); padding: 6px 8px; font-size: 11px; font-weight: var(--fw-semibold); color: var(--muted); text-align: center; }
.plan-day { background: var(--panel); min-height: 96px; padding: 6px; display: flex; flex-direction: column; gap: 3px; }
.plan-grid--week .plan-day { min-height: 220px; }
.plan-day--out { background: var(--subtle); opacity: .55; }
.plan-day--today { box-shadow: inset 0 0 0 2px var(--accent); }
.plan-day-num { font-size: 11px; font-weight: var(--fw-semibold); color: var(--muted); }
.plan-chip { font-size: 10.5px; border-radius: 6px; padding: 2px 5px; display: flex; align-items: center; gap: 4px; cursor: default; line-height: 1.3; }
.plan-chip--shoot { background: var(--accent-soft, var(--subtle)); color: var(--accent); }
.plan-chip--due { background: var(--warn-soft); color: var(--warn-ink); }
.plan-chip--post { background: var(--subtle); border: 1px solid var(--border); cursor: pointer; }
.plan-chip-dot { width: 6px; height: 6px; border-radius: 50%; flex: 0 0 auto; }
.plan-unplanned-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 6px 8px; margin-bottom: 6px; font-size: 12px; display: flex; align-items: center; justify-content: space-between; gap: 6px; cursor: grab; background: var(--panel); }
.plan-composer-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.plan-composer-head input { flex: 1; }
.plan-network-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.plan-network-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 10px; cursor: pointer; min-width: 120px; }
.plan-network-card--active { border-color: var(--accent); background: var(--accent-soft, var(--subtle)); }
.plan-network-name { font-weight: var(--fw-semibold); font-size: 13px; }
.plan-caption { width: 100%; box-sizing: border-box; font: inherit; }
.plan-counters { margin-top: 4px; }
.plan-phone-preview { width: 260px; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; margin: 12px 0; }
.plan-phone-head { display: flex; align-items: center; gap: 8px; padding: 8px; font-size: 12px; font-weight: var(--fw-semibold); }
.plan-phone-poster { width: 100%; aspect-ratio: 1 / 1; background: var(--subtle); }
.plan-phone-caption { padding: 8px; font-size: 12px; color: var(--muted); }
.plan-datetime-row { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; flex-wrap: wrap; }
.plan-suggest-row { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 10px; }
.plan-approval-row { border-top: 1px solid var(--border); padding-top: 10px; margin-top: 6px; }
.plan-waiting-note { font-size: 12px; color: var(--warn-ink); margin-bottom: 8px; }
.plan-approval-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.plan-publish-row { border-top: 1px solid var(--border); padding-top: 10px; margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
.plan-publish-line { display: flex; align-items: center; gap: 8px; font-size: 13px; }

/* ---------------- Docs as deliverables ---------------- */
.docs-view { display: flex; flex-direction: column; gap: 12px; }
.docs-body { display: grid; grid-template-columns: 260px minmax(0,1fr); gap: 14px; }
.docs-list-col { border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); padding: 10px; max-height: 700px; overflow-y: auto; }
.docs-main-col { border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); padding: 14px; display: flex; flex-wrap: wrap; gap: 14px; }
.docs-row { display: flex; align-items: center; gap: 6px; padding: 7px 8px; border-radius: var(--radius-sm); cursor: pointer; font-size: 12.5px; }
.docs-row:hover { background: var(--subtle); }
.docs-row--active { background: var(--accent-soft, var(--subtle)); }
.docs-row-icon { flex: 0 0 auto; }
.docs-row-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.docs-row-sub { color: var(--muted); font-size: 10.5px; }
.docs-banner { background: var(--warn-soft); color: var(--warn-ink); border-radius: var(--radius-sm); padding: 6px 10px; font-size: 12.5px; width: 100%; }
.docs-title-input { flex: 1; min-width: 160px; font-weight: var(--fw-semibold); }
.docs-meta-row { display: flex; align-items: center; gap: 10px; width: 100%; flex-wrap: wrap; }
.docs-meta-row .right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.docs-editor-host { flex: 1 1 420px; min-width: 280px; }
.docs-fmt-bar { display: flex; gap: 4px; margin-bottom: 6px; }
.docs-fmt-bar button { border: 1px solid var(--border); background: var(--subtle); border-radius: var(--radius-sm); padding: 3px 8px; font-size: 12px; cursor: pointer; }
.docs-ta { width: 100%; min-height: 360px; box-sizing: border-box; font: inherit; }
.docs-rail { flex: 0 0 240px; display: flex; flex-direction: column; gap: 10px; }
.docs-rail-card { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 8px 10px; }
.docs-rail-card h4 { margin: 0 0 6px; font-size: 12.5px; }
.docs-version-row { display: flex; align-items: center; justify-content: space-between; gap: 6px; font-size: 11.5px; padding: 4px 0; border-bottom: 1px solid var(--border); }
.docs-diff { margin-top: 8px; font-size: 11px; font-family: var(--font-mono, monospace); white-space: pre-wrap; max-height: 220px; overflow-y: auto; }
.docs-diff-line--add { background: var(--ok-soft); color: var(--ok-ink); }
.docs-diff-line--del { background: var(--danger-soft); color: var(--danger-ink); text-decoration: line-through; }
.docs-tpl-btn { border: 1px solid var(--border); background: var(--subtle); border-radius: var(--radius-sm); padding: 8px 10px; cursor: pointer; font-size: 13px; }
.docs-tpl-btn:hover { border-color: var(--accent); }

/* ---------------- Reports — Production section ---------------- */
.reports-production { display: flex; flex-direction: column; gap: 10px; }
.reports-range-chips { display: flex; gap: 6px; }
.reports-card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 12px; }
.reports-card h4 { margin: 0 0 8px; font-size: 13px; }
.reports-card--wide { grid-column: 1 / -1; }
.reports-donut-row { display: flex; align-items: center; gap: 14px; }
.reports-legend { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; }
.reports-legend-row { display: flex; align-items: center; gap: 6px; }
.reports-legend-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.reports-big-num { font-size: 28px; font-weight: var(--fw-bold); font-family: var(--font-display, inherit); }
.reports-row { display: flex; justify-content: space-between; font-size: 12.5px; padding: 3px 0; }
.reports-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.reports-table th, .reports-table td { text-align: right; padding: 4px 6px; border-bottom: 1px solid var(--border); }
.reports-table th:first-child, .reports-table td:first-child { text-align: left; }
.reports-bars { display: flex; align-items: flex-end; gap: 6px; height: 72px; }
.reports-bar { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; flex: 1; gap: 2px; }
.reports-bar-fill { width: 100%; max-width: 20px; background: var(--accent); border-radius: 3px 3px 0 0; }
.reports-bar-num { font-size: 9px; color: var(--muted); }

/* -------- responsive: stack the two-column layouts on phones -------- */
@media (max-width: 720px) {
  .plan-body { grid-template-columns: 1fr; }
  .plan-unplanned-rail, .plan-composer-host { grid-column: 1; }
  .docs-body { grid-template-columns: 1fr; }
}

/* -------- iOS anti-zoom: every new focusable control under 640px joins the
   ≥16px list (PLAN-STUDIO-OS §8 trap) -------- */
@media (max-width: 640px) {
  .docs-ta, .docs-title-input, .plan-caption, .plan-datetime-row input, .docs-list-col input[type="search"] { font-size: 16px; }
}
/* STUDIO-OS E END */

/* STUDIO-OS F BEGIN — SPEC-STUDIO-OS-F-2026-09-17. Jobs' List/Board/quick-tabs:
   the service pill (App.servicePill), the job stage pill re-skin (shares
   .so-stage with App.stagePill, P0), the one job card (App.jobCard) across
   its four densities, the List/Board containers, the legend, and quick tabs
   (which reuse the EXISTING .chips/.chip rules, STUDIO-OS D §1.2 — no new
   chip CSS needed there). Values ported from the approved design canvas
   (~/vvt-hub-STUDIO-OS-P0-2026-09-16/DESIGN2/project/studio2.css, the "round
   4: calmer pills" block — the LAST .svc block in that file, confirmed the
   winning one per spec) onto this app's OWN design tokens (--panel/--border/
   --text/--muted/--ok-soft/--ok-ink/--shadow/--radius-lg, …) so dark mode
   (html[data-theme="dark"] below) works for free — tokens only, never a
   literal gray. One appended block, per every prior wave's own §0 rule. */

/* -------- §2 — the service pill (App.servicePill) — round-4 anatomy: a
   neutral white pill, 1px border, an 8px colored dot, the service name, then
   the status word in the status color. --stc is set inline per pill. -------- */
.so-svc {
  display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px;
  border-radius: 999px; border: 1px solid var(--border); background: var(--panel);
  font-size: 11px; font-weight: 600; color: var(--text); white-space: nowrap; max-width: 100%;
}
.so-svc-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--stc); flex: 0 0 auto; }
.so-svc-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.so-svc-status { font-weight: 600; color: color-mix(in srgb, var(--stc) 72%, var(--text)); }
/* todo — filled light-grey pill, dark text (measured ≥4.5:1 — spec §7's own contrast test) */
.so-svc--todo { background: var(--hover); border-color: var(--border); color: #344054; }
.so-svc--todo .so-svc-status { color: #475467; }
/* raws0 — a 9% rose tint (round-4's own ".svc.solid" treatment) */
.so-svc--raws0 { background: color-mix(in srgb, var(--stc) 9%, var(--panel)); border-color: color-mix(in srgb, var(--stc) 35%, var(--panel)); }
/* ok — soft green tint + a leading check on the status word */
.so-svc--ok { background: var(--ok-soft); border-color: color-mix(in srgb, var(--ok) 45%, var(--panel)); color: var(--ok-ink); }
.so-svc--ok .so-svc-status { color: var(--ok-ink); }
.so-svc--ok .so-svc-status::before { content: '✓ '; }
/* mini — name + dot only, no status word (the full status rides the title attr) */
.so-svc--mini { height: 20px; padding: 0 7px; font-size: 10.5px; gap: 4px; }
.so-svc--mini .so-svc-status { display: none; }

/* the one-line legend (App.serviceLegend) — hidden on phones (spec §4.7) */
.so-svc-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; font-size: 11.5px; color: var(--muted); margin: 6px 0 10px; }
.so-legend-item { display: inline-flex; align-items: center; gap: 5px; }
.so-legend-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
@media (max-width: 640px) { .so-legend-host { display: none !important; } }

/* -------- §3 — App.jobCard, the ONE card for List and Board -------- */
.jcard { cursor: pointer; }
.jcard .jc-faint, .jc-faint { color: var(--faint); }
.jc-sep { color: var(--border-strong); }

/* compact — one line */
.jcard--compact {
  flex-direction: row; align-items: center; gap: 10px; padding: 8px 12px; flex-wrap: nowrap;
  overflow-x: auto; overflow-y: visible;
}
.jcard--compact .jc-addr { font-weight: 600; font-size: 12.5px; flex: 0 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 220px; }
.jcard--compact .jc-agent { font-size: 12px; color: var(--muted); flex: 0 0 auto; }
.jcard--compact .jc-pkg { font-size: 12px; color: var(--muted); flex: 0 0 auto; }
.jcard--compact .jc-svc-minis { display: flex; gap: 4px; flex: 0 0 auto; }
.jcard--compact .jc-outof { font-size: 11px; color: var(--muted); flex: 0 0 auto; white-space: nowrap; }
.jcard--compact .so-stage { margin-left: auto; flex: 0 0 auto; }
.jcard--compact .jc-upload-btn { flex: 0 0 auto; }

/* normal / expanded — thumb + full header */
.jcard--normal, .jcard--board { flex-direction: column; align-items: stretch; gap: 8px; padding: 12px; }
.jc-header { display: flex; gap: 12px; align-items: flex-start; }
.jc-poster { width: 72px; height: 54px; border-radius: var(--radius); background-color: var(--hover); background-size: cover; background-position: center; flex: 0 0 auto; }
.jc-poster.so-poster--empty { border: 1px dashed var(--border-strong); background-image: repeating-linear-gradient(45deg, var(--subtle) 0 8px, var(--hover) 8px 16px); }
.jc-headinfo { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.jc-title { font-size: 13.5px; font-weight: 600; line-height: 1.3; margin: 0; }
.jc-meta { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); flex-wrap: wrap; }
.jc-shootrow { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text); }
.jc-val { display: inline-flex; align-items: center; gap: 4px; }
.jc-pills { display: flex; flex-wrap: wrap; gap: 5px; }
.jc-pills--wrap { flex-wrap: wrap; }
/* the why line: warn color ONLY for a genuine wait (jcWhyIsWarn, tracker.js) */
.jc-why { font-size: 11.5px; color: var(--muted); line-height: 1.4; }
.jc-why--warn { color: var(--warn-ink); font-weight: 600; }
.jc-foot { display: flex; align-items: center; gap: 8px; margin-top: 2px; }
.jc-stuck-chip { display: inline-flex; align-items: center; height: 22px; padding: 0 9px; border-radius: 999px; background: var(--danger-soft); color: var(--danger-ink); font-size: 11px; font-weight: 700; }
.jc-upload-btn {
  width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--border); background: var(--panel);
  color: var(--muted); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.jc-upload-btn:hover { background: var(--hover); color: var(--text); border-color: var(--border-strong); }
@media (max-width: 640px) { .jc-upload-btn, .jc-stuck-chip { min-height: 44px; min-width: 44px; } }

/* expanded — the per-service table + note + status row + open-job link */
.jcard--expanded { box-shadow: 0 0 0 2px var(--accent-soft); }
.jc-expand { border-top: 1px solid var(--border); margin-top: 6px; padding-top: 10px; display: flex; flex-direction: column; gap: 10px; cursor: default; }
.jc-svctable { width: 100%; border-collapse: collapse; font-size: 12px; }
.jc-svctable td { padding: 5px 6px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.jc-svcrow-name { font-weight: 600; white-space: nowrap; }
.jc-svcrow-what { color: var(--muted); font-size: 11.5px; }
.jc-svcrow-sel { height: 28px; font-size: 11.5px; }
.jc-note { font-size: 12px; color: var(--muted); font-style: italic; background: var(--subtle); border-radius: var(--radius); padding: 6px 10px; }
.jc-statusrow { display: flex; align-items: center; gap: 8px; }
.jc-statusrow label { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.jc-openjob { align-self: flex-start; }

/* release-gate exception flags (jcExceptionFlags, tracker.js) — same classes
   the retired kanban card used; scope now includes .jcard so they render
   there too (the OLD .jt-board .kanban-card rule above stays, harmless, for
   any surface that still carries a literal .kanban-card). */
.jt-board .jcard .kc-flags { display: inline-flex; align-items: center; gap: 6px; margin-top: 2px; flex-wrap: wrap; }
.jt-board .jcard .kc-flags:empty { display: none; }
.jt-board .jcard .kc-fact { display: inline-flex; align-items: center; white-space: nowrap; font-size: 11px; color: var(--warn-ink); }

/* -------- List (the new default view, spec §4.1) -------- */
.so-joblist-items { display: flex; flex-direction: column; gap: 8px; }
.so-complete-fold { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 10px; }
.so-complete-fold > summary { cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--muted); list-style: none; padding: 4px 0; }
.so-complete-fold > summary::-webkit-details-marker { display: none; }
.so-complete-fold > summary::before { content: '▸ '; }
.so-complete-fold[open] > summary::before { content: '▾ '; }
.so-complete-fold .so-joblist-items { margin-top: 8px; }

/* -------- Board — six App.JOB_STAGES columns, no drag (spec §4.3) -------- */
.so-jobboard { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(220px, 1fr); gap: 12px; overflow-x: auto; padding-bottom: 8px; align-items: start; }
.so-jobboard-col { background: var(--subtle); border-radius: var(--radius-lg); padding: 10px; display: flex; flex-direction: column; gap: 8px; min-width: 220px; }
.so-jobboard-colhead { display: flex; align-items: center; gap: 6px; padding: 2px 2px 6px; border-bottom: 2px solid color-mix(in srgb, var(--stc) 45%, transparent); font-size: 12px; font-weight: 700; }
.so-jobboard-coldot { width: 8px; height: 8px; border-radius: 50%; background: var(--stc); flex: 0 0 auto; }
.so-jobboard-colname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.so-jobboard-colcount { color: var(--muted); font-weight: 600; }
.so-jobboard-cards { display: flex; flex-direction: column; gap: 8px; }
.so-jobboard-more { align-self: stretch; }

/* -------- phone (≤640px, spec §4.7): List cards stack; Board scrolls
   horizontally with snap; every target stays ≥44px -------- */
@media (max-width: 640px) {
  .jcard--compact { flex-wrap: wrap; overflow-x: visible; }
  .jcard--compact .jc-addr { max-width: 100%; flex: 1 1 100%; }
  .jc-header { flex-wrap: wrap; }
  .jc-poster { width: 100%; height: 96px; }
  .so-jobboard { scroll-snap-type: x mandatory; }
  .so-jobboard-col { scroll-snap-align: start; min-width: 84vw; }
  .so-next, .jc-openjob, .jc-svcrow-sel { min-height: 44px; }
}

/* -------- dark theme overrides -------- */
html[data-theme="dark"] .so-svc { background: var(--panel); }
html[data-theme="dark"] .so-svc--todo { background: color-mix(in srgb, var(--hover) 70%, var(--panel)); color: #e4e7ec; }
html[data-theme="dark"] .so-svc--todo .so-svc-status { color: #cbd5e1; }
html[data-theme="dark"] .jc-poster.so-poster--empty { background-image: repeating-linear-gradient(45deg, var(--subtle) 0 8px, var(--panel) 8px 16px); }
html[data-theme="dark"] .so-jobboard-col { background: color-mix(in srgb, var(--panel) 60%, #000); }
/* JUDGE F — the Normal list card reads as ONE row on a desktop — ONLY inside the Jobs list (.so-joblist); in a narrow column such as the client profile it stays stacked (JUDGE H: it overflowed into the Branding card) (address · shooter/date · pills · stage/next), per canvas board 2. */
.so-jobs-quickrow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.so-jobs-quickrow .so-jobs-quick { flex: 1; min-width: 0; }
.jc-stuck-chip { align-self: flex-start; }
.so-complete-more { margin-top: 8px; align-self: flex-start; }
@media (min-width: 1024px) {
  .so-joblist .jcard--normal:not(.jcard--expanded) { padding: 10px 14px; }
  .so-joblist .jcard--normal .jc-header { align-items: center; }
  .so-joblist .jcard--normal .jc-poster { width: 56px; height: 42px; }
  .so-joblist .jcard--normal .jc-headinfo { display: grid; grid-template-columns: minmax(220px, 300px) 170px minmax(0, 1fr) auto; column-gap: 16px; row-gap: 2px; align-items: center; }
  .so-joblist .jcard--normal .jc-title { grid-column: 1; grid-row: 1; margin: 0; }
  .so-joblist .jcard--normal .jc-meta { grid-column: 1; grid-row: 2; }
  .so-joblist .jcard--normal .kc-flags { grid-column: 1; grid-row: 3; }
  .so-joblist .jcard--normal .jc-shootrow { grid-column: 2; grid-row: 1 / span 2; }
  .so-joblist .jcard--normal .jc-pills { grid-column: 3; grid-row: 1; }
  .so-joblist .jcard--normal .jc-why { grid-column: 3; grid-row: 2; }
  .so-joblist .jcard--normal .jc-foot { grid-column: 4; grid-row: 1 / span 2; margin-top: 0; justify-content: flex-end; }
}
/* STUDIO-OS F END */

/* STUDIO-OS G BEGIN — SPEC-STUDIO-OS-G-2026-09-17. Upload-anywhere sheet/veil/
   tray, the service-chat section on the Job page, the tag-only job-card gate
   (no new class there — chat.js reuses .chat-jobcard verbatim) and the
   one-click consult popover. Tokens only; dark theme via the existing
   --panel/--border/--text/--muted vars + color-mix, same as every other
   STUDIO-OS block. */

/* -------- drop-anywhere veil (§1.1b) -------- */
.so-upl-veil { position: fixed; inset: 0; z-index: 4000; background: color-mix(in srgb, var(--accent) 10%, rgba(16,24,40,.55)); display: flex; align-items: center; justify-content: center; pointer-events: none; }
.so-upl-veil-msg { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px 40px; border: 2px dashed #fff; border-radius: 16px; color: #fff; font-size: 18px; font-weight: 700; }

/* -------- the sheet (§1.2) -------- */
.so-upl-sheet .slideover-body { display: flex; flex-direction: column; gap: 14px; }
.so-upl-drop { border: 1.5px dashed var(--accent); background: var(--accent-soft); border-radius: 12px; color: var(--accent-hover); display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; text-align: center; min-height: 110px; padding: 12px; cursor: pointer; }
.so-upl-drop:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.so-upl-drop--over { background: color-mix(in srgb, var(--accent) 18%, var(--accent-soft)); }
.so-upl-drop-main { font-weight: 700; font-size: 13.5px; }
.so-upl-drop-sub { font-size: 12px; }
.so-upl-count-empty { font-size: 12px; }
.so-upl-cnt-total { font-weight: 700; font-size: 13px; }
.so-upl-cnt-groups { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.so-upl-cnt-chip { display: inline-flex; align-items: center; height: 24px; padding: 0 9px; border-radius: 999px; border: 1px solid var(--border); background: var(--subtle); font-size: 11.5px; font-weight: 600; }
.so-upl-label { font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); margin-bottom: 4px; }
.so-upl-section { display: flex; flex-direction: column; }
.so-upl-row { display: flex; gap: 20px; flex-wrap: wrap; }
.so-upl-jobsearch { width: 100%; margin-bottom: 6px; }
.so-upl-joblist { display: flex; flex-direction: column; gap: 6px; max-height: 220px; overflow: auto; }
.so-upl-pick { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); text-align: left; width: 100%; cursor: pointer; }
.so-upl-pick:hover { background: var(--hover); }
.so-upl-pick-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; font-size: 12.5px; }
.so-upl-jobpicked { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--subtle); }
.so-upl-nojobs { font-size: 12px; padding: 6px 2px; }
.so-upl-svcs { display: flex; flex-wrap: wrap; gap: 6px; }
.so-upl-svcs[hidden] { display: none; }
.so-upl-foot { display: flex; justify-content: flex-end; width: 100%; }

/* -------- topbar Upload door (§1.1a) -------- */
#upload-anywhere-btn { display: inline-flex; align-items: center; gap: 6px; }
#mobile-topbar .mt-upload { width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border: 0; background: transparent; color: var(--text); flex-shrink: 0; }

/* -------- one-click consult popover (§4) -------- */
.so-consult-pop { display: flex; flex-direction: column; gap: 4px; padding: 4px; min-width: 170px; }
.so-consult-pop button { text-align: left; }

/* -------- Job page service section (§2) -------- */
.jp-svc-section-head { display: flex; flex-direction: column; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.jp-svc-section-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.jp-svc-received { flex: 1; min-width: 0; font-size: 12px; }
.jp-svc-chat-btn { position: relative; width: 26px; height: 26px; border-radius: 7px; border: 1px solid var(--border); background: var(--panel); color: var(--muted); display: inline-flex; align-items: center; justify-content: center; margin-right: 4px; flex: 0 0 auto; }
.jp-svc-chat-btn:hover { background: var(--hover); color: var(--text); }
.jp-svc-chat-dot { position: absolute; top: -2px; right: -2px; width: 8px; height: 8px; border-radius: 50%; background: var(--danger, #d92d20); border: 1.5px solid var(--panel); }
@media (max-width: 640px) { .jp-svc-chat-btn { width: 40px; height: 40px; } }

/* -------- whole-job thread's collapsed service-thread line (§2) -------- */
.jt-svcgroup { display: flex; align-items: center; gap: 8px; margin: 4px 12px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--subtle); font-size: 12.5px; font-weight: 600; color: var(--text); width: calc(100% - 24px); text-align: left; cursor: pointer; }
.jt-svcgroup:hover { background: var(--hover); }
.jt-svcgroup-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.jt-svcgroup-arrow { color: var(--muted); }

/* -------- expanded job card's per-service received line (App.jobCard, tracker.js) -------- */
.jc-svcrow-received { font-size: 11px; }

/* the "Tagged you on <Service>" title (renderServiceTagCard, chat.js §3) reuses
   .chat-jobcard-addr's weight/size but must WRAP — a service name makes this
   line longer than the short street addresses that class was sized for, and a
   hard ellipsis there ("Tagg…") defeats the one thing this card exists to say. */
.chat-jobcard-tagtitle { white-space: normal; overflow: visible; text-overflow: clip; }

/* -------- dark theme -------- */
html[data-theme="dark"] .so-upl-cnt-chip { background: color-mix(in srgb, var(--hover) 70%, var(--panel)); }
html[data-theme="dark"] .so-upl-jobpicked { background: color-mix(in srgb, var(--panel) 60%, #000); }
html[data-theme="dark"] .jt-svcgroup { background: color-mix(in srgb, var(--panel) 60%, #000); }
/* JUDGE G — "super duper easy to upload": the topbar door says Upload in words, not a bare arrow. */
#topheader #upload-anywhere-btn { width: auto; gap: 6px; padding: 0 12px; border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent); font-weight: 600; border-radius: 9px; }
#topheader #upload-anywhere-btn span { display: inline; max-width: none; font-size: 13px; }
/* STUDIO-OS G END */
/* STUDIO-OS H BEGIN — client profile (clients.js openDetail): tabs, Overview
   tiles, referral table. Tokens only — --panel/--border/--text/--muted/
   --accent — so light and dark themes both work without a second rule set.
   Ported from the canvas (studio.css .tabs/.tile/.pref, studio2.css .kv/.sw)
   under so-h- prefixed names, per SPEC-STUDIO-OS-H-2026-09-17 design source. */
.so-h-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border); flex-wrap: wrap; margin-bottom: 14px; }
.so-h-tabs button { border: 0; background: transparent; height: 38px; padding: 0 12px; font-size: 13.5px; font-weight: 500; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.so-h-tabs button.active { color: var(--text); font-weight: 600; border-bottom-color: var(--accent); }
.so-h-tab-n { font-size: 11px; color: var(--muted); background: var(--hover); padding: 1px 6px; border-radius: 999px; }
.so-h-tiles { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; margin-bottom: 14px; }
.so-h-tile { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px; }
.so-h-tile-label { font-size: 11px; color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
.so-h-tile-big { font-size: 22px; font-weight: 800; line-height: 1.15; margin-top: 4px; letter-spacing: -.01em; }
.so-h-tile small { color: var(--muted); font-size: 11.5px; }
.so-h-cols { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 14px; }
.so-h-modal .chip.is-on { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); font-weight: 700; }
@media (max-width: 900px) { .so-h-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } .so-h-cols { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .so-h-tiles { grid-template-columns: 1fr; } .so-h-tabs { overflow-x: auto; flex-wrap: nowrap; } }
/* JUDGE H — the profile is a wide canvas on a desktop */
@media (min-width: 1100px) { .modal.so-h-modal { width: min(1180px, 94vw); max-width: 94vw; } }
/* STUDIO-OS H END */
/* STUDIO-OS I BEGIN — SPEC-STUDIO-OS-I-2026-09-17. "My work" group headers +
   the "Yours:" line, the clickable film pill, the Board's drag state + the
   Delivered/Care-call popover. Tokens only — --panel/--border/--text/--muted/
   --accent/--warn-ink, color-mix for tints — same as every prior STUDIO-OS
   block, so dark mode (html[data-theme="dark"] below) works for free. */

/* -------- §1.2 — My work: group headers + the "Yours:" line -------- */
.so-mywork-head {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); margin: 16px 0 6px; padding-bottom: 4px; border-bottom: 1px solid var(--border);
}
.so-mywork-head:first-child { margin-top: 2px; }
.jc-yours {
  font-size: 11.5px; font-weight: 600; color: var(--accent-hover);
  background: var(--accent-soft); border-radius: 6px; padding: 3px 8px; align-self: flex-start;
}
.jc-yours strong { font-weight: 700; }
.jc-yours--compact { flex: 0 0 auto; padding: 2px 7px; }

/* -------- §2.2 — the film pill: a clickable .so-stage that opens the cut -------- */
.jc-film-pill { cursor: pointer; }
.jc-film-pill:hover, .jc-film-pill:focus-visible { filter: brightness(0.97); outline: 2px solid color-mix(in srgb, var(--stc) 50%, transparent); outline-offset: 1px; }
.jc-svcrow-next { height: 26px; padding: 0 10px; font-size: 11.5px; }

/* -------- §4 — Board: draggable cards + column drop highlight -------- */
.jcard--board[draggable="true"] { cursor: grab; }
.jcard--board.dragging { opacity: .45; cursor: grabbing; }
.so-jobboard-col.drag-over { outline: 2px dashed var(--accent); outline-offset: -2px; background: color-mix(in srgb, var(--accent) 8%, var(--subtle)); }

/* -------- §4.4/§4.5 — the Delivered/Care-call popover -------- */
.so-deliver-pop { display: flex; flex-direction: column; gap: 6px; padding: 4px; min-width: 220px; }
.so-deliver-pop-row { display: flex; align-items: center; gap: 8px; font-size: 12.5px; cursor: pointer; }
.so-deliver-pop-foot { font-size: 11px; color: var(--muted); margin-top: 2px; }

/* -------- dark theme -------- */
html[data-theme="dark"] .jc-yours { background: color-mix(in srgb, var(--accent) 22%, var(--panel)); }
html[data-theme="dark"] .so-jobboard-col.drag-over { background: color-mix(in srgb, var(--accent) 14%, var(--panel)); }
/* JUDGE I — film pills are clickable: say so quietly. */
.jc-film-pill { cursor: pointer; }
.jc-film-pill:hover, .jc-film-pill:focus-visible { border-color: var(--stc); outline: none; box-shadow: 0 0 0 2px color-mix(in srgb, var(--stc) 22%, transparent); }
/* JUDGE I — v51 bug: .help-btn stacks icon over label (column), so the topbar Upload label was clipped. One row. */
#topheader #upload-anywhere-btn { display: inline-flex; flex-direction: row; align-items: center; height: 32px; white-space: nowrap; }
/* STUDIO-OS I END */

/* STUDIO-OS PHONE BEGIN — PHONE-OPERABLE 09-17: the shared header replaced .view-header, so the
   earlier phone stacking rule no longer reached Jobs' mode controls. */
@media (max-width: 700px) {
  .so-pagehead { flex-direction: column; align-items: stretch; gap: 12px; }
  .so-pagehead > div { min-width: 0; max-width: 100%; }
  .so-pagehead .so-pagehead-right { margin-left: 0; width: 100%; }
  .so-pagehead .jt-header-btns { flex-wrap: wrap; min-width: 0; max-width: 100%; }
  .so-pagehead .seg { flex-wrap: wrap; max-width: 100%; }
  .so-jobs-quickrow { align-items: stretch; }
  .so-jobs-quickrow .so-jobs-quick { flex-basis: 100%; }
  .so-jobs-quick button, .so-pagehead button { min-height: 44px; }
  .jt-openchat, .jp-svc-chat-btn { min-width: 44px; min-height: 44px; }
  .chat-rail-organize-btn { min-width: 44px; min-height: 44px; }
  .jp-status-pill, .vpr-wrap button, .vpr-views--lens .vpr-view-btn { min-height: 44px; }
}
@media (max-width: 700px) {
  .grow-play-head { flex-wrap: wrap; }
  .grow-earn { max-width: 100%; white-space: normal; overflow-wrap: anywhere; }
  .inc-legend-row { flex-wrap: wrap; }
  .inc-legend-pts { flex-basis: 100%; white-space: normal; overflow-wrap: anywhere; padding-left: 32px; }
  .metric-volume-chart { overflow-x: auto; padding-bottom: 8px; }
  .friday-dark { margin: 0; min-width: 0; width: 100%; }
  .collapse-head { min-height: 44px; padding-left: 4px; padding-right: 4px; box-sizing: border-box; }
  .range-preset, .fri-more { min-height: 44px; }
  .range-presets { flex-wrap: wrap; border-radius: 16px; }
  .tm-matrix { min-width: var(--tm-phone-width, 560px); }
  .tm-cell { min-width: 44px; height: 44px; }
  button.icon-btn, button.jarvis-chip, button.chip, button.chip-btn,
  button.inbox-chip, button.review-fchip, button.pp-act, button.pl-tab,
  button.sales-tab, button.wizard-pill, button.copy-btn, button.fd-askanywhere,
  button.fd-seg-pill, button.guide-rf-btn, button.sec-caret, button.th-sort {
    min-height: 44px;
  }
  button.icon-btn, button.sec-caret { min-width: 44px; }
  .reports-card { min-width: 0; overflow-x: auto; }
  /* CALL-SITE FIX 2026-09-18: insights.js registers 'insights' as a REDIRECT-ONLY stub —
     it bounces to Snapshot, so body[data-view="insights"] never survives a real navigation
     and Trends' bars actually render under [data-view="dashboard"]. Both are listed. */
  body:is([data-view="insights"], [data-view="dashboard"]) .bar-chart { overflow-x: auto; padding-bottom: 8px; }
  .lead-followup-row { flex-wrap: wrap; }
  .soc-grid-2 { grid-template-columns: minmax(0, 1fr); }
  .soc-trend-row { flex-wrap: wrap; }
  .soc-trend-row .spark { max-width: 100%; }
  .soc-trend-row .spark svg { max-width: 100%; }
  .jf-field, .jf-field .input { min-width: 0; max-width: 100%; }
}
/* STUDIO-OS PHONE END */

/* STUDIO-OS WIDE BEGIN — let operational tables/boards use larger monitors while
   keeping reading-oriented pages and forms at their existing comfortable cap.
   CALL-SITE FIX 2026-09-18: for an ADMIN, App.navigate rewrites a child view id to
   its PARENT tab BEFORE writing document.body.dataset.view (core.js CHILD_PARENT),
   so [data-view="tracker"], "clients", "calendar" and "reportshub" never matched in
   the real app — measured on the running Hub, Jobs at 1920px stayed 1340px wide.
   "video" is rewritten to "postreview" by RETIRED_PARENT_REMAP and never appears at
   all. The child ids are KEPT because crew reach those views standalone; the parent
   ids are what an admin actually carries. "jobs" and "plan" widen whole (every
   segment is a board); "clienthub" and "money" also hold prose pages, so those two
   are matched by the ACTIVE SEGMENT instead — in its own rule, because one
   :has()-less browser would otherwise drop the whole list. */
@media (min-width: 1600px) {
  body:is([data-view="jobs"], [data-view="job"], [data-view="plan"],
    [data-view="postreview"], [data-view="metrics"], [data-view="insights"],
    [data-view="tracker"], [data-view="calendar"], [data-view="clients"],
    [data-view="video"], [data-view="reportshub"]) #view {
    max-width: clamp(1340px, 85vw, 1840px);
  }
  body:is([data-view="clienthub"], [data-view="money"]) #view:has(> .subtabs > .subtab.active:is([data-seg="clients"], [data-seg="reportshub"])) {
    max-width: clamp(1340px, 85vw, 1840px);
  }
}
@media (min-width: 701px) and (max-width: 860px) {
  .friday-dark { margin: 0; min-width: 0; width: 100%; }
}
/* STUDIO-OS WIDE END */
