/* ── NO TEXTURE ON THE BACKGROUND (31 Aug 2026, her call) ─────────────────
   She asked for the background to read smooth, the way vidrush.ai's does:
   no film grain, no scratches, no vignette. All three are DELETED rather
   than hidden, because 'remove it' here means remove it.
     - the grain plate was body::after (a fixed feTurbulence noise tile)
     - the vignette was html::after (a fixed radial darkening the edges)
     - the scratches were .scratch / .scr-* (real spans in landing.html)
   The blooms, the shafts and every colour are untouched - she was explicit
   that the colour does not change. */
/* ═══════════════════════════════════════════════════════════════
   TORQUEVID DESIGN SYSTEM — "Glass Studio"
   Dark navy glass UI. Single accent: electric blue.
   Principles: glassmorphism cards, gradient borders, ambient depth,
   generous spacing, subtle motion, 3-radius system.

   ── 2026 REFINEMENT PASS ──────────────────────────────────────
   Palette unchanged (navy bg, electric-blue accent). What changed:
   • Formal 4px-based spacing scale (was ad-hoc pixel values)
   • Layered elevation system (3 depth tiers + float, was 1 shadow)
   • Expanded, purpose-named easing curves + duration tokens
   • Subtle film-grain overlay on the whole surface (studio texture)
   • Tighter, heavier headline typography + calmer body line-height
   • Physical button press feedback (inset + lift, not just scale)
   • Skeleton-shimmer loading utility (ready to replace spinners)
   • Floating particle field + cursor glow (galaxy feel)
   Everything is additive — no class or ID was renamed or removed.
   ═══════════════════════════════════════════════════════════════ */

:root {
  /* the caret and form-control palette follow the theme (24 Aug a11y pack) —
     declared inside the canonical :root so tests anchored on ":root {" keep
     finding the variables block first, not a one-line stub */
  caret-color: var(--accent);
  color-scheme: dark;
  /* ── Surfaces (dark navy, translucent for glass) ── */
  --bg: #060611;
  --surface: rgba(12, 15, 30, 0.65);
  --surface-solid: #0c0f1e;
  --surface-2: rgba(18, 22, 42, 0.55);
  --surface-2-solid: #121628;
  --surface-3: rgba(26, 31, 54, 0.50);
  --surface-3-solid: #1a1f36;

  /* ── Glass ── */
  --glass-blur: 20px;
  --glass-highlight: rgba(255, 255, 255, 0.04);

  /* ── Hairlines ── */
  --border: rgba(255, 255, 255, 0.06);
  --border-strong: rgba(255, 255, 255, 0.10);

  /* ── Text ── */
  --text: #edf0f7;
  --text-2: #8b90ab;
  --text-3: #4e5270;

  /* ── Signature accent — deep amber (torque / engine heat). 2026 redesign. ── */
  /* 27 Jul reskin — violet-indigo system (her refs: AirLume light, Quantum
     dark), one hue family across both themes. Amber lives on as --brand for
     the signature touches: the logo, credit chips. */
  --accent: #f5a623;
  --accent-hover: #ffbe52;
  --accent-light: #ffb94d;
  --accent-gradient: linear-gradient(135deg, #ffb94d 0%, #f5a623 45%, #f26d3d 100%);
  --accent-ink: #1a1206;                          /* dark text on a solid amber fill */
  --accent-soft: rgba(245, 166, 35, 0.10);
  --accent-border: rgba(245, 166, 35, 0.26);
  --accent-glow: rgba(245, 166, 35, 0.16);
  --brand: #f5a623;                               /* TorqueVid amber — touches only */
  --brand-soft: rgba(245, 166, 35, 0.12);
  --brand-border: rgba(245, 166, 35, 0.30);
  --brand-ink: #1a1206;

  /* ── Green — the SECONDARY accent (pairs with amber for a fresher, richer palette) ── */
  --green: #2fd07e;
  --green-light: #57e29a;
  --green-ink: #04210f;
  --green-soft: rgba(47, 208, 126, 0.12);
  --green-border: rgba(47, 208, 126, 0.30);
  --green-glow: rgba(47, 208, 126, 0.18);
  --green-gradient: linear-gradient(135deg, #57e29a 0%, #2fd07e 55%, #17b98e 100%);

  /* ── Info (cool blue) — kept for system/in-progress signals per the design doc ── */
  --info: #5b8af5;
  --info-soft: rgba(91, 138, 245, 0.12);
  --info-border: rgba(91, 138, 245, 0.28);

  /* ── Semantic ── */
  --success: #34d399;
  --success-soft: rgba(52, 211, 153, 0.10);
  --success-border: rgba(52, 211, 153, 0.25);
  --danger: #f87171;
  --danger-soft: rgba(248, 113, 113, 0.10);
  --danger-border: rgba(248, 113, 113, 0.25);

  /* ── Type scale ──────────────────────────────────────────────────────
     SEVEN sizes. Nothing in the product may invent its own. Before this,
     44 different font sizes were in use — that's the single loudest tell
     of software nobody designed: no two things quite line up.
     Ties round UP; readability matters across this product. */
  --fs-xs:  11px;   /* badges, timecodes, micro-labels */
  --fs-sm:  13px;   /* meta, captions, secondary lines */
  --fs-md:  15px;   /* body — the default */
  --fs-lg:  18px;   /* emphasis, card titles */
  --fs-xl:  24px;   /* section headings */
  --fs-2xl: 34px;   /* page titles */
  --fs-3xl: 48px;   /* hero */

  /* ── Shape ──
     FOUR surface radii. Anything under 4px is a hairline on a bar or an
     equaliser stick, not a surface, and is left alone deliberately. */
  --r-xs: 4px;      /* hairline detail */
  --r-sm: 8px;      /* chips, inputs, small controls */
  --r: 12px;        /* cards, panels */
  --r-lg: 16px;     /* large surfaces, modals */
  --r-pill: 999px;

  /* ── Spacing scale (4px base) ── */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 20px;
  --sp-6: 24px;
  --sp-7: 32px;
  --sp-8: 40px;
  --sp-9: 56px;
  --sp-10: 72px;

  /* ── Type ── */
  --font: 'Plus Jakarta Sans', 'Inter', -apple-system, 'Segoe UI', system-ui, sans-serif;
  /* 26 Aug redesign: the display face for every heading (her pick — ClickMax's). */
  --font-display: 'Space Grotesk', 'Plus Jakarta Sans', 'Inter', system-ui, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, 'Cascadia Code', Consolas, monospace;
  --mono-size: var(--fs-sm);
  --mono-tracking: -0.01em;

  /* ── Elevation ── */
  --shadow-1:
    0 1px 2px rgba(0, 0, 0, 0.28),
    0 2px 6px rgba(0, 0, 0, 0.22),
    inset 0 1px 0 var(--glass-highlight);
  --shadow-2:
    0 2px 4px rgba(0, 0, 0, 0.30),
    0 8px 24px rgba(0, 0, 0, 0.32),
    inset 0 1px 0 var(--glass-highlight);
  --shadow-3:
    0 4px 8px rgba(0, 0, 0, 0.32),
    0 18px 48px rgba(0, 0, 0, 0.42),
    inset 0 1px 0 var(--glass-highlight);
  --shadow-float:
    0 8px 16px rgba(0, 0, 0, 0.34),
    0 32px 64px rgba(0, 0, 0, 0.46),
    inset 0 1px 0 rgba(255, 255, 255, 0.06);
  --shadow-pop: var(--shadow-2);
  --shadow-glow: 0 0 40px var(--accent-glow);

  /* ── Motion ── */
  --ease: cubic-bezier(0.25, 0.8, 0.3, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  --fast: 150ms;
  --med: 240ms;
  --slow: 380ms;
}

/* ── Reset & base ── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* No sideways scroll at phone width: the app shell clips any child that runs
   past the viewport. `clip` (not `hidden`) never becomes a scroll container, so
   the body's own vertical scroll, the sticky Look/Theme preview and the fixed
   sidebar are all untouched; inner `overflow-x:auto` rails (the channel
   switcher) keep their own scroll. landing.html already clips its own <html>;
   this is the same guard for the create/dashboard shell, which lacked one. */
.app-shell { overflow-x: clip; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-md);
  line-height: 1.65;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-feature-settings: 'cv01' on, 'ss03' on, 'zero' on;
}
button { font-family: inherit; }
a { color: var(--accent); text-decoration: none; transition: color var(--fast) var(--ease); }
a:hover { color: var(--accent-hover); }
::selection { background: rgba(245, 166, 35, 0.30); color: var(--text); }
/* Prominent, high-contrast focus ring */
/* no border-radius here: setting it on the FOCUSED ELEMENT reshaped pills and round avatars to 8px corners whenever focused (24 Aug audit) */
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; box-shadow: 0 0 0 5px rgba(245, 166, 35, 0.22); }

/* Skip-to-content link — first tab stop, visually hidden until focused */
.skip-link {
  position: fixed; top: 10px; left: 10px; z-index: 200;
  transform: translateY(-160%);
  padding: 10px 16px; border-radius: var(--r);
  background: var(--accent); color: #fff; font-weight: 650; font-size: var(--fs-md);
  box-shadow: var(--shadow-float); transition: transform var(--fast) var(--ease);
}
.skip-link:focus-visible { transform: translateY(0); color: #fff; }

/* Comfortable touch targets on touch devices */
@media (pointer: coarse) {
  .nav-item, .btn, .action-btn, .h-btn, .tab, button.theme-toggle { min-height: 44px; }
}

/* ── QUANTUM ATMOSPHERE (dark, 27 Jul, everywhere — her pick, testing option 2).
   A violet glow horizon low on the screen + two faint high glows, breathing on
   a very slow drift. All alphas low enough that text is untouched.
   FALLBACK TO OPTION 1 (hero screens only): change the selector below to
   `body.create-fs.hero-up::before, body.landing::before` — one line. ── */
body::before {
  content: "";
  position: fixed; inset: -6% 0 0 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(120% 62% at 50% 116%, rgba(109, 91, 232, 0.11), transparent 62%),
    radial-gradient(700px 540px at 12% 4%, rgba(139, 124, 255, 0.055), transparent 62%),
    radial-gradient(760px 560px at 88% 22%, rgba(167, 139, 250, 0.04), transparent 60%),
    radial-gradient(920px 660px at 50% 46%, rgba(245, 166, 35, 0.035), transparent 72%);
  animation: tvAtmoDrift 80s ease-in-out infinite alternate;
}
/* Alive at the edge of perception — never where she reads. */
@keyframes tvAtmoDrift {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(0, -1.8%, 0) scale(1.045); }
}
@media (prefers-reduced-motion: reduce) { body::before { animation: none; } }

/* ── Interactive glow (27 Jul): every primary control answers the cursor.
   box-shadow + border only — transforms stay with each component. ── */
@media (hover: hover) {
  .btn-generate:hover:not(:disabled), .btn-ai:hover:not(:disabled) {
    box-shadow: 0 8px 28px var(--accent-glow); filter: brightness(1.06);
  }
  .cs-btn:hover, .hi-card:hover, .hero-wcard:hover, .start-card:hover,
  .preset-chip:hover, .hca-item:hover, .theme-card:hover, .vc2-card:hover {
    box-shadow: 0 6px 22px var(--accent-glow);
  }
}


/* ── Scrollbars ── */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: var(--r-sm); }
::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.14); }

/* ── Typography helpers ── */
.kicker {
  font-size: var(--fs-xs); font-weight: 600;
  letter-spacing: 0.11em; text-transform: uppercase;
  color: var(--text-3);
}
.mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--mono-size);
  letter-spacing: var(--mono-tracking);
}

/* ═══ Brand mark ═══ */
/* Brand logo lives on a glossy light "app-icon" tile so the black TorqueVid
   monogram reads on BOTH the dark and light navs (a bare transparent logo would
   vanish on dark). One rule → the logo shows on every page that uses .tv-mark. */
.tv-mark {
  display: grid; place-items: center;
  background-color: #f6f7fb;
  background-image:
    radial-gradient(130% 130% at 28% 18%, rgba(255,255,255,0.95), rgba(255,255,255,0) 60%),
    url("/logo.png");
  background-size: cover, 90%;
  background-position: center, center;
  background-repeat: no-repeat, no-repeat;
  border: 1px solid rgba(18, 22, 38, 0.10);
  border-radius: 26%; flex-shrink: 0;
  box-shadow: 0 5px 16px rgba(15, 20, 35, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
.tv-mark svg { display: none; } /* replaced by the logo image above */

/* ═══ Buttons ═══ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 9px;
  border: none; border-radius: var(--r-sm);
  font-family: inherit; font-weight: 600; cursor: pointer;
  text-decoration: none; white-space: nowrap;
  letter-spacing: -0.005em;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              color var(--fast) var(--ease), box-shadow var(--med) var(--ease),
              transform var(--fast) var(--ease-spring);
}
.btn:active:not(:disabled) { transform: translateY(1px) scale(0.98); }
.btn:disabled { cursor: not-allowed; opacity: 0.45; }

.btn-accent {
  background: var(--accent-gradient);
  color: var(--accent-ink);
  font-size: var(--fs-md); padding: 11px 22px;
  box-shadow: 0 2px 12px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.18);
}
.btn-accent:hover:not(:disabled) {
  box-shadow: 0 6px 28px rgba(91, 138, 245, 0.38), inset 0 1px 0 rgba(255,255,255,0.22);
  transform: translateY(-1px);
}
.btn-accent:active:not(:disabled) {
  box-shadow: 0 1px 8px var(--accent-glow), inset 0 2px 6px rgba(0,0,0,0.25);
}

.btn-ghost {
  background: var(--surface-2);
  color: var(--text);
  border: 1px solid var(--border-strong);
  font-size: var(--fs-sm); font-weight: 550; padding: 9px 16px;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.btn-ghost:hover:not(:disabled) { background: var(--surface-3); border-color: rgba(255,255,255,0.14); box-shadow: var(--shadow-1); }
.btn-ghost:active:not(:disabled) { box-shadow: inset 0 2px 6px rgba(0,0,0,0.22); }

.btn-quiet {
  background: none; color: var(--text-2);
  font-size: var(--fs-sm); font-weight: 500;
  padding: 6px 12px; border-radius: var(--r-sm);
}
.btn-quiet:hover:not(:disabled) { background: var(--surface-2); color: var(--text); }

/* ═══ Glass cards ═══ */
.card {
  background: var(--surface);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--border);
  border-radius: var(--r);
  box-shadow: var(--shadow-2);
  animation: rise var(--slow) var(--ease-out) both;
}
@keyframes rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}
@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes float1 {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(60px, 40px) scale(1.1); }
}
@keyframes float2 {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(-50px, -30px) scale(1.15); }
}
@keyframes rise-item {
  from { opacity: 0; transform: translateX(-6px); }
  to   { opacity: 1; transform: none; }
}

/* ═══ Inputs ═══ */
/* 16px on touch devices: below 16px iOS zooms the whole viewport on focus (24 Aug audit) */
@media (pointer: coarse) { .input, input.input, textarea.input, select.input { font-size: 16px; } }
.input, input.input, textarea.input, select.input {
  width: 100%; padding: 12px 14px;
  background: rgba(10, 12, 26, 0.6);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); color: var(--text);
  font-size: var(--fs-md); font-family: inherit; outline: none;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  transition: border-color var(--fast) var(--ease), box-shadow var(--med) var(--ease), background var(--fast) var(--ease);
}
.input::placeholder { color: var(--text-3); }
.input:hover { border-color: rgba(255,255,255,0.14); }
.input:focus {
  border-color: var(--accent-border);
  box-shadow: 0 0 0 3px var(--accent-glow), 0 1px 2px rgba(0,0,0,0.2);
  background: rgba(12, 15, 30, 0.8);
}

.field-label {
  display: block; font-size: var(--fs-sm); font-weight: 600;
  color: var(--text-2); margin-bottom: 8px;
  letter-spacing: -0.006em;
}

/* ═══ Badges & pills ═══ */
.badge {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px; font-size: var(--fs-xs); font-weight: 600;
  letter-spacing: 0.02em; border-radius: var(--r-pill);
  border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text-2);
}
.badge-accent  { background: var(--accent-soft);  color: var(--accent);  border-color: var(--accent-border); }
.badge-success { background: var(--success-soft); color: var(--success); border-color: var(--success-border); }
.badge-danger  { background: var(--danger-soft);  color: var(--danger);  border-color: var(--danger-border); }

/* ═══ Spinners ═══ */
.spin {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.12);
  border-top-color: currentColor;
  animation: spin 0.7s linear infinite; flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ═══ Skeleton shimmer ═══ */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--surface-2-solid);
  border-radius: var(--r-sm);
}
.skeleton::after {
  content: ""; position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg,
    transparent, rgba(255,255,255,0.06) 40%, rgba(91,138,245,0.10) 50%,
    rgba(255,255,255,0.06) 60%, transparent);
  animation: shimmer 1.4s var(--ease-in-out) infinite;
}
@keyframes shimmer { to { transform: translateX(100%); } }

/* ═══ Toast notifications ═══ */
.toast-container {
  position: fixed; top: 20px; right: 20px; z-index: 200;
  display: flex; flex-direction: column; gap: 10px;
  pointer-events: none;
  width: min(360px, calc(100vw - 40px));
}
.toast {
  pointer-events: auto; cursor: pointer;
  display: flex; align-items: flex-start; gap: 11px;
  padding: 13px 16px; border-radius: var(--r-sm);
  background: var(--surface-2-solid);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  box-shadow: var(--shadow-3);
  color: var(--text); font-size: var(--fs-md); line-height: 1.5;
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  animation: toast-in var(--med) var(--ease-out) both;
}
.toast.leaving { animation: toast-out var(--fast) var(--ease) forwards; }
.toast .toast-dot { width: 8px; height: 8px; border-radius: 50%; margin-top: 6px; flex-shrink: 0; background: var(--accent); }
.toast .toast-msg { flex: 1; }
.toast.success { border-left-color: var(--success); }
.toast.success .toast-dot { background: var(--success); }
.toast.error   { border-left-color: var(--danger); }
.toast.error .toast-dot   { background: var(--danger); }
.toast.info    { border-left-color: var(--accent); }
.toast.info .toast-dot    { background: var(--accent); }
@keyframes toast-in  { from { opacity: 0; transform: translateX(28px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateX(28px); } }

/* ═══ Particles canvas ═══ */
#particles-canvas {
  position: fixed; inset: 0; z-index: 0;
  pointer-events: none;
  opacity: 0.7;
}

/* Cursor-follow glow removed 26 Aug (her order — read as slop). The `.cursor-glow`
   element on each page is now inert (no size/paint); its mousemove handlers keep
   running the hero-aurora lean + particle parallax, which never needed the glow. */

/* ═══ Ambient background ═══ */
.ambient {
  position: fixed; inset: 0; overflow: hidden;
  pointer-events: none; z-index: 0;
}
.ambient-orb {
  position: absolute; border-radius: 50%;
  filter: blur(120px); opacity: 0.7;
}
.ambient-1 {
  width: 600px; height: 600px;
  background: rgba(124, 92, 255, 0.11);   /* violet */
  top: -250px; left: -150px;
  animation: float1 25s ease-in-out infinite alternate;
}
.ambient-2 {
  width: 500px; height: 500px;
  background: rgba(45, 212, 191, 0.085);  /* teal */
  bottom: -250px; right: -150px;
  animation: float2 30s ease-in-out infinite alternate;
}
.ambient-3 {
  width: 440px; height: 440px;
  background: rgba(245, 166, 35, 0.06);   /* amber */
  top: 40%; left: 50%;
  animation: float1 35s ease-in-out infinite alternate-reverse;
}

/* ═══ Light theme ═══
   Opt-in via <html data-theme="light">. Same electric-blue accent,
   inverted navy → soft-paper surfaces. Everything reads from tokens,
   so this override is all it takes. */
:root[data-theme="light"] {
  color-scheme: light;
  /* Warm LIQUID GLASS: ivory / cream frosted surfaces (never plain gray), translucent
     so the blur + soft green-teal glows refract through, a bright top sheen, floating
     depth. Reads as light through warm frosted glass — Apple / Linear / Arc territory. */
  --bg: #eeedf6;
  --surface: rgba(253, 253, 255, 0.72);
  --surface-solid: #ffffff;
  --surface-2: rgba(248, 248, 253, 0.64);
  --surface-2-solid: #f6f6fb;
  --surface-3: rgba(241, 241, 249, 0.58);
  --surface-3-solid: #edecf5;
  --glass-highlight: rgba(255, 255, 255, 0.96);
  --glass-blur: 22px;
  --border: rgba(70, 66, 96, 0.11);
  --border-strong: rgba(70, 66, 96, 0.18);
  --text: #1c1d28;
  --text-2: #53536a;
  --text-3: #86869c;
  --accent-soft: rgba(245, 166, 35, 0.14);
  --accent-glow: rgba(245, 166, 35, 0.22);
  --accent-border: rgba(217, 136, 15, 0.32);
  /* Green second accent — a touch stronger alpha so it reads on bright glass */
  --green-soft: rgba(30, 175, 105, 0.16);
  --green-border: rgba(26, 155, 92, 0.36);
  --green-glow: rgba(38, 190, 120, 0.24);
  --green-ink: #04210f;
  --info-soft: rgba(91, 138, 245, 0.12);
  --shadow-1:
    0 1px 2px rgba(24, 33, 66, 0.06),
    0 2px 6px rgba(24, 33, 66, 0.06),
    inset 0 1px 0 var(--glass-highlight);
  /* Softer, deeper shadows so cards feel like they float — premium light depth */
  --shadow-2:
    0 2px 6px rgba(28, 40, 75, 0.06),
    0 16px 38px rgba(28, 40, 75, 0.11),
    inset 0 1px 0 var(--glass-highlight);
  --shadow-3:
    0 6px 14px rgba(28, 40, 75, 0.08),
    0 30px 64px rgba(28, 40, 75, 0.15),
    inset 0 1px 0 var(--glass-highlight);
  --shadow-float:
    0 10px 22px rgba(28, 40, 75, 0.10),
    0 44px 92px rgba(28, 40, 75, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.9);
}
/* Near-invisible frosted grain — only breaks up flat gradients so light never bands;
   never reads as visible film grain (light through frosted glass, not noise). */
:root[data-theme="light"] #particles-canvas { opacity: 0.5; }
/* Titanium backdrop — the brushed cool-silver gradient goes on the BODY (behind
   all content); ::before carries only TRANSPARENT glows so it never covers content
   (an opaque ::before was hiding the pricing cards + FAQ in light mode). */
:root[data-theme="light"] body {
  /* "Green glass" (Doks.ai reference): a big BRIGHT green spotlight bloom whose core sits IN
     view near the top and radiates down behind the hero, strong green blooms on both sides, a
     mid arc for depth, a faint lavender refraction — over a clearly green base. Bloom + haze
     layered and turned up so the green reads instantly (not a pale wash). */
  background:
    radial-gradient(1500px 820px at 50% 3%, rgba(40, 214, 132, 0.50), transparent 56%),       /* SPOTLIGHT bloom — core in view */
    radial-gradient(1150px 760px at 50% 44%, rgba(66, 220, 152, 0.20), transparent 62%),       /* mid arc — depth behind hero */
    radial-gradient(940px 900px at 3% 18%, rgba(92, 226, 168, 0.32), transparent 60%),         /* left bloom */
    radial-gradient(980px 900px at 98% 82%, rgba(74, 218, 158, 0.32), transparent 60%),        /* right bloom */
    radial-gradient(720px 640px at 26% 90%, rgba(186, 172, 250, 0.13), transparent 64%),       /* lavender refraction */
    linear-gradient(165deg, #e6f6ec 0%, #ddf1e5 52%, #d6ede0 100%)                             /* green base */
    fixed;
}
/* Vertical FLUTED-GLASS ribs across the whole page — soft white highlights with faint
   lilac grooves, low-contrast so it reads as premium frosted-fluted glass, not stripes. */
/* Clean green glass — the old vertical fluted ribs read as banding, so they're gone. This
   layer now just deepens the green glow + adds a soft bright sheen along the very top. */
:root[data-theme="light"] body::before {
  background:
    radial-gradient(1100px 560px at 50% 0%, rgba(150, 236, 190, 0.28), transparent 60%),   /* bright top sheen */
    radial-gradient(1050px 860px at 28% 40%, rgba(74, 208, 146, 0.18), transparent 68%),
    radial-gradient(820px 700px at 90% 86%, rgba(96, 216, 156, 0.20), transparent 62%);
}
/* Drifting green orbs — a touch brighter to feed the glassy green bloom */
:root[data-theme="light"] .ambient-orb { opacity: 0.42; filter: blur(150px); mix-blend-mode: multiply; }
:root[data-theme="light"] .ambient-1 { background: rgba(72, 210, 148, 0.19); }  /* green */
:root[data-theme="light"] .ambient-2 { background: rgba(104, 214, 162, 0.17); } /* green */
:root[data-theme="light"] .ambient-3 { background: rgba(178, 168, 238, 0.09); } /* lilac accent */

/* ── Green "grid floor" receding into the distance (light theme only) — a faint glowing
   perspective grid pinned to the bottom edge, à la the Doks.ai reference. Static (GPU-cheap),
   masked so it dissolves well before the content band, so it never fights readability.
   Lives on .ambient (fixed, overflow:hidden, on every page) → same layer as the orbs,
   i.e. behind all content, and clipped to the viewport so it can never cause overflow. ── */
:root[data-theme="light"] .ambient::after {
  content: ""; position: absolute; left: -50%; bottom: 0; width: 200%; height: 42vh;
  pointer-events: none;
  transform: perspective(300px) rotateX(62deg); transform-origin: bottom center;
  background-image:
    linear-gradient(to right, rgba(38, 190, 116, 0.22) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(38, 190, 116, 0.22) 1px, transparent 1px);
  background-size: 46px 46px;
  -webkit-mask: linear-gradient(to top, #000 0%, rgba(0,0,0,0.5) 28%, transparent 74%);
          mask: linear-gradient(to top, #000 0%, rgba(0,0,0,0.5) 28%, transparent 74%);
  opacity: 0.55;
}
@media (prefers-reduced-motion: reduce) { :root[data-theme="light"] .ambient::after { opacity: 0.42; } }

/* ═══ Pricing-card highlight. The card catches light where you point at it — a
   soft radial that follows the pointer, clipped to the card radius (inset ::after
   with border-radius:inherit, so it never spills over the corners or the "popular"
   ribbon, which is a separate ::before sitting ABOVE the card).

   This REPLACED a band of light that swept every card on a perpetual 9s loop.
   Nothing drove that sweep, so it read as a glitch rather than as material — the
   owner's words were "looks glitched … annoying … very low quality". Light that
   answers the pointer reads as a surface; light that moves on its own reads as a bug.

   --mx/--my come from the delegated pointermove listener in torquevid-motion.js,
   which arms only for fine pointers with real hover and never under reduced motion.
   With no listener at all the card simply stays still: the highlight is opacity:0
   until :hover, so nothing on this page ever animates of its own accord. ═══ */
.plan { position: relative; }
/* The highlight lives on its OWN isolated layer (translateZ + backface) so a card's
   hover-lift never forces it to re-rasterize with the card body. No `will-change`:
   hinting it kept the layer permanently re-uploading on weak GPUs, which stuttered. */
.plan::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 3;
  border-radius: inherit;
  /* Fixed at the card's top edge, NOT at the pointer. This used to be
     `at var(--mx) var(--my)` — a light that followed the cursor across the card,
     i.e. the pricing page's cursor glow. Removed 30 Aug (her order: no cursor
     glow anywhere). The hover sheen stays; only the tracking is gone. */
  background: radial-gradient(340px circle at 50% -20%,
                              rgba(255,255,255,0.11), transparent 62%);
  opacity: 0; transition: opacity .34s var(--ease, ease);
  transform: translateZ(0);
  backface-visibility: hidden;
}
/* White light vanishes on near-white glass — the light theme gets warm light. */
:root[data-theme="light"] .plan::after {
  background: radial-gradient(340px circle at 50% -20%,
                              rgba(245,166,35,0.16), transparent 62%);
}
@media (hover: hover) and (pointer: fine) { .plan:hover::after { opacity: 1; } }
/* Reduced motion: the listener never arms, so --mx/--my never move. Hovering would
   otherwise fade in a highlight frozen at the default 50%/-20% — a static blob, i.e.
   the same complaint in a new costume. It stays fully off instead. The :hover selector
   is repeated deliberately: the rule above is (0,2,1), so a bare .plan::after at
   (0,1,1) would LOSE and the blob would appear. */
@media (prefers-reduced-motion: reduce) {
  .plan::after, .plan:hover::after { transition: none; opacity: 0; }
}

/* ═══ Premium "silk" background — a soft ribbon of light slowly drifts behind
   everything (image-1 look). Two blurred highlights on the fixed .ambient layer +
   the existing film grain = flowing premium light. Transform-only drift, GPU-light. ═══ */
.ambient::before {
  content: ""; position: absolute; inset: -25%; pointer-events: none;
  background:
    radial-gradient(46% 34% at 32% 42%, rgba(255,255,255,0.055), transparent 62%),
    radial-gradient(40% 30% at 70% 60%, rgba(255,255,255,0.035), transparent 62%);
  filter: blur(46px);
  animation: silk-drift 38s ease-in-out infinite alternate;
}
:root[data-theme="light"] .ambient::before {
  background:
    radial-gradient(46% 34% at 32% 42%, rgba(190,175,250,0.13), transparent 62%),
    radial-gradient(40% 30% at 70% 60%, rgba(120,205,165,0.10), transparent 62%);
}
@keyframes silk-drift {
  0%   { transform: translate3d(-3%, -2%, 0) rotate(-2deg) scale(1.05); }
  100% { transform: translate3d(4%, 3%, 0) rotate(2deg) scale(1.13); }
}
@media (prefers-reduced-motion: reduce) { .ambient::before { animation: none; } }

/* ═══ Floating in space — cards drift gently up and down (transform-only, GPU-light).
   Desynced so they never bob in unison. Elements that GSAP drives directly (.plan
   tilt, .btn-accent magnet) are intentionally excluded so nothing fights. ═══ */
@keyframes tv-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
@media (prefers-reduced-motion: no-preference) {
  /* Only .feature floats freely — it's a grid child, never a direct [data-reveal] child,
     so it can't fight the scroll-reveal transform (that mismatch was a glitch source). */
  .feature {
    animation: tv-float 6.5s ease-in-out infinite;
    will-change: transform;
  }
  .feature:nth-child(3n)   { animation-delay: -1.2s; animation-duration: 7.2s; }
  .feature:nth-child(3n+1) { animation-delay: -3.4s; animation-duration: 6.8s; }
  /* Team roles float only AFTER they've assembled (keeps the build-on-scroll intact). */
  .team-diagram.in-view .role-chip { animation: tv-float 5.6s ease-in-out infinite; }
  .team-diagram.in-view .role-chip:nth-child(2n) { animation-delay: -1.4s; animation-duration: 6.4s; }
  .team-diagram.in-view .role-chip:nth-child(3n) { animation-delay: -2.8s; animation-duration: 6s; }
}

/* ═══ Price-drop — make the OLD price legible and show the reduction clearly.
   Everything is visible by default; the .priced class (added on scroll-in) only plays
   a one-shot emphasis, so a missed observer can never hide a price. ═══ */
.p-pricewrap { position: relative; }
.p-was { font-size: var(--fs-md); font-weight: 600; color: var(--text-2); margin-top: 14px; letter-spacing: -0.01em; }
.p-was s { color: #e5484d; text-decoration-thickness: 2px; text-decoration-color: #e5484d; }
.p-drop { display: inline-flex; align-items: center; margin: 1px 0; }
.p-drop svg { display: block; }
.p-save {
  display: inline-block; margin-top: 10px; font-size: var(--fs-sm); font-weight: 750;
  color: #04210f; background: var(--green-gradient); border-radius: var(--r-pill);
  padding: 3px 11px; box-shadow: 0 3px 12px var(--green-glow);
}
@keyframes price-arrow-bounce { 0%, 100% { transform: translateY(0); } 32% { transform: translateY(-7px); } 62% { transform: translateY(3px); } }
@keyframes price-pulse { 0% { transform: scale(1); } 42% { transform: scale(1.09); } 100% { transform: scale(1); } }
.plan.priced .p-drop  { animation: price-arrow-bounce 0.85s ease; }
.plan.priced .p-price { animation: price-pulse 0.7s ease 0.1s; transform-origin: left center; }
@media (prefers-reduced-motion: reduce) { .plan.priced .p-drop, .plan.priced .p-price { animation: none; } }

/* ═══ Minimal frost glass — a brighter top edge-highlight on the main panels
   (the recognizable glass cue) without heavy backdrop-blur, so it's premium AND
   smooth on any PC. ═══ */
.card, .compose-glass, .ws-panel, .job-card, .result-card, .package-card, .plan, .review, .quote, .stat-card {
  box-shadow: var(--shadow-2), inset 0 1px 0 var(--glass-highlight), inset 0 0 0 1px rgba(255,255,255,0.02);
}
:root[data-theme="light"] .card, :root[data-theme="light"] .plan, :root[data-theme="light"] .ws-panel,
:root[data-theme="light"] .compose-glass, :root[data-theme="light"] .stat-card, :root[data-theme="light"] .topbar {
  box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255,255,255,0.9);
}
/* The account/admin/help topbar hardcodes a dark bar for the dark theme; in light
   theme it must read as a clean frosted-light bar, not a dark slab. (Higher
   specificity than the pages' inline .topbar rule, so this wins.) */
:root[data-theme="light"] .topbar { background: rgba(252, 252, 255, 0.82); }
/* The base .input is a dark frosted navy for the dark theme. Left unstyled in
   light theme it shows through as a muddy gray box — give inputs a clean
   frosted-white fill so they match the light UI. */
:root[data-theme="light"] .input,
:root[data-theme="light"] input.input,
:root[data-theme="light"] textarea.input,
:root[data-theme="light"] select.input { background: rgba(255, 255, 255, 0.75); }
:root[data-theme="light"] .input:focus { background: #ffffff; }
/* Polished-glass panels get a whisper of top sheen — light diffusing through the
   frosted surface, clipped to the panel radius. No harsh outline. */
:root[data-theme="light"] .ws-panel { position: relative; }
:root[data-theme="light"] .ws-panel::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  border-radius: inherit;
  background: linear-gradient(180deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0.06) 22%, transparent 55%);
}
:root[data-theme="light"] .ws-panel > * { position: relative; z-index: 1; }

/* ── "Trapped in glass" — in light, content cards go a touch more translucent so the green
   bloom + grid refract through, with a crisp glass top-edge and a faint green rim. Blur kept
   at 22px (matches the base .card) so it stays smooth on a weak GPU. Text stays fully
   readable: 0.60 white + blur reads as frosted glass, not see-through. Pricing .plan is left
   to its own shine treatment (adding blur there would fight the badge + sweep). ── */
:root[data-theme="light"] .card,
:root[data-theme="light"] .job-card,
:root[data-theme="light"] .result-card,
:root[data-theme="light"] .package-card,
:root[data-theme="light"] .review,
:root[data-theme="light"] .quote,
:root[data-theme="light"] .stat-card,
:root[data-theme="light"] .compose-glass {
  background: rgba(253, 253, 255, 0.60);
  backdrop-filter: blur(22px) saturate(1.2);
  -webkit-backdrop-filter: blur(22px) saturate(1.2);
  border: 1px solid rgba(120, 200, 158, 0.26);
  box-shadow:
    var(--shadow-2),
    inset 0 1px 0 rgba(255, 255, 255, 0.96),
    inset 0 0 34px rgba(96, 210, 150, 0.05);
}

/* ═══ Subtle interaction feedback everywhere (both themes) — light, never flashy.
   Press = a soft dim (filter, so it never fights GSAP transforms on magnetic buttons);
   inputs get a warm focus glow. ═══ */
button, .btn, a.btn, .nav-item, .h-btn, .action-btn, .btn-copy, .tab {
  transition: filter var(--fast) var(--ease), transform var(--fast) var(--ease),
              background var(--fast) var(--ease), box-shadow var(--fast) var(--ease), color var(--fast) var(--ease);
}
/* Press = dim + a whisper of scale (Emil: buttons must feel like they heard you).
   `scale` is the independent property, NOT `transform` — it composes with GSAP/
   magnetic transforms instead of fighting them, which is this rule's old law. */
button:active:not(:disabled), .btn:active, .nav-item:active, .h-btn:active, .action-btn:active { filter: brightness(0.93); scale: .98; }
@media (prefers-reduced-motion: reduce) {
  button:active:not(:disabled), .btn:active, .nav-item:active, .h-btn:active, .action-btn:active { scale: none; }
}
/* Ticking numbers never jitter their neighbours (tabular digits) */
.hstat-num, .job-meta, .cu { font-variant-numeric: tabular-nums; }
input:not([type="checkbox"]):not([type="radio"]):focus, textarea:focus, select:focus {
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Theme toggle button (used in topbars / navs) */
.theme-toggle {
  width: 36px; height: 36px; flex-shrink: 0;
  display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-sm); color: var(--text-2); cursor: pointer;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.theme-toggle:hover { background: var(--surface-3); color: var(--text); border-color: var(--border-strong); }
/* Sun/moon morph: both icons stack in the single grid cell and cross-fade +
   rotate when the theme flips (a small spring makes it feel tactile), instead of
   snapping between display:none/block. */
.theme-toggle .sun, .theme-toggle .moon {
  grid-area: 1 / 1;
  transition: opacity var(--med) var(--ease), transform var(--med) var(--ease-spring);
}
.theme-toggle .sun  { opacity: 0; transform: rotate(-90deg) scale(0.5); }
.theme-toggle .moon { opacity: 1; transform: none; }
:root[data-theme="light"] .theme-toggle .sun  { opacity: 1; transform: none; }
:root[data-theme="light"] .theme-toggle .moon { opacity: 0; transform: rotate(90deg) scale(0.5); }
@media (prefers-reduced-motion: reduce) {
  .theme-toggle .sun, .theme-toggle .moon { transition: none; }
}

/* ═══ Shake (form validation feedback) ═══ */
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-8px); }
  30% { transform: translateX(7px); }
  45% { transform: translateX(-5px); }
  60% { transform: translateX(4px); }
  75% { transform: translateX(-2px); }
}
.shake { animation: shake 0.42s var(--ease-in-out); }

/* ═══ Reduced motion ═══ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   2026 POLISH PASS — bolder type, harder edges, more air.
   Everything here is deliberately LOW SPECIFICITY (element selectors and
   single classes) so any component that styled itself on purpose still
   wins. This lifts the parts nobody ever got to, which is most of them.
   ═══════════════════════════════════════════════════════════════════ */

/* ── 1. TYPOGRAPHY ────────────────────────────────────────────────
   Headings get real authority: heavier, tighter, and set on a shorter
   leading. Optical tracking gets tighter as the size grows — the thing
   that makes type look set rather than typed. */
/* ══ 26 Aug REDESIGN — the display face ══
   Headings move to Space Grotesk (her pick, after I measured clickmaxstudio.com:
   Space Grotesk 700, hero 59px, tracking -1.47px; vidrush.ai runs Manrope 800 at
   -1.76px). The pattern both share is BIG + BOLD + TIGHT, so the tracking below is
   deliberately negative and gets tighter as the size grows. Body copy stays on the
   existing sans — a display/body pair reads more designed than one family doing
   both jobs. */
h1, h2, h3, h4 {
  font-family: var(--font-display, var(--font));
  font-weight: 700;
  line-height: 1.12;
  letter-spacing: -0.03em;
  text-wrap: balance;             /* no orphaned last word in a headline */
}
h1 { letter-spacing: -0.042em; line-height: 1.02; }
h2 { letter-spacing: -0.036em; line-height: 1.06; }
/* Numbers line up in columns wherever they're compared. */
.stat-card .value, .p-price, .credits-num, .cost-estimate strong,
.gm-t, .an-num, .scm-num { font-variant-numeric: tabular-nums; }
/* Long-form copy gets a comfortable measure — nothing reads well at 120 chars. */
p, .field-hint, .ws-sub-desc, .v-d, .hc-sub { max-width: 68ch; }

/* ── 2. EDGES ─────────────────────────────────────────────────────
   The old borders sat at 6% white — below the threshold where an edge
   reads as intentional, so panels looked like they were dissolving.
   These are cut, not floated. */
:root {
  --border: rgba(255, 255, 255, 0.10);
  --border-strong: rgba(255, 255, 255, 0.17);
  --bg: #04040b;                  /* nearer true black; the amber sits harder on it */
}
:root[data-theme="light"] {
  --border: rgba(70, 66, 96, 0.16);
  --border-strong: rgba(70, 66, 96, 0.26);
}
/* A hairline that survives a HiDPI screen without turning muddy. */
.card, .ws-panel, .job-card, .result-card, .package-card, .plan, .stat-card, .hub-action {
  border-width: 1px;
}

/* ── 3. AIR ───────────────────────────────────────────────────────
   Bold by restraint: the panels breathe instead of shouting. Applied to
   the containers only, so nothing inside has to be re-measured. */
.card > .card-body { padding: var(--sp-6) var(--sp-6) var(--sp-6); }
.card > .card-head { padding: var(--sp-5) var(--sp-6); }
@media (max-width: 720px) {
  .card > .card-body { padding: var(--sp-4); }
  .card > .card-head { padding: var(--sp-4); }
}

/* ── 4. ICONS ─────────────────────────────────────────────────────
   One drawn set replaced every emoji. Sized in em so an icon always
   matches the text beside it, and inheriting colour so it picks up the
   accent, the muted tone or the dark-on-amber automatically. */
.tv-ic { width: 1em; height: 1em; display: inline-block; vertical-align: -0.125em; flex: 0 0 auto; }
/* Slots that used to hold a big emoji get a proportionate icon. */
.hc-ico .tv-ic, .he-art .tv-ic { width: 1.15em; height: 1.15em; vertical-align: -0.16em; }
.hc-ico, .he-art { color: var(--accent); }
.ha-ic .tv-ic, .v-ico .tv-ic { width: 1.05em; height: 1.05em; }
.pc-emo .tv-ic, .rc-ico .tv-ic { width: 1.05em; height: 1.05em; vertical-align: -0.16em; opacity: .9; }
/* An icon inside a button sits on the text baseline, not above it. */
button .tv-ic, .btn .tv-ic { vertical-align: -0.14em; }

/* ── 5. THE MUTED TONE WAS UNREADABLE ─────────────────────────────
   Measured, not guessed: --text-3 sat at 2.66:1 on dark and 3.48:1 on
   light. AA wants 4.5. It's the colour on the step labels (Theme /
   Background / Voice / Review), field hints and every caption — i.e.
   most of the words that explain the product, on a page built for a
   broad audience. A prior lift landed just UNDER (4.40:1 dark / 4.25:1 light on the
   worst-case surface); this clears WCAG AA — measured ≥4.6:1 dark / ≥4.9:1 light
   on the lightest surface the faint grey sits on — while staying clearly tertiary
   against --text-2. (The base #4e5270 on line 41 is shadowed by this; an audit
   that measures that stale value will wrongly read ~2.6:1.) */
:root { --text-3: #8388a4; }
:root[data-theme="light"] { --text-3: #64646f; }

/* ── 6. AMBER AS TEXT, ON A LIGHT BACKGROUND ──────────────────────
   #f5a623 on white measures 1.98:1 — the brand colour is gorgeous as a
   FILL and unreadable as TEXT. So light mode gets a deeper amber for
   words only; every fill, border and glow keeps the bright one, so the
   brand doesn't change — it just stops being illegible. */
:root { --accent-text: var(--accent); }
/* Owner's call: the brand amber is the brand amber in BOTH themes. This was
   #8a5200 — a darkened amber that passes contrast on a pale ground but reads
   BROWN, which is not the product's colour. Keeping --accent here means the
   MARKET EVIDENCE kicker, the niche labels, the takedown link, the verify link
   and the 57M+/340K+/9 figures are all amber in light mode too. */
:root[data-theme="light"] { --accent-text: var(--accent); }
:root[data-theme="light"] .ct-brand em,
:root[data-theme="light"] .ch-new-btn,
:root[data-theme="light"] .cs-opt.sel,
:root[data-theme="light"] .rp-chip.rp-voice,
:root[data-theme="light"] .step.active .step-detail,
:root[data-theme="light"] .script-tips,
:root[data-theme="light"] .nav-item.active,
:root[data-theme="light"] .step .num,
:root[data-theme="light"] .faq-item[open] summary { color: var(--accent-text); }

/* Working-page labels need readable ink on pale surfaces. Keep the brand
   amber on their existing borders, fills and icons, not on these words. */
:root[data-theme="light"] body .nav-item.active,
:root[data-theme="light"] body .sidebar-credits:not(.zero),
:root[data-theme="light"] body #creditsDisplay,
:root[data-theme="light"] body .hstat-num,
:root[data-theme="light"] body .h-btn.primary,
:root[data-theme="light"] body .h-chip.working,
:root[data-theme="light"] body .chan-chip.active .cc-name,
:root[data-theme="light"] body .chan-chip .cc-badge,
:root[data-theme="light"] body .cp-move:not(.tone-warn):not(.tone-win) .cp-move-k,
:root[data-theme="light"] body .hub-action.ha-go .ha-t,
:root[data-theme="light"] body .hub-action .ha-for { color: var(--text); }

/* ── 6b. GREEN & SEMANTIC HUES AS TEXT, ON LIGHT ──────────────────
   The same failure one hue over: --success (#34d399), --green (#2fd07e) and
   --danger (#f87171) are tuned as FILLS — pill dots, check-marks, the meter
   accent — and land at ~1.6–2.6:1 as TEXT on the pale tint they sit on in
   light mode (mint-on-mint, red-on-rose). Light mode gets a deeper ink for the
   WORDS only; every fill, border, dot and glow keeps the bright token, so
   buttons and status dots don't change. The -text tokens default to the base
   colour, so the DARK theme is visually identical. */
:root { --success-text: var(--success); --danger-text: var(--danger); --green-text: var(--green); }
:root[data-theme="light"] { --success-text: #0a6b40; --danger-text: #b42318; --green-text: #096a38; }
/* mint / success as words */
:root[data-theme="light"] .badge-success,
:root[data-theme="light"] .msg-box.success,
:root[data-theme="light"] .requested-chip,
:root[data-theme="light"] .status-pill.is-complete,
:root[data-theme="light"] .alert-success,
:root[data-theme="light"] .adjust-note,
:root[data-theme="light"] .hub-note,
:root[data-theme="light"] .doctor-score.ready,
:root[data-theme="light"] .doctor-tag.good,
:root[data-theme="light"] .btn-copy.copied,
:root[data-theme="light"] .h-chip.complete,
:root[data-theme="light"] .cookie-zone.uploaded,
:root[data-theme="light"] .cmp-yes,
:root[data-theme="light"] .prob-fix svg,
:root[data-theme="light"] .guarantee svg,
:root[data-theme="light"] .check-burst svg { color: var(--success-text); }
/* red / danger as words */
:root[data-theme="light"] .badge-danger,
:root[data-theme="light"] .msg-box.error,
:root[data-theme="light"] .status-pill.is-failed,
:root[data-theme="light"] .alert-danger,
:root[data-theme="light"] .field-error,
:root[data-theme="light"] .review-missing .rm-head,
:root[data-theme="light"] .chm-err,
:root[data-theme="light"] .sidebar-credits.zero,
:root[data-theme="light"] .cost-estimate.insufficient .ce-balance,
:root[data-theme="light"] .step.error .step-title,
:root[data-theme="light"] .h-chip.failed,
:root[data-theme="light"] .hub-note.err { color: var(--danger-text); }
/* green as words */
:root[data-theme="light"] .included-band .inc-item .inc-ck,
:root[data-theme="light"] .an-right .an-h,
:root[data-theme="light"] .an-right li::before,
:root[data-theme="light"] .se-music-chip,
:root[data-theme="light"] .se-kind.footage,
:root[data-theme="light"] .se-note,
:root[data-theme="light"] .cr-lh .cr-badge.free,
:root[data-theme="light"] .vs-col.tv .vs-list li svg { color: var(--green-text); }

/* ── 7. `hidden` MUST ACTUALLY HIDE ───────────────────────────────
   The HTML `hidden` attribute only carries `display:none` from the UA
   stylesheet, which has effectively zero specificity — so ANY author rule
   that sets `display` silently defeats it. That's why the storyboard strip,
   the stage rail and the "Watch the opening" button were on screen while
   the video was still on its first stage: the elements were `hidden` in
   the DOM and visible on the page. One rule fixes every case, now and
   for anything added later. */
[hidden] { display: none !important; }

/* ═══════════════════════════════════════════════════════════════════
   8. MOTION — smoothness is about WHAT you animate, not how long.
   Measured before touching anything: 78 infinite animations running
   continuously, 91 backdrop-filter layers, and 9 progress bars animating
   `width` (which forces layout on every update). On a modest machine that
   is the whole problem — durations were never the issue.
   ═══════════════════════════════════════════════════════════════════ */

/* ── 8a. Don't render what isn't on screen ──
   The single biggest win available: the browser skips layout, paint AND
   animation for sections scrolled out of view. contain-intrinsic-size keeps
   the scrollbar honest so nothing jumps as you scroll. */
.card, .ws-panel, .hub-action, .plan, .stat-card, .hist-card, .value-card {
  content-visibility: auto;
  contain-intrinsic-size: auto 320px;
}
/* Repeated rows: a hover repaint stays inside the row instead of
   invalidating everything around it. */
.hist-card, .voice-opt, .theme-card, .bg-card, .rp-sb, .se-clip, .preset-chip {
  contain: layout paint style;
}

/* ── 8b. Glass costs real frames ──
   A 20px backdrop blur is re-computed for every pixel under every panel on
   every frame that anything moves. 14px is visually near-identical and much
   cheaper — and while a video is producing, the blur comes off entirely. */
:root { --glass-blur: 14px; }
body.perf-lite .card,
body.perf-lite .ws-panel,
body.perf-lite .job-card,
body.perf-lite .create-topbar,
body.perf-lite .app-topbar,
body.perf-lite .create-sticky {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* While she's producing, every decorative loop stops. Nothing that carries
   meaning (progress, spinners) is touched — those are listed by name below. */
body.perf-lite [class*="-eq"] i,
body.perf-lite [class*="-wave"] i,
body.perf-lite [class*="-bars"] i,
body.perf-lite .tm-bar,
body.perf-lite .rf-bars i,
body.perf-lite .hd-wave i { animation: none !important; }
/* EXEMPT the studio-banner bars: they are the "your studio is at work" liveness
   signal, and perf-lite froze them mid-pose — which reads as a CRASH on the one
   screen whose job is to say "still working" (she reported it as stuck). The
   animation is a pure scaleY transform on five 5px bars: compositor-only,
   effectively free next to the blur/particle work perf-lite exists to stop. */
body.perf-lite .sb-reels span { animation: sbBar 1s ease-in-out infinite !important; }

/* ── 8c. One curve ──
   Interface motion should feel like one hand made it. This is the curve:
   fast to leave, gentle to arrive — the shape that reads as "physical"
   rather than "linear tween". */
:root {
  --ease: cubic-bezier(0.22, 0.61, 0.24, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --fast: 140ms;
  --med: 220ms;
  --slow: 340ms;
}

/* ── 8d. Hover and press: transform + opacity only ──
   Those are the two properties the compositor can animate without asking
   the main thread to do anything. A shadow transition repaints a large
   blurred region every frame; it is the reason card hovers feel gritty. */
.card, .hub-action, .hist-card, .plan, .value-card, .theme-card, .bg-card, .voice-opt {
  transition: transform var(--med) var(--ease), border-color var(--fast) linear, opacity var(--fast) linear;
}
/* Promote only while the pointer is actually on the element — a permanent
   will-change on every card would waste more memory than it saves. */
.card:hover, .hub-action:hover, .hist-card:hover, .plan:hover,
.value-card:hover, .theme-card:hover, .bg-card:hover, .voice-opt:hover { will-change: transform; }
button:active, .btn:active, .hub-action:active { transition-duration: 60ms; }

/* ── 8e. Progress bars ride on the compositor ──
   `width` animation = layout on every update; the video position bar did that
   four times a second. Each bar's own rule now declares scaleX (edited at
   source, not overridden from here — tv.css loads BEFORE the pages' inline
   styles, so an override would have lost). This only adds the GPU hint. */
.credits-fill, .script-meter .sm-bar span, .director-work .dw-bar span,
.se-upbar-fill, .ct-line #ctFill, .rp-bar > i, .usage-fill { will-change: transform; }

/* ── 8f. Reduced motion means STILL, not broken ──
   Everything holds its final state; nothing disappears. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .credits-fill, .script-meter .sm-bar span, .director-work .dw-bar span,
  .se-upbar-fill, .ct-line #ctFill, .rp-bar > i, .usage-fill { transition: none !important; }
}


html { background: var(--bg); }

/* ═══════════════════════════════════════════════════════════════════════════
   LIQUID GLASS KIT (27 Jul, her image-3 reference) — appended LAST so the
   cascade wins over per-component styling everywhere.
   Her picks: glass on KEY surfaces (dense reading areas stay calm solids),
   full pills + ~20px cards, amber locked.
   !important is DELIBERATE on the shape/sheen lines: every page carries its own
   higher-specificity component rules (#createView .x) that would silently win
   otherwise — this file IS the design-system layer, so it must. Future radius
   tweaks go here, not per-component.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── Shape language: buttons and inputs become pills, cards go 20px ── */
:root { --r-card: 20px; }
.btn, .btn-generate, .btn-ai, .btn-primary-sm, .btn-ghost, .cs-btn, .h-btn,
.action-btn, .ct-exit, .hero-director, .hero-ch-add, .hca-more, .idea-pick,
.gs-next, .gs-back, .ch-new-btn, .hu-btn, .opt-help {
  border-radius: var(--r-pill) !important;
}
input[type="text"], input[type="email"], input[type="password"],
input[type="search"], input[type="url"], select {
  border-radius: var(--r-pill) !important;
}
/* Multiline reading stays rectangular — a pill textarea clips its own text. */
textarea { border-radius: var(--r-lg) !important; }

/* ── Frosted panels: the big surfaces float as glass over the backdrop.
   backdrop-filter is GPU-heavy, so it goes ONLY on top-level panels — small
   cards get the translucency + sheen without the blur. ── */
/* NOT .card (bare): index carries 11 unaudited class="card" components —
   account.html's cards wait for their own pass rather than a blind match. */
.ws-card, .compose-glass, .hero-card, .hero-side, .modal, .home-card,
.auth-card, .value-card, .sc-card, .ex-card {
  border-radius: var(--r-card) !important;
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.12);
  backdrop-filter: blur(var(--glass-blur)) saturate(1.12);
  box-shadow: var(--shadow-2, 0 16px 38px rgba(0, 0, 0, 0.28)),
              inset 0 1px 0 var(--glass-highlight);
}
/* Small interactive cards: glassy skin, no blur cost. */
.hi-card, .hero-wcard, .start-card, .theme-card, .vc2-card, .idea-card {
  border-radius: 16px !important;

  box-shadow: inset 0 1px 0 var(--glass-highlight);
}
/* Dense reading areas stay calm and solid — her explicit pick. */
textarea, .ref-ta, .hpb-read, .st-row, table, .an-summary {
  -webkit-backdrop-filter: none; backdrop-filter: none;
}

/* ── The candy: primary actions read as glossy glass pills (the kit's
   hero buttons) — a top sheen, an inner base shade, the amber glow. ── */
.btn-generate, .btn-ai {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.38),
              inset 0 -8px 16px rgba(0, 0, 0, 0.14),
              0 6px 18px var(--accent-glow) !important;
}
@media (hover: hover) {
  .btn-generate:hover:not(:disabled), .btn-ai:hover:not(:disabled) {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45),
                inset 0 -8px 16px rgba(0, 0, 0, 0.12),
                0 10px 30px var(--accent-glow) !important;
  }
}

/* ── GLASS LEGIBILITY (her verdict on v1: "where is the change?").
   Dark: near-black blurred over near-black is invisible — panels now wear a
   real smoked-glass skin: a white sheen falling off from the top, a visible
   light edge. Light: the milky frosted skin over the titanium glows. ── */
.ws-card, .compose-glass, .hero-card, .hero-side, .modal, .home-card,
.auth-card, .value-card, .sc-card, .ex-card {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%,
                            rgba(255, 255, 255, 0.02) 38%,
                            rgba(255, 255, 255, 0) 100%),
    var(--surface-2) !important;
  border: 1px solid rgba(255, 255, 255, 0.15) !important;
}
:root[data-theme="light"] .ws-card, :root[data-theme="light"] .compose-glass,
:root[data-theme="light"] .hero-card, :root[data-theme="light"] .hero-side,
:root[data-theme="light"] .modal, :root[data-theme="light"] .home-card,
:root[data-theme="light"] .auth-card, :root[data-theme="light"] .value-card,
:root[data-theme="light"] .sc-card, :root[data-theme="light"] .ex-card {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.94) 0%,
                            rgba(255, 255, 255, 0.60) 42%,
                            rgba(255, 255, 255, 0.42) 100%) !important;
  border: 1px solid rgba(255, 255, 255, 0.9) !important;
  box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255, 255, 255, 0.95) !important;
}


/* ═══════════════════════════════════════════════════════════════════════════
   27 Jul — her two theme asks, appended last so they win.
   (1) LIGHT: "a bit more green, WOW but not distracting."
   (2) DARK:  "every UI the same colour, a bit more transparent and glassy
       glossy just like the light mode" + calmer surfaces, stronger contrast.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── LIGHT ──────────────────────────────────────────────────────────────────
   The green was already declared but only the LAST background layer carried
   `fixed`, so every bloom scrolled away and the page went pale below the fold.
   All layers are fixed now, the base is a touch greener, and --bg (which <html>
   paints) is green-tinted so any gap matches instead of flashing lavender. */
:root[data-theme="light"] { --bg: #e9f5ee; }
:root[data-theme="light"] body {
  background:
    radial-gradient(1500px 820px at 50% 3%,  rgba(40, 214, 132, 0.42), transparent 56%),
    radial-gradient(1150px 760px at 50% 44%, rgba(66, 220, 152, 0.20), transparent 62%),
    radial-gradient(940px 900px  at 3% 18%,  rgba(92, 226, 168, 0.30), transparent 60%),
    radial-gradient(980px 900px  at 98% 82%, rgba(74, 218, 158, 0.30), transparent 60%),
    radial-gradient(720px 640px  at 26% 90%, rgba(186, 172, 250, 0.11), transparent 64%),
    linear-gradient(165deg, #e3f5ea 0%, #d8f0e2 52%, #cfebda 100%);
  background-attachment: fixed, fixed, fixed, fixed, fixed, fixed;
}

/* ── DARK ───────────────────────────────────────────────────────────────────
   One consistent surface family (the three tiers were drifting apart in tint),
   more translucency, and the same glossy top sheen the light theme gets. */
:root:not([data-theme="light"]) {
  /* 28 Jul — WHOLE-SITE BLACK: surfaces move off navy onto the near-black card
     family, so the shell, buttons, inputs, chips, popovers and every panel match
     the black+neon cards. Tiers stay ascending so an input reads a hair above the
     card it sits on. Light theme untouched (this block is dark-only). */
  /* ★ WAVE 22 (4 Sep 2026): the same black as the floor. These six were the
     28-Jul navy-glass set and wave 18 left them behind, so a page built from
     #050507 carried panels at blue−red +8 to +9 — two blacks, one page. Each
     value keeps its lightness and drops its cast onto the family wave 18
     established (red = green, blue two to four points above them). */
  --surface:         rgba(17, 17, 21, 0.55);
  --surface-2:       rgba(23, 23, 28, 0.52);
  --surface-3:       rgba(30, 30, 36, 0.50);
  --surface-solid:   #0c0c0e;
  --surface-2-solid: #101013;
  --surface-3-solid: #17171b;
  --glass-highlight: rgba(255, 255, 255, 0.10);
  /* Calmer + higher contrast: brighter body text, a lighter secondary, and a
     third tier that is readable rather than a whisper. */
  --text:   #f2f4fa;
  --text-2: #a7adc6;
  --text-3: #6f7594;
  --border:        rgba(255, 255, 255, 0.09);
  --border-strong: rgba(255, 255, 255, 0.15);
}
/* The glossy skin, matched to light's: a bright edge and a sheen that falls off
   from the top. Applied to the same surface list the glass kit governs, so the
   whole app changes together rather than screen by screen. */
:root:not([data-theme="light"]) .ws-card,
:root:not([data-theme="light"]) .compose-glass,
:root:not([data-theme="light"]) .hero-card,
:root:not([data-theme="light"]) .hero-side,
:root:not([data-theme="light"]) .modal,
:root:not([data-theme="light"]) .home-card,
:root:not([data-theme="light"]) .auth-card,
:root:not([data-theme="light"]) .value-card,
:root:not([data-theme="light"]) .sc-card,
:root:not([data-theme="light"]) .ex-card,
:root:not([data-theme="light"]) .plan,
:root:not([data-theme="light"]) .prob-card,
:root:not([data-theme="light"]) .faq-item,
:root:not([data-theme="light"]) .how-screen {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.11) 0%,
                            rgba(255, 255, 255, 0.04) 44%,
                            rgba(255, 255, 255, 0.015) 100%),
    linear-gradient(180deg, rgba(34, 38, 58, .9), rgba(26, 29, 46, .92)) !important;
  border: 1px solid rgba(255, 255, 255, 0.17) !important;
  box-shadow: 0 22px 52px rgba(0, 0, 0, 0.58),
              inset 0 1px 0 rgba(255, 255, 255, 0.14) !important;
}
/* Calmer canvas: the nebula sits further back so panels read cleanly on it. */
:root:not([data-theme="light"]) body::before { opacity: 0.72; }

/* ═══════════════════════════════════════════════════════════════════════════
   27 Jul — "same green everywhere, landing as it was but a bit more WOW,
   still not distracting", plus scroll parallax and a hero that answers typing.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── ONE GREEN, EVERYWHERE ──────────────────────────────────────────────────
   The gradient was already global, but the app shell painted opaque surfaces
   over it, which is why the dashboard looked white while pricing looked green.
   These panes go translucent so the same backdrop reads through every page. */
:root[data-theme="light"] .app-shell,
:root[data-theme="light"] .sidebar,
:root[data-theme="light"] .main,
:root[data-theme="light"] .topbar,
:root[data-theme="light"] .view,
:root[data-theme="light"] .workspace { background: transparent; }
:root[data-theme="light"] .sidebar {
  background: linear-gradient(180deg, rgba(255,255,255,0.62), rgba(255,255,255,0.40));
  -webkit-backdrop-filter: blur(18px) saturate(1.1);
  backdrop-filter: blur(18px) saturate(1.1);
  border-right: 1px solid rgba(255,255,255,0.7);
}
:root[data-theme="light"] .topbar {
  background: rgba(255,255,255,0.52);
  -webkit-backdrop-filter: blur(16px) saturate(1.1);
  backdrop-filter: blur(16px) saturate(1.1);
}

/* ── PARALLAX DEPTH, WITHOUT JAVASCRIPT ────────────────────────────────────
   First attempt drove this from a scroll listener writing --bgy. It could not
   be verified (this preview pane fires neither rAF nor scroll events) and it
   would silently go stale wherever rAF is throttled. Mixed background-
   attachment gives the same depth with no script at all: FIXED layers are
   pinned to the viewport, SCROLL layers travel with the document, and the
   difference between them IS the parallax.
   The base and the two big blooms stay FIXED so the page never goes pale;
   only the smaller accent blooms travel. ── */
:root[data-theme="light"] body {
  background-attachment: fixed, fixed, scroll, scroll, scroll, fixed;
}
/* ── THE HERO ANSWERS TYPING ────────────────────────────────────────────────
   .is-typing is toggled on <body> while she types in the landing prompt. The
   bloom brightens and swells a little, then settles — the page acknowledging
   her, never a flash. */
body.is-typing::before {
  animation: none;
  transform: scale(1.05);
  opacity: 1;
  transition: transform 1.1s cubic-bezier(.2,.7,.2,1), opacity .6s ease;
}
body::before { transition: transform 1.6s cubic-bezier(.2,.7,.2,1), opacity 1.2s ease; }
:root[data-theme="light"] body.is-typing {
  background-color: rgba(40, 214, 132, 0.05);
  transition: background-color .5s ease;
}

@media (prefers-reduced-motion: reduce) {
  :root[data-theme="light"] body { background-attachment: fixed, fixed, fixed, fixed, fixed, fixed; }
  body.is-typing::before { transform: none; }
}

/* ── THE RECOMMENDED PLAN KEEPS ITS AMBER EDGE ──────────────────────────────
   She asked for the filled card to go back to how it looked before. It did in
   light theme — but measured against its SIBLINGS the card was identical to
   them in dark, because the glass kit above sets `border: ... !important` on
   every .plan, and the light theme's own .plan box-shadow rule dropped the
   amber glow. Both are beaten here, once, using theme-aware tokens so the one
   rule is correct in both themes. `!important` alone was NOT enough — the
   glass kit's selector carries more specificity than a bare .plan.featured,
   so the theme-scoped selectors below are what actually outrank it. */
.plan.featured,
:root[data-theme="light"] .plan.featured,
:root:not([data-theme="light"]) .plan.featured {
  border-color: var(--accent-border) !important;
  box-shadow: var(--shadow-2), 0 0 50px var(--accent-glow) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   27 Jul (2) — her screenshot round: "I do not see any green" in the app,
   "a bit too much green" on the landing page, "I want the green to be moving",
   the dashboard tiles were two different colours, and the FAQ rows read as
   grey slabs. Appended last so these win.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── GREEN, AND MOVING ──────────────────────────────────────────────────────
   The motion was already built (the .ambient orbs drift on float1/float2 for
   25-35s, transform-only so it is cheap) — the app was just hiding it. With
   that dimming gone the orbs carry the movement; here they get more travel and
   a longer, offset loop so the backdrop never visibly repeats. */
:root[data-theme="light"] .ambient-orb { opacity: 0.50; }
:root[data-theme="light"] .ambient-1 { background: rgba(58, 206, 140, 0.24); animation-duration: 34s; }
:root[data-theme="light"] .ambient-2 { background: rgba(92, 214, 158, 0.22); animation-duration: 43s; }
:root[data-theme="light"] .ambient-3 { background: rgba(120, 224, 172, 0.16); animation-duration: 52s; }

/* A second, much slower layer of movement: the whole bloom breathes. Transform
   on a fixed pseudo-element — no repaint of the page behind it. */
@keyframes tvGreenDrift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(-1.4%, 1.1%, 0) scale(1.06); }
  100% { transform: translate3d(1.2%, -0.9%, 0) scale(1.02); }
}
:root[data-theme="light"] body::before {
  animation: tvGreenDrift 78s ease-in-out infinite alternate;
  will-change: transform;
}

/* ── LANDING: THE SAME GREEN, TURNED DOWN ───────────────────────────────────
   "a bit too much green" on the front page — the marketing page is mostly
   backdrop, so the identical wash that reads as a hint inside the app reads as
   a flood out there. Same hues, lower saturation and alpha. */
:root[data-theme="light"] body.landing {
  background:
    radial-gradient(1500px 820px at 50% 3%,  rgba(40, 214, 132, 0.20), transparent 56%),
    radial-gradient(1150px 760px at 50% 44%, rgba(66, 220, 152, 0.10), transparent 62%),
    radial-gradient(940px 900px  at 3% 18%,  rgba(92, 226, 168, 0.14), transparent 60%),
    radial-gradient(980px 900px  at 98% 82%, rgba(74, 218, 158, 0.14), transparent 60%),
    radial-gradient(720px 640px  at 26% 90%, rgba(186, 172, 250, 0.08), transparent 64%),
    linear-gradient(165deg, #eef8f2 0%, #e8f5ee 52%, #e3f2ea 100%);
}
:root[data-theme="light"] body.landing::before { opacity: 0.55; }
:root[data-theme="light"] body.landing .ambient-orb { opacity: 0.32; }

/* ── ONE SURFACE FOR EVERY DASHBOARD TILE ───────────────────────────────────
   Her 4th image: the stat tiles and the action cards were visibly different
   colours. .home-card is in the glass kit above and .hstat was not, so they
   drifted apart. Same skin, both themes. */
:root:not([data-theme="light"]) .hstat {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.11) 0%,
                            rgba(255, 255, 255, 0.04) 44%,
                            rgba(255, 255, 255, 0.015) 100%),
    var(--surface-2) !important;
  border: 1px solid rgba(255, 255, 255, 0.16) !important;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.34),
              inset 0 1px 0 rgba(255, 255, 255, 0.14) !important;
}
:root[data-theme="light"] .hstat {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.94) 0%,
                            rgba(255, 255, 255, 0.60) 42%,
                            rgba(255, 255, 255, 0.42) 100%) !important;
  border: 1px solid rgba(255, 255, 255, 0.9) !important;
  box-shadow: var(--shadow-2), inset 0 1px 0 rgba(255, 255, 255, 0.95) !important;
}

/* ── THE FAQ ROWS ARE CARDS, NOT SLABS ──────────────────────────────────────
   The glass kit gives .faq-item a card surface, but the pages style it as a
   bottom-bordered list row — so each question rendered as a flush grey slab
   with a doubled divider. They are deliberate cards now: rounded, separated,
   and given a light-theme skin the kit never defined. */
.faq-item {
  border-radius: 16px;
  margin-bottom: 10px;
  overflow: hidden;
  border-bottom: 0;
  transition: transform .16s ease, border-color .16s ease;
}
.faq-item:last-child { margin-bottom: 0; }
.faq-item:hover { transform: translateY(-2px); }
.faq-item[open] { border-color: var(--accent-border) !important; }
:root[data-theme="light"] .faq-item {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.92) 0%,
                            rgba(255, 255, 255, 0.62) 100%) !important;
  border: 1px solid rgba(255, 255, 255, 0.9) !important;
  box-shadow: var(--shadow-1, 0 2px 6px rgba(28, 40, 75, 0.06)) !important;
}
.faq-item summary { padding-left: 20px !important; padding-right: 20px !important; }
.faq-item .faq-a { padding-left: 20px !important; padding-right: 20px !important; }

@media (prefers-reduced-motion: reduce) {
  :root[data-theme="light"] body::before { animation: none; }
  .faq-item:hover { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   28 Jul — her screenshot round 2: unreadable dropdowns, the black bar on the
   light Create flow, and "the dark UI colour does not look good".
   ═════════════════════════════════════════════════════════════════════════ */

/* ── SELECT POPUPS MUST BE OPAQUE ───────────────────────────────────────────
   The browser paints a native <select> menu using the ELEMENT's own background.
   Several selects were translucent (0.64-0.78), so the page showed through the
   open menu and the options sat on top of whatever was behind them. */
select {
  background-color: var(--surface-2-solid, #f6f6fb) !important;
  background-image: none !important;
}
:root:not([data-theme="light"]) select {
  background-color: #171a2e !important;
  color: var(--text);
}
select option { background-color: #ffffff; color: #1c1d28; }
:root:not([data-theme="light"]) select option { background-color: #171a2e; color: #e8eaf6; }
/* The colour-scheme drives the UA's own menu chrome (scrollbar, highlight). */
:root[data-theme="light"] select { color-scheme: light; }
:root:not([data-theme="light"]) select { color-scheme: dark; }

/* ── DARK SURFACES, BETTER ──────────────────────────────────────────────────
   Her 3rd image: flat slate panels with a hard 0.16 outline, all sitting at the
   same depth. Deeper and slightly cooler base, a softer top sheen, a quieter
   edge, and real elevation — so cards read as glass lying ON the page instead
   of rectangles drawn onto it. */
:root:not([data-theme="light"]) .home-card,
:root:not([data-theme="light"]) .hstat,
:root:not([data-theme="light"]) .ws-card,
:root:not([data-theme="light"]) .value-card,
:root:not([data-theme="light"]) .sc-card,
:root:not([data-theme="light"]) .ex-card,
:root:not([data-theme="light"]) .plan,
:root:not([data-theme="light"]) .prob-card,
:root:not([data-theme="light"]) .faq-item,
:root:not([data-theme="light"]) .how-screen {
  background:
    linear-gradient(158deg, rgba(255, 255, 255, 0.11) 0%,
                            rgba(255, 255, 255, 0.04) 44%,
                            rgba(255, 255, 255, 0.015) 100%),
    linear-gradient(180deg, rgba(34, 38, 58, .9), rgba(26, 29, 46, .92)) !important;
  border: 1px solid rgba(255, 255, 255, 0.17) !important;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.14) inset,
    0 22px 52px rgba(0, 0, 0, 0.58),
    0 2px 8px rgba(0, 0, 0, 0.3) !important;
}
/* Interactive ones answer the pointer — the amber the brand already uses. */
:root:not([data-theme="light"]) .home-card { transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease; }
:root:not([data-theme="light"]) .home-card:hover {
  transform: translateY(-3px);
  border-color: rgba(245, 166, 35, 0.42) !important;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.09) inset,
    0 24px 54px rgba(0, 0, 0, 0.52),
    0 0 34px rgba(245, 166, 35, 0.13) !important;
}
/* The primary tile keeps its amber identity, now on the same deeper base. */
:root:not([data-theme="light"]) .home-card.hc-primary {
  background:
    linear-gradient(158deg, rgba(245, 166, 35, 0.22) 0%, rgba(245, 166, 35, 0.06) 55%, rgba(255,255,255,0.02) 100%),
    linear-gradient(180deg, rgba(40, 41, 58, .92), rgba(31, 32, 47, .93)) !important;
  border-color: rgba(245, 166, 35, 0.34) !important;
}
@media (prefers-reduced-motion: reduce) {
  :root:not([data-theme="light"]) .home-card:hover { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   28 Jul — THE ONE BOX. Her call after the Ultravid reference: the opening
   screen had 14 buttons across 6 labelled zones stacked down a tall page. Now
   it is one box you type in, with the settings as pills along its top edge and
   a single Start. Amber stays (she locked it) — only the STRUCTURE is borrowed.
   ═════════════════════════════════════════════════════════════════════════ */

.ob-wrap { max-width: 880px; margin: 0 auto; }

/* The box. The light comes from INSIDE it — that is what pulls the eye to the
   one thing you are meant to do. */
.ob {
  position: relative; border-radius: 22px; padding: 14px 16px 14px;
  background:
    radial-gradient(120% 140% at 50% 120%, var(--accent-soft), transparent 62%),
    var(--surface-2);
  border: 1px solid var(--border-strong);
  box-shadow: 0 26px 70px rgba(0,0,0,.30), inset 0 1px 0 rgba(255,255,255,.07);
  transition: border-color .25s ease, box-shadow .35s ease, transform .35s ease;
}
.ob:focus-within {
  border-color: var(--accent-border);
  box-shadow: 0 30px 82px rgba(0,0,0,.36), 0 0 0 4px var(--accent-glow),
              inset 0 1px 0 rgba(255,255,255,.09);
}
/* It brightens while she types — the hook already exists on <body>. */
body.is-typing .ob { box-shadow: 0 32px 88px rgba(0,0,0,.38), 0 0 46px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,.10); }

/* ── the settings, as pills inside the top edge ── */
.ob-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.ob-slot { position: relative; }
.ob-pill {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 13px; border-radius: var(--r-pill);
  background: var(--surface-3); border: 1px solid var(--border);
  color: var(--text-2); font: inherit; font-size: 13.5px; font-weight: 650;
  cursor: pointer; transition: border-color .15s ease, color .15s ease, background .15s ease;
}
.ob-pill:hover { border-color: var(--accent-border); color: var(--text); }
.ob-pill[aria-expanded="true"] { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.ob-caret { width: 7px; height: 7px; border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor; transform: rotate(45deg) translate(-1px,-1px); opacity: .7; }

/* Popovers hold the EXISTING controls — nothing about how they work moved. */
.ob-pop {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 40;
  min-width: 240px; padding: 14px; border-radius: 16px;
  background: var(--surface-2-solid); border: 1px solid var(--border-strong);
  box-shadow: 0 22px 60px rgba(0,0,0,.42);
}
.ob-pop-wide { min-width: 460px; }
@media (max-width: 620px) { .ob-pop, .ob-pop-wide { min-width: min(84vw, 420px); } }

.ob textarea {
  width: 100%; min-height: 132px; resize: vertical;
  background: transparent !important; border: 0 !important; box-shadow: none !important;
  color: var(--text); font: inherit; font-size: 17px; line-height: 1.6;
  padding: 10px 6px 6px; outline: none;
}
.ob textarea::placeholder { color: var(--text-3); }

.ob-bottom { display: flex; align-items: center; gap: 10px; padding-top: 6px; }
.ob-read { font-size: 13px; color: var(--text-3); font-weight: 550; margin-right: auto; }
.ob-ghost {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px; border-radius: var(--r-pill);
  background: transparent; border: 1px solid var(--border-strong);
  color: var(--text-2); font: inherit; font-size: 13.5px; font-weight: 650; cursor: pointer;
  transition: border-color .15s ease, color .15s ease;
}
.ob-ghost:hover { border-color: var(--accent-border); color: var(--text); }

/* ── under the box: the title card is earned, not permanent ── */
.ob-under {
  max-width: 420px; margin: 20px auto 0;
  opacity: 0; transform: translateY(8px);
  transition: opacity .4s ease, transform .4s cubic-bezier(.2,.7,.2,1);
  pointer-events: none;
}
.ob-under.show { opacity: 1; transform: none; pointer-events: auto; }
.ob-links { display: flex; flex-direction: column; align-items: center; gap: 8px; margin-top: 22px; text-align: center; }
.ob-links #heroNote { font-size: 13px; color: var(--text-3); max-width: 560px; line-height: 1.55; }

@media (prefers-reduced-motion: reduce) {
  .ob-under { transition: none; }
  body.is-typing .ob { box-shadow: 0 26px 70px rgba(0,0,0,.30), inset 0 1px 0 rgba(255,255,255,.07); }
}

/* ═══════════════════════════════════════════════════════════════════════════
   28 Jul (2) — her screenshot of the new box: "the text is misaligned", "looks
   really plain", "you did not keep the cursor glow". All three were mine.
   ═════════════════════════════════════════════════════════════════════════ */

/* MISALIGNMENT: the pills sit at the box's padding edge, but I gave the
   textarea another 6px of its own — so the line she types started 6px right of
   the controls above it. Everything shares one left edge now. */
.ob textarea { padding-left: 0 !important; padding-right: 0 !important; }

/* THE STRETCHED BUTTON: .btn-generate is width:100% because it was built for a
   full-width card footer. Dropped into the box's flex row it swallowed the
   space and squeezed "Pick my next video" until its label wrapped to two
   lines. Both are sized to their content here. */
.ob-bottom .btn-generate {
  width: auto !important; flex: 0 0 auto;
  padding: 13px 34px; border-radius: var(--r-pill);
  /* Sized to content, but never SMALLER than the secondary action beside it —
     un-stretching it left Start narrower than "Pick my next video", which reads
     as the wrong thing being the main one. */
  min-width: 190px; font-size: 15.5px;
}
.ob-bottom .ob-ghost { flex: 0 0 auto; white-space: nowrap; }

/* THE CURSOR GLOW — GONE, 29 Aug (her third request).
   A light used to follow the pointer across the box: `.ob { --obx/--oby }` fed
   by a mousemove handler in index.html, painted as a radial on `.ob::after`.
   It had been added BACK precisely because the global cursor glow switched off
   over textareas, i.e. it existed only to put the glow where she works.
   Both halves are removed together: deleting only the JS would have frozen the
   gradient at its 50%/0% defaults and left a permanent static blob, which is
   the same complaint in a new costume.
   The box keeps everything that makes it a raised writing surface — the inner
   light in `.ob` above, `:focus-within`, `body.is-typing`, and the slow amber
   breath on `.ob::before` below. Nothing else in this file reads --obx/--oby
   (checked). */

/* Alive at rest: a slow, subtle amber breath behind the box, so it reads as a
   living object before she even touches it. Slow and quiet on purpose (her
   brand is calm, not distracting) — behind the content, never over it. */
.ob::before {
  content: ""; position: absolute; inset: -1px; z-index: -1;
  border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 54px 4px rgba(245, 166, 35, 0.10);
  animation: obBreath 7s ease-in-out infinite;
}
@keyframes obBreath { 0%, 100% { opacity: .35; } 50% { opacity: .8; } }
@media (prefers-reduced-motion: reduce) { .ob::before { animation: none; opacity: .5; } }

/* ── THE THREE WAYS IN ── */
.ob-ways { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 18px auto 0; }
.ob-way {
  display: flex; flex-direction: column; align-items: flex-start; gap: 5px;
  padding: 16px 18px; border-radius: 16px; text-align: left; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--border);
  color: var(--text-2); font: inherit;
  transition: border-color .16s ease, transform .16s ease, background .16s ease;
}
.ob-way:hover { transform: translateY(-2px); border-color: var(--accent-border); }
.ob-way.active { border-color: var(--accent); background: var(--accent-soft); }
.ob-way .ow-i { color: var(--accent); line-height: 0; }
.ob-way .ow-t { font-size: 15px; font-weight: 700; color: var(--text); }
/* --accent on the active card's amber tint measured 1.66:1 in light theme —
   amber on amber, the same failure as the invisible logo. --accent-text is the
   darkened readable-on-light accent and exists for exactly this. */
.ob-way.active .ow-t { color: var(--accent-text); }
/* --text-3 measured 1.88:1 on the light card — a grey hint on a near-white
   surface, effectively invisible. This is body copy, so it takes body ink. */
.ob-way .ow-d { font-size: 12.5px; line-height: 1.5; color: var(--text-2); }
@media (max-width: 760px) { .ob-ways { grid-template-columns: 1fr; } }

/* ── YOUR CHANNEL'S NEXT VIDEOS (the cached angles, re-homed) ── */
.ob-topics { margin: 26px auto 0; }
.ob-topics-h {
  font-size: 11.5px; font-weight: 750; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-3); margin-bottom: 10px;
}

@media (prefers-reduced-motion: reduce) {
  .ob-way:hover { transform: none; }
  /* `.ob::after { transition: none }` lived here for the pointer glow that was
     removed above. The pseudo no longer exists, so the rule went with it. */
}

/* Light theme needed its own ink for the card copy: --text-2 composited to
   2.35:1 over the translucent card, still a hint rather than a sentence.
   Measured, not guessed — this clears 4.5:1 on both the idle and active card. */
:root[data-theme="light"] .ob-way .ow-d { color: #3f4459; }

/* ═══ AUTO'S AI WRITER PANEL (28 Jul, her call) — the full Manual-style script
   writer under the box. The pills, the empty preview card, and the three
   ways-cards are HIDDEN (not deleted, so their controls the JS reads survive)
   in its favour. Its own obw* ids never touch Manual's #topic/#channel. ═══ */
/* The four rules here hid parts of the opening screen while Auto mode was on.
   There is no Auto mode (1 Sep), so `body.create-auto` is a class nothing can
   apply and these were dead selectors. Removed rather than left: a live-looking
   rule for a dead class is what gets "restored" later by someone who finds it
   and assumes the markup went missing. */
.ob-writer {
  max-width: 880px; margin: 16px auto 0; padding: 20px 22px;
  border-radius: 18px; background: var(--surface-2); border: 1px solid var(--border-strong);
  box-shadow: 0 16px 44px rgba(0,0,0,.22);
}
.ob-writer .obw-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
.ob-writer .field-label { display: block; font-size: 12.5px; font-weight: 650; color: var(--text-2); margin: 0 0 6px; }
.ob-writer .obw-input, .ob-writer .obw-opts select {
  width: 100%; padding: 11px 13px; border-radius: 11px; border: 1px solid var(--border-strong);
  background: var(--surface-3); color: var(--text); font: inherit; font-size: 14.5px;
}
.ob-writer .obw-input:focus, .ob-writer .obw-opts select:focus { outline: none; border-color: var(--accent-border); }
.ob-writer .obw-opts { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin: 14px 0 16px; }
.ob-writer .awc-item { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 600; color: var(--text-2); }
.ob-writer .awc-item select { width: auto; padding: 8px 11px; }
.ob-writer .obw-go { width: 100%; justify-content: center; padding: 14px; border-radius: 12px; }
@media (max-width: 640px) { .ob-writer .obw-grid { grid-template-columns: 1fr; } }

/* ══ Custom channel dropdown (29 Jul) ═══════════════════════════════════════
   Her "plain names" were native <select> option rows the OS draws. This gives
   each channel an avatar + name identity with an amber, animated menu. The
   native <select> stays the source of truth (visually hidden); a row-click
   writes to it and dispatches change, so the existing pick logic still runs.
   Amber only; reduce-motion turns the motion off. */
.tvsel { position: relative; }
select.tvsel-native {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); border: 0;
}
.tvsel-trigger {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; border-radius: 11px; cursor: pointer;
  background: var(--surface-3); border: 1px solid var(--border-strong);
  color: var(--text); font: inherit; font-size: 14.5px; font-weight: 600;
  transition: border-color .18s ease, box-shadow .25s ease;
}
.tvsel-trigger:hover { border-color: var(--accent-border); }
.tvsel-trigger:focus-visible,
.tvsel.open .tvsel-trigger { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
.tvsel-ava {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 7px; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; font-size: 11.5px; font-weight: 800; line-height: 1; position: relative;
}
.tvsel-ava img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.tvsel-ava-none { background: var(--surface-2-solid); border: 1px dashed var(--border-strong); color: var(--text-3); }
.tvsel-name { flex: 1 1 auto; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tvsel.is-empty .tvsel-name { color: var(--text-3); }
.tvsel-caret {
  flex: 0 0 auto; width: 8px; height: 8px; margin-left: 2px;
  border-right: 1.8px solid var(--text-3); border-bottom: 1.8px solid var(--text-3);
  transform: rotate(45deg) translateY(-2px); transition: transform .2s ease;
}
.tvsel.open .tvsel-caret { transform: rotate(-135deg) translateY(-1px); }
.tvsel-menu {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 60;
  max-height: 316px; overflow-y: auto; padding: 6px; border-radius: 14px;
  background: var(--surface-2-solid); border: 1px solid var(--border-strong);
  box-shadow: 0 24px 64px rgba(0,0,0,.55);
}
.tvsel-menu[hidden] { display: none; }
.tvsel.open .tvsel-menu { animation: tvselMenu .18s ease both; }
.tvsel-opt {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 9px 10px; border-radius: 10px; cursor: pointer; text-align: left;
  background: transparent; border: 0; color: var(--text); font: inherit;
  transition: background .14s ease;
  animation: tvselRow .22s ease both; animation-delay: calc(var(--i, 0) * 26ms);
}
.tvsel-opt:hover, .tvsel-opt:focus-visible { background: var(--accent-soft); outline: none; }
.tvsel-opt[aria-selected="true"] { background: var(--accent-soft); }
.tvsel-opt[aria-selected="true"] .tvsel-opt-name { color: var(--accent); }
.tvsel-opt .tvsel-ava { width: 30px; height: 30px; border-radius: 8px; font-size: 12.5px; }
.tvsel-opt-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tvsel-opt-name { font-size: 14.5px; font-weight: 650; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tvsel-opt-handle { font-size: 12px; color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@keyframes tvselMenu { from { opacity: 0; transform: translateY(-6px) scale(.99); } to { opacity: 1; transform: none; } }
@keyframes tvselRow { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .tvsel.open .tvsel-menu, .tvsel-opt { animation: none; }
  .tvsel-caret, .tvsel-trigger { transition: none; }
}

/* ── AUTO WRITER: Analyze → 3 angles + cinematic channel header (28 Jul) ── */
.obw-forchan {
  display: flex; align-items: center; gap: 11px; margin: 0 0 16px;
  padding: 10px 14px; border-radius: 12px;
  background: var(--accent-soft); border: 1px solid var(--accent-border);
}
.obw-forchan .ofc-ava {
  width: 30px; height: 30px; border-radius: 50%; flex-shrink: 0; overflow: hidden;
  display: grid; place-items: center; color: #fff; font-weight: 700; font-size: 13px; position: relative;
}
.obw-forchan .ofc-ava img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.obw-forchan .ofc-txt { font-size: 14px; color: var(--text); }
.obw-forchan .ofc-note { color: var(--text-3); font-weight: 400; }
@media (max-width: 620px) { .obw-forchan .ofc-note { display: none; } }

/* ── Writer panel organised (29 Jul) — Length/Angle are now label-above fields
   in the grid (matching Channel/Title, was a cramped inline row); Research is its
   own toggle row; every field answers focus with a soft amber glow; the native
   selects get a custom caret so they read as designed. Amber; reduce-motion off. */
.ob-writer .obw-input { transition: border-color .18s ease, box-shadow .25s ease; }
.ob-writer .obw-input:focus { box-shadow: 0 0 0 3px var(--accent-glow); }
.ob-writer #obwTopic { margin-bottom: 14px; }
.ob-writer select.obw-input {
  appearance: none; -webkit-appearance: none; padding-right: 34px; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b90ab' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 12px center;
}
.obw-research {
  display: flex; align-items: center; gap: 10px; margin: 2px 0 16px;
  padding: 11px 13px; border-radius: 11px; cursor: pointer;
  background: var(--surface-2); border: 1px solid var(--border);
  font-size: 14px; font-weight: 600; color: var(--text-2);
  transition: border-color .18s ease, color .18s ease, background .18s ease;
}
.obw-research:hover { border-color: var(--accent-border); color: var(--text); }
.obw-research input { flex: 0 0 auto; width: 18px; height: 18px; accent-color: var(--accent); cursor: pointer; }
@media (prefers-reduced-motion: reduce) { .ob-writer .obw-input, .obw-research { transition: none; } }

.obw-analyze-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 2px 0 6px; }
.obw-analyze {
  display: inline-flex; align-items: center; gap: 8px; padding: 10px 16px; border-radius: var(--r-pill);
  background: var(--surface-3); border: 1px solid var(--accent-border);
  color: var(--accent-text, var(--accent)); font: inherit; font-size: 13.5px; font-weight: 650; cursor: pointer;
  transition: background .15s ease, transform .15s ease;
}
.obw-analyze:hover { background: var(--accent-soft); transform: translateY(-1px); }
.obw-analyze .spinner-sm { display: none; }
.obw-analyze.loading .spinner-sm { display: inline-block; }
.obw-analyze.loading svg { display: none; }
.obw-analyze-note { font-size: 12px; color: var(--text-3); }

.obw-angles { display: flex; flex-direction: column; gap: 10px; margin: 4px 0 16px; }
.obw-angles-h { font-size: 12px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--text-3); }
.obw-angle {
  display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  padding: 14px 16px; border-radius: 13px; text-align: left; cursor: pointer;
  background: var(--surface-3); border: 1px solid var(--border-strong); color: var(--text-2);
  transition: border-color .16s ease, transform .16s ease, background .16s ease;
  animation: obaRise .4s cubic-bezier(.2,.7,.2,1) both;
}
@keyframes obaRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.obw-angle:hover { transform: translateY(-2px); border-color: var(--accent-border); }
.obw-angle.picked { border-color: var(--accent); background: var(--accent-soft); box-shadow: 0 0 0 1px var(--accent-border), 0 0 26px var(--accent-glow); }
.obw-angle .oba-t { font-size: 15px; font-weight: 700; color: var(--text); }
.obw-angle.picked .oba-t { color: var(--accent-text, var(--accent)); }
.obw-angle .oba-d { font-size: 13px; line-height: 1.5; color: var(--text-3); }
.obw-more { align-self: flex-start; margin-top: 2px; background: none; border: none; color: var(--text-2); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
.obw-more:hover { color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .obw-angle { animation: none; } }

/* ── LIVING LOOK PREVIEW (28 Jul) — the Look step's title card performs the
   selected style; params come from the dials via JS (obLookPlay). Only this
   one card moves, so the page stays calm. ── */
#createView #fmtPreview { overflow: hidden; }
#createView #fmtPreview.lp-play #fpTitle {
  animation: lpReveal var(--lp-dur, .9s) cubic-bezier(.2,.72,.2,1) both;
}
@keyframes lpReveal {
  from { opacity: 0; transform: translateY(var(--lp-shift, 12px)) scale(var(--lp-scale, 1.03)); filter: blur(var(--lp-blur, 5px)); }
  to   { opacity: 1; transform: none; filter: none; }
}
#createView #fmtPreview #fpTint { animation: lpDrift var(--lp-drift, 14s) ease-in-out infinite alternate; will-change: transform; }
@keyframes lpDrift { from { transform: scale(1.03) translate(-1%, -.5%); } to { transform: scale(1.07) translate(1%, .7%); } }
@media (prefers-reduced-motion: reduce) {
  #createView #fmtPreview.lp-play #fpTitle, #createView #fmtPreview #fpTint { animation: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   28 Jul — DARK "GLOWING GLASS" (her inspiration images: amber KEPT, glass with
   a glowing rim, glow only on FEATURED + HOVER so it reads premium, not busy).
   Light theme untouched. Applied across the dark side via shared card classes.
   Only borders/shadows change — base surfaces and text contrast are unaffected.
   ═════════════════════════════════════════════════════════════════════════ */
:root:not([data-theme="light"]) .plan,
:root:not([data-theme="light"]) .home-card,
:root:not([data-theme="light"]) .value-card,
:root:not([data-theme="light"]) .sc-card,
:root:not([data-theme="light"]) .ex-card,
:root:not([data-theme="light"]) .prob-card,
:root:not([data-theme="light"]) .ob-way,
:root:not([data-theme="light"]) .obw-angle,
:root:not([data-theme="light"]) .style-opt,
:root:not([data-theme="light"]) .theme-card {
  transition: border-color .2s ease, box-shadow .3s ease, transform .2s ease !important;
}
:root:not([data-theme="light"]) .plan:hover,
:root:not([data-theme="light"]) .home-card:hover,
:root:not([data-theme="light"]) .value-card:hover,
:root:not([data-theme="light"]) .sc-card:hover,
:root:not([data-theme="light"]) .ex-card:hover,
:root:not([data-theme="light"]) .prob-card:hover,
:root:not([data-theme="light"]) .ob-way:hover,
:root:not([data-theme="light"]) .obw-angle:hover,
:root:not([data-theme="light"]) .style-opt:hover,
:root:not([data-theme="light"]) .theme-card:hover,
:root:not([data-theme="light"]) .plan.featured,
:root:not([data-theme="light"]) .home-card.hc-primary,
:root:not([data-theme="light"]) .ob-way.active,
:root:not([data-theme="light"]) .obw-angle.picked,
:root:not([data-theme="light"]) .style-opt[aria-checked="true"] {
  border-color: rgba(245, 166, 35, .5) !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .10),
    0 0 0 1px rgba(245, 166, 35, .28),
    0 0 34px rgba(245, 166, 35, .16),
    0 20px 48px rgba(0, 0, 0, .5) !important;
}

/* ═══════════════════════════════════════════════════════════════════════════
   28 Jul — DARK, BLACK + NEON ("I don't see changes" ×2 — the first pass matched
   the existing navy glass kit almost exactly, so nothing read as new). This is a
   real departure: near-BLACK card surfaces (was navy) on a deeper near-black page,
   a faint amber edge at rest, and a strong amber NEON glow (amber core + warm-white
   inner + amber bloom) on the active/hovered card. Palette stays LOCKED amber — no
   violet. Re-declared AFTER the resting rule so the glow wins. Light untouched.
   ═════════════════════════════════════════════════════════════════════════ */
:root:not([data-theme="light"]) body {
  background:
    radial-gradient(150% 108% at 50% 16%, transparent 40%, rgba(0,0,0,.74) 100%),
    radial-gradient(920px 620px at 50% -8%, rgba(245,166,35,.12), transparent 60%),
    radial-gradient(840px 700px at 97% 106%, rgba(245,166,35,.07), transparent 62%),
    radial-gradient(680px 560px at 3% 102%, rgba(245,166,35,.05), transparent 60%),
    #04050b;
  background-attachment: fixed;
}
/* Glassier resting surface on every dark card — brighter internal top-light,
   more translucency, a crisper edge. Reads as premium glass, no amber (that is
   the glow's job). */
:root:not([data-theme="light"]) .home-card,
:root:not([data-theme="light"]) .hstat,
:root:not([data-theme="light"]) .plan,
:root:not([data-theme="light"]) .value-card,
:root:not([data-theme="light"]) .sc-card,
:root:not([data-theme="light"]) .ex-card,
:root:not([data-theme="light"]) .prob-card,
:root:not([data-theme="light"]) .faq-item,
:root:not([data-theme="light"]) .ob-way,
:root:not([data-theme="light"]) .obw-angle,
:root:not([data-theme="light"]) .style-opt {
  background:
    linear-gradient(177deg, rgba(255,255,255,.11) 0%, rgba(255,255,255,.04) 44%, rgba(255,255,255,.015) 100%),
    linear-gradient(180deg, rgba(34, 38, 58, .9), rgba(26, 29, 46, .92)) !important;
  border: 1px solid rgba(255, 255, 255, .17) !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .14),
    0 0 0 1px rgba(245, 166, 35, .05),
    0 22px 52px rgba(0, 0, 0, .58) !important;
}
/* RE-DECLARED so it wins over the resting rule above: the amber rim on featured + hover. */
:root:not([data-theme="light"]) .plan:hover,
:root:not([data-theme="light"]) .home-card:hover,
:root:not([data-theme="light"]) .value-card:hover,
:root:not([data-theme="light"]) .sc-card:hover,
:root:not([data-theme="light"]) .ex-card:hover,
:root:not([data-theme="light"]) .prob-card:hover,
:root:not([data-theme="light"]) .ob-way:hover,
:root:not([data-theme="light"]) .obw-angle:hover,
:root:not([data-theme="light"]) .style-opt:hover,
:root:not([data-theme="light"]) .plan.featured,
:root:not([data-theme="light"]) .home-card.hc-primary,
:root:not([data-theme="light"]) .ob-way.active,
:root:not([data-theme="light"]) .obw-angle.picked,
:root:not([data-theme="light"]) .style-opt[aria-checked="true"] {
  border-color: rgba(245, 166, 35, .6) !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 220, 170, .22),
    0 0 0 1px rgba(245, 166, 35, .42),
    0 0 28px rgba(245, 166, 35, .32),
    0 0 64px rgba(245, 166, 35, .16),
    0 22px 52px rgba(0, 0, 0, .6) !important;
}

/* ── 26 Aug, her order: the PRICING plan cards keep their ORIGINAL dark surface.
   The dark-card "lift" above (brighter frosted navy so app tiles pop) is what she
   asked for on the app screens, but `.plan` rides the same rule list and it changed
   the pricing cards' colour too — she wants those exactly as they were. Restoring
   the pre-lift values here (later source order wins), dark only. ── */
:root:not([data-theme="light"]) .plan {
  background:
    linear-gradient(177deg, rgba(255,255,255,.055) 0%, rgba(255,255,255,.015) 42%, rgba(255,255,255,0) 100%),
    rgba(12, 14, 24, .72) !important;
  border: 1px solid rgba(255, 255, 255, .075) !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .09),
    0 0 0 1px rgba(245, 166, 35, .045),
    0 22px 52px rgba(0, 0, 0, .62) !important;
}
/* ...and its hover / featured amber rim, re-declared so it still wins over the line above. */
:root:not([data-theme="light"]) .plan:hover,
:root:not([data-theme="light"]) .plan.featured {
  border-color: rgba(245, 166, 35, .6) !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 220, 170, .22),
    0 0 0 1px rgba(245, 166, 35, .42),
    0 0 28px rgba(245, 166, 35, .32),
    0 0 64px rgba(245, 166, 35, .16),
    0 22px 52px rgba(0, 0, 0, .6) !important;
}

/* ── CADENCE PULSE (28 Jul) — a dot sweeps a ticked track once per shot, at the
   real cut rate set by obLookPlay. Pace made visible. ── */
.look-pulse {
  position: relative; height: 16px; margin: 14px 2px 0; border-radius: 8px; overflow: hidden;
  background: repeating-linear-gradient(90deg, rgba(255,255,255,.13) 0 2px, transparent 2px 24px);
}
:root[data-theme="light"] .look-pulse { background: repeating-linear-gradient(90deg, rgba(20,22,40,.14) 0 2px, transparent 2px 24px); }
.look-pulse .lp-dot {
  position: absolute; top: 50%; left: -2%; width: 30px; height: 7px; border-radius: 7px;
  transform: translate(-50%, -50%);
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  box-shadow: 0 0 16px var(--accent-glow);
  animation: lpSweep var(--pulse-dur, 5s) linear infinite;
}
@keyframes lpSweep { from { left: -2%; } to { left: 102%; } }
@media (prefers-reduced-motion: reduce) { .look-pulse .lp-dot { animation: none; left: 12%; } }

/* ── CARD-METER FILL (28 Jul) — when a style is picked, its 5 meter bars sweep
   in and brighten (the resting bars carry a PAUSED inline fmtCut animation, so
   this needs !important to win). Staggered. Reduced-motion just brightens. ── */
.style-opt .fmt-live span { transform-origin: left center; }
.style-opt[aria-checked="true"] .fmt-live span {
  opacity: .92 !important;
  animation: fmtFill .5s cubic-bezier(.2,.7,.2,1) both !important;
}
.style-opt[aria-checked="true"] .fmt-live span:nth-child(1) { animation-delay: 0s !important; }
.style-opt[aria-checked="true"] .fmt-live span:nth-child(2) { animation-delay: .06s !important; }
.style-opt[aria-checked="true"] .fmt-live span:nth-child(3) { animation-delay: .12s !important; }
.style-opt[aria-checked="true"] .fmt-live span:nth-child(4) { animation-delay: .18s !important; }
.style-opt[aria-checked="true"] .fmt-live span:nth-child(5) { animation-delay: .24s !important; }
@keyframes fmtFill { from { transform: scaleX(0); opacity: 0; } to { transform: scaleX(1); opacity: .92; } }
@media (prefers-reduced-motion: reduce) {
  .style-opt[aria-checked="true"] .fmt-live span { animation: none !important; opacity: .92 !important; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   8 Aug — HOME: THE PRIMARY CARD IN LIGHT MODE.
   Her item: "Create a video" wears a highlighted outline in DARK only.

   ROOT CAUSE (not a missing colour — a lost cascade). The only light-mode
   claim on that card was index.html's bare `.home-card.hc-primary`
   (specificity 0,2,0, no !important). The glass kit's
   `:root[data-theme="light"] .home-card` (0,3,0, !important, line ~1148)
   outranks it and repaints background + border + box-shadow — so in light
   mode the primary card rendered BYTE-IDENTICAL to the other five and that
   index.html rule was dead code. Every hc-primary rule in this file above is
   `:root:not([data-theme="light"])`, i.e. dark-only.

   So the fix has to (a) live here, at the end, and (b) carry 0,4,0 +
   !important, or the kit eats it again.

   LIGHT NEEDS ITS OWN WEIGHT, not the dark recipe recoloured. Amber glow is
   invisible on near-white, so the hierarchy is carried by INK + EDGE + LIFT:
   a warm cream fill (opaque enough to sit forward of its translucent
   neighbours), one crisp deep-amber hairline, a soft amber halo ring, and a
   warm-shadow lift. Static — no sweep, no shimmer (she called moving shine
   on the plan cards "glitchy, not premium").

   MEASURED, not guessed (WCAG 1.4.11 wants 3:1 for a component boundary):
     #b8730a border  vs the card's cream interior  3.64:1
     #b8730a border  vs the green page behind it   3.18:1
     #a35f04 icon    vs the card's warm top corner 4.22:1  (was 1.71:1 —
                     --accent #f5a623 is a fill colour and vanishes as a mark
                     on white; light mode already keeps a deeper ink for this
                     exact reason, see --accent-text.)
   Locked by test/home-outline.test.js, which recomputes the cascade AND the
   contrast rather than trusting that the declarations exist.
   Dark mode is untouched — every selector below is light-scoped.
   ═════════════════════════════════════════════════════════════════════════ */
:root[data-theme="light"] .home-card.hc-primary {
  /* The EDGE carries the hierarchy; the fill only warms the surface. A wide
     bloom over a .76 base read as a post-it — and the page's mint bled through
     the bottom into a muddy yellow-green. So: a near-opaque warm-white card
     (no bleed, one clean surface) with the bloom pulled back to a corner
     light. Bright neighbours, warm primary, hard amber edge. */
  background:
    radial-gradient(86% 74% at 4% -10%, rgba(255, 178, 54, .30) 0%,
                                        rgba(255, 206, 124, .10) 46%,
                                        rgba(255, 255, 255, 0) 72%),
    linear-gradient(180deg, rgba(255, 253, 249, .985) 0%,
                            rgba(255, 249, 238, .965) 52%,
                            rgba(255, 246, 231, .95) 100%) !important;
  border: 1px solid #b8730a !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .95),
    0 0 0 3px rgba(245, 166, 35, .20),
    0 2px 6px rgba(122, 76, 8, .10),
    0 20px 42px -16px rgba(122, 76, 8, .34) !important;
}
/* The mark reads as a mark. Primary card only — the other five keep the kit's
   tone (flagged in the report, deliberately not redesigned here). */
:root[data-theme="light"] .home-card.hc-primary .hc-ico { color: #a35f04; }

/* The same lost cascade killed HOVER for every light-mode home card:
   index.html promises `border-color: var(--accent)` + a lifted shadow at
   0,2,0 and the kit's !important ate both, so light mode answered the
   pointer with a 3px nudge and nothing else while dark mode glowed. */
:root[data-theme="light"] .home-card:hover {
  border-color: rgba(184, 115, 10, .55) !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .95),
    0 0 0 3px rgba(245, 166, 35, .13),
    0 4px 10px rgba(28, 40, 75, .07),
    0 22px 46px -18px rgba(28, 40, 75, .30) !important;
}
/* Hovering the primary must not DROP it back to a secondary card — the rule
   above matches it too, so it gets its own louder state at 0,5,0. */
:root[data-theme="light"] .home-card.hc-primary:hover {
  border-color: #a35f04 !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .95),
    0 0 0 4px rgba(245, 166, 35, .26),
    0 4px 10px rgba(122, 76, 8, .12),
    0 26px 52px -18px rgba(122, 76, 8, .40) !important;
}

/* ════════════════════════════════════════════════════════════════════════
   PRE-GENERATE NOTICE
   One calm screen between "Generate video" and a spent credit. It is NOT a
   warning box — it is the last honest read of what this particular setup
   means for a video the customer is about to publish. Styled to match the
   Create-flow modals (.chm-overlay / .confirm-modal) so it reads as part of
   the room, not as a browser alert. Own `pgn-` namespace: index.html's local
   <style> block wins over this file on equal specificity, so nothing here
   shares a class name with it.
   ════════════════════════════════════════════════════════════════════════ */
.pgn-overlay {
  position: fixed; inset: 0; z-index: 245;
  display: none; align-items: center; justify-content: center; padding: 24px;
  background: rgba(6, 8, 18, 0.62);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.pgn-overlay.open { display: flex; animation: pgn-fade .18s ease both; }
@keyframes pgn-fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes pgn-rise { from { opacity: 0; transform: translateY(10px) scale(.985) } to { opacity: 1; transform: none } }

.pgn-card {
  width: 100%; max-width: 560px; max-height: 88vh;
  display: flex; flex-direction: column;
  background: var(--surface-solid);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-float);
  animation: pgn-rise .26s cubic-bezier(.22, 1, .36, 1) both;
}
@media (prefers-reduced-motion: reduce) {
  .pgn-overlay.open, .pgn-card { animation: none; }
}

.pgn-head { padding: 24px 26px 0; }
.pgn-title { font-size: 20px; font-weight: 800; letter-spacing: -.015em; margin: 0 0 6px; }
.pgn-lede  { font-size: 15px; line-height: 1.55; color: var(--text-2); margin: 0; }

/* The body scrolls, the head and the actions never do — so the acknowledge
   button is always on screen and the notice is always one screen tall. */
.pgn-body { padding: 18px 26px 4px; overflow-y: auto; display: grid; gap: 14px; }

.pgn-item {
  display: grid; grid-template-columns: 10px 1fr; gap: 12px; align-items: start;
  padding: 14px 16px; border-radius: 12px;
  border: 1px solid var(--border); background: var(--surface-2-solid);
}
.pgn-item::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%; margin-top: 7px;
  background: var(--text-3);
}
.pgn-item.t-clear::before { background: var(--green, #3ecf6f); }
.pgn-item.t-risk::before  { background: #e08b84; }
.pgn-item.t-risk {
  border-color: rgba(199, 64, 47, .34);
  background: rgba(199, 64, 47, .06);
}
/* "How to fix this" — the line that makes the caution dialog worth stopping for
   rather than clicking through. Set apart from the explanation above it so the
   ACTION is the part the eye lands on. */
.pgn-fix {
  margin-top: 9px; padding: 9px 12px; border-radius: 9px;
  background: var(--accent-soft, rgba(245,166,35,.10));
  border-left: 2px solid var(--accent, #f5a623);
  font-size: var(--fs-sm); line-height: 1.55; color: var(--text-2);
}
.pgn-item h4 { font-size: 14.5px; font-weight: 750; margin: 0 0 5px; letter-spacing: -.005em; }
.pgn-item p  { font-size: 14px; line-height: 1.6; color: var(--text-2); margin: 0; }

.pgn-foot {
  padding: 16px 26px 22px; margin-top: 4px;
  border-top: 1px solid var(--border);
  display: flex; flex-wrap: wrap; gap: 12px; align-items: center;
}
.pgn-again {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer;
  font-size: 14px; color: var(--text-2); margin-right: auto;
}
.pgn-again input { width: 17px; height: 17px; cursor: pointer; accent-color: var(--accent, #f5a623); }
.pgn-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.pgn-go {
  padding: 12px 22px; border: 0; border-radius: 11px; cursor: pointer;
  font: inherit; font-weight: 750; font-size: 15px;
  background: var(--accent, #f5a623); color: var(--accent-ink, #1a1205);
}
.pgn-go:hover { filter: brightness(1.06); }
.pgn-back {
  padding: 12px 18px; border-radius: 11px; cursor: pointer;
  font: inherit; font-weight: 650; font-size: 15px;
  background: transparent; color: var(--text-2);
  border: 1.5px solid var(--border-strong);
}
.pgn-back:hover { color: var(--text); border-color: var(--text-3); }
.pgn-legal { flex: 0 0 100%; font-size: 12.5px; color: var(--text-3); margin: 2px 0 0; }

/* Light mode: the dark-mode tinted rows go muddy on paper. */
:root[data-theme="light"] .pgn-overlay { background: rgba(28, 40, 75, 0.34); }
:root[data-theme="light"] .pgn-item.t-risk {
  border-color: rgba(163, 46, 30, .30);
  background: rgba(199, 64, 47, .05);
}
:root[data-theme="light"] .pgn-item.t-risk::before { background: #b4392c; }

@media (max-width: 560px) {
  .pgn-head { padding: 20px 18px 0; }
  .pgn-body { padding: 14px 18px 4px; }
  .pgn-foot { padding: 14px 18px 18px; }
  .pgn-again { margin-right: 0; flex: 0 0 100%; }
  .pgn-actions { flex: 1; }
  .pgn-go, .pgn-back { flex: 1; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — THE REDESIGN LAYER  (her hard redesign, after a 4-question quiz)
   Her answers: landing first then roll out · keep BOTH themes · Space Grotesk
   display face · amber glows on a FLAT base.

   WHY FLAT. I measured both reference sites live:
     clickmaxstudio.com → body #121212, background-image: NONE
     vidrush.ai         → body #181818, background-image: NONE
   Neither paints a page-wide gradient. All their depth comes from a few blurred
   radial blobs positioned INSIDE sections (blur 32-100px, pointer-events:none).
   That is the whole trick, and it is why they read as professional rather than
   busy. It also permanently kills the "black bar on scroll" class of bug: with no
   page-wide fixed gradient there is nothing left to expose as you scroll.

   STAGED: everything here is scoped to `body` so the landing proves the
   look first (her call). Rolling out = widen these selectors to `body`.
   UNDO: docs/backups/pre-redesign-26aug/ restores every file.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. THE FLAT CANVAS ───────────────────────────────────────────────────── */
body {
  /* One flat colour. No gradients, no attachment tricks, nothing to smear. */
  background: var(--page-base) !important;
  background-attachment: scroll !important;
  /* Bigger, tighter display scale — the references run their hero near 60px and
     their section heads near 40px. These override the global scale for the
     landing only, so every existing component grows without being rewritten. */
  --fs-2xl: clamp(30px, 4.6vw, 46px);
  --fs-3xl: clamp(38px, 7.2vw, 68px);
}
:root { --page-base: #0d0d11; }                 /* dark: flat, deep, lets amber sing */
:root:not([data-theme="light"]) { --page-base: #0d0d11; }
/* Light: a WARM neutral, not the old green wash — amber is the brand colour, and a
   green ground fights it. Same flat principle. */
:root[data-theme="light"] { --page-base: #f7f5f1; }

/* Kill every ambient/animated background layer on the redesigned page: the star
   canvas, the three blurred orbs, the grid sheen. Her order: "remove the stars and
   the interactive things". The elements stay in the DOM (other pages still use
   them until rollout) — they just paint nothing here. */
body #particles-canvas,
body .ambient,
body .ambient-orb,
body .ambient-aurora,
body .create-aurora { display: none !important; }
body::before { display: none !important; }
/* (body::after is re-used by the film-grain plate further down — see §10.) */

/* ── 2. SECTION GLOWS — the only thing in the background ──────────────────── */
/* A blurred amber blob, positioned inside a section, never interactive. This is
   the ClickMax/VidRush move, on our brand colour. Use .has-glow on a section and
   drop a <span class="glow glow-a"> inside it. */
body .section,
body .hero,
body .final-cta { position: relative; }
.glow {
  position: absolute; z-index: 0; pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(circle, var(--glow-color, rgba(245,166,35,.30)), transparent 68%);
  /* --glow-blur is a variable so the filament's brightness() can be folded in
     ONCE here. Re-declaring `filter` in a later rule would drop the blur. */
  --glow-blur: 58px;
  filter: blur(var(--glow-blur)) brightness(var(--glit, 1));
  opacity: var(--glow-o, 1);
}
/* placements — kept few and asymmetric so the page feels composed, not tiled */
.glow-tl { width: 620px; height: 420px; left: -180px; top: -120px; }
.glow-tr { width: 560px; height: 400px; right: -160px; top: -100px; }
.glow-bl { width: 520px; height: 380px; left: -140px; bottom: -140px; }
.glow-br { width: 600px; height: 420px; right: -170px; bottom: -130px; }
.glow-c  { width: 760px; height: 460px; left: 50%; top: 40%; transform: translate(-50%,-50%); }
.glow-cool { --glow-color: rgba(120, 150, 255, .16); }   /* one cool note for contrast */
/* the light theme needs far less glow or it turns to haze on a pale ground */
:root[data-theme="light"] .glow { --glow-o: .55; --glow-blur: 66px; }
:root[data-theme="light"] .glow-cool { --glow-color: rgba(120, 150, 255, .12); }
/* section content must sit above the glow */
body .section > *, body .hero > * { position: relative; z-index: 1; }

/* ── 3. SURFACES ON A FLAT GROUND ─────────────────────────────────────────── */
/* With no page gradient behind them, cards need their own quiet elevation. One
   surface recipe, both themes, so the whole page reads as one system. */
body {
  --card-bg: #16161b;
  --card-bd: rgba(255,255,255,.09);
  --card-bd-hover: rgba(245,166,35,.42);
  --card-sh: 0 1px 0 rgba(255,255,255,.04) inset, 0 18px 40px -28px rgba(0,0,0,.9);
}
:root[data-theme="light"] body {
  --card-bg: #ffffff;
  --card-bd: rgba(24,20,12,.10);
  --card-bd-hover: rgba(196,120,10,.45);
  --card-sh: 0 1px 0 rgba(255,255,255,.9) inset, 0 16px 36px -26px rgba(70,55,25,.28);
}

/* ── 4. TYPE ──────────────────────────────────────────────────────────────── */
/* Section heads: bigger, tighter, and the eyebrow gets real presence instead of
   whispering. */
body .section-head h2,
body .grow-head h2,
body .faq-intro h2 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.038em;
  line-height: 1.04;
}
body .kicker {
  font-family: var(--font); font-weight: 700; font-size: 12px;
  letter-spacing: .14em; text-transform: uppercase; color: var(--accent);
  opacity: .95;
}
body .section-head p,
body .grow-head p { font-size: 17px; line-height: 1.6; }

@media (prefers-reduced-motion: reduce) { .glow { --glow-blur: 58px; } }


/* ── 5. DE-GREEN: one brand colour ────────────────────────────────────────────
   The old light theme was built around green, and green survived in a dozen
   places that now fight the amber system on a flat ground (the hero shimmer, the
   prompt box's prismatic border, the proof-band money panels, the savings badge).
   Her brief: amber is THE colour. These are landing-scoped so the rollout carries
   them page by page.
   KEPT GREEN DELIBERATELY: the ▲ growth arrows and "up" lines on the analytics
   cards. There green is semantic (a rise), not brand — every analytics product
   on earth reads it that way, and recolouring it to amber would lose meaning. */

/* the headline highlight — amber, not green→amber→green */
body .hero h1 em,
:root[data-theme="light"] body .hero h1 em {
  background: linear-gradient(100deg, #f0a020 18%, #ffd08a 50%, #f0a020 82%);
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}
/* the prompt box — warm glass, amber edge */
:root[data-theme="light"] body .prompt-box {
  background:
    linear-gradient(160deg, rgba(255,255,255,.92), rgba(252,248,241,.72)) padding-box,
    linear-gradient(120deg, rgba(245,166,35,.55), rgba(245,166,35,.22), rgba(245,166,35,.5)) border-box !important;
  box-shadow: 0 26px 64px -30px rgba(90, 62, 20, .26), inset 0 1px 0 rgba(255,255,255,.95) !important;
}
:root:not([data-theme="light"]) body .prompt-box {
  background: var(--card-bg); border-color: var(--card-bd);
}
/* ghost buttons */
:root[data-theme="light"] body .hero-actions .btn-ghost { border-color: rgba(196,120,10,.35) !important; }
:root[data-theme="light"] body .hero-actions .btn-ghost:hover { border-color: rgba(196,120,10,.6) !important; }
/* the savings badge */
body .bc-save {
  background: rgba(245,166,35,.16) !important; color: var(--accent-text, var(--accent)) !important;
  border: 1px solid rgba(245,166,35,.34) !important;
}
/* …but on the ACTIVE (amber-filled) cycle button the amber-on-amber badge
   measured near-invisible (12 Sep 2026): dark ink on a dark tint there. */
body .bc-opt.active .bc-save {
  background: rgba(0,0,0,.18) !important; color: #1a1205 !important;
  border-color: rgba(0,0,0,.22) !important;
}
/* proof-band money panel — neutral glass, amber figure (was teal on teal) */
body .pc-est {
  background: linear-gradient(180deg, rgba(245,166,35,.09), rgba(245,166,35,.03)) !important;
  border-color: rgba(245,166,35,.20) !important;
}
body .pc-estval { color: var(--accent-text, var(--accent)) !important; }

/* ── 6. CARDS ON THE FLAT GROUND ─────────────────────────────────────────────
   Every card family on the landing gets ONE surface so the page reads as a system.
   These override the old glass-kit !important rules by later source order. */
body .pcard,
body .faq-item,
body .bx,
body .feature,
body .plan,
body .vg-card,
body .grow-dash {
  background: var(--card-bg) !important;
  border: 1px solid var(--card-bd) !important;
  box-shadow: var(--card-sh) !important;
}
body .faq-item[open] {
  background: linear-gradient(180deg, rgba(245,166,35,.07), transparent 62%), var(--card-bg) !important;
  border-color: var(--card-bd-hover) !important;
}
body .pcard:hover,
body .faq-item:hover,
body .bx:hover,
body .feature:hover,
body .plan:hover { border-color: var(--card-bd-hover) !important; }
/* the featured plan keeps its amber identity, louder on a flat ground */
body .plan.featured {
  border-color: rgba(245,166,35,.55) !important;
  box-shadow: 0 0 0 1px rgba(245,166,35,.28), 0 26px 60px -34px rgba(245,166,35,.5) !important;
}
/* the before/after faces must stay OPAQUE or the two states ghost through */
body .bx-before { background: var(--card-bg) !important; }
body .bx-after {
  background: linear-gradient(180deg, rgba(245,166,35,.17), rgba(245,166,35,.045) 62%), var(--card-bg) !important;
}


/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — REDESIGN WAVE 2 (all-in, every page)
   Her quiz answers: frosted round pill nav · BOTH card styles (glass for feature
   cards, lifted for the rest) · no bento · all four flourishes (film grain, amber
   gradient headlines, calm scroll reveals, glowing amber eyebrows).

   ★ WHY THE DARK SIDE LOOKED FLAT — measured, not guessed:
     vidrush.ai cards = rgb(49,49,49) on a rgb(24,24,24) page → a ~25-point LIFT,
     radius 20-23px, NO border, soft shadow.
     Ours were #16161b on #0d0d11 → a ~5-point lift with a hairline border, which
     is why every panel read as a murky rectangle instead of an object on a page.
   The fix is the lift, not more colour.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 7. SURFACE SCALE ─────────────────────────────────────────────────────── */
:root:not([data-theme="light"]) {
  --page-base:  #0b0b0f;   /* deeper, neutral — lets the lift read */
  --surf-1:     #191920;   /* the standard card: a real step off the page */
  --surf-2:     #21212a;   /* raised: hovered / featured / nested */
  --surf-line:  rgba(255,255,255,.055);   /* whisper, not a hairline box */
  --glass-bg:   rgba(255,255,255,.055);
  --glass-edge: rgba(255,255,255,.14);
}
:root[data-theme="light"] {
  --page-base:  #f6f4f0;
  --surf-1:     #ffffff;
  --surf-2:     #ffffff;
  --surf-line:  rgba(28,22,12,.07);
  --glass-bg:   rgba(255,255,255,.62);
  --glass-edge: rgba(255,255,255,.95);
}
body {
  --card-bg: var(--surf-1);
  --card-bd: var(--surf-line);
  --card-sh: 0 10px 30px -14px rgba(0,0,0,.65), 0 2px 6px -2px rgba(0,0,0,.35);
}
:root[data-theme="light"] body {
  --card-sh: 0 10px 28px -16px rgba(72,56,26,.28), 0 2px 6px -3px rgba(72,56,26,.14);
}

/* Every card family: lifted, borderless-feeling, generous radius.
   ★ CASCADE NOTE (a bug this pass shipped and then had to fix): the light glass kit
   declares `:root[data-theme="light"] .home-card { … !important }` at specificity
   (0,3,0). A rule written `body .home-card` is only (0,1,1) and a
   `body .home-card.hc-primary` only (0,2,1) — BOTH lose to the kit. So a "both
   themes" rule here silently did nothing in light, while still being loud enough in
   dark to flatten the app's primary card. The card lift is therefore DARK-SCOPED
   (dark is what she said looked bad), and light keeps the treatment that was already
   tuned for it — including the WCAG-checked primary-card outline that
   test/home-outline.test.js pins. Do not "simplify" these selectors back. */
:root:not([data-theme="light"]) body .pcard,
:root:not([data-theme="light"]) body .faq-item,
:root:not([data-theme="light"]) body .bx,
:root:not([data-theme="light"]) body .feature,
:root:not([data-theme="light"]) body .plan,
:root:not([data-theme="light"]) body .vg-card,
:root:not([data-theme="light"]) body .home-card,
:root:not([data-theme="light"]) body .hstat,
:root:not([data-theme="light"]) body .value-card,
:root:not([data-theme="light"]) body .sc-card,
:root:not([data-theme="light"]) body .ex-card,
:root:not([data-theme="light"]) body .prob-card,
:root:not([data-theme="light"]) body .ws-card,
:root:not([data-theme="light"]) body .how-screen,
:root:not([data-theme="light"]) body .auth-card,
:root:not([data-theme="light"]) body .gd-card,
:root:not([data-theme="light"]) body .cr-call {
  background: var(--surf-1) !important;
  border: 1px solid var(--surf-line) !important;
  border-radius: 20px !important;
  box-shadow: var(--card-sh) !important;
}
/* Light: only the LANDING card families get the new radius/shadow. The app's own
   cards (.home-card/.hstat and the primary) keep their existing light treatment. */
:root[data-theme="light"] body .pcard,
:root[data-theme="light"] body .faq-item,
:root[data-theme="light"] body .bx,
:root[data-theme="light"] body .feature,
:root[data-theme="light"] body .plan,
:root[data-theme="light"] body .vg-card,
:root[data-theme="light"] body .gd-card,
:root[data-theme="light"] body .cr-call {
  background: var(--surf-1) !important;
  border: 1px solid var(--surf-line) !important;
  border-radius: 20px !important;
  box-shadow: var(--card-sh) !important;
}
body .faq-item { border-radius: 16px !important; }
body .pcard:hover, body .faq-item:hover, body .bx:hover, body .feature:hover,
body .plan:hover, body .value-card:hover {
  border-color: rgba(245,166,35,.34) !important;
}
:root:not([data-theme="light"]) body .pcard:hover,
:root:not([data-theme="light"]) body .faq-item:hover,
:root:not([data-theme="light"]) body .bx:hover,
:root:not([data-theme="light"]) body .feature:hover,
:root:not([data-theme="light"]) body .plan:hover {
  background: var(--surf-2) !important;
  box-shadow: 0 18px 44px -18px rgba(0,0,0,.7), 0 0 0 1px rgba(245,166,35,.14) !important;
}
body .faq-item[open] {
  background: linear-gradient(180deg, rgba(245,166,35,.08), transparent 58%), var(--surf-2) !important;
  border-color: rgba(245,166,35,.3) !important;
}
/* The featured PLAN is landing-side and safe to own in both themes. The app's
   .home-card.hc-primary is deliberately NOT touched here — see the cascade note. */
:root:not([data-theme="light"]) body .plan.featured {
  background: linear-gradient(180deg, rgba(245,166,35,.13), rgba(245,166,35,.02) 55%), var(--surf-2) !important;
  border-color: rgba(245,166,35,.45) !important;
  box-shadow: 0 0 0 1px rgba(245,166,35,.22), 0 26px 60px -30px rgba(245,166,35,.42) !important;
}
/* the before/after wipe faces must stay opaque or the two states ghost */
body .bx-before { background: var(--surf-1) !important; }
body .bx-after  { background: linear-gradient(180deg, rgba(245,166,35,.18), rgba(245,166,35,.05) 62%), var(--surf-1) !important; }

/* ── 8. GLASS — for feature cards only (her "both" answer) ────────────────── */
.card-glass, body .vs-col {
  background: var(--glass-bg) !important;
  border: 1px solid var(--glass-edge) !important;
  border-radius: 22px !important;
  backdrop-filter: blur(18px) saturate(1.25);
  -webkit-backdrop-filter: blur(18px) saturate(1.25);
  box-shadow: inset 0 1px 0 var(--glass-edge), 0 18px 44px -26px rgba(0,0,0,.7) !important;
}

/* ── 9. THE NAV — a frosted round pill (ClickMax: radius 9999px, rgba bg,
       blur(12px), NO border). Ours was an opaque box with a visible border, which
       is the thing she called slop. ─────────────────────────────────────────── */
.nav { padding: 12px 16px 0 !important; }
.nav-inner {
  /* ★ THE PAGE HAS ONE RAIL (4 Sep). The pill is 1080 wide like .section and
     carries .section's own 28px padding, so the wordmark's ink lands exactly on
     the rail every section head and every card edge uses (measured: 124px at
     1280). It used to be 1060/22 here and 980 when scrolled — the logo moved
     50px sideways as you scrolled past the fold, which is the "don't move
     things while people are reading" rule breaking itself. */
  max-width: 1080px !important;
  padding: 0 28px !important;
  border-radius: 999px !important;
  background: rgba(16,16,21,.55) !important;
  backdrop-filter: blur(14px) saturate(1.4);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  border: 1px solid rgba(255,255,255,.06) !important;
  box-shadow: 0 8px 30px -12px rgba(0,0,0,.6) !important;
  transition: background .3s var(--ease, ease), box-shadow .3s ease, transform .3s ease;
}
:root[data-theme="light"] .nav-inner {
  background: rgba(255,255,255,.66) !important;
  border-color: rgba(28,22,12,.07) !important;
  box-shadow: 0 8px 28px -14px rgba(72,56,26,.28) !important;
}
/* It condenses once you leave the top — in WEIGHT, not in width. The width
   change moved the wordmark and every nav link 50px left mid-scroll. */
body.scrolled .nav-inner {
  background: rgba(12,12,16,.78) !important;
  box-shadow: 0 10px 34px -12px rgba(0,0,0,.75) !important;
}
:root[data-theme="light"] body.scrolled .nav-inner { background: rgba(255,255,255,.85) !important; }
/* nav contents follow the theme instead of being forced light on both */
:root[data-theme="light"] .nav .wordmark { color: #17140e; }
:root[data-theme="light"] .nav .nav-links a { color: rgba(40,32,18,.72); }
:root[data-theme="light"] .nav .nav-links a:hover { color: #17140e; }
:root[data-theme="light"] .nav .btn-quiet { color: rgba(40,32,18,.85); }
:root[data-theme="light"] .nav .theme-toggle { color: rgba(40,32,18,.8); background: rgba(28,22,12,.05); border-color: rgba(28,22,12,.1); }


/* ── 11. FLOURISH: amber gradient on key headline words ───────────────────── */
.hero h1 em, h2 .hl, .grow-head h2 .hl, .section-head h2 .hl {
  background: linear-gradient(100deg, #f0a020 16%, #ffd694 52%, #f0a020 86%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent; color: transparent;
}

/* ── 12. FLOURISH: glowing amber eyebrows ─────────────────────────────────── */
body .kicker {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 14px; border-radius: 999px;
  background: rgba(245,166,35,.10);
  border: 1px solid rgba(245,166,35,.26);
  box-shadow: 0 0 22px -6px rgba(245,166,35,.42), inset 0 1px 0 rgba(255,255,255,.06);
  color: var(--accent-text, var(--accent));
  font-size: 11.5px; font-weight: 700; letter-spacing: .13em; text-transform: uppercase;
}
body .kicker::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 8px 1px rgba(245,166,35,.8);
}
:root[data-theme="light"] body .kicker { background: rgba(245,166,35,.14); border-color: rgba(196,120,10,.3); }

/* ── 13. FLOURISH: calm scroll reveals ────────────────────────────────────── */
/* NOT done in CSS. The reveals are driven by GSAP (`gsap.from` + clearProps in
   torquevid-motion.js), so the elements are VISIBLE at rest and GSAP animates them
   from transparent. A CSS `[data-reveal] > * { opacity: 0 }` rule would fight that
   and — if GSAP or ScrollTrigger ever failed to load — would leave every section
   permanently invisible with no way to recover. The calm tuning lives in
   torquevid-motion.js instead (smaller rise, longer glide). */

/* ── 14. RHYTHM + BUTTONS ─────────────────────────────────────────────────── */
.btn, .btn-accent, .cta-btn { border-radius: 999px !important; }
.btn-accent {
  box-shadow: 0 8px 22px -10px rgba(245,166,35,.75), inset 0 1px 0 rgba(255,255,255,.28) !important;
}
.btn-accent:hover { box-shadow: 0 12px 30px -10px rgba(245,166,35,.9), inset 0 1px 0 rgba(255,255,255,.35) !important; }
/* the glows sit BEHIND the grain and the content, never over them */
.glow { z-index: 0 !important; }

/* ── 15. NESTED SURFACES + the hero prompt box ────────────────────────────────
   Two things the first wave-2 pass got wrong, caught on review:
   (a) the hero prompt box was not in the card list, so it stayed a flat dark
       rectangle while everything around it lifted;
   (b) a panel nested INSIDE a lifted card (a thumbnail well, a code/preview box)
       inherited the same colour as its parent and read as an empty placeholder.
       Nested things must RECESS, not match. */
body .prompt-box {
  background: var(--surf-1) !important;
  border: 1px solid var(--surf-line) !important;
  border-radius: 20px !important;
  box-shadow: var(--card-sh) !important;
}
body .prompt-box:focus-within {
  border-color: rgba(245,166,35,.42) !important;
  box-shadow: var(--card-sh), 0 0 0 3px rgba(245,166,35,.12) !important;
}
:root:not([data-theme="light"]) { --surf-recess: rgba(0,0,0,.28); }
:root[data-theme="light"]       { --surf-recess: rgba(24,20,12,.045); }
body .pc-thumb, body .pc-shot, body .gd-chart {
  background: var(--surf-recess) !important;
  border-radius: 14px !important;
}

/* ── 16. SECTION RHYTHM ───────────────────────────────────────────────────────
   The hero had ~90px of dead air under the prompt box before the next section.
   Tighten the hero's tail so the page starts moving sooner. */
body .hero { padding-bottom: 56px !important; }

/* ── 17. NO CARD-IN-CARD ──────────────────────────────────────────────────────
   The FAQ shell was a lifted card holding six more lifted cards, so the items had
   nothing to sit against and the whole block read flat. The shell recesses; only
   the items are objects. Same rule anywhere a panel wraps its own cards. */
body .faq-wrap {
  background: transparent !important;
  border: 1px solid var(--surf-line) !important;
  border-radius: 26px !important;
  box-shadow: none !important;
}
/* Wave 22: this composited to rgb(4,4,5) — DARKER than the page it sits on,
   so the FAQ container read as a hole punched in the page rather than a panel
   resting on it. The opaque panel token is the same black, one step up. */
:root:not([data-theme="light"]) body .faq-wrap { background: var(--surface-solid) !important; }
:root[data-theme="light"] body .faq-wrap { background: rgba(24,20,12,.025) !important; }

/* ── 18. THE SIDEBAR JOINS THE PAGE ───────────────────────────────────────────
   Her image 1: the app sidebar was still the old translucent NAVY glass
   (linear-gradient(rgba(15,18,36,.66) → rgba(8,10,22,.52)) + blur) while the rest
   of the site went flat, so it read as a leftover panel bolted onto a new page.
   It is now the same flat surface as the background, separated by a hairline only. */
body .sidebar {
  /* 27 Aug: `--page-base` WAS the background when the background was one flat
     colour. Now that the page carries blobs, shafts and a travelling wash, the
     flat fill reads as a panel bolted on again — the exact thing she flagged.
     Transparent is the literal answer to "the same colour as the background":
     the ambient runs straight through it. Safe because nothing scrolls beneath
     a fixed left column (the topbar keeps its fill — content DOES scroll under it). */
  background: transparent !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border-right: 1px solid var(--surf-line) !important;
  box-shadow: none !important;
}

/* …but ONLY while it is a fixed column. At <=768px index.html turns the sidebar
   into a slide-in DRAWER that floats ON TOP of the page content, so transparent
   there would let the page read straight through the nav. Give the drawer a solid
   fill back. */
@media (max-width: 768px) {
  body .sidebar { background: var(--page-base) !important; }
}
body .topbar {
  background: var(--page-base) !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
  border-bottom: 1px solid var(--surf-line) !important;
}


/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — REDESIGN WAVE 3
   Her quiz: disclaimers → /evidence · giant outlined BETA behind the hero (the
   ONLY cursor-reactive thing) · motion feel = "calm luxury, never bouncy".
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 19. THE BETA WORD ────────────────────────────────────────────────────────
   A hollow outlined word far behind the headline. The chromatic split is two
   offset copies (::before cyan, ::after magenta) under the white outline — the
   same trick as her reference, done in CSS so there is no image to load. The
   offsets are driven by --bx/--by, which the pointer nudges by a few px. */
.beta-stage {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  display: grid; place-items: center; overflow: hidden;
}
.beta-word {
  position: relative;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(120px, 27vw, 400px);
  line-height: .8;
  letter-spacing: -.04em;
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(255,255,255,.11);
  transform: translate3d(calc(var(--bx, 0px) * .35), calc(var(--by, 0px) * .35), 0);
  /* ★ NO transition on this transform. The follower damps the value itself on
     every frame; a transition on top of that starts a new 1.1s glide per frame
     and the word ends up a second behind the cursor — which is exactly how it
     read when you scrolled (4 Sep). */
  will-change: transform;
  user-select: none;
}
/* the two colour-split ghosts */
.beta-word::before, .beta-word::after {
  content: attr(data-word);
  position: absolute; inset: 0;
  -webkit-text-stroke: 1.5px currentColor;
  color: transparent;
  transition: opacity .6s ease;   /* transform is damped in JS — see .beta-word */
}
.beta-word::before {
  -webkit-text-stroke-color: rgba(90, 200, 255, .16);
  transform: translate3d(calc(-3px + var(--bx, 0px) * -1), calc(var(--by, 0px) * -.6), 0);
}
.beta-word::after {
  -webkit-text-stroke-color: rgba(245, 120, 60, .18);
  transform: translate3d(calc(3px + var(--bx, 0px)), calc(var(--by, 0px) * .6), 0);
}
:root[data-theme="light"] .beta-word { -webkit-text-stroke-color: rgba(40,32,18,.10); }
:root[data-theme="light"] .beta-word::before { -webkit-text-stroke-color: rgba(40,120,190,.13); }
:root[data-theme="light"] .beta-word::after  { -webkit-text-stroke-color: rgba(220,110,40,.15); }
/* the readable beta line keeps its pill, just quieter now the word carries the drama */
.hero .beta-line { position: relative; z-index: 2; }
@media (prefers-reduced-motion: reduce) {
  .beta-word, .beta-word::before, .beta-word::after { transition: none; }
}
@media (max-width: 700px) { .beta-word { font-size: clamp(96px, 34vw, 200px); } }

/* ── 20. CALM LUXURY: ONE MOTION LANGUAGE ─────────────────────────────────────
   Her pick. Long, soft, expo-style eases; nothing overshoots; nothing is fast.
   These variables are used by every transition below so the whole site shares one
   curve instead of nine hand-written ones. */
:root {
  --e-calm: cubic-bezier(.22, .61, .36, 1);   /* the house curve — long glide to rest */
  --e-soft: cubic-bezier(.33, 1, .68, 1);     /* for small state flips */
  --t-quick: .28s;
  --t-base:  .45s;
  --t-slow:  .8s;
}
@media (prefers-reduced-motion: no-preference) {
  /* Interactive surfaces: one shared, unhurried response. */
  body .btn, body .btn-accent, body .btn-ghost, body .btn-quiet,
  body .home-card, body .pcard, body .faq-item, body .feature, body .plan,
  body .vg-card, body .bx, body .value-card, body .gd-card, body .cr-call,
  body .nav-links a, body .theme-toggle, body .kicker, body .sc-plan, body .gd-r,
  body .bc-opt, body .nav-item, body .prompt-box {
    transition:
      background-color var(--t-base) var(--e-calm),
      background var(--t-base) var(--e-calm),
      border-color var(--t-base) var(--e-calm),
      box-shadow var(--t-base) var(--e-calm),
      color var(--t-quick) var(--e-soft),
      transform var(--t-base) var(--e-calm),
      opacity var(--t-base) var(--e-calm);
  }
  /* Lift is small and slow — the difference between premium and springy. */
  body .home-card:hover, body .pcard:hover, body .feature:hover,
  body .plan:hover, body .value-card:hover { transform: translateY(-3px); }
  body .btn-accent:hover, body .btn:hover { transform: translateY(-2px); }
  body .btn:active, body .btn-accent:active { transform: translateY(0); transition-duration: .09s; }
  /* Links draw their underline instead of snapping it on. */
  body .nav-links a { position: relative; }
  body .nav-links a::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -5px; height: 1.5px;
    background: var(--accent); border-radius: 2px;
    transform: scaleX(0); transform-origin: left;
    transition: transform var(--t-base) var(--e-calm);
  }
  body .nav-links a:hover::after { transform: scaleX(1); }
  /* Accordions open on a real curve rather than jumping. */
  body .faq-item .faq-a { animation: faqCalm var(--t-slow) var(--e-calm) both; }
  @keyframes faqCalm { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
  /* The glows breathe almost imperceptibly — the only ambient motion left, and it
     is slow enough to read as light rather than animation. */
  .glow { animation: glowBreathe 14s ease-in-out infinite alternate; }
  @keyframes glowBreathe { from { opacity: .82; } to { opacity: 1; } }
  /* Theme flip: colours cross-fade instead of hard-cutting. */
  body, body .sidebar, body .topbar, body .nav-inner, body .card, body .home-card {
    transition-property: background-color, background, border-color, color, box-shadow;
    transition-duration: var(--t-base);
    transition-timing-function: var(--e-calm);
  }
}
/* Focus is visible and calm — never a hard blue ring. */
body :focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
  transition: outline-offset var(--t-quick) var(--e-soft);
}
/* Smooth anchor scrolling, with the sticky nav accounted for. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
html { scroll-padding-top: 96px; }

/* the footer's quiet legal line — present and readable, never shouting */
body .footer-fine {
  margin-top: 14px; max-width: 760px;
  font-size: 11.5px; line-height: 1.6; color: var(--text-3);
}
body .footer-fine strong { color: var(--text-2); font-weight: 600; }
body .footer-fine a { color: var(--text-2); text-decoration: underline; text-underline-offset: 2px; }

/* ── 21. BETA WORD: sit it BEHIND the headline, and retire the old pill ────────
   First pass put the word in the hero's vertical centre, which on this hero lands
   ABOVE the headline (the hero also holds the sub, the button and the prompt box,
   so its centre is far below the h1). Constraining the stage to the top ~78% puts
   the word's centre on the headline, which is where "behind the title" actually is.
   And her order: the small FOUNDING BETA pill read as a stock badge — the giant word
   is the beta signal now, so the line beneath is plain type, not a pill. */
body .beta-stage { inset: 0 0 auto 0; height: 78%; min-height: 320px; }
body .hero .kicker.beta-line {
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
  color: var(--text-3) !important;
  font-size: 12px; font-weight: 600; letter-spacing: .2em;
}
body .hero .kicker.beta-line::before { display: none !important; }

/* ── 22. BETA WORD — the actual fix ───────────────────────────────────────────
   ROOT CAUSE (measured, not guessed): §2's `body .hero > * { position: relative;
   z-index: 1 }` also matched `.beta-stage`, so the stage was never absolute. It
   became a normal 320px block in the flow, which is why the word sat ABOVE the
   headline and pushed it down instead of sitting behind it.
   Measured geometry: hero top 72, height 1088; the h1's centre is 591px into the
   hero = 54%. So the stage is pinned there and the word centres on the headline. */
body .hero .beta-stage {
  position: absolute !important;
  z-index: 0 !important;
  top: 35%; left: 0; right: 0; bottom: auto;
  height: auto; min-height: 0;
  transform: translateY(-50%);
  display: grid; place-items: center;
  pointer-events: none;
}
/* the headline must sit ON TOP of the word, not merely beside it */
body .hero h1, body .hero .sub, body .hero .beta-line,
body .hero .hero-actions, body .hero .prompt-box { position: relative; z-index: 2; }

/* the word should read as LIT, like her reference — a soft bloom off the stroke,
   plus a touch more stroke presence. Still far enough back to never fight the H1. */
body .beta-word {
  -webkit-text-stroke-width: 2px;
  -webkit-text-stroke-color: rgba(255,255,255,.14);
  filter: drop-shadow(0 0 26px rgba(245,166,35,.16)) drop-shadow(0 0 60px rgba(120,150,255,.08));
}
:root[data-theme="light"] body .beta-word {
  -webkit-text-stroke-color: rgba(40,32,18,.13);
  filter: drop-shadow(0 0 26px rgba(245,166,35,.16));
}

/* ── 23. BETA WORD → THE CLOSING CTA (her call, 26 Aug) ───────────────────────
   Behind the hero headline the strokes crossed the letters: the headline got
   busier AND the word was unreadable because its middle was covered. At the bottom
   of the page there is no body copy to fight, so it goes bigger, brighter, and
   bleeds off both edges — which is where this treatment lives on real sites.
   §21/§22's hero-scoped rules no longer match anything; these replace them. */
body .final-cta { position: relative; overflow: hidden; }
body .final-cta .beta-stage {
  /* NOTE: do NOT use `inset: 0 !important` here — its !important beat the
     `top: 50%` below, so the word anchored to the CTA's top edge and the
     translateY(-50%) pushed half of it outside the section. Explicit sides only. */
  position: absolute !important;
  top: 50% !important; left: 0 !important; right: 0 !important; bottom: auto !important;
  transform: translateY(-50%) !important;
  height: auto; min-height: 0;
  z-index: 0 !important;
  display: grid; place-items: center;
  pointer-events: none;
}
body .final-cta .beta-word {
  /* bigger than the hero version — it is allowed to run past the edges here */
  font-size: clamp(160px, 34vw, 520px);
  -webkit-text-stroke-width: 2.5px;
  -webkit-text-stroke-color: rgba(255,255,255,.16);
  filter: drop-shadow(0 0 34px rgba(245,166,35,.22)) drop-shadow(0 0 90px rgba(120,150,255,.10));
}
:root[data-theme="light"] body .final-cta .beta-word {
  -webkit-text-stroke-color: rgba(40,32,18,.15);
  filter: drop-shadow(0 0 34px rgba(245,166,35,.20));
}
/* the closing content sits on top of it */
body .final-cta .final-cta-inner { position: relative; z-index: 2; }

/* the word lives INSIDE the closing card (the card is opaque — a sibling behind it
   was invisible). The card becomes the clipping frame; its content rides above. */
body .final-cta .final-cta-inner { position: relative; overflow: hidden; z-index: 1; }
body .final-cta .final-cta-inner > *:not(.beta-stage) { position: relative; z-index: 2; }

/* ── 24. ANALYTICS TAGS: never show a sliced pill ─────────────────────────────
   The AFTER face is clip-path'd to --p. At a narrow reveal (the armed 6% state) its
   "After" pill was cut through the middle and showed as an orange fragment over the
   "Before" pill — it read as a rendering glitch. The pills now fade with the reveal:
   "After" appears once there is room for it, "Before" steps aside near full reveal. */
body .bx .bx-after .bx-tag { opacity: 0; transition: opacity .25s var(--e-calm, ease); }
body .bx.bx-showafter .bx-after .bx-tag { opacity: 1; }
body .bx .bx-before .bx-tag { opacity: 1; transition: opacity .25s var(--e-calm, ease); }
body .bx.bx-hidebefore .bx-before .bx-tag { opacity: 0; }

/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — BACKGROUND SYSTEM (her quiz: grainy organic blobs + ghosted 16:9
   frames + a faint timeline/waveform rail · amber plus ONE cool accent · very
   slow drift · "minimalistic, not overloaded").

   THE RULE THAT KEEPS IT CLEAN: each motif appears only where it MEANS something.
   The timeline rail sits behind the Cutting Room (that section is a timeline); the
   waveform sits behind the narrators (that section is audio); the 16:9 frames sit
   where a finished video is the subject. Nothing is tiled everywhere, so the page
   reads composed rather than wallpapered.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── B1. BLOBS: organic, not circles, with a very slow drift ────────────────── */
.glow {
  /* an irregular silhouette reads as a real light bloom, a perfect circle reads
     as a CSS radial. The blur is heavy enough that the shape stays a suggestion. */
  border-radius: 62% 38% 54% 46% / 52% 58% 42% 48%;
  will-change: transform;
}
@media (prefers-reduced-motion: no-preference) {
  .glow { animation: blobDrift 68s ease-in-out infinite alternate, glowBreathe 14s ease-in-out infinite alternate; }
  .glow-tr { animation-duration: 82s, 16s; animation-delay: -12s, 0s; }
  .glow-bl { animation-duration: 74s, 15s; animation-delay: -30s, 0s; }
  .glow-br { animation-duration: 90s, 17s; animation-delay: -46s, 0s; }
  .glow-c  { animation-duration: 96s, 18s; animation-delay: -20s, 0s; }
}
@keyframes blobDrift {
  from { transform: translate3d(-14px, -8px, 0) scale(1); }
  to   { transform: translate3d(14px, 10px, 0) scale(1.06); }
}
/* .glow-c is centred with its own transform — give it a drift that keeps the centring */
@media (prefers-reduced-motion: no-preference) {
  .glow-c { animation-name: blobDriftC, glowBreathe; }
}
@keyframes blobDriftC {
  from { transform: translate(-50%, -50%) translate3d(-16px, -10px, 0); }
  to   { transform: translate(-50%, -50%) translate3d(16px, 12px, 0); }
}

/* ── B2. GHOSTED 16:9 VIDEO FRAMES ─────────────────────────────────────────── */
/* Deliberately abstract: an outlined 16:9 rectangle with a plain play triangle.
   It says "video" instantly and belongs to nobody — no red, no logo, nothing that
   could imply an affiliation with YouTube. */
:root { --vf-line: rgba(255,255,255,.055); }
:root[data-theme="light"] { --vf-line: rgba(40,32,18,.06); }
.vframe {
  position: absolute; z-index: 0; pointer-events: none;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--vf-line);
  border-radius: 16px;
  display: grid; place-items: center;
  will-change: transform;
}
.vframe::before {                     /* the play mark, even fainter than the frame */
  content: ""; width: 0; height: 0; opacity: .75;
  border-left: 26px solid var(--vf-line);
  border-top: 15px solid transparent;
  border-bottom: 15px solid transparent;
  margin-left: 8px;                   /* optically centres a triangle */
}
.vframe-a { width: min(440px, 42vw); right: -70px;  top: 6%;  transform: rotate(-5deg); }
.vframe-b { width: min(380px, 38vw); left:  -80px;  bottom: 4%; transform: rotate(6deg); }
@media (prefers-reduced-motion: no-preference) {
  .vframe-a { animation: vfA 78s ease-in-out infinite alternate; }
  .vframe-b { animation: vfB 92s ease-in-out infinite alternate; }
}
@keyframes vfA {
  from { transform: rotate(-5deg) translate3d(0, -10px, 0); }
  to   { transform: rotate(-3.2deg) translate3d(-12px, 12px, 0); }
}
@keyframes vfB {
  from { transform: rotate(6deg) translate3d(0, 8px, 0); }
  to   { transform: rotate(4.4deg) translate3d(10px, -10px, 0); }
}
@media (max-width: 820px) { .vframe { display: none; } }   /* no room; keep mobile clean */

/* ── B3. RAILS — a timeline and a waveform, each where it means something ──── */
:root { --rail-c: rgba(245,166,35,.13); }
:root[data-theme="light"] { --rail-c: rgba(196,120,10,.14); }
.rail {
  position: absolute; z-index: 0; pointer-events: none;
  left: 0; right: 0; height: 60px;
  -webkit-mask: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
          mask: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}
/* the editing timeline: evenly spaced ticks on a hairline */
.rail-timeline {
  background-image:
    repeating-linear-gradient(90deg, var(--rail-c) 0 1px, transparent 1px 46px),
    linear-gradient(90deg, var(--rail-c), var(--rail-c));
  background-size: 100% 13px, 100% 1px;
  background-position: 0 0, 0 12px;
  background-repeat: repeat-x;
}
/* the audio waveform: real varying bar heights, drawn once as an SVG tile */
.rail-wave {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='60' viewBox='0 0 128 60'%3E%3Cg fill='%23f5a623'%3E%3Crect x='0' y='25' width='3' height='10' rx='1.5'/%3E%3Crect x='8' y='19' width='3' height='22' rx='1.5'/%3E%3Crect x='16' y='13' width='3' height='34' rx='1.5'/%3E%3Crect x='24' y='21' width='3' height='18' rx='1.5'/%3E%3Crect x='32' y='8' width='3' height='44' rx='1.5'/%3E%3Crect x='40' y='17' width='3' height='26' rx='1.5'/%3E%3Crect x='48' y='24' width='3' height='12' rx='1.5'/%3E%3Crect x='56' y='15' width='3' height='30' rx='1.5'/%3E%3Crect x='64' y='10' width='3' height='40' rx='1.5'/%3E%3Crect x='72' y='22' width='3' height='16' rx='1.5'/%3E%3Crect x='80' y='18' width='3' height='24' rx='1.5'/%3E%3Crect x='88' y='12' width='3' height='36' rx='1.5'/%3E%3Crect x='96' y='23' width='3' height='14' rx='1.5'/%3E%3Crect x='104' y='16' width='3' height='28' rx='1.5'/%3E%3Crect x='112' y='20' width='3' height='20' rx='1.5'/%3E%3Crect x='120' y='14' width='3' height='32' rx='1.5'/%3E%3C/g%3E%3C/svg%3E");
  background-repeat: repeat-x;
  background-size: auto 100%;
  /* The SVG paints solid amber, so element opacity IS the final strength. At .5 the
     waveform was roughly 4x louder than the timeline rail (--rail-c is .13 alpha) and
     stopped reading as background. Matched to the same whisper. */
  opacity: .17;
}
:root[data-theme="light"] .rail-wave { opacity: .16; }
.rail-top { top: 46px; }
.rail-bottom { bottom: 40px; }
@media (prefers-reduced-motion: no-preference) {
  .rail { animation: railDrift 120s linear infinite; }
}
@keyframes railDrift { from { background-position-x: 0; } to { background-position-x: 256px; } }
@media (max-width: 820px) { .rail { height: 42px; opacity: .5; } }

/* ★ BACKGROUND LAYERS MUST STAY ABSOLUTE — this rule is load-bearing.
   §2 declares `body .section > *, body .hero > * { position: relative; z-index: 1 }`
   to lift section CONTENT above the glow. At (0,1,2) that also matched `.glow`
   itself (0,1,0) and turned every blob into a `position: relative` inline span with
   ZERO width — so the section glows silently painted nothing at all from the moment
   that rule shipped. The blobs, frames and rails re-assert absolute here, and are
   pinned to z-index 0 so they can never join the content stacking order. */
body .section > .glow, body .section > .vframe, body .section > .rail,
body .hero > .glow, body .hero > .vframe, body .hero > .rail,
body .final-cta > .glow {
  position: absolute !important;
  z-index: 0 !important;
}

/* ── B4. NO SIDEWAYS SCROLL FROM THE BACKGROUND ───────────────────────────────
   The blobs and frames deliberately bleed past the edges (left:-180px, right:-70px)
   so they feel like light entering the page rather than shapes placed on it. Once
   they became real boxes that bleed pushed the document wider than the viewport and
   the whole page could scroll sideways.
   `overflow-x: clip` — NOT `hidden` — is the fix: hidden creates a scroll container,
   which would make the sticky nav stick to that container instead of the viewport.
   clip just clips. */
html { overflow-x: clip; }

/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — BACKGROUND ROUND 2 (her refine pass)
   Her report: "the background is not fully all the way on all the background".
   MEASURED at 1920x1080 — she was right and it was my bug: the hero box is only
   1280px and .section only 1320px, and every ambient layer was positioned INSIDE
   those boxes. So on any viewport wider than ~1340px roughly 300px down each edge
   got no light at all, which reads as dark vertical bands. Everything ambient is
   now FULL-BLEED via `calc(50% - 50vw)`, which breaks out of the centred column at
   any width without needing a wrapper element.
   Plus, her picks: real (ghosted) thumbnails, a slow light sweep, scroll parallax,
   depth blur, and a second blob layer.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── R1. FULL BLEED ────────────────────────────────────────────────────────── */
.glow-tl { left:  calc(50% - 50vw - 140px); }
.glow-tr { right: calc(50% - 50vw - 140px); }
.glow-bl { left:  calc(50% - 50vw - 120px); }
.glow-br { right: calc(50% - 50vw - 140px); }
.glow-c  { width: min(1180px, 92vw); }
.rail    { left: calc(50% - 50vw); right: calc(50% - 50vw); }

/* ── R2. A SECOND, DEEPER BLOB LAYER (her "one more blob pair") ─────────────
   Drawn as an echo INSIDE the existing blob, so it costs no extra blurred layer:
   wider, fainter, offset — it reads as light further back rather than a 2nd shape. */
.glow::after {
  content: ""; position: absolute; inset: -38% -30% -30% -34%;
  border-radius: 48% 52% 46% 54% / 55% 45% 55% 45%;
  background: radial-gradient(circle, var(--glow-color, rgba(245,166,35,.30)), transparent 70%);
  opacity: .42;
}
@media (prefers-reduced-motion: no-preference) {
  .glow::after { animation: blobEcho 104s ease-in-out infinite alternate; }
}
@keyframes blobEcho {
  from { transform: translate3d(10px, 6px, 0) scale(1); }
  to   { transform: translate3d(-12px, -8px, 0) scale(1.09); }
}

/* ── R3. GHOSTED THUMBNAILS — real videos, texture until you reach for them ── */
.bgthumb {
  position: absolute; z-index: 0;
  display: block; width: min(430px, 34vw);
  border-radius: 16px; overflow: hidden;
  text-decoration: none; line-height: 0;
  opacity: .10;                                  /* texture, not content */
  filter: blur(3px) saturate(.55);               /* depth blur — it sits far back */
  transition: opacity .55s var(--e-calm, ease), filter .55s var(--e-calm, ease),
              transform .55s var(--e-calm, ease), box-shadow .55s var(--e-calm, ease);
  will-change: transform, opacity;
}
.bgthumb img { width: 100%; height: auto; display: block; border-radius: 16px; }
.bgthumb-r { right: calc(50% - 50vw + 24px); top: 12%; transform: rotate(-4deg); }
.bgthumb-l { left:  calc(50% - 50vw + 24px); bottom: 10%; transform: rotate(4deg); }
/* reaching for it brings it forward: sharp, lit, and obviously clickable */
.bgthumb:hover, .bgthumb:focus-visible {
  opacity: .92;
  filter: blur(0) saturate(1);
  box-shadow: 0 30px 70px -30px rgba(0,0,0,.8), 0 0 0 1px rgba(245,166,35,.35);
  z-index: 3;
}
.bgthumb-r:hover, .bgthumb-r:focus-visible { transform: rotate(0deg) scale(1.03); }
.bgthumb-l:hover, .bgthumb-l:focus-visible { transform: rotate(0deg) scale(1.03); }
.bgthumb-play {
  position: absolute; inset: 0; margin: auto;
  width: 54px; height: 54px; border-radius: 50%;
  background: rgba(12,12,16,.62); backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,.5);
  opacity: 0; transition: opacity .45s var(--e-calm, ease), transform .45s var(--e-calm, ease);
  transform: scale(.82);
}
.bgthumb-play::before {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 0; height: 0;
  border-left: 16px solid #fff;
  border-top: 10px solid transparent;
  border-bottom: 10px solid transparent;
  transform: translateX(3px);
}
.bgthumb:hover .bgthumb-play, .bgthumb:focus-visible .bgthumb-play { opacity: 1; transform: scale(1); }
:root[data-theme="light"] .bgthumb { opacity: .13; filter: blur(3px) saturate(.5); }
:root[data-theme="light"] .bgthumb:hover, :root[data-theme="light"] .bgthumb:focus-visible {
  opacity: .95; filter: blur(0) saturate(1);
  box-shadow: 0 30px 66px -32px rgba(70,55,25,.5), 0 0 0 1px rgba(196,120,10,.35);
}
@media (max-width: 900px) { .bgthumb { display: none; } }   /* no room; mobile stays clean */
@media (prefers-reduced-motion: reduce) { .bgthumb { transition: opacity .2s linear; } }

/* ── R4. SLOW LIGHT SWEEP — a studio light passing the section ──────────────── */
.sweep {
  position: absolute; z-index: 0; pointer-events: none;
  top: 0; bottom: 0; left: calc(50% - 50vw); width: 100vw;
  overflow: hidden;
}
.sweep::before {
  content: ""; position: absolute; top: -20%; bottom: -20%; width: 46%;
  background: linear-gradient(100deg, transparent, rgba(245,166,35,.075) 45%, rgba(255,255,255,.03) 60%, transparent);
  filter: blur(28px);
  transform: translateX(-80%) skewX(-12deg);
}
@media (prefers-reduced-motion: no-preference) {
  .sweep::before { animation: sweepPass 34s cubic-bezier(.45,0,.55,1) infinite; }
}
@keyframes sweepPass {
  0%   { transform: translateX(-90%) skewX(-12deg); opacity: 0; }
  12%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { transform: translateX(300%) skewX(-12deg); opacity: 0; }
}
:root[data-theme="light"] .sweep::before {
  background: linear-gradient(100deg, transparent, rgba(245,166,35,.10) 45%, rgba(255,255,255,.5) 60%, transparent);
}

/* ── R5. every ambient layer stays absolute + behind content ────────────────── */
body .section > .glow, body .section > .vframe, body .section > .rail,
body .section > .bgthumb, body .section > .sweep,
body .hero > .glow, body .hero > .vframe, body .hero > .rail,
body .hero > .bgthumb, body .hero > .sweep,
body .final-cta > .glow {
  position: absolute !important;
}
body .section > .glow, body .section > .rail, body .section > .sweep,
body .hero > .glow, body .hero > .rail, body .hero > .sweep,
body .final-cta > .glow { z-index: 0 !important; }

/* ── R6. PARALLAX PLUMBING ────────────────────────────────────────────────────
   `translate` is its OWN property, so it composes with the `transform` that the
   drift keyframes and the thumbnail rotations already own. Writing parallax into
   `transform` would have fought them (the animation would simply win). Same trick
   the old hero-aurora used. */
/* Scroll parallax on the background removed 30 Aug — see the WAVE 14 note.
   Kept as an explicit 0 rather than deleted, so any stale --par cannot move it. */
.glow, .bgthumb, .rail { translate: 0 0; }
@media (prefers-reduced-motion: reduce) { .glow, .bgthumb, .rail { translate: none; } }

/* The ambient layers deliberately overflow the viewport (full-bleed blobs plus the
   `.glow::after` echo, which reaches a further ~30% past its parent). `overflow-x:
   clip` on the ROOT alone did not stop the document growing to 2266px at 1920 wide,
   so body clips as well. `clip` (never `hidden`) on both: hidden would create a
   scroll container and the sticky nav would then stick to it instead of the viewport. */
body { overflow-x: clip; }

/* ── R7. THE STAT ON REVEAL ───────────────────────────────────────────────────
   Her ask: when a background thumbnail comes forward it should prove, with a real
   number, that this format is a genuine opportunity. The framing she picked is the
   MULTIPLE - not "big channel got big views" but "a 1-6K-subscriber channel
   out-reached its own subscriber count many times over", which is the part a
   beginner with nothing can actually relate to.
   Every figure is that video's own PUBLIC count, read from its YouTube page on
   26 Aug 2026, and each card names the channel it belongs to and links to it, so a
   number never floats free of the channel that earned it. */
.bgthumb-stat {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: block; padding: 34px 16px 13px;
  background: linear-gradient(180deg, transparent, rgba(6,6,10,.62) 42%, rgba(6,6,10,.93));
  opacity: 0; transform: translateY(8px);
  transition: opacity .45s var(--e-calm, ease), transform .45s var(--e-calm, ease);
  text-align: left; line-height: 1.25;
}
.bgthumb-stat b {
  display: block; font-family: var(--font-display, inherit);
  font-size: 25px; font-weight: 700; letter-spacing: -.03em; color: #fff;
  font-variant-numeric: tabular-nums;
}
.bgthumb-stat i {
  display: block; font-style: normal; margin-top: 3px;
  font-size: 12.5px; font-weight: 700; color: var(--accent);
}
.bgthumb-stat u {
  display: block; margin-top: 5px; text-decoration: none;
  font-size: 11px; font-weight: 600; color: rgba(255,255,255,.62);
}
.bgthumb:hover .bgthumb-stat, .bgthumb:focus-visible .bgthumb-stat { opacity: 1; transform: none; }
/* the play mark steps aside so it never sits on the number */
.bgthumb:hover .bgthumb-play, .bgthumb:focus-visible .bgthumb-play { transform: scale(1) translateY(-16px); }
@media (prefers-reduced-motion: reduce) { .bgthumb-stat { transition: opacity .2s linear; transform: none; } }

/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — SURFACE SCRATCHES (her idea: "like a scratch on a car")
   Her picks: hairlines PLUS a couple of swirl clusters · very few, near the edges
   only · and they GLINT as the light sweep crosses them.

   WHY IT IS BUILT THIS WAY. A scratch on paint is only visible because it catches
   light, so these sit almost invisible at rest and brighten as a moving band passes
   ALONG them — the bright copy is revealed through an animated mask, not faded as a
   whole, so the highlight actually travels the length of the scratch.

   THE RISK THIS AVOIDS: scattered marks on a screen read as dust, dead pixels or a
   dirty display rather than as design. So they are (a) few, (b) shallow-angled and
   grouped like real panel damage, (c) confined to outer margins and the empty
   padding between sections, never across a headline or a card, and (d) hidden
   entirely on narrow screens where there is no margin to hold them.
   ═══════════════════════════════════════════════════════════════════════════ */
:root {
  /* dark paint: a scratch cuts through to a lighter layer, so it reads brighter */
  --scr-o: .16;          /* at rest */
  --scr-glint: .5;       /* while the light is on it */
}
/* light paint: a scratch reads DARKER than the surface — her "gray scratches" */
:root[data-theme="light"] {
}
/* Below ~1180px the column fills the viewport, so there is no margin to hold a
   scratch without it crossing copy. They simply do not exist there. */

/* ★ Scratches join the ambient-layer contract. §2's `body .section > *` /
   `body .hero > *` rule (position:relative; z-index:1, to lift CONTENT above the
   glow) also matches these, and would turn each scratch into a relative, in-flow
   span — the exact failure that silently killed the section glows for three waves.
   Re-assert absolute + behind. Anything ambient added here in future needs this. */
body .section > .scratch, body .hero > .scratch, body .final-cta > 
/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — ROUND 4: the landing's background becomes the DEFAULT everywhere,
   the headline grows 10%, and the page stops feeling dead.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── D1. HEADLINE +10% (her measure) ──────────────────────────────────────── */
/* It rendered 70px at 1920 (the >=1400px rule's clamp ceiling). +10% = 77px, and
   the floor/vw scale up by the same factor so it grows proportionally everywhere. */
body .hero h1 { font-size: clamp(42px, 7.9vw, 77px); }

/* ── D2. THE DEFAULT BACKGROUND, ON EVERY PAGE ────────────────────────────────
   Her call: the landing hero's background is now the site-wide default. It used to
   be assembled from per-section .glow elements that only the landing has, so every
   other page got the flat base and nothing else. It is now ONE fixed ambient plate
   painted on `body::before`, so /app, /plans, /login, /account, /legal — everything
   — inherits the same light. (§ earlier killed body::before along with the old grid
   sheen; this deliberately revives it for a different job, hence the display reset.) */
body::before {
  display: block !important;
  content: "";
  position: fixed; inset: 0;
  z-index: -1;                      /* behind everything, including page content */
  pointer-events: none;
  background:
    radial-gradient(1200px 780px at 4% 6%,   rgba(245,166,35,.20), transparent 62%),
    radial-gradient(1000px 700px at 98% 88%, rgba(120,150,255,.13), transparent 60%),
    radial-gradient(1100px 460px at 50% -8%, rgba(245,166,35,.075), transparent 68%);
  /* the whole plate drifts through warmer/cooler over a minute — "alive" without
     anything actually moving (her pick) */
  animation: ambientDrift 74s ease-in-out infinite alternate;
  will-change: filter;
}
:root[data-theme="light"] body::before {
  background:
    radial-gradient(1200px 780px at 4% 6%,   rgba(245,166,35,.30), transparent 62%),
    radial-gradient(1000px 700px at 98% 88%, rgba(120,150,255,.14), transparent 60%),
    radial-gradient(1100px 460px at 50% -8%, rgba(245,166,35,.12), transparent 68%);
}
@keyframes ambientDrift {
  from { filter: hue-rotate(-6deg) saturate(.96) brightness(.97); }
  to   { filter: hue-rotate(7deg)  saturate(1.06) brightness(1.05); }
}
@media (prefers-reduced-motion: reduce) { body::before { animation: none; } }


/* ── D4. THE LIGHT ANSWERS YOUR SCROLLING (her pick) ─────────────────────────
   --scrollv is written by torquevid-motion.js: 0 at rest, up to 1 when moving fast.
   The ambient brightens and the blobs stretch a little, then settle. Nothing jumps —
   the transition is long enough that it reads as momentum, not a flash. */
body::before {
  transition: opacity .9s var(--e-calm, ease);
  opacity: calc(1 + (var(--scrollv, 0) * .22));
}
.glow {
  transition: transform .9s var(--e-calm, ease), opacity .9s var(--e-calm, ease);
}
@media (prefers-reduced-motion: no-preference) {
  .glow { scale: calc(1 + (var(--scrollv, 0) * .05)); }
}

/* ── D5. AN OCCASIONAL PASSING HIGHLIGHT (her pick) ──────────────────────────
   Rare on purpose: a wide soft band crosses the whole viewport roughly every 96s, so
   it lands as an event rather than a loop you start waiting for. Fixed to the
   viewport, behind content, never interactive. */
.passlight {
  position: fixed; inset: -20% -40%; z-index: -1; pointer-events: none;
  background: linear-gradient(104deg, transparent 42%, rgba(255,224,178,.055) 49%,
              rgba(255,255,255,.03) 52%, transparent 60%);
  filter: blur(46px);
  transform: translateX(-70%);
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .passlight { animation: passLight 96s cubic-bezier(.4,0,.6,1) infinite; }
}
@keyframes passLight {
  0%, 6%   { transform: translateX(-70%); opacity: 0; }
  14%      { opacity: 1; }
  30%      { opacity: 1; }
  40%      { transform: translateX(70%); opacity: 0; }
  100%     { transform: translateX(70%); opacity: 0; }
}
:root[data-theme="light"] .passlight {
  background: linear-gradient(104deg, transparent 42%, rgba(255,255,255,.5) 49%,
              rgba(255,236,205,.35) 52%, transparent 60%);
}

/* ── D6. SCRATCHES v2 — paint damage, not a scratched monitor ─────────────────
   Her verdict on v1: it looked like a scratched monitor, and was too small. She was
   right, and the reason is physical rather than cosmetic:
     * a real scratch has TWO edges — a bright CATCH where light hits the lip, and a
       dark GROOVE beside it. A single flat line carries no depth cue, so the brain
       reads it as a mark ON the glass. Every scratch below is drawn twice, ~1px apart.
     * real scratches TAPER — they begin and end at nothing. Each stroke is painted
       with a gradient that fades to zero at both ends, so none of them starts abruptly.
     * they arrive in FAMILIES — a hand, a branch or a polisher leaves 2-3 roughly
       parallel marks of differing depth, never one isolated line.
     * and they are LONG: 900x260 art instead of 400x120.
   ── */
:root {
}
/* On pale paint the roles SWAP: the groove is the dark stroke and the catch is a
   faint white lip. That is why light needs its own art, not just a lower opacity. */
:root[data-theme="light"] {
}
.scr-line  { width: 900px; height: 260px; }
.scr-swirl { width: 420px; height: 420px; }

/* Re-placed for the bigger art. scr-6 and scr-2 previously landed 171px apart on the
   same side (her image 2 — "two scratches next to each other"); they are now in
   different sections on opposite sides, and every pair is checked by the harness. */
/* Offsets found by MEASUREMENT, not taste. With the art at 900x260 the harness swept
   offsets against every glyph rect on the page:
       -0px   -> 33 overlaps            -320px -> 11 overlaps
       -520px ->  0 overlaps, 395px still visible   <- chosen
       -660px ->  0 overlaps, only 255px visible
   So each scratch runs OFF the screen edge with ~395px on-page: the longest a scratch
   can be here without ever touching a word — and running off the edge is what a real
   one does anyway. Shrinking the art instead only ever lost visible length. */
.scr-1 { top: 90px;    left:  calc(50% - 50vw - 520px); transform: rotate(-9deg); }
.scr-2 { top: 30px;    left:  calc(50% - 50vw - 520px); transform: rotate(-5deg); }
.scr-3 { top: 40px;    right: calc(50% - 50vw - 520px); transform: rotate(-6deg); }
.scr-4 { top: 26px;    left:  calc(50% - 50vw - 286px); transform: rotate(11deg); }
.scr-5 { top: 56px;    left:  calc(50% - 50vw - 286px); transform: rotate(-14deg); }
.scr-6 { bottom: 20px; right: calc(50% - 50vw - 520px); transform: rotate(7deg); }

/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — ROUND 5: NEVER PURE BLACK (or pure white), ANYWHERE
   Her report: "it goes from the background being colourful with the amber to
   straight up just black", plus a visible box edge near the team section.

   ★ DIAGNOSIS (from a real capture, not a guess): the ambient was TWO SMALL BLOBS
   pinned to the viewport corners — amber top-left, cool bottom-right — so the entire
   middle and right of the screen was sitting on the raw base colour, which was
   #0b0b0f, i.e. effectively black. And because the plate was `position: fixed`, the
   SAME corner stayed lit while everything scrolled past it, so the page read as one
   permanently-lit corner attached to a dead black field. The "box" she saw was that
   single amber blob's falloff ending against a section boundary.

   ★ HER FIX (quiz): a fixed base wash so no region is ever black, PLUS accents that
   TRAVEL with the page so no two screenfuls are lit the same way, on a noticeably
   lifted and tinted floor.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── E1. THE FLOOR IS NO LONGER BLACK ─────────────────────────────────────── */
:root,
:root:not([data-theme="light"]) {
  /* was #0b0b0f (essentially black). A blue-grey charcoal: still deep and premium,
     but it is a COLOUR, not an absence of one. White body text measures ~14:1 here,
     so nothing loses legibility — checked, not assumed. */
  --page-base: #14151c;
}
:root[data-theme="light"] {
  /* warm paper, never a blank white sheet */
  --page-base: #f4f1ea;
}

/* ── E2. THE FIXED BASE WASH — full viewport, no dead quadrant ─────────────
   Five large overlapping washes instead of two corner blobs, so every region of the
   screen carries some tone and no single gradient's edge is ever visible on its own.
   Low alpha on purpose: this is the floor, not the feature. */
body::before {
  display: block !important;
  content: "";
  position: fixed; inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(1500px 1000px at 2% 4%,    rgba(245,166,35,.22), transparent 66%),
    radial-gradient(1300px 900px  at 99% 92%,  rgba(120,150,255,.17), transparent 64%),
    radial-gradient(1600px 900px  at 96% 6%,   rgba(255,180,90,.10), transparent 62%),
    radial-gradient(1400px 950px  at 4% 98%,   rgba(150,130,255,.10), transparent 62%),
    radial-gradient(1700px 1200px at 50% 50%,  rgba(120,140,190,.09), transparent 72%);
  animation: ambientDrift 74s ease-in-out infinite alternate;
  will-change: filter;
  transition: opacity .9s var(--e-calm, ease);
  opacity: calc(1 + (var(--scrollv, 0) * .22));
}
:root[data-theme="light"] body::before {
  background:
    radial-gradient(1500px 1000px at 2% 4%,    rgba(245,166,35,.26), transparent 66%),
    radial-gradient(1300px 900px  at 99% 92%,  rgba(120,150,255,.13), transparent 64%),
    radial-gradient(1600px 900px  at 96% 6%,   rgba(255,190,110,.13), transparent 62%),
    radial-gradient(1400px 950px  at 4% 98%,   rgba(149,174,243,.09), transparent 62%),
    radial-gradient(1700px 1200px at 50% 50%,  rgba(190,175,150,.10), transparent 72%);
}

/* ── E3. TRAVELLING LIGHT — belongs to the PAGE, not the screen ─────────────
   Painted on <body>, whose box is the whole document, so these washes sit at fixed
   points DOWN THE PAGE and scroll past with the content. That is what stops every
   screenful looking identical. `background-attachment: scroll` is the whole point —
   `fixed` here would recreate the exact problem this replaces. */
body {
  background-color: var(--page-base) !important;
  /* Alphas raised after a pixel check: at .07-.08 over a #14151c floor the washes were
     mathematically present but visually invisible — the page still read as flat black.
     These are the values where colour actually reads on screen, and body text still
     measures ~15:1 against the darkest point, so nothing is traded for it. */
  background-image:
    radial-gradient(1100px 820px at 88% 6%,  rgba(245,166,35,.17),  transparent 62%),
    radial-gradient(1000px 780px at 6% 24%,  rgba(110,145,255,.15), transparent 60%),
    radial-gradient(1200px 900px at 92% 44%, rgba(245,166,35,.15),  transparent 62%),
    radial-gradient(1000px 820px at 10% 62%, rgba(150,125,255,.13), transparent 60%),
    radial-gradient(1200px 880px at 86% 82%, rgba(245,166,35,.16),  transparent 62%),
    radial-gradient(1100px 820px at 12% 96%, rgba(110,145,255,.14), transparent 60%) !important;
  background-attachment: scroll !important;
  background-repeat: no-repeat !important;
}
:root[data-theme="light"] body {
  background-image:
    radial-gradient(1100px 820px at 88% 6%,  rgba(245,166,35,.16), transparent 60%),
    radial-gradient(1000px 780px at 6% 24%,  rgba(120,150,255,.10), transparent 58%),
    radial-gradient(1200px 900px at 92% 44%, rgba(245,166,35,.14), transparent 60%),
    radial-gradient(1000px 820px at 10% 62%, rgba(149,174,243,.09), transparent 58%),
    radial-gradient(1200px 880px at 86% 82%, rgba(245,166,35,.15), transparent 60%),
    radial-gradient(1100px 820px at 12% 96%, rgba(120,150,255,.09), transparent 58%) !important;
}

/* Surfaces lift with the floor so cards stay objects ON a page rather than holes in
   it — the lift, not the colour, is what makes a dark UI read as designed. */
:root:not([data-theme="light"]) {
  --surf-1: #1e2029;
  --surf-2: #262833;
  --card-bg: #1e2029;
}

/* ═══════════════════════════════════════════════════════════════════════════
   26 AUG — ROUND 6: kill the moving light's BOX, and deepen the default
   background everywhere (her picks: a third deeper blob layer + soft light
   shafts, on the DEFAULT background so every page gets them).
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── F1. THE BOX AROUND THE MOVING LIGHT — found and fixed ──────────────────
   She could see a box ONLY while the light was travelling, and nothing when it
   was still. That is exactly what this was:

     .sweep      position:absolute; top:0; bottom:0   (i.e. its SECTION's height)
                 overflow: hidden;                    <-- the culprit
     .sweep::before  top:-20%; bottom:-20%            (drawn taller ON PURPOSE so
                                                       its ends would never show)

   The band was deliberately drawn taller than the section so it had no visible
   ends — and `overflow: hidden` then cut it straight back off at the section's
   exact top and bottom. So a moving bright shape carried two perfectly straight
   horizontal cuts: a box. With nothing moving there was no bright shape to reveal
   the cut, which is why the page looked clean at rest.

   The clip was only ever there to stop sideways overflow, and that is handled at
   html+body level now. So: no clip, and the band is feathered on ALL FOUR edges so
   it can never present a hard edge in any direction. */
.sweep {
  overflow: visible !important;
  top: -30% !important; bottom: -30% !important;      /* reach past the section too */
}
.sweep::before {
  top: 0 !important; bottom: 0 !important;
  /* feather every edge — the band now fades out rather than being cut off */
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%),
    linear-gradient(to right,  transparent 0%, #000 14%, #000 86%, transparent 100%);
          mask-image:
    linear-gradient(to bottom, transparent 0%, #000 22%, #000 78%, transparent 100%),
    linear-gradient(to right,  transparent 0%, #000 14%, #000 86%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
  filter: blur(40px);                                  /* softer, so no edge can read */
}

/* The passing highlight gets the same treatment — it is fixed and already bleeds
   past the viewport, but a feathered edge costs nothing and removes any doubt. */
.passlight {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 18%, #000 82%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0%, #000 18%, #000 82%, transparent 100%);
}

/* ── F2. A THIRD, DEEPER BLOB LAYER — on the DEFAULT background ─────────────
   Her call: this belongs to the default background, so every page gets it, not
   just the landing. A real fixed element, NOT `html::before` — the pseudo route was
   tried first at z-index -2 and proved invisible (see the paint-order note below).
   Bigger, slower and fainter than the base wash, drifting on a different cycle, so
   the light reads as having distance in it rather than being one flat sheet. */
.bgdeep {
  content: "";
  position: fixed; inset: -12%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(1800px 1300px at 18% 82%, rgba(245,166,35,.10), transparent 64%),
    radial-gradient(1700px 1250px at 82% 18%, rgba(120,150,255,.09), transparent 64%),
    radial-gradient(2000px 1400px at 62% 68%, rgba(190,120,255,.055), transparent 68%);
  animation: deepDrift 122s ease-in-out infinite alternate;
  will-change: transform;
}
:root[data-theme="light"] .bgdeep {
  background:
    radial-gradient(1800px 1300px at 18% 82%, rgba(245,166,35,.16), transparent 64%),
    radial-gradient(1700px 1250px at 82% 18%, rgba(120,150,255,.11), transparent 64%),
    radial-gradient(2000px 1400px at 62% 68%, rgba(159,182,244,.08), transparent 68%);
}
@keyframes deepDrift {
  from { transform: translate3d(-26px, -18px, 0) scale(1); }
  to   { transform: translate3d(26px, 20px, 0) scale(1.07); }
}

/* ── F3. SOFT LIGHT SHAFTS — also on the default background ─────────────────
   Faint angled beams, like light through a window. Heavily blurred so the
   repeating gradient never bands, and drifting slowly enough that you would have
   to stare to catch it moving. Same deal: a real element on every page. */
.bgshaft {
  content: "";
  position: fixed; inset: -25%;
  z-index: -1;
  pointer-events: none;
  background: repeating-linear-gradient(104deg,
    transparent 0 170px,
    rgba(255,226,180,.030) 170px 250px,
    transparent 250px 430px);
  filter: blur(34px);
  opacity: .85;
  animation: shaftDrift 96s linear infinite;
  will-change: transform;
}
:root[data-theme="light"] .bgshaft {
  background: repeating-linear-gradient(104deg,
    transparent 0 170px,
    rgba(255,214,150,.055) 170px 250px,
    transparent 250px 430px);
  opacity: .7;
}
@keyframes shaftDrift {
  from { transform: translate3d(-220px, 0, 0); }
  to   { transform: translate3d(220px, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .bgdeep, .bgshaft { animation: none; }
}

/* ── F4. THE PAINT-ORDER BUG THAT MADE AMBIENT LAYERS INVISIBLE ───────────────
   PROVED IN PIXELS: `.bgdeep`/`.bgshaft` (and by the same rule `body::before` and
   `.passlight`) rendered with artwork, correct size and running animations — and
   painted NOTHING. Toggling them changed mean background luminance by 0.18, i.e.
   noise.

   WHY. `body` is not a stacking context, so a negative-z-index child of body joins
   the ROOT's stacking context. Paint order there is:
       1. the root element's background
       2. negative-z-index descendants      <- our ambient layers
       3. in-flow block descendants          <- BODY, including its own background
   So body's opaque `background-color` painted straight over every one of them.

   FIX: the base colour belongs on `html`, not `body`. Then html paints the floor at
   step 1, the ambient layers paint at step 2, and body — now transparent — no longer
   covers them. Body keeps its travelling background-IMAGE, which composites above.
   Do not move the base colour back to body: it silently blanks the whole ambient. */
html { background-color: var(--page-base); }
body { background-color: transparent !important; }

/* ═══════════════════════════════════════════════════════════════════════════
   27 AUG — WAVE 12: CLEAR THE DARK FOG · UI THAT READS AS UI
   DARK ONLY. The light theme is deliberately untouched — she likes it as it is.

   Measured before changing anything (fogaudit, background-only sampling):
     all layers on -> mean 51.7, darkest 1% = 38.9, 66% of the page bunched in
     one mid band, ZERO pixels below L=12. There was no dark anywhere in the
     "dark" theme — that flatness is what reads as fog.
   Contribution when each layer is switched off:
     base wash -8.1 · section glows -5.7 · deep blobs -4.2 · travelling -3.5
     ... and FILM GRAIN -0.9. The grain was never the problem; it moved the
     page by 0.9 of a ~30-point lift. The problem was FOURTEEN large radials
     (6 travelling + 5 fixed + 3 deep) each adding a little to every pixel.
   Switching the deep blob layer off nearly DOUBLED the contrast range
     (20.8 -> 49.8), i.e. it was actively flattening the picture.

   The reference reads premium because it has DEAD QUADRANTS — genuinely
   near-black areas for one strong bloom to pop off. Global haze is the exact
   opposite. So: deep floor, fewer blooms, each one LOCAL and steep instead of
   broad and gentle. Her call was to KEEP the deep blobs and the shafts and
   make them good — so they are rebuilt as local light, not as a veil.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── W12-1. THE FLOOR GOES DEEP ─────────────────────────────────────────────
   #14151c (L=21) -> #0a0b0e (L=11). Still a colour, not an absence of one, and
   still not pure black — but now there is somewhere for light to fall FROM. */
:root:not([data-theme="light"]) { --page-base: #0a0b0e; }

/* ── W12-2. THE FIXED BASE WASH — 5 broad washes become 2 real blooms ───────
   The old set deliberately had "no dead quadrant", which is precisely the note
   that produced the fog. Two corner blooms now, with a STEEP falloff: bright
   core, fast decay, a tail that is genuinely zero rather than a long ramp. */
:root:not([data-theme="light"]) body::before {
  background:
    radial-gradient(1250px 900px at 0% 0%,
      rgba(245,166,35,.34)  0%,
      rgba(245,166,35,.155) 17%,
      rgba(245,166,35,.052) 34%,
      rgba(245,166,35,.011) 52%,
      transparent 66%),
    radial-gradient(1150px 850px at 100% 100%,
      rgba(108,140,255,.26) 0%,
      rgba(108,140,255,.115) 18%,
      rgba(108,140,255,.038) 36%,
      rgba(108,140,255,.009) 54%,
      transparent 68%);
}

/* ── W12-3. THE TRAVELLING LIGHT — same six stations, half the haze ─────────
   Keeping six points down the document is what stops every screenful looking
   identical, so the POSITIONS stay. What changes is the shape: smaller, steeper
   and roughly 40% weaker, so between them the page actually goes dark. */
:root:not([data-theme="light"]) body {
  background-image:
    radial-gradient(820px 620px at 90% 6%,  rgba(245,166,35,.20) 0%,  rgba(245,166,35,.07) 26%,  rgba(245,166,35,.014) 48%, transparent 64%),
    radial-gradient(760px 580px at 5% 24%,  rgba(108,140,255,.17) 0%, rgba(108,140,255,.055) 27%, rgba(108,140,255,.011) 49%, transparent 64%),
    radial-gradient(880px 660px at 94% 44%, rgba(245,166,35,.18) 0%,  rgba(245,166,35,.062) 26%, rgba(245,166,35,.012) 48%, transparent 64%),
    radial-gradient(760px 600px at 8% 62%,  rgba(131,160,241,.15) 0%, rgba(131,160,241,.05) 27%,  rgba(131,160,241,.010) 49%, transparent 64%),
    radial-gradient(860px 640px at 88% 82%, rgba(245,166,35,.19) 0%,  rgba(245,166,35,.065) 26%, rgba(245,166,35,.013) 48%, transparent 64%),
    radial-gradient(800px 620px at 10% 96%, rgba(108,140,255,.16) 0%, rgba(108,140,255,.052) 27%, rgba(108,140,255,.010) 49%, transparent 64%)
    !important;
}

/* ── W12-4. THE DEEP LAYER — kept, rebuilt as DEPTH instead of a veil ───────
   It was three 1800-2000px radials covering the whole viewport, which is why
   removing it doubled the contrast range: a uniform lift cannot create depth,
   it can only remove it. Now two tight, corner-anchored blooms with a hard core
   and a real zero, drifting against each other on a different clock — so it
   reads as something BEHIND the page rather than a wash on top of it. */
:root:not([data-theme="light"]) .bgdeep {
  background:
    radial-gradient(900px 720px at 12% 96%,
      rgba(245,166,35,.20) 0%, rgba(245,166,35,.07) 20%,
      rgba(245,166,35,.018) 38%, transparent 55%),
    radial-gradient(840px 680px at 90% 8%,
      rgba(120,150,255,.17) 0%, rgba(120,150,255,.06) 21%,
      rgba(120,150,255,.015) 39%, transparent 56%);
}

/* ── W12-5. THE SHAFTS — kept, rebuilt as light through a window ────────────
   Two faults made them read as haze: `repeating-linear-gradient` gives perfectly
   even beams (mechanical — the AI-slop tell), and covering the whole viewport at
   blur(34px) turns beams into fog. Real shafts are IRREGULAR and they land in
   ONE PLACE. So: five beams of deliberately different widths and strengths, a
   tighter blur so an edge can actually read, and a soft radial mask that keeps
   them inside the top-right quadrant — the rest of the page stays clean. */
:root:not([data-theme="light"]) .bgshaft {
  /* ★ PROVED IN PIXELS: with the beams in the FIRST 45% of the gradient line and
     the mask centred at 82% (top-right), the two never overlapped — the mask was
     zeroing exactly where the beams were, and an A/B measured maxDelta 0.00. The
     beams must live where the mask lets light through. They sit on the RIGHT now,
     which also gives the empty right half of the hero something to hold. */
  background: linear-gradient(101deg,
    transparent 0 52%,
    rgba(255,232,196,.052) 52% 55.4%,
    transparent 55.4% 59%,
    rgba(255,228,190,.030) 59% 60.6%,
    transparent 60.6% 66%,
    rgba(255,235,205,.066) 66% 71.5%,
    transparent 71.5% 77%,
    rgba(255,230,195,.024) 77% 78.4%,
    transparent 78.4% 84%,
    rgba(255,233,200,.044) 84% 88%,
    transparent 88% 100%);
  filter: blur(22px);
  opacity: .9;
  -webkit-mask-image: radial-gradient(1500px 1100px at 82% 4%, #000 0%, rgba(0,0,0,.72) 30%, rgba(0,0,0,.26) 52%, transparent 74%);
          mask-image: radial-gradient(1500px 1100px at 82% 4%, #000 0%, rgba(0,0,0,.72) 30%, rgba(0,0,0,.26) 52%, transparent 74%);
}

/* ── W12-6. NO FILM GRAIN IN DARK (her call) ────────────────────────────────
   It measured at 0.9 of the lift, so this is not the fog fix — it is the last
   veil coming off a surface that is now clean. Light keeps it: that is where
   the paper feel belongs, and she likes the light theme as it stands. */

/* ── W12-7. UI SURFACES: DARK, AND WITHOUT A COLOUR ─────────────────────────
   Her note: the sidebar had become the background, so "Channels" was hard to
   pick out. Wave 11 made it transparent because at that time the background
   WAS one flat colour — that is superseded, do not put it back.
   Two things separate a panel from the page: it is darker, and it has no hue
   while the background does. The dark card surface was rgba(34,38,58,.9) —
   a blue-violet. These are neutral (R~G~B), so the page carries all the colour
   and every clickable surface reads as an object sitting on top of it. */
:root:not([data-theme="light"]) {
  /* --surface-solid was #0c0f1e: chroma 18, a clear navy. It is the last tinted
     surface left in dark (a probe for "any large opaque dark element with chroma"
     found exactly one — the Cutting Room / Editor state card, via this token).
     Same luminance, hue removed, so nothing shifts brightness. */
  --surface-solid: #111113;
  --surf-1:    #17171a;   /* the standard card */
  --surf-2:    #1d1d21;   /* raised / hovered / nested */
  --surf-line: rgba(255,255,255,.075);
}
:root:not([data-theme="light"]) body .sidebar {
  background: #121214 !important;
  border-right: 1px solid rgba(255,255,255,.08) !important;
  box-shadow: 1px 0 0 rgba(0,0,0,.5), 6px 0 26px -18px rgba(0,0,0,.9) !important;
}
:root:not([data-theme="light"]) body .topbar {
  background: #121214 !important;
  border-bottom: 1px solid rgba(255,255,255,.08) !important;
}
/* The card surfaces, re-stated neutral.
   ★ MEASURED, not assumed — most of the families I first listed here were DEAD
   CODE. `:root:not([data-theme="light"]) body .home-card` (0,3,1) at ~2467 already
   wins over `:root:not([data-theme="light"]) .home-card` (0,3,0), whatever the
   source order, and it resolves to `var(--surf-1)`. So .home-card/.hstat/.plan/
   .value-card/.sc-card/.ex-card/.prob-card/.faq-item/.pcard/.bx are ALREADY
   neutral purely from the token above — re-listing them here would look like a
   fix while changing nothing. .plan is deliberately absent for a second reason:
   her standing order at ~1968 is that the PRICING cards keep their own surface.
   What is left is the set nothing else covers: these still resolved to the old
   blue-violet glass or to `--surface`. Same flat token as everything else, so
   every card in the app is one surface. */
:root:not([data-theme="light"]) .ob-way,
:root:not([data-theme="light"]) .obw-angle,
:root:not([data-theme="light"]) .style-opt,
:root:not([data-theme="light"]) .card,
:root:not([data-theme="light"]) .ws-panel,
:root:not([data-theme="light"]) .stat-card,
:root:not([data-theme="light"]) .hub-action {
  background: var(--surf-1) !important;
  border: 1px solid var(--surf-line) !important;
}

/* ── 27 AUG — THE SIGN-IN BETA ONLY: no cursor tracking, a torch instead ──
   SCOPED TO .login-beta. The first version of this block was unscoped and so
   changed the LANDING word too — she wants that one back exactly as it was
   (leaning toward the cursor, chromatic ghosts, no torch). Everything here now
   sits behind .login-beta, so the landing is untouched by any of it. */
.login-beta .beta-word { transform: none !important; transition: none !important; }
.login-beta .beta-word::before, .login-beta .beta-word::after { transform: none !important; }

/* the stage stays inert; only the letters are hittable */
.login-beta .beta-word { pointer-events: auto; }

/* the lit copy, revealed only inside a soft circle at the cursor */
.login-beta .beta-torch {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  font: inherit; letter-spacing: inherit; line-height: inherit;
  color: transparent;
  -webkit-text-stroke: 1.6px rgba(245,166,35,.55);
  opacity: 0;
  transition: opacity .45s ease;
  -webkit-mask-image: radial-gradient(circle var(--torch-r, 170px) at var(--tx, -999px) var(--ty, -999px), #000 0%, rgba(0,0,0,.55) 45%, transparent 72%);
          mask-image: radial-gradient(circle var(--torch-r, 170px) at var(--tx, -999px) var(--ty, -999px), #000 0%, rgba(0,0,0,.55) 45%, transparent 72%);
}
.login-beta .beta-word.lit .beta-torch { opacity: 1; }
:root[data-theme="light"] .login-beta .beta-torch { -webkit-text-stroke-color: rgba(190,110,20,.50); }
@media (prefers-reduced-motion: reduce) { .login-beta .beta-torch { transition: none; } }
/* a coarse pointer cannot hover, so the torch simply never appears there */
@media (hover: none) { .login-beta .beta-torch { display: none; } }

/* ══ PROFILE CARD ══════════════════════════════════════════════════════════
   Lives here rather than in index.html because the PUBLIC profile page needs
   the identical component. Two copies would drift. */
/* ── THE PROFILE CARD ────────────────────────────────────────────────
   Her reference: a soft colour halo bleeding out from behind a rounded
   card, with the avatar straddling the boundary. Adapted rather than
   copied — the reference's blue/orange/lavender would break the single
   amber accent locked on 27 Aug, so the halo is generated from the
   person's own avatar instead (sampled in the browser at upload) and
   amber stays the accent for chrome. No photo → a neutral halo (below).

   The blur lives on .pf-aura, a decorative SIBLING, never on .pf-card:
   a `filter` on the card would make it a containing block for any
   position:fixed descendant — the trap that broke the takeover pins. */
/* The band is PADDING on the card, not a margin on .pf-inner. A top margin on
   the inner collapses straight out through .pf-card (no padding, no border to
   stop it), which moved the whole card down instead of opening a colour band
   inside it — measured innerTopGap: 0. Padding cannot collapse. */
/* ── NO PHOTO ─────────────────────────────────────────────────
   The halo is SAMPLED FROM THE PERSON'S OWN PICTURE. With no picture there
   is nothing to sample, and the old fallback was full amber — a saturated
   band wrapped around a pale accent-tinted monogram, which read as a hole
   punched in the card rather than a face waiting to be added.
   So: no picture => a soft NEUTRAL band and a SOLID monogram with real
   contrast. These are ordinary defaults on the element, so the instant a
   picture exists acctPaintAura writes --pf-a/b/c inline and the warm,
   photo-derived look is back untouched. */
.pf-card, .pf-mini {
  --pf-a: #322f3a; --pf-b: #2a282f; --pf-c: #1f1d24;
  --pf-mono-bg: #33353f; --pf-mono-ink: #e7e9f2;
}
:root[data-theme="light"] .pf-card, :root[data-theme="light"] .pf-mini {
  --pf-a: #e6e2d9; --pf-b: #efebe3; --pf-c: #dbd6cb;
  --pf-mono-bg: #4a4753; --pf-mono-ink: #ffffff;
}
.pf-card { position: relative; border-radius: 30px; margin-bottom: 18px; isolation: isolate;
  padding: 82px 7px 7px; }
/* Two layers, and the difference matters. ::after is the BAND — crisp, clipped
   to the card's own radius, which is what makes the colour read as part of the
   card rather than a cloud floating behind it. .pf-aura is only the soft bleed
   that escapes past the edge. Stacking inside .pf-card (isolate): aura -1,
   band 0, .pf-inner 1 — the band hides the aura's middle, only its rim shows. */
.pf-card::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit; z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(64% 120% at 16% 8%,   var(--pf-a) 0%, transparent 64%),
    radial-gradient(66% 120% at 74% 2%,   var(--pf-b) 0%, transparent 68%),
    radial-gradient(80% 130% at 50% 108%, var(--pf-c) 0%, transparent 72%);
}
.pf-aura {
  position: absolute; inset: -6px; border-radius: 36px; z-index: -1;
  filter: blur(18px); opacity: .75; pointer-events: none;
  background:
    radial-gradient(60% 100% at 16% 10%,  var(--pf-a) 0%, transparent 66%),
    radial-gradient(62% 100% at 74% 6%,   var(--pf-b) 0%, transparent 68%),
    radial-gradient(76% 110% at 50% 104%, var(--pf-c) 0%, transparent 72%);
}
:root:not([data-theme="light"]) .pf-card::after { opacity: .78; }
:root:not([data-theme="light"]) .pf-aura { opacity: .5; filter: blur(20px); }

.pf-inner {
  position: relative; z-index: 1;
  margin: 0; padding: 0 26px 24px;
  background: var(--surf-1); border: 1px solid var(--surf-line);
  border-radius: 26px;
}
/* The avatar straddles the halo and the surface, exactly as it does in
   the reference. Its ring is the surface colour so the cut reads clean. */
.pf-avatar-wrap { position: absolute; top: -60px; left: 22px; width: 116px; height: 116px; }
.pf-avatar {
  width: 116px; height: 116px; border-radius: 50%; display: block;
  object-fit: cover; background: var(--surf-2);
  border: 5px solid var(--surf-1);
  box-shadow: 0 6px 20px rgba(0,0,0,.16);
}
.pf-avatar-letter {
  display: grid; place-items: center;
  font-family: var(--font-display); font-size: 42px; font-weight: 700;
  /* A real fill, not a tint: a pale disc inside the surface-coloured ring
     was the second half of the hole. */
  color: var(--pf-mono-ink); background: var(--pf-mono-bg);
}
.pf-avatar-edit {
  position: absolute; right: -2px; bottom: 2px;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; cursor: pointer;
  background: var(--surf-1); color: var(--text-2);
  border: 1px solid var(--surf-line); box-shadow: 0 2px 8px rgba(0,0,0,.14);
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.pf-avatar-edit:hover { color: var(--accent-text); border-color: var(--accent-border); }
.pf-avatar-edit svg { width: 16px; height: 16px; }

/* Name sits beside the avatar, so the text column starts clear of it. */
.pf-head { padding-left: 156px; min-height: 60px; padding-top: 14px; }
.pf-name {
  font-family: var(--font-display); font-size: var(--fs-xl); font-weight: 700;
  letter-spacing: -0.03em; line-height: 1.15; margin: 0; overflow-wrap: anywhere;
}
.pf-badges { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 9px; }

/* EARNED vs FLAIR. The difference is deliberate and load-bearing: if a
   self-chosen pill could pass for an earned one, no earned pill means
   anything. Earned = solid tint + tick. Flair = outline, no tick, and
   never the accent colour. */
.pf-badge {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-xs); font-weight: 650; line-height: 1;
  padding: 6px 11px; border-radius: 999px; white-space: nowrap;
  border: 1px solid transparent;
}
.pf-badge svg { width: 11px; height: 11px; flex-shrink: 0; }
.pf-badge[data-tone="accent"] { background: var(--accent-soft, rgba(245,166,35,.16)); color: var(--accent-text); border-color: var(--accent-border); }
/* plan + cool are NEUTRAL, not lavender and teal. The palette locked on 27 Aug is
   one accent on a neutral UI ("the page carries the colour"); four hues of pill on
   one card read as pastel candy, and this file argues for that lock two comments
   above before breaking it. Earned still cannot be mistaken for flair: solid fill +
   tick + 650 weight against transparent, no tick, 550 — no hue needed to say it. */
.pf-badge[data-tone="plan"],
.pf-badge[data-tone="cool"]   { background: var(--surface-3); color: var(--text); border-color: var(--border-strong); }
.pf-badge[data-tone="warm"]   { background: rgba(226,168,74,.16); color: #a4702a; border-color: rgba(226,168,74,.30); }
.pf-badge[data-tone="quiet"]  { background: transparent; color: var(--text-3); border-color: var(--border-strong); font-weight: 550; }
:root:not([data-theme="light"]) .pf-badge[data-tone="warm"] { color: #e8b264; }

.pf-bio { font-size: var(--fs-sm); color: var(--text-2); line-height: 1.55; margin: 16px 0 0; overflow-wrap: anywhere; }
.pf-bio:empty { display: none; }
.pf-tiles { margin-top: 20px; }

/* Below ~620px the avatar can no longer sit beside the name. */
@media (max-width: 620px) {
  .pf-inner { padding: 0 18px 20px; }
  .pf-avatar-wrap { width: 92px; height: 92px; top: -48px; left: 16px; }
  .pf-avatar { width: 92px; height: 92px; border-width: 4px; }
  .pf-avatar-letter { font-size: 34px; }
  .pf-head { padding-left: 122px; min-height: 48px; padding-top: 10px; }
}

/* ── the same object, shrunk, in the sidebar ───────────────────────── */
/* NOT .sidebar-user any more: that class is display:flex with its own
   padding, which made .pf-mini-inner a flex item that refused to shrink —
   measured 280px of card inside a 240px sidebar, spilling over the content.
   The mini card owns its spacing now. */
.pf-mini {
  position: relative; margin: 10px 0 0; padding-top: 10px;
  border-top: 1px solid var(--border); border-radius: 16px; isolation: isolate;
  min-width: 0;
}
.pf-mini::after { inset: 10px 0 0; border-radius: 16px; }
.pf-mini .pf-aura { inset: -3px; border-radius: 19px; filter: blur(9px); opacity: .6; }
:root:not([data-theme="light"]) .pf-mini::after { opacity: .55; }
:root:not([data-theme="light"]) .pf-mini .pf-aura { opacity: .42; filter: blur(11px); }
.pf-mini-inner {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: 14px;
  background: var(--surf-1); border: 1px solid var(--surf-line);
}
.pf-mini-avatar {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0; object-fit: cover;
  background: var(--pf-mono-bg);
  display: grid; place-items: center;
  font-size: var(--fs-sm); font-weight: 700; color: var(--pf-mono-ink);
  border: 1.5px solid var(--surf-line);
}
.pf-mini-info { min-width: 0; flex: 1; }
.pf-mini-name { font-size: var(--fs-sm); font-weight: 650; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
/* The email is hidden here, not removed: with it, the plan badge and the logout
   button left the name about 60px and it truncated to "Vero…". The account page
   shows the address in full, and the reference card carries none. It stays in
   the DOM because the existing sidebar code still writes to it.
   On a minimised sidebar there is no name either, so the badge can come back. */
.pf-mini-sub { display: none; }
.pf-mini-name { font-size: var(--fs-sm); line-height: 1.3; }
/* One badge only down here — the plan, or the strongest thing they hold. */
.pf-mini-badge {
  font-size: 10px; font-weight: 700; letter-spacing: .02em;
  padding: 2px 7px; border-radius: 999px; flex-shrink: 0;
  background: var(--accent-soft, rgba(245,166,35,.16)); color: var(--accent-text);
  border: 1px solid var(--accent-border);
}
body.nav-min .pf-mini-info, body.nav-min .pf-mini-badge { display: none; }
body.nav-min .pf-mini-inner { justify-content: center; padding: 9px 6px; }

/* Flair picker — the chips look like the OUTLINE flair badge they will
   become, so what you pick is what you get. */
.pf-flair-note { font-weight: 400; color: var(--text-3); font-size: var(--fs-xs); }
.pf-flair-picker { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 8px; }
.pf-flair {
  font-size: var(--fs-xs); font-weight: 550; line-height: 1;
  padding: 7px 12px; border-radius: 999px; cursor: pointer;
  background: transparent; color: var(--text-3);
  border: 1px solid var(--border);
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.pf-flair:hover { color: var(--text-2); border-color: var(--border-strong); }
.pf-flair.on { color: var(--text); border-color: var(--text-3); background: var(--surf-2); font-weight: 650; }
.pf-public-link { margin-top: 10px; font-size: var(--fs-sm); color: var(--text-2); display: flex; gap: 8px; flex-wrap: wrap; align-items: baseline; }
.pf-public-link a { color: var(--accent-text); overflow-wrap: anywhere; }
#accountView textarea.input { resize: vertical; min-height: 60px; font-family: inherit; }
/* ══ TWO-FACTOR PANEL ══════════════════════════════════════════════════ */
.tfa { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--border); }
.tfa-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.tfa-what { min-width: 0; flex: 1; }
.tfa-what b { display: block; font-size: var(--fs-sm); font-weight: 650; }
.tfa-what span { display: block; font-size: var(--fs-xs); color: var(--text-3); margin-top: 2px; }
.tfa-pill { font-size: var(--fs-xs); font-weight: 700; padding: 3px 10px; border-radius: 999px;
  color: var(--success, #059669); background: var(--success-soft, rgba(52,211,153,.12));
  border: 1px solid var(--success-border, rgba(52,211,153,.28)); }
.tfa-panel { margin-top: 16px; padding: 16px; border-radius: var(--r);
  background: var(--surf-2); border: 1px solid var(--border); }
.tfa-scan { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; }
.tfa-qr { width: 148px; height: 148px; border-radius: 10px; background: #fff; padding: 6px; flex-shrink: 0; }
.tfa-manual { min-width: 0; }
.tfa-manual .k { font-size: var(--fs-xs); color: var(--text-3); margin-bottom: 5px; }
.tfa-manual code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-sm);
  letter-spacing: .08em; overflow-wrap: anywhere; color: var(--text); }
/* A one-time code is digits — a monospace, well-spaced field stops transcription errors. */
.tfa-code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-lg);
  letter-spacing: .34em; text-align: center; max-width: 220px; }
.tfa-warn { font-weight: 700; font-size: var(--fs-sm); color: var(--danger, #dc2626); margin-bottom: 6px; }
.tfa-codes { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 8px; margin: 12px 0 14px; }
.tfa-rc { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--fs-sm);
  padding: 8px 10px; border-radius: 8px; text-align: center; letter-spacing: .04em;
  background: var(--surf-1); border: 1px solid var(--border); color: var(--text); }
#accountView .tfa-panel .btn { margin-right: 8px; }
@media (max-width: 620px) { .tfa-scan { justify-content: center; } }
/* Progress toward the next earned badge. A thin rule that fills as they get
   closer — measured from the LAST milestone, not from zero, so it actually
   moves between 50 and 100. Hidden entirely once every milestone is held. */
.pf-next {
  position: relative; margin: 12px 0 0; padding-bottom: 9px;
  font-size: var(--fs-xs); color: var(--text-3); font-weight: 550;
}
.pf-next::before, .pf-next::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 3px; border-radius: 999px;
}
.pf-next::before { width: 100%; background: var(--border); }
.pf-next::after  { width: var(--pf-next, 0%); background: var(--accent); transition: width .7s var(--ease); }
.pf-next[hidden] { display: none; }


/* ═══════════════════════════════════════════════════════════════════════════
   29 AUG — WAVE 13
   Four items, all of them things she asked for by name:
     13a  LIGHT SIDEBAR — opaque, per design-system.md §4 (it was violating it)
     13b  HOME PRIMARY CARD — "a clean, nice amber", not brown, in light
     13c  BACKGROUND — structure with a reason + pointer parallax + slow breathe
     13d  MOTION FOUNDATION — one curve and one duration scale

   Everything else in §3/§4 stays. Nothing here adds an `opacity: 0` rest state,
   every ambient animation is inside `prefers-reduced-motion: no-preference`, and
   no pure black / pure white is introduced.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 13a. THE LIGHT SIDEBAR WAS TRANSPARENT ─────────────────────────────────
   MEASURED before the fix: `.sidebar` in light computed `rgba(0, 0, 0, 0)` with
   `box-shadow: none` — the nav tabs were floating straight on the page, which is
   exactly the thing §4 forbids ("a transparent sidebar made Channels hard to
   pick out"). Dark has had `#121214` since wave 12; light had no equivalent.

   WHY IT WAS TRANSPARENT: `body .sidebar { background: transparent !important }`
   (0,1,1) from wave 2 §18 governs BOTH themes, and wave 12 only overrode it for
   dark with `:root:not([data-theme="light"]) body .sidebar` (0,3,1). The light
   half of that pair was never written. index.html's own
   `:root[data-theme="light"] .sidebar` (0,2,0, no !important) could never win —
   dead code, and a second reason to fix it here rather than there.

   THE COLOUR, by the §4 rule: the panel is an object because it has almost no
   hue while the page has plenty. Light's page floor is #f4f1ea (warm paper,
   visibly amber-tinted by the ambient blooms on the left edge, which is exactly
   where the sidebar sits). #fbfaf9 is chroma ~2 and +7 in luminance over the
   floor — the same size of step dark uses (#121214 over #0a0b0e). It is also
   deliberately NOT #ffffff: no pure white on the page itself (round 5). */
:root[data-theme="light"] body .sidebar {
  background: #fbfaf9 !important;
  -webkit-backdrop-filter: none !important;
          backdrop-filter: none !important;
  border-right: 1px solid rgba(28, 22, 12, .10) !important;
  box-shadow: 1px 0 0 rgba(28, 22, 12, .035),
              6px 0 26px -18px rgba(28, 22, 12, .34) !important;
}
/* §4 also requires the drawer stay opaque at <=768px, where index.html turns the
   column into a slide-over that floats ON TOP of the content. The rule above is
   (0,3,1) and outranks the <=768px `body .sidebar` (0,1,1) — media queries add
   no specificity — so the drawer inherits this opaque fill. That is the correct
   outcome, but it is only correct because the fill is opaque: stated here so the
   next person does not "simplify" it into a translucent one. Restated inside the
   query anyway, so the requirement is visible where the drawer is defined. */
@media (max-width: 768px) {
  :root[data-theme="light"] body .sidebar { background: #fbfaf9 !important; }
}
/* The user card at the foot of the sidebar was near-white on a transparent
   panel; on a near-white panel it needs its own edge or it dissolves. */
:root[data-theme="light"] body .sidebar .pf-mini,
:root[data-theme="light"] body .sidebar .sidebar-user {
  border-color: rgba(28, 22, 12, .12) !important;
}

/* ── 13b. THE HOME "CREATE A VIDEO" CARD READ BROWN ─────────────────────────
   Her words: she wants "a clean, nice amber".

   MEASURED FIRST. The card is painted by `:root[data-theme="light"]
   .home-card.hc-primary` (8 Aug block above); in the browser it resolved to
   border #b8730a, icon #a35f04 and a 95-98%-WHITE fill with one 30%-alpha amber
   corner bloom. That is the whole problem: a dark ochre hairline on a cream
   field has no amber anywhere near it, so the eye reads the hairline as brown.
   Amber over a warm-tinted near-white surface always does this.

   WHY THE HAIRLINE STAYS DARK. WCAG 1.4.11 wants 3:1 for a component boundary,
   and the boundary here has to clear it against BOTH the card interior and the
   page. Solving for it: the border's relative luminance cannot exceed ~0.229, so
   a bright amber hairline (#f5a623, L=0.46) is arithmetically impossible. There
   is no version of this where the 1px edge is "clean amber" and still legible.
   test/home-outline.test.js pins that, correctly.

   SO THE COLOUR MOVES TO WHERE THE PIXELS ARE. At 251x126 the fill is ~31,000
   px and the hairline ~750: the fill is what she is actually looking at. It
   becomes a real light amber, the ring becomes a real amber instead of a 20%
   whisper, and the hairline goes fully chromatic (#bb6f00, blue channel 0) at
   the same luminance it had. Result: an amber object with an amber edge, rather
   than a brown line drawn on cream.

   MEASURED AFTER (same maths as the pinning test):
     border #bb6f00 vs card interior  3.23:1   (needs 3)
     border #bb6f00 vs page behind    3.25:1   (needs 3)   [was 3.18]
     icon   #a35f04 vs card top       4.06:1   (needs 3, and >2x raw --accent)
     --text on the new interior      13.87:1
   Same specificity as the 8 Aug rule (0,3,0) + !important, later in source, so
   it wins on source order — and the pinning test resolves the winner the same
   way. Dark is untouched. */
:root[data-theme="light"] .home-card.hc-primary {
  background:
    radial-gradient(88% 76% at 6% -12%, rgba(255, 176, 46, .34) 0%,
                                        rgba(255, 201, 112, .13) 44%,
                                        rgba(255, 255, 255, 0) 72%),
    linear-gradient(180deg, rgba(255, 250, 238, .985) 0%,
                            rgba(255, 242, 214, .975) 48%,
                            rgba(255, 231, 189, .97) 100%) !important;
  border: 1px solid #bb6f00 !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .82),
    0 0 0 3px rgba(245, 166, 35, .34),
    0 2px 6px rgba(122, 76, 8, .10),
    0 20px 42px -16px rgba(122, 76, 8, .30) !important;
}
:root[data-theme="light"] .home-card.hc-primary:hover {
  border-color: #a86205 !important;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .82),
    0 0 0 4px rgba(245, 166, 35, .46),
    0 4px 10px rgba(122, 76, 8, .12),
    0 26px 52px -18px rgba(122, 76, 8, .36) !important;
}

/* ── 13c. THE BACKGROUND: DETAIL WITH A REASON ──────────────────────────────
   Her words: "add a little bit more detail to the background to make it feel
   premium like it's actually supposed to be there and have a reason, not just
   low-quality fog style" — plus pointer parallax and a slow breathe.

   The fog is not fixed by adding haze; it is fixed by adding STRUCTURE. Two new
   layers, neither of which needs a new element in any of the 14 pages:

     html::after      a photographic frame vignette — darkens the edges, which
                      is the one addition that makes the picture DARKER, not
                      lighter, and gives the blooms a frame to sit inside.
     .bgdeep::after   EMPTY as of 31 Aug 2026 — see the note at the rule itself.
                      It held a fine architectural grid (never asked for), then
                      briefly a set of scratch marks (also not wanted here). The
                      background is the blooms, the shafts, the vignette and the
                      film grain, and nothing else. Do not re-add a texture layer.

   ★ TWO MEASURED TRAPS, both of which produced a layer that A/B'd at 0.00:

   (1) The vignette started on `.bgdeep::before` and moved to `html::after`.
       `.bgdeep` is `inset: -12%` and deepDrift scales it to 1.07, so its box is
       ~1786x1116 behind a 1440x900 viewport: percentage stops resolve against
       THAT box and the whole dark ring landed off-screen. A/B: dMean 0.00.
       `html::after` has no transformed ancestor, so `inset: 0` is the viewport
       exactly. It also disproves the note at F2 above, which was written before
       the base colour moved to `html`: with the floor on html, a fixed
       negative-z pseudo of the ROOT paints (measured +18.3 mean with a 50% red
       test fill) instead of being covered by body's own background.

   (2) The grid's mask has the same problem, so the pseudo is inset by 9.68%
       (= 12/124) which maps its box back onto the viewport. Without that, the
       mask centres sat off-screen top-left and the grid A/B'd at 0.00 too.

   The grid rides `.bgdeep`'s existing 122s deepDrift and carries its own 104s
   breathe on top — inside the 74-122s band §5 asks for. No new element, no new
   listener, and reduced motion already switches `.bgdeep` still. */
:root[data-theme="light"] html::after,
html[data-theme="light"]::after {
  background: radial-gradient(farthest-corner at 30% 24%,
    rgba(116, 98, 68, 0)    0%,
    rgba(116, 98, 68, 0)    44%,
    rgba(116, 98, 68, .045) 64%,
    rgba(116, 98, 68, .105) 84%,
    rgba(116, 98, 68, .145) 100%);
}
/* ── NOTHING ON THIS LAYER (31 Aug 2026) ──────────────────────────────────
   `.bgdeep::after` is deliberately EMPTY. It held a fine architectural grid
   (which she never asked for), and then briefly sixteen scratch marks, which she
   also did not want here: "just remove the scratches that you add and keep the
   ones that we have done before in the landing page, remove the ones from the
   background."

   The landing page's OWN scratches are a different thing and are untouched —
   they are real elements in landing.html (`.scratch.scr-line`, `.scratch.scr-swirl`,
   hand-placed in outer margins) with their own styling. Do not confuse the two,
   and do not "restore" a texture layer here to match them.

   The background is the blooms, the shafts, the html::after vignette and the
   film grain. That is the whole recipe. If it ever reads as fog again, fix it
   with the light, not by laying a pattern over it. */

/* crisper shaft edges — the beams were blurred past the point where an edge
   could read, which is what turned "light through a window" back into haze.
   Blur redistributes light, it does not add any, so this is health-neutral. */
:root:not([data-theme="light"]) .bgshaft { filter: blur(17px); }
:root[data-theme="light"] .bgshaft { filter: blur(26px); }

/* ── POINTER PARALLAX ───────────────────────────────────────────────────────
   ★ CORRECTION TO THE BRIEF, measured not assumed: `--mx`/`--my` are NOT a
   document-level pointer channel. torquevid-motion.js writes them in PIXELS
   onto the hovered `.plan` CARD only (`e.target.closest(".plan")`), for the
   pricing-card highlight at ~line 612. A custom property set on a `.plan`
   cannot reach `.bgdeep`, which is not its descendant — and redefining --mx on
   :root would feed a unitless number into `.plan::after`'s
   `radial-gradient(... at var(--mx, 50%) ...)` and break that gradient.

   So this consumes a NEW pair, `--pmx`/`--pmy`, normalised -0.5..0.5, written on
   document.documentElement. That still needs ONE line inside the EXISTING
   delegated pointermove listener in torquevid-motion.js — a file this pass does
   not own, so it is reported rather than edited. Until it lands both variables
   stay 0 and nothing moves: the layers are correct at rest, never hidden.

   Movement is deliberately tiny and heavily damped — 1.4s on the torque curve —
   and depth is inverted: the deep layer moves least, the near layer most. */
/* ── THE BACKGROUND DOES NOT MOVE WITH THE CURSOR (31 Aug 2026) ────────────
   Her words: "make sure when I move my cursor the background stays still."

   What used to be here: `.bgdeep` drifted 15px/12px and `.bgshaft` 42px/30px
   from --pmx/--pmy, damped over 1.4s. That is a parallax — the whole background
   sliding as the pointer moves — and it is exactly what she asked to be rid of.
   Both declarations are removed, and the handler in torquevid-motion.js no
   longer writes the variables at all, so nothing can drive them by accident.

   --pmx/--pmy are KEPT DEFINED at 0 rather than deleted, because a `calc()`
   against an undefined custom property makes the whole declaration invalid — if
   any rule anywhere still reads them, it degrades to "no movement" instead of
   dropping a `translate` that something else needed.

   The light still responds: the nearest bloom warms and cools. Brightness is not
   movement, and it was her separate, explicit pick. */
:root { --pmx: 0; --pmy: 0; }
@media (prefers-reduced-motion: no-preference) {
  /* .glow already owns `translate` for the SCROLL parallax (--par). Both are
     folded into one declaration — writing a second `translate` rule would simply
     delete the scroll parallax. */
  /* THE LEAN IS GONE (31 Aug 2026). `translate: var(--glean-x) var(--glean-y)`
     lived here and pushed the nearest light 22px away from the pointer. She
     chose a filament warm-up instead — brightness only, the light never moves —
     so there is deliberately no `translate` on `.glow` any more, and the handler
     no longer writes --glean-x/--glean-y. Left as a comment rather than deleted
     silently, because the history matters: --pmx sheet-drift was removed from
     the lights on 30 Aug, then the lean on 31 Aug. `--par` (SCROLL parallax) was
     already gone from the background — it changed every scroll frame while this
     declaration was transitioned for pointer damping, so the two fought and the
     light slid ~800ms behind the scroll. Do not reintroduce a transform here
     without re-reading that. */
}

/* ── 13d. MOTION FOUNDATION ─────────────────────────────────────────────────
   Her note: the app "feels unpolished, glitchy". MEASURED cause, not a guess —
   tv.css contained 13 different cubic-beziers, 80 transitions on the bare `ease`
   keyword and 22 distinct durations. Components were literally disagreeing about
   how fast and on what curve they move, and the eye reads that as jitter.

   ONE scale. The curve is the torque curve from design-system.md §5. The legacy
   token names are re-pointed at it in the same block, so every rule already
   written against --ease / --e-calm / --t-base converges without being touched —
   that is most of the file. Naming is deliberate and stable so index.html can
   adopt the same tokens. */
:root {
  --tv-ease:       cubic-bezier(.16, 1, .3, 1);    /* the torque curve — the default for everything */
  --tv-ease-soft:  cubic-bezier(.33, 1, .68, 1);   /* small state flips: colour, opacity */
  --tv-ease-inout: cubic-bezier(.65, 0, .35, 1);   /* symmetric: open/close, cross-fades */

  --tv-dur-press:  110ms;   /* the answer to a press — must feel instant */
  --tv-dur-1:      180ms;   /* hover tint, icon, small colour change */
  --tv-dur-2:      280ms;   /* pills, chips, toggles, focus rings */
  --tv-dur-3:      440ms;   /* surfaces: shadow, lift, panels */
  --tv-dur-4:      680ms;   /* entrances and reveals */

  /* legacy aliases — the whole file converges through these */
  --ease:    var(--tv-ease);
  --e-calm:  var(--tv-ease);
  --e-soft:  var(--tv-ease-soft);
  --t-quick: var(--tv-dur-1);
  --t-base:  var(--tv-dur-3);
  --t-slow:  var(--tv-dur-4);
}
/* The rules that hand-wrote `ease` never read a token, so they need reaching
   directly. Shared primitives only — this changes the CURVE, never what is
   transitioned and never a layout. `body ` prefixes put every selector at
   (0,1,1) so it outranks the bare class rules it is correcting. */
@media (prefers-reduced-motion: no-preference) {
  body .btn, body .btn-accent, body .btn-ghost, body .btn-quiet,
  body .btn-generate, body .btn-ai, body .btn-sm, body .theme-toggle,
  body .nav-item, body .nav-links a, body .sidebar-logout, body .sidebar-toggle,
  body .home-card, body .hstat, body .card, body .ws-panel, body .stat-card,
  body .hub-action, body .ob-way, body .obw-angle, body .style-opt,
  body .pill, body .chip, body .badge, body .tab, body .ob-pill,
  body .voice-opt, body .ai-lvl, body .tf-opt, body .pal-card, body .start-card,
  body .vc2-card, body .bc-opt, body .gd-card,
  body input, body select, body textarea, body .input {
    /* !important is not laziness here, it is the only thing that reaches.
       PROBED: index.html writes these primitives as `#createView .style-opt`
       (1,1,0) — an ID selector no theme-scoped class rule can outrank — and
       `:root:not([data-theme="light"]) .home-card { ... !important }` (0,3,0)
       covers the dark card families. Without !important the scale landed on
       roughly half the app and the other half stayed on the bare `ease`
       keyword, which is the disagreement this block exists to end.
       It overrides ONLY the curve: transition-property and duration are
       untouched, so nothing changes about what moves or for how long. */
    transition-timing-function: var(--tv-ease) !important;
  }
  /* A press is the one place a long curve feels broken — it must land now. */
  body .btn:active, body .btn-accent:active, body .btn-generate:active,
  body .btn-ai:active, body .home-card:active, body .nav-item:active,
  body .ob-way:active, body .style-opt:active {
    transition-duration: var(--tv-dur-press);
  }
  /* ★ PROBED, not assumed. Three families came back still on the bare `ease`
     keyword: .home-card, .ob-way and .style-opt. The 28 Jul dark-glass block at
     ~1876 declares them `transition: ... ease !important` at (0,3,0), which
     outranks a (0,1,1) rule no matter how late it appears — the exact trap §6 of
     design-system.md warns about, and the reason this pass measured the winner
     in a browser instead of trusting the source order. (0,3,1) + !important is
     what it takes. The list mirrors that rule's own selector list exactly. */
  :root:not([data-theme="light"]) body .plan,
  :root:not([data-theme="light"]) body .home-card,
  :root:not([data-theme="light"]) body .value-card,
  :root:not([data-theme="light"]) body .sc-card,
  :root:not([data-theme="light"]) body .ex-card,
  :root:not([data-theme="light"]) body .prob-card,
  :root:not([data-theme="light"]) body .ob-way,
  :root:not([data-theme="light"]) body .obw-angle,
  :root:not([data-theme="light"]) body .style-opt,
  :root:not([data-theme="light"]) body .theme-card {
    transition-timing-function: var(--tv-ease) !important;
  }
}

/* ══════ WAVE 14 · STILL BACKGROUND, REACTIVE LIGHTS (30 Aug, her call) ══════
   "I want the backgrounds to be completely static. I just wanted the light and
   the light bulbs ... to be just a little bit reactive to the cursor."
   Plus: static in the APP, some life kept on the LANDING page. */

/* ══ FILAMENT WARM-UP (31 Aug 2026, her pick) ═══════════════════════════════
   The light DOES NOT MOVE. It used to be pushed 22px away from the pointer,
   swell 3.5% and fire a travelling ripple ring; she chose the opposite reading:
   a bulb that warms as you come near it and cools as you leave, staying exactly
   where it is.

   Why this shape and not a lean: anything that translates toward or away from
   the cursor is legible as "the light is tracking me", which is the effect she
   has asked to be rid of everywhere. Brightness alone has no direction, so it
   cannot chase anything — it can only respond.

   The asymmetry is the whole illusion. A real filament has thermal mass: it
   comes up fast-ish and falls away slowly. Equal in and out reads as a hover
   state; 700ms up against 900ms down reads as heat. `--glit-dur` is written by
   the pointer handler, which knows whether this light is rising or falling —
   CSS cannot express a per-direction duration on a variable-driven property, so
   the direction has to come from the one place that knows it.
   ★ IT DRIVES brightness(), NOT opacity — MEASURED, and the first version was
   wrong. `opacity: calc(var(--glow-o,1) * var(--glit,1))` looked right and was
   completely dead: opacity is CLAMPED to a maximum of 1, and on the dark theme
   `--glow-o` is unset, i.e. already 1. Multiplying 1 by 1.226 clamps straight
   back to 1. Driving the pointer to opposite corners and reading the PAINTED
   value gave a delta of 0.0000 on all eight lights while `--glit` was correctly
   reaching 1.226 — the variable moving is not the effect existing. (It would
   have appeared to work on the LIGHT theme, where --glow-o is .55 and there is
   headroom, which is exactly how this hides.)

   `filter: brightness()` has no upper clamp, so the same 1.0-1.4 range is real
   in both themes and does not require dimming the light at rest to make room.
   The blur is folded in through --glow-blur at the base rule; re-declaring
   `filter` anywhere later would silently drop it. */
@media (prefers-reduced-motion: no-preference) {
  .glow {
    /* `transition` is a SHORTHAND — declaring only `filter` here would silently
       drop the transform/opacity easing set earlier for the scroll response
       (§D4: .glow eases transform and opacity over .9s, and scale rides
       --scrollv). Both are restated so this rule adds the filament rather than
       replacing what was already tuned. */
    transition: filter var(--glit-dur, 900ms) var(--tv-ease, cubic-bezier(.16, 1, .3, 1)),
                transform .9s var(--e-calm, ease),
                opacity   .9s var(--e-calm, ease);
  }
}

/* ── THE APP IS STILL ──
   Every autonomous ambient animation off everywhere EXCEPT the landing page.
   The blooms keep their small uniform pointer drift, which is the only honest
   "reacts to your cursor" available to a layer whose lights live inside one
   gradient and therefore have no addressable position. */
/* EVERY page, landing included (30 Aug). She asked for "completely static" with
   no qualification this time, which supersedes the earlier "keep some life on
   the landing page". The only thing that moves a background layer now is the
   pointer. */
/* `.glow` is deliberately NOT in this list any more: it carries the slow float
   below. Everything else on the page stays frozen. */
.glow::after,
.bgdeep, .bgdeep::before, .bgdeep::after,
.bgshaft, .bgshaft::before, .bgshaft::after,
.passlight, .rail, .scratch, .sweep, .bgthumb,
body::before, body::after,
html::before, html::after { animation: none !important; }

/* ── App lights (30 Aug) ──
   A fixed stage so .glow's absolute placement resolves against the VIEWPORT
   rather than a section — the app has no .section wrappers to hang them on.
   overflow:hidden because the placements are deliberately off-canvas (left:-180px)
   and a fixed element that overflows can still produce a scrollbar. */
.app-lights {
  position: fixed; inset: 0; z-index: -1;
  pointer-events: none; overflow: hidden;
}
/* Fainter than the landing page's. design-system.md §3 pins measured background
   health (mean luminance, the share of genuinely dark pixels) and adding light is
   exactly what regresses it. A token, so it is tuned against the measurement. */
.app-lights .glow { --glow-o: .56; }
:root[data-theme="light"] .app-lights .glow { --glow-o: .40; }

/* ══════ WAVE 15 · PRESS FEEDBACK (30 Aug) ══════
   Measured before writing this: 10 `:active` rules existed in the entire sheet,
   and `.btn` was the only common control that answered a press. A control that
   does not move under the finger reads as unresponsive no matter how fast the
   thing behind it is — and these are the surfaces touched most.

   `scale:` the property, not `transform: scale()` — several of these already use
   transform for layout and this composes rather than replacing it. */
/* PROVEN IN A BROWSER, not assumed: without this the winning rule for
   `#briefSteps .cwb-step` is `transition: color, background, border-color` — no
   `scale` — so the press SNAPPED. CSS cannot add one property to an existing
   transition list, and restating every property of every winning rule would rot
   the moment one of them changed. One scoped `!important`, on one declaration. */
.cwb-step, .sg-step, .ct-step, .nav-item, .seg > button, .style-opt,
.gfx-chip, .pal-card, .home-card, .voice-item, .tf-opt, .fb-seg > button,
.tab, .chip, .src-row, [role="tab"], [role="radio"], [role="switch"] {
  transition: scale var(--tv-dur-press, 110ms) var(--tv-ease, cubic-bezier(.16, 1, .3, 1)) !important;
}
.cwb-step:active, .sg-step:active, .ct-step:active, .nav-item:active,
.seg > button:active, .style-opt:active, .gfx-chip:active, .pal-card:active,
.home-card:active, .voice-item:active, .tf-opt:active, .fb-seg > button:active,
.tab:active, .chip:active, .src-row:active,
[role="tab"]:active, [role="radio"]:active, [role="switch"]:active {
  scale: 0.978;
}
/* The Create tab's option cards define their own transition at `#createView
   .style-opt` (1,1,0), and index.html's inline sheet loads after this file — so
   at equal weight the page wins on source order. (1,2,0) + importance clears it. */
body #createView .style-opt, body #createView .voice-opt, body #createView .ai-lvl,
body #createView .tf-opt, body #createView .pal-card, body #createView .gfx-chip {
  transition: scale var(--tv-dur-press, 110ms) var(--tv-ease, cubic-bezier(.16, 1, .3, 1)) !important;
}
body #createView .style-opt:active, body #createView .voice-opt:active,
body #createView .ai-lvl:active, body #createView .tf-opt:active,
body #createView .pal-card:active, body #createView .gfx-chip:active { scale: 0.978; }

/* Disabled controls must NOT answer — a press response on something that will not
   act is a lie about what is going to happen. */
.cwb-step:disabled:active, .seg > button:disabled:active, .style-opt[aria-disabled="true"]:active,
.nav-item[aria-disabled="true"]:active, [role="tab"][aria-disabled="true"]:active { scale: 1; }

/* Reduced motion keeps the FEEDBACK and drops the movement — §6: fewer and
   gentler, never zero. The control still acknowledges the press, via opacity. */
@media (prefers-reduced-motion: reduce) {
  .cwb-step, .sg-step, .ct-step, .nav-item, .seg > button, .style-opt,
  .gfx-chip, .pal-card, .home-card, .voice-item, .tf-opt, .fb-seg > button,
  .tab, .chip, .src-row, [role="tab"], [role="radio"], [role="switch"] {
    transition: opacity var(--tv-dur-press, 110ms) linear !important;
  }
  .cwb-step:active, .sg-step:active, .ct-step:active, .nav-item:active,
  .seg > button:active, .style-opt:active, .gfx-chip:active, .pal-card:active,
  .home-card:active, .voice-item:active, .tf-opt:active, .fb-seg > button:active,
  .tab:active, .chip:active, .src-row:active,
  [role="tab"]:active, [role="radio"]:active, [role="switch"]:active {
    scale: 1; opacity: .82;
  }
}

/* ══════ WAVE 15 · SHAPES, NOT BLOBS (30 Aug, her ask) ══════
   "I do not want the lights to be just blobs — different shapes, motions, styles.
    Not something too complicated, just a little bit more creativity."
   Every light was border-radius: 50% — five identical circles. Each now has its
   own organic radius and its own tilt, so they read as forms with a direction
   rather than as repeated dots. Still soft, still ambient, nothing clever.

   `rotate:` the PROPERTY, never `transform` — `.glow-c` centres itself with
   transform and would be destroyed by a transform here. `rotate`, `translate`
   and `scale` compose independently, which is exactly why they exist. */
.glow-tl { border-radius: 62% 38% 46% 54% / 58% 44% 56% 42%; rotate: -14deg; }
.glow-tr { border-radius: 40% 60% 58% 42% / 46% 62% 38% 54%; rotate:  11deg; }
.glow-bl { border-radius: 55% 45% 38% 62% / 42% 56% 44% 58%; rotate:   8deg; }
.glow-br { border-radius: 47% 53% 63% 37% / 60% 40% 60% 40%; rotate: -19deg; }
.glow-c  { border-radius: 58% 42% 44% 56% / 52% 48% 52% 48%; rotate:   5deg; }

/* The app's four sit at the corners of a workspace, so they are calmer and
   flatter than the landing's — wider than tall, tilted less. */
.app-lights .glow-tl { border-radius: 64% 36% 48% 52% / 54% 46% 54% 46%; rotate:  -9deg; }
.app-lights .glow-tr { border-radius: 38% 62% 54% 46% / 48% 58% 42% 52%; rotate:   7deg; }
.app-lights .glow-bl { border-radius: 52% 48% 40% 60% / 44% 54% 46% 56%; rotate:   6deg; }
.app-lights .glow-br { border-radius: 45% 55% 60% 40% / 58% 42% 58% 42%; rotate: -12deg; }

/* Footprint, not brightness. The organic radii + tilt spread each light wider
   than the circle it replaced, and the measurement that failed was the DARK
   SHARE — they were eating the near-black quadrant §3 exists to protect.
   Smaller and tighter to their corners; the light keeps its presence. */
.app-lights .glow-tl { width: 470px; height: 310px; left: -150px; top: -110px; }
.app-lights .glow-tr { width: 430px; height: 300px; right: -140px; top: -95px; }
.app-lights .glow-bl { width: 400px; height: 285px; left: -120px; bottom: -120px; }
.app-lights .glow-br { width: 460px; height: 315px; right: -145px; bottom: -115px; }

/* ══════ WAVE 16 · SLOW FLOAT + THE RIPPLE (30 Aug, her call) ══════

   FOUR PROPERTIES, FOUR JOBS. transform = the float (below), translate = the
   pointer displacement (JS), scale = the swell, opacity = the lift. They are
   independent CSS properties and compose; routing two of them through
   `transform` is what would make them fight.

   Drift is 26-38px over 96-118s, every light on its own timing so they never
   move together — inside the 74-122s band design-system.md §5 specifies. You
   have to watch one deliberately to catch it. */
@media (prefers-reduced-motion: no-preference) {
  .glow-tl { animation: tvFloatA 104s ease-in-out infinite alternate; }
  .glow-tr { animation: tvFloatB 118s ease-in-out infinite alternate; }
  .glow-bl { animation: tvFloatC  96s ease-in-out infinite alternate; }
  .glow-br { animation: tvFloatD 110s ease-in-out infinite alternate; }
  /* ⚠ .glow-c centres itself with transform: translate(-50%,-50%). Its float
     MUST carry that centring or the light jumps to the wrong place. */
  .glow-c  { animation: tvFloatCentre 112s ease-in-out infinite alternate; }
}
@keyframes tvFloatA { from { transform: translate(0, 0) rotate(0deg); }      to { transform: translate(30px, -24px) rotate(3deg); } }
@keyframes tvFloatB { from { transform: translate(0, 0) rotate(0deg); }      to { transform: translate(-26px, 30px) rotate(-3deg); } }
@keyframes tvFloatC { from { transform: translate(0, 0) rotate(0deg); }      to { transform: translate(34px, 20px) rotate(2deg); } }
@keyframes tvFloatD { from { transform: translate(0, 0) rotate(0deg); }      to { transform: translate(-30px, -26px) rotate(-2deg); } }
@keyframes tvFloatCentre {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(calc(-50% + 28px), calc(-50% - 22px)) rotate(2deg); }
}

/* ── THE RIPPLE IS GONE (31 Aug 2026) ──
   A soft ring used to travel outward from where the pointer met the light, on
   `::before`, rate-limited to one per 1.1s. It is removed along with the lean:
   she chose a filament that warms and cools without moving, and a travelling
   ring is the most movement of anything that was here — it also re-fired every
   time the nearest light changed, so crossing the page produced a run of them.
   `--rx`/`--ry` are no longer written by the handler and `.rippling` is never
   added. `.glow::after` (the echo blob) is untouched; this pseudo was chosen
   originally precisely so removing it could not delete that layer, which is why
   deleting it now is safe. */

/* ══ PROCEED WITH CAUTION (her image 12) ══════════════════════════════════════
   The two ways out are LISTED, one under the other, under a line that says a
   choice is required. Two buttons side by side read as "primary and cancel";
   this reads as two answers to a question, which is what it is. */
/* ...but ONLY in the caution state. A clean setup has no choice to make: it
   has an acknowledgement and a way back, which is what the ORIGINAL footer
   already was (amber primary right, quiet secondary beside it, checkbox left).
   Dressing that in image 12's stacked rows gives a low-risk video the shape of
   a warning, and the glyphs point at a question that is hidden. */
.pgn-foot.pgn-caution { flex-direction: column; align-items: stretch; }
.pgn-choose {
  font-size: 13px; color: var(--text-3); margin: 0 0 2px;
}
.pgn-choose[hidden] { display: none; }
.pgn-foot.pgn-caution .pgn-actions { display: grid; gap: 8px; width: 100%; }
/* Calm: the markup lists accept-first for the caution list; on one row this
   app puts the primary last. Reverse the row rather than reorder the DOM,
   so the keyboard order still leads with the safe answer. */
.pgn-foot:not(.pgn-caution) .pgn-actions { flex-direction: row-reverse; }
.pgn-foot:not(.pgn-caution) .pgn-act-i { display: none; }
.pgn-foot.pgn-caution .pgn-act {
  display: flex; align-items: center; gap: 12px; width: 100%;
  text-align: left; padding: 14px 18px; border-radius: 11px;
  font: inherit; font-size: 15px; cursor: pointer;
}
.pgn-foot.pgn-caution .pgn-act-i { flex: 0 0 auto; opacity: .55; font-size: 15px; line-height: 1; }
/* The accepting answer is not the loud one. It is the riskier of the two, and
   an amber slab under "I acknowledge the risk" is an invitation to click it
   without reading. Both are quiet; the words carry the weight. */
.pgn-foot.pgn-caution .pgn-act.pgn-go {
  background: var(--surface-3, rgba(255,255,255,.06));
  color: var(--text); font-weight: 650;
  border: 1.5px solid var(--border-strong);
}
.pgn-foot.pgn-caution .pgn-act.pgn-go:hover { border-color: var(--accent-border); color: var(--text); filter: none; }
.pgn-foot.pgn-caution .pgn-act.pgn-back { font-weight: 650; }
.pgn-foot.pgn-caution .pgn-again { margin-right: 0; margin-top: 4px; }

/* A problem is the headline, not a paragraph heading. */
.pgn-strip {
  display: block; padding: 11px 14px; border-radius: 9px; margin-bottom: 10px;
  /* A GRADIENT, dark amber into gold, left to right — read off the video at
     1920px, not off a screenshot. The tint still carries the accent so the
     contrast pin below holds; the words stay --text. */
  background: linear-gradient(90deg, var(--accent-soft, rgba(245,166,35,.10)), rgba(245,166,35,.30));
  border: 1px solid var(--accent-border, rgba(245,166,35,.26));
  /* The words stay --text. Amber as WORDS measures 1.72:1 on the light ground
     — the same trap the tally light hit — and this is the one sentence in the
     dialog that has to be read. The tint, the border and the ⚠ carry the
     colour; the brand amber stays amber everywhere it is a label. */
  color: var(--text); font-weight: 700; font-size: 14px; line-height: 1.45;
}
.pgn-fix-h { font-size: 13px; font-weight: 700; color: var(--text); margin: 12px 0 6px; }
.pgn-fixes { list-style: none; display: grid; gap: 6px; margin: 0; padding: 0; }
.pgn-fixes li {
  font-size: 14px; color: var(--text-2); line-height: 1.5;
  padding-left: 16px; position: relative;
}
.pgn-fixes li::before { content: "\203A"; position: absolute; left: 0; color: var(--text-3); }
.pgn-ex {
  margin: 10px 0 0; padding: 10px 12px; border-radius: 9px;
  background: var(--surface-3, rgba(255,255,255,.05));
  font-size: 13px; color: var(--text-2); line-height: 1.5;
}
.pgn-warn { color: var(--accent, #f5a623); margin-right: 8px; }
.pgn-warn[hidden] { display: none; }

/* A repeated toast is the SAME toast, nudged — see showToast. Three identical
   messages stacked is noise pretending to be information. */
@keyframes toastAgain {
  0%   { transform: translateX(0); }
  30%  { transform: translateX(-5px); }
  60%  { transform: translateX(3px); }
  100% { transform: translateX(0); }
}
.toast.toast-again { animation: toastAgain .26s var(--ease-out, ease-out); }
@media (prefers-reduced-motion: reduce) { .toast.toast-again { animation: none; } }

/* ══ THE BRAND-PROFILE WALK, ON TOP OF THE CREATE SCREEN ═══════════════════
   VidRush never navigates you away to set a profile up — you press "new brand
   profile" and the walk happens over the page you were writing on. #styleView
   is MOVED into this overlay for the duration (see inlineOn/inlineOff in
   style-profiles.js) and put back after, so there is one walk, not a copy. */
.spf-over {
  position: fixed; inset: 0; z-index: 260;
  display: none; overflow-y: auto;
  background: rgba(6, 6, 17, .72);
  backdrop-filter: blur(10px) saturate(1.05);
  -webkit-backdrop-filter: blur(10px) saturate(1.05);
  padding: var(--sp-6) var(--sp-5) var(--sp-8);
}
.spf-over.open { display: block; animation: spfOverIn .22s var(--ease-out, ease-out) both; }
@keyframes spfOverIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .spf-over.open { animation: none; } }
/* #styleView is a .view — inside the overlay it must ignore the app's own
   view machinery and simply be the thing on screen. */
.spf-over > .view { display: block !important; max-width: 1080px; margin: 0 auto; }
:root[data-theme="light"] .spf-over { background: rgba(28, 40, 75, .34); }
/* The page behind must not scroll under the walk. */
body.spf-over-on { overflow: hidden; }

/* ── Video language ── one true answer, and it says why ── */
.spf-lang { display: grid; gap: var(--sp-3); }
.spf-lang-opt {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  width: 100%; text-align: left; cursor: default;
  padding: 14px 16px; border-radius: var(--r);
  border: 1.5px solid var(--accent-border); background: var(--accent-soft);
  font: inherit; color: var(--text);
}
.spf-lang-n { font-size: var(--fs-md); font-weight: 700; }
.spf-lang-m { font-size: var(--fs-sm); font-weight: 650; color: var(--accent); }


/* ══════ WAVE 17 · THE PHONE TAB BAR + A HOME THAT KNOWS YOU (3 Sep, the redesign) ══════
   docs/superpowers/specs/2026-09-03-website-redesign-design.md, Phase B.
   Desktop keeps the sidebar; the bar exists only below 768px, where the sidebar
   is a drawer and a phone had no visible navigation. Neutral bar, amber only on
   the active EDGE and the centre orb — never on words. Hidden wherever another
   surface owns the whole screen. */
.tabbar { display: none; }
@media (max-width: 768px) {
  body:not(.landing) .tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); align-items: end;
    padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px));
    background: var(--page-base);
    border-top: 1px solid var(--surf-line);
    box-shadow: 0 -8px 24px -18px rgba(0, 0, 0, .6);
  }
  .tb-item {
    appearance: none; background: none; border: 0; color: var(--text-3); font: inherit; cursor: pointer;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-height: 52px; padding: 6px 2px; border-radius: var(--r-sm); position: relative;
    -webkit-tap-highlight-color: transparent;
  }
  .tb-item svg { width: 24px; height: 24px; }
  .tb-item > span:last-child { font-size: 12px; font-weight: 600; letter-spacing: .01em; line-height: 1; }
  /* active = two changes: the words go to --text and bold, and an amber edge appears */
  .tb-item.active { color: var(--text); }
  .tb-item.active > span:last-child { font-weight: 700; }
  .tb-item.active::before {
    content: ""; position: absolute; top: 0; left: 50%; width: 22px; height: 2px;
    transform: translateX(-50%); border-radius: 2px; background: var(--accent);
  }
  /* Create: the one accented action, raised out of the bar */
  .tb-create .tb-orb {
    display: grid; place-items: center; width: 48px; height: 48px; border-radius: 50%; margin-top: -22px;
    background: var(--accent-gradient, var(--accent)); color: var(--accent-ink, #1a1205);
    box-shadow: 0 8px 24px -8px rgba(245, 166, 35, .6);
  }
  .tb-create .tb-orb svg { width: 24px; height: 24px; }
  .tb-create.active::before { display: none; }
  /* room for the bar under every view */
  body:not(.landing) .page-scroll { padding-bottom: calc(110px + env(safe-area-inset-bottom, 0px)) !important; }
  /* surfaces that own the whole screen */
  body.step-page .tabbar, body.producing .tabbar, body.spf-over-on .tabbar { display: none !important; }
  /* anything else pinned to the bottom edge moves up, clear of the bar */
  body:not(.landing) .fb-fab { bottom: calc(84px + env(safe-area-inset-bottom, 0px)) !important; }
  body:not(.landing) .draft-pill { bottom: calc(150px + env(safe-area-inset-bottom, 0px)) !important; }
  /* the topbar at phone width: Home is one tap away on the bar, so the Back
     button is redundant here; it was the thing pushing the theme toggle off the edge */
  body:not(.landing) .topbar-back { display: none !important; }
}
@media (max-width: 480px) { body:not(.landing) .topbar [data-sound-toggle] { display: none; } }
@media (prefers-reduced-motion: no-preference) { .tb-item { transition: color .18s var(--e-soft, ease); } }
:root[data-theme="light"] .tabbar { box-shadow: 0 -8px 24px -18px rgba(28, 22, 12, .35); }
/* Home on a first visit: the Create card leads, full width */
#homeView.first-run .hc-primary { grid-column: 1 / -1; }


/* ══════ WAVE 18 · THE AGGRESSIVE DARK (3 Sep 2026, her pick: option B) ══════
   Her call: "make the dark version just a bit more darker, a more of an
   aggressive dark". Option B of the three measured against the real tokens.

   ★ THE ARGUMENT IS NOT MOOD — IT IS THAT A CARD READS AS AN OBJECT.
   A card is only a card because it is a STEP off the page. Measured, in
   relative luminance:

                     page → card      page → panel     card → raised
     wave 12 (old)      2.59x            1.69x            1.44x
     wave 18 (this)     3.65x            2.39x            1.53x

   Nothing about the card colours' relationship to each other changed; the
   floor dropped, so every surface above it lifts further. Text contrast rises
   with it (--text 17.9 → 18.5 on the page, 16.3 → 17.1 on a card; the amber
   9.71 → 10.05), and the hairline does NOT start glowing: rgba(255,255,255,.075)
   composites against whatever it sits on, so its ratio to the ground stays
   1.15 either way (checked, because it was the obvious risk).

   Still a colour and not an absence of one, and still not pure black — the
   design-system rule ("no pure black and no pure white anywhere on the page
   itself") holds: #050507 has a blue lean, like the floor it replaces.

   Every value below moves TOGETHER. Darkening the page alone would leave the
   sidebar, the topbar and --surface-solid reading as pale slabs, because they
   are pinned to literals rather than to the tokens. */
:root:not([data-theme="light"]) {
  --page-base: #050507;   /* was #0a0b0e — the floor */
  --surface-solid: #0c0c0e; /* was #111113 — opaque panels (the Cutting Room state card) */
  --surf-1: #111114;      /* was #17171a — the standard card */
  --surf-2: #17171b;      /* was #1d1d21 — raised / hovered / nested */
  /* ★ PRE-EXISTING AND FIXED HERE: --text-3 was #6f7594, which measured
     3.96:1 on a card — under the 4.5 floor for the captions and labels it is
     used for. The darker ground alone only took it to 4.17, so the token
     itself had to move. #7b81a1 measures 4.93 on a card, 5.33 on the page and
     4.68 even on a raised surface, so it passes on every ground we have. */
  --text-3: #7b81a1;
}
/* The sidebar and the topbar are pinned to a literal (wave 12), not to a token,
   so they do not follow --page-base on their own. Same relative step as before. */
:root:not([data-theme="light"]) body .sidebar,
:root:not([data-theme="light"]) body .topbar { background: #0c0c0e !important; }


/* ══════ WAVE 19 · THE WAIT (3 Sep 2026) ══════
   The longest screen in the product was the emptiest. It now states what is
   running and how much is left; the estimate is quiet because it is an
   estimate, and it sits with the percentage rather than competing with it. */
.cinema-left {
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-2);
  font-variant-numeric: tabular-nums;   /* an estimate that changes must not shift the row */
}
.cinema-left:empty { display: none; }

/* ══════ WAVE 20 · THUMB-SIZED TARGETS ON A PHONE (3 Sep 2026) ══════
   Measured on a 375px viewport across Home, History, Channels, Style profiles
   and Account: a 23px-tall button, 23px switches, 33px chips, 40px inputs and
   a 34px avatar edit. The guideline is 44x44 (UX Peak, and every platform's own
   HIG) and the reason is not pedantry — a target under a thumb's own size is a
   mis-tap, and a mis-tap on a paid action is worse than an ugly one.

   Two techniques, on purpose:
   · text-ish controls simply GROW — they have room and a taller pill reads fine;
   · controls whose SIZE IS THEIR MEANING (a toggle switch, a round avatar
     badge) keep their pixels and gain an invisible hit area, so nothing about
     the design moves.
   The empty states themselves were already right — every one has a message and
   the action that fills it; this is the part that was not. */
@media (max-width: 768px) {
  body:not(.landing) .chm-gfx,
  body:not(.landing) .hh-brand,
  body:not(.landing) .gx-chip,
  body:not(.landing) .gx-go,
  body:not(.landing) .cp-move-go,
  body:not(.landing) .chan-chip,
  body:not(.landing) .btn-primary-sm,
  body:not(.landing) .gs-act,
  body:not(.landing) .gs-empty-go {
    min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  /* every text entry and picker in the app */
  body:not(.landing) .page-scroll input[type="text"],
  body:not(.landing) .page-scroll input[type="email"],
  body:not(.landing) .page-scroll input[type="search"],
  body:not(.landing) .page-scroll input[type="password"],
  body:not(.landing) .page-scroll select { min-height: 44px; }

  /* ── the invisible hit area ───────────────────────────────────────────────
     A switch that is 44px TALL is not a switch any more, so the pixels stay and
     the target grows underneath. Scoped to #accountView because
     `#createView .switch::before` is already the "OFF" caption — a global rule
     here would silently delete that label. */
  #accountView .switch { position: relative; }
  #accountView .switch::before,
  .pf-avatar-edit::before {
    content: ""; position: absolute; top: 50%; left: 50%;
    width: max(44px, 100%); height: 44px;
    transform: translate(-50%, -50%);
    border-radius: inherit;
  }
}


/* ══════ WAVE 21 · THE LANDING'S BACKGROUND IS THE BEAMS (3 Sep 2026, her pick) ══════
   public/beams.js paints the landing's background now: soft diagonal light
   beams drifting on the deep floor (a vanilla port of 21st.dev's "Beams
   Background", retuned to the house amber/cool pair).

   The fourteen soft-light layers this page used to run stand down HERE, on
   body.landing only — the app keeps its still background (30 Aug, her call):
     · body::before          the two corner blooms
     · body background-image the six travelling blooms
     · .bgdeep .bgshaft      the deep blobs and the light shafts (and the
                             architectural grid on .bgdeep::after)
     · .passlight .sweep     the crossing light and the studio sweep
     · .ambient              the three orbs
     · .glow                 the eight per-section lights (their pointer
                             behaviour goes quiet with them — beams are the life now)
   html::after (the frame vignette) stays; the beams wrapper adds its own
   projector falloff on top.

   Specificity note (measured): the bloom rules are `:root:not([data-theme="light"]) body`
   and `:root[data-theme="light"] body`, both (0,2,1) with !important — a plain
   `body.landing` (0,1,1) lost to them and the six blooms kept painting under the
   beams. Both lines below are written at (0,3,1) so they win in both themes. */
html body.landing,
:root:not([data-theme="light"]) body.landing,
:root[data-theme="light"] body.landing { background-image: none !important; }
body.landing::before, body.landing::after { display: none !important; }
body.landing .bgdeep, body.landing .bgshaft, body.landing .passlight,
body.landing .ambient, body.landing .sweep, body.landing .glow, body.landing .scratch { display: none !important; }


/* ═════════ WAVE 23 · THE MOVING LIGHT (4 Sep 2026, her order) ═════════════
   Her references: a spotlight running the edge of a card (image 2), a slow
   animated border on a button (image 5), the primary button carrying a real
   glow (image 4), and the video wall's title lifting off the page (image 3).

   ★ ONE MECHANISM. A conic gradient rotates behind a ring-shaped mask, so a
   single soft highlight travels the border. Cards get it slow and faint;
   buttons get it a little brighter. Nothing follows the cursor — she removed
   the cursor-following card glow on 30 Aug and that decision stands. The light
   moves on its own, at its own pace, whether or not anyone is pointing at it.

   ★ WHY A MASKED PSEUDO-ELEMENT AND NOT A BORDER. tv.css already owns
   border, background, border-radius and box-shadow on every card family
   with !important (wave 7). A gradient border would collide with that wall; a
   ::before ring sits on top of it and needs no !important at all.

   ★ SPEED. 14s per turn on a card, 9s on a button — slow enough to read as
   light rather than as a loading spinner, which is her whole note. The cards
   are staggered so the six do not march in step. Under prefers-reduced-motion
   the ring is still there and simply does not turn.  */
@property --tv-ang { syntax: "<angle>"; initial-value: 0turn; inherits: false; }
@keyframes tvEdgeTurn { to { --tv-ang: 1turn; } }
@keyframes tvFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }

/* the ring itself — shared by cards and buttons */
.tv-edge { position: relative; }
.tv-edge::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1.5px;
  background: conic-gradient(from var(--tv-ang),
    transparent 0turn,
    var(--tv-edge-colour, rgba(245,166,35,.85)) 0.09turn,
    transparent 0.22turn,
    transparent 1turn);
  /* the mask leaves only the 1px frame: fill minus content-box */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
  opacity: var(--tv-edge-rest, .34);
  pointer-events: none; z-index: 0;
  transition: opacity .45s var(--ease, ease);
}
@media (prefers-reduced-motion: no-preference) {
  .tv-edge::before { animation: tvEdgeTurn var(--tv-edge-speed, 14s) linear infinite; }
}
@media (hover: hover) and (pointer: fine) {
  .tv-edge:hover::before { opacity: var(--tv-edge-hover, .95); }
}
/* light theme: the same light, quieter, because it is travelling on paper */
:root[data-theme="light"] .tv-edge { --tv-edge-colour: rgba(196,120,10,.7); --tv-edge-rest: .3; --tv-edge-hover: .8; }

/* ── the six crew cards (her image 2) ──────────────────────────────────────
   The card's own content sits above the ring; the spill is a shadow rather
   than a blurred layer, because .feature clips its overflow and a glow drawn
   outside it would simply be cut off. */
body .feature { position: relative; }
body .feature > * { position: relative; z-index: 1; }
body .feature { --tv-edge-speed: 16s; --tv-edge-rest: .38; --tv-edge-hover: 1; }
body .feature:nth-child(2)::before { animation-delay: -2.6s; }
body .feature:nth-child(3)::before { animation-delay: -5.2s; }
body .feature:nth-child(4)::before { animation-delay: -7.8s; }
body .feature:nth-child(5)::before { animation-delay: -10.4s; }
body .feature:nth-child(6)::before { animation-delay: -13s; }
@media (hover: hover) and (pointer: fine) {
  :root:not([data-theme="light"]) body .feature:hover {
    box-shadow: 0 26px 60px -26px rgba(245,166,35,.42), 0 0 0 1px rgba(245,166,35,.20), var(--card-sh) !important;
  }
  :root[data-theme="light"] body .feature:hover {
    box-shadow: 0 26px 60px -30px rgba(196,120,10,.30), 0 0 0 1px rgba(196,120,10,.18), var(--card-sh) !important;
  }
}

/* ── the primary buttons (her images 4 and 5) ──────────────────────────────
   The glow is the button's own light on the surface under it, not a halo drawn
   around the words: amber stays a fill and an edge here, never the label. */
.btn-accent, .prompt-send {
  --tv-edge-colour: rgba(255,255,255,.9);
  --tv-edge-speed: 9s; --tv-edge-rest: .5; --tv-edge-hover: 1;
}
body .btn-accent {
  box-shadow: 0 8px 22px -10px rgba(245,166,35,.75), 0 0 26px -8px rgba(245,166,35,.5),
              inset 0 1px 0 rgba(255,255,255,.28);
}
body .btn-accent:hover {
  box-shadow: 0 12px 30px -10px rgba(245,166,35,.95), 0 0 38px -6px rgba(245,166,35,.7),
              inset 0 1px 0 rgba(255,255,255,.32);
}
/* ── THE PRIMARY BUTTON POPS ON HOVER; IT DOES NOT WASH OUT ────────────────
   Her report, 5 Sep: "the Get started text kind of fades out and is white and
   you cannot even see it any more. I want it to pop out just a tiny bit."
   The label's `color` never actually changed — measured, identical at rest and
   on hover. What washes it is the surround: a 38px amber bloom outside the
   pill and a white inset along its top edge, both growing on hover, with the
   amber field of the new background behind them. The dark ink keeps its value
   while everything around it gets brighter, so it LOSES contrast without ever
   changing colour. That is why it reads as fading.
   So: deepen the ink a step, cut the white inset back rather than up, and give
   the button a small physical pop. It comes toward you instead of dissolving. */
body .btn-accent { transition: transform .16s var(--ease, ease), box-shadow .16s var(--ease, ease); }
body .btn-accent:hover:not(:disabled) {
  color: #120C03;                          /* a step below --accent-ink #1a1206 */
  /* !important because .btn-accent:hover further up this file already sets
     box-shadow with it, and an !important declaration beats specificity. This
     is the local convention here, not a new one. */
  box-shadow: 0 10px 26px -12px rgba(245,166,35,.85), 0 0 22px -10px rgba(245,166,35,.45),
              inset 0 1px 0 rgba(255,255,255,.18) !important;
  transform: translateY(-1px) scale(1.025);
}
body .btn-accent:active:not(:disabled) { transform: translateY(0) scale(.995); }
@media (prefers-reduced-motion: reduce) {
  body .btn-accent:hover:not(:disabled),
  body .btn-accent:active:not(:disabled) { transform: none; }
}
:root[data-theme="light"] body .btn-accent {
  box-shadow: 0 8px 22px -10px rgba(196,120,10,.45), 0 0 24px -10px rgba(245,166,35,.45),
              inset 0 1px 0 rgba(255,255,255,.5);
}

/* ── the app's Generate button (her image 4) ───────────────────────────────
   It is .btn-generate, not .btn-accent, so it needs saying twice. Same light:
   a cast on the surface underneath, stronger while the pointer is on it, and
   the travelling edge — it is the one button in the product that spends money,
   and it should look like the moment it is. */
body .btn-generate {
  box-shadow: 0 4px 20px var(--accent-glow), 0 0 30px -6px rgba(245,166,35,.55),
              inset 0 1px 0 rgba(255,255,255,.3);
}
body .btn-generate:hover:not(:disabled) {
  box-shadow: 0 8px 30px var(--accent-glow), 0 0 44px -4px rgba(245,166,35,.75),
              inset 0 1px 0 rgba(255,255,255,.34);
}
/* a button that cannot act must not glow — a lit control that will not do
   anything is a lie about what is going to happen */
body .btn-generate:disabled { box-shadow: none; }
:root[data-theme="light"] body .btn-generate {
  box-shadow: 0 4px 20px var(--accent-glow), 0 0 26px -8px rgba(196,120,10,.5),
              inset 0 1px 0 rgba(255,255,255,.5);
}

/* ── the video wall's title lifts (her image 3) ────────────────────────────
   Four pixels, eight seconds. Enough that the line is not dead; small enough
   that it never moves while someone is reading a word of it. */
@media (prefers-reduced-motion: no-preference) {
  .thumb-showcase-title { animation: tvFloat 8s var(--ease, ease-in-out) infinite; will-change: transform; }
}


/* ═════════ WAVE 24 · THE STATES THAT NEVER PAINTED (4 Sep 2026) ═══════════
   Measured in the browser, not read off the source:

     .pcard:hover        border stayed rgba(255,255,255,.075) — the amber never came
     .vg-card:hover      byte-identical to idle apart from a 2px lift
     .faq-item[open]     byte-identical to a CLOSED item: no tint, no amber edge
     .cr-call.active     nothing but a 3px lift

   The intent was written three times over three rounds (tv.css 2510, 2522,
   landing 1070) and lost every time to wave 7's card wall, because the wall
   sets the border SHORTHAND at (0,3,1) !important and each of these set
   border-color at (0,2,1). A longhand cannot beat a shorthand at equal
   weight. These are re-stated at the wall's own specificity, with the
   shorthand, after it — the only three things that had to change.

   ★ An OPEN question that looks exactly like a closed one is the one that
   actually costs something: it is the page's own FAQ telling you nothing
   happened when it did. */
:root:not([data-theme="light"]) body .pcard:hover,
:root:not([data-theme="light"]) body .vg-card:hover,
:root:not([data-theme="light"]) body .cr-call:hover,
:root:not([data-theme="light"]) body .faq-item:hover {
  border: 1px solid rgba(245,166,35,.34) !important;
}
:root[data-theme="light"] body .pcard:hover,
:root[data-theme="light"] body .vg-card:hover,
:root[data-theme="light"] body .cr-call:hover,
:root[data-theme="light"] body .faq-item:hover {
  border: 1px solid rgba(196,120,10,.32) !important;
}
/* the voice cards answer with the same lift and ground as every other card */
:root:not([data-theme="light"]) body .vg-card:hover {
  background: var(--surf-2) !important;
  box-shadow: 0 12px 26px -16px rgba(245,166,35,.5) !important;
}
/* ★ open ≠ closed */
:root:not([data-theme="light"]) body .faq-item[open] {
  border: 1px solid rgba(245,166,35,.30) !important;
  background: linear-gradient(180deg, rgba(245,166,35,.08), transparent 58%), var(--surf-2) !important;
}
:root[data-theme="light"] body .faq-item[open] {
  border: 1px solid rgba(196,120,10,.28) !important;
  /* the card token, not a literal white: the design-system rule is that no pure
     white is painted on the page, and wave 13 pins it. */
  background: linear-gradient(180deg, rgba(245,166,35,.10), transparent 58%), var(--surf-1) !important;
}
/* the callout the mock is pointing at */
:root:not([data-theme="light"]) body .cr-call.active {
  border: 1px solid rgba(245,166,35,.42) !important;
  background: var(--surf-2) !important;
}
:root[data-theme="light"] body .cr-call.active {
  border: 1px solid rgba(196,120,10,.36) !important;
}

/* ═════════ WAVE 25 · THE SHADER BACKGROUND, EVERYWHERE (5 Sep 2026) ════════
   Her order: "I like the background we have on the landing page, please apply
   it everywhere else — and wherever the background might be a bit too
   distracting, make sure the only moving thing is just a bit the light beams,
   staying in place and not wandering around like on the landing page."

   So the wrapper's rules live HERE now rather than in landing.html, because
   fourteen pages mount it and two copies of a rule is how two copies drift.
   landing.html keeps only the one rule that is genuinely local to it — the
   standdown of its own .beams — and that one still sits below the .beams block
   in that file for the reason recorded there.

   Calm vs full is decided by the MOUNT, not by CSS: every page but the landing
   writes data-calm on the wrapper and warp-bg.js turns the drift off and slows
   the clock. Nothing here knows the difference.

   ★ DARK THEME ONLY, on every page for the same reason it was on the landing:
   the light theme's type is near-black and all but vanishes on this field.
   The light theme keeps the still background it has always had, and the loop
   stops rather than drawing to a canvas nobody can see. */
.warpbg { position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.warpbg canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
:root[data-theme="light"] .warpbg { display: none; }

/* The shader stands the still layers down, and the light theme hands them back.
   body.warp-bg is added by the script only once WebGL is really working, so a
   machine without it keeps the drifting blobs rather than getting a black page
   — the same fallback the landing has always had. */
body.warp-bg .bgdeep, body.warp-bg .bgshaft { display: none; }
:root[data-theme="light"] body.warp-bg .bgdeep,
:root[data-theme="light"] body.warp-bg .bgshaft { display: block; }

/* ★ THE CALM SCRIM — why a working page needs one and the landing does not.
   The landing is COMPOSED around this field: one huge white headline, and the
   crest happens to fall away from it. Every other page puts ordinary body text
   straight onto the background, and the field's brightest band reaches
   rgb(161,108,8) — measured by running the shader's own maths, and confirmed on
   the Legal page, where the opening paragraph sat on bare amber. Against that
   band --text is 3.75:1 and --text-2 is 2.02:1. Body text at 2:1 is not a look,
   it is unreadable.
   So a calm page lays the page floor back over the field. At .52 the crest
   settles to rgb(80,55,8): --text-2 clears 4.5:1 and --text is comfortably past
   it, while the amber is still amber — deeper, closer to the dark centre and lit
   edges of the frame she pointed at. It is one flat layer, not a gradient: text
   goes wherever the page puts it, and a gradient leaves a bright ring for it to
   land in. The landing is untouched. */
.warpbg[data-calm]::after {
  content: ""; position: absolute; inset: 0;
  background: var(--page-base); opacity: .52;
  pointer-events: none;
}
/* ★ …and DEEPER on a page of dense small print. This is the one place the
   background and the type system genuinely conflict, and the numbers say so:
   at .52 the crest clears 4.5:1 for --text (9.3) and --text-2 (5.0), but
   --text-3 lands at 2.9. Lifting --text-3 instead was tried on paper and
   refused — to clear 4.5 on this field it has to become #a3a7bd, which is
   --text-2 in all but name, so the three text levels collapse into two.
   Deepening the veil everywhere was tried and looked at: at .82 the background
   is all but gone, which is not what she asked for.
   So the depth follows the page. Evidence, Legal and Welcome set 11-13px
   de-emphasised labels straight onto the background — seventeen of them on
   Evidence alone — and there .82 takes the crest to rgb(33,24,7), where every
   token clears 4.5. Everywhere else keeps the background at full presence,
   because nothing small sits on it: the app's Home, Plans and Admin have zero
   --text-3 on the field, measured. */
.warpbg[data-calm="deep"]::after { opacity: .82; }

/* Create recovery and narrow-screen controls. Keep the approved visual language. */
#createView .mk-draft {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-4); margin-bottom: var(--sp-5);
  border: 1px solid var(--accent-border); border-radius: var(--r);
  background: var(--surface-solid, var(--surface)); color: var(--text);
  font-size: var(--fs-sm);
}
#createView .mk-draft[hidden] { display: none; }
#createView .mk-draft-actions { display: flex; gap: var(--sp-3); flex-shrink: 0; }
.mm-format .mm-card { max-width: 600px; }
.mm-format .mm-body { grid-template-columns: minmax(0, 1fr); }
body.step-page .gf-q-total span:last-child { color: var(--text); }
:root[data-theme="light"] body .sp-pill.ok,
:root[data-theme="light"] body #createView .mk-rec-badge.ok { color: #176b42; }
@media (max-width: 560px) {
  #createView .mk-draft { flex-direction: column; align-items: stretch; }
  #createView .mk-draft-actions { flex-wrap: wrap; }
  body #styleView .spf-bar { flex-direction: column; align-items: stretch; gap: var(--sp-3); }
  body #styleView .spf-bar-right { justify-content: flex-end; }
  body #createView .mk-rec { grid-template-columns: 64px minmax(0, 1fr); row-gap: var(--sp-2); }
  body #createView .mk-rec-t { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
  body #createView .mk-rec-badge { grid-column: 2; justify-self: start; }
  body.step-page .sp-q-art { display: none; }
  body.step-page .sp-tabs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); overflow: visible; }
  body.step-page .sp-tab { min-width: 0; white-space: normal; font-size: var(--fs-sm); }
  body.step-page .sp-pill { white-space: nowrap; }
}

/* All working pages share the landing ground, including light mode and
   WebGL fallback. Editor panels keep their neutral viewing surfaces. */
:root body.shared-page-background {
  background-color: transparent !important;
  background-image: none !important;
}
body.shared-page-background::before,
body.shared-page-background::after,
body.shared-page-background .bgdeep,
body.shared-page-background .bgshaft,
body.shared-page-background .passlight,
body.shared-page-background .ambient,
body.shared-page-background .sweep,
body.shared-page-background .glow,
body.shared-page-background .scratch { display: none !important; }
.shared-page-background .beams { position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: hidden; }
.shared-page-background .beams canvas { position: absolute; inset: 0; width: 100%; height: 100%; filter: blur(22px); transform: translateZ(0); }
.shared-page-background .beams::after { content: ""; position: absolute; inset: 0; background: radial-gradient(120% 90% at 50% 40%, transparent 45%, var(--page-base) 100%); opacity: .85; }
body.shared-page-background.warp-bg .beams { display: none; }
:root[data-theme="light"] body.shared-page-background.warp-bg .beams { display: block; }

/* Channel creation and actual graphic previews, 21 September 2026. */
#channelsView .feat-cost, #channelsView .idea-thumb, #channelsView .ai-sum,
#channelsView .an-summary, #channelsView .gs-sub { color: var(--text); opacity: 1; }
#channelsView .gs-empty-go .feat-cost, #channelsView .cp-move-go .feat-cost,
#channelsView .btn-primary-sm .feat-cost, #channelsView .br-go .feat-cost { color: inherit; opacity: 1; }
#channelsView .idea-thumb { background: var(--surf-2); padding: 10px 12px; border-radius: 8px; }
.channel-style-dialog { color: var(--text); background: var(--surf-1); border: 1px solid var(--border); border-radius: 20px; padding: 28px; width: min(520px, calc(100vw - 32px)); box-sizing: border-box; margin: auto; max-height: calc(100dvh - 32px); overflow: auto; }
.channel-style-dialog::backdrop { background: rgba(0,0,0,.65); }
.channel-style-dialog h2 { margin: 0 32px 16px 0; }
.channel-style-dialog p { color: var(--text-2); line-height: 1.6; }
.channel-style-dialog label { display: grid; gap: 12px; }
.channel-style-dialog select { width: 100%; min-height: 48px; background: var(--surf-2); color: var(--text); border: 1px solid var(--border); border-radius: 10px; padding: 10px; }
.channel-style-close { position: absolute; right: 16px; top: 12px; color: var(--text); background: none; border: 0; font-size: 28px; min-width: 44px; min-height: 44px; cursor: pointer; }
.channel-style-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.channel-style-actions button { min-height: 44px; }
.channel-ideas-loading { display: flex; align-items: center; gap: 14px; padding: 28px; color: var(--text); }
.channel-spinner { width: 22px; height: 22px; border: 3px solid var(--border); border-top-color: var(--accent); border-radius: 50%; flex: none; }
@media (prefers-reduced-motion: no-preference) { .channel-spinner { animation: channel-spin 1s linear infinite; } }
@keyframes channel-spin { to { transform: rotate(360deg); } }
.cookie-visual { margin: 0 0 20px 40px; padding: 14px; background: var(--surf-2); border: 1px solid var(--border); border-radius: 12px; color: var(--text); }
.cookie-toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.cookie-toolbar > span { flex: 1; border: 1px solid var(--border); border-radius: 20px; padding: 8px 12px; }
.cookie-extension { padding: 14px 0; }.cookie-extension span { font-weight: 700; }
.cookie-visual small { color: var(--text-2); }.help-step-desc a { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.spf-gfx-grid { grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)) !important; }
.spf-gfx-mock { display: block; aspect-ratio: 16/9; height: auto !important; width: 100%; overflow: hidden; }
.spf-gfx-mock video { width: 100%; height: 100%; object-fit: contain; display: block; }
@media (max-width: 480px) { .spf-gfx-grid { grid-template-columns: minmax(0, 1fr) !important; } .cookie-visual { margin-left: 0; } }

.help-step-body .cookie-visual { margin: 14px 0; }
#channelsView .br-why, #channelsView .br-note, #channelsView .br-none, #channelsView .br-confwhy,
#channelsView .br-caveats li, #channelsView .br-k, #channelsView .br-avoid p,
#channelsView .br-sig, #channelsView .br-load span { color: var(--text); opacity: 1; }
.spf-gfx-card { min-width: 0; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--surf-1); }
.spf-gfx-watch { display: block; width: 100%; border: 0; padding: 10px; background: transparent; color: var(--text); cursor: pointer; text-align: left; }
.spf-gfx-watch > span:last-child { display: block; padding-top: 8px; font-size: 12px; }
#styleView .spf-gfx-card .spf-gfx { width: 100%; height: auto; border: 0; border-top: 1px solid var(--border); border-radius: 0; }
.motion-preview-dialog { width: min(1100px, calc(100vw - 32px)); }
.motion-preview-dialog .spf-gfx-mock { border-radius: 8px; }
body:has(#cookieHelpModal.show) .fb-fab { visibility: hidden; }
@media (max-width: 480px) {
  #cookieHelpModal { padding: 12px; }
  #cookieHelpModal .help-card { padding: 24px 20px; max-height: calc(100dvh - 24px); }
  #cookieHelpModal .help-step { gap: 10px; }
  #cookieHelpModal .help-warn { padding: 12px; }
}
#channelsView .gs-empty-go .feat-cost { display: block; margin: 4px 0 0; }
/* Create and Review use the existing website dropdown and source-card patterns. */
#createView .mk-add-anchor { position: relative; display: inline-flex; }
#createView .mk-add-anchor #mkMenu { top: auto; bottom: calc(100% + var(--sp-2)); left: 0; min-width: min(244px, calc(100vw - 64px)); }
.cs-select .cs-list { background: var(--surface-2-solid); max-height: min(340px, 50vh); overflow-y: auto; overscroll-behavior: contain; }
.cs-list[hidden] { display: none !important; }
.cs-select .cs-opt { white-space: normal; }
.cs-select .cs-opt.sel { color: var(--text); }
.cs-select .cs-opt[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
.cs-select .cs-opt:focus-visible, .cs-select .cs-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#styleView .spf-field .cs-select, .sp-field .cs-select { width: 100%; min-width: 0; }
#styleView .spf-field .cs-btn, .sp-field .cs-btn { width: 100%; justify-content: space-between; text-align: left; }
.cs-val { overflow-wrap: anywhere; }
.sp-page .gf-outliers { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.gf-outlier-meta { padding: 0 var(--sp-3) var(--sp-3); font: inherit; font-size: var(--fs-sm); color: var(--text-2); line-height: 1.5; }
.sp-page .gf-outlier-c { padding-bottom: var(--sp-2); }
@media (max-width: 480px) { .sp-page .gf-outliers { grid-template-columns: minmax(0, 1fr); } }
#channelsView #ytChannelSelection { display: grid; gap: var(--sp-3); padding: var(--sp-5); margin: var(--sp-4) 0; }
#channelsView #ytChannelSelection[hidden] { display: none !important; }
#channelsView #ytChannelSelection .btn { min-height: 44px; padding: 10px 14px; white-space: normal; overflow-wrap: anywhere; text-align: left; }
