/* studio.css — Smile Studio premium medical/film FUI theme (Phase 1).
   Standalone, no Tailwind build. Served at /sim-static/sim/studio.css.
   Dark is the default (staff surface); [data-theme="light"] overrides for
   patient/lead carry (later phase). Aesthetic: restrained graphite/near-black
   base, disciplined PURPLE brand accent used sparingly, thin surgical-white +
   muted-cyan measurement lines, translucent glass panels, generous negative
   space, small mono technical type, slow precise motion. No neon, no random
   grids, no spinners, no meaningless percentages. */

/* ---------- design tokens: dark (default) ---------- */
:root,
[data-theme="dark"] {
  --bg: #0a0b0d;
  --bg-elev: #101216;
  --panel: rgba(20, 22, 27, 0.72);
  --panel-solid: #14161b;
  --panel-border: rgba(255, 255, 255, 0.08);
  --hairline: rgba(255, 255, 255, 0.12);   /* thin surgical-white line */
  --measure: rgba(120, 210, 230, 0.55);    /* muted cyan measurement line */
  --text: #e7e9ee;
  --text-dim: #9aa0ab;
  --text-faint: #6b7180;
  --accent: #8b5cf6;                         /* disciplined purple */
  --accent-strong: #a78bfa;
  --accent-soft: rgba(139, 92, 246, 0.16);
  --danger: #e0696b;
  --warn: #d8a657;
  --ok: #6fc28a;
  --radius: 14px;
  --radius-sm: 10px;
  --shadow: 0 18px 50px rgba(0, 0, 0, 0.55);
  --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --sans: "Sora", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
}

/* ---------- design tokens: light override ---------- */
[data-theme="light"] {
  --bg: #f4f5f7;
  --bg-elev: #ffffff;
  --panel: rgba(255, 255, 255, 0.86);
  --panel-solid: #ffffff;
  --panel-border: rgba(15, 18, 25, 0.10);
  --hairline: rgba(15, 18, 25, 0.14);
  --measure: rgba(20, 130, 150, 0.55);
  --text: #14161b;
  --text-dim: #4c5360;
  --text-faint: #828a99;
  --accent: #7c3aed;
  --accent-strong: #6d28d9;
  --accent-soft: rgba(124, 58, 237, 0.12);
  --shadow: 0 18px 50px rgba(15, 18, 25, 0.14);
}

* { box-sizing: border-box; }

body.smile-studio {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  letter-spacing: 0.01em;
  min-height: 100vh;
  /* Leave room for the fixed bottom command bar. The bar wraps to 2+ rows in
     portrait, so its height is NOT constant — a fixed reservation (was 132px)
     let the wrapped bar overlap page content. JS (studio.js measureCommandBar)
     observes the live bar height and writes --cmdbar-h; we fall back to 132px
     before the first measurement and add the safe-area inset. */
  padding-bottom: calc(var(--cmdbar-h, 132px) + 16px + env(safe-area-inset-bottom, 0px));
}
/* secondary staff pages (login/history) have no fixed command bar */
body.smile-studio.no-cmdbar { padding-bottom: 0; }

/* PWA standalone safe-area: with viewport-fit=cover the webview extends under
   the iOS status bar / notch, so the top header (and side-of-screen content in
   landscape) must inset by the safe-area. Header base padding is Tailwind px-6
   py-3 (24px / 12px); we add the insets on top. Portrait + landscape both. */
body.smile-studio > header {
  padding-top: calc(12px + env(safe-area-inset-top, 0px));
  padding-left: calc(24px + env(safe-area-inset-left, 0px));
  padding-right: calc(24px + env(safe-area-inset-right, 0px));
  /* Item 1: keep the top bar (logo / search / settings) pinned while the page
     scrolls. z-30 sits ABOVE page content but BELOW the slide-over scrims (z-40)
     so opening a drawer dims the header too. An opaque, theme-aware background is
     required — the header has no fill of its own, so scrolled content would
     otherwise bleed through. */
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--bg);
}

.smile-studio main { display: block; }

[x-cloak] { display: none !important; }

/* ===== Branded launch splash / preloader (dashboard) =====
   Real CSS (NOT arbitrary Tailwind — those classes are absent from the prebuilt
   output.css). Fixed, full-screen, ABOVE everything (header z-30, modals z-210). */
.app-splash {
  position: fixed;
  inset: 0;
  z-index: 12000;
  background: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
}
.app-splash__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;          /* FULL coverage — crop the side margins, no white edges */
  object-position: center;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: none;
}
.app-splash__ver {
  position: absolute;
  top: calc(12px + env(safe-area-inset-top, 0px));
  right: calc(14px + env(safe-area-inset-right, 0px));
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: #2563eb;             /* brand blue */
  opacity: 0.85;
  z-index: 2;
}
/* Frosted "command" pill — premium medical-AI GLASS (replaces the old 3-colour gradient).
   A deep-navy translucent panel that BLURS the splash behind it (backdrop-filter), a crisp
   light top-edge highlight, a faint brand-blue ring, layered depth shadow, and a slow
   luminous sheen that sweeps across (high-tech cue). The label sits above the sheen. */
.app-splash__start {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 210px;
  min-height: 54px;            /* ≥44px touch target */
  padding: 0 36px;
  border: 1px solid rgba(255,255,255,0.40);
  border-radius: 16px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Richer, deeper blues pulled from the splash art (blue-950 → blue-900 → blue-700),
     near-opaque so the button reads as a solid premium control instead of a washed
     translucent panel (owner: "richer & darker than what you have"). */
  background: linear-gradient(160deg, rgba(23,37,84,0.96) 0%, rgba(30,58,138,0.94) 52%, rgba(29,78,216,0.92) 100%);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
  backdrop-filter: blur(16px) saturate(1.4);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.55),
    inset 0 -1px 0 rgba(8,16,40,0.30),
    0 0 0 1px rgba(37,99,235,0.18),
    0 20px 44px -14px rgba(15,30,80,0.58),
    0 3px 12px rgba(2,6,23,0.20);
  transition: transform 140ms ease, box-shadow 220ms ease, border-color 220ms ease;
}
/* luminous diagonal sheen sweeping across the glass; sits ABOVE the panel, BELOW the label */
.app-splash__start::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(112deg, transparent 30%, rgba(255,255,255,0.42) 47%, rgba(125,211,252,0.30) 53%, transparent 66%);
  transform: translateX(-130%);
  animation: splashSheen 3.6s cubic-bezier(.45,0,.2,1) infinite;
}
.app-splash__start-label {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 700;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: #f4f9ff;
  text-shadow: 0 1px 2px rgba(8,16,40,0.35);
}
.app-splash__start-label::after {
  content: "\203A";             /* › "go" chevron */
  font-size: 18px; line-height: 1; opacity: 0.85;
  transition: transform 160ms ease;
}
.app-splash__start:hover {
  transform: translateY(-1px);
  border-color: rgba(255,255,255,0.62);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.62),
    inset 0 -1px 0 rgba(8,16,40,0.30),
    0 0 0 1px rgba(37,99,235,0.30),
    0 26px 54px -14px rgba(15,30,80,0.66),
    0 3px 12px rgba(2,6,23,0.22);
}
.app-splash__start:hover .app-splash__start-label::after { transform: translateX(3px); }
.app-splash__start:active { transform: translateY(1px) scale(0.992); }
@keyframes splashSheen { 0%, 18% { transform: translateX(-130%); } 60%, 100% { transform: translateX(130%); } }
@media (prefers-reduced-motion: reduce) { .app-splash__start::before { animation: none; } }
.app-splash__bar-wrap {
  position: absolute;
  left: 50%;
  /* Centered on the middle "AI Smile Design" card, then nudged ~12px LEFT (owner). */
  transform: translateX(calc(-50% - 12px));
  /* PORTRAIT: sit in the GAP between the model's mouth (~37% from bottom) and the feature
     cards (~20% from bottom) so START never covers the mouth or the AI Smile Design card.
     27% centered the gap; owner nudged it down a total of 25px to sit lower in the gap. */
  bottom: calc(27% - 25px);
  width: min(420px, 78vw);
  text-align: center;
  z-index: 1;
}
/* LANDSCAPE splash (Splash1-L): title is upper-left, the face is on the right, and the 3
   feature cards run along the bottom-centre — so START drops into the clear strip BELOW
   the cards, centred, narrower. (Tunable — nudge `bottom` if a device's cover-crop shifts
   the cards.) */
@media (orientation: landscape) {
  /* LANDSCAPE (owner's view): float START ~20px ABOVE the TOP of the middle "AI Smile
     Design" card instead of below the card row. The card top sits ~34% from the bottom
     in the cover-cropped 4:3 landscape art, so bottom = that + ~18px clears it. */
  .app-splash__bar-wrap { bottom: calc(34% + 18px); width: min(340px, 52vw); }
}
.app-splash__track {
  height: 6px;
  width: 100%;
  border-radius: 999px;
  background: rgba(15, 23, 42, 0.12);
  overflow: hidden;
}
.app-splash__fill {
  height: 100%;
  width: 0%;
  border-radius: 999px;
  background: linear-gradient(90deg, #3b82f6, #8b5cf6);
  transition: width 160ms ease-out;
}
.app-splash__status {
  margin-top: 9px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: #64748b;
}

/* (Retired dead prototype top-bar CSS: .studio-topbar / .studio-brand /
   .studio-topbar-actions — 0 template/JS references, verified 2026-06-01.) */

/* ---------- co-brand lockup (client logo + "Powered by NPW") ----------
   Client logo is the primary mark (top-left); New Patient Wizard is a quiet
   "Powered by" credit. The client logo art is theme-specific (white text for the
   dark UI, blue text for light), so swap the two PNGs on the <html> data-theme
   (this app toggles data-theme, NOT a Tailwind .dark class). Default theme is
   dark, so the light variant is hidden until [data-theme="light"]. */
.brand-logo--light { display: none; }
[data-theme="light"] .brand-logo--dark  { display: none; }
[data-theme="light"] .brand-logo--light { display: inline-block; }
.powered-by { white-space: nowrap; }

/* ---------- generic glass panel ---------- */
.glass {
  background: var(--panel);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 48px;
  min-width: 48px;
  padding: 0 18px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--hairline);
  background: var(--panel-solid);
  color: var(--text);
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 160ms ease, background 160ms ease, color 160ms ease;
}
.btn:hover { border-color: var(--accent); }
.btn:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.btn-primary {
  background: linear-gradient(135deg, #7c6fb0, #6d28d9);
  border-color: transparent;
  color: #fff;
  box-shadow: 0 0 24px -8px var(--accent-strong);
}
.btn-primary:hover { box-shadow: 0 0 32px -6px var(--accent-strong); transform: translateY(-1px); }
/* green "Send to patient" — the canonical share CTA, promoted into the dock
   (Pain 3). Kept green (not purple .btn-primary) so the share action stays
   distinct. Resting bg is green-700 (#15803d): white-on-#15803d = 5.02:1, which
   passes WCAG AA for the 14px label (green-600 #16a34a was only 3.30:1 — fails). */
.btn-send {
  background: #15803d; /* green-700 — 5.02:1 with #fff */
  border-color: transparent;
  color: #fff;
  font-weight: 600;
  box-shadow: 0 0 24px -10px #15803d;
}
.btn-send:hover { background: #166534; /* green-800 */ border-color: transparent; }
.btn-send:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-ghost { background: transparent; }
.btn-icon { padding: 0; width: 48px; }
.btn .icon { width: 18px; height: 18px; display: inline-block; }
.btn .icon svg { width: 100%; height: 100%; display: block; }

/* icon helper baseline (inline SVG, no emoji) */
.icon { display: inline-flex; width: 18px; height: 18px; }
.icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; }
.icon-ok   { color: var(--ok); }
.icon-warn { color: var(--warn); }
.icon-x    { color: var(--danger); }

/* NOTE: the FUI prototype classes .studio-hero / .studio-pane / .video-col
   (and .studio-topbar above) were RETIRED here — the live staff page uses a
   Tailwind single-column flow + .split-stage/.cmp-split-grid for Split, not
   these. Grep across all templates/JS = 0 references (verified 2026-06-01).
   The live Split-mode CSS lives further down (.split-stage / .split-handle). */

/* ---------- bottom command bar (§11.2) ---------- */
.command-bar {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  width: min(1180px, calc(100vw - 24px - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)));
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  z-index: 40;
  box-shadow: var(--shadow);
}
.command-bar .cb-group { display: flex; align-items: center; gap: 8px; }
.command-bar .cb-spacer { flex: 1 1 auto; }

/* Export / Share popover (Pain 1) — anchored above the dock's Export button.
   z-index 60 sits above the bar (40) and below cinema (200). */
.cb-export { position: relative; }
.export-pop {
  position: absolute;
  bottom: calc(100% + 8px);
  right: 0;
  min-width: 248px;
  max-width: min(320px, calc(100vw - 24px));
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  z-index: 60;
  box-shadow: var(--shadow);
}
.export-pop .btn { width: 100%; justify-content: flex-start; min-height: 44px; }

/* ---------- results / filmstrip rail (Pain 4) ----------
   Horizontal strip of video tiles surfaced directly under the stage so renders
   are discoverable without scrolling to the bottom of the page. Each provider
   tile flips: idle trigger → live progress → play-poster + ready badge. */
.results-rail {
  display: flex;
  gap: 10px;
  overflow-x: auto;
  padding: 4px 2px 10px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}
.rail-tile { position: relative; flex: 0 0 auto; width: 150px; }
.rail-thumb {
  position: relative; display: block; width: 100%; aspect-ratio: 4 / 3;
  border-radius: 12px; overflow: hidden; padding: 0;
  border: 1px solid var(--hairline); background: var(--bg-elev); cursor: pointer;
}
.rail-thumb:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.rail-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rail-trigger {
  width: 100%; aspect-ratio: 4 / 3; min-height: 96px;
  border-radius: 12px; border: 1px dashed var(--hairline);
  background: var(--panel-solid); color: var(--text-dim);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; font-size: 12px; font-weight: 600; cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease;
}
.rail-trigger:hover { border-color: var(--accent); color: var(--text); }
.rail-trigger:disabled { cursor: progress; }
.rail-trigger .icon { width: 22px; height: 22px; }
/* status badge over a tile — opaque pill + light ring keeps ≥3:1 non-text
   contrast against any underlying poster. */
.rail-badge {
  position: absolute; left: 8px; bottom: 8px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
  padding: 3px 8px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.66); color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.35);
}
.rail-badge--ready { background: #16a34a; border-color: rgba(255, 255, 255, 0.5); }
.rail-play {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: #fff; pointer-events: none;
}
.rail-play svg { width: 34px; height: 34px; filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6)); }
.rail-prog {
  position: absolute; left: 8px; right: 8px; bottom: 8px;
  background: rgba(0, 0, 0, 0.55); border-radius: 999px; padding: 4px 8px;
}
.rail-prog-track { height: 4px; border-radius: 999px; background: rgba(255,255,255,0.3); overflow: hidden; }
.rail-prog-fill { height: 100%; background: var(--accent-strong); border-radius: 999px; transition: width 300ms ease; }
.rail-label { margin-top: 6px; font-size: 11px; color: var(--text-dim); text-align: center; }

/* segmented compare toggle (Before / Split / After) */
.segmented {
  display: inline-flex;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--panel-solid);
}
.segmented button {
  min-height: 48px;
  min-width: 64px;
  padding: 0 14px;
  border: 0;
  background: transparent;
  color: var(--text-dim);
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.segmented button + button { border-left: 1px solid var(--hairline); }
.segmented button[aria-pressed="true"],
.segmented button.active {
  background: var(--accent-soft);
  color: var(--accent-strong);
}

/* Iconed variant (item 11): SVG over a small caption, replacing the plain text
   labels for the Before&After / Split compare toggle. */
.segmented.seg-iconed button {
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  padding: 6px 12px;
  letter-spacing: 0.04em;
  display: inline-flex;
}
.segmented.seg-iconed .seg-ico { display: inline-flex; }
.segmented.seg-iconed .seg-ico svg { width: 18px; height: 18px; display: block; }
.segmented.seg-iconed .seg-txt { font-size: 9.5px; line-height: 1; }

/* compact zoom-link toggle (command bar) — icon-only, ~36px, no emoji */
.zoom-link-btn {
  margin-left: 8px;
  min-width: 36px;
  width: 36px;
  min-height: 36px;
  height: 36px;
  padding: 0;
  border-radius: 999px;
  color: var(--text-dim);
}
.zoom-link-btn .icon { width: 18px; height: 18px; }
.zoom-link-btn .icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; display: block; }
.zoom-link-btn:hover { border-color: var(--accent); color: var(--text); }
.zoom-link-btn.active,
.zoom-link-btn[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-strong);
}
.zoom-link-btn:disabled { opacity: 0.55; cursor: default; }
.zoom-link-btn:disabled:hover { border-color: var(--hairline); }

/* horizontal shade rail */
.shade-rail-h {
  display: flex;
  align-items: center;
  gap: 10px;
}
.shade-rail-h .rail-notch {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  min-height: 48px;
  min-width: 48px;
  padding: 4px 6px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: border-color 140ms ease, color 140ms ease;
}
.shade-rail-h .rail-notch .tooth-swatch {
  width: 26px;
  height: 26px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
  transition: transform 140ms ease;
}
.shade-rail-h .rail-notch .tooth-swatch svg { width: 22px; height: 24px; display: block; }
.shade-rail-h .rail-notch .tooth-swatch path {
  fill: var(--tooth-fill, #cccccc);          /* shade colour fills the tooth crown */
  stroke: var(--hairline);                   /* thin enamel hairline for light shades */
  stroke-width: 0.6;
  vector-effect: non-scaling-stroke;
  transition: fill 160ms ease;
}
.shade-rail-h .rail-notch.active .tooth-swatch { transform: scale(1.12); }
.shade-rail-h .rail-notch.active .tooth-swatch path {
  stroke: var(--accent-strong);
  stroke-width: 1.1;
}
.shade-rail-h .rail-notch .lbl {
  font-family: var(--mono);
  font-size: 10px;
  letter-spacing: 0.06em;
}
.shade-rail-h .rail-notch.active {
  border-color: var(--accent);
  color: var(--accent-strong);
}

/* shade-opacity control — toggle button + vertical slider popover (FUI).
   The toggle is now a borderless .mask-notch (item: match Mask/Brush; the icon span is
   .mask-ico so its 26×26 size + the active background/border come from .mask-notch). Only
   the glyph color (dim → accent on active) is kept here. */
.opacity-control { position: relative; display: inline-flex; align-items: center; margin-left: 8px; }
.opacity-toggle .opacity-glyph { color: var(--text-faint); transition: color 160ms ease; }
.opacity-toggle.active .opacity-glyph,
.opacity-toggle[aria-pressed="true"] .opacity-glyph { color: var(--accent-strong); }
/* Opacity is meaningless with NO shade ("None"/transparent), so it's disabled there
   (item: gray it out until a real shade is picked). Native [disabled] gray + no-tap. */
.opacity-toggle:disabled { opacity: 0.4; cursor: not-allowed; }
.opacity-toggle:disabled .opacity-glyph { color: var(--text-faint); }
.opacity-pop {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 14px 12px 12px;
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
  background: var(--panel-solid);
  box-shadow: var(--shadow);
  z-index: 60;
}
.opacity-pop::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 6px solid transparent;
  border-top-color: var(--panel-solid);
}
.opacity-readout {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--accent-strong);
  min-width: 34px;
  text-align: center;
}
.opacity-range {
  -webkit-appearance: slider-vertical;
  appearance: slider-vertical;
  writing-mode: vertical-lr;
  direction: rtl;                 /* 100% at top, 0% at bottom */
  width: 26px;
  height: 132px;
  margin: 0;
  background: transparent;
  cursor: pointer;
  touch-action: none;             /* our @touchmove drives the value — never scroll/pan */
}
.opacity-range:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 4px; }
.opacity-range::-webkit-slider-runnable-track {
  width: 6px; height: 100%; border-radius: 999px;
  background: linear-gradient(to top, var(--accent-soft), var(--accent));
  border: 1px solid var(--hairline);
}
.opacity-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; margin-left: -7px;
  border-radius: 50%; background: var(--text);
  border: 2px solid var(--accent-strong);
  box-shadow: 0 0 10px -2px var(--accent-strong);
}
.opacity-range::-moz-range-track {
  width: 6px; height: 100%; border-radius: 999px;
  background: linear-gradient(to top, var(--accent-soft), var(--accent));
  border: 1px solid var(--hairline);
}
.opacity-range::-moz-range-thumb {
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--text); border: 2px solid var(--accent-strong);
  box-shadow: 0 0 10px -2px var(--accent-strong);
}

/* variant stepper */
.variant-stepper {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.variant-stepper .step-btn {
  min-height: 48px;
  min-width: 48px;
}
.variant-stepper .variant-label {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text);
  min-width: 54px;
  text-align: center;
}

/* Item 10 — two-button Lab selector (replaces the chevron stepper in the dock). */
.lab-selector { display: inline-flex; align-items: stretch; gap: 8px; }
.lab-btn {
  display: inline-flex;
  flex-direction: row;          /* compact: icon + label on one line, ~30px tall */
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 30px;
  min-width: 60px;
  padding: 4px 10px;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--panel-solid);
  color: var(--text-dim);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, border-color 140ms ease, opacity 140ms ease;
}
.lab-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--text); }
.lab-btn.active {
  background: var(--accent-soft);
  border-color: var(--accent-strong);
  color: var(--accent-strong);
}
.lab-btn-name { font-family: var(--mono); font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; line-height: 1.1; }
.lab-btn-state { font-size: 8.5px; letter-spacing: 0.02em; opacity: 0.8; line-height: 1; margin-left: 4px; }
.lab-btn.processing { opacity: 0.55; cursor: progress; }
.lab-btn.processing .lab-btn-state { color: var(--accent-strong); animation: labpulse 1.4s ease-in-out infinite; }
.lab-btn.failed { opacity: 0.5; cursor: not-allowed; }
.lab-btn:disabled { cursor: not-allowed; }
@keyframes labpulse { 0%,100% { opacity: 0.5; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .lab-btn.processing .lab-btn-state { animation: none; } }

/* Video + Regenerate lab buttons (item 10/11) — icon + small name, same compact chip. */
.lab-btn--video, .lab-btn--regen { gap: 5px; }
.lab-btn .lab-vico { display: inline-flex; }
.lab-btn .lab-vico svg { width: 14px; height: 14px; stroke: currentColor; fill: none; }
.lab-btn--regen { color: var(--text-dim); }
.lab-btn--regen:hover:not(:disabled) { border-color: var(--accent); color: var(--text); }
.lab-btn--video.active { border-color: var(--accent-strong); background: var(--accent-soft); color: var(--accent-strong); }

/* ---------- Per-lab version arrows (overlay on the AFTER image) ----------
   Step v1/v2/… within the current lab. Container is click-through (pointer-events
   none) so taps between the arrows still open Present; only the buttons + badge are
   interactive/visible. z-index 5 sits above the canvas, below modals/cinema. */
.ver-arrows { position: absolute; inset: 0; z-index: 5; pointer-events: none; }
.ver-arrow {
  position: absolute; top: 50%; transform: translateY(-50%);
  pointer-events: auto;
  width: 42px; height: 42px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(10,11,13,0.5); color: #fff;
  border: 1px solid rgba(255,255,255,0.25);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  box-shadow: 0 4px 14px rgba(0,0,0,0.35); cursor: pointer;
  transition: background 140ms ease;
}
.ver-arrow:hover  { background: rgba(10,11,13,0.65); }
.ver-arrow:active { background: rgba(10,11,13,0.8); }
.ver-arrow svg { width: 22px; height: 22px; }
.ver-arrow--prev { left: 10px; }
.ver-arrow--next { right: 10px; }
.ver-badge {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
  pointer-events: none;
  padding: 3px 11px; border-radius: 999px;
  background: rgba(10,11,13,0.55); color: #fff;
  font-family: var(--mono); font-size: 12px; letter-spacing: 0.04em;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}

/* Full-screen button overlaid on the video preview (top-right of the after pane). */
.video-fs-btn {
  position: absolute; top: 12px; right: 12px; z-index: 4;
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 11px;
  border: 1px solid rgba(255,255,255,0.25);
  background: rgba(10,11,13,0.45); color: #fff;
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,0.35);
}
.video-fs-btn:hover { background: rgba(10,11,13,0.65); }
.video-fs-btn .gico svg { width: 18px; height: 18px; stroke: currentColor; fill: none; }

/* ---------- bottom-sheet (secondary actions / overflow) ---------- */
.sheet-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 50;
}
.bottom-sheet {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 51;
  padding: 18px 18px calc(18px + env(safe-area-inset-bottom));
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  background: var(--panel-solid);
  border-top: 1px solid var(--hairline);
  box-shadow: var(--shadow);
}
.bottom-sheet .sheet-grip {
  width: 40px; height: 4px; border-radius: 999px;
  background: var(--hairline); margin: 0 auto 14px;
}
.bottom-sheet .sheet-actions {
  display: grid; gap: 10px;
}
.bottom-sheet .sheet-actions .btn { width: 100%; justify-content: flex-start; }

/* ---------- Brush re-tap confirm (apply / discard unsaved mask) ----------
   Shown when staff tap the Brush tool again while the editor has unsaved strokes.
   z-index 2100 sits above the full-screen brush layout AND Present (200). */
.brush-confirm-backdrop {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 2100;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
.brush-confirm-card {
  width: min(360px, 100%);
  border-radius: 18px;
  background: var(--panel-solid);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow);
  padding: 22px 20px 18px;
  text-align: center;
}
.brush-confirm-card h3 {
  margin: 0 0 6px;
  font-size: 17px; font-weight: 700; color: var(--text);
}
.brush-confirm-card p {
  margin: 0 0 18px;
  font-size: 14px; line-height: 1.4; color: var(--text-dim);
}
.brush-confirm-actions { display: grid; gap: 10px; }
.brush-confirm-actions .btn { width: 100%; justify-content: center; min-height: 46px; }

/* ---------- progress (slim, no spinner) ---------- */
.progress-bar {
  height: 3px;
  width: 100%;
  background: var(--hairline);
  border-radius: 999px;
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  background: var(--accent);
  transition: width 400ms ease;
}
.progress-label {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--text-dim);
  letter-spacing: 0.06em;
}

/* ---------- Smile Report drawer ---------- */
.report-drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(420px, 92vw);
  z-index: 60;
  padding: 22px;
  overflow-y: auto;
  background: var(--panel-solid);
  border-left: 1px solid var(--hairline);
  box-shadow: var(--shadow);
}
.report-drawer h2 {
  font-family: var(--mono);
  font-size: 14px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin: 4px 0 18px;
}
.report-drawer .drawer-close {
  position: absolute; top: 16px; right: 16px;
}
.consensus-badge {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid var(--hairline);
  color: var(--text-dim);
  background: var(--accent-soft);
}
.report-section { margin-top: 20px; }
.report-section h3 {
  font-size: 13px; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.1em;
  margin: 0 0 10px;
}
.report-section ul { list-style: none; margin: 0; padding: 0; }
.report-section li {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 8px 0;
  border-bottom: 1px solid var(--hairline);
  font-size: 14px; color: var(--text);
}

/* ---------- archetype cards ---------- */
.archetype-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 12px;
}
.archetype-card {
  border: 1px solid var(--panel-border);
  border-radius: var(--radius-sm);
  padding: 14px;
  background: var(--bg-elev);
  cursor: pointer;
  transition: border-color 140ms ease;
}
.archetype-card.active { border-color: var(--accent); }

/* ---------- history / login (theme parity) ---------- */
.studio-card {
  background: var(--panel-solid);
  border: 1px solid var(--panel-border);
  border-radius: var(--radius);
  padding: 22px;
}

/* ====================================================================
   Cinema / Reveal presentation mode (§11.3) — ADDITIVE full-screen overlay.
   Forced dark regardless of theme. Stacks the before <img> + a one-time PNG
   snapshot of the live result canvas; reuses the dormant baSlider state/handlers.
   ==================================================================== */
.cinema {
  position: fixed;
  inset: 0;
  z-index: 200;                 /* above command-bar (40), sheets (60) */
  background: #0a0b0d;          /* forced dark (graphite near-black) */
  color: #f5f6f8;
  display: flex;
  flex-direction: column;
  overscroll-behavior: contain;
}
.cinema-stage {
  position: relative;
  flex: 1 1 auto;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  touch-action: none;           /* let drag own the gesture */
  cursor: ew-resize;
  user-select: none;
}
.cinema-before,
.cinema-after {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  pointer-events: none;
}
/* after layer is clipped from the right by (100 - ba)% so it reveals the
   right-hand "after" portion as the handle moves; --ba is bound to baSliderPct */
.cinema-after {
  clip-path: inset(0 calc(100% - var(--ba, 50) * 1%) 0 0);
}
/* Suppress iOS Safari's native long-press selection callout/magnifier across the
   cinema overlay so the in-app loupe (bound to .cinema-stage) is the only response. */
.cinema-stage, .cinema-before, .cinema-after {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
/* ---------- premium FUI split-reveal handle (shared by cinema + main) ----------
   Thin luminous divider (surgical-white core + soft accent glow) + a centered
   glass grip puck with left/right chevron affordances. */
.cinema-handle,
.split-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--ba, 50) * 1%);
  width: 2px;
  transform: translateX(-1px);
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(255, 255, 255, 0.0) 0%,
    rgba(255, 255, 255, 0.92) 12%,
    rgba(255, 255, 255, 0.92) 88%,
    rgba(255, 255, 255, 0.0) 100%);
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.30),
    0 0 10px 0 rgba(167, 139, 250, 0.45);
}
.cinema-handle { z-index: 2; }
.split-handle  { z-index: 6; }
.cinema-handle-grip,
.split-handle-grip {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 46px;
  height: 46px;
  transform: translate(-50%, -50%);
  border-radius: 999px;
  background: rgba(16, 18, 22, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.30);
  backdrop-filter: blur(10px) saturate(1.2);
  -webkit-backdrop-filter: blur(10px) saturate(1.2);
  box-shadow:
    0 6px 18px rgba(0, 0, 0, 0.50),
    inset 0 1px 0 rgba(255, 255, 255, 0.18),
    0 0 0 4px rgba(139, 92, 246, 0.12);
  pointer-events: none;
}
.cinema-handle-grip::before,
.cinema-handle-grip::after,
.split-handle-grip::before,
.split-handle-grip::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 7px;
  height: 7px;
  border: solid rgba(255, 255, 255, 0.92);
  border-width: 0 0 2px 2px;
}
.cinema-handle-grip::before,
.split-handle-grip::before { left: 13px; transform: translateY(-50%) rotate(45deg); }
.cinema-handle-grip::after,
.split-handle-grip::after { right: 13px; transform: translateY(-50%) rotate(-135deg); }

/* Exit/Done — top-LEFT, safe-area padded, modern glass pill (keeps the thumb off
   the iPad right-edge gesture zone + away from the reveal grip). */
.cinema-exit {
  position: absolute;
  top: calc(14px + env(safe-area-inset-top, 0px));
  left: calc(14px + env(safe-area-inset-left, 0px));
  z-index: 30;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  padding: 0 16px 0 13px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  background: rgba(16, 18, 22, 0.55);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  color: #f5f6f8;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.01em;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  cursor: pointer;
  transition: border-color 160ms ease, background 160ms ease, transform 120ms ease;
}
.cinema-exit:hover { border-color: var(--accent); background: rgba(22, 24, 30, 0.62); }
.cinema-exit:active { transform: scale(0.97); }
.cinema-exit:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.cinema-exit .icon { width: 18px; height: 18px; display: inline-flex; }
.cinema-exit .icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; }
.cinema-exit-label { line-height: 1; }
@media (max-width: 380px) {
  .cinema-exit { padding: 0; width: 44px; justify-content: center; }
  .cinema-exit-label { display: none; }
}
.cinema-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
  z-index: 1;
}
.cinema-controls {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 14px;
  padding: 16px 18px calc(16px + env(safe-area-inset-bottom, 0px));
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(8px);
}
.cinema-controls .segmented button {
  min-height: 48px;
  min-width: 84px;
  color: #f5f6f8;
}
.cinema-controls .segmented button.active {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.cinema-shade { display: flex; align-items: center; gap: 8px; }
.cinema-shade .rail-notch {
  min-width: 48px;
  min-height: 48px;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.05);
  color: #e9eaee;
  cursor: pointer;
  padding: 4px 6px;
  font-size: 11px;
}
.cinema-shade .rail-notch.active { border-color: var(--accent-strong); background: var(--accent-soft); }
.cinema-shade .rail-notch .swatch {
  width: 22px; height: 22px; border-radius: 6px;
  border: 1px solid rgba(0, 0, 0, 0.25);
}
.cinema-variant { display: flex; align-items: center; gap: 8px; }
.cinema-variant .step-btn {
  width: 48px; height: 48px;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.05);
  color: #f5f6f8;
  cursor: pointer;
}
.cinema-variant .step-btn .icon svg,
.cinema-variant .step-btn svg { width: 20px; height: 20px; stroke: currentColor; fill: none; }
.cinema-variant .variant-label { min-width: 56px; text-align: center; font-weight: 600; }
.cinema-controls .btn {
  min-height: 48px;
  color: #f5f6f8;
  border-color: rgba(255, 255, 255, 0.22);
  background: rgba(255, 255, 255, 0.06);
}
.cinema-controls .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.cinema-controls .btn .icon svg { width: 18px; height: 18px; stroke: currentColor; fill: none; }

/* ====== Present-mode control drawer (Feature A) ======
   Thin purple tab docked to the right edge of the Present overlay; tapping it slides a
   compact panel in/out (shade / mask / brush / version). Lives inside .cinema (z-index
   stacking context), positioned above the stage. Hidden in landscape (rotate prompt). */
.cinema-drawer {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  z-index: 36;                 /* above the stage; near the exit pill */
  display: flex;
  align-items: center;
  padding-right: env(safe-area-inset-right, 0px);
  pointer-events: none;        /* only the tab + panel are interactive */
}
.cinema-drawer-tab,
.cinema-drawer-panel { pointer-events: auto; }
.cinema-drawer-tab {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  min-height: 76px;
  border-radius: 14px 0 0 14px;
  border: 1px solid rgba(139, 92, 246, 0.55);
  border-right: none;
  background: rgba(139, 92, 246, 0.24);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  color: #c4b5fd;
  cursor: pointer;
  box-shadow: -5px 0 20px rgba(0, 0, 0, 0.45);
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
  /* Item 17b: the tab slides left WITH the panel on open (transform animated). */
  transition: background 160ms ease, border-color 160ms ease,
              transform 260ms cubic-bezier(0.33, 1, 0.68, 1);
}
/* Item 17b: when open, the tab moves left by the panel width to abut the panel's
   left edge (slides together). MUST equal .cinema-drawer-panel width (84px). */
.cinema-drawer.is-open .cinema-drawer-tab { transform: translateX(-84px); }
.cinema-drawer-tab:hover,
.cinema-drawer-tab:focus-visible {
  background: rgba(139, 92, 246, 0.4);
  border-color: rgba(167, 139, 250, 0.85);
  outline: none;
}
.cinema-drawer-tab svg { width: 18px; height: 18px; }
/* Item: enlarge the TAP target without changing the visible tab graphic — a
   transparent ::before extends the button's hit area outward on all sides. */
.cinema-drawer-tab::before {
  content: "";
  position: absolute;
  top: -22px; bottom: -22px; left: -26px; right: -10px;
}

.cinema-drawer-panel {
  /* Item 17b: ABSOLUTE (out of the flex flow) so the collapsed panel reserves NO
     width — that lets the tab sit flush against the screen's right edge when closed
     instead of floating ~84px inboard. Anchored to the drawer's right edge, centered. */
  position: absolute;
  right: 0;
  top: 50%;
  display: flex;
  flex-direction: column;
  gap: 0;
  width: 84px;
  max-height: 82vh;
  overflow-y: auto;
  overscroll-behavior: contain;
  border-radius: 16px 0 0 16px;
  border: 1px solid rgba(139, 92, 246, 0.35);
  border-right: none;
  background: rgba(14, 15, 20, 0.9);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  backdrop-filter: blur(18px) saturate(1.3);
  box-shadow: -8px 0 34px rgba(0, 0, 0, 0.55), inset 1px 0 0 rgba(139, 92, 246, 0.12);
  padding: 12px 0;
  transform: translateY(-50%) translateX(100%);    /* off-screen right until opened */
  transition: transform 240ms cubic-bezier(0.32, 0, 0.67, 0);
}
.cinema-drawer.is-open .cinema-drawer-panel {
  transform: translateY(-50%) translateX(0);
  transition: transform 260ms cubic-bezier(0.33, 1, 0.68, 1);
}

.cdp-section { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 8px; }
.cdp-label {
  font-family: var(--mono);
  font-size: 9px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(167, 139, 250, 0.7);
  line-height: 1;
}
.cdp-divider { width: 46px; height: 1px; background: rgba(255, 255, 255, 0.08); margin: 0 auto; }

.cdp-swatch {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  width: 56px; min-height: 46px; padding: 4px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 9px; background: transparent;
  color: rgba(230, 232, 238, 0.6); cursor: pointer;
  transition: border-color 140ms ease, background 140ms ease, color 140ms ease;
}
.cdp-swatch:hover { border-color: rgba(167, 139, 250, 0.5); background: rgba(139, 92, 246, 0.08); }
.cdp-swatch.active { border-color: #a78bfa; background: rgba(139, 92, 246, 0.18); color: #c4b5fd; }
.cdp-swatch-fill {
  display: block; width: 24px; height: 24px; border-radius: 6px;
  border: 1px solid rgba(0, 0, 0, 0.25); flex: 0 0 auto;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.cdp-none-fill { background: transparent; border-color: rgba(255, 255, 255, 0.45); position: relative; }
.cdp-none-fill::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 132%; height: 2px; background: var(--danger, #e0696b);
  border-radius: 1px; transform: translate(-50%, -50%) rotate(-43deg);
}
.cdp-swatch-code { font-family: var(--mono); font-size: 9px; letter-spacing: 0.03em; line-height: 1; }

.cdp-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  width: 56px; min-height: 54px; padding: 6px 4px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px; background: transparent;
  color: rgba(230, 232, 238, 0.62); cursor: pointer;
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.05em;
  transition: border-color 140ms ease, background 140ms ease, color 140ms ease;
}
.cdp-btn svg { width: 20px; height: 20px; flex: 0 0 auto; }
.cdp-btn:hover { border-color: rgba(167, 139, 250, 0.5); color: #e7e9ee; }
.cdp-btn.active { border-color: #a78bfa; background: rgba(139, 92, 246, 0.18); color: #c4b5fd; }

.cdp-variant { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.cdp-step {
  display: inline-flex; align-items: center; justify-content: center;
  width: 42px; height: 42px; border-radius: 9px;
  border: 1px solid rgba(255, 255, 255, 0.14); background: transparent;
  color: rgba(230, 232, 238, 0.72); cursor: pointer;
  transition: border-color 140ms ease, background 140ms ease;
}
.cdp-step:hover { border-color: rgba(167, 139, 250, 0.5); background: rgba(139, 92, 246, 0.08); }
.cdp-step svg { width: 18px; height: 18px; }
.cdp-variant-label {
  font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase;
  color: #e7e9ee; text-align: center; min-height: 14px;
}

/* Item 10/19 — present-mode Lab selector (two buttons, dark-glass to match drawer). */
.cdp-labs { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.cdp-lab-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  width: 64px; min-height: 40px; padding: 4px 6px;
  border: 1px solid rgba(255, 255, 255, 0.14); border-radius: 9px; background: transparent;
  color: rgba(230, 232, 238, 0.72); cursor: pointer;
  transition: border-color 140ms ease, background 140ms ease, color 140ms ease, opacity 140ms ease;
}
.cdp-lab-btn:hover:not(:disabled) { border-color: rgba(167, 139, 250, 0.5); }
.cdp-lab-btn.active { border-color: #a78bfa; background: rgba(139, 92, 246, 0.18); color: #c4b5fd; }
.cdp-lab-name { font-family: var(--mono); font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase; line-height: 1.1; }
.cdp-lab-state { font-size: 8px; letter-spacing: 0.02em; opacity: 0.8; line-height: 1; }
.cdp-lab-btn.processing { opacity: 0.5; cursor: progress; }
.cdp-lab-btn.failed { opacity: 0.45; cursor: not-allowed; }

/* Hide the drawer in landscape on touch (the Present rotate prompt takes over). */
@media (orientation: landscape) and (pointer: coarse) {
  .cinema-drawer { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  .cinema-drawer-panel { transition: none !important; }
}

/* Slider-first in BOTH orientations — orientation only adjusts spacing/scale,
   never which compare control is primary (§11.1). */
@media (orientation: portrait) {
  .cinema-controls { gap: 10px; padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px)); }
}

@media (prefers-reduced-motion: reduce) {
  .cinema-after, .cinema-handle, .split-handle { transition: none !important; }
}

/* ====================================================================
   GUIDED CAPTURE (§11.4) — additive live-camera overlay with face guide
   ==================================================================== */
.capture {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  height: 100dvh; /* dynamic viewport — covers under iOS toolbars cleanly */
  z-index: 2000;  /* above the page header/command bar so nothing bleeds through */
  display: flex;
  flex-direction: column;
  background: var(--graphite-900, #0b0e13);
  color: #fff;
}
[x-cloak] { display: none !important; }

.capture-stage {
  position: relative;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.capture-feed {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000;
}
/* Mirror ONLY the front/selfie camera (natural for self-framing). The rear
   camera (staff photographing the patient) is shown true-orientation. The overlay
   canvas mirror is matched in face_capture.js via the `mirror` option. */
.capture-feed.is-selfie { transform: scaleX(-1); }
.capture-guide {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.capture-guide svg {
  width: min(62vmin, 420px);
  height: min(82vmin, 560px);
}
.capture-guide ellipse {
  fill: none;
  stroke: rgba(255, 255, 255, 0.85);
  stroke-width: 2;
  stroke-dasharray: 7 9;
}
.capture-hint {
  position: absolute;
  bottom: 6%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2; /* above .capture-overlay (z-index:1) so mesh strokes never cross the chip */
  margin: 0;
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.5);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
.capture-exit {
  position: absolute;
  top: calc(16px + env(safe-area-inset-top, 0px));
  right: 16px;
  z-index: 2;
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  cursor: pointer;
}
.capture-exit:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.capture-exit svg { width: 22px; height: 22px; stroke: currentColor; fill: none; }

/* top-LEFT tools (flip camera / reset scan) — mirror the exit button styling */
.capture-tools {
  position: absolute;
  top: calc(16px + env(safe-area-inset-top, 0px));
  left: 16px;
  z-index: 3;
  display: flex;
  gap: 10px;
}
.capture-iconbtn {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  cursor: pointer;
}
.capture-iconbtn:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.capture-iconbtn:active { transform: scale(0.94); }
.capture-iconbtn svg { width: 22px; height: 22px; }

.capture-controls {
  display: flex;
  flex-wrap: wrap; /* lets .capture-mode (flex-basis:100%, order:-1) take its own row */
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 24px calc(24px + env(safe-area-inset-bottom, 0px));
}
.capture-controls .btn {
  min-height: 48px;
  padding: 10px 18px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.25);
  background: transparent;
  color: #fff;
  font-weight: 600;
  cursor: pointer;
}
.capture-controls .btn:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
.capture-shutter {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  border: none;
  background: #fff;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}
.capture-shutter:focus-visible { outline: 3px solid var(--accent-strong); outline-offset: 3px; }
.capture-shutter-ring {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  border: 3px solid var(--graphite-900, #0b0e13);
}
.capture-shutter:active { transform: scale(0.94); }

@media (orientation: portrait) {
  .capture-controls { padding-bottom: calc(28px + env(safe-area-inset-bottom, 0px)); }
}
/* iPad / large touch screens: the bottom-centre shutter needs a two-hand reach on a
   13" iPad. Float it on the RIGHT edge, vertically centred, so it's tappable with the
   thumb in BOTH portrait and landscape. Cancel / Upload / Auto-Manual stay in the
   bottom bar (the shutter is pulled out of that flex row by position:fixed). */
@media (pointer: coarse) and (min-width: 768px) {
  .capture-shutter {
    position: fixed;
    top: 50%;
    right: calc(env(safe-area-inset-right, 0px) + 28px);
    transform: translateY(-50%);
    z-index: 2001;            /* above .capture-controls (within the .capture overlay) */
    width: 88px;
    height: 88px;
  }
  .capture-shutter-ring { width: 70px; height: 70px; }
  .capture-shutter:active { transform: translateY(-50%) scale(0.94); }
  /* Keep the bottom controls from sliding under the floating shutter. */
  .capture-controls { padding-right: calc(env(safe-area-inset-right, 0px) + 132px); }
}
@media (prefers-reduced-motion: reduce) {
  .capture-guide ellipse, .capture-shutter { transition: none !important; }
}

/* ---------- live face-mesh overlay + coaching (Guided-Capture overlay) ---------- */
/* Canvas sits exactly over the feed; mesh is drawn mirrored IN the canvas context
   (the element itself is NOT CSS-mirrored — mapping math lives in face_capture.js).
   pointer-events:none so the shutter/controls underneath stay tappable. */
.capture-overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1; /* above .capture-feed, below .capture-exit (z-index:2) */
}

/* ?meshdbg=1 telemetry readout (diagnostic; only renders when meshDbg is set). */
.capture-meshdbg {
  position: absolute;
  top: calc(8px + env(safe-area-inset-top, 0px));
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  margin: 0;
  padding: 4px 8px;
  border-radius: 6px;
  background: rgba(0, 0, 0, 0.6);
  color: #6df0a0;
  font: 600 11px/1.3 ui-monospace, Menlo, monospace;
  white-space: pre;
  pointer-events: none;
}

/* GOOD / capturing variants of the (now standalone) coaching chip — GREEN to
   match the mesh's "ready" color (matches face_capture.js GOOD_GREEN #22e06a). */
.capture-hint.is-good {
  background: #22e06a;
  color: #04140d;
}
.capture-hint.is-capturing {
  background: #22e06a;
  color: #04140d;
  animation: captureHintPulse 1s ease-in-out infinite;
}
/* Prominent lighting alert on the capture screen (amber, above the controls). */
.capture-bright-alert {
  position: absolute;
  left: 50%;
  bottom: 16px;
  transform: translateX(-50%);
  z-index: 6;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100vw - 48px);
  padding: 10px 16px;
  border-radius: 999px;
  background: rgba(245, 158, 11, 0.95);  /* amber-500 */
  color: #1a1206;
  font-size: 14px;
  font-weight: 600;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
}
.capture-bright-alert svg { width: 18px; height: 18px; flex: 0 0 auto; }
@keyframes captureHintPulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* Auto-snap "capturing" pulse around the shutter (green). */
.capture-shutter.is-capturing { box-shadow: 0 0 0 0 rgba(34, 224, 106, 0.7); animation: captureShutterPulse 0.9s ease-out infinite; }
@keyframes captureShutterPulse {
  0%   { box-shadow: 0 0 0 0 rgba(34, 224, 106, 0.7); }
  100% { box-shadow: 0 0 0 18px rgba(34, 224, 106, 0); }
}

/* Auto / Manual segmented toggle — sits on its own centered row ABOVE the
   Cancel / shutter / Upload row (order:-1 + full basis) so the existing 3-item
   layout is unchanged in both orientations. */
.capture-mode {
  order: -1;
  flex: 0 0 100%;
  display: flex;
  justify-content: center;
  gap: 0;
  margin-bottom: 2px;
}
.capture-mode-group {
  display: inline-flex;
  padding: 3px;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.18);
}
.capture-mode-btn {
  min-height: 36px;
  padding: 6px 18px;
  border: none;
  border-radius: 999px;
  background: transparent;
  color: rgba(255, 255, 255, 0.75);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
}
.capture-mode-btn.is-active {
  background: #fff;
  color: var(--graphite-900, #0b0e13);
}
.capture-mode-btn:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .capture-hint.is-capturing, .capture-shutter.is-capturing { animation: none !important; }
}

/* ---------- Phase 4 (§4): on-face measurement overlay + staged readout ----------
   Drawn over the Before image ONLY while processing (scanActive); fades out on
   ready. STAFF ONLY. The SVG viewBox is 0..1000 and uses preserveAspectRatio
   meet, matching the img's object-contain — lines sit on the actual face. */
.scan-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 5;
  transition: opacity 0.6s ease;
}
.scan-overlay--fade { opacity: 0; }

/* FaceMesh canvas (tesselation + lips) — positioned/sized inline by syncScanRect()
   to the displayed image rect. Sits below the surgical SVG lines. Fades in once the
   mesh is drawn so it reveals as part of the scan rather than popping in. */
/* FaceMesh canvas — positioned/sized inline by syncScanRect(); vivid colors come
   from drawMesh. Phase + sweep (below) control its reveal. */
.scan-overlay__mesh,
.scan-mesh {
  position: absolute;
  pointer-events: none;
  opacity: 1;
}

/* ===== Scene rotation (Task 3) =====
   The BEFORE scan rotates 4 scenes, one per sweep (data-scene 0..3). Scene 0
   shows the FACE MESH canvas; scenes 1–3 show the measurement SVG. The inactive
   layer is hidden so only the current scene's layer is revealed by the sweep. */
.scan-overlay__svg,
.scan-mesh { transition: opacity 0.5s ease; }
.scan-overlay:not([data-scene="0"]) .scan-mesh { opacity: 0; }
.scan-overlay[data-scene="0"] .scan-overlay__svg { opacity: 0; }

/* ===== Sweeping scan line + top→bottom band-by-band wipe of the active layer =====
   The bar travels top→bottom; the active layer is clip-path-wiped in lockstep
   (same 3s linear period) so it reveals as the line descends, then re-scans. */
.scan-sweep { position: absolute; pointer-events: none; z-index: 7; overflow: hidden; }
.scan-sweep__bar {
  position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: linear-gradient(90deg, transparent,
    var(--accent-strong, #a78bfa) 22%, #7cf0dc 50%, var(--accent-strong, #a78bfa) 78%, transparent);
  box-shadow: 0 0 18px 3px rgba(167, 139, 250, 0.5), 0 0 8px 1px rgba(124, 240, 220, 0.7);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .scan-overlay.is-scanning .scan-sweep__bar { animation: scan-line-move 3s linear infinite; }
  .scan-overlay.is-scanning[data-scene="0"] .scan-mesh         { animation: scan-wipe 3s linear infinite; }
  .scan-overlay.is-scanning:not([data-scene="0"]) .scan-overlay__svg { animation: scan-wipe 3s linear infinite; }
}
@keyframes scan-line-move {
  0%   { top: 0%;   opacity: 0; }
  8%   { opacity: 1; }
  92%  { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}
/* reveal grows from the top to the line; holds briefly fully-revealed, then re-scans */
@keyframes scan-wipe {
  0%   { clip-path: inset(0 0 100% 0); }
  90%  { clip-path: inset(0 0 0 0); }
  100% { clip-path: inset(0 0 0 0); }
}

.scan-overlay__svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
/* measurement primitives — thin surgical lines + landmark dots */
.scan-overlay__svg .ov-line,
.scan-overlay__svg .ov-arc,
.scan-overlay__svg .ov-box {
  fill: none;
  stroke: var(--accent-strong, #a78bfa);
  stroke-width: 1.6;
  vector-effect: non-scaling-stroke;
}
.scan-overlay__svg .ov-box {
  stroke: var(--accent, #8b5cf6);
  stroke-dasharray: 6 6;
  opacity: 0.55;
}
.scan-overlay__svg .ov-midline { stroke-dasharray: 4 6; opacity: 0.7; }
.scan-overlay__svg .ov-ipd { stroke-width: 2; }
.scan-overlay__svg .ov-commissure { opacity: 0.75; }
.scan-overlay__svg .ov-arc {
  stroke-width: 2;
  opacity: 0.85;
  filter: drop-shadow(0 0 4px var(--accent-soft, rgba(139, 92, 246, 0.4)));
}
/* Solid dots with a soft dark halo (NOT a white ring — a white-ringed oval reads
   as a "0" once preserveAspectRatio="none" stretches the circle). */
.scan-overlay__svg .ov-dot {
  fill: var(--accent-strong, #a78bfa);
  stroke: none;
  filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.7));
}
.scan-overlay__svg .ov-label {
  fill: var(--accent-strong, #a78bfa);
  font-family: var(--mono, monospace);
  font-size: 22px;
  font-weight: 600;
  paint-order: stroke;
  stroke: rgba(0, 0, 0, 0.55);
  stroke-width: 3;
}
@media (prefers-reduced-motion: no-preference) {
  .scan-overlay__svg .ov-line,
  .scan-overlay__svg .ov-arc,
  .scan-overlay__svg .ov-box {
    stroke-dashoffset: 0;
    animation: ov-draw 1.1s ease forwards;
  }
  .scan-overlay__svg .ov-dot { animation: ov-pop 0.4s ease backwards; }
}
@keyframes ov-draw {
  from { stroke-dasharray: 600; stroke-dashoffset: 600; opacity: 0; }
  to   { stroke-dasharray: 600; stroke-dashoffset: 0; }
}
@keyframes ov-pop { from { opacity: 0; transform: scale(0.2); } to { opacity: 1; } }

/* ONE mono readout line per scene (Task 3), bottom-left of the BEFORE image. The
   multi-row telemetry + clinical findings live in the AFTER-pane console. */
.scene-readout {
  position: absolute;
  left: 12px;
  bottom: 12px;
  max-width: 70%;
  padding: 2px 9px;
  border-radius: 4px;
  border-left: 2px solid var(--accent, #8b5cf6);
  background: rgba(11, 14, 19, 0.5);
  font-family: var(--mono, monospace);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.75);
}

/* ---------- Task 2: render-time analysis console (AFTER pane) ----------
   Graphite-glass FUI telemetry panel shown while generating && !caseReady. Thin
   surgical lines, IBM Plex Mono, disciplined purple (--accent) + muted cyan
   (--measure). Vanishes with the generating branch when caseReady. STAFF ONLY. */
/* ----- generating header: ring spinner + mono title (replaces the plain text) ----- */
.fui-genhead { display: flex; align-items: center; gap: 12px; }
.fui-genring {
  width: 26px; height: 26px; flex: none; border-radius: 50%;
  border: 2px solid var(--panel-border, rgba(255, 255, 255, 0.12));
  border-top-color: var(--accent-strong, #a78bfa);
  box-shadow: 0 0 14px -3px var(--accent-soft, rgba(139, 92, 246, 0.6));
}
@media (prefers-reduced-motion: no-preference) {
  .fui-genring { animation: fui-spin 0.8s linear infinite; }
}
@keyframes fui-spin { to { transform: rotate(360deg); } }
.fui-genhead__title {
  font-family: var(--mono, monospace); font-size: 12px; letter-spacing: 0.04em;
  color: var(--text, #e7e9ee); margin: 0;
}
.fui-genhead__sub {
  font-family: var(--mono, monospace); font-size: 10.5px; letter-spacing: 0.05em;
  color: var(--text-faint, #6b7180); margin: 2px 0 0;
}

.fui-console {
  position: relative;
  flex: 1;
  min-height: 220px;
  display: flex;
  flex-direction: column;
  gap: 13px;
  padding: 14px 16px 14px;
  border: 1px solid var(--panel-border, rgba(255, 255, 255, 0.08));
  border-top: 1px solid var(--accent, #8b5cf6);
  border-radius: var(--radius-sm, 10px);
  background:
    /* faint hairline grid — technical texture, very low contrast */
    repeating-linear-gradient(0deg, rgba(255,255,255,0.018) 0 1px, transparent 1px 28px),
    repeating-linear-gradient(90deg, rgba(255,255,255,0.018) 0 1px, transparent 1px 28px),
    linear-gradient(180deg, rgba(167, 139, 250, 0.05), rgba(0, 0, 0, 0.06)),
    var(--panel, rgba(20, 22, 27, 0.72));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05),
              0 0 0 1px rgba(139, 92, 246, 0.06),
              0 18px 50px -24px rgba(0, 0, 0, 0.7);
  overflow: hidden;
  font-family: var(--mono, monospace);
}
/* slow vertical scan sheen sweeping the panel — "alive" without neon */
.fui-scan {
  position: absolute; left: 0; right: 0; top: 0; height: 38%;
  pointer-events: none; z-index: 1; opacity: 0.5;
  background: linear-gradient(180deg, transparent, rgba(167, 139, 250, 0.07) 60%, rgba(124, 240, 220, 0.05) 85%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  .fui-scan { animation: fui-console-scan 5.5s ease-in-out infinite; }
}
@keyframes fui-console-scan {
  0%   { transform: translateY(-110%); opacity: 0; }
  12%  { opacity: 0.55; }
  88%  { opacity: 0.55; }
  100% { transform: translateY(310%); opacity: 0; }
}

/* console header: LIVE pulse · title · system tag */
.fui-console__hd {
  position: relative; z-index: 2;
  display: flex; align-items: center; gap: 10px;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--panel-border, rgba(255, 255, 255, 0.08));
  font-size: 10px; letter-spacing: 0.16em;
}
.fui-console__title { color: var(--text-dim, #9aa0ab); font-weight: 600; }
.fui-console__sys { margin-left: auto; color: var(--text-faint, #6b7180); letter-spacing: 0.1em; }
.fui-live { display: inline-flex; align-items: center; gap: 5px; color: var(--accent-strong, #a78bfa); font-weight: 600; }
.fui-live__dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: #7cf0dc; box-shadow: 0 0 8px 1px rgba(124, 240, 220, 0.8);
}
@media (prefers-reduced-motion: no-preference) {
  .fui-live__dot { animation: fui-live-blink 1.4s ease-in-out infinite; }
}
@keyframes fui-live-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

/* tiny section labels (TELEMETRY / CLINICAL FINDINGS) */
.fui-section-label,
.fui-clinical__hd {
  position: relative; z-index: 2;
  font-size: 9px; letter-spacing: 0.16em;
  color: var(--text-faint, #6b7180);
}
.fui-section-label::after,
.fui-clinical__hd::after {
  content: ""; display: inline-block; vertical-align: middle;
  width: 36px; height: 1px; margin-left: 8px;
  background: linear-gradient(90deg, var(--accent-soft, rgba(139,92,246,0.4)), transparent);
}

/* corner ticks (reuse the .ticks treatment from .fui-card) */
.fui-console > .ticks { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.fui-console > .ticks > i {
  position: absolute; width: 12px; height: 12px;
  border: 1.5px solid var(--accent, #8b5cf6); opacity: 0.5;
}
.fui-console > .ticks > i.tl { top: 8px; left: 8px; border-right: 0; border-bottom: 0; }
.fui-console > .ticks > i.tr { top: 8px; right: 8px; border-left: 0; border-bottom: 0; }
.fui-console > .ticks > i.bl { bottom: 8px; left: 8px; border-right: 0; border-top: 0; }
.fui-console > .ticks > i.br { bottom: 8px; right: 8px; border-left: 0; border-top: 0; }

/* ----- stage stepper: ACQUIRE › MAP › ANALYZE › DESIGN › QA ----- */
.fui-stepper {
  position: relative; z-index: 2;
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 4px 2px;
  font-size: 10px; letter-spacing: 0.08em;
}
.fui-step { display: inline-flex; align-items: center; gap: 5px; }
.fui-step__tick {
  width: 6px; height: 6px; border-radius: 50%;
  border: 1px solid var(--text-faint, #6b7180);
  background: transparent;
  transition: background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
}
.fui-step__label { color: var(--text-faint, #6b7180); transition: color 0.4s ease; }
.fui-step__sep { color: var(--text-faint, #6b7180); opacity: 0.5; margin: 0 4px; }
.fui-step.is-done .fui-step__tick {
  background: var(--measure, rgba(120, 210, 230, 0.55));
  border-color: var(--measure, rgba(120, 210, 230, 0.55));
}
.fui-step.is-done .fui-step__label { color: var(--text-dim, #9aa0ab); }
.fui-step.is-active .fui-step__tick {
  background: var(--accent-strong, #a78bfa);
  border-color: var(--accent-strong, #a78bfa);
  box-shadow: 0 0 8px 1px var(--accent-soft, rgba(139, 92, 246, 0.5));
}
.fui-step.is-active .fui-step__label { color: var(--accent-strong, #a78bfa); font-weight: 600; }
@media (prefers-reduced-motion: no-preference) {
  .fui-step.is-active .fui-step__tick { animation: fui-step-pulse 1.6s ease-in-out infinite; }
}
@keyframes fui-step-pulse {
  0%, 100% { box-shadow: 0 0 6px 0 var(--accent-soft, rgba(139, 92, 246, 0.4)); }
  50%      { box-shadow: 0 0 12px 2px var(--accent-soft, rgba(139, 92, 246, 0.7)); }
}

/* ----- metric chips ----- */
.fui-chips {
  position: relative; z-index: 2;
  display: flex; flex-wrap: wrap; gap: 5px;
}
.fui-chip {
  display: inline-flex; align-items: baseline; gap: 5px;
  padding: 2px 7px;
  border: 1px solid var(--panel-border, rgba(255, 255, 255, 0.1));
  border-radius: 5px;
  background: rgba(255, 255, 255, 0.03);
  font-size: 10px;
}
.fui-chip__k { color: var(--text-faint, #6b7180); letter-spacing: 0.04em; }
.fui-chip__v { color: var(--measure, rgba(120, 210, 230, 0.9)); font-weight: 600; }

/* ----- staged readout rows (FIXED layout; term + scramble value) ----- */
.fui-readout {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; gap: 3px;
  font-size: 11px; line-height: 1.5;
}
.fui-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline; gap: 10px;
  padding: 2px 8px;
  border-radius: 4px;
  border-left: 2px solid var(--panel-border, rgba(255, 255, 255, 0.12));
  background: rgba(255, 255, 255, 0.015);
  transition: border-color 0.4s ease;
}
.fui-row.is-resolved {
  border-left-color: var(--accent, #8b5cf6);
  background: linear-gradient(90deg, rgba(167,139,250,0.06), rgba(255,255,255,0.015));
}
.fui-row__term { color: var(--text-dim, #9aa0ab); letter-spacing: 0.03em; }
.fui-row__val {
  text-align: right;
  color: var(--accent-strong, #a78bfa); font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 6ch;          /* reserve space so resolving never reflows the row */
  white-space: nowrap;
}
.fui-row__pending {
  color: var(--text-faint, #6b7180); font-weight: 400;
  letter-spacing: 0.05em; opacity: 0.7;
}
/* entrance: chips + rows fade/slide in as they mount (transform/opacity only —
   never layout, so the FIXED panel doesn't reflow). */
@media (prefers-reduced-motion: no-preference) {
  .fui-chip { animation: fui-in 0.4s ease backwards; }
  .fui-readout .fui-row { animation: fui-in 0.45s ease backwards; }
}
@keyframes fui-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }

/* ----- clinical findings subsection (staff-only; arrives mid-scan) ----- */
.fui-clinical {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; gap: 5px;
  padding-top: 10px;
  border-top: 1px solid var(--panel-border, rgba(255, 255, 255, 0.08));
}
.fui-clinical__hd {
  font-size: 9px; letter-spacing: 0.14em;
  color: var(--text-faint, #6b7180);
}
/* findings carry their own clinical value color (cyan measure) + severity
   accent on the left border; mild/moderate/severe map to the design tokens. */
/* Clinical values can be long (region + severity), so they WRAP instead of
   getting cropped: term takes its content width, value fills the rest and wraps. */
.fui-clinical .fui-row { grid-template-columns: max-content minmax(0, 1fr); align-items: start; }
.fui-clinical .fui-row__val {
  color: var(--measure, rgba(120, 210, 230, 0.9));
  white-space: normal; min-width: 0; overflow-wrap: anywhere; line-height: 1.45;
}
.fui-clinical .fui-row__term { padding-top: 1px; }
.fui-clinical .fui-row.sev-mild     { border-left-color: var(--ok, #6fc28a); }
.fui-clinical .fui-row.sev-moderate { border-left-color: var(--warn, #d8a657); }
.fui-clinical .fui-row.sev-severe   { border-left-color: var(--danger, #e0696b); }
.fui-clinical .fui-row.sev-severe .fui-row__val { color: var(--danger, #e0696b); }

/* ----- LIVE FEED — terminal-style streaming analysis log -----
   Append-only feedLines[] type in one line at a time (typeLine), wrap in FULL (no
   truncation), and the box auto-scrolls (JS pins scrollTop; overflow hidden hides
   the bar + clips cleanly). A top mask fades older lines as they scroll off. */
.fui-feed {
  position: relative; z-index: 2;
  display: flex; flex-direction: column; gap: 2px;
  max-height: 224px; overflow: hidden;
  font-size: 11px; line-height: 1.5;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 18px, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 18px, #000 100%);
}
.fui-feed__line {
  padding: 1px 8px;
  border-left: 2px solid transparent;
  color: var(--accent-strong, #a78bfa);
  white-space: normal; overflow-wrap: anywhere; word-break: break-word;
}
.fui-feed__line.sys  { color: var(--text-dim, #9aa0ab); }
.fui-feed__line.data { color: var(--accent-strong, #a78bfa); border-left-color: rgba(167, 139, 250, 0.35); }
.fui-feed__line.find { color: var(--measure, rgba(120, 210, 230, 0.9)); border-left-color: var(--measure, rgba(120, 210, 230, 0.6)); }
.fui-feed__line.sev-mild     { border-left-color: var(--ok, #6fc28a); }
.fui-feed__line.sev-moderate { border-left-color: var(--warn, #d8a657); }
.fui-feed__line.sev-severe   { border-left-color: var(--danger, #e0696b); color: var(--danger, #e0696b); }
.fui-caret {
  display: inline-block; width: 0.55ch; margin-left: 1px;
  color: var(--accent, #8b5cf6); font-weight: 700;
}
@media (prefers-reduced-motion: no-preference) {
  .fui-caret { animation: fui-caret-blink 1s steps(1, end) infinite; }
}
@keyframes fui-caret-blink { 50% { opacity: 0; } }

/* ====================================================================
   STAFF RE-SKIN BRIDGE (full FUI conversion) — neutralize the legacy
   Tailwind light-theme utilities still present in sim_staff.html so the
   workbench matches docs/prototypes/smile_workbench_fui.html. Scoped to
   body.smile-studio ONLY → patient/lead/share (output.css) are untouched.
   Restyle-only: no markup/binding changes needed for color fidelity.
   text-white is intentionally NOT remapped (white stays on filled buttons).
   ==================================================================== */

/* page base + radial brand glow (matches prototype body::before) */
html:has(body.smile-studio) { background: var(--bg); }
body.smile-studio { position: relative; }
body.smile-studio::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(60vw 45vw at 50% -8%, rgba(124, 111, 176, 0.10), transparent 70%),
    radial-gradient(50vw 40vw at 50% 120%, rgba(116, 214, 196, 0.04), transparent 70%);
}
[data-theme="light"] body.smile-studio::before {
  background:
    radial-gradient(60vw 45vw at 50% -8%, rgba(124, 111, 176, 0.07), transparent 70%),
    radial-gradient(50vw 40vw at 50% 120%, rgba(116, 214, 196, 0.05), transparent 70%);
}

/* --- surfaces: white/slate panels → graphite --- */
body.smile-studio .bg-white { background-color: var(--panel-solid) !important; }
body.smile-studio .bg-slate-50,
body.smile-studio .bg-slate-100 { background-color: var(--bg-elev) !important; }
body.smile-studio .bg-slate-200 { background-color: var(--hairline) !important; }

/* gradients: empty/generating panels flatten to graphite; bar/▶ fills → accent */
body.smile-studio .bg-gradient-to-br { background-image: none !important; background-color: var(--bg-elev) !important; }
body.smile-studio .bg-gradient-to-r  { background-image: none !important; background-color: var(--accent) !important; }

/* accent (blue → disciplined purple) */
body.smile-studio .bg-blue-50,
body.smile-studio .bg-blue-100,
body.smile-studio .bg-blue-200 { background-color: var(--accent-soft) !important; }
body.smile-studio .bg-blue-600,
body.smile-studio .bg-blue-600\/80 { background-color: var(--accent) !important; }
body.smile-studio .bg-blue-700 { background-color: var(--accent-strong) !important; }
body.smile-studio .bg-amber-500 { background-color: var(--warn) !important; }

/* semantic tints (kept meaningful, but dark) */
body.smile-studio .bg-green-50,
body.smile-studio .bg-emerald-50 { background-color: rgba(111, 194, 138, 0.12) !important; }
body.smile-studio .bg-green-600 { background-color: var(--ok) !important; }
body.smile-studio .bg-green-700 { background-color: #5aa874 !important; }
body.smile-studio .bg-amber-50 { background-color: rgba(216, 166, 87, 0.12) !important; }
body.smile-studio .bg-red-50 { background-color: rgba(224, 105, 107, 0.12) !important; }

/* --- borders --- */
body.smile-studio .border-slate-100,
body.smile-studio .border-slate-200 { border-color: var(--panel-border) !important; }
body.smile-studio .border-slate-300 { border-color: var(--hairline) !important; }
body.smile-studio .divide-slate-100 > :not([hidden]) ~ :not([hidden]) { border-color: var(--panel-border) !important; }
body.smile-studio .border-blue-200 { border-color: var(--accent) !important; }
body.smile-studio .border-green-200,
body.smile-studio .border-green-300,
body.smile-studio .border-emerald-200 { border-color: rgba(111, 194, 138, 0.40) !important; }
body.smile-studio .border-red-200 { border-color: rgba(224, 105, 107, 0.40) !important; }
body.smile-studio .border-amber-200 { border-color: rgba(216, 166, 87, 0.40) !important; }

/* --- text --- */
body.smile-studio .text-slate-800,
body.smile-studio .text-slate-700 { color: var(--text) !important; }
body.smile-studio .text-slate-600,
body.smile-studio .text-slate-500 { color: var(--text-dim) !important; }
body.smile-studio .text-slate-400,
body.smile-studio .text-slate-300 { color: var(--text-faint) !important; }
body.smile-studio .text-blue-300,
body.smile-studio .text-blue-400,
body.smile-studio .text-blue-500,
body.smile-studio .text-blue-600,
body.smile-studio .text-blue-700,
body.smile-studio .text-blue-800 { color: var(--accent-strong) !important; }
body.smile-studio .text-green-600,
body.smile-studio .text-green-800,
body.smile-studio .text-emerald-600,
body.smile-studio .text-emerald-700 { color: var(--ok) !important; }
body.smile-studio .text-red-500,
body.smile-studio .text-red-600,
body.smile-studio .text-red-700 { color: var(--danger) !important; }
body.smile-studio .text-amber-600,
body.smile-studio .text-amber-700,
body.smile-studio .text-amber-800 { color: var(--warn) !important; }

/* --- hover / active / focus variants --- */
body.smile-studio .hover\:bg-slate-50:hover,
body.smile-studio .hover\:bg-white:hover { background-color: rgba(255, 255, 255, 0.05) !important; }
body.smile-studio .hover\:bg-slate-100:hover { background-color: var(--bg-elev) !important; }
body.smile-studio .hover\:bg-blue-50:hover,
body.smile-studio .hover\:bg-blue-100:hover,
body.smile-studio .active\:bg-blue-200:active { background-color: var(--accent-soft) !important; }
body.smile-studio .hover\:bg-blue-700:hover { background-color: var(--accent-strong) !important; }
body.smile-studio .hover\:bg-green-700:hover { background-color: #5aa874 !important; }
body.smile-studio .hover\:text-blue-600:hover,
body.smile-studio .hover\:text-blue-800:hover { color: var(--accent-strong) !important; }
body.smile-studio .hover\:text-slate-700:hover,
body.smile-studio .hover\:text-slate-800:hover { color: var(--text) !important; }
body.smile-studio .hover\:text-red-600:hover,
body.smile-studio .hover\:text-red-700:hover { color: var(--danger) !important; }
body.smile-studio .hover\:border-blue-400:hover { border-color: var(--accent) !important; }
body.smile-studio .focus\:ring-blue-500:focus { --tw-ring-color: var(--accent) !important; }

/* --- form fields → dark --- */
body.smile-studio input[type="text"],
body.smile-studio input:not([type="checkbox"]):not([type="radio"]) {
  background-color: var(--bg) !important;
  color: var(--text) !important;
}
body.smile-studio input::placeholder { color: var(--text-faint) !important; }

/* ====================================================================
   FUI CRAFT — port the DETAILING from docs/prototypes/smile_workbench_fui.html
   (brand mark, mono micro-labels, thin-hairline translucent panels, corner
   ticks, toggle-icon fix). Goes beyond recolor → matches the prototype's craft.
   ==================================================================== */

/* brand mark: tooth glyph in a purple-bordered rounded square (prototype .mk) */
body.smile-studio .brand-mk {
  width: 30px; height: 30px; flex: 0 0 auto;
  border-radius: 8px; border: 1px solid rgba(167, 139, 250, 0.40);
  display: inline-grid; place-items: center; background: var(--accent-soft);
}
body.smile-studio .brand-mk svg { width: 17px; height: 17px; stroke: var(--accent-strong); fill: none; }
body.smile-studio .brand-title { font-size: 15px; font-weight: 600; color: var(--text); line-height: 1.1; }
body.smile-studio .brand-sub {
  font-family: var(--mono); font-size: 9px; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--text-faint); margin-top: 2px;
}

/* primary CTA — prototype's purple gradient + glow (the "Analyze & Design" btn) */
body.smile-studio .fui-cta {
  background: linear-gradient(135deg, #7c6fb0, #6d28d9) !important;
  border: none !important; color: #fff !important;
  box-shadow: 0 0 24px -8px var(--accent-strong) !important;
}
body.smile-studio .fui-cta:hover:not(:disabled) {
  box-shadow: 0 0 32px -6px var(--accent-strong) !important; transform: translateY(-1px);
}

/* mono technical micro-labels (BEFORE/AFTER, section heads) + pills */
body.smile-studio .fui-wlabel {
  font-family: var(--mono) !important; font-weight: 600 !important;
  font-size: 14px !important; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--accent-strong) !important;
}
body.smile-studio .fui-pill {
  font-family: var(--mono) !important; font-size: 10px; letter-spacing: 0.08em;
  color: var(--text-faint) !important; border: 1px solid var(--hairline) !important;
  border-radius: 999px; background: transparent !important;
}

/* thin-hairline translucent panel (replaces the heavy white rounded card) */
body.smile-studio .fui-card {
  position: relative; border: 1px solid var(--hairline) !important;
  background: var(--panel) !important; border-radius: 14px; overflow: hidden;
  box-shadow: none !important;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
/* four corner registration ticks — subtle FUI framing (prototype .reg) */
body.smile-studio .fui-card > .ticks { position: absolute; inset: 0; pointer-events: none; z-index: 4; }
body.smile-studio .fui-card > .ticks > i {
  position: absolute; width: 16px; height: 16px;
  border: 1px solid var(--hairline); opacity: 0.7;
}
body.smile-studio .fui-card > .ticks > i.tl { top: 10px; left: 10px; border-right: 0; border-bottom: 0; }
body.smile-studio .fui-card > .ticks > i.tr { top: 10px; right: 10px; border-left: 0; border-bottom: 0; }
body.smile-studio .fui-card > .ticks > i.bl { bottom: 10px; left: 10px; border-right: 0; border-top: 0; }
body.smile-studio .fui-card > .ticks > i.br { bottom: 10px; right: 10px; border-left: 0; border-top: 0; }

/* theme toggle: the injected sun/moon SVG has no fill/stroke attrs and isn't in a
   .icon wrapper → was rendering as a solid black dot. Stroke it + size it. */
body.smile-studio button[aria-label="Toggle light/dark theme"] { color: var(--text-dim) !important; }
body.smile-studio button[aria-label="Toggle light/dark theme"]:hover { color: var(--accent-strong) !important; }
body.smile-studio button[aria-label="Toggle light/dark theme"] svg,
body.smile-studio [x-html] > svg { stroke: currentColor !important; fill: none !important; }
body.smile-studio button[aria-label="Toggle light/dark theme"] svg { width: 20px; height: 20px; }

/* ---------- reduced motion (§7/§9) ---------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ====================================================================
   MAIN-PAGE SPLIT REVEAL (Before&After side-by-side  vs.  drag slider)
   The live AFTER canvas stays full underneath; the BEFORE photo overlays
   it clipped by baSliderPct. Left of the handle = AFTER (live shade/variant),
   right = BEFORE — same convention as the Focus/cinema reveal.
   ==================================================================== */
.split-stage { touch-action: pan-y; }
.split-before {
  position: absolute;
  inset: 0;                       /* fill the SAME box as .split-after so the two reveal
                                     layers are pixel-identical and the split lines up. Was
                                     top/left/right + height:auto, which let the BEFORE photo
                                     and the AFTER render size to their own content heights —
                                     if they differ at all the reveal misaligns (worst in
                                     landscape, where max-height clamps each differently). */
  z-index: 5;
  pointer-events: none;
  clip-path: inset(0 0 0 calc(var(--ba, 50) * 1%));
}
.split-before img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 0.75rem;
}
/* Static AFTER snapshot for the Split reveal (iPad "white Split" fix). The live WebGL
   canvas can lose its GL context on the Split reflow → a blank/white after; this overlay
   covers it with a captured snapshot of the render so the reveal is never white (same
   proven approach as Present mode). Mirrors .split-before's box so the two halves align;
   sits ABOVE the (possibly-blank) live canvas but BELOW the before overlay (z 5) + the
   drag handle (z 6). pointer-events:none so taps/drags fall through to the stage. */
.split-after {
  position: absolute;
  inset: 0;                       /* same box as .split-before (see note above) */
  z-index: 4;
  pointer-events: none;
}
.split-after img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: 0.75rem;
}
/* .split-handle look is unified with .cinema-handle above (premium FUI grip + the
   .split-handle-grip chevron puck). This legacy block is intentionally a no-op
   anchor — the live styles live in the shared "premium FUI split-reveal handle"
   section near .cinema-handle. */

/* ====================================================================
   FOCUS / PRESENT — minimal controls (Split + Before/After) + portrait lock
   ==================================================================== */
.cinema-btn {
  min-height: 48px;
  min-width: 120px;
  padding: 0 22px;
  border-radius: 999px;
  font-weight: 600;
  font-size: 15px;
  color: #f5f6f8;
  background: rgba(255, 255, 255, 0.10);
  border: 1px solid rgba(255, 255, 255, 0.20);
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease;
}
.cinema-btn:hover { background: rgba(255, 255, 255, 0.16); }
.cinema-btn.active { background: #fff; color: #0a0b0d; border-color: #fff; }

/* Portrait-only Focus on touch devices: landscape hides the reveal + asks to rotate */
.cinema-rotate { display: none; }
@media (orientation: landscape) and (pointer: coarse) {
  .cinema .cinema-stage,
  .cinema .cinema-controls { display: none !important; }
  .cinema .cinema-rotate {
    display: flex;
    position: absolute;
    inset: 0;
    z-index: 20;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 18px;
    padding: 24px;
    text-align: center;
    color: #e2e8f0;
  }
  .cinema .cinema-rotate svg { width: 60px; height: 60px; stroke: currentColor; fill: none; opacity: 0.9; }
  .cinema .cinema-rotate p { font-size: 17px; font-weight: 600; }
}

/* ====================================================================
   Focus/Present + Split reliability fixes (result-UI regressions)
   ==================================================================== */
/* #5: the X must sit ABOVE the landscape rotate overlay (z-index 20) so the
   presenter can always close Focus mode, even in landscape. */
.cinema-exit { z-index: 30; }
/* #5: bigger rotate prompt (icon + text) so it reads across the room. */
@media (orientation: landscape) and (pointer: coarse) {
  .cinema .cinema-rotate svg { width: 96px; height: 96px; }
  .cinema .cinema-rotate p { font-size: 22px; }
}
/* #2: in Split mode the AFTER pane was a full-width column with a height-capped
   image, leaving big side gutters (the "medium + empty" look). Cap + center the
   column to the 4:5 reframe aspect at the 72vh height cap so the reveal fills the
   stage and the before/after handle tracks the image edge. */
/* #2: Split mode = ONE centered column, not a left-pinned medium pane. Override
   Tailwind's grid (which left-aligns the lone item in a stretched track) with a
   single track capped to the 4:5 reframe at the 72vh height cap, centered in the
   row. The AFTER pane fills that track and the reveal handle tracks the image. */
.cmp-split-grid {
  grid-template-columns: minmax(0, calc(72vh * 0.8)) !important;
  justify-content: center;
}
.cmp-split { width: 100%; }

/* BEFORE / AFTER labels as bottom-center pill OVERLAYS on each hero image (item: move the
   labels off the TOP so the whole stage rides higher + shows more without scrolling).
   pointer-events:none so taps fall through to the image; centered at the image bottom.
   BEFORE = dark glass, AFTER = accent — same pill shape, just relocated. */
.ba-tag {
  position: absolute; left: 50%; bottom: 12px; transform: translateX(-50%);
  z-index: 6; padding: 5px 14px; border-radius: 999px; pointer-events: none;
  font-family: var(--mono); font-weight: 600; font-size: 12px; letter-spacing: 0.16em;
  text-transform: uppercase; color: #fff; line-height: 1; white-space: nowrap;
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  box-shadow: 0 4px 14px rgba(0,0,0,0.25);
}
.ba-tag--before { background: rgba(15,18,23,0.78); }
.ba-tag--after  { background: var(--accent-strong); }
/* Split mode: the single centered pill is wrong (the slider reveals BOTH sides), so it's
   hidden and these two side pills label each half — AFTER on the left, BEFORE on the right,
   matching the reveal convention (left of the handle = after, right = before). */
.ba-tag--split-left  { left: 12px; right: auto; transform: none; }
.ba-tag--split-right { left: auto; right: 12px; transform: none; }

/* Selected video MODEL button (render sheet) — make the active pick obvious with an accent
   ring (item: accent-color outline). Ring via box-shadow so it adds no grid layout shift. */
.vmodel-on { box-shadow: 0 0 0 2px var(--accent-strong), 0 1px 2px 0 rgba(0,0,0,.05); }

/* Looping "…" for the header "Regenerating 2 New Labs in process" indicator — animates
   the trailing dots so staff see it's alive (CSS content steps; reduced-motion → static). */
.regen-dots::after { content: ""; animation: regenDots 1.4s steps(1) infinite; }
@keyframes regenDots {
  0%   { content: ""; }
  25%  { content: "."; }
  50%  { content: ".."; }
  75%  { content: "..."; }
  100% { content: ""; }
}
@media (prefers-reduced-motion: reduce) { .regen-dots::after { content: "…"; animation: none; } }

/* ====================================================================
   Apple Pencil teeth-mask editor (SMILE_SIM_PENCIL_MASK_EDITOR).
   The draw layer sits ABOVE the WebGL canvas (z-index 6 > split-before's
   z-index 5) and is positioned/sized to the painted object-contain box by
   _syncMaskOverlayRect(). Pointer events route to it; the system cursor is
   hidden in favor of the .brush-cursor-ring. touch-action:none so pen/finger
   strokes don't scroll/zoom (also set inline as a belt-and-braces).
   ==================================================================== */
.mask-draw-layer {
  position: absolute;
  z-index: 6;
  cursor: none;
  touch-action: none;
  pointer-events: auto;
  border-radius: 0.75rem;
}
.brush-cursor-ring {
  display: none;   /* belt-and-braces: x-show flips to block only while actively painting */
  position: absolute;
  z-index: 7;
  pointer-events: none;
  border: 2px solid rgba(167, 139, 250, 0.95);   /* --accent-strong */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55), inset 0 0 0 1px rgba(0, 0, 0, 0.35);
  background: rgba(139, 92, 246, 0.10);
  border-radius: 50%;
  transform: translate(-50%, -50%);              /* center on the cursor point */
}

/* Staff shade-tools cluster — opacity + Mask + Brush kept inline on ONE row next to
   the swatches (gap handles spacing; the per-notch margin-left is zeroed below). */
.shade-tools { display: inline-flex; align-items: center; gap: 8px; margin-left: 10px; }
.shade-tools .mask-notch { margin-left: 0; }
.shade-tools .opacity-control { margin-left: 0; }

/* ---- Brush toolbar (own full-width row, shown only while editing the mask) ----
   2026 touch layout: a size slider with a live preview dot + readout, an Add/Erase
   segment, then Cancel/Save — generous targets, no cramped −/+ steppers. */
/* Glassmorphism control button — modern, theme-aware; used by the brush toolbar
   (Add/Erase/Undo/Redo/Cancel/Apply) so they match the Present/Save dock buttons. */
.glass-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 46px; min-width: 46px; padding: 0 16px;
  border-radius: 13px;
  border: 1px solid var(--hairline);
  background: var(--panel-solid);
  color: var(--text);
  font-family: var(--mono); font-size: 12px; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase;
  cursor: pointer;
  -webkit-backdrop-filter: blur(10px) saturate(1.2);
  backdrop-filter: blur(10px) saturate(1.2);
  box-shadow: 0 1px 0 rgba(255,255,255,0.08) inset, 0 6px 16px rgba(0,0,0,0.16);
  transition: background 140ms ease, border-color 140ms ease, transform 110ms ease, color 140ms ease;
}
.glass-btn:hover:not(:disabled) { border-color: var(--accent); }
.glass-btn:active:not(:disabled) { transform: translateY(1px); }
.glass-btn:disabled { opacity: 0.38; cursor: not-allowed; }
.glass-btn.active { background: var(--accent-soft); border-color: var(--accent-strong); color: var(--accent-strong); }
.glass-btn--primary { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; box-shadow: 0 6px 18px -4px var(--accent-strong); }
.glass-btn--primary:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); }
.glass-btn .gico { display: inline-flex; }
.glass-btn .gico svg { width: 20px; height: 20px; stroke: currentColor; fill: none; }

.brush-toolbar-wrap { width: 100%; justify-content: center; }
.brush-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 16px;
  width: 100%;
  padding: 4px 6px;
}
.brush-toolbar .bt-size { display: inline-flex; align-items: center; gap: 10px; }
.brush-toolbar .bt-dot {
  display: inline-block;
  border-radius: 50%;
  background: var(--text);
  border: 1px solid var(--accent-strong);
  box-shadow: 0 0 8px -2px var(--accent-strong);
  flex: 0 0 auto;
  transition: width 90ms ease, height 90ms ease;
}
.brush-toolbar .bt-slider {
  -webkit-appearance: none;
  appearance: none;
  width: 168px;
  height: 26px;
  margin: 0;
  background: transparent;
  cursor: pointer;
}
.brush-toolbar .bt-slider:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 4px; }
.brush-toolbar .bt-slider::-webkit-slider-runnable-track {
  height: 6px; border-radius: 999px;
  background: linear-gradient(to right, var(--accent-soft), var(--accent));
  border: 1px solid var(--hairline);
}
.brush-toolbar .bt-slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 22px; height: 22px; margin-top: -9px;
  border-radius: 50%; background: var(--text);
  border: 2px solid var(--accent-strong);
  box-shadow: 0 0 10px -2px var(--accent-strong);
}
.brush-toolbar .bt-slider::-moz-range-track {
  height: 6px; border-radius: 999px;
  background: linear-gradient(to right, var(--accent-soft), var(--accent));
  border: 1px solid var(--hairline);
}
.brush-toolbar .bt-slider::-moz-range-thumb {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--text); border: 2px solid var(--accent-strong);
  box-shadow: 0 0 10px -2px var(--accent-strong);
}
.brush-toolbar .bt-readout {
  min-width: 48px;
  font-family: var(--mono, monospace);
  font-size: 12px;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}
.brush-toolbar .bt-actions { display: inline-flex; align-items: center; gap: 8px; }

/* Mask-edit toggle styled as a shade-rail notch (icon + "Mask" label), matching
   .shade-rail-h .rail-notch / .swatch dimensions so heights line up. */
.mask-notch {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-height: 48px;
  min-width: 48px;
  padding: 4px 6px;
  margin-left: 8px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-faint);
  cursor: pointer;
  transition: border-color 140ms ease, color 140ms ease, background 140ms ease;
}
.mask-notch .mask-ico { width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; }
.mask-notch .mask-ico svg { width: 100%; height: 100%; display: block; }
.mask-notch .lbl { font-family: var(--mono); font-size: 12px; letter-spacing: 0.06em; }
/* active: 3-class selector overrides the legacy solid-fill .mask-edit-toggle.active
   so the active notch matches .rail-notch.active (outline + accent text); the green
   tooth fill stays (hardcoded #00e000, independent of color). */
.mask-notch.mask-edit-toggle.active,
.mask-notch.mask-edit-toggle[aria-pressed="true"],
.mask-notch.mask-viz-toggle.active,
.mask-notch.mask-viz-toggle[aria-pressed="true"],
.mask-notch.opacity-toggle.active,
.mask-notch.opacity-toggle[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--accent-strong);
}

/* "No color" / transparent shade swatch — WHITE tooth outline + diagonal red slash,
   no fill (Illustrator no-color convention). The white outline reads clearly on the
   rail (the container drop-shadow keeps it legible on light backgrounds too). */
.shade-rail-h .rail-notch.transparent-notch .tooth-swatch.transparent-swatch path {
  fill: none;
  stroke: #ffffff;
  stroke-width: 1.4;
  vector-effect: non-scaling-stroke;
}
.shade-rail-h .rail-notch.transparent-notch .no-color-slash {
  stroke: var(--danger);
  stroke-width: 2;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.shade-rail-h .rail-notch.transparent-notch.active .tooth-swatch.transparent-swatch path {
  stroke: var(--accent-strong);
  stroke-width: 1.1;
}
.mask-mode-seg { display: inline-flex; gap: 0; }
.mask-mode-seg .seg-btn {
  min-width: 56px;
  padding: 0 12px;
  border-radius: 0;
}
.mask-mode-seg .seg-btn:first-child {
  border-top-left-radius: var(--radius-sm);
  border-bottom-left-radius: var(--radius-sm);
}
.mask-mode-seg .seg-btn:last-child {
  border-top-right-radius: var(--radius-sm);
  border-bottom-right-radius: var(--radius-sm);
  border-left: none;
}
.mask-mode-seg .seg-btn.active,
.mask-mode-seg .seg-btn[aria-pressed="true"] {
  background: var(--accent-soft);
  border-color: var(--accent);
  color: var(--text);
}
.mask-save-btn { min-width: 72px; }
.mask-cancel-btn { min-width: 72px; }

/* ====== Bigger brush-edit (Feature B) ======
   When the Brush editor opens, the Before pane is hidden and the grid collapses to ONE
   column (.brush-solo) so the AFTER canvas spans the FULL width — much bigger for pen
   work. This is kept IN NORMAL FLOW (NOT position:fixed) and the canvas stays w-full /
   top-left, so the mask draw-overlay (positioned by _syncMaskOverlayRect) stays exactly
   aligned with the canvas — no centering offset, no containing-block surprise. A
   ResizeObserver re-syncs the overlay when the canvas widens. Save/Cancel removes the
   class → the prior dual/split layout returns. */
.brush-solo { grid-template-columns: 1fr !important; }
/* a little taller than the default 72vh while editing (override the inline cap). */
.split-stage.brush-full canvas { max-height: 80vh !important; }

/* ---------- Portrait / narrow: let the command bar wrap so nothing is cropped ----------
   The bottom command bar is a fixed-width flex row. The brush toolbar already takes its
   own full-width row; here we just let the bar + teleported canvas-controls wrap so the
   shade rail + shade-tools never overflow the right edge in portrait on iPad. */
@media (orientation: portrait), (max-width: 900px) {
  .command-bar {
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px 12px;
    padding: 12px 12px;
  }
  /* The flex spacer fights wrapping (forces a full-width gap row) — drop it so the
     groups simply center and wrap onto clean rows in portrait. */
  .command-bar .cb-spacer { display: none; }
  .command-bar > .cb-group { flex-wrap: wrap; justify-content: center; min-width: 0; }
  /* the teleport host + its inner wrapper must wrap too */
  #cb-canvas-slot,
  .cb-canvas-controls {
    flex-wrap: wrap;
    justify-content: center;
    max-width: 100%;
    min-width: 0;
    gap: 10px;
  }
  .cb-canvas-controls .cb-group { flex-wrap: wrap; justify-content: center; min-width: 0; }
  /* keep the shade-tools cluster intact (don't let opacity/Mask/Brush split apart) */
  .shade-tools { flex-wrap: nowrap; }
  /* brush toolbar: wrap its clusters and let them center instead of cropping */
  .brush-toolbar { gap: 10px 14px; padding: 4px 2px; }
  .brush-toolbar .bt-size { flex: 1 1 100%; justify-content: center; }
}

/* ---------- Landscape on touch (iPad): analysis strip + command-bar fixes ----------
   (1) The always-on Smile Analysis chip strip was getting hidden behind the fixed
       command bar in landscape (short viewport). Hide it there — the same metrics
       live in the Full Smile Report side pane, and the report button still shows.
   (2) The bottom command bar is a fixed-width flex row that did NOT wrap outside
       portrait, so on narrower iPads in landscape the primary group (incl. the green
       "Send to Patient" CTA) overflowed past the bar's right edge and became
       unreachable. Let it wrap + shrink here too, exactly like portrait. */
/* The landscape-only Full Report button: hidden in portrait (the analysis card carries
   its own inline "Full Report ›"); shown only where the card itself is hidden. */
.report-cta-landscape { display: none; }

@media (orientation: landscape) and (pointer: coarse) {
  .smile-analysis-chips { display: none !important; }
  .report-cta-landscape { display: flex !important; }

  .command-bar {
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px 12px;
    padding: 12px 14px;
  }
  .command-bar .cb-spacer { display: none; }
  .command-bar > .cb-group { flex-wrap: wrap; justify-content: center; min-width: 0; }
  #cb-canvas-slot,
  .cb-canvas-controls {
    flex-wrap: wrap;
    justify-content: center;
    max-width: 100%;
    min-width: 0;
    gap: 10px;
  }
  .cb-canvas-controls .cb-group { flex-wrap: wrap; justify-content: center; min-width: 0; }
}
