/* AloftCast — "Porcelain" design system, PINNED DARK-ONLY (Andy 2026-07-04: SightLine's users are
   blind, so a single always-dark theme is clearer than a light/dark toggle). The dark warm-charcoal
   palette below is now the RESOLVED theme on :root itself — it applies on first visit with no stored
   preference and regardless of prefers-color-scheme. The theme toggle is removed (see .theme-toggle,
   made inert below; the server still injects the button + THEME_HEAD, both neutered here in CSS).
   The variable ARCHITECTURE is unchanged (one var set drives every screen); only the resolved values
   are pinned to dark. Dark is hand-tuned (not an auto-invert): cards read as lifted via subtle borders
   where shadows can't show. Video-call controls stay glassy over live video. */
:root {
  color-scheme: dark;
  --bg: #17140F; --bg-top: #1C1813; --bg-2: #131009;
  --surface: #262019; --surface-sunken: #1E1A15;
  --text: #F4EEE4; --text-dim: #ABA093; --text-faint: #96897B;
  --primary: #E0684F; --primary-strong: #C5533F; --primary-soft: #2F2019; --accent: #E8B36B; --btn-bg: #C25036;
  --line: rgba(255,255,255,0.09); --line-strong: rgba(255,255,255,0.16); --card-border: rgba(255,255,255,0.08);
  --radius-sm: 12px; --radius: 18px; --radius-lg: 24px; --radius-pill: 999px;
  --shadow-card: inset 0 1px 0 rgba(255,255,255,0.05), 0 10px 28px -14px rgba(0,0,0,0.7);
  --shadow-card-hover: inset 0 1px 0 rgba(255,255,255,0.07), 0 16px 40px -16px rgba(0,0,0,0.75);
  --shadow-primary: inset 0 1px 0 rgba(255,255,255,0.10), 0 12px 34px -10px rgba(224,104,79,0.5);
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --brand: var(--primary); /* legacy alias for any leftover var(--brand) use */
}

/* Dark is pinned on :root above, so no prefers-color-scheme branch and no [data-theme="light"] path
   can flip the palette back to light. A stale localStorage `aloft_theme=light` (which THEME_HEAD would
   set as data-theme="light") is deliberately IGNORED — we re-assert the dark palette for that attr too
   so the resolved theme is dark in every state. [data-theme="dark"] is a harmless no-op (same values). */
:root[data-theme="light"] {
  --bg: #17140F; --bg-top: #1C1813; --bg-2: #131009;
  --surface: #262019; --surface-sunken: #1E1A15;
  --text: #F4EEE4; --text-dim: #ABA093; --text-faint: #96897B;
  --primary: #E0684F; --primary-strong: #C5533F; --primary-soft: #2F2019; --accent: #E8B36B; --btn-bg: #C25036;
  --line: rgba(255,255,255,0.09); --line-strong: rgba(255,255,255,0.16); --card-border: rgba(255,255,255,0.08);
  --shadow-card: inset 0 1px 0 rgba(255,255,255,0.05), 0 10px 28px -14px rgba(0,0,0,0.7);
  --shadow-card-hover: inset 0 1px 0 rgba(255,255,255,0.07), 0 16px 40px -16px rgba(0,0,0,0.75);
  --shadow-primary: inset 0 1px 0 rgba(255,255,255,0.10), 0 12px 34px -10px rgba(224,104,79,0.5);
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100vh; height: 100dvh; /* vh = old-Chrome(<108) fallback */ color: var(--text);
  background: linear-gradient(180deg, var(--bg-top) 0%, var(--bg) 55%, var(--bg-2) 100%);
  font-family: var(--font);
  -webkit-tap-highlight-color: transparent; overscroll-behavior: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; }
/* Scoped to the SPA shell ONLY (#app). A bare `main` selector here once clipped every
   STATIC page (<main class="sl"> on sightline/captionline/elder) into an unscrollable
   100dvh box — Andy's can't-scroll bug, 2026-07-03. Static pages manage their own layout. */
main#app { position: relative; width: 100%; height: 100vh; height: 100dvh; overflow: hidden; } /* vh fallback for <108 */

/* ---- screens (one shown at a time, switched via [data-screen] on <main>) */
.screen { position: absolute; inset: 0; display: none; }
main[data-screen="loading"] .screen-loading,
main[data-screen="home"]    .screen-home,
main[data-screen="call"]    .screen-call,
main[data-screen="ai"]      .screen-ai,
main[data-screen="error"]   .screen-error { display: flex; }

/* ---- loading + error */
.screen-loading, .screen-error { align-items: center; justify-content: center; flex-direction: column;
  padding: 24px; text-align: center; gap: 16px; }
.screen-error h2 { margin: 0; font-size: 22px; font-weight: 600; }
.screen-error p  { margin: 0; color: var(--text-dim); max-width: 280px; line-height: 1.5; }
.spinner { width: 40px; height: 40px; border-radius: 50%; border: 3px solid var(--line-strong); border-top-color: var(--primary);
  animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* ---- home */
/* overflow-y:auto — the screen is an absolute inset:0 box; without it, content taller than the
   viewport (the SightLine/CaptionLine row + footer on phones) is CLIPPED with no way to scroll.
   Same bug class as the 7/1 hidden Start button on .screen-ai. */
.screen-home { flex-direction: column; align-items: center; justify-content: space-between; overflow-y: auto;
  padding: max(48px, env(safe-area-inset-top)) 24px calc(env(safe-area-inset-bottom, 0px) + 40px); }
.home-head { text-align: center; margin-top: 28px; }
.home-head h1 { margin: 0 0 8px; font-size: 30px; font-weight: 800; letter-spacing: -0.7px; color: var(--text); }
.home-head p  { margin: 0; color: var(--text-dim); font-size: 15px; font-weight: 500; max-width: 300px; }
.home-cta { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.btn-call { width: 96px; height: 96px; border-radius: 50%; background: var(--btn-bg); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 36px;
  box-shadow: var(--shadow-primary); transition: transform 0.12s var(--ease); }
.btn-call:active { transform: scale(0.94); }
.btn-call-label { font-size: 14px; color: var(--text-dim); }

/* ---- home cards (AI-first landing) — floating white cards on porcelain */
.home-cards { display: flex; flex-direction: column; gap: 14px; width: 100%; max-width: 420px; margin: 24px auto; }
.home-card { display: flex; align-items: center; gap: 16px; width: 100%; min-height: 72px;
  padding: 18px 20px; border-radius: var(--radius); text-align: left; text-decoration: none; color: var(--text);
  background: var(--surface); border: 1px solid var(--card-border); box-shadow: var(--shadow-card);
  transition: transform 0.12s var(--ease), box-shadow 0.15s var(--ease); }
.home-card:active { transform: scale(0.98); box-shadow: var(--shadow-card-hover); }
.home-card .card-emoji { font-size: 28px; line-height: 1; flex: none; }
.home-card .card-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.home-card .card-title { font-size: 17px; font-weight: 700; letter-spacing: -0.2px; color: var(--text); }
.home-card .card-sub { font-size: 13px; color: var(--text-dim); line-height: 1.35; }
.home-card.primary { min-height: 96px; padding: 24px 20px;
  background: linear-gradient(180deg, var(--surface), var(--primary-soft)); border-color: var(--primary-soft);
  box-shadow: var(--shadow-primary); }
.home-card.primary:active { box-shadow: var(--shadow-card-hover); }
.home-card.primary .card-emoji { font-size: 34px; }
.home-card.primary .card-title { font-size: 19px; color: var(--primary-strong); }
.home-foot { margin: 0; text-align: center; font-size: 12px; color: var(--text-faint); line-height: 1.6; }
.home-foot-hint { font-size: 11px; color: var(--text-dim); }
.home-access { margin: 4px 0 14px; text-align: center; font-size: 13px; display: flex; gap: 10px; justify-content: center; align-items: center; }
.home-access a { color: var(--text-dim); text-decoration: none; font-weight: 600;
  /* SC 2.5.8 target size: this link is the sole working Video Call control, so it owes >=24px.
     inline-flex + min-height + padding lifts the ~18px line box to a 40px tap target. */
  display: inline-flex; align-items: center; min-height: 40px; padding: 8px 12px; border-radius: 10px; }
.home-access a:active { color: var(--primary); }
.home-access span { color: var(--text-faint); }

/* ---- call (full-bleed live video; controls stay glassy over it) */
.video-remote { position: absolute; inset: 0; background: #111; }
.video-remote > div, .video-remote video { width: 100% !important; height: 100% !important; object-fit: cover !important; }
.video-local { position: absolute; top: max(16px, env(safe-area-inset-top)); right: 16px;
  width: 96px; height: 144px; border-radius: 12px; overflow: hidden; background: #222;
  border: 1px solid rgba(255,255,255,0.1); z-index: 5; }
.video-local > div, .video-local video { width: 100% !important; height: 100% !important; object-fit: cover !important; }
.call-status { position: absolute; top: max(16px, env(safe-area-inset-top)); left: 16px; z-index: 5;
  display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: 999px;
  background: rgba(0,0,0,0.5); backdrop-filter: blur(8px); color: #fff; font-size: 13px; }
.net-dot { width: 8px; height: 8px; border-radius: 50%; background: #888; transition: background 0.3s; }
.net-dot.good { background: #4ade80; } .net-dot.ok { background: #facc15; } .net-dot.bad { background: #ef4444; }
.audio-only-banner { position: absolute; top: 80px; left: 50%; transform: translateX(-50%); z-index: 5;
  padding: 6px 14px; border-radius: 999px; background: rgba(250,204,21,0.15); color: #facc15;
  font-size: 12px; border: 1px solid rgba(250,204,21,0.3); display: none; }
.audio-only-banner.show { display: block; }

.controls { position: absolute; bottom: max(32px, env(safe-area-inset-bottom)); left: 0; right: 0; z-index: 5;
  display: flex; justify-content: center; gap: 14px; padding: 0 16px; }
/* Round control chips: light surface by default (used on the AI screen over porcelain). */
.ctrl { width: 56px; height: 56px; border-radius: 50%; background: var(--surface); color: var(--text);
  border: 1px solid var(--card-border); box-shadow: var(--shadow-card); display: flex; align-items: center; justify-content: center;
  font-size: 22px; transition: transform 0.1s, background 0.15s; }
.ctrl:active { transform: scale(0.92); }
.ctrl.hangup { background: var(--btn-bg); color: #fff; box-shadow: var(--shadow-primary); }
.ctrl.recording { background: var(--btn-bg); color: #fff; box-shadow: var(--shadow-primary); animation: recpulse 1.4s ease-in-out infinite; }
@keyframes recpulse { 0%,100% { opacity: 1; } 50% { opacity: 0.62; } }
/* Over live video, controls are glassy-dark instead of white chips. */
.screen-call .ctrl { background: rgba(255,255,255,0.16); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  color: #fff; box-shadow: none; }
.screen-call .ctrl.hangup { background: var(--btn-bg); }
.screen-call .ctrl.off { background: rgba(255,255,255,0.32); }
.ctrl.lowdata { font-size: 11px; flex-direction: column; gap: 2px; }
.ctrl.lowdata.on { background: rgba(250,204,21,0.3); color: #facc15; }

/* ---- AI mode. Camera is HEIGHT-capped (not flex:1 eating the screen) so the Start / Hold button
   always has room above the phone's nav bar; the screen also scrolls as a last-resort safety net. */
.screen-ai { flex-direction: column; align-items: center; justify-content: space-between; overflow-y: auto;
  padding: max(28px, env(safe-area-inset-top)) 20px calc(env(safe-area-inset-bottom, 0px) + 56px); }
.ai-head { text-align: center; margin-top: 12px; }
.ai-head h1 { margin: 0 0 8px; font-size: 24px; font-weight: 800; letter-spacing: -0.4px; color: var(--text); }
.ai-head p { margin: 0 auto; color: var(--text-dim); font-size: 14px; max-width: 300px; line-height: 1.45; }
.ai-stage { position: relative; flex: 0 0 auto; display: flex; align-items: center; justify-content: center; width: 100%; margin: 12px 0; }
.ai-cam { height: min(46vh, 340px); aspect-ratio: 3 / 4; width: auto; background: var(--surface-sunken); border-radius: var(--radius);
  overflow: hidden; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--card-border); box-shadow: var(--shadow-card); }
.ai-cam video { width: 100%; height: 100%; object-fit: cover; }
.ai-camhint { font-size: 12px; color: var(--text-faint); }
.ai-orb { position: absolute; bottom: 14px; width: 14px; height: 14px; border-radius: 50%;
  background: var(--primary); opacity: 0.35; transition: opacity 0.2s; }
.ai-orb.on { opacity: 1; animation: orbpulse 1s ease-in-out infinite; }
@keyframes orbpulse { 0%,100% { transform: scale(1); } 50% { transform: scale(1.55); } }
.ai-status { font-size: 15px; color: var(--text-dim); font-weight: 500; min-height: 22px; text-align: center; margin-bottom: 10px; }
.ai-hint { font-size: 12px; color: var(--text-faint); text-align: center; margin: -4px 0 8px; }
.model-badge { font-size: 11px; color: var(--primary); font-weight: 700; text-align: center; margin: -4px 0 8px; min-height: 14px; }
.ptt-btn { width: auto; min-width: 210px; padding: 20px 40px; border-radius: var(--radius-pill); margin: 4px 0 10px;
  background: var(--btn-bg); font-size: 18px; font-weight: 700; color: #fff;
  touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;
  box-shadow: var(--shadow-primary); transition: transform .12s var(--ease), filter .12s, box-shadow .12s; }
.ptt-btn.holding { transform: scale(1.06); filter: brightness(1.06);
  box-shadow: 0 0 0 8px rgba(197,83,63,0.16), var(--shadow-primary); }
.big-pill { width: auto; height: auto; padding: 16px 44px; border-radius: var(--radius-pill);
  background: var(--btn-bg); color: #fff; font-size: 18px; font-weight: 700; box-shadow: var(--shadow-primary); }
/* AI/composed screens lay out in a flex column — keep the controls IN FLOW so the status
   line sits above the button, not behind the absolute-positioned call-screen controls. */
.screen-ai .controls { position: static; padding-bottom: 4px; }

/* ---- three-brain composed picker (Free / Local / Premium / Claude) */
.byom-toggle { display: flex; gap: 8px; justify-content: center; margin-top: 6px; flex-wrap: wrap; }
.byom-btn { padding: 8px 16px; border-radius: var(--radius-pill); background: var(--surface); color: var(--text-dim);
  border: 1px solid var(--card-border); box-shadow: var(--shadow-card); font-size: 13px; font-weight: 600; transition: transform 0.1s var(--ease); }
.byom-btn:active { transform: scale(0.96); }
.byom-btn.on { background: var(--btn-bg); color: #fff; box-shadow: var(--shadow-primary); }
.ai-caption { font-size: 13px; color: var(--text-dim); text-align: center; max-width: 340px;
  margin: 0 auto 6px; min-height: 18px; max-height: 66px; overflow: hidden; line-height: 1.35; }
.mode-hint { font-size: 11px; color: var(--text-faint); text-align: center; margin: 4px 0 0; min-height: 14px; }
.local-pick { display: block; margin: 6px auto 0; padding: 6px 12px; border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); border: 1px solid var(--line-strong); font-size: 12px; }
.ai-caption.help { max-height: none; color: var(--text); }
.ai-caption.help .local-help { text-align: left; line-height: 1.45; }

/* ---- accessibility: honor reduced-motion (vestibular users), and a keyboard focus ring */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important; }
  .ai-orb.on { animation: none; opacity: 1; }
}
:where(button, a, select, .home-card, .ptt-btn):focus-visible {
  outline: 3px solid var(--primary); outline-offset: 2px; border-radius: var(--radius-sm); }

/* ---- light/dark toggle: REMOVED (Andy 2026-07-04, dark-only). The server (server/index.js
   THEME_TOGGLE, which this worker must not touch) still injects the #themeToggle button on every
   page; this rule makes it inert everywhere so it never renders. app.js also removes the node from
   the DOM on the home screen, and ai.js already hides it in the SightLine room. Kept as an explicit
   inert rule (not deleted) so the injected button can never reappear if the CSS below changes. */
.theme-toggle { display: none !important; }

/* ---- AI character (persona) picker — chosen before Start; matches .mode-hint / .local-pick look */
.char-pick { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 8px 0 0;
  font-size: 12px; font-weight: 600; color: var(--text-dim); }
.char-pick select { padding: 7px 12px; border-radius: var(--radius-sm); background: var(--surface);
  color: var(--text); border: 1px solid var(--line-strong); box-shadow: var(--shadow-card);
  font-size: 13px; font-weight: 600; transition: transform 0.1s var(--ease); }
.char-pick select:active { transform: scale(0.98); }

/* ---- accessibility utilities (appended 2026-07-02, client-polish) -------------------------- */
/* Visually-hidden: present for screen readers, off-screen for sighted users. Standard clip pattern.
   Used for the /see live-view <h1> so SR users get a page heading without changing the visual layout. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}
/* Target size (WCAG 2.2 SC 2.5.8): grow the /text Sound / Read-aloud switches to ≥24px tall.
   The base .txt-switch (in text.js) is 38×22; this bumps height to 24 and nudges the knob to match.
   text.js injects its <style> at runtime (AFTER this file loads), so the base rule would win on
   an equal-specificity tie — use `input.txt-switch-lg` (element+class) to outrank the base .txt-switch. */
input.txt-switch-lg { height: 24px; }
input.txt-switch-lg::after { width: 18px; height: 18px; }
input.txt-switch-lg:checked::after { transform: translateX(16px); }

/* ---- SightLine-first home (2026-07-03: the home IS SightLine; one button into the video AI) */
.sl-home-eyes { display: block; font-size: 56px; line-height: 1; margin-bottom: 10px; }
.sl-go { box-sizing: border-box; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; width: 100%; max-width: 420px; min-height: 50vh; min-height: 50dvh; margin: 26px auto; padding: 30px 24px;
  /* HALF-SCREEN ROYAL-BLUE HERO (Andy 2026-07-04): the biggest possible target for the primary
     action — blind-first. Royal blue: white text ~4.9:1 (AA), kinder to colorblind users than red,
     and frees RED to mean exclusively 'recording' in the app. Icon/splash stay terracotta pending
     a full-rebrand decision. 50vh line = old-Chrome fallback for 50dvh. */
  border-radius: var(--radius-lg); background: #4169E1; border: 0;
  appearance: none; -webkit-appearance: none; font: inherit;
  box-shadow: 0 6px 30px rgba(65,105,225,.35); text-decoration: none; text-align: center; cursor: pointer;
  transition: transform 0.12s var(--ease), box-shadow 0.15s var(--ease); }
.sl-go:active { transform: scale(0.985); box-shadow: var(--shadow-card-hover); }
.sl-go .sl-go-title { font-size: 30px; font-weight: 800; letter-spacing: -0.4px; color: #fff; line-height: 1.15; }
.sl-go .sl-go-sub { font-size: 17px; font-weight: 600; color: #fff; line-height: 1.4; max-width: 30ch; }
.sl-go:focus-visible { outline: 4px solid var(--text); outline-offset: 3px; }
/* Video Call stays functional via the quiet row; its old card is kept in DOM (startBtn id) but hidden. */
.home-cards-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

/* ---- Voice icon row (home, TOP — where the moon toggle used to sit). Andy 2026-07-04: a single
   compact centered row of ICON-ONLY companion chips (the 5 alternatives to the default SightLine
   voice). Tap = choose + audition; tap the selected one again = back to the SightLine default.
   Radiogroup semantics live in app.js; names are in the aria-label, not visible. */
.voice-strip { width: 100%; max-width: 420px; margin: 8px auto 0; text-align: center; }
.voice-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }
/* Icon-only chip: a real <button>, ≥48px target, emoji only (no visible name). */
.voice-chip { display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; padding: 0; border-radius: 50%; cursor: pointer;
  background: var(--surface); color: var(--text); border: 1px solid var(--line-strong);
  box-shadow: var(--shadow-card); transition: transform 0.1s var(--ease), border-color 0.12s var(--ease), box-shadow 0.12s var(--ease); }
.voice-chip:active { transform: scale(0.92); }
.voice-chip-emoji { font-size: 26px; line-height: 1; }
.voice-chip:focus-visible { outline: 3px solid var(--primary); outline-offset: 3px; }
/* Selected voice: primary ring + tinted background + a slight scale so it's obvious at a glance
   (aria-checked drives it — no JS class needed). Also announced via the aria-live status line. */
.voice-chip[aria-checked="true"] { border-color: var(--primary); background: var(--primary-soft);
  box-shadow: 0 0 0 3px var(--primary) inset; transform: scale(1.08); }
.voice-chip[aria-checked="true"]:active { transform: scale(1.02); }
/* Currently-playing: a gentle pulse so the audition is visible. */
.voice-chip.playing { border-color: var(--accent); }
.voice-chip.playing .voice-chip-emoji { animation: voiceChipPulse 0.9s var(--ease) infinite; }
@keyframes voiceChipPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) { .voice-chip.playing .voice-chip-emoji { animation: none; } }
/* "Your companion: X" — kept for the aria-live announcement, visually quiet. */
.voice-strip-status { margin: 8px 0 0; min-height: 16px; font-size: 12px; font-weight: 500; color: var(--text-faint); }

/* ---- Secondary big buttons (home) — Video Call + Share your screen. Same VISUAL FAMILY as .sl-go
   (full-width, large, stacked title + sub) but visually SECONDARY: outlined on a quiet fill instead
   of the primary terracotta, so the "Open SightLine" button stays the clear hero. */
.sl-secondary { box-sizing: border-box; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 6px; width: 100%; max-width: 420px; min-height: 96px; margin: 12px auto 0;
  padding: 20px 22px; border-radius: var(--radius-lg); background: var(--surface);
  border: 2px solid var(--line-strong); box-shadow: var(--shadow-card); text-decoration: none;
  text-align: center; cursor: pointer; color: var(--text);
  transition: transform 0.12s var(--ease), border-color 0.12s var(--ease), box-shadow 0.15s var(--ease); }
.sl-secondary:active { transform: scale(0.985); box-shadow: var(--shadow-card-hover); }
.sl-secondary .sl-secondary-title { display: flex; align-items: center; gap: 10px;
  font-size: 22px; font-weight: 800; letter-spacing: -0.3px; color: var(--text); line-height: 1.15; }
.sl-secondary .sl-secondary-sub { font-size: 16px; font-weight: 500; color: var(--text-dim); line-height: 1.4; max-width: 34ch; }
.sl-secondary:focus-visible { outline: 4px solid var(--primary); outline-offset: 3px; }

/* ---- "How to use SightLine" — a clear MEDIUM secondary row right below the big buttons (moved out
   of the footer; Andy: a blind person can't find it down there). Quieter than .sl-secondary. */
.sl-howto { box-sizing: border-box; display: flex; flex-direction: column; align-items: center;
  gap: 4px; width: 100%; max-width: 420px; min-height: 64px; margin: 12px auto 0; padding: 14px 20px;
  border-radius: var(--radius); background: transparent; border: 1px solid var(--line-strong);
  text-decoration: none; text-align: center; cursor: pointer; color: var(--text);
  transition: transform 0.12s var(--ease), border-color 0.12s var(--ease); }
.sl-howto:active { transform: scale(0.985); }
.sl-howto .sl-howto-title { font-size: 18px; font-weight: 700; letter-spacing: -0.2px; color: var(--text); }
.sl-howto .sl-howto-sub { font-size: 15px; font-weight: 500; color: var(--text-dim); }
.sl-howto:focus-visible { outline: 3px solid var(--primary); outline-offset: 2px; }

/* ── THE WHOLE SCREEN IS THE DOOR — blind-home roundtable WINNER, ported verbatim (2026-07-18).
   The home viewport above the thin footer is ONE native button (#slOpen .sl-door). The visual is
   the winning mockup's door of light: a breathing golden ring around one verb on a dark radial
   ground, glow halo, hairline footer. Variants carried from the mockup: light scheme,
   prefers-contrast:more (pure black/white, halo off), prefers-reduced-motion (no breathing).
   Honesty divergences from the mockup are in the template comment (app.js). */
.screen-home {
  --sl-bg:#050608; --sl-bg2:#0c1119; --sl-ink:#ffffff; --sl-ink-dim:#c7cdd8;
  --sl-door:#ffdfa6; --sl-door-glow:rgba(255,206,132,.34); --sl-link:#ffdfa6;
  --sl-hair:rgba(255,255,255,.14); --sl-focus:#8fd8ff;
  background:radial-gradient(125% 82% at 50% 40%, var(--sl-bg2), var(--sl-bg) 68%);
  color:var(--sl-ink); min-height:100dvh; overflow:hidden;
}
@media (prefers-color-scheme: light){
  .screen-home{
    --sl-bg:#faf7f1; --sl-bg2:#ffffff; --sl-ink:#0a0b0d; --sl-ink-dim:#3a4048;
    --sl-door:#7a4a00; --sl-door-glow:rgba(150,100,20,.18); --sl-link:#6b4200;
    --sl-hair:rgba(0,0,0,.18); --sl-focus:#0b57d0;
  }
}
.screen-home .home-head{ position:absolute; width:1px; height:1px; overflow:hidden; } /* the h1 stays first in DOM for the reader; the DOOR owns every pixel above the footer */
.sl-door{
  flex:1 1 auto; align-self:stretch; margin:0; padding:0 22px; border:0;
  background:transparent; color:inherit; font:inherit;
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:clamp(20px,5.2vh,44px); cursor:pointer;
  -webkit-tap-highlight-color:transparent; touch-action:manipulation;
}
.sl-door:focus{ outline:none; }
.sl-door:focus-visible{ outline:none; box-shadow:inset 0 0 0 4px var(--sl-focus); }
.sl-door-aperture{ position:relative; display:grid; place-items:center; }
.sl-door-halo{
  position:absolute; width:clamp(230px,66vw,300px); aspect-ratio:1; border-radius:50%;
  box-shadow:0 0 80px 22px var(--sl-door-glow);
  animation:sl-door-halo 6.5s ease-in-out infinite; pointer-events:none;
}
.sl-door-ring{
  width:clamp(230px,66vw,300px); aspect-ratio:1; border-radius:50%;
  border:clamp(8px,2.4vw,12px) solid var(--sl-door);
  box-shadow:0 0 60px var(--sl-door-glow), inset 0 0 42px var(--sl-door-glow);
  display:grid; place-items:center;
  animation:sl-door-breathe 6.5s ease-in-out infinite;
}
.sl-door-word{
  font-size:clamp(54px,21vw,104px); font-weight:800; letter-spacing:-.03em;
  line-height:1; color:var(--sl-ink);
}
.sl-door-hint{
  font-size:clamp(16px,4.4vw,21px); font-weight:600; letter-spacing:.005em;
  color:var(--sl-ink-dim); text-align:center; max-width:20ch;
}
.sl-door-status{
  flex:0 0 auto; padding:0 22px; text-align:center; pointer-events:none;
  font-size:clamp(15px,4vw,19px); font-weight:600; color:var(--sl-ink-dim); margin:0 0 4px;
}
.screen-home .home-foot{
  flex:0 0 auto; border-top:1px solid var(--sl-hair); margin:0;
  padding:10px 14px calc(10px + env(safe-area-inset-bottom));
  color:var(--sl-ink-dim); font-size:clamp(12px,3.4vw,14px); line-height:1.7;
}
.screen-home .home-foot a{
  color:var(--sl-link); text-decoration:underline; text-underline-offset:3px; padding:0 3px;
}
.screen-home .home-foot-hint{ color:var(--sl-ink-dim); opacity:.85; }
@keyframes sl-door-breathe{ 0%,100%{ transform:scale(1); } 50%{ transform:scale(1.035); } }
@keyframes sl-door-halo{ 0%,100%{ opacity:.28; transform:scale(1); } 50%{ opacity:.5; transform:scale(1.12); } }
@media (prefers-reduced-motion: reduce){
  .sl-door-ring, .sl-door-halo{ animation:none; }
}
@media (prefers-contrast: more){
  .screen-home{
    --sl-bg:#000; --sl-bg2:#000; --sl-ink:#fff; --sl-ink-dim:#fff;
    --sl-door:#fff; --sl-door-glow:transparent; --sl-link:#fff; --sl-hair:#fff; --sl-focus:#ff0;
    background:#000;
  }
  .sl-door-halo{ display:none; }
  .sl-door-ring{ box-shadow:none; border-width:12px; }
}
