/* ============================================================
   design-system.css — v1.0 (2026-05-10)

   Single source of truth for the candidate interview experience.
   Used by templates/interview_orb.html (Phase 1 orb UI). Will also
   be wired into preinterview.html and the post-interview ending
   screen in Batch 2/3.

   IMPORTANT: This file is referenced ONLY by recording-on
   templates. The legacy waveform UI (templates/index.html +
   static/js/app.js + static/css/styles.css) is untouched and
   remains the fallback when recording.enabled is false.

   Structure:
     1.  Tokens (color, type, space, shadow, radius, motion)
     2.  Reset / base type
     3.  Atmosphere (page background)
     4.  Layout primitives
     5.  Components (header chrome, buttons, cards, chips, etc.)
     6.  Orb (interview-page specific)
     7.  Toast / modals / overlays
     8.  Wizard (preinterview multi-step flow)
     9.  Ending screen + feedback area
     10. Accessibility (focus rings, reduced-motion)
   ============================================================ */


/* ============================================================
   1. TOKENS
   ============================================================ */
:root {
  /* ── Color: pearl-white palette (replaces ElevenLabs warm cream
     so the cool orb doesn't fight a warm background). Premium
     SaaS standard (Stripe / Vercel / Linear range). ── */
  --color-eggshell:    #fcfcfc;       /* body bg, near-white reduces 30-min session glare */
  --color-pearl:       #f8f9fb;       /* secondary surface, faint cool tint */
  --color-card:        #ffffff;       /* question card */
  --color-powder:      #f3f4f6;
  --color-chalk:       #e5e7ea;
  --color-fog:         #b1b3b6;
  --color-gravel:      #6e7178;       /* secondary text */
  --color-slate:       #9ea1a8;       /* tertiary / inactive */
  --color-obsidian:    #0a0a0a;       /* primary text + primary CTA */
  --color-signal-blue: #0447ff;       /* focus rings + active-cue accents */
  --color-ember:       #ff4704;       /* destructive / recording */
  --color-ember-soft:  rgba(255, 71, 4, 0.08);
  --color-success:     #166534;
  --color-warn:        #92400e;

  /* Form/auth-surface borders — sized to be visible on white without
     relying on atmosphere halo for contrast. Added when the design
     system was extended beyond interview-only surfaces (auth, landing,
     account). Use --color-chalk only for hairline rules; for any
     interactive border (input/secondary-button/auth-card), use these. */
  --color-border-input:        rgba(10, 10, 10, 0.14);
  --color-border-input-hover:  rgba(10, 10, 10, 0.22);
  --color-border-card:         rgba(10, 10, 10, 0.06);

  /* Eyebrow pill tints — informational blue variant */
  --color-tint-blue:           rgba(220, 232, 250, 0.55);
  --color-tint-blue-border:    rgba(58, 116, 218, 0.18);

  /* Active-state caption colors (subtle blue/red tint per state) */
  --color-cue-listen:  #2f6ad9;
  --color-cue-speak:   #c2350a;

  /* Semantic score-badge backgrounds (used in feedback area) */
  --color-score-high-bg: #dcfce7;
  --color-score-mid-bg:  #fef3c7;
  --color-score-low-bg:  rgba(255, 71, 4, 0.12);

  /* ── Voice-spectrum conic gradient — orb body ── */
  --voice-spectrum: conic-gradient(from 180deg,
    rgb(61, 117, 216) 11.59deg, rgb(42, 104, 210) 26.32deg,
    rgb(117, 190, 229) 32.39deg, rgb(82, 208, 233) 38.91deg,
    rgb(33, 89, 186) 75.35deg, rgb(58, 116, 218) 85.04deg,
    rgb(108, 215, 236) 94.53deg, rgb(90, 185, 241) 122.11deg,
    rgb(51, 161, 229) 129.97deg, rgb(31, 95, 207) 136.72deg,
    rgb(44, 84, 202) 144.31deg, rgb(58, 158, 207) 175.99deg,
    rgb(49, 103, 197) 183.87deg, rgb(173, 232, 243) 201.64deg,
    rgb(216, 241, 245) 224.87deg, rgb(165, 219, 230) 239.73deg,
    rgb(43, 157, 214) 268.17deg, rgb(174, 224, 233) 286.21deg,
    rgb(32, 186, 208) 329.65deg, rgb(30, 83, 176) 343.55deg,
    rgb(47, 64, 210) 359.26deg);

  /* ── Typography ── */
  --font-ui:    'Inter', ui-sans-serif, system-ui, sans-serif;
  --font-mono:  'Geist Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* ── Shadow stacks ── */
  --shadow-card-soft: 0 1px 2px rgba(0,0,0,0.02), 0 6px 16px rgba(0,0,0,0.03), 0 24px 48px rgba(0,0,0,0.04);
  --shadow-card:      0 1px 2px rgba(0,0,0,0.03), 0 8px 20px rgba(20, 30, 50, 0.05), 0 28px 56px rgba(20, 30, 50, 0.06);
  --shadow-inset:     inset 0 0 0 1px rgba(0,0,0,0.04);
  --shadow-button:    0 1px 2px rgba(0,0,0,0.04), 0 0 0 1px rgba(0,0,0,0.06);
  --shadow-modal:     0 24px 48px rgba(0,0,0,0.18);
  --shadow-pip:       0 0 0 1px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04), 0 16px 40px rgba(0,0,0,0.08);

  /* ── Radius scale ── */
  --radius-sm: 5px;
  --radius-md: 12px;
  --radius-lg: 16px;
  --radius-xl: 18px;
  --radius-pill: 9999px;

  /* ── Motion (keep all animations subtle for 30-min sessions) ── */
  --ease-out: cubic-bezier(0.4, 0, 0.2, 1);
  --duration-fast: 150ms;
  --duration-base: 300ms;
  --duration-slow: 600ms;

  /* ── App header (landing / wizard / orb) ──
     Centralized so .progress-bar offset and .ending-screen padding-top
     stay in sync when padding changes. --header-h must equal
     (--header-padding-y * 2) + brand-pill-height(40) + 1px border. */
  --header-padding-y: 8px;
  --header-padding-x: 32px;
  --header-h: 57px;
}


/* ============================================================
   2. RESET / BASE
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: var(--font-ui);
  background: var(--color-eggshell);
  color: var(--color-obsidian);
  min-height: 100vh;
  font-size: 14px;
  line-height: 1.43;
  letter-spacing: 0.1px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  position: relative;
}

/* Default for the orb interview page — overflow:hidden so PIP
   and overlays anchor cleanly. Other pages that use this CSS
   (preinterview, etc.) can override on body class. */
body.page-orb { overflow: hidden; }

/* Pre-Socket-connect dim — the `connecting` class is set on body
   when the orb template renders and removed by orb_app.js once
   Socket.IO connects. Provides a subtle "not yet ready" cue
   alongside the "Connecting" caption. */
body.page-orb.connecting main { opacity: 0.5; }


/* ============================================================
   3. ATMOSPHERE — cool radial wash that extends the orb's halo
      into the page (replaces warm cream that was fighting
      the orb's blue color family).
   ============================================================ */
.atmosphere::before {
  content: '';
  position: fixed;
  inset: 0;
  background:
    radial-gradient(ellipse 70% 55% at 50% 38%,
      rgba(220, 232, 250, 0.45) 0%,
      rgba(220, 232, 250, 0.15) 35%,
      transparent 65%),
    radial-gradient(ellipse 100% 80% at 50% 100%,
      rgba(225, 232, 245, 0.25) 0%,
      transparent 55%);
  pointer-events: none;
  z-index: 0;
}

.atmosphere::after {
  content: '';
  position: fixed;
  inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.4 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.04;
  mix-blend-mode: multiply;
  pointer-events: none;
  z-index: 1;
}


/* ============================================================
   4. LAYOUT — header, main stage, top progress bar
   ============================================================ */
/* Progress bar + hairline divider — sit below the header.
   Positions bumped 76→84 / 78→86 in v6 polish to clear the taller
   header (40→48px) so the chrome stack reads as a clean band. */
.progress-bar {
  position: fixed;
  /* Sits flush against the header's bottom border — uses the same
     --header-h token so this stays in sync if header padding changes. */
  top: var(--header-h);
  left: 0;
  width: 100%;
  height: 3px;
  background: rgba(0,0,0,0.04);
  z-index: 60;
}
.progress-fill {
  height: 100%;
  width: 0%;
  /* Signal-blue (the design system's active-cue accent) so the bar
     reads unmistakably as interview progress, not the header border. */
  background: var(--color-signal-blue);
  transition: width 0.4s ease;
}
/* Hidden: the new header carries its own border-bottom, so this
   separate divider line is redundant. Element kept for HTML
   compatibility but visually suppressed. */
.header-divider {
  display: none;
}

/* Orb-screen header — fixed-position variant of .app-header so it
   floats over the orb stage. NOTE: this is a CLASS (.header-orb),
   not the bare `header` element, because the bare selector silently
   applied to every <header> in the app regardless of class and was
   the root cause of a multi-iteration alignment bug in 2026-05. */
.header-orb {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  padding: var(--header-padding-y) var(--header-padding-x);
  display: flex;
  justify-content: space-between;
  align-items: center;
  /* Above .ending-screen (z-index: 150) so the brand bar stays
     visible on the thank-you screen — same chrome top to bottom
     of the candidate journey. */
  z-index: 200;
  background: var(--color-pearl);
  border-bottom: 1px solid var(--color-border-card);
}

/* When the ending-screen is shown, the in-interview controls on the
   right side of the header are no longer meaningful — hide them so
   the bar shows brand only, matching landing / select-interview. */
body:has(.ending-screen.active) .rec-indicator,
body:has(.ending-screen.active) .elapsed,
body:has(.ending-screen.active) .end-btn,
body:has(.ending-screen.active) .header-divider-v {
  display: none;
}
.header-left  { display: flex; align-items: center; gap: 18px; }
.header-right { display: flex; align-items: center; gap: 18px; }

.header-divider-v {
  width: 1px;
  height: 22px;
  background: var(--color-chalk);
}

/* Main stage — viewport-adaptive optical centering. The cluster
   sits at ~38-42% from top on every monitor size (14" MBP at
   100% and 27" external both render the same relative position). */
.main-stage {
  position: relative;
  z-index: 2;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 100px 32px 60px;
}


/* ============================================================
   5. COMPONENTS
   ============================================================ */

/* ── Brand wordmark (top-left) ──
   36px image height (bumped from 28 in v6 polish) so customer brand
   has proper presence next to the 180px orb. max-width keeps wide
   wordmarks bounded.

   Logo frame (v6 polish 2026-05-10): customer-uploaded logos often
   have a white background fill (not transparent). On surfaces with
   the cool atmospheric page bg, that white square jutted out as a
   visible rectangle. Wrapping the img in a subtle white rounded
   chip eliminates the visual seam regardless of the underlying
   logo's bg — transparent or white-fill, both look clean.

   Height retuned from 36→30 so the chip total (img + 5px padding
   × 2 = 40px) fits within the 48px header with 4px breathing room.

   Wordmark text fallback (.brand-name) is intentionally NOT framed —
   text doesn't have the white-rectangle problem to solve. */
.brand          { display: flex; align-items: center; }
.brand img {
  height: 30px;
  width: auto;
  max-width: 160px;
  object-fit: contain;
  background: #ffffff;
  padding: 5px 10px;
  border-radius: 10px;
  border: 1px solid rgba(0,0,0,0.05);
  box-shadow: 0 1px 2px rgba(0,0,0,0.03);
  box-sizing: content-box;
}
.brand-name {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 18px;
  letter-spacing: -0.2px;
  color: var(--color-obsidian);
}

/* ── Recording chip (next to brand when recording) ── */
.rec-indicator {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 11px 5px 9px;
  border-radius: var(--radius-pill);
  background: var(--color-ember-soft);
  border: 1px solid rgba(255, 71, 4, 0.15);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.6px;
  color: var(--color-cue-speak);
  text-transform: uppercase;
}
.rec-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--color-ember);
  box-shadow: 0 0 0 0 rgba(255, 71, 4, 0.5);
  animation: rec-pulse 2s ease-in-out infinite;
}
@keyframes rec-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,71,4,0.45); }
  70%      { box-shadow: 0 0 0 6px rgba(255,71,4,0); }
}

/* ── Elapsed timer ── */
.elapsed {
  font-family: var(--font-mono);
  font-size: 14px;
  font-weight: 500;
  color: var(--color-obsidian);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.2px;
}

/* ── Buttons ── */

/* Ghost pill (default for End interview, Cancel buttons).
   Hover state shifts to ember red — destructive-action affordance
   visible only when targeted, neutral at rest. */
.end-btn {
  background: rgba(255,255,255,0.7);
  backdrop-filter: blur(8px);
  color: var(--color-obsidian);
  border: 1px solid rgba(0,0,0,0.06);
  height: 36px;
  padding: 0 16px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.1px;
  cursor: pointer;
  box-shadow: var(--shadow-button);
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.end-btn:hover {
  background: #ffffff;
  color: var(--color-cue-speak);
  border-color: rgba(255, 71, 4, 0.35);
}

/* Modal action buttons (used in exit-modal) */
.btn-link {
  background: none;
  border: none;
  color: var(--color-gravel);
  font-family: var(--font-ui);
  font-size: 14px;
  cursor: pointer;
  padding: 0 8px;
}
.btn-link:hover { color: var(--color-obsidian); }

/* Primary obsidian pill — used for "I agree, continue", "Continue",
   "Begin interview", exit-modal "End interview" submit. The single
   primary CTA class across the candidate-facing app. */
.btn-primary {
  background: var(--color-obsidian);
  color: #ffffff;
  border: 1px solid var(--color-obsidian);
  height: 40px;
  padding: 0 18px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  box-shadow: var(--shadow-button);
  transition: transform 0.15s ease, box-shadow 0.2s ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}
.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(0,0,0,0.08), 0 12px 32px rgba(0,0,0,0.12);
}
.btn-primary:active { transform: translateY(0); }
.btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
  box-shadow: var(--shadow-button);
}

/* ── Question card (AI's question display) ── */
.question-card {
  background: var(--color-card);
  border-radius: var(--radius-xl);
  padding: 22px 32px 26px;
  max-width: 740px;
  border: 1px solid rgba(0,0,0,0.05);
  box-shadow: var(--shadow-card);
  transition: opacity 0.4s ease, transform 0.4s ease;
  text-align: center;
  position: relative;
}
.question-card p {
  font-size: 18px;
  line-height: 1.55;
  color: var(--color-obsidian);
  font-weight: 400;
  letter-spacing: -0.1px;
}
.question-card.dimmed { opacity: 0.45; transform: scale(0.985); }
.question-card.hidden { opacity: 0; transform: translateY(8px); pointer-events: none; }

/* ── Fix #5: Audio fallback controls ── */
/* Shown only when audio_unavailable event fires (both TTS providers failed).
   Candidate reads the question card above, then clicks Ready to continue.
   Hidden by default and re-hidden after Ready is clicked. */
.audio-fallback-controls {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  margin-top: 24px;
  max-width: 740px;
  text-align: center;
  transition: opacity 0.3s ease;
}
.audio-fallback-controls.hidden {
  opacity: 0;
  pointer-events: none;
  height: 0;
  margin-top: 0;
  overflow: hidden;
}
.audio-fallback-notice {
  font-size: 14px;
  color: var(--color-gravel);
  font-style: italic;
  margin: 0;
}
.audio-fallback-ready-btn {
  font-family: inherit;
  font-size: 16px;
  font-weight: 500;
  padding: 12px 32px;
  border-radius: var(--radius-md);
  background: var(--color-obsidian);
  color: #fff;
  border: none;
  cursor: pointer;
  transition: transform 0.15s ease, background 0.15s ease;
  box-shadow: var(--shadow-card);
}
.audio-fallback-ready-btn:hover {
  transform: translateY(-1px);
}
.audio-fallback-ready-btn:active {
  transform: translateY(0);
}
.audio-fallback-ready-btn:focus-visible {
  outline: 2px solid var(--color-cue-speak, #4a90e2);
  outline-offset: 3px;
}

/* ── AI speaker label (small chip in question card top-left) ── */
.ai-speaker-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 10px;
  font-family: var(--font-ui);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.8px;
  color: var(--color-gravel);
  text-transform: uppercase;
}
.ai-speaker-dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: linear-gradient(135deg, #6c8ee0 0%, #3a74da 100%);
}

/* ── Ambient keyboard hint (bottom-left corner) ──
   Always visible with two-state opacity that tracks orb interactivity.
   Persistent affordance — show/hide jitter would draw attention every
   state change. Quiet by default, full presence when actionable.
     - Default (non-actionable: connecting / thinking / speaking):
       opacity 0.45 — present, demoted; doesn't mislead the user into
       pressing SPACE during AI speech
     - .actionable (idle / listening): opacity 0.9 — full presence */
.kbd-hint {
  position: fixed;
  bottom: 32px;
  left: 32px;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 400;
  color: var(--color-gravel);
  letter-spacing: 0.1px;
  z-index: 30;
  transition: opacity 0.4s ease;
  opacity: 0.45;
}
.kbd-hint.actionable { opacity: 0.9; }
/* Touch devices have no SPACE key — hide the hint to avoid confusion.
   `(hover: none) and (pointer: coarse)` reliably matches touch primary
   without misfiring on hybrid laptops. */
@media (hover: none) and (pointer: coarse) {
  .kbd-hint { display: none; }
}
.kbd-key {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  color: var(--color-obsidian);
  background: #ffffff;
  border: 1px solid rgba(0,0,0,0.10);
  border-radius: var(--radius-sm);
  padding: 2px 8px;
  letter-spacing: 0.6px;
  box-shadow: 0 1px 0 rgba(0,0,0,0.06);
}

/* ── PIP self-view (webcam preview) ── */
.pip {
  position: fixed;
  bottom: 32px;
  right: 32px;
  width: 240px;
  height: 160px;
  background: #ffffff;
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-pip);
  overflow: hidden;
  transition: opacity 0.3s ease, transform 0.3s ease;
  z-index: 40;
}
.pip.hidden {
  opacity: 0;
  transform: translateY(8px) scale(0.96);
  pointer-events: none;
}
.pip-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--color-pearl);
  transform: scaleX(-1);
}
.pip-placeholder {
  height: 100%;
  background: var(--color-pearl);
  display: flex;
  align-items: center;
  justify-content: center;
}
.pip-camera-icon {
  width: 28px; height: 22px;
  border: 1.5px solid var(--color-fog);
  border-radius: var(--radius-sm);
  position: relative;
  opacity: 0.55;
}
.pip-camera-icon::after {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 10px; height: 10px;
  border: 1.5px solid var(--color-fog);
  border-radius: 50%;
}
.pip-label {
  position: absolute;
  bottom: 10px;
  left: 10px;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px 4px 7px;
  border-radius: var(--radius-pill);
  background: rgba(10,10,10,0.55);
  backdrop-filter: blur(8px);
  color: #ffffff;
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.4px;
}
.pip-mic-icon {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: #4ade80;
  box-shadow: 0 0 4px rgba(74, 222, 128, 0.6);
}
/* .pip-close removed in v6.1 (2026-05-10) — the button only hid the PIP
   overlay; MediaRecorder + GCS upload were unaffected. Misleading affordance
   for candidates who thought they were stopping video recording. Recording
   is non-negotiable per consent, so the button is gone entirely. */


/* ============================================================
   6. ORB — interview-page specific
   ============================================================ */

.orb-container {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-bottom: 36px;
}
.orb-wrap {
  position: relative;
  width: 200px;
  height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Outer halo — multi-hue (cyan-warm at top, blue at bottom),
   matches orb gradient direction. State-dependent re-tint. */
.orb-wrap::before {
  content: '';
  position: absolute;
  width: 440px;
  height: 440px;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 35%,
      rgba(108, 215, 236, 0.42) 0%,
      rgba(82, 208, 233, 0.28) 18%,
      transparent 55%),
    radial-gradient(circle at 50% 65%,
      rgba(58, 116, 218, 0.50) 0%,
      rgba(33, 89, 186, 0.22) 22%,
      rgba(58, 116, 218, 0.08) 45%,
      transparent 70%);
  filter: blur(56px);
  pointer-events: none;
  z-index: -2;
  transition: opacity 0.4s ease, transform 0.6s ease;
}
.orb-wrap.listening::before { animation: halo-breathe 1.6s ease-in-out infinite; }
.orb-wrap.speaking::before  { animation: halo-pulse   1.4s ease-in-out infinite; }
.orb-wrap.thinking::before  { opacity: 0.65; }

/* Inner halo — tight bloom hugging the orb */
.inner-halo {
  position: absolute;
  width: 240px;
  height: 240px;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(108, 215, 236, 0.40) 0%,
    rgba(58, 116, 218, 0.20) 38%,
    transparent 65%);
  filter: blur(14px);
  pointer-events: none;
  z-index: -1;
}

/* Concentric ring (only on listening) */
.orb-wrap::after {
  content: '';
  position: absolute;
  width: 210px;
  height: 210px;
  border-radius: 50%;
  border: 1px solid rgba(0,0,0,0.06);
  pointer-events: none;
  transition: opacity 0.3s ease;
  opacity: 0;
}
.orb-wrap.listening::after {
  opacity: 1;
  animation: ring-expand 1.6s ease-out infinite;
}

@keyframes ring-expand {
  0%   { transform: scale(1);    opacity: 0.7; }
  100% { transform: scale(1.28); opacity: 0; }
}
@keyframes halo-breathe {
  0%, 100% { transform: scale(1);    opacity: 1; }
  50%      { transform: scale(1.05); opacity: 0.9; }
}
@keyframes halo-pulse {
  0%, 100% { transform: scale(1);    opacity: 1; }
  50%      { transform: scale(1.05); opacity: 0.92; }
}

/* The orb itself */
.orb {
  width: 180px;
  height: 180px;
  border-radius: 50%;
  background: var(--voice-spectrum);
  box-shadow:
    var(--shadow-inset),
    0 1px 2px rgba(0,0,0,0.06),
    0 20px 50px rgba(33, 89, 186, 0.20);
  cursor: pointer;
  position: relative;
  transition: transform 0.4s var(--ease-out);
  outline: none;
}
.orb::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, rgba(255, 255, 255, 0.5), transparent 55%);
  pointer-events: none;
}
.orb:hover { transform: scale(1.015); }

/* Orb state animations — all subtle for 30-min session comfort.
   Speaking was the worst offender at 12% scale jumps in 0.7s
   cycle; now 4% in 1.4s, no rotation. Listening stripped of
   rotation. Thinking slowed to 18s. */
.orb-idle       { animation: idle-breathing 6s ease-in-out infinite; }
.orb-listening  { animation: listening-pulse 1.6s ease-in-out infinite; }
.orb-thinking   { animation: thinking-rotate 18s linear infinite; }
.orb-speaking   { animation: speaking-pulse 1.4s ease-in-out infinite; }
.orb-connecting { animation: idle-breathing 6s ease-in-out infinite; }

@keyframes idle-breathing {
  0%, 100% { transform: scale(1)    rotate(0deg); }
  50%      { transform: scale(1.03) rotate(8deg); }
}
@keyframes listening-pulse {
  0%, 100% { transform: scale(1.0)  rotate(0deg); }
  50%      { transform: scale(1.03) rotate(0deg); }
}
@keyframes thinking-rotate {
  0%   { transform: rotate(0deg)   scale(1.0); }
  100% { transform: rotate(360deg) scale(1.0); }
}
@keyframes speaking-pulse {
  0%, 100% { transform: scale(1.0)  rotate(0deg); }
  50%      { transform: scale(1.04) rotate(0deg); }
}

/* State caption — does double-duty as status + action cue.
   Idle / listening = action cues, color-tinted.
   Thinking / speaking = passive status with animated dots. */
.orb-state-caption {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 15px;
  letter-spacing: 0.1px;
  color: var(--color-gravel);
  height: 22px;
  transition: opacity 0.3s ease, color 0.3s ease;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.orb-state-caption.active-cue     { color: var(--color-obsidian); font-weight: 500; }
.orb-state-caption.active-listen  { color: var(--color-cue-listen); font-weight: 500; }
.orb-state-caption.passive-status { color: var(--color-gravel); font-weight: 500; }
.orb-state-caption.active-speak   { color: var(--color-cue-speak); font-weight: 500; }

/* Inline animated dots inside caption (thinking / speaking) */
.caption-dots {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.caption-dots span {
  width: 4px; height: 4px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.4;
  animation: caption-typing 1.2s ease-in-out infinite;
}
.caption-dots span:nth-child(2) { animation-delay: 0.15s; }
.caption-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes caption-typing {
  0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
  30%           { opacity: 1;    transform: translateY(-2px); }
}


/* ============================================================
   7. TOAST / MODALS / OVERLAYS
   ============================================================ */

/* ── Toast (non-blocking error / status surface) ── */
.toast-container {
  position: fixed;
  top: 96px;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: 480px;
  padding: 0 16px;
  z-index: 240;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.toast {
  pointer-events: auto;
  background: #ffffff;
  border-left: 4px solid var(--color-ember);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  box-shadow: var(--shadow-inset), var(--shadow-card-soft);
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-family: var(--font-ui);
  font-size: 14px;
  color: var(--color-obsidian);
  line-height: 1.5;
  animation: toast-in 0.25s ease-out;
}
.toast.fade-out { animation: toast-out 0.25s ease-in forwards; }
.toast-icon {
  flex-shrink: 0;
  color: var(--color-ember);
  font-weight: 700;
  font-size: 16px;
  line-height: 1.2;
}
.toast-msg { flex: 1; }
.toast-close {
  flex-shrink: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-gravel);
  font-size: 18px;
  line-height: 1;
  padding: 0 4px;
}
.toast-close:hover { color: var(--color-obsidian); }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* ── Long-answer warning banner (Req 1) ──
   Top-center heads-up shown at 5 min / 7 min during a recording, and on
   force-end at 8 min. Amber accent (warn, not error). No buttons, no
   close — auto-dismiss only. Positioned ~32px above the toast so a
   toast + banner combo doesn't collide visually.
   See tasks/req1_implementation_plan.md and prototypes/req1_long_answer_warnings.html. */
.long-answer-banner-anchor {
  position: fixed;
  top: 64px;
  left: 0; right: 0;
  z-index: 240;
  pointer-events: none;
  display: flex;
  justify-content: center;
}
.long-answer-banner {
  pointer-events: auto;
  background: #ffffff;
  border-left: 4px solid var(--color-warn);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  box-shadow: var(--shadow-inset), var(--shadow-card-soft);
  display: flex;
  align-items: flex-start;
  gap: 12px;
  font-family: var(--font-ui);
  font-size: 14px;
  color: var(--color-obsidian);
  line-height: 1.5;
  max-width: 480px;
  width: calc(100% - 32px);
  animation: long-answer-in 0.25s ease-out;
}
.long-answer-banner.is-hidden { display: none; }
.long-answer-banner.fade-out { animation: long-answer-out 0.25s ease-in forwards; }
.long-answer-banner-icon {
  flex-shrink: 0;
  color: var(--color-warn);
  font-size: 18px;
  line-height: 1.2;
}
.long-answer-banner-msg { flex: 1; }
@keyframes long-answer-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes long-answer-out {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-8px); }
}

@keyframes toast-out {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-8px); }
}

/* ── Reconnect overlay ── */
.reconnect-overlay {
  position: fixed;
  inset: 0;
  background: rgba(252, 252, 252, 0.92);
  backdrop-filter: blur(4px);
  display: none;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 16px;
  z-index: 200;
}
.reconnect-overlay.active { display: flex; }
.reconnect-overlay .msg {
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 500;
  color: var(--color-obsidian);
}
.reconnect-overlay .sub {
  font-family: var(--font-ui);
  font-size: 13px;
  color: var(--color-gravel);
}

/* ── Autoplay-fail overlay (iOS Safari unlock) ── */
.autoplay-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.65);
  backdrop-filter: blur(2px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 250;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.autoplay-overlay__card {
  background: var(--color-eggshell);
  border-radius: var(--radius-lg);
  padding: 32px 36px;
  max-width: 420px;
  box-shadow: var(--shadow-inset), var(--shadow-modal);
  text-align: center;
  margin: 16px;
}
.autoplay-overlay__card h2 {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 20px;
  color: var(--color-obsidian);
  margin-bottom: 8px;
  letter-spacing: -0.2px;
}
.autoplay-overlay__card p {
  font-family: var(--font-ui);
  font-size: 14px;
  color: var(--color-gravel);
  line-height: 1.5;
}

/* ── Exit-interview modal (6-reason capture, legacy parity) ── */
.exit-modal {
  position: fixed;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 220;
}
.exit-modal.active { display: flex; }
.exit-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(2px);
}
.exit-modal-card {
  position: relative;
  width: 100%;
  max-width: 440px;
  background: #ffffff;
  border-radius: var(--radius-lg);
  padding: 32px;
  box-shadow: var(--shadow-inset), var(--shadow-modal);
  margin: 16px;
}
.exit-modal-card h2 {
  font-family: var(--font-ui);
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.2px;
  color: var(--color-obsidian);
  margin-bottom: 8px;
}
.exit-modal-card p {
  font-size: 14px;
  color: var(--color-gravel);
  margin-bottom: 24px;
}
.exit-modal-card select {
  width: 100%;
  height: 40px;
  padding: 0 12px;
  border: 1px solid var(--color-chalk);
  border-radius: var(--radius-md);
  background: #ffffff;
  color: var(--color-obsidian);
  font-family: var(--font-ui);
  font-size: 14px;
  cursor: pointer;
}
.exit-modal-card select:focus-visible {
  outline: 2px solid var(--color-signal-blue);
  outline-offset: 1px;
}
.exit-modal-error {
  display: none;
  color: var(--color-ember);
  font-size: 13px;
  margin-top: 8px;
}
.exit-modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 28px;
  align-items: center;
}


/* ============================================================
   8. WIZARD (preinterview multi-step flow)
       Used by templates/preinterview.html. The wizard reuses the
       header chrome + atmosphere + .btn-primary / .btn-link
       primitives defined above. These are the wizard-specific
       additions (card, step progress, device check rows, level
       meter, permission banner).
   ============================================================ */

/* Wizard layout — main centers the card vertically. Vertical padding kept
   modest so the tallest step (step 2: webcam preview + device-check rows)
   still clears the bottom of a 14" MacBook viewport at 100% zoom. */
.main-wizard {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 32px;
}

.wizard-card {
  width: 100%;
  max-width: 560px;
  background: var(--color-card);
  border-radius: var(--radius-xl);
  padding: 40px;
  border: 1px solid rgba(0,0,0,0.05);
  box-shadow: var(--shadow-card);
}

/* 3-step progress bar (sits at top of wizard card) */
.step-progress {
  display: flex;
  gap: 6px;
  margin-bottom: 22px;
}
.step-dot {
  flex: 1;
  height: 4px;
  background: var(--color-chalk);
  border-radius: 2px;
  transition: background 0.3s ease;
}
.step-dot.active { background: var(--color-obsidian); }
.step-dot.done   { background: var(--color-gravel); }

/* Wizard typography */
.wizard-card h1 {
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.2px;
  margin-bottom: 8px;
  color: var(--color-obsidian);
}
.wizard-card .subtitle {
  color: var(--color-gravel);
  margin-bottom: 20px;
  font-size: 14px;
}
.wizard-card .body {
  margin-bottom: 20px;          /* breathing room before next element (e.g., check-row) */
}
.wizard-card .body p {
  margin-bottom: 12px;
  color: var(--color-gravel);
  font-size: 14px;
}
.wizard-card .body p:last-child { margin-bottom: 0; }
.wizard-card .body strong {
  color: var(--color-obsidian);
  font-weight: 500;
}

/* Closer line — italic, gravel, smaller. Used after a check-row to read as
   a closing reassurance rather than an instruction. Frames the candidate
   for the action ahead without competing with it. */
.wizard-card .closer {
  margin-top: 16px;
  font-size: 13px;
  font-style: italic;
  color: var(--color-gravel);
  text-align: center;
}

/* Action row at bottom of each step */
.wizard-actions {
  display: flex;
  gap: 12px;
  margin-top: 24px;
  align-items: center;
}
.wizard-actions.between { justify-content: space-between; }

/* Step visibility — driven by preinterview.js */
.step              { display: none; }
.step.active       { display: block; }

/* Webcam preview tile (step 2). max-height keeps the card short enough to
   clear the bottom of a 14" MacBook viewport; the feed is object-fit:cover
   so a slightly-wider-than-16:9 box just crops the self-view, no letterbox. */
.preview {
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 220px;
  background: var(--color-pearl);
  border-radius: var(--radius-md);
  margin: 12px 0;
  overflow: hidden;
  position: relative;
}
.preview video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scaleX(-1);
}

/* Device-check row (camera/mic status with embedded level meter) */
.check-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 16px;
  border: 1px solid var(--color-chalk);
  border-radius: var(--radius-md);
  margin-bottom: 8px;
  font-size: 14px;
  background: var(--color-card);
}
.check-row .status {
  font-size: 12px;
  color: var(--color-gravel);
}
.check-row.ok .status   { color: var(--color-success); font-weight: 500; }
.check-row.fail .status { color: var(--color-ember);   font-weight: 500; }

.meter {
  flex: 1;
  height: 6px;
  background: var(--color-chalk);
  border-radius: 3px;
  margin: 0 12px;
  overflow: hidden;
}
.meter-fill {
  height: 100%;
  width: 0%;
  background: var(--color-success);
  transition: width 0.05s linear;
}

/* Permission banner (shown when getUserMedia is denied) */
.banner {
  background: var(--color-ember-soft);
  color: var(--color-cue-speak);
  padding: 12px 16px;
  border-radius: var(--radius-md);
  font-size: 13px;
  margin-bottom: 16px;
  border: 1px solid rgba(255, 71, 4, 0.15);
}

/* Wizard header — uses the shared .app-header rule in §12 so the
   landing, wizard, and preinterview screens render an identical bar.
   The brand-only wizard variant has no right-side child, so the
   `justify-content: space-between` on the shared rule is a no-op for it. */

/* Wizard generic button — neutral pill (used for "Test audio") */
.btn {
  height: 40px;
  padding: 0 20px;
  border-radius: var(--radius-pill);
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--color-chalk);
  background: var(--color-card);
  color: var(--color-obsidian);
  box-shadow: var(--shadow-button);
  transition: transform 0.15s ease, background 0.15s ease, border-color 0.15s ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.btn:hover     { transform: translateY(-1px); border-color: rgba(0,0,0,0.12); }
.btn:active    { transform: translateY(0); }
.btn:disabled  { opacity: 0.4; cursor: not-allowed; transform: none; }

/* Wizard body class — atmosphere uses page-wizard variant
   (flex column instead of absolute-positioned fixed header) */
body.page-wizard {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}


/* ============================================================
   9. ENDING SCREEN + FEEDBACK AREA
   ============================================================ */

.ending-screen {
  position: fixed;
  inset: 0;
  background: var(--color-eggshell);
  overflow-y: auto;
  z-index: 150;
  /* padding-top clears the floating header (z-index 200) plus a
     ~31px breathing gap so the ending-card never tucks under the
     brand bar. Derived from --header-h to stay in sync. */
  padding: calc(var(--header-h) + 31px) 32px 64px;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
}
.ending-screen.active { display: flex; }
.ending-content { width: 100%; max-width: 720px; }

/* (v6 polish 2026-05-10: top `.ending-brand` block removed — customer
   logo now sits inside the card at larger size as the primary visual.
   Kept as a comment placeholder so the section structure is legible.) */

/* Centered card containing the closure copy + status chip + optional
   feedback area. The card visually mirrors the interview's question card
   (same border-radius, same shadow stack) so the experience reads as
   continuous from interview to closure. */
.ending-card {
  background: var(--color-card);
  border-radius: var(--radius-xl);
  padding: 48px 40px 40px;
  border: 1px solid rgba(0,0,0,0.05);
  box-shadow: var(--shadow-card);
  text-align: center;
  position: relative;
}

/* Customer logo at top of ending card — the primary brand visual on
   the thanks screen. v6 polish (2026-05-10): replaces the orb mark.
   Reasoning: the orb's metaphor was "AI voice presence" — once voice
   is over, the candidate should be reminded whose company they just
   interviewed with, not of the AI. Reinforces customer brand at the
   most reflective moment.

   Sizing: 56px image height (~2× the header brand to feel like the
   primary visual on this card). Wordmark fallback for B2C masterprep
   (no customer_logo configured) at 24px font. */
.ending-customer-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 28px;
  min-height: 56px;
}
/* Same logo-frame treatment as header `.brand img` for consistency.
   Even on the white card the frame's subtle border gives the logo a
   defined edge; on customers whose logo file has a white background,
   the frame absorbs that bg invisibly. Padding scaled up to match
   the larger logo size (8/14 vs 5/10 in header). */
.ending-customer-mark img {
  height: 56px;
  width: auto;
  max-width: 240px;
  object-fit: contain;
  background: #ffffff;
  padding: 8px 14px;
  border-radius: 12px;
  border: 1px solid rgba(0,0,0,0.05);
  box-shadow: 0 1px 2px rgba(0,0,0,0.03);
  box-sizing: content-box;
}
.ending-customer-mark .brand-name {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 24px;
  letter-spacing: -0.3px;
  color: var(--color-obsidian);
}

/* Closure typography — three lines, decisive, calm */
.ending-headline {
  font-family: var(--font-ui);
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.3px;
  color: var(--color-obsidian);
  margin-bottom: 12px;
  line-height: 1.3;
}
.ending-body {
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.55;
  color: var(--color-gravel);
  max-width: 480px;
  margin: 0 auto 28px;
}

/* "Recording saved" status chip — visible only when recording is on,
   gives the candidate a moment of closure that their work is preserved. */
.ending-status-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 14px 6px 12px;
  border-radius: var(--radius-pill);
  background: var(--color-score-high-bg);
  border: 1px solid rgba(22, 101, 52, 0.18);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  color: var(--color-success);
  letter-spacing: 0.2px;
}
.ending-status-chip-icon {
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--color-success);
  color: #ffffff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-weight: 700;
  line-height: 1;
}

/* Legacy `.ending-message` kept as fallback for the error/expired path
   — when showEndingScreen is called in error mode, the structured
   headline/body are hidden and a single message paragraph is shown. */
.ending-message {
  font-family: var(--font-ui);
  font-size: 18px;
  line-height: 1.6;
  color: var(--color-obsidian);
  text-align: center;
  margin-bottom: 24px;
  white-space: pre-wrap;
}
.ending-message.is-hidden { display: none; }

.feedback-area {
  display: none;
  background: #ffffff;
  border-radius: var(--radius-lg);
  padding: 32px;
  margin-top: 16px;
  box-shadow: var(--shadow-inset), var(--shadow-card-soft);
  text-align: left;
  font-family: var(--font-ui);
  color: var(--color-obsidian);
  line-height: 1.7;
}
.feedback-area.active { display: block; }
.feedback-area h2 {
  font-weight: 500;
  font-size: 20px;
  letter-spacing: -0.2px;
  margin-bottom: 20px;
  color: var(--color-obsidian);
}
.feedback-meta {
  display: flex;
  gap: 24px;
  align-items: center;
  flex-wrap: wrap;
  margin-bottom: 24px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--color-chalk);
}
.feedback-meta-label {
  color: var(--color-gravel);
  font-size: 12px;
  display: block;
  margin-bottom: 4px;
  letter-spacing: 0.4px;
  text-transform: uppercase;
}
.feedback-meta-value {
  color: var(--color-obsidian);
  font-size: 15px;
  font-weight: 500;
}
.score-badge {
  display: inline-flex;
  align-items: center;
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  font-weight: 600;
  font-size: 16px;
}
.score-badge--high { background: var(--color-score-high-bg); color: var(--color-success); }
.score-badge--mid  { background: var(--color-score-mid-bg);  color: var(--color-warn); }
.score-badge--low  { background: var(--color-score-low-bg);  color: var(--color-ember); }

.feedback-body {
  color: var(--color-obsidian);
  line-height: 1.8;
  font-size: 14px;
}
.feedback-section-header {
  font-weight: 700;
  font-size: 15px;
  color: var(--color-obsidian);
  display: block;
  margin-top: 20px;
  margin-bottom: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--color-chalk);
  letter-spacing: 0;
}
.feedback-rating-line          { font-weight: 700; font-size: 15px; }
.feedback-rating-line--high    { color: var(--color-success); }
.feedback-rating-line--mid     { color: var(--color-warn); }
.feedback-rating-line--low     { color: var(--color-ember); }
.feedback-error {
  color: var(--color-ember);
  font-size: 14px;
}


/* ============================================================
   10. SELECT INTERVIEW (interview-type chooser)
       Used by templates/select_interview_orb.html — the recording-on
       variant of the "Choose your interview" screen. Reuses the
       wizard chrome (header-wizard, atmosphere, wizard-card,
       btn-primary) plus the chooser rows + "before you begin"
       footer below.
   ============================================================ */

/* Main holds the card AND the footer notes; column-stack, centered,
   sized so the common 2-3-type case fits one viewport (no scroll). */
.select-main {
  flex-direction: column;
  gap: 24px;
  padding: 36px 32px;
}

/* Slightly wider than the device-check wizard card to give the
   interview-type rows + descriptions room to breathe. */
.select-card { max-width: 600px; }

/* Greeting eyebrow above the heading */
.eyebrow {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--color-gravel);
  margin-bottom: 10px;
}

/* Chooser rows */
.type-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 4px;
}
.type-option { position: relative; cursor: pointer; }
.type-option input[type="radio"] {
  position: absolute;
  opacity: 0;
  width: 0; height: 0;
}
.type-label {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 13px 16px;
  border: 1px solid var(--color-chalk);
  border-radius: var(--radius-md);
  background: var(--color-card);
  transition: border-color 0.15s ease, background 0.15s ease;
}
.type-option:hover .type-label { border-color: rgba(0,0,0,0.18); }
.type-option input[type="radio"]:checked + .type-label {
  border-color: var(--color-obsidian);
  background: var(--color-pearl);
}

/* Custom radio dot — mirrors the device-check visual language */
.type-radio {
  width: 18px; height: 18px;
  flex-shrink: 0;
  margin-top: 1px;
  border: 1.5px solid var(--color-fog);
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: border-color 0.15s ease;
}
.type-radio::after {
  content: '';
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--color-obsidian);
  transform: scale(0);
  transition: transform 0.15s ease;
}
.type-option input[type="radio"]:checked + .type-label .type-radio { border-color: var(--color-obsidian); }
.type-option input[type="radio"]:checked + .type-label .type-radio::after { transform: scale(1); }

.type-text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.type-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--color-obsidian);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.type-desc {
  font-size: 13px;
  line-height: 1.4;
  color: var(--color-gravel);
}

/* "Upcoming" (disabled) type */
.type-option.upcoming { cursor: default; }
.type-option.upcoming .type-label { opacity: 0.5; }
.type-option.upcoming:hover .type-label { border-color: var(--color-chalk); }
.type-badge {
  font-family: var(--font-ui);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--color-gravel);
  background: var(--color-powder);
  border-radius: var(--radius-pill);
  padding: 2px 7px;
}

/* "Paid Plan" (locked) type — clickable; opens upgrade modal */
.type-option.locked { cursor: pointer; display: block; }
.type-option.locked .type-label { opacity: 0.6; border-style: dashed; }
.type-option.locked:hover .type-label { opacity: 0.85; border-color: var(--color-signal-blue); }
.type-option.locked:focus-visible .type-label {
  outline: 2px solid var(--color-signal-blue);
  outline-offset: 2px;
}
.type-option.locked .type-radio::before {
  content: '';
  display: block;
  width: 10px;
  height: 10px;
  margin: 4px auto;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><rect x='3' y='11' width='18' height='11' rx='2' ry='2'/><path d='M7 11V7a5 5 0 0 1 10 0v4'/></svg>");
  background-repeat: no-repeat;
  background-size: contain;
}
.type-badge.paid {
  color: #fff;
  background: var(--color-signal-blue, #2563EB);
}

/* Upgrade modal — pricing tiers for free B2C users who click a locked type */
.upgrade-modal {
  position: fixed;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 230;
}
.upgrade-modal.active { display: flex; }
.upgrade-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(2px);
}
.upgrade-modal-card {
  position: relative;
  width: 100%;
  max-width: 520px;
  background: #ffffff;
  border-radius: var(--radius-lg);
  padding: 32px;
  box-shadow: var(--shadow-inset), var(--shadow-modal);
  margin: 16px;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
}
.upgrade-modal-close {
  position: absolute;
  top: 12px;
  right: 16px;
  background: none;
  border: none;
  font-size: 28px;
  line-height: 1;
  color: var(--color-gravel);
  cursor: pointer;
  padding: 4px 8px;
}
.upgrade-modal-close:hover { color: var(--color-obsidian); }
.upgrade-badge {
  display: inline-block;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--color-signal-blue, #2563EB);
  background: #EFF6FF;
  border-radius: var(--radius-pill);
  padding: 4px 10px;
  margin-bottom: 12px;
}
.upgrade-modal-card h2 {
  font-family: var(--font-ui);
  font-weight: 600;
  font-size: 22px;
  letter-spacing: -0.2px;
  color: var(--color-obsidian);
  margin: 0 0 8px;
}
.upgrade-modal-subtitle {
  font-size: 14px;
  color: var(--color-gravel);
  margin: 0 0 20px;
}
.upgrade-pricing-cards {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 20px;
}
.upgrade-pricing-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  border: 1px solid var(--color-chalk);
  border-radius: var(--radius-md);
  background: #fff;
}
.upgrade-pricing-card .plan-tag {
  display: inline-block;
  margin-left: 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: #fff;
  background: var(--color-signal-blue, #2563EB);
  border-radius: var(--radius-pill);
  padding: 2px 6px;
  vertical-align: middle;
}
.upgrade-pricing-card .card-info { display: flex; flex-direction: column; gap: 2px; }
.upgrade-pricing-card .card-name {
  font-weight: 600;
  font-size: 14px;
  color: var(--color-obsidian);
  display: flex;
  align-items: center;
  gap: 8px;
}
.upgrade-pricing-card .card-sessions {
  font-size: 12px;
  color: var(--color-gravel);
}
.upgrade-pricing-card .btn-buy {
  background: var(--color-obsidian);
  color: #fff;
  border: none;
  border-radius: var(--radius-md);
  padding: 8px 16px;
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  min-width: 88px;
}
.upgrade-pricing-card .btn-buy:hover { background: #000; }
.upgrade-pricing-card .btn-buy:disabled { opacity: 0.6; cursor: not-allowed; }
.upgrade-pricing-card .card-price {
  font-weight: 600;
  font-size: 14px;
  color: var(--color-gravel);
}
/* Discount pricing (optional, per-plan) — struck list price sits OUTSIDE the buy
   button so orb_app.js's `originalText = btn.textContent` restore-on-failure stays
   clean (never "₹1,499₹999"). */
.upgrade-pricing-card .card-buy {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}
.upgrade-pricing-card .price-original {
  font-size: 12px;
  color: var(--color-gravel);
  text-decoration: line-through;
}
.upgrade-pricing-card .discount-badge {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: #fff;
  background: #059669;
  border-radius: var(--radius-pill);
  padding: 2px 6px;
}
.upgrade-modal-terms {
  font-size: 12px;
  color: var(--color-gravel);
  margin: 0;
  text-align: center;
}
.upgrade-modal-terms a {
  color: var(--color-signal-blue, #2563EB);
  text-decoration: none;
}
.upgrade-modal-terms a:hover { text-decoration: underline; }

/* CTA row */
.select-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 24px;
}

/* "Before you begin" footer strip — quiet, sits below the card */
.before-you-begin {
  width: 100%;
  max-width: 600px;
}
.before-you-begin h2 {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--color-gravel);
  margin-bottom: 10px;
}
.before-you-begin ul {
  margin: 0;
  padding-left: 18px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--color-gravel);
}
.before-you-begin li { margin-bottom: 4px; }
.before-you-begin li:last-child { margin-bottom: 0; }
.before-you-begin strong { color: var(--color-obsidian); font-weight: 500; }


/* ============================================================
   11. FORM PRIMITIVES
       Field group (label + input + helper) and the secondary button
       used for OAuth-style alt-CTAs. Added when the design system
       was extended to cover auth/landing surfaces — the interview
       flow has almost no text inputs, so these never existed before.

       Borders use --color-border-input (rgba 0.14), NOT --color-chalk,
       so they remain visible on white surfaces without depending on
       the atmosphere halo for contrast.
   ============================================================ */

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  text-align: left;
}
.field-label {
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 500;
  color: var(--color-obsidian);
  letter-spacing: -0.05px;
}
.field-input {
  height: 44px;
  padding: 0 14px;
  border: 1px solid var(--color-border-input);
  border-radius: var(--radius-md);
  background: #ffffff;
  font-family: var(--font-ui);
  font-size: 14px;
  color: var(--color-obsidian);
  width: 100%;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
}
.field-input::placeholder { color: var(--color-slate); }
.field-input:hover { border-color: var(--color-border-input-hover); }
.field-input:focus-visible {
  outline: none;
  border-color: var(--color-signal-blue);
  box-shadow: 0 0 0 3px rgba(4, 71, 255, 0.12);
}
.field-input:disabled {
  background: var(--color-pearl);
  color: var(--color-gravel);
  cursor: not-allowed;
}
.field-helper {
  font-size: 12px;
  color: var(--color-gravel);
  line-height: 1.4;
}
.field--error .field-input { border-color: var(--color-ember); }
.field--error .field-helper { color: var(--color-ember); }

/* Secondary CTA — white pill paired with .btn-primary. Used for OAuth
   ("Continue with Google") and any alt action that sits next to the
   primary obsidian button. Border weight matches .field-input so the
   form reads as one connected group. */
.btn-secondary {
  height: 44px;
  padding: 0 18px;
  border-radius: var(--radius-md);
  background: #ffffff;
  border: 1px solid var(--color-border-input);
  font-family: var(--font-ui);
  font-size: 14px;
  font-weight: 500;
  color: var(--color-obsidian);
  cursor: pointer;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.btn-secondary:hover {
  background: #fafbfc;
  border-color: var(--color-border-input-hover);
  transform: translateY(-1px);
}
.btn-secondary:active { transform: translateY(0); }
.btn-secondary:focus-visible {
  outline: 2px solid var(--color-signal-blue);
  outline-offset: 2px;
}
.btn-secondary svg { width: 18px; height: 18px; flex-shrink: 0; }

/* Block-fill modifier for .btn-primary inside auth/form cards */
.btn-primary.btn-block {
  width: 100%;
  height: 44px;
}

/* "OR" rule with text label — used between OAuth and email/password */
.divider-text {
  display: flex;
  align-items: center;
  gap: 12px;
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
  color: var(--color-slate);
  text-transform: uppercase;
  letter-spacing: 0.8px;
  margin: 4px 0;
}
.divider-text::before,
.divider-text::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--color-chalk);
}


/* ============================================================
   12. AUTH / LANDING PATTERNS
       Used by templates/landing_tenant.html (B2B + B2C) and any
       sign-in/sign-up surface. The .auth-* primitives compose a
       centered card with optional intro copy above and trust
       elements below. Mirrors .wizard-card's visual recipe so
       candidates moving landing → wizard → interview see a single
       coherent design language.
   ============================================================ */

body.page-auth {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Slim header for auth/landing AND wizard pages — lighter than the
   orb-page fixed header. Brand left, optional action link right.
   Shared by .app-header (landing) and .header-wizard (preinterview,
   select-interview) so the candidate sees the same bar across the
   whole flow. justify-content: space-between is a no-op when the
   header has only a single child (brand). */
.app-header,
.header-wizard {
  position: relative;
  z-index: 50;
  padding: var(--header-padding-y) var(--header-padding-x);
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--color-border-card);
  background: var(--color-pearl);
}
.app-header-link {
  font-family: var(--font-ui);
  font-size: 13px;
  color: var(--color-gravel);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  transition: color 0.15s ease;
}
.app-header-link:hover { color: var(--color-obsidian); }
.app-header-link svg { width: 12px; height: 12px; opacity: 0.7; }
.app-header-link strong { color: var(--color-obsidian); font-weight: 500; }

/* Stage — main column that stacks intro → card → help → attribution.
   .auth-shell--centered vertically centers (B2B utility sign-in);
   default top-aligns with padding (B2C with hero + social proof below). */
.auth-shell {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 40px 32px;
}
.auth-shell--centered { justify-content: center; }

.auth-card {
  width: 100%;
  max-width: 420px;
  background: var(--color-card);
  border-radius: var(--radius-xl);
  padding: 32px;
  border: 1px solid var(--color-border-card);
  box-shadow: var(--shadow-card);
}
.auth-card-title {
  font-family: var(--font-ui);
  font-size: 18px;
  font-weight: 600;
  color: var(--color-obsidian);
  margin-bottom: 20px;
  text-align: center;
}
.auth-card-sub {
  font-size: 13px;
  color: var(--color-gravel);
  text-align: center;
  margin-bottom: 20px;
}

/* Form column inside .auth-card — gap matches .field internal rhythm */
.auth-stack {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Large customer mark above auth card (B2B brand presence). Mirrors
   the .ending-customer-mark treatment so the candidate sees the same
   logo chip at both ends of their journey. */
.auth-customer-mark {
  margin: 0 auto 24px;
  min-height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.auth-customer-mark img {
  height: 56px;
  width: auto;
  max-width: 240px;
  object-fit: contain;
  background: #ffffff;
  padding: 8px 14px;
  border-radius: 12px;
  border: 1px solid rgba(0, 0, 0, 0.05);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  box-sizing: content-box;
}

/* Intro copy block above auth card (eyebrow + headline + sub) */
.auth-intro {
  text-align: center;
  margin-bottom: 24px;
  max-width: 380px;
}
.auth-eyebrow {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--color-gravel);
  margin-bottom: 10px;
}
.auth-headline {
  font-family: var(--font-ui);
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.4px;
  color: var(--color-obsidian);
  margin-bottom: 6px;
  line-height: 1.25;
}
.auth-sub {
  font-size: 14px;
  color: var(--color-gravel);
  line-height: 1.5;
}

/* Trouble-signing-in line below auth card */
.auth-help {
  margin-top: 18px;
  font-size: 13px;
  color: var(--color-gravel);
  text-align: center;
}
.auth-help a {
  color: var(--color-obsidian);
  text-decoration: underline;
  text-decoration-color: var(--color-chalk);
  text-underline-offset: 3px;
  transition: text-decoration-color 0.15s ease;
}
.auth-help a:hover { text-decoration-color: var(--color-obsidian); }

/* Tight helper line inside the auth card (e.g. "Trouble signing in?"
   directly under the Sign In button). Visually quieter than
   .auth-help because it lives on white, not on the page surface. */
.in-card-help {
  margin-top: 4px;
  text-align: center;
  font-size: 12px;
  color: var(--color-slate);
  line-height: 1.5;
}
.in-card-help a {
  color: var(--color-gravel);
  text-decoration: underline;
  text-decoration-color: var(--color-chalk);
  text-underline-offset: 3px;
  transition: color 0.15s ease;
}
.in-card-help a:hover { color: var(--color-obsidian); }

/* Stacked secondary "practice" card below the B2B auth card.
   Pure white + hairline border + no shadow — pops against the
   atmosphere-tinted body, while the missing shadow keeps it
   visually subordinate to the shadowed auth card above. */
.practice-card {
  width: 100%;
  max-width: 420px;
  margin-top: 10px;
  padding: 14px 24px;
  background: var(--color-card);
  border: 1px solid var(--color-border-card);
  border-radius: var(--radius-xl);
  box-shadow: none;
  text-align: center;
}
.practice-card__title {
  font-size: 13px;
  line-height: 1.5;
  color: var(--color-gravel);
  font-weight: 500;
  margin: 0;
}
.practice-card__cta {
  color: var(--color-obsidian);
  font-weight: 500;
  text-decoration: underline;
  text-decoration-color: var(--color-chalk);
  text-underline-offset: 3px;
  margin-left: 4px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
  transition: text-decoration-color 0.15s ease;
}
.practice-card__cta:hover { text-decoration-color: var(--color-obsidian); }
.practice-card__cta .arrow {
  display: inline-block;
  transition: transform 0.15s ease;
}
.practice-card__cta:hover .arrow { transform: translateX(2px); }
.practice-card__note {
  margin: 4px 0 0;
  font-size: 11px;
  line-height: 1.5;
  color: var(--color-slate);
  letter-spacing: 0.1px;
}

@media (max-width: 520px) {
  .practice-card { padding: 14px 18px; }
}

/* "Powered by ___" attribution — quiet, all-caps */
.attribution-line {
  margin-top: 28px;
  text-align: center;
  font-family: var(--font-ui);
  font-size: 11px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--color-slate);
}
.attribution-line strong { font-weight: 500; color: var(--color-gravel); }


/* ============================================================
   13. MARKETING / HERO PATTERNS
       Used by the B2C variant of landing_tenant.html (self-serve)
       and any future marketing surface. Larger typographic scale
       than auth-intro; adds eyebrow pill, reassurance pill, social
       proof strip, legal consent, quiet secondary link.
   ============================================================ */

.hero-copy {
  text-align: center;
  max-width: 580px;
  margin: 0 auto 40px;
}
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  background: var(--color-tint-blue);
  border: 1px solid var(--color-tint-blue-border);
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.4px;
  color: var(--color-cue-listen);
  margin-bottom: 20px;
}
.hero-eyebrow .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-cue-listen);
}
.hero-headline {
  font-family: var(--font-ui);
  font-size: 36px;
  font-weight: 500;
  letter-spacing: -0.8px;
  color: var(--color-obsidian);
  line-height: 1.15;
  margin-bottom: 14px;
}
.hero-sub {
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.55;
  color: var(--color-gravel);
  max-width: 480px;
  margin: 0 auto;
}

/* Reassurance pill (e.g., "1 free interview, no card required").
   Green-tinted — reads as good news, not alert. Sits inside the auth
   card below the primary CTA. */
.reassure-pill-wrap { display: flex; justify-content: center; margin-top: 18px; }
.reassure-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-pill);
  background: var(--color-score-high-bg);
  border: 1px solid rgba(22, 101, 52, 0.18);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 500;
  color: var(--color-success);
  letter-spacing: 0.2px;
}
.reassure-pill svg { width: 12px; height: 12px; }

/* Legal consent line — Terms / Privacy below the auth card */
.legal-consent {
  margin-top: 14px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--color-slate);
  text-align: center;
}
.legal-consent a {
  color: var(--color-gravel);
  text-decoration: underline;
  text-decoration-color: var(--color-chalk);
  text-underline-offset: 3px;
  transition: color 0.15s ease;
}
.legal-consent a:hover { color: var(--color-obsidian); }

/* Social proof strip — grayscale logo wall. Quiet authority. */
.proof-strip {
  margin-top: 56px;
  padding-top: 32px;
  border-top: 1px solid var(--color-chalk);
  text-align: center;
  max-width: 720px;
  width: 100%;
}
.proof-label {
  font-family: var(--font-ui);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--color-slate);
  margin-bottom: 18px;
}
.proof-logos {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 36px;
}
.proof-logos img {
  height: 22px;
  width: auto;
  opacity: 0.55;
  filter: grayscale(1);
  transition: opacity 0.15s ease, filter 0.15s ease;
}
.proof-logos img:hover { opacity: 0.85; filter: grayscale(0.4); }

/* Quiet secondary link (e.g., "Recruiter? Access portal") */
.muted-link {
  margin-top: 36px;
  text-align: center;
  font-size: 12px;
  color: var(--color-slate);
}
.muted-link a {
  color: var(--color-gravel);
  text-decoration: none;
  border-bottom: 1px dotted var(--color-chalk);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.muted-link a:hover {
  color: var(--color-obsidian);
  border-bottom-color: var(--color-gravel);
}

/* Grant-badge attribution footer */
.site-footer {
  padding: 28px 16px 32px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  opacity: 0.55;
  transition: opacity 0.2s ease;
}
.site-footer:hover, .site-footer:focus-within { opacity: 1; }
.site-footer .footer-label {
  font-family: var(--font-ui);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-gravel);
}
.site-footer img { width: 140px; height: auto; display: block; }

@media (max-width: 520px) {
  /* Narrow viewports: same vertical padding as desktop (so the
     header doesn't grow on small screens) but tighter side padding
     to reclaim horizontal room. */
  .app-header,
  .header-wizard,
  .header-orb { padding: var(--header-padding-y) 20px; }
  .auth-shell { padding: 36px 20px 48px; }
  .auth-shell--centered { padding-top: 36px; padding-bottom: 48px; }
  .auth-card { padding: 24px; }
  .auth-headline { font-size: 20px; }
  .hero-headline { font-size: 28px; letter-spacing: -0.6px; }
  .hero-sub { font-size: 15px; }
  .proof-strip { margin-top: 40px; padding-top: 24px; }
  .proof-logos { gap: 24px; }
  .proof-logos img { height: 18px; }
}


/* ============================================================
   14. ACCESSIBILITY
   ============================================================ */

/* Visible focus ring — every interactive element should be reachable
   via keyboard with a clear indicator. */
.btn-primary:focus-visible,
.btn-secondary:focus-visible,
.btn:focus-visible,
.end-btn:focus-visible,
.btn-link:focus-visible,
.toast-close:focus-visible,
.orb:focus-visible {
  outline: 2px solid var(--color-signal-blue);
  outline-offset: 3px;
}
.type-option input[type="radio"]:focus-visible + .type-label {
  outline: 2px solid var(--color-signal-blue);
  outline-offset: 2px;
}

/* Visually-hidden helper (SR-only labels) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Honour OS-level reduced-motion preference. Animations don't
   carry critical information here (orb state has a text caption
   too) so we can disable motion for users who request it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
