/* ==========================================================================
   OSCAR Booking — patient booking surface
   Register: Stripe / Linear / Apple. Legible, neutral, branded not decorated.
   Light is the default for every viewer, including system-dark.
   Dark applies only when the reader explicitly stamps data-theme="dark".
   ========================================================================== */

:root {
  --ground: #ffffff;
  --surface: #f7f8fa;
  --surface-2: #eef0f4;
  --ink: #10131a;
  --ink-soft: #545a67;
  --ink-faint: #878e9c;
  --line: #e4e7ec;
  --line-strong: #cbd1da;
  --accent: #0f7355;
  --accent-soft: #edf5f2;
  --accent-ink: #0b5741;
  --on-accent: #ffffff;
  /* Text colour for anything painted on --accent-soft (2026-09-01, dark-mode
     hover/selected fix). Paired to the TINT, not the accent hue like
     --accent-ink is — public/app.js's applyClinicLook() sets this inline
     alongside --accent-soft, computed from --accent-soft's own relative
     luminance, so it stays legible no matter which theme (light/dark) the
     clinic's tint happens to read as. Default here just inherits --ink,
     which already flips correctly for the two built-in themes below. */
  --accent-soft-ink: var(--ink);
  --warn: #8a5300;
  --warn-soft: #fdf5e6;
  --danger: #a4231a;
  --danger-soft: #fdf1f0;
  --band-limited: #f0b100;
  --band-full: #e5261f;
  --shadow: 0 1px 2px rgba(16, 19, 26, 0.05);
  --shadow-lift: 0 1px 3px rgba(16, 19, 26, 0.08), 0 6px 16px rgba(16, 19, 26, 0.06);
  --radius: 8px;
  --radius-sm: 6px;
  /* Corner "shape" (patient booking page only, per-clinic `look.corners`,
     2026-08-31) — cards and buttons/inputs are two distinct radius tiers so
     `pill` can round controls all the way (999px) without turning a card
     into a stadium shape. Default = today's actual values on every existing
     selector, so an untouched clinic is byte-identical. */
  --radius-control: var(--radius-sm);
  --radius-card: var(--radius);
  /* Body typeface (patient booking page only, per-clinic `look.font`,
     2026-08-31). `--font-control` is what buttons/inputs actually render in
     — it inherits `--font-body` by default (today's behavior: one typeface
     everywhere) and only splits from it in serif mode (see the `data-font`
     overrides below), where controls stay on a plain system-UI face. */
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  --font-control: var(--font-body);
}

/* Per-clinic look overrides (2026-08-31) — set via `data-font`/`data-corners`
   on <html> by public/app.js's `applyClinicLook()`, driven by `GET
   /api/clinic`'s `look.font`/`look.corners` (schema.md `settings.look`).
   Absent/empty `look` means neither attribute is ever set, so these blocks
   never match and every existing clinic renders identically to before this
   feature. */
:root[data-font='serif'] {
  --font-body: Georgia, 'Times New Roman', serif;
  --font-control: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
}
:root[data-font='rounded'] {
  --font-body: ui-rounded, 'SF Pro Rounded', 'Nunito', system-ui, sans-serif;
}
:root[data-corners='square'] {
  --radius: 4px;
  --radius-sm: 4px;
  --radius-control: 4px;
  --radius-card: 4px;
}
:root[data-corners='pill'] {
  --radius-control: 999px;
  --radius-card: 16px;
}

:root[data-theme='dark'] {
  --ground: #0c0e13;
  --surface: #14171e;
  --surface-2: #1c2029;
  --ink: #eceef2;
  --ink-soft: #9ba3b0;
  --ink-faint: #6b7280;
  --line: #232833;
  --line-strong: #333a47;
  --accent: #2fa07c;
  --accent-soft: #14251f;
  --accent-ink: #6fc9a8;
  --on-accent: #06120e;
  --accent-soft-ink: var(--ink);
  --warn: #d9a441;
  --warn-soft: #241c0c;
  --danger: #e0685e;
  --danger-soft: #241413;
  --band-limited: #fbbf24;
  --band-full: #ff4d42;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  --shadow-lift: 0 1px 3px rgba(0, 0, 0, 0.6), 0 6px 16px rgba(0, 0, 0, 0.45);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

/* Load-bearing (2026-08-27, measured live): the UA stylesheet only hides
   [hidden] via `[hidden] { display: none }`, and ANY component rule that
   sets `display` on that same element — even one with lower specificity,
   even one written for the visible state — silently outranks the UA rule
   the instant both apply. `.pf-fill { display: block }` did exactly this:
   `$('pf-fill-code').hidden = true` correctly set the attribute, and the
   element rendered anyway with no click handler attached (app.js only
   wires the handler when a demoCode exists), because the UA's `[hidden]`
   rule and `.pf-fill`'s `display:block` have equal specificity and the
   component rule won on source order. `!important` here is the fix, not a
   style crutch — it makes `hidden` an ABSOLUTE property no later
   `display` declaration can defeat, for every element in this app, not
   just the one that got caught. Never remove this to "simplify" a single
   component's CSS. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { margin: 0; text-wrap: balance; letter-spacing: -0.021em; }
p { margin: 0; }
button { font: inherit; font-family: var(--font-control); color: inherit; }

/* ------------------------------------------------------------------ shell */

.shell {
  max-width: 620px;
  margin: 0 auto;
  /* Flow layout, not a viewport-height frame (2026-09-01 fix): the footer
     sits directly under whatever content the current step actually has, so
     a short step (e.g. a small practitioner list) never leaves a void
     between the content and a footer that used to be pinned to the bottom
     of the viewport regardless of content length. */
  display: flex;
  flex-direction: column;
  background: var(--ground);
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--ground);
  border-bottom: 1px solid var(--line);
  padding: 15px 24px;
  display: flex;
  align-items: center;
  gap: 12px;
}

.brand {
  font-weight: 600;
  font-size: 15px;
  letter-spacing: -0.015em;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* 2026-09-01 fix: a long clinic name ellipsised in the slim top bar at
   narrow widths ("Fit for Life Wellness & Rehab Ce…") — the name never
   truncates, it wraps to two lines and the bar grows to fit instead. */
@media (max-width: 600px) {
  .brand {
    overflow: visible;
    text-overflow: clip;
    white-space: normal;
    line-height: 1.25;
  }
  .topbar { padding-top: 12px; padding-bottom: 12px; }
  .steps { top: 72px; }
}

.backbtn {
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 6px;
  margin: -6px 0 -6px -8px;
  border-radius: var(--radius-control);
  color: var(--ink-soft);
  display: none;
  line-height: 0;
}
.backbtn.on { display: inline-flex; }
.backbtn:hover { background: var(--surface-2); color: var(--ink); }
.backbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.themetoggle {
  border: 1px solid var(--line);
  background: var(--ground);
  color: var(--ink-soft);
  cursor: pointer;
  height: 30px;
  padding: 0 10px;
  border-radius: var(--radius-control);
  font-size: 12px;
  font-weight: 500;
  flex: none;
}
.themetoggle:hover { background: var(--surface-2); color: var(--ink); }
.themetoggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ------------------------------------------------------------- progress */

.steps {
  display: flex;
  gap: 4px;
  padding: 0 24px 14px;
  background: var(--ground);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 54px;
  z-index: 19;
}
.steps .bar {
  height: 2px;
  flex: 1;
  background: var(--line);
}
.steps .bar.done { background: var(--accent); }

/* ---------------------------------------------------------------- panes */

main { flex: 1; padding: 22px 24px 32px; }

.pane { display: none; }
.pane.on { display: block; }

.eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 8px;
}
.title { font-size: 26px; font-weight: 640; margin-bottom: 6px; line-height: 1.2; }
.sub { color: var(--ink-soft); font-size: 14px; margin-bottom: 28px; max-width: 46ch; }

/* ------------------------------------------------------------- services */

.group-label {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 28px 0 10px;
}
.group-label:first-of-type { margin-top: 0; }

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

/* Practitioner grid — the entry, step 1 of the funnel (2026-08-27). Row
   cards (avatar-left, text-right) instead of stacked photo-on-top tiles:
   with no headshot files existing for any demo tenant, a full-width square
   image slot was dead weight that made a 17-practitioner clinic
   (?clinic=demo) a long scroll of near-empty tiles. A row card's height is
   driven by two short text lines, not a reserved photo box, so it scans at
   speed with or without a future photo (object-fit: cover keeps a real
   photo looking correct in the same 44px slot).

   Fixed 2026-08-27 (measured live, 1440x900): the previous breakout
   (position:relative; left:50%; translateX(-50%)) recentered the grid on
   the FULL VIEWPORT, not on the .shell/main content column the heading,
   step label, and simband live in — main measured left:411 right:1029
   width:618 while the grid measured left:110 right:1330 width:1220, a
   ~300px overhang on both sides that made the cards visually disagree
   with the text introducing them. Fix stays inside the existing 618px
   column (no breakout, no width/position override) and adds columns by
   narrowing cards instead of widening the grid — still clears 17
   practitioners well inside one screen at every width.

   Fixed-geometry rebuild (2026-08-31, Chris rejecting the row-card build:
   "i dont like the spacing here on this page with the cards and the
   services they offer. its not unison across the board with a fixed
   locaion for profile pic and name and service"). The row-card's
   `align-items: center` vertically centred the 44px avatar against
   whatever the 2-line name box's actual content height was — a 1-line
   name centred the photo higher than a 2-line name did, and the role
   line inherited that same drift underneath it. Every element now sits
   on an explicit grid slot instead of being centred against its sibling:
   photo fixed at `align-self: start` in column 1, name/role stacked in
   column 2 with their own reserved 2-line boxes (unchanged from the prior
   pass — see the comment on `.prac-name` below). Because every slot's
   height is now fixed by construction (not by content), every card in
   the grid lands on the identical total height without needing a
   hard-coded row px that would fight the clinic's own font-size overrides
   (`--font-serif`/`--font-rounded` swap family only, never size). Column
   breakpoints: 1-up below 640, 2-up >=640 — all inside the same 618px
   `.shell` frame (unchanged; see the desktop-framed-card comment further
   down), so the per-card width narrows as columns add, never the frame.

   MEASURED, not assumed (agent-browser, 1440x900, fit-for-life with two
   providers carrying real long titles set via the settings overlay):
   a THIRD column was tried first per the original 3-up->=1100 spec, and
   at that width each card's text column is ~93px (572px content width /
   3 cards, minus 24px card padding and the 56+12px avatar+gap). Even the
   SHORTER required string, "Registered Massage Therapist" (Alex Kidd),
   clipped after "Registered / Massage..." — 1.5 lines of content, not 2 —
   and the longer "Registered Massage Therapist & Acupuncturist" (Rui
   Tian) clipped the same way. The 620px `.shell` frame this app commits
   to elsewhere (see the desktop-framed-card comment) makes a 3-up text
   column too narrow for these titles at ANY viewport, so there is no
   min-width where 3-up both fits the frame and clears real title
   lengths — the fix is capping at 2 columns, never a wider breakpoint
   trigger. 2 columns gives ~190px per text column, which clears both
   strings in 2 lines (re-measured, same method, both cards). */
.prac-grid { display: grid; grid-template-columns: 1fr; gap: 8px; align-items: stretch; }
@media (min-width: 640px) {
  .prac-grid { grid-template-columns: repeat(2, 1fr); }
}
/* Desktop columns (2026-08-31, `.shell` widens to 1120px at >=1024 — see
   the desktop section at the bottom of this file). 3-up at >=1024, 4-up at
   >=1360, both still inside the same 1120px `.shell` cap (the shell does
   NOT grow past 1360 — the extra viewport just gives 4 narrower cards the
   same total 1120px frame). `minmax(240px, 1fr)` is a measured floor, not
   a guess: at >=1360 with the 1120px cap, 4 columns average ~262px/card
   (~170px text column after the 56px avatar+12px gap+24px padding), which
   is tighter than the 2-column ~190px text column that was the original
   fixed-geometry measurement — MEASURED via agent-browser against
   fit-for-life's two longest real titles ("Registered Massage Therapist &
   Osteopathic Manual Practitioner" et al.) before shipping; the floor
   stops a card from shrinking past what 2-line clamp needs if a future
   clinic's title runs longer than today's fixtures. */
@media (min-width: 1024px) {
  .prac-grid { grid-template-columns: repeat(3, minmax(240px, 1fr)); }
}
@media (min-width: 1360px) {
  .prac-grid { grid-template-columns: repeat(4, minmax(240px, 1fr)); }
}

.prac {
  display: grid;
  grid-template-columns: 56px 1fr;
  column-gap: 12px;
  align-items: start;
  text-align: left;
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--ground);
  cursor: pointer;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.prac:hover { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-soft-ink); }
.prac:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.prac-avatar {
  width: 56px;
  height: 56px;
  align-self: start;
  border-radius: var(--radius-card);
  object-fit: cover;
  background: var(--surface-2);
  display: block;
}
.prac-meta { min-width: 0; width: 100%; align-self: start; }
/* Every card the SAME height regardless of name length or a long combined
   role string ("RMT & Osteopathic Manual Practitioner" — Firouzeh Nassiri,
   fit-for-life). `min-height` reserves the full 2-line box for both name
   and role whether or not the actual text wraps that far, so a
   1-line-name/1-line-role card and a 2-line-name/2-line-role card commit
   to the identical reserved height — text past 2 lines clips with an
   ellipsis, full string always available via the element's `title` attr
   (set in public/app.js). A 1-line name leaves its own second line empty
   rather than growing/shrinking, which is what pins the role slot below
   it to the same y-position on every card (the fixed-geometry ask). */
/* 2026-08-31 (Chris: "i dont like the whitespace between the dr name and
   their title"). The 2-line reserved name box left a full empty line
   above the role whenever a name was one line — the name is now a
   SINGLE line (ellipsis + `title` attr carries the full string for
   anything that clips), sitting a fixed 3px gap above the role. The role
   keeps its own fixed 2-line box unchanged, so a 1-line role's leftover
   whitespace lands at the BOTTOM of the card, never between name and
   role. Cards stay equal height across the grid: name's line-height is
   now fixed (single line, not a reserved 2-line box), and the role box
   was already fixed — nothing content-driven is left in the meta column. */
.prac-name {
  font-weight: 600;
  font-size: 14.5px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.prac-disc {
  font-size: 12.5px;
  line-height: 1.3;
  color: var(--ink-soft);
  margin-top: 3px;
}

/* Practitioner profile — identity block (avatar next to name/discipline),
   an OPTIONAL media-only cover band above it, bio, then services (or the
   degrade browse into the pre-existing discipline tiles below).

   Rebuilt 2026-09-01 (Chris, looking at a flat grey box with the clinic's
   name in it: "every page keeps getting messed up and out of whack. I
   want the 'fit for life header' to be a video / placeholder if we dont
   have the video"). Every profile now renders the SAME banner-shaped band
   (`.prof-banner`) regardless of what fills it — video, image, or a
   designed placeholder, resolved by `headerBandNode()` in app.js. This
   retires the old split between a click-to-play `.prof-cover` media band
   and a name-beside-avatar `.prof-heading` for providers without media —
   that split is exactly what let the flat "clinic name in a box"
   typographic fallback exist; deleting the split deletes the fallback.

   `.has-cover` on #pane-service hides the pane's own eyebrow/H1/sub — the
   heading block below renders the SAME name/discipline itself (with or
   without a cover band above it), so leaving the static title visible
   would double the identity AND its reserved margins would eat into the
   fit-to-screen budget even emptied to ''. */
.has-cover .eyebrow,
.has-cover #t-service,
.has-cover #service-sub {
  display: none;
}

/* The band breaks out of main's 24px SIDE padding only — symmetric,
   self-contained inside main's own box (never viewport-relative). This is
   deliberately NOT the 100vw/left:50%/translateX(-50%) trick: that one
   recentered on the full viewport instead of the .shell content column
   and overflowed asymmetrically (see the .prac-grid comment above — the
   bug Chris caught once already on this same funnel). A margin/width
   pair scoped to main's own padding can never disagree with anything
   outside main, so nothing below the band is at risk of the same defect.
   The -22px top margin only cancels main's own 22px top padding — it's
   neutralized below when the "simulated demonstration" `.simband` is
   prepended ahead of `#pane-service`, so the band respects simband's own
   margin instead of reaching past it for main's edge. */
.prof-name-wrap { min-width: 0; }
.prof-name { font-size: 21px; font-weight: 640; letter-spacing: -0.018em; line-height: 1.2; }
.prof-disc { font-size: 13px; color: var(--ink-soft); margin-top: 3px; }

.prof-bio { color: var(--ink-soft); font-size: 14px; line-height: 1.5; max-width: 52ch; margin: 0 0 18px; }

/* Header band (rebuilt 2026-09-01, `headerBandNode()` in app.js) — video,
   image, or designed placeholder all render into this same shape.
   `overflow: visible` so the headshot can overlap the band's own bottom
   edge instead of sitting inside it. 16:9 is the mobile ratio. On the
   single-column desktop layout the band now spans the FULL shell width
   (up to ~1120px), so 16:9 would run ~630px tall and push the name below
   the fold (Chris, 2026-09-01, looking at the 1440px screenshot) — the
   >=1024 override below trades that for a wide, short hero band instead:
   16:5 capped at 320px tall. */
.prof-banner {
  position: relative;
  margin: -22px -24px 40px;
  width: calc(100% + 48px);
  aspect-ratio: 16 / 9;
  background: var(--surface-2);
  overflow: visible;
}
@media (min-width: 1024px) {
  .prof-banner {
    aspect-ratio: 16 / 5;
    max-height: 320px;
  }
  .prof-banner-mono {
    font-size: clamp(40px, 9vw, 72px);
  }
}
.simband + #pane-service.has-cover #service-list .prof-banner:first-child {
  margin-top: 0;
}
/* Video/iframe fill: object-fit: cover — unlike the retired `.prof-cover`
   (contain, to protect a practitioner's own uncontrolled source), the
   media reaching this band is either a clinic-composed banner/video or
   the designed placeholder, so a full-bleed fill reads as intentional
   rather than a crop risk. Visible focus ring since both are keyboard-
   reachable interactive media (video controls / iframe). */
.prof-banner-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border: 0;
  border-bottom: 1px solid var(--line);
}
.prof-banner-media:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}
/* Designed placeholder (Chris: "a video / placeholder if we dont have the
   video" — never the old flat "clinic name in a box" text fallback). A
   soft accent gradient with a faint, large, right-aligned monogram — NO
   text, since the clinic's own name already sits in the page header
   above this band. */
.prof-banner-placeholder {
  background: linear-gradient(135deg, var(--accent), var(--accent-ink));
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.prof-banner-mono {
  font-size: clamp(56px, 13vw, 128px);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--on-accent);
  opacity: 0.18;
  padding-right: 5%;
  max-width: 100%;
  overflow: hidden;
  pointer-events: none;
  user-select: none;
}

/* Single-column profile, desktop included (2026-09-01 — Chris, looking at
   the old 58/42 split: "the services shouldnt be listed beside a HEADER.
   They should be listed below in a list."). `.prof-cols`/`.prof-col-main`/
   `.prof-col-side` are still built by renderPractitionerProfile() in
   app.js, but stay `display: contents` at every width now — no grid, no
   sticky side column. DOM order (identity -> quote -> bio -> facts ->
   focus -> services) IS the render order at every breakpoint: header band
   full width, then identity/bio, then "Services they offer" full width
   below, one row per service (the existing `.svc` rows). The former
   >=1024px 58/42 grid + 16:6 desktop banner override is retired along
   with it — the band keeps its mobile 16:9 full-bleed shape at every
   width, since it now spans the whole content column regardless of
   viewport. */
.prof-cols,
.prof-col-main,
.prof-col-side {
  display: contents;
}
.prof-banner-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-bottom: 1px solid var(--line);
}
.prof-banner-avatar {
  position: absolute;
  left: 24px;
  bottom: -36px;
  z-index: 1;
  width: 72px;
  height: 72px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-2);
  border: 3px solid var(--ground);
  box-shadow: var(--shadow-lift);
  display: block;
}
.prof-banner-avatar.avatar-mono { font-size: 24px; }

.prof-name-wrap-banner { margin: 0 0 12px; }
.prof-name-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.prof-credentials { font-size: 12.5px; color: var(--ink-faint); margin-top: 2px; }

/* One-line pull quote (2026-08-31, `provider.quote`) — accent-ink italic,
   the one spot on this page allowed to carry the clinic's accent as text
   color rather than only borders/backgrounds. */
.prof-quote { font-style: italic; color: var(--accent-ink); font-size: 14.5px; line-height: 1.5; margin: 0 0 14px; }

/* Compact facts row (2026-08-31) — years in practice / languages /
   education, joined with a middle dot, only the facts actually present. */
.prof-facts { font-size: 13px; color: var(--ink-soft); margin: 0 0 14px; font-variant-numeric: tabular-nums; }

/* Focus-area chips (2026-08-31) — reuses `.tag`, the SAME classification-
   pill class the pronouns tag and the calendar band legend already use;
   this app has no separate chip/badge component and none is needed. */
.prof-focus { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 18px; }

/* Media -> headshot -> monogram (avatarNode, public/app.js) — used by the
   practitioner grid (`.prac-avatar`) and the profile header's overlapping
   headshot (`.prof-banner-avatar`). Declared AFTER those classes on
   purpose: same specificity, so source order is what makes this
   fallback's background/display/alignment win over the sizing rules
   above it, while still inheriting their width/height/margin (never
   redeclared here). */
.avatar-mono {
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 640;
  letter-spacing: 0.02em;
  color: var(--accent-soft-ink);
  background: var(--accent-soft);
  border: 1px solid var(--line);
}
.prac-avatar.avatar-mono { font-size: 15px; }

/* Discipline tiles — the funnel's degrade path now (2026-08-27; was step 1
   before practitioner-first). Tap-first: the whole tile advances. */
.cat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
@media (max-width: 380px) { .cat-grid { grid-template-columns: 1fr; } }

.cat {
  text-align: left;
  padding: 16px;
  min-height: 84px;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--ground);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 6px;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.cat:hover { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-soft-ink); }
.cat:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cat-name { font-weight: 600; font-size: 15px; line-height: 1.3; letter-spacing: -0.01em; }
.cat-meta { font-size: 12.5px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

.svc {
  display: flex;
  align-items: center;
  gap: 16px;
  width: 100%;
  text-align: left;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--ground);
  cursor: pointer;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.svc:hover { border-color: var(--line-strong); background: var(--surface); }
.svc:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.svc.on { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-soft-ink); }

.svc-main { flex: 1; min-width: 0; }
.svc-name { font-weight: 550; font-size: 15px; line-height: 1.35; }
.svc-meta { font-size: 13px; color: var(--ink-soft); margin-top: 2px; font-variant-numeric: tabular-nums; }
.svc-chev { color: var(--ink-faint); flex: none; line-height: 0; align-self: center; }

/* A card with a description grows taller than its chevron, so it can no
   longer center-align the row (the chevron would float mid-paragraph) —
   `.has-desc` switches the row to top-align instead; the chevron's own
   align-self keeps it visually centered against the name/meta line only
   when no description is present (the common case, unchanged). Mirrors
   `.prof-bio`'s restrained typography scale (styles.css ~L467) — same
   font-size/line-height/color, so a service description reads as the same
   register as a practitioner bio. */
.svc.has-desc { align-items: flex-start; }
.svc-desc { font-size: 13px; color: var(--ink-soft); line-height: 1.5; margin: 6px 0 0; white-space: pre-line; }

/* Classification tag — carries a fact, never decoration. */
.tag {
  display: inline-block;
  font-size: 12px;
  font-weight: 550;
  padding: 2px 7px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-soft);
  background: var(--ground);
  white-space: nowrap;
}
.tag.warn { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 35%, transparent); background: var(--warn-soft); }

/* ------------------------------------------------------------- calendar */

.cal { margin-bottom: 18px; }

/* Step 2 must fit calendar + times on one screen; its header is tighter. */
#pane-time .sub { margin-bottom: 14px; }

.cal-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
}
.cal-month {
  flex: 1;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.015em;
}
.cal-nav {
  border: 1px solid var(--line-strong);
  background: var(--ground);
  color: var(--ink-soft);
  border-radius: var(--radius-control);
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  cursor: pointer;
  flex: none;
  line-height: 0;
}
.cal-nav:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
.cal-nav:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.cal-nav:disabled { opacity: 0.3; cursor: not-allowed; }

.cal-dow, .cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
}
.cal-dow {
  margin-bottom: 4px;
}
.cal-dow span {
  text-align: center;
  font-size: 11px;
  font-weight: 550;
  color: var(--ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.cal-pad { display: block; }

.cal-day {
  position: relative;
  /* Booksy-compact: fixed short rows so the whole month AND the day's times
     share one screen. Square tiles pushed the times below the fold. */
  height: 38px;
  width: 100%;
  border: 1px solid transparent;
  border-radius: var(--radius-control);
  background: transparent;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  padding: 0;
  font: inherit;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.cal-num { font-size: 13.5px; font-weight: 550; font-variant-numeric: tabular-nums; line-height: 1; }

/* The availability band. Colour is never the only signal — a day that cannot
   be picked is also not a button and carries aria-disabled. */
.cal-bar {
  width: 16px;
  height: 3px;
  border-radius: 2px;
  background: var(--accent);
}
.cal-day.limited .cal-bar { background: var(--band-limited); }
.cal-day.full .cal-bar { background: var(--band-full); }

button.cal-day { cursor: pointer; background: var(--surface); border-color: var(--line); }
button.cal-day:hover { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-soft-ink); }
button.cal-day:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* A full day is a door to the waitlist, so it stays a button — but its number
   is muted so the eye still reads the month by where the room is. */
button.cal-day.full { color: var(--ink-soft); }
button.cal-day.full:hover { border-color: var(--band-full); background: var(--danger-soft); }

.cal-day.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.cal-day.on .cal-bar { background: var(--on-accent); opacity: 0.8; }

/* Closed, past, beyond — visible for orientation, plainly not choosable. */
.cal-day.closed { color: var(--ink-faint); }
.cal-day.past, .cal-day.beyond { color: var(--ink-faint); opacity: 0.4; }

/* Away — a pinned practitioner on time off (or every confirmed practitioner
   away). Distinct from closed: closed means the clinic doesn't run that day,
   away means people just aren't in — so it gets its own filled grey tile
   rather than reading as identical to "clinic shut". Not a button (see
   drawMonth): there is no waitlist door for a day nobody works. */
.cal-day.away { color: var(--ink-faint); background: var(--surface-2); }

.cal-day.loading .cal-num { opacity: 0.25; }
.cal-day.loading { background: var(--surface); border-color: var(--line); }

.cal-key {
  font-size: 12px;
  color: var(--ink-faint);
  margin-top: 8px;
  line-height: 1.45;
}

/* PW audit item 5 — the clinic's cancellation policy, under the slot
   picker and on the review step. Same quiet register as .cal-key/.hint
   (never a colored notice — this isn't a warning, it's fine print), set
   apart with a hairline divider so it reads as its own line, not a run-on
   of whatever sits above it. Empty text collapses the divider too, so a
   not-yet-booted page never flashes a bare rule with nothing under it. */
.policy-note {
  font-size: 12px;
  color: var(--ink-faint);
  line-height: 1.45;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}
.policy-note:empty { display: none; }

/* --------------------------------------------------------------- slots */

.prov-block { margin-bottom: 26px; }
.prov-block:last-child { margin-bottom: 0; }
.prov-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--line);
}
.prov-name { font-weight: 570; font-size: 14px; }
.prov-disc { font-size: 13px; color: var(--ink-faint); }
.prov-count { margin-left: auto; font-size: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

.slots { display: flex; flex-wrap: wrap; gap: 6px; }
.slot {
  padding: 8px 13px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  background: var(--ground);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.slot:hover { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-soft-ink); }
.slot:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.slot.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* --------------------------------------------------------------- forms */

.field { margin-bottom: 16px; }
.field label {
  display: block;
  font-size: 13px;
  font-weight: 550;
  margin-bottom: 6px;
  color: var(--ink);
}
.field input, .field textarea, .field select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  background: var(--ground);
  color: var(--ink);
  font: inherit;
  font-family: var(--font-control);
  font-size: 15px;
}
.field input::placeholder, .field textarea::placeholder { color: var(--ink-faint); }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: var(--accent);
}
.field textarea { min-height: 72px; resize: vertical; }
.field .hint { font-size: 12px; color: var(--ink-faint); margin-top: 5px; }
.field.err input, .field.err textarea, .field.err select {
  border-color: var(--danger);
}
.field.err input:focus, .field.err textarea:focus, .field.err select:focus {
  outline-color: var(--danger);
  border-color: var(--danger);
}
.field .field-error {
  font-size: 12px;
  font-weight: 550;
  color: var(--danger);
  margin-top: 5px;
}
/* Money-row affix layout (2026-09-03, "Value of one visit") — a `$` prefix
 * and a read-only currency `.tag` bracket the editable number input, so the
 * currency reads as identity metadata (derived, never desk-writable) rather
 * than a second input. Reuses `.field`'s existing input/hint/error chrome —
 * this only replaces the plain `width:100%` input with a flex row. */
.field .money-row { display: flex; align-items: center; gap: 8px; }
.field .money-row .money-affix { font-size: 15px; color: var(--ink-soft); flex: none; }
.field .money-row input { width: auto; flex: 1; min-width: 0; }
.field .money-row .tag { flex: none; }
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
@media (max-width: 440px) { .row2 { grid-template-columns: 1fr; } }

/* Step-3 vertical rhythm: the patient-type toggle, the identity block, and
   the visit-details note are three distinct sections, not one undifferentiated
   stack of fields — a consistent, generous gap between them (not just one
   accidental gap before the footer) reads as intentional at both a 430px
   phone and a 1440px desktop frame. Each section keeps its own tight internal
   .field spacing; only the SPACE BETWEEN sections grows. */
.details-body { display: flex; flex-direction: column; gap: 30px; }
.details-body > .seg { margin-bottom: 0; }
.details-section { padding-top: 2px; }
.details-section .group-label { margin: 0 0 12px; }
@media (min-width: 720px) {
  .details-body { gap: 36px; padding-top: 4px; }
}

.seg {
  display: flex;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  overflow: hidden;
  margin-bottom: 22px;
}
.seg button {
  flex: 1;
  padding: 10px 8px;
  border: 0;
  background: var(--ground);
  cursor: pointer;
  font-size: 14px;
  font-weight: 530;
  color: var(--ink-soft);
}
.seg button + button { border-left: 1px solid var(--line-strong); }
.seg button.on { background: var(--accent); color: var(--on-accent); }
.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* --------------------------------------------------------- simulated sms */
/* Demo-only "your phone" panel: shows the code/confirmation texts arriving
   as received SMS bubbles, reusing the exact dryrun message bodies the
   server actually generated (never a second, invented copy). Deliberately
   plain — a quiet phone-frame card, not a decorative device mockup. */
.pf-sim {
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--surface);
  padding: 14px;
  margin-bottom: 16px;
}
.pf-sim-head {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 12px;
}
.pf-sim-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
}
.pf-sim-thread { display: flex; flex-direction: column; gap: 8px; }
.pf-sim-msg {
  align-self: flex-start;
  max-width: 88%;
  padding: 9px 12px;
  border-radius: 14px;
  border-bottom-left-radius: 4px;
  background: var(--surface-2);
  color: var(--ink);
  font-size: 13.5px;
  line-height: 1.45;
  animation: pf-sim-in 0.18s ease-out;
}
.pf-sim-msg .pf-sim-time {
  display: block;
  margin-top: 4px;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--ink-faint);
}
@keyframes pf-sim-in {
  from { opacity: 0; transform: translateY(3px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .pf-sim-msg { animation: none; }
}
.pf-fill {
  display: block;
  margin-top: 8px;
}

/* -------------------------------------------------------------- review */

.summary {
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--surface);
  padding: 4px 16px;
  margin-bottom: 20px;
}
.summary .r {
  display: flex;
  justify-content: space-between;
  gap: 20px;
  padding: 11px 0;
  font-size: 14px;
}
.summary .r + .r { border-top: 1px solid var(--line); }
.summary .k { color: var(--ink-soft); flex: none; }
.summary .v { font-weight: 550; text-align: right; }

.notice {
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  font-size: 13px;
  color: var(--ink-soft);
  margin-bottom: 18px;
}
.notice.warn { background: var(--warn-soft); border-color: color-mix(in srgb, var(--warn) 28%, transparent); color: var(--warn); }
.notice.err { background: var(--danger-soft); border-color: color-mix(in srgb, var(--danger) 28%, transparent); color: var(--danger); }
.notice strong { font-weight: 640; }
.notice .rb-btn { border: 0; background: none; padding: 0; color: inherit; font: inherit;
  font-weight: 640; text-decoration: underline; cursor: pointer; flex: none; }

/* Simulation tint band — quiet, not a warning. Sits above the booking card
   for a demo tenant. Uses the tenant's own accent-soft/accent-ink tokens so
   it never reads as an alarm and holds contrast in both themes. */
.simband {
  background: var(--accent-soft);
  color: var(--accent-soft-ink);
  border-radius: var(--radius-sm);
  padding: 9px 14px;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.5;
  margin-bottom: 18px;
}
.simband-back {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 600;
  white-space: nowrap;
}
.simband-back:hover,
.simband-back:focus-visible {
  text-decoration-thickness: 2px;
}
.simband-back:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 2px;
  border-radius: 2px;
}

/* ---------------------------------------------------------------- cta */

/* In-flow, not pinned (2026-09-01 fix): the bar sits directly under the
   content of the current step with normal bottom padding, never floating
   fixed over a viewport taller than the content — see the .shell comment
   above. Visual style (background/border/padding) is unchanged. */
.footer {
  background: var(--ground);
  border-top: 1px solid var(--line);
  padding: 14px 24px calc(14px + env(safe-area-inset-bottom));
}
.legalfoot {
  max-width: 620px;
  margin: 0 auto;
  padding: 12px 24px calc(12px + env(safe-area-inset-bottom));
  display: flex;
  justify-content: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--ink-faint);
}
.legalfoot a { color: var(--ink-faint); text-decoration: underline; text-underline-offset: 2px; }
.legalfoot a:hover, .legalfoot a:focus-visible { color: var(--ink-soft); }
.footer-inner {
  max-width: 620px;
  margin: 0 auto;
  display: flex;
  gap: 12px;
  align-items: center;
}

.cta {
  flex: 1;
  padding: 12px 20px;
  border: 1px solid var(--accent);
  border-radius: var(--radius-control);
  background: var(--accent);
  color: var(--on-accent);
  font-size: 15px;
  font-weight: 570;
  cursor: pointer;
  transition: background-color 0.12s ease;
}
.cta:hover:not(:disabled) { background: var(--accent-ink); border-color: var(--accent-ink); }
.cta:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cta:disabled { opacity: 0.38; cursor: not-allowed; }
.cta.ghost {
  background: var(--ground);
  color: var(--ink);
  border-color: var(--line-strong);
  flex: none;
  padding: 12px 16px;
}
.cta.ghost:hover:not(:disabled) { background: var(--surface-2); border-color: var(--line-strong); }

/* Inline text action inside a .hint line (e.g. "Use my email instead") — no
   button chrome, just the accent underline the rest of the product already
   uses for links. */
.linklike {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--accent);
  text-decoration: underline;
  cursor: pointer;
}
.linklike:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.footmeta { font-size: 13px; color: var(--ink-soft); line-height: 1.35; }
.footmeta b { display: block; color: var(--ink); font-weight: 570; }

/* ------------------------------------------------------------- states */

.empty {
  padding: 28px 20px;
  color: var(--ink-soft);
  font-size: 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  background: var(--surface);
}
.empty b { display: block; color: var(--ink); font-weight: 570; margin-bottom: 4px; font-size: 15px; }
.empty p { margin: 0; line-height: 1.5; }
/* CLINIC CONFIRMED ONLY empty states (2026-09-01) — an optional secondary
   action ("See all services") sits inside the same card, on its own row,
   never full-width-stretched like the footer's `.cta` (no flex parent
   here to give that rule meaning). */
.empty .cta { display: inline-flex; margin-top: 14px; }

.skel {
  height: 58px;
  border-radius: var(--radius-card);
  background: var(--surface);
  border: 1px solid var(--line);
  margin-bottom: 6px;
}

.center { text-align: center; }
.stack { display: flex; flex-direction: column; gap: 10px; }

.done-mark {
  width: 44px;
  height: 44px;
  border: 1px solid var(--accent);
  border-radius: 50%;
  color: var(--accent);
  display: grid;
  place-items: center;
  margin: 8px auto 20px;
  font-size: 20px;
  line-height: 0;
}

/* Defect 1 fix (2026-08-29): an unarmed-real tenant (writeEnabled:false,
   no appointmentNo came back) must never show the success checkmark — a
   real patient reading a green check + "You're booked" believes a slot is
   held when OSCAR holds nothing. Reuses the existing --warn token, same
   discipline as invariant 34's OTP-kill-switch box (no invented tint). */
.done-mark.pending {
  border-color: var(--warn);
  color: var(--warn);
}

code {
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 12px;
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 3px;
}

.payload {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 13px 14px;
  font-family: ui-monospace, 'SF Mono', Menlo, monospace;
  font-size: 11.5px;
  line-height: 1.6;
  overflow-x: auto;
  white-space: pre;
  color: var(--ink-soft);
  margin-top: 12px;
}

.disclose {
  border-top: 1px solid var(--line);
  margin-top: 18px;
  padding-top: 14px;
}
.disclose summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 550;
  color: var(--ink-soft);
  list-style: none;
}
.disclose summary::-webkit-details-marker { display: none; }
.disclose summary:hover { color: var(--ink); }
.disclose summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ------------------------------------------------------------- desktop */
/* The flow is one column by design (Booksy register); on a wide screen the
   column becomes a framed card on a tinted page instead of floating loose
   in white space. The in-flow footer keeps the same 620px frame. */
@media (min-width: 720px) {
  body { background: var(--surface); }
  .shell {
    border-left: 1px solid var(--line);
    border-right: 1px solid var(--line);
  }
  /* .footer is already inside the framed .shell (in-flow, see the .footer
     rule above) so it inherits the shell's left/right border for free —
     no separate fixed-positioning override needed here anymore. */
}

/* --------------------------------------------------------- desktop wide */
/* 2026-08-31, Chris on the 1440-wide live site: "why is this a mobile look
   on a desktop site?" — the 618/620px column above stayed the frame for
   phone AND desktop alike, so a 1440px screen was one narrow ribbon lost
   in empty margins. This block only fires >=1024px; the 620px-frame
   `@media (min-width: 720px)` block above (720-1023, a tablet/narrow-
   desktop band) is untouched, and everything below 720 is untouched.

   Embedded/iframe note: this product has no live iframe embed today —
   `/embed.js` (server.js) renders a plain link-out `<a>` styled with the
   clinic's accent that top-level-navigates to this page; it does not embed
   this page inside an <iframe> on the clinic's own site. That makes the
   "don't fire inside a narrow iframe" requirement automatic rather than
   something to code defensively: `@media` reads the frame's OWN viewport,
   and there is no frame narrower than the real one to misread. If a real
   iframe embed is ever added, nothing here needs to change — a clinic
   site embedding this page at, say, 620px wide would still never cross
   these >=1024 queries, exactly like a narrow browser window today. */
@media (min-width: 1024px) {
  .shell { max-width: 1120px; }

  /* The footer stayed pinned to the old 618px frame (`.footer-inner`'s
     own `max-width: 620px`, set in the 720px band above) even after the
     shell widened — a narrow bar floating centred under a 1120px page reads
     as misaligned, not centred-on-purpose. Matching left/right edges to the
     shell fixes that; letting `.cta`'s existing `flex: 1` stretch to fill
     the new 1120px gap would just trade "too narrow" for "absurdly wide,"
     so button SIZE stays exactly what it is below 1024 (`flex: none` here
     overrides the base `.cta{flex:1}`) and `.footmeta` absorbs the freed
     space instead, right-aligning the button group — the Stripe/Linear
     checkout register TASTE.md anchors on (§1: "same register as Stripe/
     Apple/Tesla/Amazon"): primary action fixed-size and anchored to the
     trailing edge, context copy filling the leading space, never a CTA
     stretched to the width of the page. Applies to every step, including
     details/review — their form is capped at 640 (see above), but the
     footer bar itself still spans the full shell, not the form. */
  .footer-inner { max-width: 1120px; }
  .footer-inner .footmeta { flex: 1; }
  .footer-inner .cta { flex: none; min-width: 200px; }

  /* Step 2 (calendar): month grid left, that day's slots right — sticky so
     both are visible together, per the brief. Pure CSS, no markup change:
     `.eyebrow`/`.title`/`.sub` are pinned to span both columns (each still
     gets its own auto row since a full-span item can't share a row), then
     `.cal` and `#slot-area` — already DOM siblings, unchanged from the
     mobile build — fall into column 1 and column 2 of the next open row. */
  /* `.on`, always — an ID selector outranks `.pane { display: none }`
     (0,1,0) at even lower specificity, so an unscoped `#pane-time {
     display: grid }` would force the time pane visible at EVERY step, not
     just when it's the active one. Caught live in this build's own
     browser walk (agent-browser, 1440x900 step-1 screenshot showed the
     calendar bleeding through under the practitioner grid) before it
     shipped. Same discipline on `#pane-details`/`#pane-review` below. */
  #pane-time.on {
    display: grid;
    grid-template-columns: 1fr 380px;
    column-gap: 32px;
    align-items: start;
  }
  #pane-time.on > .eyebrow,
  #pane-time.on > .title,
  #pane-time.on > .sub {
    grid-column: 1 / -1;
  }
  #pane-time.on > .cal { grid-column: 1; }
  #pane-time.on > #slot-area {
    grid-column: 2;
    position: sticky;
    top: 96px;
  }

  /* Step 3/4 (details, review): a 1120px-wide form or a 1120px-wide review
     card both read worse than the same content at a sane reading width —
     "wide forms are worse" per the brief. Keep the existing 640px content
     centred inside the wider shell rather than stretching it; neither pane
     carries a form AND a summary card at once today (details = form only,
     review = summary only), so there is nothing to place beside it yet —
     this still satisfies "the form at max 640 centred inside the wide
     shell, with a summary card beside it on the right if one exists." */
  #pane-details.on,
  #pane-review.on {
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
  }
}

/* ==========================================================================
   Schedule room (2026-09-02) — the front-desk's read-only OSCAR appointment
   book. Ported from the approved comp
   (~/SecondBrain/tasks/2026-09-02-oscar-staff-calendar/mockup/schedule.html)
   verbatim for the grid/tile/drawer mechanics; every class below is new
   (sched-*, appt, provcol, drawer, etc. don't exist elsewhere in this
   file) so nothing here redefines an existing token or component rule —
   `.chip`/`.badge`/`.btn`/`.widget`/`.overlay` all already live in
   public/desk.html's own inline <style> and are reused as-is by the room's
   toolbar; the one place this room's chips need a DIFFERENT selected style
   than desk.html's global `.chip[aria-pressed]` (accent-soft fill here vs
   that black fill, reserved for the Day|Week toggle) is scoped under
   `.sched-chips`/`.sched-chips-scroll` so it never leaks onto any other
   chip in the app. Runs on the SAME light/dark tokens as the rest of the
   desk (--accent/--ground/--ink/--line/... from the :root blocks above) —
   dark mode needs no separate appt/tile rules, only the grid-line and
   status-tint color-mixes below, which already resolve off those tokens.
   ========================================================================== */

:root {
  --gridline-hour: rgba(17, 24, 39, .14);
  --gridline-half: rgba(17, 24, 39, .07);
  --gridline-quarter: rgba(17, 24, 39, .035);
}
[data-theme='dark'] {
  /* Same three-weight rule system, inverted for a dark ground — a literal
     dark-navy rgba over a near-black background would be invisible. */
  --gridline-hour: rgba(255, 255, 255, .16);
  --gridline-half: rgba(255, 255, 255, .09);
  --gridline-quarter: rgba(255, 255, 255, .045);
}

/* ------------------------------------------------------------- toolbar */
.sched-toolbar { display: flex; align-items: center; gap: 10px; margin: 14px 0 10px; flex-wrap: wrap; }
.datenav { display: flex; align-items: stretch; border: 1px solid var(--line-strong);
           border-radius: var(--radius-sm); overflow: hidden; height: 36px; box-sizing: border-box; }
.datenav button { border: 0; background: var(--ground); color: var(--ink-soft); padding: 0 12px;
                   font-size: 13px; cursor: pointer; line-height: 1; display: flex; align-items: center; }
.datenav button:hover { background: var(--surface-2); }
.datenav button:disabled { opacity: .45; cursor: default; }
.datenav button:disabled:hover { background: var(--ground); }
.datenav .datepick { border-left: 1px solid var(--line-strong); border-right: 1px solid var(--line-strong);
                      font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; padding: 0 14px;
                      background: var(--ground); cursor: pointer; }
.viewtoggle { display: flex; align-items: stretch; border: 1px solid var(--line-strong);
              border-radius: var(--radius-sm); overflow: hidden; height: 36px; box-sizing: border-box; }
.viewtoggle button { border: 0; background: var(--ground); color: var(--ink-soft); padding: 0 16px;
                      font-size: 13px; font-weight: 550; cursor: pointer; display: flex; align-items: center; }
.viewtoggle button[aria-pressed="true"] { background: var(--ink); color: var(--ground); }
.sched-chips { display: flex; gap: 6px; flex-wrap: wrap; }
.sched-chips-scroll { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 2px; }
/* Selected = accent-soft fill + accent text here (never the black fill
   `.chip[aria-pressed]` uses globally, reserved for the Day|Week toggle). */
.sched-chips .chip[aria-pressed="true"],
.sched-chips-scroll .chip[aria-pressed="true"] {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink);
}
/* One-line practitioner chips (2026-09-02 fix) — a real roster like Fit
   for Life's (18, several with long OSCAR-supplied role text baked into
   the raw name) was wrapping the global `.chip`'s text to 3-4 lines
   because `.sched-chips-scroll` is an `overflow-x:auto` flex row, which
   sets each child's automatic minimum width to 0 and lets the browser
   shrink-and-wrap the label instead of scrolling past it. `flex: none` +
   `white-space: nowrap` restores the intended "scroll past it" behavior;
   height is pinned so every chip in the row reads as the same control
   regardless of whether it carries a role suffix. */
.sched-chips-scroll .chip {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 36px;
  white-space: nowrap;
  box-sizing: border-box;
}
.sched-chips-scroll .chip .chip-role {
  color: var(--ink-faint);
  font-weight: 500;
}
.sched-chips-scroll .chip[aria-pressed="true"] .chip-role { color: var(--accent-ink); opacity: .8; }

.freshline { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--ink-faint);
             padding: 6px 2px 0; }
.freshline .dot-live { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: none; }
.freshline button { border: 0; background: none; color: var(--accent-ink); font-weight: 600;
                     font-size: 12px; cursor: pointer; padding: 0; }
.freshline button:hover { text-decoration: underline; }
.freshline.stale { color: var(--danger); }
.freshline.stale .dot-live { background: var(--danger); }

.sched-legend2 { font-size: 11px; color: var(--ink-faint); padding: 4px 2px 16px; display: flex;
                 align-items: center; gap: 6px; }
.sched-legend2 .a-dot { position: static; margin-left: 0; }

/* ---------------------------------------------------------------- grid */
.sched-shell { border: 1px solid var(--line); border-radius: 10px; background: var(--ground); overflow: hidden; }
/* Real rosters run past what any viewport fits side by side (Fit for
   Life: 18 practitioners) — columns hold a 200px floor and the room
   scrolls HORIZONTALLY rather than squeezing names into unreadable
   slivers; the gutter and header both stay sticky against that scroll
   (2026-09-02 fix). */
.sched-shell.daybox { max-height: calc(100vh - 260px); overflow-y: auto; overflow-x: auto; }
.sched-shell.stale { opacity: 0.55; filter: grayscale(0.35); pointer-events: none; }
.sched-cols-head { display: flex; border-bottom: 1px solid var(--gridline-hour); background: var(--surface);
                    position: sticky; top: 0; z-index: 5; min-width: max-content; }
.sched-gutter-head { width: 56px; flex: none; position: sticky; left: 0; z-index: 7; background: var(--surface); }
.provcol-head { flex: 0 0 200px; width: 200px; min-width: 200px; padding: 12px 14px; border-left: 1px solid var(--gridline-hour);
                display: flex; align-items: flex-start; gap: 8px; box-sizing: border-box; }
.provcol-head .swatch { width: 4px; align-self: stretch; border-radius: 2px; flex: none; margin-top: 1px; }
/* Name wraps to a second line rather than ellipsis/overlap when it doesn't
   fit the 200px column — "never overlap, never ellipsis" (2026-09-02). */
.provcol-head .pname { font-size: 13px; font-weight: 600; line-height: 1.25; overflow-wrap: break-word;
                        white-space: normal; min-width: 0; }
.provcol-head .pmeta { font-size: 11px; color: var(--ink-faint); margin-top: 2px; }

.sched-body { display: flex; position: relative; }
.sched-gutter { width: 56px; flex: none; position: sticky; left: 0; z-index: 4; background: var(--ground); }
.gutter-label { position: absolute; right: 10px; left: 0; text-align: right; transform: translateY(-100%);
                font-size: 12px; font-weight: 500; line-height: 1; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.gutter-sublabel { position: absolute; right: 10px; left: 0; text-align: right; transform: translateY(-100%);
                    font-size: 10px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }

.provcol { flex: 0 0 200px; width: 200px; min-width: 200px; position: relative; border-left: 1px solid var(--gridline-hour); background: var(--ground); box-sizing: border-box; }
/* Week view's 7 columns at day's 200px (1400px + gutter) forced horizontal
   scroll at 1440 — narrower here since week already reads as compact
   tiles, not a distinct layout language. 150px * 7 + 56px gutter = 1106px,
   fits without scroll. Day keeps 200px (far fewer columns shown at once). */
.provcol-head.week-col, .provcol.week-col { flex: 0 0 150px; width: 150px; min-width: 150px; }
.hourline { position: absolute; left: 0; right: 0; border-top: 1px solid var(--gridline-hour); }
.halfline { position: absolute; left: 0; right: 0; border-top: 1px solid var(--gridline-half); }
.quarterline { position: absolute; left: 0; right: 0; border-top: 1px solid var(--gridline-quarter); }
.hatched { position: absolute; left: 0; right: 0; z-index: 1;
           background: repeating-linear-gradient(135deg, rgba(17,24,39,.05), rgba(17,24,39,.05) 6px, transparent 6px, transparent 12px); }
[data-theme='dark'] .hatched {
  background: repeating-linear-gradient(135deg, rgba(255,255,255,.06), rgba(255,255,255,.06) 6px, transparent 6px, transparent 12px);
}
.hatched-label { position: absolute; top: 6px; left: 8px; font-size: 11px; font-weight: 600;
                  color: var(--ink-faint); background: var(--ground); padding: 1px 6px; border-radius: 4px; }
.away-band { position: absolute; left: 2px; right: 2px; background: rgba(17,24,39,.045); border-radius: 6px; z-index: 1; }
[data-theme='dark'] .away-band { background: rgba(255,255,255,.05); }
.away-band span { position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
                   font-size: 11px; font-weight: 600; color: var(--ink-faint); letter-spacing: 0.02em;
                   background: var(--ground); padding: 1px 8px; border-radius: 4px; white-space: nowrap; }

.slot-empty { position: absolute; left: 2px; right: 2px; z-index: 1; border: 0; background: none;
              cursor: pointer; border-radius: 6px; padding: 0; overflow: hidden; text-align: left; }
.slot-empty .lbl { display: none; font-size: 11px; font-weight: 600; color: var(--accent);
                    padding-left: 8px; line-height: inherit; }
.slot-empty:hover, .slot-empty:focus-visible { outline: 1px dashed var(--accent); outline-offset: -1px;
                    background: color-mix(in srgb, var(--accent) 6%, transparent); }
.slot-empty:hover .lbl, .slot-empty:focus-visible .lbl { display: block; }

.nowline-seg { position: absolute; left: 0; right: 0; height: 0; border-top: 2px solid var(--danger); z-index: 3; }
.nowdot-wrap { position: absolute; left: 0; width: 56px; display: flex; align-items: center;
               justify-content: flex-end; gap: 4px; transform: translateY(-50%); z-index: 4; }
.nowdot-wrap .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--danger); flex: none; }
.nowdot-wrap .lbl { font-size: 11px; font-weight: 600; color: var(--danger); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- tiles */
.appt {
  position: absolute; left: 2px; right: 2px; border-radius: 6px; border: 0;
  border-left: 3px solid var(--ink-soft); padding: 2px 7px; overflow: hidden;
  cursor: pointer; z-index: 2; box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: center; text-align: left;
}
.appt:hover { filter: brightness(0.97); }
.appt:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.appt .a-name { font-size: 12px; font-weight: 600; line-height: 13px; color: var(--ink); overflow: hidden; white-space: nowrap; }
.appt .a-what { font-size: 12px; font-weight: 400; line-height: 13px; color: var(--ink-soft); overflow: hidden; white-space: nowrap; }
.appt .a-time { font-size: 11px; line-height: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.appt .a-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%;
               background: var(--accent); margin-left: 5px; vertical-align: middle; }
.appt.tiny { padding: 0 7px; }
.appt.tiny .a-name { line-height: 12px; }
.appt.tiny .a-what, .appt.tiny .a-time { display: none; }
.appt.notime .a-time { display: none; }

.appt.st-booked, .appt.st-arrived { background: var(--accent-soft); }
.appt.st-inroom { background: color-mix(in srgb, var(--accent) 16%, var(--ground)); border-left-color: var(--accent); }
.appt.st-done { background: color-mix(in srgb, var(--ink-faint) 8%, var(--ground)); border-left-color: var(--ink-faint); }
.appt.st-done .a-name, .appt.st-done .a-what, .appt.st-done .a-time { color: var(--ink-soft); }
.appt.st-noshow { background: color-mix(in srgb, var(--warn) 12%, var(--ground)); border-left-color: var(--warn); }
.appt.st-cancelled { background: color-mix(in srgb, var(--ink-faint) 6%, var(--ground)); border-left-color: var(--line-strong); }
.appt.st-cancelled .a-name { text-decoration: line-through; color: var(--ink-soft); }
.appt.st-cancelled .a-what, .appt.st-cancelled .a-time { color: var(--ink-faint); }
/* st-requested (2026-09-04, NO-EMR DESK lane) — a no-EMR booking request the
   desk hasn't confirmed/declined yet (see STATUS_LABEL in schedule.js). */
.appt.st-requested { background: color-mix(in srgb, var(--warn) 10%, var(--ground)); border-left-color: var(--warn); border-left-style: dashed; }

/* -------------------------------------------------------------- drawer
   Own overlay class (never `.overlay`/`#modal` — those are the existing
   CENTERED modal desk.html already uses elsewhere in this app; this is a
   right-edge drawer, a different shape, so it gets its own class rather
   than a variant flag on a shared one). */
.sched-overlay { position: fixed; inset: 0; background: rgba(10, 12, 16, 0.44); z-index: 50;
                 display: flex; justify-content: flex-end; }
.sched-overlay[hidden] { display: none; }
.sched-drawer { width: min(420px, 100%); height: 100%; background: var(--ground); border-left: 1px solid var(--line);
                box-shadow: var(--shadow-lift); display: flex; flex-direction: column; }
.sched-drawer-head { display: flex; align-items: flex-start; gap: 12px; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.sched-drawer-head .dh-name { font-size: 17px; font-weight: 640; }
.sched-drawer-head .dh-sub { font-size: 12.5px; color: var(--ink-faint); margin-top: 2px; }
.sched-drawer-x { border: 0; background: none; cursor: pointer; color: var(--ink-soft); font-size: 15px;
                   padding: 6px 8px; border-radius: var(--radius-sm); line-height: 1; margin-left: auto; }
.sched-drawer-x:hover { background: var(--surface-2); color: var(--ink); }
.sched-drawer-body { padding: 18px 20px; overflow-y: auto; flex: 1; }
.dkv { display: grid; grid-template-columns: 96px 1fr; gap: 10px 14px; font-size: 13.5px; margin-bottom: 20px; }
.dkv dt { color: var(--ink-soft); }
.dkv dd { margin: 0; font-weight: 550; }
.dnote { font-size: 13px; color: var(--ink-soft); background: var(--surface); border-radius: 8px;
         padding: 10px 12px; margin-bottom: 18px; line-height: 1.5; }
.drawer-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.drawer-honest { font-size: 12.5px; color: var(--ink-faint); border-top: 1px solid var(--line);
                 padding-top: 14px; line-height: 1.5; }
/* Move/Cancel real-write form (2026-09-04, NO-EMR DESK lane) — same
   inline-row register as .drawer-actions above, plain inputs, no chrome. */
.drawer-move-form { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: -6px 0 16px; }
.drawer-move-form input { border: 1px solid var(--line); border-radius: 8px; padding: 6px 8px;
                           font-size: 13px; color: var(--ink); background: var(--ground); }
.drawer-action-error { font-size: 12.5px; color: var(--warn); margin: -8px 0 12px; }
.appt-card { border: 1px solid var(--line); border-radius: 10px; padding: 14px 16px 4px; margin-bottom: 4px; }
.appt-status-word { display: inline-block; font-size: 11px; font-weight: 600; padding: 2px 9px;
                     border-radius: 999px; background: var(--surface-2); color: var(--ink-soft); }
.drawer-section-label { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 640;
                         letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-faint);
                         margin: 24px 0 4px; }
.drawer-section-label a { margin-left: auto; font-size: 12px; font-weight: 600; letter-spacing: 0;
                           text-transform: none; color: var(--accent-ink); text-decoration: none; }
.drawer-section-label a:hover { text-decoration: underline; }

/* Thread bubbles reuse desk.html's OWN global `.msgs`/`.msgrow`/`.bubble`/
   `.msg-meta` (same page, same document — Conversations room's visual
   language, never restyled here per this lane's brief) — only the
   loading/empty notes and a "sending…" pending tint are new. */
.msgs-loading, .msgs-empty-note { font-size: 13px; color: var(--ink-faint); padding: 10px 0; }
.msgrow.pending .bubble { opacity: 0.65; }

/* ------------------------------------------------------------- composer
   Ported from the approved comp (mockup/schedule.html) verbatim — two
   modes (Write it / Tell the assistant), one active at a time, a visibly-
   a-draft bubble (dashed border, warm tint) that never looks sent. */
.composer { margin-top: 6px; padding-top: 14px; border-top: 1px solid var(--line); }
.composer-tabs { display: flex; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
                  overflow: hidden; margin-bottom: 10px; height: 32px; }
.composer-tabs button { flex: 1; border: 0; background: var(--ground); font-size: 12.5px; font-weight: 600;
                         color: var(--ink-soft); cursor: pointer; }
.composer-tabs button + button { border-left: 1px solid var(--line-strong); }
.composer-tabs button[aria-pressed="true"] { background: var(--ink); color: var(--ground); }
.composer-pane { display: none; }
.composer-pane.on { display: block; }
.composer textarea, .composer input[type="text"] {
  width: 100%; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: 9px 11px; font: inherit; font-size: 13px; color: var(--ink); background: var(--ground);
}
.composer textarea { min-height: 64px; resize: vertical; }
.composer-row { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
.composer-note { font-size: 11px; color: var(--ink-faint); margin-top: 10px; }
.composer-counter { font-size: 11px; color: var(--ink-faint); text-align: right; margin-top: 4px; }
.composer-error { font-size: 12px; color: var(--danger); margin-top: 6px; }
.draft-wrap { margin-top: 12px; }
.draft-label { font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
               color: var(--warn); margin-bottom: 6px; text-align: right; }
.draft-bubble { border: 1.5px dashed var(--warn); border-radius: 15px; border-bottom-right-radius: 5px;
                padding: 8px 12px; font-size: 13px; line-height: 1.42; color: var(--ink);
                background: var(--warn-soft); max-width: 88%; margin-left: auto; white-space: pre-wrap; }
.draft-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }

/* -------------------------------------------------------------- states */
.sched-state-empty { border: 1px solid var(--line); border-radius: 10px; background: var(--ground);
                      padding: 64px 32px; text-align: center; max-width: 460px; margin: 40px auto; }
.sched-state-empty .se-icon { width: 44px; height: 44px; border-radius: 50%; background: var(--surface-2);
                                display: flex; align-items: center; justify-content: center; margin: 0 auto 16px;
                                color: var(--ink-faint); }
.sched-state-empty h3 { font-size: 16px; font-weight: 620; margin-bottom: 6px; }
.sched-state-empty p { font-size: 13.5px; color: var(--ink-soft); line-height: 1.5; margin-bottom: 16px; }
.sched-state-empty a.btn { text-decoration: none; display: inline-block; }

.unreach-banner { display: flex; align-items: center; gap: 10px; background: var(--warn-soft);
                  border: 1px solid color-mix(in srgb, var(--warn) 34%, transparent); border-radius: 8px;
                  padding: 10px 14px; font-size: 13px; color: var(--warn); font-weight: 550; margin-bottom: 14px; }
.unreach-banner button { margin-left: auto; }

@media (max-width: 760px) {
  .sched-shell.daybox { max-height: calc(100vh - 300px); }
}
@media (max-width: 480px) {
  .sched-toolbar { gap: 8px; }
  .provcol-head .pmeta { display: none; }
  .sched-chips { flex-wrap: nowrap; overflow-x: auto; padding-bottom: 2px; }
  .sched-chips .chip { flex: none; }
  .sched-drawer { width: 100%; height: auto; max-height: 82vh; align-self: flex-end; margin-top: auto;
                  border-left: 0; border-top: 1px solid var(--line); border-radius: 14px 14px 0 0; }
  .sched-overlay { align-items: flex-end; }
}

/* =================================================================
   Outreach + Results rooms (2026-09-03) — public/desk/outreach.js and
   public/desk/results.js. Same tokens/type scale/component grammar the
   shipped desk already uses (--accent, hairline --line borders,
   .widget/.btn/.viewhead/.empty2/.count reused as-is elsewhere in this
   file — see those files' own header comments for the reuse audit).
   Ported from the approved comp (~/SecondBrain/tasks/2026-09-03-oscar-
   brain/mockup/outreach-v6.html); every new component below is prefixed
   `oc-` (Outreach) or `res-` (Results) so it can never collide with the
   Today room's own `.stat`/`.chip` classes defined earlier in this file
   and in desk.html's inline <style>.
   ================================================================= */

.toplead2 { font-size: 13px; color: var(--ink-faint); margin: 0 0 20px; }
.oc-mode-banner { font-size: 13px; color: var(--ink-soft); background: var(--surface);
                   border: 1px solid var(--line); border-radius: var(--radius-sm);
                   padding: 10px 14px; margin: 0 0 16px; }
.oc-empty { border: 1px dashed var(--line-strong); border-radius: 10px; background: var(--ground);
            padding: 40px 24px; text-align: center; max-width: 460px; margin: 24px auto; }
.oc-empty h3 { font-size: 15px; font-weight: 620; margin: 0 0 6px; }
.oc-empty p { font-size: 13px; color: var(--ink-soft); line-height: 1.5; margin: 0 0 14px; }

/* -- slot grid (level 1) -- */
.oc-slot-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 18px; }
@media (max-width: 1180px) { .oc-slot-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 760px) { .oc-slot-grid { grid-template-columns: 1fr; } }
.oc-slot-card { display: block; text-align: left; width: 100%; border: 1px solid var(--line);
                border-radius: 10px; background: var(--ground); padding: 0; cursor: pointer; overflow: hidden; }
.oc-slot-card:hover { border-color: var(--line-strong); background: var(--surface); }
.oc-slot-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.oc-card-body { padding: 15px 16px 0; }
.oc-sc-top { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.oc-sc-prac { font-size: 15.5px; font-weight: 640; }
.oc-sc-date { font-size: 12px; color: var(--ink-faint); white-space: nowrap; flex: none; }
.oc-sc-time { font-size: 13.5px; font-weight: 620; margin-top: 6px; font-variant-numeric: tabular-nums; }
.oc-sc-service { font-size: 12.5px; color: var(--ink-soft); margin-top: 3px; }
.oc-sc-source { font-size: 11.5px; color: var(--ink-faint); margin-top: 3px; padding-bottom: 12px; }
.oc-sc-status { margin: 0; padding: 9px 16px; font-size: 12px; font-weight: 640; border-top: 1px solid var(--line);
                background: var(--surface); color: var(--ink-soft); }
.oc-status-offering { background: var(--accent-soft); color: var(--accent-ink); }
.oc-status-filled { background: var(--surface-2); color: var(--ink-soft); }
.oc-status-nobody_fit, .oc-status-held { background: var(--warn-soft); color: var(--warn); }
.oc-status-expired { background: var(--surface-2); color: var(--ink-faint); }
.oc-status-scanning { background: var(--surface-2); color: var(--ink-faint); }

/* -- slot view (level 2) -- */
.oc-slot-header { margin-bottom: 14px; }
.oc-slot-header .oc-card-body { padding: 15px 16px; }
.oc-funnel-card { margin-bottom: 14px; }
.oc-funnel-head { display: flex; align-items: baseline; gap: 8px; padding: 11px 16px; border-bottom: 1px solid var(--line); }
.oc-funnel-title { font-size: 13.5px; font-weight: 620; color: var(--ink); margin: 0; flex: 1; }
.oc-funnel-time { font-size: 11.5px; color: var(--ink-faint); font-weight: 550; white-space: nowrap; font-variant-numeric: tabular-nums; }
.oc-funnel-bars { display: flex; align-items: flex-end; gap: 4px; padding: 20px 16px 14px; }
.oc-funnel-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; min-width: 0; position: relative; }
.oc-funnel-bar-btn { border: 0; background: none; padding: 0; width: 100%; display: flex; flex-direction: column;
                      align-items: center; cursor: pointer; }
.oc-funnel-bar-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.oc-fb-num { font-size: 13px; font-weight: 660; color: var(--ink); margin-bottom: 3px; font-variant-numeric: tabular-nums; }
.oc-fb-shape { width: 72%; min-height: 4px; border-radius: 5px 5px 3px 3px; background: var(--accent); opacity: .55; }
.oc-funnel-bar-btn[aria-expanded="true"] .oc-fb-shape { opacity: 1; }
.oc-fb-label { font-size: 11px; color: var(--ink-soft); margin-top: 5px; text-align: center; line-height: 1.25; }
.oc-funnel-detail { margin-top: 8px; padding: 8px; border: 1px solid var(--line-strong); border-radius: 8px;
                     background: var(--surface); font-size: 11.5px; color: var(--ink-soft); width: 100%; text-align: left; }
.oc-fd-row { padding: 3px 0; }
.oc-fd-more { color: var(--ink-faint); margin-top: 2px; }
.oc-fd-empty { color: var(--ink-faint); }
@media (max-width: 760px) {
  .oc-funnel-bars { flex-direction: column; align-items: stretch; gap: 10px; }
  .oc-funnel-col { flex-direction: row; align-items: center; gap: 10px; }
  .oc-funnel-bar-btn { flex-direction: row; align-items: center; gap: 10px; }
  .oc-fb-num { margin-bottom: 0; width: 26px; text-align: right; }
  .oc-fb-shape { height: 12px !important; width: auto; flex: 1; }
  .oc-fb-label { margin-top: 0; text-align: left; }
}

.oc-prospect-columns { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
@media (max-width: 760px) { .oc-prospect-columns { grid-template-columns: 1fr; } }
.oc-plane { border: 1px solid var(--line); border-top: 3px solid var(--line-strong); border-radius: 10px;
            background: var(--ground); padding: 10px 12px 12px; }
.oc-plane-reached { border-top-color: var(--accent); }
.oc-plane-skip { border-top-color: var(--warn); }
.oc-plane-head { display: flex; align-items: baseline; gap: 7px; margin-bottom: 8px; }
.oc-plane-head h4 { font-size: 11.5px; font-weight: 640; letter-spacing: .03em; text-transform: uppercase;
                     color: var(--ink-soft); margin: 0; }
.oc-plane-reached .oc-plane-head h4 { color: var(--accent-ink); }
.oc-plane-skip .oc-plane-head h4 { color: var(--warn); }
.oc-pc-count { font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.oc-plane-cards { display: flex; flex-direction: column; gap: 8px; }
.oc-plane-empty { font-size: 11.5px; color: var(--ink-faint); padding: 4px 2px; }
.oc-mini-card { border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
                padding: 7px 10px; text-align: left; cursor: pointer; width: 100%; display: flex;
                flex-direction: column; gap: 3px; }
.oc-mini-card:hover { background: var(--surface-2); border-color: var(--line-strong); }
.oc-mini-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.oc-mc-row1 { display: flex; align-items: baseline; gap: 6px; }
.oc-mc-rank { flex: none; font-size: 11px; font-weight: 640; color: var(--ink-faint); font-variant-numeric: tabular-nums;
              white-space: nowrap; max-width: 40%; overflow: hidden; text-overflow: ellipsis; }
.oc-mc-name { flex: 1; min-width: 0; font-size: 12.5px; font-weight: 620; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.oc-mc-phone { flex: none; font-size: 11px; color: var(--ink-faint); font-variant-numeric: tabular-nums; margin-left: auto; }
.oc-mc-when { font-size: 11px; color: var(--ink-soft); line-height: 1.4; }

/* -- mode badge (two-mode outreach, 2026-09-03) -- */
.oc-mode-badge { font-size: 11.5px; font-weight: 620; color: var(--ink-soft); margin: 0; padding: 8px 16px 0; }

/* -- fishing batch grouping (2026-09-03) -- */
.oc-batch-group { border: 1px dashed var(--line-strong); border-radius: 8px; padding: 8px; display: flex;
                   flex-direction: column; gap: 6px; }
.oc-batch-head { font-size: 11px; font-weight: 640; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .03em; }
.oc-batch-cards { display: flex; flex-direction: column; gap: 6px; }
.oc-batch-cards .oc-mini-card { border-color: var(--line); }

/* -- Filling ahead (2026-09-03, two-mode outreach) -- */
.oc-fillahead-widget { margin-top: 4px; margin-bottom: 4px; }
.oc-fa-body { border-top: 1px solid var(--line); padding: 14px 16px; }
.oc-fa-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
@media (max-width: 760px) { .oc-fa-grid { grid-template-columns: 1fr; } }
.oc-fa-card { display: block; text-align: left; width: 100%; border: 1px solid var(--line); border-radius: 10px;
              background: var(--ground); padding: 10px 12px; cursor: pointer; display: flex; flex-direction: column; gap: 5px; }
.oc-fa-card:hover { border-color: var(--line-strong); background: var(--surface); }
.oc-fa-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.oc-fa-due { font-size: 11.5px; color: var(--ink-soft); line-height: 1.4; }
.oc-fa-options { display: flex; flex-direction: column; gap: 2px; }
.oc-fa-opt { font-size: 11.5px; color: var(--ink); font-variant-numeric: tabular-nums; }
.oc-fa-when { font-size: 11px; font-weight: 620; color: var(--accent-ink); }
.oc-fa-budget { font-size: 11.5px; color: var(--ink-faint); margin: 12px 0 0; }

/* -- Live agent activity (Outreach) -- */
.oc-activity-widget { margin-top: 4px; }
.oc-activity-toggle { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
                       padding: 11px 16px; border: 0; background: none; cursor: pointer; }
.oc-activity-toggle h3 { font-size: 12.5px; font-weight: 640; letter-spacing: .04em; text-transform: uppercase;
                          color: var(--ink-soft); flex: 1; margin: 0; }
.oc-activity-list { display: flex; flex-direction: column; max-height: 340px; overflow-y: auto; border-top: 1px solid var(--line); }
.oc-activity-row { display: flex; gap: 10px; padding: 9px 16px; border-top: 1px solid var(--line); }
.oc-activity-row:first-child { border-top: 0; }
.oc-activity-row.warn { background: var(--warn-soft); }
.oc-activity-time { flex: none; min-width: 62px; font-size: 11px; font-weight: 640; color: var(--ink-faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
.oc-activity-text { flex: 1; font-size: 12px; line-height: 1.5; color: var(--ink-soft); }

/* -- prospect modal body + rules modal body (rendered inside the shared
      #modal shell desk.js's openModal() already owns) -- */
.oc-pm-when { font-size: 13px; font-weight: 620; color: var(--ink); margin: 0 0 12px; }
.oc-pm-why { display: flex; flex-direction: column; gap: 7px; background: var(--accent-soft);
             border-radius: 8px; padding: 12px 14px; margin-bottom: 16px; }
.oc-pm-why-line { font-size: 13px; color: var(--ink); line-height: 1.5; margin: 0; padding-left: 14px; position: relative; }
.oc-pm-why-line::before { content: '\2022'; position: absolute; left: 0; color: var(--accent-ink); font-weight: 700; }
.oc-pm-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.oc-pm-table th { text-align: left; font-size: 11px; font-weight: 640; text-transform: uppercase; letter-spacing: .03em;
                   color: var(--ink-faint); padding: 0 10px 6px 0; border-bottom: 1px solid var(--line); }
.oc-pm-table td { padding: 7px 10px 7px 0; border-bottom: 1px solid var(--line); color: var(--ink); }
.oc-pm-table tr:last-child td { border-bottom: 0; }
.oc-pm-caption { font-size: 11.5px; color: var(--ink-faint); margin-top: 8px; }
.oc-pm-empty { font-size: 12.5px; color: var(--ink-faint); }
.oc-pm-foot { border-top: 1px solid var(--line); padding-top: 14px; margin-top: 14px; }
.oc-pm-foot a { font-size: 12.5px; font-weight: 620; text-decoration: none; }
.oc-pm-foot a:hover { text-decoration: underline; }

.oc-rule-row { padding: 13px 0; border-bottom: 1px solid var(--line); }
.oc-rule-row:last-child { border-bottom: 0; }
.oc-rule-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.oc-rule-label { font-size: 13px; font-weight: 640; }
.oc-rule-floor-tag { font-size: 11px; font-weight: 640; color: var(--ink-faint); text-transform: uppercase; letter-spacing: .03em; white-space: nowrap; }
.oc-rule-value { font-size: 12.5px; color: var(--ink); margin-top: 3px; font-variant-numeric: tabular-nums; }
.oc-rule-note { font-size: 12px; color: var(--ink-soft); margin-top: 4px; line-height: 1.5; }

/* -- Results toolbar + stat tiles -- */
.res-toolbar { display: flex; align-items: center; justify-content: flex-end; gap: 10px; margin: 4px 0 14px; }
.res-toolbar-label { font-size: 11.5px; font-weight: 620; letter-spacing: .03em; text-transform: uppercase;
                      color: var(--ink-faint); margin-right: auto; }
.res-period-seg { display: inline-flex; border: 1px solid var(--line-strong); border-radius: 999px; padding: 3px;
                   background: var(--surface); gap: 2px; }
.res-period-seg button { border: 0; background: none; padding: 6px 13px; border-radius: 999px; font-size: 12.5px;
                          font-weight: 600; color: var(--ink-soft); cursor: pointer; }
.res-period-seg button[aria-pressed="true"] { background: var(--ground); color: var(--ink); box-shadow: var(--shadow-lift); }
.res-period-seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.res-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 14px; }
.res-stat { border: 1.5px solid var(--line-strong); border-radius: 10px; padding: 14px 16px; background: var(--ground);
            box-shadow: var(--shadow-lift); text-align: left; width: 100%; display: block; min-width: 0; }
.res-stat .n { font-size: 27px; font-weight: 660; font-variant-numeric: tabular-nums; line-height: 1.1; }
.res-stat .l { font-size: 12px; color: var(--ink-soft); margin-top: 4px; }
.res-stat .sub { font-size: 12px; color: var(--ink-faint); margin-top: 6px; line-height: 1.4; }
.res-stat .sub a { font-size: 12px; }
.res-stat.accent { border-color: var(--accent); background: var(--accent-soft); }
.res-stat.accent .n, .res-stat.accent .l { color: var(--accent-ink); }
.res-stat.accent .sub { color: var(--accent-ink); opacity: .85; }
.res-stat.clickable { cursor: pointer; }
.res-stat.clickable[aria-expanded="true"] { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft), var(--shadow-lift); }
@media (max-width: 760px) { .res-stats { grid-template-columns: repeat(2, 1fr); } .res-toolbar { justify-content: space-between; } }

/* -- Agent results widget -- */
.res-results-widget { margin-bottom: 22px; }
.res-results-head { padding: 14px 18px 12px; border-bottom: 1px solid var(--line); }
.res-results-head h3 { font-size: 15px; font-weight: 640; letter-spacing: -.01em; margin: 0 0 2px; }
.res-results-head p { font-size: 12.5px; color: var(--ink-faint); margin: 0; }
.res-results-body { padding: 18px; display: flex; flex-direction: column; gap: 24px; }
.res-block-label { font-size: 11.5px; font-weight: 640; letter-spacing: .04em; text-transform: uppercase;
                    color: var(--ink-soft); margin: 0 0 12px; }

.res-rb-chart { display: flex; align-items: flex-end; gap: 5px; height: 132px; }
.res-rb-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; min-width: 0; }
.res-rb-track { width: 62%; display: flex; align-items: flex-end; justify-content: center; height: 100px; }
.res-rb-bar { width: 100%; border-radius: 4px 4px 2px 2px; background: color-mix(in srgb, var(--accent) 14%, var(--surface-2));
              display: flex; align-items: flex-end; }
.res-rb-fill { width: 100%; background: var(--accent); border-radius: 4px 4px 0 0; }
.res-rb-col[data-empty="true"] .res-rb-bar { background: var(--surface-2); }
.res-rb-day { font-size: 11px; color: var(--ink-faint); margin-top: 6px; text-align: center; }
.res-rb-legend { display: flex; align-items: center; gap: 16px; margin-top: 12px; font-size: 11.5px; color: var(--ink-soft); }
.res-rb-legend-item { display: flex; align-items: center; gap: 6px; }
.res-rb-swatch { width: 10px; height: 10px; border-radius: 2.5px; display: inline-block; }
.res-rb-swatch.cancelled { background: color-mix(in srgb, var(--accent) 14%, var(--surface-2)); border: 1px solid var(--line-strong); }
.res-rb-swatch.refilled { background: var(--accent); }

.res-conv-strip { display: flex; align-items: stretch; gap: 8px; flex-wrap: wrap; }
.res-conv-step { flex: 1 1 120px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
                  padding: 12px 14px; text-align: center; min-width: 0; }
.res-conv-step .cs-n { font-size: 21px; font-weight: 660; font-variant-numeric: tabular-nums; color: var(--ink); }
.res-conv-step .cs-l { font-size: 11.5px; color: var(--ink-soft); margin-top: 3px; }
.res-conv-arrow { flex: none; display: flex; align-items: center; justify-content: center; color: var(--ink-faint); font-size: 14px; }

.res-source-table { display: flex; flex-direction: column; gap: 10px; }
.res-source-row { display: flex; align-items: center; gap: 12px; }
.res-source-label { flex: 0 0 168px; font-size: 12.5px; color: var(--ink); }
.res-source-track { flex: 1 1 auto; height: 8px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.res-source-fill { height: 100%; background: var(--accent); border-radius: 999px; }
.res-source-n { flex: 0 0 auto; min-width: 20px; text-align: right; font-size: 12.5px; font-weight: 640;
                font-variant-numeric: tabular-nums; color: var(--ink); }
@media (max-width: 760px) { .res-source-label { flex-basis: 128px; font-size: 12px; } }

.res-trust-lines { display: flex; flex-direction: column; gap: 8px; }
.res-trust-row { font-size: 12.5px; color: var(--ink); }
.res-time-saved-line { font-size: 12.5px; color: var(--ink); background: var(--accent-soft); border-radius: 8px;
                        padding: 10px 14px; line-height: 1.5; }
.res-time-saved-line b { color: var(--accent-ink); font-weight: 660; }
.res-ts-note { color: var(--ink-soft); }

/* -- Cancellations drill-down panel (Results' one surviving stat detail) -- */
.stat-detail { border: 1px solid var(--line); border-radius: 10px; background: var(--ground); margin-bottom: 22px; overflow: hidden; }
.sd-head { display: flex; align-items: baseline; gap: 8px; padding: 11px 16px; border-bottom: 1px solid var(--line); background: var(--surface); }
.sd-head h3 { font-size: 12.5px; font-weight: 640; letter-spacing: .04em; text-transform: uppercase; color: var(--ink-soft); margin: 0; flex: 1; }
.sd-close { border: 0; background: none; color: var(--ink-faint); cursor: pointer; font-size: 17px; line-height: 1;
            padding: 2px 4px; border-radius: var(--radius-sm); }
.sd-close:hover { background: var(--surface-2); color: var(--ink); }
.sd-list { display: flex; flex-direction: column; max-height: 340px; overflow-y: auto; }
.sd-row { padding: 10px 16px; border-top: 1px solid var(--line); font-size: 12.5px; line-height: 1.5; }
.sd-row:first-child { border-top: 0; }
.sd-row b { font-weight: 620; color: var(--ink); }
.sd-row .sd-sub { color: var(--ink-soft); }
.sd-empty { padding: 14px 16px; font-size: 12.5px; color: var(--ink-faint); }
}
