  /* ============================================================================
     CLIPKILN DESIGN SYSTEM
     Dark-blue, Swiss-minimal, built for perceived quality. Everything below
     consumes tokens — no raw hex in component rules, so the whole product can be
     retuned from one block.
     ============================================================================ */

  /* Inter, self-hosted (frontend/fonts, whitelisted in main.py). No external
     request on any page load: the typeface arrives with the app or not at all.
     Batch 2 A2: TWO faces of the same family. Inter-Latin.woff2 (113 KB) is the full font
     cut to the characters this site and its guides use plus Latin-1 (pyftsubset, every
     axis and layout feature kept), and it is the one index.html preloads. The full font
     (353 KB) stays declared FIRST with no unicode-range: for overlapping ranges the browser
     tries the LAST face first, so it is only downloaded for a character the subset lacks
     (a title in Cyrillic, say). Same outlines either way: no visual change.
     Regenerate the subset (and this range) with the command in NOTES-batch2.md, round A2. */
  @font-face {
    font-family: "Inter";
    /* Relative to this file (/ui/app.css, /preview/ui/app.css on preview), never a bare
       "/fonts/": on preview that would load production's copy (fix V2). */
    src: url("../fonts/Inter-Variable.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
  }
  @font-face {
    font-family: "Inter";
    src: url("../fonts/Inter-Latin.woff2") format("woff2");
    font-weight: 100 900;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0, U+20-7E, U+A0-AC, U+AE-FF, U+103, U+131, U+152-153, U+159, U+17D, U+21B, U+250, U+259-25A, U+26A, U+26C, U+279, U+2B2, U+2BB-2BC, U+2C6, U+2C8, U+2DA, U+2DC, U+304, U+308, U+3C6, U+414, U+430, U+2000-200B, U+2010-2027, U+202F-2055, U+2057, U+205F, U+20AC, U+2122, U+2190-2194, U+2197, U+2212, U+221A, U+222B, U+2248, U+2264, U+25B6, U+25C0, U+2600, U+2605, U+26A0, U+2713, U+2717, U+2764, U+2B06, U+FEFF;
  }

  /* The tokens (one :root) and the @layer order live in frontend/ui/tokens.css, inlined above. */
  @layer base {

  * { box-sizing: border-box; }

  html { -webkit-text-size-adjust: 100%; }

  body {
    margin: 0;
    font-family: var(--font);
    font-size: var(--fs-base);
    line-height: 1.6;
    letter-spacing: -0.006em;
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-feature-settings: "cv02", "cv03", "cv04", "ss01";
  }

  /* Display type: the single biggest lever on whether a page reads as premium.
     Large, tight, and heavy — the opposite of the uniform 20px it replaced. */
  h1, h2, h3, h4 {
    letter-spacing: -0.03em;
    line-height: 1.12;
    font-weight: 640;
    margin: 0 0 var(--s3);
    text-wrap: balance;
  }
  h1 { font-size: var(--fs-3xl); }
  h2 { font-size: var(--fs-2xl); }
  h3 { font-size: var(--fs-lg); letter-spacing: -0.02em; }
  h4 { font-size: var(--fs-md); letter-spacing: -0.015em; }
  p  { margin: 0 0 var(--s4); }

  a { color: var(--accent-text); text-decoration: none; transition: color var(--t-fast) var(--ease); }
  a:hover { color: var(--accent-text-2); }

  ::selection { background: var(--accent-line); color: #FFFFFF; }

  /* Focus: visible, consistent, never removed. */
  :focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 2px;
    border-radius: var(--r-sm);
  }

  ::-webkit-scrollbar { width: 11px; height: 11px; }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: var(--border-3); border-radius: var(--r-full);
    border: 3px solid var(--bg);
  }
  ::-webkit-scrollbar-thumb:hover { background: var(--muted-2); }
  @media (prefers-reduced-motion: reduce) {

  /* Motion is opt-out at the platform level, per the pre-delivery checklist. */
    *, *::before, *::after {
      animation-duration: .001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: .001ms !important;
      scroll-behavior: auto !important;
    }
  }
  }
  @layer components {

  /* ---------------------------------------------------------------- header --- */
  /* ⚠️ `body > header`, NOT a bare `header`. This rule is sticky at top:0 with z-index 50 —
     ABOVE .appnav's 40 — and a bare element selector caught every other <header> in the app
     too. connGroup() builds each Connections platform block as `<header class="conn-row
     conn-platform-head">`, so the "YouTube Shorts" row inherited all of this: it detached on
     scroll, stuck to the top of the viewport and painted OVER the tab bar, instead of sliding
     behind it like every other tab. The page header is the only direct child of <body>, so
     scoping it there means what this rule always intended.
     The two mobile overrides below carry `body >` as well, to keep the same specificity —
     without it this rule (0,0,2) would outrank them (0,0,1) and their padding would stop
     applying. */
  body > header {
    display: flex; justify-content: space-between; align-items: center;
    padding: var(--s4) var(--s6);
    border-bottom: 1px solid var(--border);
    background: rgba(6, 10, 18, 0.72);
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    position: sticky; top: 0; z-index: var(--z-header);
  }
  header h1 {
    font-size: var(--fs-lg); margin: 0; font-weight: 680;
    letter-spacing: -0.03em; display: flex; align-items: center; gap: 8px;
  }
  /* The C mark, kept — drawn rather than an image so it stays crisp at any size. */
  header h1::before {
    content: "C";
    display: grid; place-items: center;
    width: 30px; height: 30px; border-radius: 9px;
    font-size: 17px; font-weight: 750; letter-spacing: -0.04em;
    color: var(--accent-ink);
    background: linear-gradient(150deg, var(--accent-2), var(--accent));
    box-shadow: 0 2px 10px var(--accent-glow);
  }
  header h1 .wm { display: inline; letter-spacing: -0.035em; }
  header h1 .wm i { font-style: normal; color: var(--accent-text); }

  #userbar {
    font-size: var(--fs-sm); color: var(--muted);
    display: flex; gap: var(--s3); align-items: center;
  }
  #userbar button {
    background: transparent; border: 1px solid var(--border-2); color: var(--text);
    padding: 8px 13px; border-radius: var(--r-full); cursor: pointer;
    font: inherit; font-size: var(--fs-sm); font-weight: 520;
    transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
  }
  #userbar button:hover { background: var(--panel-2); border-color: var(--border-3); }

  main { max-width: none; margin: 0; padding: 0; }
  #appSection { max-width: 1680px; margin: 0 auto; padding: 0 var(--s5) var(--s9); }

  /* ------------------------------------------------------------------ tabs --- */
  /* A segmented control rather than a row of buttons: one recessed track, one
     raised active pill. Reads as a control, not as navigation debris. */
  .tabs {
    display: flex; gap: 2px; margin-bottom: var(--s6); flex-wrap: wrap;
    background: var(--panel); padding: 8px; border-radius: var(--r-lg);
    border: 1px solid var(--border); width: fit-content; max-width: 100%;
  }
  .tabs button {
    background: transparent; border: 0; color: var(--muted);
    padding: 8px 13px; border-radius: 11px; cursor: pointer;
    font: inherit; font-size: var(--fs-sm); font-weight: 560; white-space: nowrap;
    transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
  }
  .tabs button:hover { color: var(--text); background: var(--panel-2); }
  .tabs button.active {
    background: var(--panel-3); color: var(--text);
    box-shadow: var(--sh-1), inset 0 1px 0 rgba(255, 255, 255, .06);
  }

  /* ----------------------------------------------------------------- cards --- */
  .card {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    padding: var(--s5);
    margin-bottom: var(--s5);
    box-shadow: var(--sh-1);
  }
  .card h2, .card h3 { margin-top: 0; }

  /* The MCP tab's connector link: a thing to copy, not prose. Left-aligned in its own box so
     a long link selects cleanly; the card styling comes from the guide kit below. */
  #tab-mcp pre.mcp-link { text-align: left; margin: 0 0 var(--s3); white-space: pre-wrap; word-break: break-all; }
  }
  @layer screens {

  /* ---- The guide kit: the Clip & Earn tab and the MCP page. Scoped to those two tabs, so
     nothing else on the site moves; :is() carries the id's specificity, so Clip & Earn renders
     exactly as it did when it was scoped to that tab alone. The .earn-* names are historical:
     they are the shared guide classes now.
     Two layers: skim cards (step badge, bold "do this", bullets) and <details> deep dives.
     Graphics are inline SVG coloured from these variables — no third-party images.
     Callouts: amber = watch out, accent = Clipkiln. */
  :is(#tab-earn, #tab-mcp) .card { scroll-margin-top: calc(var(--hdr, 55px) + 89px); }
  }
  @layer components {
  :is(#tab-earn, #tab-mcp) .earn-hero { background: linear-gradient(180deg, var(--panel-2), var(--panel)); }
  }
  @layer screens {
  :is(#tab-earn, #tab-mcp) .earn-kicker { color: var(--accent-text-2); font-size: var(--fs-xs); letter-spacing: .04em; text-transform: uppercase; margin: 0 0 var(--s2); }
  :is(#tab-earn, #tab-mcp) .card h2.earn-title {
    font-size: var(--fs-2xl); line-height: 1.15; color: var(--text); text-transform: none;
    letter-spacing: -0.01em; margin: 0 0 var(--s3);
  }
  :is(#tab-earn, #tab-mcp) .earn-lede { font-size: var(--fs-md); line-height: 1.6; color: var(--muted); margin: 0 0 var(--s4); }
  :is(#tab-earn, #tab-mcp) .earn-inline-more { color: var(--accent-text-2); font-weight: 600; }
  :is(#tab-earn, #tab-mcp) h3 { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); }
  :is(#tab-earn, #tab-mcp) .earn-n {
    flex: 0 0 auto; width: 30px; height: 30px; border-radius: var(--r-full);
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent-soft); border: 1px solid var(--accent-line);
    color: var(--accent-text-2); font-size: var(--fs-sm); font-weight: 700;
  }
  :is(#tab-earn, #tab-mcp) .earn-n-warn { background: var(--amber-soft); border-color: var(--amber-line); color: var(--amber); }
  :is(#tab-earn, #tab-mcp) .earn-do { font-weight: 600; color: var(--text); margin: 0 0 var(--s3); }
  :is(#tab-earn, #tab-mcp) ul, :is(#tab-earn, #tab-mcp) ol { margin: 0 0 var(--s3); padding-left: 1.15em; color: var(--muted); line-height: 1.6; }
  :is(#tab-earn, #tab-mcp) li { margin-bottom: var(--s1); }
  :is(#tab-earn, #tab-mcp) li strong, :is(#tab-earn, #tab-mcp) .earn-check strong, :is(#tab-earn, #tab-mcp) .earn-deep strong { color: var(--text); }
  :is(#tab-earn, #tab-mcp) .earn-check { color: var(--muted); margin: 0 0 var(--s3); }
  :is(#tab-earn, #tab-mcp) a:not(.earn-badge):not(.earn-cta) { color: var(--accent-text-2); }
  :is(#tab-earn, #tab-mcp) code { background: var(--panel-3); padding: 1px 8px; border-radius: 6px; font-size: .9em; }

  :is(#tab-earn, #tab-mcp) .earn-steps { display: flex; flex-wrap: wrap; gap: var(--s2); }
  :is(#tab-earn, #tab-mcp) .earn-steps a {
    padding: 8px 13px; border-radius: var(--r-full); font-size: var(--fs-sm);
    background: var(--panel-2); border: 1px solid var(--border-2); color: var(--text); text-decoration: none;
  }
  :is(#tab-earn, #tab-mcp) .earn-steps a:hover { border-color: var(--accent-line); }

  :is(#tab-earn, #tab-mcp) .earn-warn, :is(#tab-earn, #tab-mcp) .earn-kiln {
    padding: var(--s3) var(--s4); border-radius: var(--r); margin: 0 0 var(--s3);
    font-size: var(--fs-sm); line-height: 1.6; color: var(--text);
  }
  :is(#tab-earn, #tab-mcp) .earn-warn { background: var(--amber-soft); border: 1px solid var(--amber-line); }
  :is(#tab-earn, #tab-mcp) .earn-kiln { background: var(--accent-soft); border: 1px solid var(--accent-line); }

  /* Name badges — our own pills, deliberately NOT any brand's mark or colours. */
  :is(#tab-earn, #tab-mcp) .earn-badge {
    display: inline-block; padding: 8px 8px; border-radius: var(--r-full); font-size: var(--fs-xs);
    font-weight: 700; white-space: nowrap; text-decoration: none; color: var(--text);
    border: 1px solid var(--border-3); background: var(--panel-3);
  }
  :is(#tab-earn, #tab-mcp) .earn-b1 { border-color: var(--violet-line); background: var(--violet-soft); }
  :is(#tab-earn, #tab-mcp) .earn-b2 { border-color: var(--cyan-line); background: var(--cyan-soft); }
  :is(#tab-earn, #tab-mcp) .earn-b3 { border-color: var(--accent-line); background: var(--accent-soft); }

  :is(#tab-earn, #tab-mcp) .earn-table-wrap { overflow-x: auto; margin: 0 0 var(--s3); }
  :is(#tab-earn, #tab-mcp) .earn-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
  :is(#tab-earn, #tab-mcp) .earn-table th, :is(#tab-earn, #tab-mcp) .earn-table td {
    text-align: left; vertical-align: top; padding: var(--s2); border-bottom: 1px solid var(--border-2); color: var(--muted);
  }
  :is(#tab-earn, #tab-mcp) .earn-table th { font-weight: 600; color: var(--muted-2); }
  :is(#tab-earn, #tab-mcp) .earn-table td:first-child { color: var(--text); }
  :is(#tab-earn, #tab-mcp) .earn-compare { min-width: 560px; }

  :is(#tab-earn, #tab-mcp) details {
    margin-top: var(--s2); border: 1px solid var(--border-2); border-radius: var(--r); background: var(--bg-2);
  }
  :is(#tab-earn, #tab-mcp) summary {
    cursor: pointer; list-style: none; padding: var(--s3) var(--s4); color: var(--text);
    font-size: var(--fs-sm); font-weight: 600; display: flex; align-items: center; gap: var(--s2);
  }
  :is(#tab-earn, #tab-mcp) summary::-webkit-details-marker { display: none; }
  :is(#tab-earn, #tab-mcp) summary::before {
    content: "+"; flex: 0 0 auto; width: 20px; height: 20px; border-radius: var(--r-full);
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent-soft); color: var(--accent-text-2); font-weight: 700;
  }
  :is(#tab-earn, #tab-mcp) details[open] summary::before { content: "\2212"; }
  :is(#tab-earn, #tab-mcp) .earn-deep { padding: 0 var(--s4) var(--s3); font-size: var(--fs-sm); line-height: 1.65; color: var(--muted); }
  :is(#tab-earn, #tab-mcp) .earn-deep p { margin: 0 0 var(--s3); }

  :is(#tab-earn, #tab-mcp) .earn-calc {
    border: 1px solid var(--accent-line); border-radius: var(--r-lg); padding: var(--s4);
    background: var(--panel-2); margin: 0 0 var(--s4);
  }
  :is(#tab-earn, #tab-mcp) .earn-calc-title { font-weight: 700; color: var(--text); margin: 0 0 var(--s3); }
  :is(#tab-earn, #tab-mcp) .earn-calc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s3); }
  :is(#tab-earn, #tab-mcp) .earn-calc label { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-xs); color: var(--muted); }
  :is(#tab-earn, #tab-mcp) .earn-calc input { width: 100%; box-sizing: border-box; }
  :is(#tab-earn, #tab-mcp) .earn-calc-out { margin: var(--s4) 0 var(--s2); }
  :is(#tab-earn, #tab-mcp) #earnPay { font-size: var(--fs-2xl); font-weight: 800; color: var(--good); margin-right: var(--s2); }
  :is(#tab-earn, #tab-mcp) .earn-calc-why { color: var(--muted); font-size: var(--fs-sm); }

  :is(#tab-earn, #tab-mcp) .earn-svg { display: block; width: 100%; max-width: 720px; height: auto; margin: var(--s4) auto; }
  :is(#tab-earn, #tab-mcp) .earn-svg text { fill: var(--text); font-family: var(--font); font-size: 14px; font-weight: 600; }
  :is(#tab-earn, #tab-mcp) .earn-svg .earn-t-sm { fill: var(--muted); font-size: 12px; font-weight: 500; }
  :is(#tab-earn, #tab-mcp) .earn-svg .earn-t-big { font-size: 18px; fill: var(--good); font-weight: 800; }
  :is(#tab-earn, #tab-mcp) .earn-svg .earn-t-cap { fill: var(--amber); }
  :is(#tab-earn, #tab-mcp) .earn-svg .earn-t-hook { font-size: 11px; font-weight: 700; fill: var(--amber); }
  :is(#tab-earn, #tab-mcp) .earn-svg .earn-t-cap1 { font-size: 11px; fill: var(--text); }
  :is(#tab-earn, #tab-mcp) .earn-svg .earn-t-cap2 { font-size: 13px; font-weight: 800; fill: var(--accent-text-2); }
  :is(#tab-earn, #tab-mcp) .earn-node rect { fill: var(--panel-2); stroke: var(--border-3); }
  :is(#tab-earn, #tab-mcp) .earn-node-good rect { fill: var(--good-soft); stroke: var(--good); }
  :is(#tab-earn, #tab-mcp) .earn-f-accent { fill: var(--accent); }
  :is(#tab-earn, #tab-mcp) .earn-f-accent-soft { fill: var(--accent-soft); stroke: var(--accent-line); }
  :is(#tab-earn, #tab-mcp) .earn-f-amber { fill: var(--amber); }
  :is(#tab-earn, #tab-mcp) .earn-f-amber-soft { fill: var(--amber-soft); stroke: var(--amber-line); }
  :is(#tab-earn, #tab-mcp) .earn-f-good { fill: var(--good); }
  :is(#tab-earn, #tab-mcp) .earn-f-danger { fill: var(--danger); }
  :is(#tab-earn, #tab-mcp) .earn-f-panel { fill: var(--panel-3); }
  :is(#tab-earn, #tab-mcp) .earn-s-accent { fill: none; stroke: var(--accent); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
  :is(#tab-earn, #tab-mcp) .earn-s-thick { stroke-width: 4; }
  :is(#tab-earn, #tab-mcp) .earn-s-line { stroke: var(--border-3); stroke-width: 1.5; }
  :is(#tab-earn, #tab-mcp) .earn-s-cap { stroke: var(--amber); stroke-width: 1.5; stroke-dasharray: 6 5; }
  :is(#tab-earn, #tab-mcp) .earn-bar rect { fill: var(--accent); }
  :is(#tab-earn, #tab-mcp) .earn-bar rect.earn-ghost { fill: var(--accent-soft); stroke: var(--accent-line); stroke-dasharray: 4 4; }
  /* Phone stand-in for the two flow diagrams: numbered chips joined by arrows. */
  :is(#tab-earn, #tab-mcp) .earn-flow-list {
    display: none; flex-direction: column; gap: var(--s2); list-style: none; counter-reset: earnflow;
    padding: 0; margin: var(--s4) 0;
  }
  :is(#tab-earn, #tab-mcp) .earn-flow-list li {
    counter-increment: earnflow; margin: 0; padding: var(--s2) var(--s3); border-radius: var(--r);
    background: var(--panel-2); border: 1px solid var(--border-3); color: var(--text); font-weight: 600;
  }
  :is(#tab-earn, #tab-mcp) .earn-flow-list li::before { content: counter(earnflow); color: var(--accent-text-2); margin-right: var(--s2); }
  :is(#tab-earn, #tab-mcp) .earn-flow-list li:last-child { background: var(--good-soft); border-color: var(--good); }

  :is(#tab-earn, #tab-mcp) .earn-split { display: grid; grid-template-columns: 1fr 200px; gap: var(--s5); align-items: start; }
  :is(#tab-earn, #tab-mcp) .earn-phone { margin: 0; max-width: 200px; }
  :is(#tab-earn, #tab-mcp) .earn-phone-body { fill: var(--panel-3); stroke: var(--border-3); }
  :is(#tab-earn, #tab-mcp) .earn-phone-screen { fill: var(--bg); }
  :is(#tab-earn, #tab-mcp) .earn-legend { list-style: none; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); font-size: var(--fs-sm); }
  :is(#tab-earn, #tab-mcp) .earn-dot { display: inline-block; width: 10px; height: 10px; border-radius: 3px; margin-right: 8px; vertical-align: middle; }
  :is(#tab-earn, #tab-mcp) .earn-dot-amber { background: var(--amber); }
  :is(#tab-earn, #tab-mcp) .earn-dot-accent { background: var(--accent); }
  :is(#tab-earn, #tab-mcp) .earn-dot-panel { background: var(--panel-3); border: 1px solid var(--border-3); }

  :is(#tab-earn, #tab-mcp) .earn-sources { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--s4); }
  :is(#tab-earn, #tab-mcp) .earn-src-h { font-weight: 700; color: var(--text); margin: 0 0 var(--s2); font-size: var(--fs-sm); }
  }
  @layer components {
  :is(#tab-earn, #tab-mcp) .earn-cta {
    display: inline-block; padding: 8px 21px; border-radius: var(--r); font-weight: 700; text-decoration: none;
    background: var(--accent); color: var(--accent-ink);
  }
  }
  @layer screens {
  :is(#tab-earn, #tab-mcp) .earn-fine { color: var(--muted-2); font-size: var(--fs-xs); line-height: 1.6; margin: var(--s3) 0 0; }
  @media (max-width: 640px) {

  /* Phones: the phone mock-up drops under the bullets; the wide diagrams keep their
     proportions (viewBox) and just get smaller. */
    :is(#tab-earn, #tab-mcp) .card h2.earn-title { font-size: var(--fs-xl); }
    :is(#tab-earn, #tab-mcp) .earn-split { grid-template-columns: 1fr; }
    :is(#tab-earn, #tab-mcp) .earn-phone { margin: 0 auto; max-width: 180px; }
    :is(#tab-earn, #tab-mcp) .earn-flow, :is(#tab-earn, #tab-mcp) .earn-kiln-flow { display: none; }
    :is(#tab-earn, #tab-mcp) .earn-flow-list { display: flex; }
    :is(#tab-earn, #tab-mcp) .earn-bars text, :is(#tab-earn, #tab-mcp) .earn-timeline text { font-size: 22px; }
    :is(#tab-earn, #tab-mcp) .earn-bars .earn-t-sm, :is(#tab-earn, #tab-mcp) .earn-timeline .earn-t-sm { font-size: 19px; }
    /* The comparison table becomes one card per program, each cell labelled. */
    :is(#tab-earn, #tab-mcp) .earn-compare { min-width: 0; }
    :is(#tab-earn, #tab-mcp) .earn-compare thead { display: none; }
    :is(#tab-earn, #tab-mcp) .earn-compare tr { display: block; padding: var(--s3) 0; border-bottom: 1px solid var(--border-2); }
    :is(#tab-earn, #tab-mcp) .earn-compare td { display: block; border: 0; padding: 2px 0; }
    :is(#tab-earn, #tab-mcp) .earn-compare td[data-label]:not(:first-child)::before {
      content: attr(data-label) ": "; color: var(--muted-2); font-weight: 600;
    }
    :is(#tab-earn, #tab-mcp) .earn-compare td:first-child { margin-bottom: var(--s2); }
  }
  }
  @layer components {

  /* --------------------------------------------------------------- buttons --- */
  button {
    font-family: inherit;
    transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
                color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease),
                box-shadow var(--t-fast) var(--ease);
  }
  button:disabled { opacity: .45; cursor: not-allowed; }
  /* Library full: looks disabled but stays clickable, so a click can say why. */
  body.saved-full .save-to-account:not(:disabled) { opacity: .45; cursor: not-allowed; }

  .primary, .inline-primary {
    background: linear-gradient(180deg, var(--accent-2), var(--accent));
    color: var(--accent-ink);
    border: 0; border-radius: var(--r-full);
    padding: 13px 21px; cursor: pointer;
    font-size: var(--fs-base); font-weight: 620; letter-spacing: -0.011em;
    box-shadow: 0 1px 2px rgba(0, 0, 8, .3), 0 6px 20px var(--accent-glow);
  }
  .primary:hover:not(:disabled), .inline-primary:hover:not(:disabled) {
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(0, 0, 8, .3), 0 10px 30px var(--accent-glow);
  }
  .primary:active:not(:disabled), .inline-primary:active:not(:disabled) { transform: translateY(0); }
  .primary:disabled, .inline-primary:disabled { box-shadow: none; }

  .secondary {
    background: var(--panel-2); color: var(--text);
    border: 1px solid var(--border-2); border-radius: var(--r-full);
    padding: 8px 21px; cursor: pointer;
    font-size: var(--fs-base); font-weight: 550; letter-spacing: -0.011em;
  }
  .secondary:hover:not(:disabled) { background: var(--panel-3); border-color: var(--border-3); }

  .danger-btn, .danger {
    background: var(--danger-soft); color: var(--danger);
    border: 1px solid rgba(255, 107, 107, .28);
  }
  .danger-btn:hover:not(:disabled), .danger:hover:not(:disabled) {
    background: rgba(255, 107, 107, .18); border-color: rgba(255, 107, 107, .45);
  }
  }
  @layer base {

  /* ----------------------------------------------------------------- forms --- */
  input[type="text"], input[type="email"], input[type="password"], input[type="number"],
  input[type="time"], input[type="search"], input[type="url"], select, textarea {
    font: inherit; font-size: var(--fs-base); color: var(--text);
    background: var(--bg-2);
    border: 1px solid var(--border-2);
    border-radius: var(--r); padding: 8px 13px;
    transition: border-color var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease),
                background var(--t-fast) var(--ease);
  }
  }
  @layer components {
  input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) {
    border-color: var(--border-3);
  }
  }
  @layer base {
  input:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
    background: var(--panel);
  }
  }
  @layer components {
  input::placeholder, textarea::placeholder { color: var(--muted-2); }
  input:disabled, select:disabled, textarea:disabled { opacity: .5; cursor: not-allowed; }
  }
  @layer base {
  textarea { line-height: 1.6; resize: vertical; }
  }
  @layer components {
  label { font-size: var(--fs-sm); color: var(--muted); font-weight: 520; }

  /* Range inputs are where cheap UIs give themselves away — the browser default is
     unstyled on every platform. One consistent track and thumb everywhere. */
  input[type="range"] {
    -webkit-appearance: none; appearance: none;
    height: 4px; border-radius: var(--r-full);
    background: var(--panel-3);
    cursor: pointer; padding: 0; border: 0;
  }
  input[type="range"]:disabled { cursor: not-allowed; opacity: .5; }
  input[type="range"]::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 17px; height: 17px; border-radius: 50%;
    background: #FFFFFF; border: 0;
    box-shadow: 0 1px 4px rgba(0, 0, 8, .45), 0 0 0 1px rgba(0, 0, 8, .16);
    transition: transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
  }
  input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.14); }
  input[type="range"]::-moz-range-thumb {
    width: 17px; height: 17px; border-radius: 50%;
    background: #FFFFFF; border: 0; box-shadow: 0 1px 4px rgba(0, 0, 8, .45);
  }
  input[type="range"]::-moz-range-track { height: 4px; border-radius: var(--r-full); background: var(--panel-3); }

  input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); width: 16px; height: 16px; cursor: pointer; }

  /* --------------------------------------------------------------- surfaces --- */
  .hint { color: var(--muted); font-size: var(--fs-sm); line-height: 1.55; }
  .muted { color: var(--muted); }
  code, pre, .mono { font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; }
  }
  @layer base {
  code { background: var(--panel-2); padding: 2px 8px; border-radius: 6px; font-size: .9em; }
  }
  @layer components {
  hr { border: 0; border-top: 1px solid var(--border); margin: var(--s5) 0; }



  /* ---------------------------------------------------------------- app nav ---
     Was a 180px sidebar, which cost a fifth of the width on every screen and made
     the app read as a prototype. Now a full-width bar directly under the header:
     one recessed track, one raised active pill. */
  .appnav {
    position: sticky; top: var(--hdr, 61px); z-index: var(--z-nav);
    margin: 0 0 var(--s5); padding: 0 var(--s5);
    background: rgba(6, 10, 18, 0.78);
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    border-bottom: 1px solid var(--border);
  }
  .appnav-track {
    display: flex; justify-content: center; gap: 8px; padding: 13px 0;
    max-width: 1680px; margin: 0 auto; overflow-x: auto;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .appnav-track::-webkit-scrollbar { display: none; }
  .appnav-track button {
    background: transparent; border: 1px solid transparent; color: var(--muted);
    padding: 13px 21px; border-radius: 12px; cursor: pointer; white-space: nowrap;
    font: inherit; font-size: var(--fs-base); font-weight: 570; letter-spacing: -0.012em;
    transition: color var(--t-fast) var(--ease), background var(--t-fast) var(--ease),
                border-color var(--t-fast) var(--ease);
  }
  .appnav-track button:hover { color: var(--text); background: var(--panel-2); }
  /* The active tab gets a light edge and a lift — unmistakable at a glance, which the
     old low-contrast fill was not. */
  /* The selected tab is white with black text — the highest contrast pair available,
     so there is never a question which section you are in. */
  .appnav-track button.active {
    background: #FFFFFF; color: #000008; font-weight: 640;
    border-color: #FFFFFF;
    box-shadow: 0 2px 10px rgba(0, 0, 8, .45), 0 0 0 4px rgba(255, 255, 255, .07);
  }
  .appnav-track button.active:hover { background: #FFFFFF; color: #000008; }
  .appnav-track button.preview-locked { position: relative; }
  @media (min-width: 769px) and (max-width: 1679px) {
  /* 13 tabs (Clip & Earn added) overflow the bar between the phone layout and ~1680px,
     and a CENTRED overflowing flex row pushes Home off the left edge where no scroll can
     reach it. Tighter padding in that range makes the row fit again (measured: 1366px
     overflowed at the real 22px padding); `safe center` is the backstop — it centres when
     the row fits and falls back to start-aligned when it does not. Wider screens and
     the ≤768px phone nav are untouched. */
    .appnav-track { justify-content: safe center; }
    /* #appSection: `#appSection button` (further down) sets the real 22px padding. */
    #appSection .appnav-track button { padding: 13px 13px; }
  }
  @media (min-width: 769px) and (max-width: 1300px) {
    #appSection .appnav-track button { padding: 13px 8px; }
  }

  .layout { display: block; }
  .content { min-width: 0; }

  /* Density. The old shell wasted a third of the viewport on gutters and a sidebar;
     these keep the content column wide and the rhythm tight. */

  #appSection .card { padding: var(--s5); margin-bottom: var(--s4); }
  #appSection .card h2 { font-size: var(--fs-lg); margin-bottom: var(--s4); }

  /* ===== component layer: carried through, repainted by the tokens above ===== */

  /* Subtitle fonts, bundled from the server so each picker option previews in its own
     typeface — and matches exactly what gets burned into the video. "CK " prefix keeps
     them from colliding with a same-named local font that could differ. */
  @font-face { font-family:'CK Anton';           src:url('/fonts/Anton-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Grobold';          src:url('/fonts/Grobold.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Bebas Neue';       src:url('/fonts/BebasNeue-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Oswald';           src:url('/fonts/Oswald.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Montserrat';        src:url('/fonts/Montserrat.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Poppins';           src:url('/fonts/Poppins-Bold.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Roboto';            src:url('/fonts/Roboto-Bold.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Liberation Sans';   src:url('/fonts/LiberationSans-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Carlito';           src:url('/fonts/Carlito-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK DejaVu Sans';       src:url('/fonts/DejaVuSans.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Liberation Serif';  src:url('/fonts/LiberationSerif-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Caladea';           src:url('/fonts/Caladea-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Bangers'; src:url('/fonts/Bangers-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Luckiest Guy'; src:url('/fonts/LuckiestGuy-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Archivo Black'; src:url('/fonts/ArchivoBlack-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Lilita One'; src:url('/fonts/LilitaOne-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Permanent Marker'; src:url('/fonts/PermanentMarker-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Bungee'; src:url('/fonts/Bungee-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Titan One'; src:url('/fonts/TitanOne-Regular.ttf') format('truetype'); font-display:swap; }
  @font-face { font-family:'CK Russo One'; src:url('/fonts/RussoOne-Regular.ttf') format('truetype'); font-display:swap; }
  select.font-preview option { font-size: 16px; }
  .card h2 { margin-top: 0; font-size: 16px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
  }
  @layer base {

  input[type="text"], input[type="email"], input[type="password"], input[type="number"], select {
    width: 100%; padding: 8px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--bg-2); color: var(--text); margin-bottom: 8px; font-size: 14px;
  }
  }
  @layer components {
  input[type="file"] {
    width: 100%; padding: 8px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--bg-2); color: var(--text); margin-bottom: 8px; font-size: 14px;
  }
  button.secondary {
    background: none; border: 1px solid var(--border); color: var(--text);
    padding: 8px 13px; border-radius: 8px; cursor: pointer; font-size: 13px;
  }
  /* The tab's primary action, painted accent but keeping the .secondary geometry so it
     still sits level with "Preview a script" beside it. Dark ink on the accent for the
     same reason .armed uses it: white on this blue does not carry. */
  button.secondary.accent {
    background: var(--accent); border-color: var(--accent); color: var(--accent-ink);
    font-weight: 600;
  }
  button.secondary.accent:hover:not(:disabled) {
    background: var(--accent-2); border-color: var(--accent-2);
  }
  /* Stop the run. Deliberately NOT accent and NOT danger: it sits inches from "Run once
     now", and two blue buttons side by side get mis-clicked. Filled dark rather than
     outlined so it reads as a real action with equal weight, not a disabled twin. It is
     not danger-red either — stopping a run destroys nothing, and red would say it does. */
  button.secondary.dark {
    background: var(--bg); border-color: var(--border-2); color: var(--text);
    font-weight: 600;
  }
  button.secondary.dark:hover:not(:disabled) {
    background: #000008; border-color: var(--muted);
  }
  button.danger-btn {
    border-color: var(--danger); color: var(--danger);
  }
  button.danger-btn:hover {
    background: var(--danger)22;
  }

  /* ---- Auth screens ---- */
  .auth-screen {
    max-width: 400px; margin: 55px auto 0;
  }
  .auth-screen .card { padding: 34px; }
  .auth-screen h2 { font-size: 22px; color: var(--text); margin-bottom: 21px; text-transform: none; letter-spacing: normal; }
  .auth-footer { text-align: center; margin-top: 13px; color: var(--muted); font-size: 14px; }
  .auth-footer a { color: var(--accent-text); text-decoration: none; cursor: pointer; }
  .auth-footer a:hover { text-decoration: underline; }

  /* Signup consent. The box is never pre-ticked -- see register(). */
  .consent-row {
    display: flex; align-items: flex-start; gap: 8px;
    margin: 8px 0 13px; font-size: 14px; color: var(--muted); line-height: 1.5;
  }
  .consent-row input[type="checkbox"] {
    width: 16px; height: 16px; margin: 2px 0 0; flex: 0 0 auto;
    accent-color: var(--accent); cursor: pointer;
  }
  .consent-row label { cursor: pointer; }
  .consent-row a { color: var(--accent-text); }

  /* "Continue with Google". White with the four-colour G, per Google's sign-in branding
     rules — they are specific about it, and a restyled button reads as a phishing page. */
  .google-btn {
    width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;
    background: #FFFFFF; color: #000044; border: 1px solid #747775; border-radius: var(--r-full);
    padding: 8px 21px; cursor: pointer; font-size: var(--fs-base); font-weight: 560;
    font-family: inherit;
  }
  .google-btn:hover:not(:disabled) { background: #FFFFFF; }
  .google-btn .google-g { width: 18px; height: 18px; flex: 0 0 auto; }
  .auth-or {
    display: flex; align-items: center; gap: 13px; margin: 13px 0;
    color: var(--muted); font-size: 13px;
  }
  .auth-or::before, .auth-or::after {
    content: ""; flex: 1; height: 1px; background: var(--border);
  }

  /* Site footer. The legal documents have to be reachable from inside the app. */
  .site-footer {
    max-width: 960px; margin: 0 auto; padding: 21px 21px 34px;
    border-top: 1px solid var(--border); color: var(--muted); font-size: 13px;
    display: flex; gap: 13px; flex-wrap: wrap;
  }
  .site-footer a { color: var(--muted); text-decoration: none; }
  .site-footer a:hover { color: var(--text); text-decoration: underline; }
  #authError { color: var(--danger); font-size: 13px; min-height: 18px; margin-top: 8px; text-align: center; }

  /* ---- Pills ---- */
  .pill {
    display: inline-block; padding: 3px 8px; border-radius: 999px;
    font-size: 12px; font-weight: 600; text-transform: uppercase;
  }
  .pill.free { background: var(--border-2); color: var(--muted); }
  .pill.paid { background: rgba(0, 221, 0, 0.15); color: var(--good); }
  .pill.base { background: rgba(0, 221, 0, 0.15); color: var(--good); }
  .pill.pro { background: rgba(255, 180, 84, 0.15); color: var(--warn); }
  /* <!--max-plan--> */ .pill.max { background: rgba(255, 180, 84, 0.15); color: var(--warn); } /* <!--/max-plan--> */
  .pill.unlimited { background: linear-gradient(135deg, rgba(0, 221, 0, 0.2), rgba(112, 112, 255, 0.2)); color: #00DD00; }
  .pill.developer { background: rgba(112, 112, 255, 0.15); color: var(--accent-text); }

  .usagebar { height: 6px; background: var(--border-2); border-radius: 4px; overflow: hidden; margin-top: 8px; }
  .usagebar-fill { height: 100%; background: var(--accent-2); }

  /* ---- Clip cards ---- */
  .clip-item {
    border: 1px solid var(--border); border-radius: 10px; padding: 13px;
    margin-bottom: 13px; display: flex; flex-direction: column; gap: 8px;
  }
  .clip-meta { display: flex; justify-content: space-between; align-items: baseline; }
  .clip-title { font-size: 15px; font-weight: 700; color: var(--text); }
  .clip-reason { color: var(--accent-text-2); font-size: 13px; font-weight: 600; margin-top: -8px; }
  .clip-tag { color: var(--accent-text-2); font-size: 12px; font-weight: 600; }
  .clip-score { color: var(--accent-text); font-size: 15px; font-weight: 700; white-space: nowrap; }

  /* --- Evidence-based virality badge + breakdown (services/virality.py) --- */
  .v-holder { position: relative; display: inline-block; }
  .v-panel { position: absolute; top: calc(100% + 6px); left: 0; z-index: var(--z-panel); width: 330px; max-width: 84vw;
    background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
    box-shadow: 0 10px 30px rgba(0, 0, 8, .45); padding: 13px; text-align: left; white-space: normal; cursor: default; }
  .v-panel-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
  .v-score { font-size: 30px; font-weight: 800; line-height: 1; }
  .v-score.great { color: var(--accent-text-2); } .v-score.good { color: var(--good); }
  .v-score.working { color: var(--warn); } .v-score.low { color: var(--muted); }
  .v-score small { font-size: 13px; font-weight: 600; color: var(--muted); }
  .v-band { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
  .v-summary { font-size: 13px; color: var(--text); margin: 8px 0 8px; }
  .v-factor { margin-bottom: 8px; }
  .v-factor-top { display: flex; justify-content: space-between; font-size: 12px; font-weight: 600; color: var(--text); }
  .v-factor-label { cursor: help; border-bottom: 1px dotted var(--border); }
  .v-factor-score { color: var(--muted); }
  .v-bar { height: 5px; border-radius: 999px; background: var(--border); margin: 3px 0; overflow: hidden; }
  .v-bar i { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
  .v-note { font-size: 11.5px; color: var(--muted); line-height: 1.35; }
  .v-stale { font-size: 11.5px; color: var(--muted); line-height: 1.45; margin-bottom: 8px;
    padding: 8px 8px; border-radius: 8px; background: #000044;
    border: 1px solid var(--border); }
  .v-restale { margin-left: 8px; padding: 2px 8px; border-radius: 6px; font: inherit;
    border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer; }
  .v-restale:hover { background: rgba(148, 163, 184, .14); }
  .v-bar-btn.is-stale { opacity: .78; }
  .v-honest { font-size: 11.5px; color: var(--muted); line-height: 1.35;
    margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
  .v-lever { font-size: 12.5px; color: var(--text); margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
  .v-cite { font-size: 10.5px; color: var(--muted); margin-top: 8px; font-style: italic; line-height: 1.35; }
  .story-virality-head { display: flex; align-items: center; gap: 8px; margin-bottom: 13px; }
  /* Prominent above-the-video banner. Colour = virality tier (cyan/green/amber/neutral). */
  .v-banner { display: block; width: 100%; margin: 13px 0 8px; }
  .v-bar-btn { display: flex; align-items: center; gap: 8px; width: 100%; font: inherit;
    cursor: pointer; padding: 8px 13px; border-radius: 10px; border: 1px solid transparent; text-align: left; }
  .v-bar-btn.great   { color: var(--accent-text-2); border-color: var(--accent-2); background: var(--accent-soft);
    box-shadow: 0 0 0 1px var(--accent-line); }
  .v-bar-btn.good    { color: var(--good); border-color: var(--good); background: rgba(0, 221, 0, .14); }
  .v-bar-btn.working { color: var(--warn); border-color: var(--warn); background: rgba(255, 180, 84, .14); }
  .v-bar-btn.low     { color: var(--muted); border-color: var(--border-3); background: #000044; }
  .v-bar-btn.muted   { color: var(--muted); border-color: var(--border); background: transparent;
    justify-content: center; font-weight: 600; }
  .v-bar-fire { font-size: 18px; line-height: 1; }
  .v-bar-num  { font-size: 24px; font-weight: 800; line-height: 1; }
  .v-bar-band { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
  .v-bar-caret { margin-left: auto; font-size: 12px; font-weight: 600; opacity: .85; white-space: nowrap; }
  .clip-excerpt { color: var(--muted); font-size: 13px; font-style: italic; }
  .clip-controls { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
  .clip-controls input[type="number"] { width: 90px; margin: 0; }
  .clip-controls select { width: auto; margin: 0; }
  /* Upload-to-platforms menu */
  .upload-menu { margin-top: 8px; padding: 8px; border: 1px solid rgba(153, 153, 153, .3);
                 border-radius: 8px; max-width: 420px; display: flex; flex-direction: column; gap: 8px; }
  .upload-opt { display: flex; align-items: center; gap: 8px; cursor: pointer; }
  .upload-opt.disabled { opacity: .5; cursor: not-allowed; }
  .upload-opt input { margin: 0; }
  /* TikTok's posting screen inside the upload menu. New classes only, so nothing that
     already exists changes on desktop or mobile. */
  .tt-preview { width: 100%; max-width: 160px; aspect-ratio: 9 / 16; object-fit: cover;
                border-radius: 8px; background: #000008; align-self: center; }
  .tt-banner { padding: 8px 13px; border-radius: 8px; font-weight: 600; line-height: 1.4;
               background: rgba(254, 44, 85, .14); border: 1px solid rgba(254, 44, 85, .55); }
  .tt-group { border: 1px solid rgba(153, 153, 153, .3); border-radius: 8px; padding: 8px 8px;
              margin: 0; display: flex; flex-direction: column; gap: 8px; }
  .tt-group legend { padding: 0 8px; font-size: .92em; }
  .tt-sub { margin: -2px 0 2px 21px; }
  .tt-label { font-weight: 600; }
  .tt-declare { font-size: .88em; line-height: 1.45; color: var(--muted); }
  /* What is still missing, under the greyed Upload now / Schedule buttons. */
  .upload-why { font-size: .82em; line-height: 1.45; padding: 8px 13px; border-radius: 8px;
                background: rgba(254, 44, 85, .10); border: 1px solid rgba(254, 44, 85, .4); }
  .upload-why p { margin: 0 0 3px; font-weight: 600; }
  .upload-why ul { margin: 0; padding-left: 21px; }
  .tt-declare a { color: inherit; text-decoration: underline; }
  /* The "upload is faster" note on the Add a video card. Deliberately quiet — it is a
     helpful aside, not a warning, and it sits between the source picker and the category
     buttons where someone who has just chosen a source will read it. */
  .upload-nudge { margin: 8px 0 13px; padding: 8px 13px; font-size: .9em; line-height: 1.45;
                  color: var(--muted); background: #000044;
                  border: 1px solid rgba(255, 255, 255, .10); border-radius: 8px;
                  max-width: 620px; }
  .upload-nudge strong { color: var(--text); }
  /* The live-stream note under the progress bar (see checkPastedLink). Same quiet box as the
     upload nudge; hidden unless a live link is pasted, so the card is unchanged otherwise. */
  .live-note { margin: 8px 0 0; }
  /* "Your current plan supports" on the Add a video card (renderPlanSupports). */
  .plan-supports ul { margin: 8px 0 0; padding-left: 21px; }
  /* The nudge and the plan list side by side, each at least 300px: on a phone there is no
     room for two, so they wrap and stack. The row carries the nudge's own outer margin. */
  .nudge-row { display: flex; flex-wrap: wrap; gap: 13px; margin: 8px 0 13px; }
  .nudge-row > .upload-nudge { flex: 1 1 300px; margin: 0; }
  .live-note button { margin-left: 8px; padding: 8px 8px; font-size: 13px; }
  /* Edit in CapCut. The panel reuses .upload-menu wholesale; these are only the two
     things it does not already have — a greyed (but still clickable) button, because the
     panel is where the reason for being unavailable is shown, and a radio row. */
  .capcut-opt { display: flex; align-items: flex-start; gap: 8px; cursor: pointer;
                font-size: .92em; line-height: 1.35; }
  .capcut-opt input { margin: 3px 0 0 0; flex: none; }
  .capcut-reason { margin: 0; opacity: .85; }
  #appSection button.capcut-off { opacity: .5; }
  /* One block per platform, so a menu holding three TikToks, two channels and a Drive reads
     as three groups rather than six near-identical ticks. */
  .upload-plat-group { border: 1px solid rgba(153, 153, 153, .25); border-radius: 8px;
                       padding: 8px 8px; display: flex; flex-direction: column; gap: 8px; }
  .upload-plat-group.disabled { opacity: .5; }
  /* What each ticked platform gets (title/description, TikTok's caption + posting screen):
     one box per platform, inside its group under the ticks, folded with the arrow. */
  .upload-sec { border: 1px solid rgba(153, 153, 153, .25); border-radius: 8px; padding: 8px 8px; }
  .upload-sec[open] { display: flex; flex-direction: column; gap: 8px; }
  .upload-sec > summary { cursor: pointer; list-style: none; display: flex; align-items: center;
                          gap: 8px; font-weight: 600; font-size: .85em; }
  .upload-sec > summary::-webkit-details-marker { display: none; }
  .upload-sec > summary::after { content: "▾"; margin-left: auto; opacity: .6; }
  .upload-sec:not([open]) > summary::after { content: "▸"; }
  .upload-plat-head { display: flex; align-items: center; gap: 8px;
                      font-weight: 650; font-size: .9em; }
  .upload-plat-head .conn-mark { width: 17px; height: 17px; }
  .upload-plat-head .conn-mark svg { width: 15px; height: 15px; }
  .upload-opt-acct { padding-left: 8px; }
  .upload-reason { opacity: .7; font-size: .85em; }
  .upload-field { display: flex; flex-direction: column; gap: 8px; font-size: .85em; opacity: .9; }
  .upload-field input, .upload-field textarea {
      font: inherit; padding: 8px 8px; border: 1px solid rgba(153, 153, 153, .35);
      border-radius: 6px; background: #000044; color: inherit; resize: vertical; }
  .upload-fieldnote { font-size: .8em; opacity: .6; margin: 0; }
  .upload-connect { margin-left: auto; font-size: .85em; }
  .upload-acct { margin-left: auto; font-size: .85em; opacity: .8; }
  .upload-status { margin-top: 8px; font-size: .9em; }
  /* Shown only when a player actually failed, so a black rectangle is never the
     whole story the page tells. */
  .video-error { margin: 8px 0; font-size: .9em; color: #C0392B; }
  .upload-status .up-ok { color: #00DD00; }
  .upload-status .up-err, .upload-err { color: #C0392B; }
  .upload-status .up-pending { opacity: .8; }
  /* Connections tab */
  .conn-row { display: flex; align-items: center; gap: 13px; flex-wrap: wrap;
              padding: 13px 0; border-bottom: 1px solid rgba(153, 153, 153, .2); }
  .conn-name { font-weight: 600; min-width: 130px; }
  .conn-row .secondary { margin-left: auto; }
  /* Each platform is a block of its own, so two TikToks and a YouTube do not read as three
     similar lines in one flat list. */
  .conn-platform { border: 1px solid var(--border); border-radius: 10px;
                   padding: 8px 13px; margin-bottom: 13px; }
  .conn-platform-head { border-bottom: 1px solid var(--border); }
  .conn-platform .conn-row:last-child { border-bottom: none; }
  /* One connected account under its platform. Indented and quieter than the platform row
     so a platform with five accounts still reads as one thing, not five. */
  .conn-acct-row { padding-left: 34px; border-bottom-style: dashed; }
  /* The platform's own handle, under a name the user chose — renaming must not hide which
     account actually gets posted to. */
  .conn-handle { display: block; font-weight: 400; font-size: .8rem; opacity: .6; }
  .conn-acct-row .conn-name { font-weight: 500; }
  .conn-acct-row .secondary { margin-left: 8px; }
  .conn-acct-row .secondary:first-of-type { margin-left: auto; }
  .conn-default { font-size: .78rem; opacity: .7; text-transform: uppercase;
                  letter-spacing: .04em; margin-left: auto; }
  .conn-subhead { margin: 21px 0 2px; font-size: 0.98rem; }
  .auto-plat-off { opacity: .55; }
  .auto-plat-conn { flex-wrap: wrap; }
  .auto-plat-conn .conn-name, .auto-plat-conn .upload-reason { opacity: .7; }
  .auto-plat-conn .auto-connect-btn { margin-left: auto; min-height: 44px; }
  #autoEnableReason .auto-connect-btn { display: flex; margin-top: 8px; min-height: 44px; }
  }
  @layer base {
  .num-input { width: 66px; padding: 8px 8px; border:1px solid var(--border); border-radius:6px;
               background: var(--panel); color: var(--text); font-variant-numeric: tabular-nums; }
  }
  @layer components {
  .logo-editor { display:flex; gap:13px; align-items:flex-start; flex-wrap:wrap; margin-top:8px; }
  .logo-controls { flex:1; min-width:220px; display:flex; flex-direction:column; gap:8px; }
  .logo-preview { position:relative; width:112px; height:200px; flex:0 0 auto; border-radius:10px;
    background:linear-gradient(140deg,#000044,#000044); border:1px solid var(--border); overflow:hidden; }
  .logo-preview img { position:absolute; display:none; height:auto; }
  .logo-preview-empty { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    font-size:12px; color:#FFFFFF; text-align:center; padding:8px; }
  }
  @layer screens {

  /* ---- The Other tab: two tools behind one menu ------------------------------------
     All new class names, so nothing here can reach an existing screen. */
  .other-pick { display: flex; gap: 21px; flex-wrap: wrap; margin-top: 8px; }
  }
  @layer components {
  .other-option { flex: 1 1 320px; text-align: left; display: flex; flex-direction: column;
                  gap: 8px; padding: 21px 21px; border-radius: 14px; cursor: pointer;
                  background: var(--card); border: 1px solid var(--border);
                  transition: border-color .15s ease, transform .15s ease; }
  .other-option:hover { border-color: var(--accent); transform: translateY(-2px); }
  }
  @layer screens {
  .other-option-title { font-size: 1.5rem; font-weight: 700; line-height: 1.2; }
  .other-option-desc { color: var(--muted); font-size: .95rem; line-height: 1.5; }
  .other-option-cost { font-size: .85rem; font-weight: 600; color: var(--accent-text); }
  /* ⚠️ These are cards that happen to be <button>s, and `#appSection button` (pill radius,
     12px 22px padding) outranks a bare class — so they rendered as pills whose round ends
     cut through the title and the cost line. #appSection here restores the card's own
     corners and room; the phone rule in the last media block still sets its own padding. */
  #appSection .other-option { padding: 21px 21px; border-radius: var(--r-lg); }
  .other-rate { margin: 0 0 8px; padding: 8px 13px; border-radius: 8px;
                background: rgba(153, 153, 153, .14); font-size: .95rem; line-height: 1.5; }
  .transcript-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
                    margin-bottom: 8px; }
  }
  @layer base {
  .transcript-box { width: 100%; min-height: 420px; max-height: 70vh; resize: vertical;
                    font: 14px/1.7 ui-monospace, SFMono-Regular, Menlo, monospace;
                    padding: 13px; border-radius: 8px; border: 1px solid var(--border);
                    background: var(--bg); color: inherit; }
  }
  @layer components {

  /* Google Drive structure modal */
  .drive-modal { max-width: 520px; width: min(520px, 92vw); text-align: left; }
  }
  @layer screens {
  .bulk-sched-row { margin: 8px 0 8px; }
  /* Same width as the Order by dropdown under it (the full card), centred, dark (secondary). */
  .bulk-sched-row button { display: flex; width: 100%; justify-content: center; align-items: center;
    gap: 8px; flex-wrap: wrap; text-align: center; }
  .modal-box.bulk-sched-modal { max-width: 560px; width: min(560px, 92vw); text-align: left;
    max-height: 90vh; overflow-y: auto; box-sizing: border-box; }
  .bulk-sched-h { font-size: 14px; margin: 13px 0 8px; }
  .bulk-sched-clips { display: flex; flex-direction: column; gap: 8px; max-height: 40vh;
    overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; padding: 8px; }
  .bulk-sched-clips .comp-clip.locked .comp-clip-pick { opacity: .45; }
  .bulk-sched-state { font-size: 12px; color: var(--muted); }
  .bulk-sched-state.bad { color: #FF2A2A; }
  .bulk-sched-time { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0; }
  .bulk-sched-time span { flex: 1 1 180px; min-width: 0; overflow-wrap: anywhere; }
  .bulk-sched-time.clash input { border-color: #FF2A2A; }
  }
  @layer components {
  .bulk-sched-why { font-size: 12px; color: var(--muted); text-align: right; margin: 8px 0 0; }
  }
  @layer screens {
  .bulk-sched-cap { margin: 8px 0; }
  .bulk-sched-cap-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; font-size: 13px; }
  .bulk-sched-cap-head span { min-width: 0; overflow-wrap: anywhere; }
  .bulk-sched-cap textarea { width: 100%; box-sizing: border-box; margin-top: 8px; }
  .bulk-sched-plan { font-size: 13px; color: var(--muted); margin: 8px 0 0; padding-left: 21px; }
  .drive-preview { background: rgba(153, 153, 153, .15); padding: 2px 8px; border-radius: 5px; }
  }
  @layer components {
  .drive-send-bar { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 13px; }
  }
  @layer screens {
  .drive-send-bar .inline-primary { flex: 0 0 auto; }
  .drive-crumbs { font-size: .9rem; margin-bottom: 8px; display: flex; flex-wrap: wrap; gap: 8px; }
  .drive-crumbs a { cursor: pointer; color: var(--accent-text); }
  .drive-crumbs .sep { opacity: .5; }
  .drive-list { max-height: 240px; overflow-y: auto; border: 1px solid var(--border);
                border-radius: 8px; padding: 8px; margin-bottom: 8px; }
  .drive-folder-row { display: flex; align-items: center; gap: 8px; padding: 8px 8px;
                      border-radius: 6px; cursor: pointer; }
  .drive-folder-row:hover { background: #000044; }
  .drive-folder-row .df-name { flex: 1; }
  .drive-folder-row .df-send { flex: 0 0 auto; font-size: .8rem; padding: 3px 8px; }
  .drive-newfolder { display: flex; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
  .drive-newfolder input { flex: 1; min-width: 140px; }

  .clip-hook { flex-basis: 100%; color: var(--muted); font-size: 12px; }
  .clip-hook strong { color: var(--text); font-weight: 600; }
  }
  /* Batch 2 fix V1: components, not screens, so `#appSection .clip-controls button` (components)
     still wins on specificity, as it did before the A1 layers. */
  @layer components {
  .clip-hook-edit { margin-left: 8px; padding: 2px 8px; font-size: 12px; }
  }
  @layer screens {
  .clip-hook-input { width: min(100%, 360px); margin: 0 8px; }
  /* Some subtitle lines could not be translated. --warn, not the red error colour:
     the clip rendered and is genuinely usable, it is just partly in the wrong
     language. The slot is display:contents so an empty one claims no row in the
     .clip-controls flex container. */
  .clip-slot { display: contents; }
  .clip-warning { flex-basis: 100%; color: var(--warn); font-size: 12px; }
  }
  @layer components {

  .subtitle-toggle {
    display: flex; align-items: center; gap: 8px; font-size: 13px;
    color: var(--text); cursor: pointer; user-select: none;
  }
  .subtitle-toggle input[type="checkbox"] { width: auto; margin: 0; cursor: pointer; }
  }
  @layer screens {

  .script-kicker { color: var(--muted); font-size: 12px; text-transform: uppercase;
    letter-spacing: .06em; margin: 0 0 8px; }
  .broll-credit { color: var(--muted); font-size: 12px; flex-basis: 100%; }
  .broll-credit a { color: var(--accent-text-2); }
  /* An anchor showExportResult can refresh the credit into. display:contents so the
     slot itself never becomes a flex item — the layout is exactly as it was when the
     slot is empty, and .broll-credit stays a direct child of .clip-controls when set. */
  .broll-credit-slot { display: contents; }

  .subtitle-size-row {
    display: flex; align-items: center; gap: 8px; font-size: 13px;
  }
  .subtitle-size-row input[type="range"] {
    width: 80px; margin: 0; accent-color: var(--accent); cursor: pointer;
  }
  .subtitle-size-row input[type="range"]:disabled {
    accent-color: var(--border); cursor: not-allowed; opacity: 0.4;
  }
  .subtitle-size-label { color: var(--muted); white-space: nowrap; }
  .subtitle-size-value { color: var(--accent-text); font-weight: 600; min-width: 28px; text-align: right; font-size: 12px; }
  .color-pick { display: inline-flex; align-items: center; gap: 8px; }
  }
  @layer components {
  .color-pick input[type="color"] {
    width: 36px; height: 26px; padding: 0; border: 1px solid var(--border);
    border-radius: 4px; background: none; cursor: pointer;
  }
  }
  @layer screens {
  .color-pick input[type="color"]:disabled { opacity: 0.4; cursor: not-allowed; }
  .color-hex { font-size: 12px; font-family: monospace; color: var(--muted); min-width: 66px; }
  }
  /* Batch 2 fix V1: components, so `#appSection select` keeps winning on specificity. */
  @layer components {
  .subtitle-pos-select {
    background: var(--bg-2); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; padding: 2px 8px; font-size: 12px; cursor: pointer; width: auto;
  }
  .subtitle-pos-select:disabled { opacity: 0.4; cursor: not-allowed; }
  .subtitle-font-select {
    background: var(--bg-2); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; padding: 2px 8px; font-size: 12px; cursor: pointer; width: auto;
  }
  .subtitle-font-select:disabled { opacity: 0.4; cursor: not-allowed; }
  .subtitle-lang-select {
    background: var(--bg-2); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; padding: 2px 8px; font-size: 12px; cursor: pointer; width: auto;
  }
  .subtitle-lang-select:disabled { opacity: 0.4; cursor: not-allowed; }
  }
  @layer screens {
  }
  @layer components {

  /* ---- Crop pan preview (9:16 only, live video) ---- */
  .crop-preview {
    position: relative; width: 100%; aspect-ratio: 16 / 9;
    background: #000008; border: 1px solid var(--border);
    border-radius: 6px; overflow: hidden; margin-top: 8px;
  }
  .crop-preview.hidden { display: none; }
  .crop-preview video {
    width: 100%; height: 100%; object-fit: cover; display: block;
  }
  .crop-window {
    position: absolute; border: 2px solid var(--accent);
    background: rgba(112, 112, 255, 0.08); cursor: grab;
    border-radius: 3px;
    box-shadow: 0 0 0 9999px rgba(0, 0, 8, 0.65), inset 0 0 0 1px rgba(112, 112, 255, 0.3);
    /* ABOVE the picture, whichever thing is painting it. This used to have no z-index and
       relied on being later in the DOM than the <video>. That broke the moment the scrub
       canvas was added between them at z-index 1: the canvas covered the drag box entirely,
       so turning Dynamic position ON made the box vanish — and turning it OFF brought it
       back, because that path plays the video, which hides the canvas. The label above
       already declares z-index 2 for the same reason; the window needs to say so too. */
    z-index: 2;
  }
  .crop-window:hover { background: rgba(112, 112, 255, 0.16); }
  .crop-window.dragging { cursor: grabbing; background: rgba(112, 112, 255, 0.22); }
  .crop-preview-label {
    position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%);
    font-size: 11px; color: #CCCCCC; pointer-events: none;
    text-shadow: 0 0 6px #000008; white-space: nowrap; z-index: 2;
  }

  /* ---- Dynamic video position (per-clip framing, 9:16 only) ---- */
  .dynpos { margin-top: 8px; }
  .dynpos.hidden { display: none; }
  .dynpos-toggle { display: flex; align-items: center; gap: 8px; font-size: 13px; cursor: pointer; }
  .dynpos-hint { color: var(--muted); font-size: 11px; }
  .dynpos-editor { margin-top: 8px; }
  .dynpos-editor.hidden { display: none; }
  .dynpos-autoopts { margin: 8px 0 2px; padding-left: 21px; }
  .dynpos-autoopts.hidden { display: none; }
  .dynpos-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .dynpos-controls button { flex: 0 0 auto; width: auto; padding: 8px 8px; font-size: 12px; }
  .dynpos-status { color: var(--muted); font-size: 11px; margin-left: auto; }

  /* Timeline 1 — the video: filmstrip thumbnails, click/drag to scrub */
  .framing-vidtl {
    position: relative; height: 46px; background: #000008;
    border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
    cursor: pointer; margin-bottom: 8px; user-select: none;
  }
  .framing-strip { position: absolute; inset: 0; display: flex; }
  .framing-tile { flex: 1 1 0; min-width: 0; height: 100%; overflow: hidden; }
  .framing-tile + .framing-tile { border-left: 1px solid rgba(0, 0, 8, 0.55); }
  .framing-tile canvas { width: 100%; height: 100%; object-fit: cover; display: block; }
  .framing-playhead {
    position: absolute; top: 0; bottom: 0; width: 2px; background: #FFFFFF;
    opacity: 0.85; pointer-events: none; z-index: 3;
  }
  .framing-time {
    position: absolute; right: 6px; top: 3px; font-size: 10px; color: #FFFFFF;
    text-shadow: 0 0 4px #000008; z-index: 4; pointer-events: none;
  }

  /* Timeline 2 — the positioning: auto-follow band + orange manual blocks */
  .framing-bandtl {
    position: relative; height: 46px; background: var(--panel, #000044);
    border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
    margin-bottom: 8px; user-select: none;
  }
  .framing-band {
    position: absolute; left: 3px; right: 3px; top: 5px; bottom: 13px;
    border-radius: 6px; background: var(--accent); opacity: 0.4;
  }
  .framing-band.off { background: #3A3F4A; }
  .framing-band-label {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    font-size: 10px; color: rgba(255, 255, 255, 0.8); text-transform: uppercase;
    letter-spacing: 0.8px; pointer-events: none;
  }
  .framing-blocks { position: absolute; inset: 0; }
  /* Speaker overrides (subject locks) on the POSITION timeline: a reference only. Drawn
     UNDER the orange position blocks (z 1 < 2), which are solid, so where a position block
     sits the green is completely covered — no mixed colours — just as the position block
     wins at render. A click selects that override (for Delete block); dragging and resizing
     happen on the Speaker overrides timeline. */
  .framing-locks { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
  .framing-lock {
    position: absolute; top: 5px; bottom: 13px; background: var(--good);
    border-radius: 5px; pointer-events: auto; cursor: pointer;
  }
  .framing-lock.selected { box-shadow: 0 0 0 2px #FFFFFF, 0 1px 6px rgba(0, 0, 8, 0.6); }
  .framing-lock.legacy { pointer-events: none; cursor: default; }
  .dynpos-controls .secondary.armed {
    /* Dark ink, not white: #fff on this green is 1.77:1 and the label vanished at the
       moment the button became active and the user most needed to read it. */
    background: var(--good); border-color: var(--good); color: var(--accent-ink);
  }
  .framing-block {
    position: absolute; top: 5px; bottom: 13px; background: #FF7700;
    border: 1px solid rgba(255, 255, 255, 0.35); border-radius: 5px;
    cursor: grab; z-index: 2; box-shadow: 0 1px 4px rgba(0, 0, 8, 0.5);
  }
  .framing-block.selected { box-shadow: 0 0 0 2px #FFFFFF, 0 1px 6px rgba(0, 0, 8, 0.6); }
  .framing-block.drawing { opacity: 0.65; border-style: dashed; }
  .framing-handle {
    position: absolute; top: 0; bottom: 0; width: 9px; cursor: ew-resize;
  }
  .framing-handle.l { left: 0; border-radius: 4px 0 0 4px; }
  .framing-handle.r { right: 0; border-radius: 0 4px 4px 0; }
  .framing-handle:hover { background: rgba(0, 0, 8, 0.25); }

  /* Sound effects: the same two timelines as the position editor (filmstrip to scrub, a band
     below it), with a pin per sound instead of a block. Its own editor class, so the
     split-mode "needs re-process" rule that hides .dynpos-editor never hides it. */
  .sfx-editor { margin-top: 8px; }
  .sfx-editor.hidden, .sfx-picker.hidden { display: none; }
  .speed-editor { margin-top: 8px; }
  .speed-editor.hidden, .speed-warn.hidden { display: none; }
  /* Inset by the graph's plot padding (SPEED_PAD_L / SPEED_PAD_R), so a moment on the
     filmstrip sits directly above the same moment on the curve. */
  .speed-lane { margin: 0 34px 0 8px; }
  .speed-graph {
    display: block; width: 100%; height: 140px; box-sizing: border-box;
    background: var(--panel, #000044); border: 1px solid var(--border); border-radius: 6px;
    touch-action: none; user-select: none; cursor: crosshair;
  }
  .speed-graph:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
  .speed-graph .grid { stroke: rgba(148, 163, 184, 0.14); stroke-width: 1; }
  .speed-graph .grid.one { stroke: rgba(148, 163, 184, 0.4); stroke-dasharray: 4 3; }
  .speed-graph .lbl { fill: var(--muted); font-size: 9px; }
  .speed-graph .curve { fill: none; stroke: var(--cyan); stroke-width: 2; }
  .speed-graph .pt { fill: var(--panel, #000044); stroke: var(--cyan); stroke-width: 2; cursor: grab; }
  .speed-graph .pt.sel { fill: var(--cyan); }
  .speed-graph .ph { stroke: #FFFFFF; stroke-width: 1; opacity: 0.6; }
  .speed-readout { font-size: 12px; margin: 8px 0; min-height: 1.3em; }
  .speed-readout b { color: var(--cyan); }
  .speed-warn { color: var(--warn); font-size: 11px; margin: 8px 0 0; }
  .sfx-marker {
    position: absolute; top: 5px; bottom: 13px; width: 2px; margin-left: -1px;
    background: #7070FF; z-index: 2; cursor: grab;
  }
  .sfx-marker.dragging { cursor: grabbing; }
  .sfx-marker span {
    position: absolute; left: 3px; top: 0; max-width: 110px; padding: 1px 8px;
    border-radius: 5px; background: #7070FF; color: #160B26; font-size: 10px; font-weight: 600;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; box-sizing: border-box;
    box-shadow: 0 1px 4px rgba(0, 0, 8, 0.5);
  }
  .sfx-marker.flip span { left: auto; right: 3px; }
  .sfx-marker.missing, .sfx-marker.missing span { background: #6B6F7A; color: #FFFFFF; }
  .sfx-marker.selected span { box-shadow: 0 0 0 2px #FFFFFF, 0 1px 6px rgba(0, 0, 8, 0.6); }
  .sfx-add-btn { background: #7070FF !important; border-color: #7070FF !important; color: #160B26 !important; }
  .sfx-picker {
    margin: 8px 0; max-height: 220px; overflow-y: auto; padding: 8px;
    border: 1px solid var(--border); border-radius: 8px; background: var(--panel, #000044);
  }
  .sfx-row { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
  .sfx-row button { width: auto; flex: 0 0 auto; padding: 8px 8px; font-size: 12px; }
  .sfx-row .sfx-pick { flex: 1 1 auto; min-width: 0; text-align: left; overflow: hidden;
                       text-overflow: ellipsis; white-space: nowrap; }
  .sfx-empty { margin: 8px 8px; font-size: 12px; color: var(--muted); }
  .sfx-vol.hidden { display: none; }
  .sfx-vol { margin-top: 8px; font-size: 12px; }
  /* The transcript under the sound timeline: one line that slides so the word being said at
     the playhead sits on the centre mark. */
  .sfx-tx {
    position: relative; height: 30px; margin-bottom: 8px; overflow: hidden;
    border: 1px solid var(--border); border-radius: 6px; background: var(--panel, #000044);
    user-select: none;
  }
  .sfx-tx-track {
    position: absolute; left: 50%; top: 0; bottom: 0; display: flex; align-items: center;
    white-space: nowrap; will-change: transform;
  }
  .sfx-tx-track span { margin-right: 0.4em; font-size: 13px; color: var(--muted); cursor: pointer; }
  .sfx-tx-track span.now { color: #FFFFFF; font-weight: 700; }
  .sfx-tx-mark {
    position: absolute; left: 50%; top: 0; bottom: 0; width: 2px; margin-left: -1px;
    background: #FFFFFF; opacity: 0.85; pointer-events: none;
  }
  .sfx-tx-empty { position: absolute; inset: 0; display: flex; align-items: center;
                  justify-content: center; font-size: 12px; color: var(--muted); }

  /* ---- Split-screen editing (clip.layout split2 AND last rendered as split). Everything
     below is scoped to .split-mode or to split-only elements, so the single-frame editor is
     untouched. Colours: orange = position blocks (as always), blue = upper half, pink =
     lower half — the buttons wear the colour of the blocks they add. ---- */
  .split-only { display: none; }
  .dynpos.split-mode .split-only { display: revert; }
  .dynpos.split-mode .split-only.dynpos-controls { display: flex; }
  .dynpos.split-mode .single-only { display: none; }
  /* Split mode stacks three rows of pill buttons; give them room so they never touch. */
  .dynpos.split-mode .dynpos-controls { row-gap: 8px; column-gap: 8px; margin-top: 8px; }
  /* The position button wears the position-block orange everywhere; the speaker buttons the
     speaker-lane blue. */
  .dynpos .add-pos-btn { background: #FF7700; border-color: #FF7700; color: #000008; }
  .speaker-btn { background: #7070FF !important; border-color: #7070FF !important; color: #000044 !important; }
  .dynpos:not(.auto-on) .auto-only { display: none; }
  .dynpos.split-mode .auto-only { display: none; }
  .speaker-tl { height: 40px; }
  /* Drags on every editor timeline (position, speaker, split) are pointer-captured
     (trackPointer): the browser must not take the touch for a scroll and cancel it. */
  .framing-bandtl { touch-action: none; }
  .speaker-tl .split-lane.upper { top: 4px; height: 20px; border-bottom: 0; }
  .framing-block.split-person.pending { opacity: .7; border-style: dashed; }
  .ts-saved { color: var(--muted); font-size: 12px; font-weight: 400; }
  /* A paid-plan option shown to Free: visible so they can see what they're missing, greyed and
     unusable, with the reason beside it. Clicking anywhere on it opens the plans. */
  .paid-locked { opacity: .55; cursor: pointer; }
  .paid-locked input { pointer-events: none; }
  .paid-tag { display: inline-block; margin-left: 8px; padding: 1px 8px; border-radius: 999px;
              font-size: 11px; font-weight: 600; white-space: nowrap;
              background: rgba(255, 180, 84, 0.16); color: #FF7700; border: 1px solid rgba(255, 180, 84, 0.4); }
  .speaker-locked-note { margin: 2px 0 8px; font-size: 12px; color: var(--muted); cursor: pointer; }
  .clip-process { margin: 13px 0 13px; }
  .clip-process .primary { width: 100%; }
  .clip-process-hint { margin: 8px 2px 0; font-size: 12px; line-height: 1.45; color: var(--muted); }
  .reprocess-note {
    margin: 0 0 8px; padding: 8px 13px; border-radius: 8px; font-size: 13px;
    background: rgba(255, 180, 84, 0.12); border: 1px solid rgba(255, 180, 84, 0.45); color: #FF7700;
  }
  .dynpos.needs-reprocess .dynpos-editor > :not(.reprocess-note) { display: none !important; }
  .split-upper-btn { background: #7070FF !important; border-color: #7070FF !important; color: #000044 !important; }
  .split-lower-btn { background: #FF5FA2 !important; border-color: #FF5FA2 !important; color: #000044 !important; }
  /* Waiting for the faces ("Identifying faces…"): grey, not a faded blue/pink. */
  .split-upper-btn:disabled, .split-lower-btn:disabled {
    background: #000044 !important; border-color: #000044 !important; color: #C9CCD4 !important;
  }
  .split-tl { cursor: pointer; }
  .split-tl.split-active { border-color: #FFFFFF; box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35); }
  .dynpos.split-mode .dynpos-status { display: none; }
  .split-tl-label {
    position: absolute; right: 6px; bottom: 2px; font-size: 9px; letter-spacing: .5px;
    text-transform: uppercase; color: rgba(255, 255, 255, 0.78); pointer-events: none; z-index: 1;
  }
  /* The scrub time, on the picture: bottom-right, clear of the centred drag hint. */
  .crop-time {
    position: absolute; right: 6px; bottom: 6px; z-index: 4; pointer-events: none;
    background: rgba(0, 0, 8, 0.7); color: #FFFFFF; font-size: 11px; font-weight: 600;
    font-variant-numeric: tabular-nums; padding: 2px 8px; border-radius: 4px;
  }
  /* Both split timelines: two lanes (upper / lower), brighter than the single-frame track so
     an EMPTY timeline still reads as a place to put something. */
  .split-lanes-tl { height: 70px; background: #252A35; border-color: rgba(255, 255, 255, 0.28); }
  .split-lane { position: absolute; left: 0; right: 0; height: 26px; }
  .split-lane.upper { top: 4px; background: rgba(112, 112, 255, 0.14); border-bottom: 1px solid rgba(255, 255, 255, 0.22); }
  .split-lane.lower { top: 30px; background: rgba(255, 95, 162, 0.14); }
  .split-lane span {
    position: absolute; left: 6px; top: 50%; transform: translateY(-50%); font-size: 10px;
    font-weight: 600; letter-spacing: .5px; text-transform: uppercase; pointer-events: none;
  }
  .split-lane.upper span { color: #9CCAFF; }
  .split-lane.lower span { color: #FF2A2A; }
  .framing-block.split-person, .framing-block.split-pos {
    top: auto; bottom: auto; height: 22px; display: flex; align-items: center; gap: 8px;
    padding: 0 8px; font-size: 10px; color: #000044; overflow: hidden; white-space: nowrap; }
  .framing-block.split-person.upper, .framing-block.split-pos.upper { top: 6px; }
  .framing-block.split-person.lower, .framing-block.split-pos.lower { top: 32px; }
  .framing-block.split-person.upper { background: #7070FF; }
  .framing-block.split-person.lower { background: #FF5FA2; }
  .framing-block.split-pos { background: #FF7700; }
  .framing-block.split-person img { width: 16px; height: 16px; border-radius: 3px; flex: 0 0 auto; }
  .split-overlay { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
  .split-box {
    position: absolute; border: 2px solid; border-radius: 3px; cursor: grab; pointer-events: auto;
    box-shadow: 0 0 0 9999px rgba(0, 0, 8, 0.35);
  }
  .split-box.upper { border-color: #7070FF; background: rgba(112, 112, 255, 0.10); }
  .split-box.lower { border-color: #FF5FA2; background: rgba(255, 95, 162, 0.10); }
  .split-box span, .split-face span {
    position: absolute; left: 3px; top: 2px; font-size: 10px; font-weight: 600; color: #FFFFFF;
    text-shadow: 0 1px 2px #000008; pointer-events: none;
  }
  .split-face {
    position: absolute; border: 2px solid rgba(255, 255, 255, 0.8); border-radius: 6px;
    cursor: pointer; pointer-events: auto; background: rgba(0, 0, 8, 0.15);
  }
  .split-face:hover { border-color: #FFFFFF; background: #000044; }
  .split-face.picked { border-color: #7070FF; box-shadow: 0 0 0 2px #7070FF; }
  .split-face.picked.lower { border-color: #FF5FA2; box-shadow: 0 0 0 2px #FF5FA2; }
  .split-hint { font-size: 12px; color: var(--muted); margin: 8px 0 8px; }
  .framing-timeruler {
    position: absolute; left: 0; right: 0; bottom: 0; height: 13px;
    display: flex; justify-content: space-between; align-items: center;
    padding: 0 8px; font-size: 9px; color: var(--muted); pointer-events: none;
  }

  #statusLine { color: var(--muted); font-size: 13px; margin: 13px 0 0; }

  .progress-container {
    display: flex; align-items: center; gap: 13px; margin-top: 8px;
  }
  .progress-bar {
    flex: 1; height: 6px; background: var(--border-2); border-radius: 4px; overflow: hidden;
  }
  .progress-fill {
    height: 100%; background: linear-gradient(135deg, var(--accent), var(--accent-2));
    border-radius: 4px; transition: width 0.4s ease; width: 0%;
  }
  /* Indeterminate: a moving highlight for "waiting / no numeric progress yet" states
     (queued for a transcription slot, preparing, reconnecting) — clearly working, not frozen. */
  .progress-fill.indeterminate {
    width: 100% !important; transition: none;
    background: linear-gradient(90deg, transparent 0%, var(--accent) 50%, transparent 100%);
    background-size: 40% 100%; background-repeat: no-repeat;
    animation: prog-indet 1.1s ease-in-out infinite;
  }
  @keyframes prog-indet { 0% { background-position: -40% 0; } 100% { background-position: 140% 0; } }
  #pollRetryBtn { margin-left: 8px; padding: 3px 8px; font-size: 12px; }
  .progress-pct {
    font-size: 13px; font-weight: 600; color: var(--accent-text); min-width: 36px; text-align: right;
  }

  /* Both compose areas that get greyed out while their pipeline works need to be a
     positioning context for the .card-overlay that covers them. Same overlay, same rule. */
  .add-video-body, .story-compose-body { position: relative; }
  .card-overlay {
    position: absolute; inset: 0; background: rgba(0, 0, 68, 0.75);
    border-radius: 8px; z-index: 10; cursor: not-allowed;
  }
  .cancel-btn { display: block; margin-top: 8px; margin-bottom: 21px; width: 100%; }
  /* 364px, matching the story player, so a 9:16 clip reads as a video rather than a
     thumbnail and a whole one fits in view. Auto margins centre it in the card. */
  video.preview { width: 100%; max-width: 364px; border-radius: 8px; margin-top: 8px;
    display: block; margin-left: auto; margin-right: auto; }

  .clip-processing { color: var(--warn); font-size: 13px; font-weight: 600; margin: 8px 0; }
  .clip-error { color: var(--danger); font-size: 13px; font-weight: 600; margin: 8px 0; }
  .dl-button { margin-top: 8px; }
  /* Max Phase C: the "Sharpen with AI" row under a finished clip. */
  .ai-up-row { margin-top: 8px; padding: 0 var(--s4); line-height: 1.5; }
  /* `#appSection button` pads every button into a pill; these two are inline text links. */
  #appSection .ai-up-row .linklike, .ai-up-row .linklike { padding: 2px 0; min-height: 0;
    border-radius: 0; font-size: var(--fs-sm); text-decoration: underline; color: var(--text); }
  .ai-up-row:empty { display: none; }
  .ai-up-btn { border-color: var(--orange); color: var(--text); }
  .ai-up-4k { margin-left: 8px; font-size: var(--fs-sm); color: var(--muted); text-decoration: underline; }
  .ai-up-status { margin: 8px 0 0; font-size: var(--fs-sm); color: var(--orange); }
  .ai-up-note { margin: 6px 0 0; font-size: var(--fs-sm); color: var(--muted); }
  /* Max Phase D5: the story's AI visuals choice. */
  .ai-vis-opts { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
  .ai-vis-opt { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); color: var(--text); }
  .ai-vis-opt.is-off { color: var(--muted); }
  /* The clip being upscaled: its editing panel greyed out (and `inert`, set by the page). */
  .clip-controls.ai-up-locked { opacity: .4; filter: grayscale(.7); pointer-events: none;
                                user-select: none; transition: opacity var(--t) var(--ease); }
  .ai-up-lock-note { margin: 0 0 var(--s3); padding: 8px 13px; border-radius: var(--r);
    font-size: var(--fs-sm); color: var(--text); background: rgba(255, 119, 0, .12);
    border: 1px solid rgba(255, 119, 0, .35); }

  /* ---- Category picker ---- */
  .category-label { color: var(--muted); font-size: 13px; margin: 13px 0 8px; }

  .defaults-section { margin-top: 13px; padding-top: 13px; border-top: 1px solid var(--border); }
  .feature-highlight {
    background: rgba(112, 112, 255, 0.07); border: 1px solid rgba(112, 112, 255, 0.28);
    border-radius: 8px; padding: 8px 13px; margin-bottom: 13px;
  }
  .defaults-label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; margin: 0 0 8px; }
  .defaults-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
  .defaults-row label { font-size: 13px; color: var(--text); white-space: nowrap; }
  .defaults-row select {
    background: var(--bg-2); color: var(--text); border: 1px solid var(--border);
    border-radius: 6px; padding: 8px 8px; font-size: 13px; cursor: pointer;
  }
  .defaults-row input[type="range"] { width: 100px; accent-color: var(--accent); }
  .defaults-row .range-value { color: var(--accent-text); font-weight: 600; font-size: 13px; min-width: 28px; text-align: right; }
  .category-picker { display: flex; gap: 8px; margin-bottom: 13px; flex-wrap: wrap; }
  .category-option {
    flex: 1; min-width: 140px; display: flex; flex-direction: column; align-items: flex-start;
    gap: 8px; text-align: left; background: var(--bg-2); border: 1px solid var(--border);
    color: var(--text); padding: 13px 13px; border-radius: 10px; cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
  }
  .category-option:hover { border-color: var(--accent-2); }
  .category-option.active { border-color: var(--accent); background: rgba(112, 112, 255, 0.1); }
  .category-emoji { font-size: 20px; }
  .category-name { font-weight: 600; font-size: 14px; }
  .category-desc { color: var(--muted); font-size: 12px; line-height: 1.3; }

  /* ---- Topic filter (optional) ---- */
  .topic-optional { color: var(--muted); font-size: 12px; font-weight: 400; }
  }
  @layer base {
  .topic-input {
    width: 100%; background: var(--bg-2); color: var(--text); border: 1px solid var(--border);
    border-radius: 8px; padding: 8px 13px; font-size: 14px; margin-bottom: 8px;
  }
  }
  @layer components {
  .topic-input:focus { outline: none; border-color: var(--accent); }
  .topic-hint { color: var(--muted); font-size: 12px; line-height: 1.4; margin: 0 0 13px; }

  /* ---- No clips matched the topic ---- */
  .no-match {
    border: 1px solid var(--border); border-radius: 10px; padding: 21px;
    color: var(--text); font-size: 14px; line-height: 1.5;
  }
  .no-match-title { font-weight: 600; margin: 0 0 8px; }
  .no-match-body { color: var(--muted); margin: 0; }

  /* ---- Clip length slider ---- */
  .length-toggle-row {
    display: flex; align-items: center; gap: 8px; margin-top: 13px; margin-bottom: 8px;
  }
  .length-toggle-row label { font-size: 13px; color: var(--text); cursor: pointer; display: flex; align-items: center; gap: 8px; }
  .length-toggle-row input[type="checkbox"] { width: auto; cursor: pointer; }
  .slider-row { display: flex; align-items: center; gap: 13px; margin-bottom: 13px; margin-top: 8px; }
  .slider-row input[type="range"] { flex: 1; accent-color: var(--accent); }
  .slider-label { font-size: 13px; color: var(--muted); white-space: nowrap; }
  .slider-value { font-size: 14px; font-weight: 600; color: var(--accent-text); min-width: 36px; text-align: right; }

  /* ---- Layout ---- */
  .topbar-actions { display: flex; justify-content: flex-end; margin-bottom: 13px; }

  /* ---- Saved clips ---- */
  .saved-clip-item {
    border: 1px solid var(--border); border-radius: 10px; padding: 13px;
    margin-bottom: 13px; display: flex; flex-direction: column; gap: 8px;
  }
  .saved-clip-meta { display: flex; justify-content: space-between; align-items: baseline; }
  .empty-state { color: var(--muted); font-size: 14px; padding: 21px 0; text-align: center; }

  /* ---- Modals ---- */
  .modal-overlay {
    position: fixed; inset: 0; background: rgba(0, 0, 8, 0.6);
    display: flex; align-items: center; justify-content: center; z-index: var(--z-modal);
  }
  .modal-box {
    background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
    padding: 21px; max-width: 360px; text-align: center;
  }
  .modal-box p { margin-top: 0; }
  .modal-actions { display: flex; gap: 8px; justify-content: center; margin-top: 13px; }

  /* ---- Custom subtitle position: drag the caption to an exact height ---- */
  .subpos-preview {
    position: relative; width: 100%; max-width: 200px; flex-basis: 100%;
    background: repeating-conic-gradient(#000044 0% 25%, #232327 0% 50%) 50% / 16px 16px;
    border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
    /* Auto side margins centre it in BOTH homes: on a clip card it is a flex item on its
       own line (.clip-controls is flex-start, so it sat hard left), and in the extract
       panel it is a plain block narrower than its container (same result). */
    margin: 8px auto;
  }
  .subpos-preview.hidden { display: none; }
  /* The subtitler's Fix words / Change highlights / Update subtitles row. */
  .other-sub-edit { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 13px; }
  .other-sub-edit .hint { flex-basis: 100%; margin: 0; }
  .subpos-watermark {
    position: absolute; left: 50%; transform: translateX(-50%);
    /* The renderer's own numbers (_WM_TOP_PCT, _WM_HEIGHT_PCT) so the preview shows the
       real collision rather than an approximation of it. */
    top: 70.31%; height: 11.51%; width: auto; opacity: .75; pointer-events: none;
  }
  .subpos-watermark.hidden { display: none; }
  .subpos-band {
    position: absolute; left: 3.7%; right: 3.7%;   /* the .ass MarginL/R of 40 on PlayResX 1080 */
    transform: translateY(-50%); text-align: center;
    background: rgba(255, 255, 255, .14); border: 1px dashed var(--accent); border-radius: 4px;
    color: #FFFFFF; font-weight: 700; font-size: 11px; padding: 3px 0;
    cursor: grab; user-select: none; touch-action: none; z-index: 2;
  }
  .subpos-band.dragging { cursor: grabbing; background: rgba(112, 112, 255, .28); }

  /* ---- Keyword overlay: manual highlighted-word picking ---- */
  .kw-modal { max-width: 640px; width: min(92vw, 640px); text-align: left; }
  .kw-words {
    max-height: 46vh; overflow-y: auto; margin: 13px 0;
    background: var(--bg-2); border: 1px solid var(--border); border-radius: 10px;
    padding: 13px 13px; line-height: 2.1; user-select: none;
  }
  .kw-word {
    display: inline-block; padding: 1px 3px; margin: 0 1px; border-radius: 4px;
    cursor: pointer; color: #FFFFFF; font-size: 15px;
  }
  .kw-word:hover { background: #000044; }
  .kw-word.hl { font-weight: 700; }
  /* A corrected word, so what you changed is visible at a glance in a wall of text. */
  .tx-edited { text-decoration: underline dotted; text-underline-offset: 3px; color: var(--accent-text); }
  /* A word that will be beeped. Distinct from an edited word: one changed what is said,
     the other changes whether you hear it. */
  .tx-beeped { background: rgba(192, 57, 43, .28); color: #FF2A2A; letter-spacing: 1px; }
  .tx-input {
    display: inline-block; font-size: 15px; padding: 0 3px; margin: 0 1px;
    border: 1px solid var(--accent); border-radius: 4px;
    background: var(--bg); color: #FFFFFF;
  }
  .kw-caps-row { display: flex; align-items: center; gap: 8px; margin: 8px 0 8px; font-size: 13px; color: var(--muted); }

  /* Manage-keys popover (Automation tab) */
  .key-manager {
    position: absolute; top: calc(100% + 6px); left: 0; z-index: var(--z-popover);
    min-width: 280px; max-width: 360px;
    background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 8, 0.4); padding: 13px;
  }
  .key-manager-head { font-weight: 600; font-size: 0.9rem; margin-bottom: 8px; }
  .key-manager-list { display: flex; flex-direction: column; gap: 8px; }
  .key-manager-row {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    padding: 8px 8px; border: 1px solid var(--border); border-radius: 8px;
  }
  .key-manager-row .km-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
  .key-manager-row .km-provider { font-weight: 600; font-size: 0.85rem; }
  .key-manager-row .km-hint {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem; color: var(--muted);
  }
  .key-manager-row button {
    flex: 0 0 auto; padding: 8px 8px; font-size: 0.8rem;
    background: transparent; border: 1px solid var(--warn); color: var(--warn); border-radius: 7px;
  }
  .key-manager-empty { font-size: 0.85rem; color: var(--muted); }


  .source-video-card { margin-bottom: 21px; }
  .source-video-card video { width: 100%; border-radius: 8px; max-height: 500px; background: #000008; }
  }
  @layer screens {

  /* ---- Contact form ---- */
  .contact-header { margin-bottom: 21px; }
  }
  @layer components {
  .contact-header h2 { font-size: 22px; color: var(--text); margin: 0 0 8px; text-transform: none; letter-spacing: normal; }
  }
  @layer screens {
  .contact-header p { color: var(--muted); font-size: 14px; margin: 0; line-height: 1.5; }

  .form-group { margin-bottom: 21px; }
  }
  @layer components {
  .form-label {
    display: block; font-size: 13px; font-weight: 600; color: var(--text);
    margin-bottom: 8px;
  }
  }
  @layer screens {
  .form-label .required { color: var(--danger); margin-left: 2px; }

  .char-counter {
    text-align: right; font-size: 11px; color: var(--muted);
    margin-top: 2px; transition: color 0.15s ease;
  }
  .char-counter.over { color: var(--danger); }

  .field-error {
    font-size: 12px; color: var(--danger); margin-top: 8px; min-height: 0;
  }
  .field-error:empty { display: none; }
  }
  @layer components {

  textarea.contact-textarea {
    width: 100%; padding: 8px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--bg-2); color: var(--text); font-size: 14px; font-family: inherit;
    resize: vertical; min-height: 140px;
  }
  textarea.contact-textarea:focus,
  input.contact-input:focus,
  select.contact-select:focus {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px rgba(112, 112, 255, 0.2);
  }
  /* Story tab. Same vocabulary as the rest of the page — the script box is the
     contact form's textarea, taller; the parts reuse the card's spacing. */
  textarea.story-script {
    width: 100%; padding: 8px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--bg-2); color: var(--text); font-size: 14px; font-family: inherit;
    resize: vertical; min-height: 200px; line-height: 1.5;
  }
  textarea.story-script:focus { outline: none; border-color: var(--accent); }
  }
  @layer screens {
  .story-part { padding-bottom: 21px; margin-bottom: 21px; border-bottom: 1px solid var(--border); }
  .story-part:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
  /* Was 280px and hugging the left: display:block with no auto margins in a column
     that is now far wider. Centred, and up 1.3x. */
  video.story-video { width: 100%; max-width: 364px; border-radius: 8px; display: block;
    margin: 0 auto 8px; }
  }
  @layer base {

  .contact-input {
    width: 100%; padding: 8px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--bg-2); color: var(--text); margin-bottom: 0; font-size: 14px;
  }
  .contact-select {
    width: 100%; padding: 8px; border-radius: 8px; border: 1px solid var(--border);
    background: var(--bg-2); color: var(--text); font-size: 14px; cursor: pointer;
    -webkit-appearance: none; appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238b8fa3' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 10px center; padding-right: 34px;
  }
  }
  @layer screens {
  .contact-select.has-value { color: var(--text); }
  }
  @layer components {

  .file-drop-zone {
    border: 2px dashed var(--border); border-radius: 10px; padding: 21px;
    text-align: center; cursor: pointer; transition: border-color 0.2s ease, background 0.2s ease;
    position: relative;
  }
  .file-drop-zone:hover { border-color: var(--accent-2); }
  .file-drop-zone.dragover {
    border-color: var(--accent); background: rgba(112, 112, 255, 0.06);
  }
  }
  @layer screens {
  .file-drop-zone .drop-icon { font-size: 28px; display: block; margin-bottom: 8px; }
  .file-drop-zone .drop-text { color: var(--muted); font-size: 13px; margin: 0; }
  }
  @layer components {
  .file-drop-zone .drop-text strong { color: var(--text); }
  }
  @layer screens {
  .file-drop-zone .drop-hint { color: var(--muted); font-size: 11px; margin: 8px 0 0; }
  .file-drop-zone input[type="file"] { display: none; }

  .file-list { margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
  .file-item {
    display: flex; align-items: center; gap: 8px;
    background: var(--bg-2); border: 1px solid var(--border); border-radius: 8px;
    padding: 8px 13px; font-size: 13px;
  }
  .file-item-icon { font-size: 18px; flex-shrink: 0; }
  .file-item-info { flex: 1; min-width: 0; }
  .file-item-name {
    color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .file-item-size { color: var(--muted); font-size: 11px; }
  .file-item-remove {
    background: none; border: none; color: var(--danger); cursor: pointer;
    font-size: 16px; padding: 2px 8px; border-radius: 4px; flex-shrink: 0;
    line-height: 1;
  }
  .file-item-remove:hover { background: rgba(255, 107, 107, 0.1); }
  }
  @layer components {

  .info-message {
    display: flex; align-items: flex-start; gap: 8px;
    font-size: 12px; color: var(--warn); padding: 8px 8px;
    background: rgba(255, 180, 84, 0.08); border: 1px solid rgba(255, 180, 84, 0.2);
    border-radius: 8px; margin-top: 8px;
  }
  }
  @layer screens {
  .info-message .info-icon { flex-shrink: 0; font-size: 14px; margin-top: 1px; }

  .diagnostics-section {
    border: 1px solid var(--border); border-radius: 8px; padding: 13px;
    margin-bottom: 21px;
  }
  }
  @layer components {
  .diagnostics-section h3 {
    font-size: 13px; color: var(--text); margin: 0 0 8px; text-transform: none; letter-spacing: normal;
  }
  }
  @layer screens {
  .diagnostics-section p {
    font-size: 12px; color: var(--muted); margin: 0 0 8px; line-height: 1.4;
  }
  }
  @layer components {
  .diagnostics-checkbox {
    display: flex; align-items: center; gap: 8px; font-size: 13px;
    color: var(--text); cursor: pointer; margin-bottom: 8px;
  }
  .diagnostics-checkbox input[type="checkbox"] { width: auto; margin: 0; cursor: pointer; accent-color: var(--accent); }
  }
  @layer screens {

  .music-controls { margin-top: 13px; padding-top: 8px; border-top: 1px solid var(--border); }
  }
  @layer components {
  .music-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; }
  .music-row label { font-size: 13px; color: var(--text); white-space: nowrap; }
  .music-row select { background: var(--bg-2); color: var(--text); border: 1px solid var(--border); border-radius: 6px; padding: 8px 8px; font-size: 13px; cursor: pointer; flex: 1; min-width: 120px; }
  }
  @layer screens {
  .music-row input[type="range"] { width: 100px; accent-color: var(--accent); }
  }
  @layer components {
  .music-row .range-value { color: var(--accent-text); font-weight: 600; font-size: 13px; min-width: 32px; text-align: right; }
  .music-row .loop-label { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text); cursor: pointer; }
  .music-row .loop-label input[type="checkbox"] { width: auto; margin: 0; cursor: pointer; accent-color: var(--accent); }
  }
  @layer screens {

  .privacy-notice {
    font-size: 12px; color: var(--muted); margin: 13px 0; line-height: 1.5;
    padding: 8px; background: rgba(112, 112, 255, 0.04); border-radius: 8px;
  }

  .submit-loading {
    display: inline-block; width: 16px; height: 16px; border: 2px solid rgba(0, 0, 8, 0.3);
    border-top-color: var(--bg-2); border-radius: 50%; animation: spin 0.6s linear infinite;
    vertical-align: middle; margin-right: 8px;
  }
  /* Sits exactly over the preview video and is shown ONLY while dragging the timeline.
     Seeking the real element costs ~0.8s a time (long-GOP sources); a sprite tile costs
     nothing, so the picture can track the pointer. The video takes over again on release. */
  .crop-scrub { position:absolute; inset:0; width:100%; height:100%;
                object-fit:contain; pointer-events:none; z-index:1; }
  }
  @layer components {
  @keyframes spin { to { transform: rotate(360deg); } }

  .success-panel {
    text-align: center; padding: 34px 21px;
  }
  .success-panel .success-icon {
    font-size: 48px; display: block; margin-bottom: 13px;
  }
  .success-panel h2 {
    font-size: 24px; color: var(--good); margin: 0 0 8px;
    text-transform: none; letter-spacing: normal;
  }
  .success-panel p {
    color: var(--muted); font-size: 14px; line-height: 1.5; margin: 0 0 21px;
  }

  .error-panel {
    text-align: center; padding: 34px 21px;
  }
  .error-panel .error-icon { font-size: 48px; display: block; margin-bottom: 13px; }
  .error-panel h2 {
    font-size: 24px; color: var(--danger); margin: 0 0 8px;
    text-transform: none; letter-spacing: normal;
  }
  .error-panel p {
    color: var(--muted); font-size: 14px; line-height: 1.5; margin: 0 0 21px;
  }

  .toast-container {
    position: fixed; bottom: 24px; right: 24px; z-index: var(--z-toast);
    display: flex; flex-direction: column; gap: 8px; max-width: 360px;
  }
  .toast {
    padding: 13px 13px; border-radius: 8px; font-size: 13px;
    box-shadow: 0 4px 16px rgba(0, 0, 8, 0.3); animation: toastIn 0.25s ease;
    display: flex; align-items: center; gap: 8px;
  }
  .toast-success { background: rgba(0, 221, 0, 0.15); color: var(--good); border: 1px solid rgba(0, 221, 0, 0.3); }
  .toast-error { background: rgba(255, 107, 107, 0.15); color: var(--danger); border: 1px solid rgba(255, 107, 107, 0.3); }
  /* Advisory, not a failure: the upload went through, we just think it will do poorly. */
  .toast-warn { background: var(--amber-soft); color: var(--amber); border: 1px solid var(--amber-line); }
  /* A banner that stays up while the page scrolls under it: the tint over a solid panel, or the
     page's text shows through the translucent toast colour and neither can be read. */
  .toast-warn.toast-solid { background: linear-gradient(var(--amber-soft), var(--amber-soft)), var(--panel); }
  /* The toasts sit above dialogs (z-index 200 vs 100); a banner must not cover one. */
  body:has(.modal-overlay:not(.hidden)) .toast-solid { display: none; }
  .toast-success.toast-solid { background: linear-gradient(rgba(0, 221, 0, 0.15), rgba(0, 221, 0, 0.15)), var(--panel); }
  @keyframes toastIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
  }
/* --- Recent Uploads (job history) ----------------------------------------- */
/* There was no --bad variable: the palette had --good and --warn but nothing for a
   failure, which is why every error state in this app was previously plain text. */
.upload-row {
  display: flex; align-items: center; gap: 13px;
  padding: 13px 13px; margin-bottom: 8px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
}
.upload-main { flex: 1; min-width: 0; }
/* The filename is the only unbounded field on the row. Without this it pushes the
   buttons off the right edge as soon as someone uploads a long-named file. */
.upload-main strong {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.upload-actions { display: flex; gap: 8px; flex-shrink: 0; }
.upload-actions button { padding: 8px 13px; font-size: 13px; }
.badge {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 600; border: 1px solid transparent;
}
.badge.good    { color: var(--good);  border-color: var(--good);  background: rgba(0, 221, 0, .10); }
.badge.working { color: var(--warn);  border-color: var(--warn);  background: rgba(255, 180, 84, .10); }
.badge.bad     { color: var(--danger);      border-color: var(--danger);      background: rgba(255, 107, 107, .10); }
.badge.muted   { color: var(--muted); border-color: var(--border); }
.small { font-size: 12.5px; color: var(--muted); }
.small.error { color: var(--danger); }
@media (max-width: 640px) {

  .upload-row { flex-wrap: wrap; }
  .upload-actions { width: 100%; }
  }
  }
  @layer screens {

  /* ===================================================================
     HOME — the front door at "/" and the first tab inside the app.

     ONE implementation. mountHome() moves #homePage between
     #homePublicMount (logged out) and #tab-home (logged in); nothing
     below is duplicated for the two cases.

     Rules this block keeps to, deliberately:
       - Two radii, the app's own: 12px panels, 8px controls. No third.
       - No gradient. button.primary above is a blue->cyan gradient, and
         a gradient button fill is the loudest AI tell there is. The
         home page's primary action is a SOLID --accent instead.
       - No shadows. The borders already do that work everywhere else.
       - ONE accent, doing one job: the primary action, and the moments
         the scorer picked (the lit waveform segments, the karaoke word).
         Delete the accent and the page still reads top to bottom.
       - Type scale 13 / 15 / 18 / 27 / 44, with weight carrying the
         hierarchy inside a size rather than four sizes of nearly-medium.
  =================================================================== */
  .home { padding-bottom: 8px; }
  .home a { color: var(--accent-text); }
  }
  @layer components {

  /* Logged-out header actions. The first of these is the first tabbable
     control on the page, so its focus and hover states are not optional. */
  #userbar button.home-navbtn {
    border-radius: 8px; padding: 8px 13px; font-size: 14px; font-weight: 500;
    transition: border-color .18s ease, color .18s ease, background .18s ease;
  }
  #userbar button.home-navbtn:hover { border-color: var(--muted); }
  #userbar button.home-navbtn-accent {
    background: var(--accent); border-color: var(--accent); color: var(--bg-2); font-weight: 600;
  }
  #userbar button.home-navbtn-accent:hover { background: var(--accent); border-color: var(--accent); }
  }
  @layer screens {
  /* Preview mode: the "just looking" badge, and the locked account-only tabs. */
  .preview-badge {
    display: inline-block; padding: 3px 8px; border-radius: 999px; font-size: 12px;
    font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
    background: rgba(112, 112, 255, .15); color: var(--accent-text); border: 1px solid rgba(112, 112, 255, .4);
  }
  .sidebar button.preview-locked::after { content: " 🔒"; opacity: .8; font-size: .9em; }
  /* Automation tab */
  .auto-live { margin-top: 8px; padding: 13px 13px; border-radius: 8px; font-weight: 600;
      background: #000044; border: 1px solid rgba(153, 153, 153, .25); }
  .auto-live.running { background: rgba(112, 112, 255, .12); border-color: rgba(112, 112, 255, .45); color: var(--accent-text); }
  /* Not enough quota for the current settings — the run cannot finish, so say so loudly. */
  .auto-live.blocked { background: rgba(255, 107, 107, .12); border-color: rgba(255, 107, 107, .5); color: var(--danger); }
  .auto-quota-error { color: var(--danger); font-weight: 600; margin-top: 8px; }
  .auto-quota-error:empty { display: none; }
  .auto-queue { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
  }
  @layer components {
  .auto-queue-row { display: flex; justify-content: space-between; align-items: center;
      gap: 13px; padding: 8px 13px; border: 1px solid rgba(153, 153, 153, .25);
      border-radius: 8px; background: #000044; }
  }
  @layer screens {
  .auto-queue-title { font-weight: 600; word-break: break-word; }
  .auto-queue-err { color: var(--danger); font-size: 12px; margin-top: 8px; }
  /* Two buttons on a failed row (Try again, Remove) rather than one; they wrap on a phone
     instead of squeezing the title. */
  .auto-queue-actions { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
  }
  @layer components {
  /* A row that could not be fetched. Tinted rather than merely red-texted, so the group
     below the working list reads as a separate thing at a glance. */
  .auto-queue-row.auto-queue-failed { border-color: rgba(255, 107, 107, .45);
      background: rgba(255, 107, 107, .07); }
  }
  @layer screens {
  .auto-queue-deadhead { margin-top: 13px; }
  /* The compilation pool: videos already on the box, offered to the list below it. New
     class names only, so no other tab can move because of them. */
  /* Side by side, the shape the Compilations tab uses: what you have, and what is queued.
     ONE column until the left pane is shown, so clip mode looks exactly as it always did. */
  .auto-queue-panes { display: grid; grid-template-columns: 1fr; gap: var(--s3);
      margin-top: 13px; }
  .auto-queue-panes:has(> .auto-pool:not(.hidden)) { grid-template-columns: 1fr 1fr; }
  .auto-queue-pane, .auto-pool { padding: 13px; border: 1px solid var(--border);
      border-radius: 8px; background: #000044; min-width: 0; }
  .auto-queue-pane h4, .auto-pool-head h4 { margin: 0 0 8px; }
  .auto-pool.hidden { display: none; }
  .auto-pool-head { display: flex; align-items: center; justify-content: space-between;
      gap: 8px; flex-wrap: wrap; }
  .auto-pool-head h4 { margin: 0; }
  .auto-pool-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px;
      max-height: 320px; overflow-y: auto; }
  .auto-pool-row { display: flex; align-items: center; justify-content: space-between;
      gap: 13px; padding: 8px 8px; border: 1px solid rgba(153, 153, 153, .2);
      border-radius: 6px; }
  /* Already in the list: kept on screen, faded. A picker that shrinks as you use it looks
     like it is losing your videos. */
  .auto-pool-row.auto-pool-in { opacity: .55; }
  /* Why the next run will or won't touch this item. Sits under the state line, quieter
     than an error, because "still downloading" is normal rather than wrong. */
  .auto-queue-why { margin-top: 8px; }
  /* After a Run once now that could not start yet: the reason stops being a quiet
     hint and becomes the thing the user is waiting on.
     Scoped under #appSection to outrank `#appSection .hint` (1,1,0), which these rows
     also carry — a bare .auto-queue-why.waiting is 0,2,0 and loses to it, silently, as
     a muted grey that looks exactly like the non-urgent state it is meant to replace. */
  #appSection .auto-queue-why.waiting { color: var(--danger); }
  /* Still downloading — the same orange as .clip-processing, because it is the same idea:
     something is under way and you are waiting on it. */
  #appSection .auto-queue-why.downloading { color: var(--warn); font-weight: 600; }
  /* Ready to be picked up by the next run — green, so a glance down the list separates
     "will run" from "still working". */
  #appSection .auto-queue-state.ready { color: var(--good); font-weight: 600; }
  .auto-queue-ready { border-color: rgba(0, 221, 0, .45); background: rgba(0, 221, 0, .07); }
  }
  @layer components {
  .auto-row { display: flex; flex-wrap: wrap; align-items: center; gap: 13px; margin: 8px 0; }
  .auto-field { display: flex; flex-direction: column; gap: 8px; font-size: .9em; }
  .auto-field input[type="text"], .auto-field input[type="password"], .auto-field input[type="time"],
  .auto-field select, #autoModel, #autoPrompt {
      font: inherit; padding: 8px 8px; border: 1px solid rgba(153, 153, 153, .35);
      border-radius: 6px; background: #000044; color: inherit; }
  .auto-field select { min-width: 220px; }
  .auto-key-note { padding: 8px 8px; border-radius: 6px; background: rgba(255, 180, 84, .10);
      border: 1px solid rgba(255, 180, 84, .35); }
  }
  @layer screens {
  .auto-enable-card.auto-locked { opacity: .65; }
  .auto-row.auto-locked { opacity: .55; }
  .uploads-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 13px; }
  }
  @layer components {
  .uploads-head h2 { margin: 0; }
  .uploads-switch { display: inline-flex; align-items: center; gap: 8px; font-size: var(--fs-sm); color: var(--muted); }
  }
  @layer screens {
  .sched-item { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0;
    padding: 13px 0; border-bottom: 1px solid var(--border); }
  .sched-item .sched-when { font-weight: 620; color: #FFFFFF; }
  .sched-item .sched-label { flex: 1 1 160px; min-width: 0; overflow-wrap: anywhere; color: var(--muted); }
  .sched-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }
  .sched-row input[type="datetime-local"] { flex: 1 1 200px; min-width: 0; }
  .sched-field { border-top: 1px solid var(--border); padding-top: var(--s4); margin-top: var(--s3); }
  /* Both re-asserted over #appSection's unified white edge, whose border shorthand
     otherwise wins and silently drops the amber warning fill and the hover feedback. */
  #appSection .auto-key-note { background: rgba(255, 180, 84, .10) !important;
    border: 1px solid rgba(255, 180, 84, .45) !important; }
  }
  @layer components {
  #appSection .clip-item:hover { border-color: rgba(255, 255, 255, .38) !important; }
  }
  @layer screens {
  /* The readout belongs beside the control it reports, not beside its caption. */
  .auto-row > .slider-value { align-self: flex-end; padding-bottom: 8px; }
  }
  @layer components {
  .auto-enable-card.auto-locked .auto-toggle { cursor: not-allowed; }
  }
  @layer screens {
  /* Pro-gate: hide the whole config and show only the upgrade banner for non-Pro users. */
  #autoLockedBanner { display: none; }
  #tab-automation.automation-locked > .card { display: none; }
  #tab-automation.automation-locked > #autoLockedBanner { display: block; }
  #autoPrompt { width: 100%; resize: vertical; }
  .auto-toggle { font-weight: 600; }
  .auto-plan { font-size: .95em; }
  }
  @layer components {
  .auto-preview { white-space: pre-wrap; max-height: 340px; overflow-y: auto; padding: 13px;
      border-radius: 8px; background: #000044; border: 1px solid rgba(153, 153, 153, .25);
      font-size: .9em; margin-top: 8px; }
  }
  @layer screens {
  .auto-run { border-top: 1px solid rgba(153, 153, 153, .2); padding: 8px 0; font-size: .9em; }
  .auto-run-head { margin-bottom: 8px; }
  }
  @layer components {
  .home-cta:focus-visible, .home-ghost:focus-visible,
  #userbar button.home-navbtn:focus-visible, .home a:focus-visible {
    outline: 2px solid var(--accent-text-2); outline-offset: 3px;
  }
  }
  @layer screens {

  /* ---- Hero ---- */
  .home-hero {
    display: grid; grid-template-columns: 1.02fr .98fr; gap: 55px;
    align-items: center; padding: 34px 0 55px;
  }
  .home.in-app .home-hero { grid-template-columns: 1fr; gap: 34px; padding: 8px 0 34px; }
  .home-actions { display: flex; flex-wrap: wrap; gap: 13px; align-items: center; }
  /* .hidden is declared earlier in this stylesheet, so on equal specificity the
     display:flex above would win and BOTH action rows would render at once. It did. */
  .home-actions.hidden { display: none; }
  }
  @layer components {
  .home-cta {
    background: var(--accent); border: 1px solid var(--accent); color: var(--bg-2);
    font-weight: 600; font-size: 15px; padding: 13px 21px; border-radius: 8px;
    cursor: pointer; transition: background .18s ease, border-color .18s ease;
  }
  }
  @layer screens {
  .home-cta:hover { background: var(--accent); border-color: var(--accent); }
  }
  @layer components {
  .home-ghost {
    background: none; border: 1px solid var(--border); color: var(--text);
    font-weight: 500; font-size: 15px; padding: 13px 21px; border-radius: 8px;
    cursor: pointer; transition: border-color .18s ease, color .18s ease;
  }
  }
  @layer screens {
  .home-ghost:hover { border-color: var(--muted); }
  .home-note { font-size: 13px; line-height: 1.6; color: var(--muted); margin: 21px 0 0; max-width: 46ch; }
  }
  @layer components {

  /* ---- The figure: one recording in, three clips out ---- */
  /* The highlighted segments mark what the scorer kept — same teal as the CTA,
     same job: "this is the thing that matters here." */
  /* A clip frame, not a person-icon. Horizontal bars suggest visual content in
     the frame — enough to show the output is video, not a document. */
  /* Doppo flagged tracking on these. He is right that the grammar wants 0, so it is
     set explicitly rather than left to the font. The UPPERCASE stays: these represent
     burned-in captions, which really are uppercase in the rendered clips — it is
     representational, not decoration. */
  /* The highlighted word in each caption mockup — teal marks what the player
     would light up on a word-timed caption. */

  /* Doppo: the focus ring was the browser default — a square outline with no radius,
     no offset and no transition, on a page where every other corner is rounded. It is
     the one style a keyboard user sees constantly and it was the only uncrafted thing
     on the page. :focus-visible so it appears for keyboard use and not on mouse click. */
  .home a:focus-visible,
  .home button:focus-visible {
    outline: 2px solid var(--accent-text);
    outline-offset: 3px;
    border-radius: 8px;
    transition: outline-color 200ms ease;
  }
  }
  @layer screens {
  @media (max-width: 880px) {

  /* ---- Sections ---- */

  /* ---- Plans ---- */

  /* ---- Closing ---- */

    .home-hero { grid-template-columns: 1fr; gap: 34px; padding: 21px 0 34px; }
  }
  }
  @layer components {
  /* (removed the prefers-color-scheme:light figure overrides — dark-only site) */



  /* ---------------------------------------------------------------- plan bar ---
     Was a full card at the top of the app, above the nav, on every tab including
     ones that spend nothing. Now one strip under the nav, on the three tabs where
     minutes are actually consumed. */
  .auto-nudge {
    flex-basis: 100%; display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
    padding-top: var(--s3); border-top: 1px solid var(--border-2);
  }
  .auto-nudge p { margin: 0; flex: 1 1 260px; }
  .planbar {
    display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
    padding: 13px var(--s5); margin-bottom: var(--s5);
    background: linear-gradient(94deg, var(--accent-soft), transparent 62%), var(--panel);
    border: 1px solid var(--border-2); border-left: 3px solid var(--accent);
    border-radius: var(--r); font-size: var(--fs-sm);
  }
  .planbar-label {
    font-size: var(--fs-xs); letter-spacing: .14em; text-transform: uppercase;
    color: var(--muted-2); font-weight: 640; flex: 0 0 auto;
  }
  .planbar #planInfo { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; flex: 1; }
  .planbar #planInfo p { margin: 0; }

  /* ------------------------------------------------------- app: less boxing ---
     Every tab wrapped everything in a card, so a page read as a stack of grey
     rectangles. Sections now sit on the page, separated by rhythm and a rule; a
     surface is kept only where one genuinely groups a discrete object. */
  #appSection .card {
    background: transparent; border: 0; border-radius: 0;
    padding: 0 0 var(--s6); margin: 0 0 var(--s6);
    border-bottom: 1px solid var(--border); box-shadow: none;
  }
  #appSection .card:last-child { border-bottom: 0; }
  #appSection .card h2 { font-size: var(--fs-lg); letter-spacing: -0.026em; margin-bottom: var(--s4); }
  #appSection .card.source-video-card, #appSection .auto-enable-card {
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--r-lg); padding: var(--s5);
  }

  /* ---- extracted clips: centred and wide, side by side on a big screen ---- */
  #clipsList {
    display: grid; gap: var(--s4);
    grid-template-columns: repeat(auto-fill, minmax(min(500px, 100%), 1fr));
    align-items: start;
  }
  .clip-item {
    margin-bottom: 0 !important;
    background: var(--panel); border: 1px solid var(--border-2) !important;
    border-radius: var(--r-lg) !important; padding: var(--s5) !important;
    transition: border-color var(--t) var(--ease), box-shadow var(--t) var(--ease);
  }
  .clip-item:hover { border-color: var(--border-3) !important; box-shadow: var(--sh-2); }
  /* Same cap as the story player. This rule previously set max-width:100% and, sitting
     later in the sheet at equal specificity, it beat video.preview's 364px — so a clip
     stretched to the full width of its grid cell. */
  .clip-item video, .clip-item .preview {
    width: 100%; max-width: 364px; border-radius: var(--r); background: #000008;
    display: block; margin-left: auto; margin-right: auto;
  }


  /* ------------------------------------------------------ centring + contrast ---
     Everything sat left-aligned in a wide shell, which read as lopsided rather than
     spacious. Content is centred, and the option groups inside the tabs are centred
     within their sections so a settings panel reads as balanced.
     Contrast: the page was one dark blue at three brightnesses and everything merged.
     White is used deliberately now — headings, active states, key values, control
     edges — so there is a top of the tonal range to read against. */
  #appSection > .content, #appSection .content { max-width: 1400px; margin: 0 auto; }

  /* headings and key numbers move to true white; body stays soft */
  #appSection h2, #appSection h3, #appSection h4 { color: #FFFFFF; }
  /* The home page is deliberately excluded — it is signed off as it is. */
  #appSection strong, #appSection b { color: #FFFFFF; }
  .slider-value, .range-value { color: #FFFFFF; font-weight: 620; }
  legend, .defaults-label { color: #FFFFFF !important; }
  }
  @layer base {

  /* controls read as controls: lighter edges, lighter fill */
  input[type="text"], input[type="email"], input[type="password"], input[type="number"],
  input[type="time"], input[type="search"], input[type="url"], select, textarea {
    border-color: rgba(255, 255, 255, .16);
    background: #000044;
  }
  }
  @layer components {
  input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) {
    border-color: rgba(255, 255, 255, .28); background: #000044;
  }
  input[type="range"] { background: rgba(255, 255, 255, .14); }
  input[type="range"]::-webkit-slider-thumb { box-shadow: 0 1px 5px rgba(0, 0, 8, .55), 0 0 0 1px rgba(255, 255, 255, .35); }
  .secondary { background: #000044; border-color: rgba(255, 255, 255, .20); }
  .secondary:hover:not(:disabled) { background: #000044; border-color: rgba(255, 255, 255, .34); }

  /* option groups: centred blocks with a real surface, not left-hugging rows */
  #appSection .defaults-section {
    max-width: 980px; margin: var(--s5) auto 0; padding: var(--s5);
    background: #000044; border: 1px solid rgba(255, 255, 255, .10);
    border-radius: var(--r-lg); border-top: 1px solid rgba(255, 255, 255, .10);
  }
  #appSection .defaults-label {
    text-align: center; font-size: var(--fs-xs); letter-spacing: .16em;
    margin-bottom: var(--s5); font-weight: 640;
  }
  #appSection .defaults-row, #appSection .music-row {
    justify-content: center; margin-bottom: 13px; gap: 13px;
  }
  #appSection .defaults-group {
    padding: var(--s4); margin-bottom: var(--s4); border-radius: var(--r);
    background: #000044; border: 1px solid rgba(255, 255, 255, .09);
  }
  /* the labelled controls line up on a centred axis instead of ragged left */
  #appSection .defaults-row > label:first-child,
  #appSection .music-row > label:first-child {
    min-width: 8.5rem; text-align: right; color: #FFFFFF;
    /* Sits a touch left of the box's right edge. Padding rather than a smaller min-width
       or a margin: box-sizing is border-box, so this moves the TEXT and leaves the label's
       footprint — and therefore every control beside it — exactly where it was. */
    padding-right: 13px;
  }

  /* section headers centred where the content under them is centred */
  #appSection .card > h2 { text-align: center; }
  #appSection .card > h2 + .hint, #appSection .card > .hint:first-of-type { text-align: center; }

  /* the plan strip centres too */
  .planbar { max-width: 1400px; margin-left: auto; margin-right: auto; justify-content: center; }
  .planbar #planInfo { flex: 0 1 auto; justify-content: center; }


  /* ------------------------------------------------- outlines, size, breathing ---
     Three complaints in one pass: boxes had no edge so they dissolved into the
     background, controls were too small to feel like targets, and the functional
     tabs were packed tight enough to be tiring to read. */

  /* Every grouping box gets a visible white edge. Home is excluded on purpose. */
  #appSection .defaults-section,
  #appSection .defaults-group,
  #appSection .card.source-video-card,
  #appSection .auto-enable-card,
  #appSection .auto-key-note,
  #appSection .planbar,
  #appSection .clip-item,
  #appSection .saved-clip-item,
  #appSection .conn-row,
  #appSection .auto-queue-row,
  #appSection .file-drop-zone {
    border: 1px solid rgba(255, 255, 255, .22) !important;
    border-radius: var(--r-lg);
    background: #000044;
  }
  #appSection .defaults-group { background: #000044; }
  #appSection .planbar { border-left: 3px solid var(--accent) !important; }

  /* Breathing room. The functional tabs were laid out at a density that suited a
     narrow column and reads as noise once everything is centred and wide. */
  #appSection .defaults-section { padding: var(--s6); margin-top: var(--s6); }
  #appSection .defaults-group { padding: var(--s5); margin-bottom: var(--s5); }
  #appSection .defaults-row, #appSection .music-row { margin-bottom: 21px; gap: 13px; }
  #appSection .card { padding-bottom: var(--s7); margin-bottom: var(--s7); }
  #appSection .hint { margin-bottom: var(--s3); line-height: 1.65; }
  #appSection .conn-row { padding: 13px 21px; gap: 13px; margin-bottom: 13px; }

  /* Interactive targets: bigger, and comfortably past the 44px guidance where it
     matters. Small controls are the main reason a UI feels fiddly. */
  #appSection button, #appSection .secondary, #appSection .primary {
    padding: 13px 21px; font-size: var(--fs-base); border-radius: var(--r-full);
  }
  #appSection .clip-controls button { padding: 8px 21px; }
  #appSection input[type="checkbox"], #appSection input[type="radio"] {
    width: 20px; height: 20px; margin-right: 8px;
  }
  #appSection label { font-size: var(--fs-base); }
  #appSection select, #appSection input[type="text"], #appSection input[type="number"],
  #appSection input[type="time"], #appSection input[type="password"], #appSection textarea {
    padding: 13px 13px; font-size: var(--fs-base);
  }
  /* Ranges must be able to SHRINK. A hard min-width pushed the bounce row — two
     sliders plus their labels — past the edge of its container. Flexible basis with
     a floor that still leaves a usable track. */
  #appSection input[type="range"] {
    height: 6px; flex: 1 1 130px; min-width: 96px; max-width: 240px;
  }
  /* ...but `.auto-field` is a COLUMN flex container (caption above control), and in a
     column the flex BASIS is the height. So `flex: 1 1 130px` was overriding `height: 6px`
     and rendering every automation slider as a 130px-tall blob with a giant thumb —
     Subtitle size, Bounce intensity and Bounce speed all measured 129x130. Along the
     column the control should take its natural height and stretch across instead. */
  #appSection .auto-field > input[type="range"] {
    flex: 0 0 auto; height: 6px; width: 100%; max-width: 240px;
  }
  /* the rows that hold two sliders side by side must wrap rather than overflow */
  #defaultBounceOpts, #autoBounceOpts, #storyBounceOpts, #storySubBounceOpts {
    flex-wrap: wrap; max-width: 100%; row-gap: 8px;
  }
  /* A caption, its slider and its readout are ONE control and must break as one.
     Laid out flat in a wrapping row they wrapped BETWEEN themselves, which put
     "Speed" at the end of one line and the slider it labels at the start of the
     next — so the row read as "Intensity, slider, value, Speed" and the speed
     slider looked like it belonged to Intensity. Grouping them means the wrap
     can only ever fall between the two controls. */
  .bounce-field { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
  #appSection .bounce-field > label { flex: 0 0 auto; }
  #appSection .defaults-row, #appSection .music-row, #appSection .auto-row { max-width: 100%; }
  #appSection input[type="range"]::-webkit-slider-thumb { width: 21px; height: 21px; }
  #appSection input[type="range"]::-moz-range-thumb { width: 21px; height: 21px; }
  /* a toggle row is a target, so give the whole row the pointer and some padding */
  #appSection .auto-toggle, #appSection label:has(> input[type="checkbox"]) {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 8px 8px; cursor: pointer;
  }

  /* Contrast. Muted-on-dark was the readability complaint; lift the mid tones and
     let state colours actually differ from one another. */
  #appSection { color: #DDE4EF; }
  #appSection .hint { color: #999999; }
  #appSection .upload-reason { color: #999999; }
  #appSection .upload-acct { color: var(--good); font-weight: 600; }
  #appSection .defaults-label { color: #FFFFFF !important; }
  #appSection .slider-value, #appSection .range-value { color: #FFFFFF; }

  /* ---- connection rows with their platform mark ---- */
  .conn-mark {
    flex: 0 0 auto; width: 30px; height: 30px; display: grid; place-items: center;
    background: #000044; border-radius: 9px;
  }
  .conn-mark svg { width: 19px; height: 19px; display: block; }
  #appSection .conn-name { font-weight: 620; color: #FFFFFF; font-size: var(--fs-md); }
  .conn-subhead { margin-top: var(--s7); color: #FFFFFF; }


  /* ------------------------------------------------------- rendered video sizing ---
     The story player sat at 280px with display:block and no auto margins, so it hugged
     the left of a now much wider column. Centred, and up 1.3x to 364px.
     Extracted clips were capped at 300px — small enough that a 9:16 clip read as a
     thumbnail rather than something you could judge. Matched to the story size so both
     tabs show a finished video at the same scale and a whole clip fits in view. */
  /* The controls under a story part line up with the player above them rather than
     starting at the far edge of the card. */
  #appSection .story-part { max-width: 720px; margin-left: auto; margin-right: auto; }
  #appSection .story-part .defaults-label { text-align: center; }


  /* ----------------------------------------------------------- containment ---
     Controls were pushing out of their boxes. The cause is structural rather than
     any one width: a flex item defaults to min-width:auto, so it refuses to shrink
     below its content, and a row of enlarged buttons and selects simply grows past
     its parent instead of wrapping. Fixed once, here, rather than per component. */
  #appSection .card,
  #appSection .clip-item,
  #appSection .story-part,
  #appSection .defaults-section,
  #appSection .defaults-group,
  #appSection .conn-row,
  #appSection .auto-queue-row { min-width: 0; }

  /* every flex row inside the app wraps and may shrink */
  #appSection .clip-controls,
  #appSection .clip-meta,
  #appSection .subtitle-size-row,
  #appSection .subtitle-toggle,
  #appSection .defaults-row,
  #appSection .music-row,
  #appSection .auto-row,
  #appSection .upload-row,
  #appSection .conn-row {
    flex-wrap: wrap; min-width: 0; max-width: 100%;
  }
  #appSection .clip-controls > *,
  #appSection .defaults-row > *,
  #appSection .music-row > *,
  #appSection .auto-row > * { min-width: 0; }

  /* controls shrink rather than overhang; a select's own min-width was the usual culprit */
  #appSection select, #appSection input[type="text"], #appSection input[type="number"],
  #appSection input[type="time"], #appSection input[type="password"],
  #appSection input[type="email"], #appSection textarea {
    max-width: 100%; min-width: 0;
  }
  #appSection .auto-field { min-width: 0; max-width: 100%; }
  #appSection .auto-field select { min-width: 0; }
  #appSection button { max-width: 100%; }
  /* long unbroken strings — filenames, URLs, transcript excerpts — must not set the
     width of the box that holds them */
  #appSection .clip-title, #appSection .clip-excerpt, #appSection .clip-hook,
  #appSection .conn-name, #appSection .auto-queue-title, #appSection .upload-name {
    min-width: 0; overflow-wrap: anywhere;
  }


  /* The two tab-level calls to action. Full width so the primary action is unmissable
     and the hit target spans the card, rather than a small pill lost among the settings
     above it. */
  #extractBtn, #storyMakeBtn {
    display: block; width: 100%; margin-top: var(--s5);
    padding: 13px 21px; font-size: var(--fs-md); font-weight: 640;
  }
  }
  @layer screens {

  /* ============================================================================
     HOME — editorial layout, scroll-linked motion.
     Motion here is driven by scroll POSITION, not by scroll events, so it runs
     backwards on the way up for free. --p is a 0→1 progress value JS writes onto
     the stage; every transform below is a pure function of it.
     ============================================================================ */

  .home { max-width: none; margin: 0; }
  .home-wrap { max-width: 1560px; margin: 0 auto; padding: 0 clamp(var(--s5), 4vw, var(--s8)); }

  /* reveals: bidirectional on purpose — the class is removed on exit */
  .js-motion .rv { opacity: 0; transform: translateY(20px); }
  .js-motion .rv.in { opacity: 1; transform: none; }
  .js-motion .rv {
    transition: opacity 620ms var(--ease), transform 620ms var(--ease);
    transition-delay: var(--d, 0ms);
  }
  @media (prefers-reduced-motion: reduce) {
    .js-motion .rv { opacity: 1 !important; transform: none !important; }
  }


  /* ---- masthead: the wordmark, large and centred, above the hero line ---- */
  .hero-mark {
    text-align: center; margin: 0 0 var(--s7);
    font-size: clamp(2.6rem, 8vw, 5.6rem);
    font-weight: 720; letter-spacing: -0.055em; line-height: 1;
    display: flex; align-items: center; justify-content: center; gap: .18em;
  }
  /* Same split as the header mark — white word, accent second half — so the brand
     reads identically in both places. */
  .hero-mark span { color: #FFFFFF; }
  /* background-IMAGE, not the `background` shorthand, on both gradient texts: a shorthand
     written back by anything (Dark Reader did, see the darkreader-lock meta) resets
     background-clip and turns the word into a solid block. */
  .hero-mark i {
    font-style: normal;
    background-image: linear-gradient(96deg, var(--accent-2), var(--accent));
    -webkit-background-clip: text; background-clip: text; color: transparent;
  }
  .hero-mark::before {
    content: "C";
    display: grid; place-items: center; flex: 0 0 auto;
    width: .92em; height: .92em; border-radius: .26em;
    font-size: .58em; font-weight: 760; letter-spacing: -0.04em;
    color: var(--accent-ink);
    background: linear-gradient(150deg, var(--accent-2), var(--accent));
    box-shadow: 0 6px 26px var(--accent-glow);
  }
  /* It arrives before anything else on the page, so it gets its own entrance rather
     than waiting on the scroll observer. */
  .js-motion .hero-mark { opacity: 0; transform: translateY(14px) scale(.985); }
  .js-motion .hero-mark.in {
    opacity: 1; transform: none;
    transition: opacity 760ms var(--ease), transform 760ms var(--ease);
  }
  @media (prefers-reduced-motion: reduce) {
    .js-motion .hero-mark { opacity: 1 !important; transform: none !important; }
  }
  @media (max-width: 560px) { .hero-mark { margin-bottom: var(--s6); } }

  /* ------------------------------------------------------------------ hero --- */
  .hero {
    min-height: 86vh; display: grid; align-content: center;
    padding: var(--s8) 0 var(--s7); position: relative; overflow: hidden;
  }
  /* one soft light source, top-left, the way a studio lights a product shot */
  .hero::before {
    content: ""; position: absolute; inset: -30% -10% auto -25%; height: 90%;
    background: radial-gradient(closest-side, rgba(112, 112, 255, .16), transparent 72%);
    pointer-events: none;
  }
  .hero-inner { position: relative; }
  .hero-kicker {
    display: inline-flex; align-items: center; gap: 8px;
    font-size: var(--fs-sm); font-weight: 550; color: var(--muted);
    border: 1px solid var(--border-2); background: var(--panel);
    padding: 8px 13px; border-radius: var(--r-full); margin-bottom: var(--s6);
  }
  }
  @layer components {
  .hero-kicker b { color: var(--text); font-weight: 620; }
  }
  @layer screens {
  .hero-kicker i {
    width: 6px; height: 6px; border-radius: 50%; background: var(--good);
    box-shadow: 0 0 9px var(--good); font-style: normal;
  }
  /* Display type: the whole premium impression rests here. Huge, tight, layered
     in on a stagger so the eye reads it as composed rather than dumped. */
  .hero h1 {
    font-size: clamp(3rem, 8.2vw, 7.4rem);
    line-height: .94; letter-spacing: -0.052em; font-weight: 700;
    margin: 0 0 var(--s6); max-width: 15ch;
  }
  .hero h1 .l {
    display: block; overflow: hidden;
    /* The mask that makes the line slide up also clips descenders — the g in "long"
       was being cut and overlapping the line beneath. Pad below the baseline, then
       pull it back so the line rhythm is untouched. */
    padding-bottom: .14em; margin-bottom: -.10em;
  }
  .hero h1 .l > span { display: block; }
  .js-motion .hero h1 .l > span { transform: translateY(105%); }
  .js-motion .hero h1.in .l > span {
    transform: none; transition: transform 900ms var(--ease);
  }
  .js-motion .hero h1.in .l:nth-child(2) > span { transition-delay: 90ms; }
  .js-motion .hero h1.in .l:nth-child(3) > span { transition-delay: 180ms; }
  .hero h1 em {
    font-style: normal; color: transparent;
    background-image: linear-gradient(94deg, var(--accent-2) 10%, var(--accent) 70%);
    -webkit-background-clip: text; background-clip: text;
  }
  .hero-lead {
    font-size: clamp(1rem, 1.5vw, 1.2rem); line-height: 1.58;
    color: var(--muted); max-width: 44ch; margin: 0 0 var(--s6);
  }
  }
  @layer components {
  .hero-cta { display: flex; gap: 13px; flex-wrap: wrap; align-items: center; }
  }
  @layer screens {
  .hero-cta.hidden { display: none; }

  /* ------------------------------------------------------- scroll sequence --- */
  /* A tall stage with a sticky viewport inside it. Scrolling through the stage
     drives --p from 0 to 1; scrolling back drives it to 0 again. */
  .stage { position: relative; height: 340vh; }
  .stage-pin {
    position: sticky; top: 0; height: 100vh;
    display: grid; place-items: center; overflow: hidden;
  }
  .stage-inner { width: min(1100px, 92vw); position: relative; }

  .stage-label {
    font-size: var(--fs-xs); letter-spacing: .18em; text-transform: uppercase;
    color: var(--muted-2); margin-bottom: var(--s4); font-weight: 600;
  }
  .stage-title {
    font-size: clamp(1.7rem, 3.6vw, 3rem); letter-spacing: -0.04em;
    font-weight: 690; line-height: 1.06; margin: 0 0 var(--s6); max-width: 18ch;
  }
  /* four captions cross-fade as --p advances; only one is ever opaque */
  .stage-steps { position: relative; height: 4.4em; margin-bottom: var(--s5); }
  .stage-step {
    position: absolute; inset: 0; opacity: 0;
    transition: opacity 320ms var(--ease), transform 320ms var(--ease);
    transform: translateY(8px); color: var(--muted); font-size: var(--fs-md);
    max-width: 46ch; line-height: 1.55;
  }
  .stage-step.on { opacity: 1; transform: none; }
  }
  @layer components {
  .stage-step b { color: var(--text); font-weight: 620; }
  }
  @layer screens {

  /* the film strip: source frames that compress, then clips that lift out */
  /* progress rail under the strip, filled by --p */
  .stage-rail { height: 2px; background: var(--border); margin-top: var(--s5); border-radius: 2px; }
  .stage-rail i {
    display: block; height: 100%; border-radius: 2px;
    background: linear-gradient(90deg, var(--accent), var(--accent-2));
    width: calc(var(--p, 0) * 100%);
  }



  /* The tracking specimen in the craft section — four frames, the lit ones showing
     where the crop is holding the speaker. Kept after the dead-CSS purge because,
     unlike the rest of the old figure classes, these are still in the markup. */
  .strip { display: flex; gap: 8px; align-items: stretch; }
  .strip-cell {
    flex: 1; border-radius: 10px; border: 1px solid var(--border-2);
    background: linear-gradient(168deg, var(--panel-2), var(--panel));
    position: relative; overflow: hidden;
  }
  .strip-cell.keep {
    border-color: var(--cyan-line);
    background: linear-gradient(168deg, var(--panel-3), var(--panel)),
                radial-gradient(circle at 50% 38%, var(--cyan-soft), transparent 66%);
    box-shadow: 0 0 0 1px var(--cyan-line), 0 10px 28px rgba(112, 112, 255, .15);
  }
  .strip-cell::after {
    content: ""; position: absolute; left: 10px; right: 10px; bottom: 10px; height: 4px;
    border-radius: 2px; background: var(--border-3);
  }
  .strip-cell.keep::after { background: var(--cyan); }

  /* ---- the reel: a 48-minute source being scanned, then three shorts lifting out ---- */
  .reel { position: relative; }
  .reel-track {
    position: relative; height: 132px; border-radius: var(--r);
    background: linear-gradient(180deg, var(--panel-2), var(--panel));
    border: 1px solid var(--border-2); overflow: hidden;
  }
  .reel-wave { position: absolute; inset: 0; display: flex; align-items: center; gap: 2px; padding: 0 8px; }
  .reel-wave i {
    flex: 1; border-radius: 1px; background: var(--border-3);
    transition: background 260ms var(--ease), transform 260ms var(--ease);
  }
  .reel-wave i.lit { background: linear-gradient(180deg, var(--cyan), var(--accent)); }
  /* candidate windows: dim until the scan reaches them, then they claim their span */
  .reel-window {
    position: absolute; top: 8px; bottom: 8px; border-radius: 9px;
    border: 1px solid transparent; opacity: 0;
    transition: opacity 380ms var(--ease), border-color 380ms var(--ease), background 380ms var(--ease);
  }
  .reel-window[data-w="0"] { left: 4%;  width: 13%; }
  .reel-window[data-w="1"] { left: 41%; width: 15%; }
  .reel-window[data-w="2"] { left: 74%; width: 12%; }
  .reel-window.found {
    opacity: 1; border-color: var(--cyan-line); background: var(--cyan-soft);
    box-shadow: 0 0 0 1px var(--cyan-line), 0 10px 30px rgba(112, 112, 255, .16);
  }
  .reel-head {
    position: absolute; top: 0; bottom: 0; width: 2px; left: 0;
    background: linear-gradient(180deg, transparent, #FFFFFF 18%, #FFFFFF 82%, transparent);
    box-shadow: 0 0 14px rgba(255, 255, 255, .75); opacity: 0;
  }
  .reel.scanning .reel-head { opacity: 1; }
  .reel-time {
    display: flex; justify-content: space-between; margin-top: 8px;
    font-size: var(--fs-xs); color: var(--muted-2);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  }
  .reel-time span:nth-child(2) { color: var(--cyan); font-weight: 700; }

  /* the output: three 9:16 shorts that rise out of the timeline */
  .reel-out { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s4); margin-top: var(--s5); }
  .short {
    margin: 0; opacity: 0; transform: translateY(26px) scale(.97);
    transition: opacity 520ms var(--ease), transform 520ms var(--ease);
  }
  .short.up { opacity: 1; transform: none; }
  .short:nth-child(2) { transition-delay: 90ms; }
  .short:nth-child(3) { transition-delay: 180ms; }
  .short i {
    display: block; aspect-ratio: 9 / 16; max-height: 190px; border-radius: var(--r);
    border: 1px solid var(--accent-line);
    background: linear-gradient(168deg, var(--panel-3), var(--panel)),
                radial-gradient(circle at 50% 34%, var(--accent-soft), transparent 62%);
    box-shadow: 0 14px 40px rgba(112, 112, 255, .20); margin: 0 auto;
  }
  .short figcaption {
    display: flex; align-items: center; gap: 8px; margin-top: 13px;
    font-size: var(--fs-sm); color: var(--muted); line-height: 1.35;
  }
  }
  @layer components {
  .short b {
    flex: 0 0 auto; font-size: var(--fs-xs); font-weight: 750; padding: 3px 8px;
    border-radius: var(--r-full); background: var(--good-soft); color: var(--good);
  }
  }
  @layer screens {
  @media (max-width: 900px) {
    .reel-track { height: 96px; }
    .short i { max-height: 130px; }
  }

  /* ---------------------------------------------------------------- editorial --- */
  .sec { padding: var(--s8) 0; }
  .sec-head { max-width: 54rem; margin-bottom: var(--s6); }
  }
  @layer components {
  .sec-label {
    font-size: var(--fs-xs); letter-spacing: .18em; text-transform: uppercase;
    color: var(--accent-text); font-weight: 620; margin-bottom: var(--s4);
  }
  }
  @layer screens {
  .sec h2 {
    font-size: clamp(2rem, 4.6vw, 3.4rem); letter-spacing: -0.043em;
    line-height: 1.04; font-weight: 690; margin: 0 0 var(--s4);
  }
  .sec p.lead { font-size: var(--fs-md); color: var(--muted); margin: 0; max-width: 46ch; line-height: 1.6; }

  /* asymmetric feature rows — alternating, not a grid of identical boxes */
  .row {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--s7);
    align-items: center; padding: var(--s6) 0; border-top: 1px solid var(--border);
  }
  .row:first-of-type { border-top: 0; }
  .row.flip > *:first-child { order: 2; }
  .row h3 { font-size: clamp(1.35rem, 2.3vw, 1.9rem); letter-spacing: -0.032em; margin: 0 0 var(--s3); }
  }
  @layer components {
  .row p { color: var(--muted); margin: 0 0 var(--s4); line-height: 1.62; }
  }
  @layer screens {
  .row ul { list-style: none; padding: 0; margin: 0; }
  .row li { display: flex; gap: 13px; padding: 8px 0; color: var(--muted); font-size: var(--fs-sm); }
  .row li::before {
    content: ""; flex: 0 0 auto; width: 16px; height: 16px; margin-top: 2px; border-radius: 50%;
    background: var(--accent-soft) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%235FD0FF' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E") center/10px no-repeat;
  }
  .row-visual {
    border-radius: var(--r-xl); border: 1px solid var(--border-2); padding: var(--s5);
    background: linear-gradient(168deg, var(--panel-2), var(--panel)); box-shadow: var(--sh-2);
  }

  /* Real output, shown at the shape it was made in. The videos are preload="none" with a
     poster, so the page costs a 28-48KB still until someone actually presses play — two
     1080-tall clips eagerly loading would be most of the landing page's weight. */
  /* Sized to 364px — the SAME cap the extract tab presents a finished clip at
     (video.preview / .clip-item video). These are real output, so they are shown at the
     size the product shows output, rather than at whatever the grid cell allows: at full
     column width they render 644x1146 each and the section runs taller than two viewports.
     Track width is fixed rather than 1fr so the cards cannot grow past that cap. */
  .demo-grid {
    /* +2px is the card's own 1px borders: the track sizes the CARD, and it is the VIDEO
       inside it that has to measure 364px to match. Without it the clips land at 362 and
       are quietly 2px narrower than the same clip on the extract tab. */
    --demo-w: 364px;
    display: grid; grid-template-columns: repeat(2, minmax(0, calc(var(--demo-w) + 2px)));
    justify-content: center; gap: var(--s6);
  }
  /* The head is centred to sit over the capped grid — left-anchored copy above a centred
     pair reads as a mistake rather than a showcase. The closing section centres too. */
  .demo-sec .sec-head { margin-left: auto; margin-right: auto; text-align: center; }
  /* text-align on the head centres the WORDS; it cannot centre the paragraph. p.lead is
     capped at 46ch with margin:0, so inside the centred head its BOX still hugs the left
     edge — measured 186px off centre at 1440px, 168px at 900px, and only accidentally
     right on a phone, where the viewport is narrower than 46ch so the cap never binds.
     Auto margins centre the box the words sit in. */
  .demo-sec .sec-head p.lead { margin-left: auto; margin-right: auto; }
  .demo-card {
    border-radius: var(--r-xl); border: 1px solid var(--border-2); overflow: hidden;
    background: linear-gradient(168deg, var(--panel-2), var(--panel)); box-shadow: var(--sh-2);
  }
  .demo-card video {
    display: block; width: 100%; aspect-ratio: 9 / 16; object-fit: cover;
    background: #000008; border-bottom: 1px solid var(--border);
  }
  .demo-meta { padding: var(--s4) var(--s5) var(--s5); }
  .demo-meta h3 { font-size: 1.05rem; letter-spacing: -0.02em; margin: 0 0 8px; }
  .demo-meta p { color: var(--muted); font-size: var(--fs-sm); line-height: 1.55; margin: 0; }
  .demo-tag {
    display: inline-block; margin-bottom: 8px; padding: 3px 8px; border-radius: var(--r-full);
    background: var(--accent-soft); border: 1px solid var(--accent-line);
    color: var(--accent-text-2); font-size: 12px; font-weight: 600;
  }
  /* Two per row at EVERY width, including the app's narrow column and on a phone: the
     clips are paired deliberately (row one is the same podcast cut two ways, row two is
     the same output reached two ways — through Claude, and on autopilot) and the pairing
     is the point of showing them. minmax(0, ...) lets the tracks shrink below the 364px
     cap rather than overflowing, so narrow just means smaller, never one-up or clipped.
     The gap tightens as space runs out so the shrinking goes to the video, not the gutter. */
  @media (max-width: 900px) { .demo-grid { gap: var(--s4); } }
  @media (max-width: 560px) {
  /* ...but NOT on a phone. Two 9:16 clips across 390px leaves each one ~165px wide, and
     what these clips are showing off is the burned-in captions — which at that width are
     not legible, so the pairing is being bought with the very thing it is meant to prove.
     One per row gives each clip ~342px. The pairing still reads: they sit adjacent, just
     stacked. (The type shrinks below only existed to survive the cramped two-up.) */
    .demo-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s5); }
  }

  /* caption specimen */
  .spec { display: grid; gap: 8px; }
  .spec-line { font-size: var(--fs-lg); font-weight: 700; letter-spacing: -0.02em; }
  .spec-line s { text-decoration: none; color: var(--muted-2); }
  .spec-line u { text-decoration: none; color: var(--accent-text-2); }
  .spec-bar { height: 6px; border-radius: 3px; background: var(--panel-3); overflow: hidden; }
  .spec-bar i { display: block; height: 100%; width: 62%; background: linear-gradient(90deg, var(--accent), var(--accent-2)); }

  /* factor bars for the virality panel */
  .fbar { display: grid; grid-template-columns: 8.5rem 1fr 2.4rem; gap: 13px; align-items: center; padding: 8px 0; }
  .fbar span { font-size: var(--fs-sm); color: var(--muted); }
  .fbar i { height: 5px; border-radius: 3px; background: var(--panel-3); display: block; overflow: hidden; }
  .fbar i b { display: block; height: 100%; background: linear-gradient(90deg, var(--accent), var(--accent-2)); border-radius: 3px; }
  .fbar em { font-style: normal; font-size: var(--fs-sm); color: var(--text); font-weight: 620; text-align: right; }

  /* ----------------------------------------------------------------- numbers --- */
  .nums { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--border);
          border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; }
  .nums div { background: var(--panel); padding: var(--s6) var(--s5); }
  .nums b { display: block; font-size: clamp(1.6rem, 2.7vw, 2.2rem); font-weight: 700;
            letter-spacing: -0.04em; margin-bottom: 8px; }
  .nums span { font-size: var(--fs-sm); color: var(--muted); line-height: 1.45; }

  /* ----------------------------------------------------------------- pricing --- */
  .plans { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s4); }
  /* <!--yearly--> Monthly / Yearly switch (cut out of the page while yearly is off). */
  .bill-switch-row { grid-column: 1 / -1; display: flex; align-items: center; justify-content: center;
                     gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s2); }
  .bill-label { font-size: var(--fs-base); font-weight: 650; color: var(--muted);
                transition: color var(--t) var(--ease); }
  .bill-label.is-on { color: var(--fg); }
  .bill-save { display: inline-block; margin-left: 2px; padding: 3px 8px; border-radius: 999px;
               font-size: var(--fs-sm); font-weight: 650; color: var(--good);
               background: rgba(0, 221, 0, .12); border: 1px solid rgba(0, 221, 0, .3); }
  /* The button is only the tap area; the pill is drawn inside it, so the mobile tap-target
     rule can stretch the button without stretching the switch. Blue on Monthly, green on
     Yearly — both states are a real choice, neither reads as "off". */
  /* ⚠️ PADDING 0, AND !important: inside the app (the Pricing tab's clone) a general
     `#appSection button` rule pads every button 12px 22px, which squeezed the pill to 36px
     and left the colour as a knob-sized patch. Nothing about a switch wants padding. */
  .bill-switch { display: inline-flex; align-items: center; justify-content: center;
                 width: 80px; height: 48px; padding: 0 !important; border: 0; background: none;
                 cursor: pointer; flex: 0 0 auto; min-width: 80px; }
  .bill-track { position: relative; display: block; width: 72px; min-width: 72px; height: 38px;
                flex: 0 0 auto; box-sizing: border-box; border-radius: 999px;
                background-color: var(--accent); border: 1px solid var(--accent);
                /* The colour is ALSO a background-image: colour-rewriting tools (Dark Reader and
                   friends) rewrite background-color but leave images, so the pill keeps its
                   colour instead of shrinking to a coloured knob. */
                background-image: linear-gradient(var(--accent), var(--accent));
                forced-color-adjust: none;
                box-shadow: 0 0 0 4px rgba(112, 112, 255, .14);
                transition: background var(--t) var(--ease), border-color var(--t) var(--ease),
                            box-shadow var(--t) var(--ease); }
  .bill-switch[aria-checked="true"] .bill-track {
    background-color: var(--good); background-image: linear-gradient(var(--good), var(--good));
    border-color: var(--good); box-shadow: 0 0 0 4px rgba(0, 221, 0, .16); }
  .bill-knob { position: absolute; top: 4px; left: 4px; width: 28px; height: 28px; border-radius: 50%;
               background-color: #FFFFFF; forced-color-adjust: none; box-shadow: 0 2px 5px rgba(0, 0, 8, .35);
               transition: transform var(--t) var(--ease); }
  .bill-switch[aria-checked="true"] .bill-knob { transform: translateX(34px); }
  .bill-switch:focus-visible .bill-track { outline: 2px solid #FFFFFF; outline-offset: 3px; }
  .plan-yearly { font-size: var(--fs-sm); font-weight: 600; color: var(--good); margin: 0 0 8px; }
  @media (max-width: 420px) {
  /* Narrow phones: Monthly · switch · Yearly stay on one line; the badge drops below, centred. */
    .bill-switch-row { column-gap: var(--s2); row-gap: 2px; }
    .bill-save-wrap { flex-basis: 100%; text-align: center; }
    .bill-save { margin-left: 0; }
  }
  /* <!--/yearly--> */
  .plan { background: var(--panel); border: 1px solid var(--border); border-radius: var(--r-xl);
          padding: var(--s6); display: flex; flex-direction: column;
          transition: border-color var(--t) var(--ease), transform var(--t) var(--ease); }
  .plan:hover { border-color: var(--border-3); transform: translateY(-3px); }
  .plan-tag { font-size: var(--fs-xs); letter-spacing: .13em; text-transform: uppercase;
              color: var(--muted); font-weight: 640; }
  .plan-fig { font-size: clamp(2.1rem, 3.6vw, 2.9rem); font-weight: 700; letter-spacing: -0.045em; margin: var(--s3) 0 2px; }
  .plan-fig span { font-size: var(--fs-base); font-weight: 500; color: var(--muted); letter-spacing: -0.01em; }
  .plan-rate { font-size: var(--fs-sm); color: var(--muted-2); margin-bottom: 8px; }
  /* Base's line where Pro's trial line sits, so the two cards keep the same rhythm — the
     `:not(:has(.plan-trial))` rule below adds bottom margin to a card with neither. */
  .plan-sub { font-size: var(--fs-sm); font-weight: 600; color: var(--muted);
              margin: 0 0 var(--s4); }
  .plan-trial-spent { color: var(--muted) !important; font-weight: 500; }
  /* A pending cancellation is information, not an error — the plan is still working. */
  .plan-cancelling { color: var(--warn); font-size: var(--fs-sm); margin: 8px 0 0; }
  }
  @layer components {
  /* The plan you are on: still legible, plainly not a button. */
  .plan-cta-current { opacity: 0.55; cursor: default; }
  }
  @layer screens {
  /* The second Pro option: a real choice, but plainly the quieter of the two. */
  /* ⚠️ ANY CLASS RULE THAT SETS `display` BEATS THE BROWSER'S OWN `[hidden]` RULE, and then
     `el.hidden = true` sets the attribute faithfully and changes nothing on screen. That is
     exactly what happened here: someone who had ended their trial and paid was still shown
     the option to end it, because `.plan-skip-link { display: block }` outranked
     `[hidden] { display: none }`. The JS was right, the server was right, and the CSS quietly
     overruled both.
     Declared once, globally, so the next element toggled by the attribute cannot repeat it. */
  [hidden] { display: none !important; }
  }
  @layer components {

  .plan-skip-link { display: block; margin: var(--s3) auto 0; text-align: center;
                    color: var(--muted); text-decoration: underline;
                    font-size: var(--fs-sm); line-height: 1.4; }
  }
  @layer screens {
  .plan-skip-link:hover { color: var(--fg); }
  }
  @layer components {
  /* Leaving should be findable, not shouted. A link rather than a button, muted until
     hovered — the account panel is not the place for a red rectangle. */
  .linklike { background: none; border: 0; padding: 0; font: inherit; cursor: pointer; }
  }
  @layer screens {
  .danger-link { color: var(--muted); text-decoration: underline; font-size: var(--fs-sm); }
  .danger-link:hover { color: var(--bad, #FF6B6B); }
  .plan-trial { font-size: var(--fs-sm); font-weight: 600; color: var(--good);
    margin: 0 0 var(--s5); letter-spacing: -0.01em; }
  /* Free carries no trial line, so it keeps the spacing the paid pair produces. */
  .plan:not(:has(.plan-trial)):not(:has(.plan-sub)) .plan-rate { margin-bottom: var(--s5); }
  .plan ul { list-style: none; padding: 0; margin: 0 0 var(--s6); flex: 1; }
  .plan li { color: var(--muted); font-size: var(--fs-sm); padding: 8px 0; border-top: 1px solid var(--border); }
  .plan li:first-child { border-top: 0; }

  /* ------------------------------------------------------------------- close --- */
  .close { text-align: center; padding: var(--s9) 0 var(--s8); }
  .close h2 { font-size: clamp(2.2rem, 5.6vw, 4.2rem); letter-spacing: -0.048em; line-height: 1.02; margin: 0 0 var(--s5); }
  .close p { color: var(--muted); font-size: var(--fs-md); max-width: 40ch; margin: 0 auto var(--s6); }
  .close .hero-cta { justify-content: center; }
  @media (max-width: 900px) {

    .hero { min-height: auto; padding: var(--s8) 0 var(--s7); }
    .stage { height: 300vh; }
    .row, .row.flip > *:first-child { grid-template-columns: 1fr; order: 0; }
    .row { gap: var(--s5); padding: var(--s6) 0; }
    .nums { grid-template-columns: repeat(2, 1fr); }
    .plans { grid-template-columns: 1fr; }
    .sec { padding: var(--s8) 0; }
  }
  /* <!--max-plan--> Four cards (A2): a row of four where they fit, two by two below that, and
     the single column above (<= 900px) untouched. */
  @media (min-width: 1181px) { .plans:has(> .plan-max) { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
  @media (min-width: 901px) and (max-width: 1180px) {
    .plans:has(> .plan-max) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  /* ------------------------------------------------- pricing: one look per tier ---
     Each tier gets its own colour, icon and button so the four don't read as one card
     printed four times. Only palette colours are used. The emphasis follows the evidence:
     a plan stands out by contrast with its neighbours (the isolation effect), not by a
     "magic" hue. So exactly ONE card, Pro, wears the gold of the primary CTA and sits
     raised. Free recedes, Base is the brand blue, and Max is the orange-to-gold premium edge.
     Every flag is a checkable fact or plainly an opinion; see the comments in index.html.
     No timers and no scarcity, which the DMCCA bans. */
  .plans { row-gap: var(--s5); }
  .plan { position: relative; --tier: var(--muted); --tier-ink: var(--cta-ink); }
  .plan-flag { position: absolute; top: 0; left: 50%; transform: translate(-50%, -50%);
               padding: 5px 13px; border-radius: var(--r-full); white-space: nowrap;
               font-size: var(--fs-xs); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
               background-color: var(--tier); background-image: linear-gradient(var(--tier), var(--tier));
               color: var(--tier-ink); forced-color-adjust: none; box-shadow: 0 6px 18px rgba(0, 0, 8, .45); }
  .plan-tag { display: inline-flex; align-items: center; gap: 8px; color: var(--tier); }
  .plan-tag::before { content: ""; width: 18px; height: 18px; flex: 0 0 auto; background: currentColor;
                      -webkit-mask: var(--tier-icon) center / contain no-repeat; mask: var(--tier-icon) center / contain no-repeat; }
  /* Ticks in the tier's colour: the list reads as "what you get", not small print. */
  .plan li { position: relative; padding-left: 26px; }
  .plan li::before { content: ""; position: absolute; left: 0; top: 11px; width: 15px; height: 15px; background: var(--tier);
                     -webkit-mask: var(--tick) center / contain no-repeat; mask: var(--tick) center / contain no-repeat; }
  .plan { --tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3 8.5l3 3 7-7' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
  .plan li strong { color: var(--text); }

  /* Free: the way in. Outlined, no fill and no glow, so it never competes. */
  .plan-free { background: transparent; border-color: var(--border-2);
    --tier-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2l2.2 6.6L21 11l-6.8 2.4L12 20l-2.2-6.6L3 11l6.8-2.4z'/%3E%3C/svg%3E"); }
  .plan-free .plan-fig { color: var(--muted); }

  /* Base: the brand blue. Most people's plan, so it looks solid and trustworthy. */
  .plan-base { --tier: var(--accent-text-2); border-color: var(--accent-line);
    background: linear-gradient(180deg, rgba(0, 0, 221, .22), var(--panel) 46%);
    --tier-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M13.5 2L4 14h7l-1.5 8L20 10h-7z'/%3E%3C/svg%3E"); }
  .plan-base .plan-flag { --tier: var(--accent); --tier-ink: #FFFFFF; }
  .plan-base .plan-cta, .plan-base .plan-cta:hover:not(:disabled) {
    background-color: var(--accent); background-image: linear-gradient(var(--accent), var(--accent));
    border-color: var(--accent-text); color: #FFFFFF; }

  /* Pro: the spotlight. Gold, the colour of "go", is used on this card and nowhere else in
     the row, with a filled CTA, a lifted card and a warm glow. The trial line becomes a
     pill because a free week is the strongest risk-reversal on the page. */
  .plan-pro { --tier: var(--gold); border: 2px solid var(--gold);
    background: radial-gradient(120% 70% at 50% 0%, rgba(255, 204, 0, .16), transparent 60%), var(--panel);
    box-shadow: 0 0 0 4px rgba(255, 204, 0, .10), 0 28px 70px rgba(255, 204, 0, .14);
    --tier-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2.5l2.9 6 6.6.9-4.8 4.6 1.2 6.5L12 17.4l-5.9 3.1 1.2-6.5L2.5 9.4l6.6-.9z'/%3E%3C/svg%3E"); }
  .plan-pro:hover { border-color: var(--gold); }
  .plan-pro .plan-cta, .plan-pro .plan-cta:hover:not(:disabled) {
    background-color: var(--gold); background-image: linear-gradient(var(--gold), var(--gold));
    border-color: var(--gold); color: var(--cta-ink); font-weight: 700; }
  .plan-pro .plan-trial:not(.plan-trial-spent) { display: inline-block; align-self: flex-start;
    padding: 5px 13px; border-radius: var(--r); background: var(--good-soft); border: 1px solid rgba(0, 221, 0, .3); }

  /* Max: the premium tier. Darker than the panel with an orange-to-gold edge drawn as a
     background layer (border-image would drop the radius) and a crown. */
  .plan-max { --tier: var(--orange); border: 2px solid transparent;
    background: linear-gradient(180deg, rgba(255, 119, 0, .12), rgba(255, 119, 0, 0) 42%) padding-box,
                linear-gradient(var(--bg-2), var(--bg-2)) padding-box,
                linear-gradient(150deg, var(--orange), var(--gold) 45%, var(--border-2) 85%) border-box;
    --tier-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M3 7l4.5 4L12 4l4.5 7L21 7l-2 12H5z'/%3E%3C/svg%3E"); }
  .plan-max:hover { border-color: transparent; }
  .plan-max .plan-flag { background-image: linear-gradient(90deg, var(--orange), var(--gold)); }
  .plan-max .plan-cta, .plan-max .plan-cta:hover:not(:disabled) {
    background-color: var(--orange); background-image: linear-gradient(90deg, var(--orange), #FF9900);
    border-color: var(--orange); color: var(--cta-ink); font-weight: 700; }

  /* Lift Pro only when the cards stand in one row; a lifted card in a 2x2 grid or a single
     column just looks misaligned. */
  /* `translate`, not `transform`: the scroll reveal (.rv.in) and the hover both own
     `transform`, and either would cancel the lift. The two properties add up instead. */
  @media (min-width: 901px) { .plans:not(:has(> .plan-max)) > .plan-pro { translate: 0 -13px; } }
  @media (min-width: 1181px) { .plans:has(> .plan-max) > .plan-pro { translate: 0 -13px; } }
  /* One button shape on every card, so only the colour says which plan is pushed. */
  .plan .plan-cta { width: 100%; padding: 13px 21px; border-radius: var(--r-sm);
                    font-size: 15px; font-weight: 700; }
  .plan-free .plan-cta { border-color: var(--border-3); }
  /* <!--/max-plan--> */
  @media (max-width: 560px) {
    .nums { grid-template-columns: 1fr; }
  }
  }
  @layer components {

  /* Phones. Declared AFTER the 900px block on purpose: both rules are one class of
     specificity, so on a 390px screen — where both match — the later one is the one
     that lands. Moving this above 900px silently restores 300vh.
       - 300vh of pinned stage is three phone screens of thumb-work for one animation.
         180vh keeps the sequence and halves the scrolling.
       - 100vh on iOS Safari is the LARGE viewport, so the pinned panel jumped every
         time the URL bar collapsed. 100dvh is the one that tracks it. */

  /* Utility, declared LAST on purpose: it must outrank the display rule of
     whatever it is applied to (.auto-row, .auto-field and .home-actions all set
     display, and a mid-sheet .hidden silently loses to them). */
  .hidden { display: none; }
  }
  @layer screens {
  /* The Clipkiln mark is compulsory on the free plan, so the per-clip tick box is not shown
     to them: a checkbox implying it is optional would be a lie the server then refuses to
     honour (User.wants_watermark ORs the plan rule in). */
  body.free-plan .clip-watermark-row { display: none; }
  /* The "working on it" line under an auth button. Deliberately MUTED: it sits next to
     the error paragraph, and a red "Signing up…" reads as a failure at the exact moment
     the user is least sure anything is happening. */
  .auth-pending {
    margin: 8px 0 0; text-align: center; font-size: 13.5px; color: var(--muted);
  }
  /* The whole uploads list while a job is being opened. `disabled` already greys the
     buttons; this makes it obvious the LIST is busy rather than one row, and pointer-events
     catches anything clickable that is not a button. */
  .uploads-busy { opacity: .55; pointer-events: none; }
  }
  @layer base {
  .forgot-link {
    display: block; margin-top: 8px; text-align: center;
    color: #7070FF; font-size: 13.5px; cursor: pointer;
  }
  }
  @layer screens {
  .forgot-link:hover { text-decoration: underline; }
  /* Arrived here from a Claude chat: mark WHICH clip was asked for, among fifteen that look
     alike. Fades out on its own — the ring is a pointer, not a state worth keeping. */
  .just-opened { animation: justOpened 2.4s ease-out 1; border-radius: 12px; }
  @keyframes justOpened {
    0%, 55% { box-shadow: 0 0 0 2px var(--accent, #7070FF), 0 0 22px rgba(112, 112, 255, .45); }
    100%    { box-shadow: 0 0 0 0 rgba(112, 112, 255, 0); }
  }

  /* ===== Compilations tab =====================================================
     All NEW class names. Nothing here reaches an existing selector, so no other
     tab can move because of it — the same "untouched by construction" argument the
     mobile layer below makes, by a different route. The two-column layout is the
     desktop shape; the mobile layer stacks it. */
  .comp-head { display: flex; align-items: center; justify-content: space-between;
               gap: var(--s3, 13px); flex-wrap: wrap; }
  .comp-head-actions { display: flex; gap: var(--s2, 8px); align-items: center; }
  .comp-panes { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4, 13px);
                margin: var(--s4, 13px) 0; align-items: start; }
  .comp-pane { border: 1px solid var(--border, #000044); border-radius: var(--r, 12px);
               padding: var(--s3, 13px); background: var(--panel-2, #000044);
               min-width: 0; }
  .comp-pane h3 { margin: 0 0 var(--s2, 8px); font-size: var(--fs-md, 15px); }
  .comp-sel-head { display: flex; align-items: baseline; justify-content: space-between;
                   gap: var(--s2, 8px); flex-wrap: wrap; }
  .comp-sort { display: flex; gap: var(--s1, 8px); flex-wrap: wrap; }

  .comp-job { border-bottom: 1px solid var(--border, #000044); }
  .comp-job:last-child { border-bottom: 0; }
  .comp-job-head { display: flex; flex-direction: column; align-items: flex-start;
                   gap: 2px; width: 100%; text-align: left; background: none;
                   border: 0; padding: var(--s2, 8px) 0; cursor: pointer;
                   color: var(--text, #FFFFFF); }
  .comp-job-head .comp-job-name { font-weight: 600; overflow-wrap: anywhere; }
  .comp-job-head.open .comp-job-name { color: var(--accent-text, #7070FF); }
  .comp-job-clips { padding: 0 0 var(--s2, 8px) var(--s3, 13px); }

  .comp-clip { display: grid; grid-template-columns: auto 1fr;
               gap: var(--s2, 8px); align-items: start; padding: 2px 0; }
  .comp-clip-pick { display: flex; align-items: center; padding-top: 8px; }
  .comp-clip-title { grid-column: 2; display: flex; flex-direction: column;
                     align-items: flex-start; gap: 1px; background: none; border: 0;
                     padding: 8px 0; text-align: left; cursor: pointer;
                     color: var(--text, #FFFFFF); overflow-wrap: anywhere; }
  .comp-clip-preview { grid-column: 2; }
  /* Deliberately small: this is a "which clip is this?" check, not a viewing experience,
     and several open at once should still leave the list readable. */
  video.comp-preview { width: 100%; max-width: 220px; border-radius: 8px;
                       background: #000008; margin: 8px 0 8px; }

  .comp-sel-row { display: flex; align-items: center; gap: var(--s2, 8px);
                  padding: var(--s2, 8px) 0;
                  border-bottom: 1px solid var(--border, #000044); }
  .comp-sel-row:last-child { border-bottom: 0; }
  .comp-sel-row.dragging { opacity: .6; background: var(--accent-soft, rgba(112, 112, 255, .12)); }
  /* touch-action:none is what makes the drag work on a phone at all: without it the
     browser claims the gesture for scrolling and pointermove never arrives. */
  .comp-drag { cursor: grab; touch-action: none; user-select: none;
               color: var(--muted, #999999); font-size: 18px; padding: 0 2px; }
  .comp-sel-n { min-width: 1.4em; color: var(--muted, #999999); font-variant-numeric: tabular-nums; }
  .comp-sel-title { flex: 1 1 auto; min-width: 0; }
  .comp-actions { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center;
    justify-content: center; margin-top: var(--s2); }
  /* The menu and its status line sit BELOW that row as ordinary blocks (see the note in
     the markup), and centre under it because the buttons above them are centred. */
  .comp-panel { margin-inline: auto; }
  .comp-render-row { display: flex; align-items: center; gap: var(--s3, 13px);
                     flex-wrap: wrap; margin-top: var(--s3, 13px); }
  /* ---- Compilations tab ----
     All-new classes, so nothing above is altered: these cannot change the look of an
     existing element, only of markup that did not exist before. Declared AFTER the
     #appSection layer so a bare .comp-* rule is not outranked by it. */
  #compLockedBanner { display: none; }
  /* Portrait, so it has to be capped by HEIGHT or a 9:16 video fills the viewport and the
     upgrade button falls below the fold — the one thing the banner exists to be clicked. */
  .comp-demo { display: flex; flex-direction: column; align-items: center; gap: var(--s2);
    margin: var(--s3) 0; }
  .comp-demo video { max-height: 420px; max-width: 100%; border-radius: var(--r-sm);
    background: #000008; }
  }
  @layer components {
  .comp-demo .hint { text-align: center; margin: 0; }
  }
  @layer screens {
  #tab-compilations.comp-locked > #compLockedBanner { display: block; }
  /* The body STAYS VISIBLE when locked, greyed and inert. Hiding it left a free account
     looking at a refusal with no idea what it was being refused — the tab has to show
     what the feature does for the upgrade line to mean anything. pointer-events:none on
     the wrapper stops drags and clicks that `disabled` does not cover (the preview's
     caption, the reorder handles). */
  #tab-compilations.comp-locked > #compBody { opacity: .62; pointer-events: none;
    filter: saturate(.6); }
  #tab-compilations.comp-locked #compBody a { pointer-events: none; }
  .comp-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; }
  .comp-head-actions { display: flex; gap: var(--s2); align-items: center; }
  .comp-panes { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); margin: var(--s4) 0; }
  .comp-pane { min-width: 0; }
  .comp-pane h3 { margin: 0 0 var(--s2); font-size: var(--fs-md, 15px); }
  .comp-sel-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s2); flex-wrap: wrap; }
  .comp-sort { display: flex; gap: var(--s2); }
  .comp-job { border: 1px solid var(--border); border-radius: var(--r-sm); margin-bottom: var(--s2); overflow: hidden; }
  .comp-job-head { display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
                   width: 100%; text-align: left; background: var(--panel-2); border: 0;
                   padding: var(--s2) var(--s3); cursor: pointer; color: var(--text); }
  .comp-job-head.open { background: var(--accent-soft); }
  .comp-job-name { font-weight: 600; overflow-wrap: anywhere; }
  .comp-job-clips { padding: var(--s2) var(--s3); display: flex; flex-direction: column; gap: 2px; }
  .comp-clip { display: grid; grid-template-columns: auto 1fr; align-items: start; gap: var(--s2); }
  .comp-clip-preview { grid-column: 1 / -1; }
  .comp-clip-pick { display: flex; align-items: center; padding-top: 8px; }
  .comp-clip-title { display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
                     background: none; border: 0; color: var(--text); text-align: left;
                     padding: 8px 0; cursor: pointer; overflow-wrap: anywhere; }
  .comp-clip-title:hover { color: var(--accent-text); }
  /* A split-screen clip goes into a compilation as a single frame: said, small, by its title. */
  .comp-split-note { font-size: 11px; font-weight: 400; color: var(--muted); white-space: nowrap; }
  video.comp-preview { width: 100%; max-width: 240px; border-radius: var(--r-sm); background: #000008; margin: var(--s2) 0; }
  .comp-sel-row { display: grid; grid-template-columns: auto auto 1fr auto auto; align-items: center;
                  gap: var(--s2); padding: var(--s2); border: 1px solid var(--border);
                  border-radius: var(--r-sm); margin-bottom: 8px; background: var(--panel-2); }
  .comp-sel-row.dragging { opacity: .65; border-color: var(--accent); }
  /* touch-action:none is what makes the drag work on a phone: without it the browser
     claims the gesture for scrolling and pointermove never arrives. */
  .comp-drag { cursor: grab; touch-action: none; padding: 0 8px; color: var(--muted); font-size: 18px; user-select: none; }
  .comp-sel-n { color: var(--muted); min-width: 1.4em; text-align: right; }
  .comp-sel-title { min-width: 0; }
  .comp-render-row { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin-top: var(--s3); }
  /* The logo preview is a fixed-width block in a full-width panel, so without this it
     sits hard left with a lot of empty space beside it. */
  #compLogoPreview { margin-left: auto; margin-right: auto; }

  /* ---- The WYSIWYG preview ----
     .cw-stage is a FIXED 1080x1920 box that is then scaled onto the preview frame,
     because that is exactly what libass does: the .ass is authored at that PlayRes and
     burned with no original_size, so it is stretched onto whatever frame ffmpeg makes.
     Scaling the same canvas the same way is what makes the preview honest at 1:1 and
     16:9, where text really does squash — an approximation drawn in CSS percentages
     would quietly disagree with the render. */
  .comp-wysiwyg { position: relative; width: 100%; max-width: 260px; margin: var(--s3) auto;
    border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: #0B0D12; }
  .cw-stage { position: absolute; top: 0; left: 0; width: 1080px; height: 1920px;
    transform-origin: top left; }
  .cw-video { position: absolute; left: 0; right: 0; bottom: 0;
    background: repeating-conic-gradient(#000044 0% 25%, #232327 0% 50%) 50% / 96px 96px; }
  .cw-band { position: absolute; left: 0; right: 0; top: 0; overflow: hidden; }
  /* Each band line is absolutely positioned by renderCompPreview at the same y the .ass
     puts it, so this sets no line-height of its own: the browser and libass disagree
     about a font's line advance by nearly two to one (GROBOLD at 63px: 100 vs 60), and
     any ratio here could only ever be right for one font. */
  .cw-band-inner { position: absolute; left: 60px; right: 60px; top: 0;
    text-align: center; font-weight: 700; }
  .cw-sub { position: absolute; left: 40px; right: 40px; transform: translateY(-50%);
    text-align: center; font-weight: 700; color: #FFFFFF; line-height: normal;
    text-shadow: 0 0 14px #000008, 0 0 14px #000008, 0 0 6px #000008; }
  /* `top` is set by renderCompPreview from the same numbers the renderer uses, so the
     two cannot drift; this is only the fallback before the first paint. */
  .cw-wm { position: absolute; left: 50%; transform: translateX(-50%);
    top: 73.96%; height: 11.51%; width: auto; opacity: .75; }
  .cw-wm.hidden { display: none; }
  /* touch-action:none is what makes the drag work on a phone — without it the browser
     claims the gesture for scrolling and pointermove never arrives. */
  .cw-sub.cw-draggable { cursor: grab; touch-action: none;
    outline: 2px dashed var(--accent); outline-offset: 6px; border-radius: 6px; }
  .cw-sub.dragging { cursor: grabbing; }
  .comp-preview-head { display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s2); flex-wrap: wrap; }
  .comp-scrub { display: flex; flex-direction: column; align-items: center; gap: 8px;
    max-width: 320px; margin: 0 auto var(--s2); text-align: center; }
  .comp-scrub.hidden { display: none; }
  }
  @layer components {
  .comp-scrub-label { font-size: var(--fs-sm, 13px); color: var(--muted); }
  }
  @layer screens {
  /* The slider keeps the app's own range styling — every other slider on the page looks
     like this, and a one-off here read as a different kind of control. Only the row
     around it is ours: centred, with a visible label. */
  .comp-scrub input[type="range"] { width: 100%; }

  /* ---- Preview (logged out): a peek tab is shown greyed and inert under a sign-up card ---- */
  .preview-peek > [inert] { opacity: .5; filter: saturate(.55); user-select: none; }
  .preview-peek-banner { border: 1px solid var(--accent, #7070FF); text-align: center; }
  .preview-peek-banner h2 { margin-top: 0; }

  /* ---- Blur frame (compilations) ----
     New classes only. The preview layers mirror rendering.frame_layout's numbers; the chip
     rows wrap, so they need no phone-specific rule. */
  .cw-frame-bg { position: absolute; inset: 0;
    background: repeating-conic-gradient(#3A3F4A 0% 25%, #000044 0% 50%) 50% / 96px 96px;
    filter: blur(28px) brightness(.85); transform: scale(1.1); }
  .cw-frame-bg.hidden, .cw-frame-text.hidden { display: none; }
  /* z-index: on the band style this lettering is drawn ON the strip (#cwBand), which comes
     later in the stage and would otherwise paint over it. */
  .cw-frame-text { position: absolute; inset: 0; pointer-events: none; z-index: 2;
    font-family: 'CK Anton', Impact, sans-serif; color: #FFFFFF; }
  .cw-frame-text > div { position: absolute; white-space: nowrap; line-height: 1;
    -webkit-text-stroke: 12px #000008; paint-order: stroke fill;
    text-shadow: 4px 5px 0 rgba(0, 0, 8, .5); }
  .comp-chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0 8px; }
  .comp-sel-row .comp-chip-row { grid-column: 1 / -1; margin: 0; }
  }
  @layer components {
  .comp-chip { font-family: 'CK Anton', Impact, sans-serif; font-size: 15px; letter-spacing: .02em;
    padding: 3px 8px; min-height: 30px; border-radius: 6px; cursor: pointer;
    background: #000044; border: 1px solid var(--border); text-shadow: 0 0 2px #000008, 0 0 2px #000008; }
  }
  @layer screens {
  .comp-chip:hover, .comp-chip:focus-visible { border-color: var(--accent); }
  .comp-chip-pop { position: fixed; z-index: var(--z-picker); display: flex; flex-wrap: wrap; gap: 8px;
    align-items: center; width: 212px; padding: 8px; border-radius: 8px;
    background: var(--panel-2, #000044); border: 1px solid var(--border);
    box-shadow: 0 8px 24px rgba(0, 0, 8, .45); }
  .comp-chip-pop.hidden { display: none; }
  .comp-chip-pop .sw { width: 28px; height: 28px; border-radius: 6px; cursor: pointer;
    border: 2px solid rgba(255, 255, 255, .25); padding: 0; }
  .comp-chip-pop input[type="color"] { width: 44px; height: 30px; padding: 0; }
  .comp-chip-pop .secondary { padding: 8px 8px; font-size: 13px; }
  }
  @layer mobile {
  @media (max-width: 768px) {

  /* =====================================================================
     MOBILE LAYER — everything below applies ONLY at <=768px.
     Desktop is untouched by construction: CSS inside a media query cannot
     apply above its breakpoint, and nothing above this block was edited.

     ⚠️ HISTORY, so it is not repeated: the first version of this layer was
     written against `.sidebar`, and the app nav was later rebuilt as
     `.appnav` / `.appnav-track`. Nothing updated this block, so `.sidebar`
     matched NOTHING (zero occurrences in the markup) and `.layout
     { flex-direction: column }` was inert because `.layout` is display:block.
     The layer existed, looked plausible, and did nothing. If you rename a
     shell element, grep this block for the old name.

     ⚠️ THIS BLOCK MUST STAY LAST IN THE STYLESHEET. It used to sit above the
     ~250 lines of `#appSection ...` rules, which beat it on specificity (1,1,0
     vs 0,1,0) or simply by coming later at equal specificity — so a second
     tranche of it silently did nothing, exactly like the `.sidebar` rules did.
     Anything here that has to override an `#appSection` rule must ALSO carry
     `#appSection`; matching source order alone is not enough against an ID.
     ===================================================================== */

    /* ---- 1. THE HEADER. It is sticky, so whatever it costs it costs on
       every screen, permanently — and it had no mobile rules at all. Three
       #userbar labels wrapped to two lines each and the bar stood ~190px
       tall, a quarter of the phone spent before any content.
       The wordmark TEXT goes and the C badge stays: it is still the tap
       target home, and "Clipkiln" is set 48px tall in the hero directly
       beneath it. That buys the width to keep all three actions on one
       line, which is worth more than the repetition.
       --hdr is measured off the header at runtime, so .appnav's sticky
       offset follows this down on its own — no JS change. ---- */
    /* space-between alone leaves NO gap when the two children meet in the middle,
       which is how the address ended up butted against the C mark. */
    body > header { padding: var(--s3) var(--s4); gap: var(--s3); }
    header h1 { font-size: var(--fs-md); gap: 8px; }
    header h1::before { width: 26px; height: 26px; font-size: 15px; border-radius: 8px; }
    header h1 .wm { display: none; }
    /* THE LOGGED-IN HEADER. Logged out it is three buttons and fits; logged IN
       it is `email + plan pill + Log out`, and #userbar is a flex item that
       cannot shrink below min-content. A 35-character address made the header
       408px wide on a 390px screen, which is three of the reported faults at
       once: the email ran into the C mark, "Log out" sat at x=336..408 with its
       fill crossing the viewport edge, and — because the header is a
       document-level box — EVERY tab inherited an 18px strip of unpainted dead
       space on the right that the body background does not reach.
       min-width:0 is the part that actually lets a flex item shrink; the
       ellipsis is what it shrinks INTO. */
    /* The bar has three shapes — public (3 buttons), preview (a badge + 3) and
       logged-in (email + pill + Log out) — so hiding "the third button" is a
       guess that breaks on whichever shape you did not picture. Instead every
       item is allowed to SHRINK, with an ellipsis as the backstop, and the
       primary CTA is pinned so it is never the thing that gives.
       NOT flex-wrap: wrapping removes the pressure to shrink, so the logged-in
       bar stopped ellipsising its address and took the header from 69px to
       100px at 390px — a regression dressed up as a fix. */
    #userbar { gap: var(--s2); min-width: 0; flex: 0 1 auto; flex-wrap: nowrap; }
    /* The ADDRESS absorbs every bit of the shrinking, because it is the only
       item here that loses nothing by being cut short. Buttons are actions and
       hold their full label — flex-shrink on them truncated "Log out" to
       "Log ...", which is worse than any layout problem it solved. */
    #userbar > span:first-child { flex: 0 1 auto; }
  }
  }
  @layer components {
  @media (max-width: 768px) {
    #userbar button { white-space: nowrap; padding: 8px 13px; font-size: var(--fs-sm);
                      flex: 0 0 auto; }
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    #userbar > span:first-child {
      min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    header h1 { flex: 0 0 auto; }

    /* ---- 2. THE APP NAV — the bug behind "it's off the left".
       .appnav-track is an `overflow-x: auto` strip that was also
       `justify-content: center`. Centring a scroller places its leading
       items at NEGATIVE offsets, and negative scroll space cannot be
       reached: Home / Extract / Voiceover sat off-screen to the left with
       "Uploads" sliced down its edge, unreachable at any scroll position.
       A scroller has to start at its start edge. ---- */
    .appnav { padding: 0; }
    .appnav-track {
      justify-content: flex-start;
      padding: 8px var(--s4);
      scroll-padding-inline: var(--s4);
      scroll-snap-type: x proximity;
      -webkit-overflow-scrolling: touch;
    }
  }
  }
  @layer components {
  @media (max-width: 768px) {
    .appnav-track button {
      flex: 0 0 auto;          /* labels stay on one line; the strip scrolls */
      scroll-snap-align: start;
      min-height: 44px;
      padding: 8px 13px;
      font-size: var(--fs-sm);
    }

    /* ---- 3. TAP TARGETS. Apple and Google both require 44px. The uploads
       row buttons were ~25px tall with four crammed side by side, which is
       what made the app feel unusable rather than merely cramped. ---- */
    button, button.secondary, button.primary,
    button.inline-primary, .upload-actions button {
      min-height: 44px;
    }
    button.secondary, .upload-actions button { padding: 8px 13px; font-size: 14px; }

    /* Uploads: stack the row and give the actions real width instead of
       four tiny buttons fighting over one line. */
    /* ⚠️ `#appSection .upload-row` sets flex-wrap: wrap. Turn such a row into a
       COLUMN and it becomes a multi-LINE column container: flex lines are laid
       side by side and each is sized to its widest item, so `align-items:
       stretch` no longer binds them to the container. The filename carries
       white-space:nowrap (the desktop ellipsis guard), so its line measured
       615px inside a 358px row and dragged the whole document out to 648px —
       the "text leaves the page and everything shifts left" report. A stacked
       row must not wrap, and needs #appSection to say so. */
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    #appSection .upload-row { flex-wrap: nowrap; }
    .upload-row { flex-direction: column; align-items: stretch; gap: 8px; }
    /* On a phone a long filename is better on two lines than ellipsised to
       nothing; the desktop one-line guard is what needed overriding. */
    #appSection .upload-main strong {
      white-space: normal; overflow-wrap: anywhere;
    }
    .upload-actions { width: 100%; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
    .upload-actions button { width: 100%; }

    /* ---- 4. NOTHING MAY OVERFLOW ITS BOX. ---- */
  }
  }
  @layer components {
  @media (max-width: 768px) {
    .auth-screen, .modal-box, .preview, .story-video, .site-footer {
      max-width: 100%;
      width: auto;
    }
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    .auth-screen { margin-top: 21px; }
    .auth-screen .card { padding: 21px; }

    /* The category pills are `flex: 1; min-width: 140px`. Merely letting them
       shrink — which is all the old `min-width: 0` did — squashed three of
       them to ~105px each and "Emotionally intense" broke out through the
       pill's right border. Three abreast is a desktop shape; give each one
       the full row and the copy fits inside the box it belongs to. */
    .category-option { flex: 1 1 100%; min-width: 0; }

    /* Rows that must wrap rather than overhang. .slider-row was missing from
       this set: its content measured 379px inside a 342px box, which put the
       value readout at x=367..403 on a 390px screen — off the right edge. */
    .defaults-row, .music-row, .slider-row, .subtitle-size-row { flex-wrap: wrap; min-width: 0; }
    /* A `flex: 1` select beside a nowrap label collapses to nothing:
       #defaultMusicTrack rendered 29px wide holding 55px of content. Give
       the control its own line instead of a sliver of the label's. */
  }
  }
  @layer components {
  @media (max-width: 768px) {
    .music-row select, .slider-row input[type="range"],
    .subtitle-size-row input[type="range"] { flex: 1 1 100%; min-width: 0; }

    /* These labels carry `white-space: nowrap` so the desktop control rows stay on one
       line. On a phone that is precisely the "text leaves the box" bug: "Auto-follow the
       speaker" measured 239px of unbreakable text inside a 212px row. Let them wrap. */
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    .defaults-row label, .music-row label, .slider-label, .subtitle-size-label,
    .conn-name, .auto-queue-title { white-space: normal; overflow-wrap: anywhere; }

    /* The desktop defaults panel centres each row and pins its caption to a
       right-aligned 8.5rem column, so the labelled controls sit on one tidy
       centred axis. On a phone the rows wrap, and a centred 136px right-aligned
       caption then floats to a different x on every row — "Aspect", "Sub Pos",
       "Sub Font" and "Extras" each landing somewhere else, aligned to nothing.
       On a narrow screen the caption belongs on its own line, above what it
       labels, left-aligned like every other mobile form.
       #appSection is required here: without it these lose to the desktop rules
       on specificity no matter where they appear. */
    #appSection .defaults-row, #appSection .music-row, #appSection .auto-row {
      justify-content: flex-start; align-items: center;
    }
    #appSection .defaults-row > label:first-child,
    #appSection .music-row > label:first-child {
      min-width: 0; flex: 1 1 100%; text-align: left; margin-bottom: 2px;
    }

    /* Ranges: `#appSection input[type="range"]` pins min-width:96px, so the
       bounce row (two sliders + two readouts + a checkbox label) could not fit
       and the Speed slider was squeezed to a stub. Let them take their own
       line and use the width that is actually there. */
  }
  }
  @layer components {
  @media (max-width: 768px) {
    #appSection input[type="range"] { flex: 1 1 140px; min-width: 0; max-width: none; }
    /* Inside a grouped bounce control the SLIDER is the only part with anywhere to go:
       the caption and the readout are a handful of characters each and cannot shrink
       without clipping ("1.00x" was being squeezed into 28px). Pin them, let the track
       absorb the difference, and keep the group on one line so the pairing survives. */
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    #appSection .bounce-field { flex-wrap: nowrap; max-width: 100%; }
    #appSection .bounce-field > label,
    #appSection .bounce-field > .range-value,
    #appSection .bounce-field > .slider-value,
    #appSection .bounce-field > .subtitle-size-label,
    #appSection .bounce-field > .subtitle-size-value { flex: 0 0 auto; }
    #appSection .bounce-field input[type="range"] { flex: 1 1 64px; min-width: 0; }
    #defaultBounceOpts, #autoBounceOpts, #storyBounceOpts, #storySubBounceOpts,
    #appSection .subtitle-size-row {
      display: flex; flex-wrap: wrap; align-items: center; row-gap: 8px; max-width: 100%;
    }

    /* Modals: near full-screen, and reachable with a thumb. */
  }
  }
  @layer screens {
  @media (max-width: 768px) {
    .modal-box { width: calc(100vw - 32px); max-width: 420px; padding: 21px; }

    /* ---- 5. iOS FORCE-ZOOM. Safari zooms the page in on focus for any
       input under 16px and does not zoom back out. 16px is the fix; it is a
       platform rule, not a preference.
       The 44px floor belongs to controls you TYPE IN. Applied to every
       input — as it was — it also stretched range sliders, checkboxes and
       radios into 44px blocks and distorted them. ---- */
  }
  }
  @layer base {
  @media (max-width: 768px) {
    input, select, textarea, .contact-input { font-size: 16px; }
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    input:not([type="range"]):not([type="checkbox"]):not([type="radio"]),
    select, .contact-input { min-height: 44px; }
  }
  }
  @layer base {
  @media (max-width: 768px) {
    textarea { min-height: 120px; }

    /* ---- 6. Breathing room. Desktop padding is tight on a phone. ---- */
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    #appSection { padding-left: var(--s4); padding-right: var(--s4); }
    .content { min-width: 0; }
  }
  }
  @layer components {
  @media (max-width: 768px) {
    .card { padding: var(--s4); }
    h1 { font-size: 24px; }
    h2 { font-size: 19px; }

    /* NOTE: there is deliberately no `body { overflow-x: hidden }` here.
       It was hiding the overflow above rather than fixing it — and on iOS
       an overflow rule on body can kill `position: sticky` on the header
       and the nav. The causes are fixed; the page is measured at 360/390/430
       to prove scrollWidth === clientWidth. If something overflows again,
       find it, do not re-add this. */

  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    .stage { height: 180vh; }
    /* The pin is `top: 0`, so its centred content slid up under the sticky header —
       "How it works" was half-hidden behind it. --hdr is the header's measured
       height; padding (with the global border-box) keeps the centring honest. */
    .stage-pin { height: 100dvh; padding-top: var(--hdr, 55px); }
    /* A grid item's default `min-width: auto` refuses to shrink below its content's
       min-content width. Each .short holds a figcaption whose score badge is
       `flex: 0 0 auto`, so at 360px the three tracks together demanded 306px inside a
       283px box and the reel spilled. minmax(0, 1fr) lets the tracks actually shrink. */
    /* The four cross-fading steps are `position: absolute; inset: 0` inside a
       container given a FIXED 4.4em height — measured against the parent's 15px,
       so ~66px. At 390px the longest step wraps to four or five 26px lines and
       simply ran out through the bottom of its box and over the reel beneath it.
       Stacking them in one grid cell instead keeps the cross-fade and lets the
       container take the height of the tallest step, with no magic number. */
    .stage-steps { height: auto; min-height: 0; display: grid; }
    .stage-step { position: relative; grid-area: 1 / 1; inset: auto; }

    .reel-out { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .short, .short figcaption { min-width: 0; }
    /* The badge is `flex: 0 0 auto` and the quote beside it is a bare text node, which
       becomes an anonymous flex item that min-width cannot reach. Wrapping the line is
       the only thing that lets the caption fit a 84px track at 360px. */
    .short figcaption { font-size: var(--fs-xs); gap: 8px; flex-wrap: wrap;
                        overflow-wrap: anywhere; line-height: 1.3; }

    /* Compilations: two columns do not fit a phone, so the picker and the selection
       stack. Carries #appSection because the grid it overrides is declared above the
       ~250-line #appSection layer, which beats a bare class on specificity. */
    #appSection .comp-panes { grid-template-columns: 1fr; }
    #appSection .comp-pane { padding: var(--s2); }
    /* The compilation pool: a shorter scroll box on a phone, and the row stacks so a long
       filename does not push its Add button off the edge. */
    /* One column on a phone: two lists side by side at 375px is two unreadable columns. */
    #appSection .auto-queue-panes,
    #appSection .auto-queue-panes:has(> .auto-pool:not(.hidden)) {
        grid-template-columns: 1fr; }
    #appSection .auto-pool-list { max-height: 200px; }
    #appSection .auto-pool-row { flex-wrap: wrap; }
    /* The drag handle and the Remove button are the two touch targets in the row;
       the title field takes what is left rather than pushing them off the edge. */
    /* .comp-sel-row is a 5-column GRID (the earlier display:flex rule is overridden), so
       flex-wrap on it did nothing and the row kept five rigid columns on a phone. */
    #appSection .comp-sel-row { grid-template-columns: auto auto 1fr; }
    #appSection .comp-sel-row > .hint { grid-column: 2 / -1; }
    #appSection .comp-sel-row > button { grid-column: 1 / -1; justify-self: end; }
    #appSection .comp-drag { font-size: 22px; padding: 0 8px; }
    #appSection .comp-sort { width: 100%; }
    #appSection .comp-sort button { flex: 1 1 auto; }
    #appSection video.comp-preview { max-width: 100%; }

    /* ---- The framing timeline's resize handles are 9px, which is a mouse target, not a
       fingertip one — and the position blocks only became draggable by touch at all in the
       same change that widened these. 14px is still well under half of the 1.5s default
       block. On a block short enough for the two handles to meet, a tap resizes rather than
       moves; at that size resizing is the likelier intent anyway. ---- */
    .framing-handle { width: 14px; }

    /* ---- The frame-step row splits the line evenly, the way .comp-sort does above, so
       both buttons are a comfortable thumb target instead of two small pills hugging the
       left edge. #appSection because the global #appSection layer would otherwise win on
       specificity — see this block's header. The 44px tap floor already applies. ---- */
    #appSection .dynpos-nudge { width: 100%; }
    #appSection .dynpos-nudge button { flex: 1 1 auto; }
  }
  @media (max-width: 380px) {


  /* Small phones: shave the action padding so the common bars still fit one
     line at 360px and only genuinely tiny screens take the wrap above. */
    body > header { padding: var(--s3) var(--s3); gap: var(--s2); }
    #userbar { gap: 8px; }
  }
  }
  @layer components {
  @media (max-width: 380px) {
    #userbar button { padding: 8px 8px; font-size: 12.5px; }
    /* Preview mode carries a fourth item — a "Preview" chip — and four do not fit.
       The chip is the one that is only a label, and the same fact is stated in
       full in the banner at the top of the page body, so it is what goes. */
  }
  }
  @layer mobile {
  @media (max-width: 380px) {
    #userbar .preview-badge { display: none; }
    /* Backstop for genuinely tiny screens: only bites when there is real overflow
       (the logged-in bar already fits, so "Log out" is never touched), and never
       the primary CTA. */
    #userbar button:not(.home-navbtn-accent) {
      flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis;
    }
  }
  @media (max-width: 760px) {


  /* ---- The Other tab on a phone. LAST in the stylesheet, and inside a max-width query,
     so desktop is untouched by construction. #appSection on every selector, because ~250
     lines of #appSection rules above beat a bare class on specificity. ---------------- */
    #appSection .other-pick { flex-direction: column; gap: 13px; }
    #appSection .other-option { flex: 1 1 auto; padding: 21px 13px; gap: 8px; }
    #appSection .other-option-title { font-size: 1.2rem; }
    #appSection .transcript-bar { gap: 8px; }
    #appSection .transcript-bar h3 { flex: 1 1 100%; }
    #appSection .transcript-bar button { flex: 1 1 auto; }
    #appSection .transcript-box { min-height: 300px; font-size: 13px; }
    /* The source row's link box needs the whole width once the dropdown has taken its
       share, or the placeholder is three characters wide. */
    #appSection #otherSubUrl, #appSection #otherTxUrl { flex: 1 1 100%; min-width: 0; }
  }
  /* Sound-effect pin volume (dB): "Volume − [+3 dB] +" on one line, the slider full width
     under it. Carries #appSection to beat the app-wide control sizing above; it targets only
     the .sfx-vol row, which nothing else uses. */
  #appSection .sfx-vol { flex-wrap: wrap; gap: 8px; }
  #appSection .sfx-vol .sfx-step {
    flex: 0 0 44px; width: 44px; height: 44px; padding: 0; border-radius: 10px;
    font-size: 20px; line-height: 1;
  }
  #appSection .sfx-vol .sfx-db {
    flex: 0 0 96px; width: 96px; height: 44px; padding: 0 8px; text-align: center;
    box-sizing: border-box;
  }
  #appSection .sfx-vol input[type="range"] { flex: 1 1 100%; max-width: none; min-width: 0; }
  #appSection .sfx-warn { flex: 1 1 100%; margin: 0; font-size: 12px; color: #FF7700; }
  #appSection .sfx-warn.hidden { display: none; }
  /* Filmstrips scrub by pointer (mouse, touch, pen): the browser must not take a drag on
     them for a page scroll, same as the band timelines below them. */
  .framing-vidtl { touch-action: none; }
  /* Play / pause over the preview: the clip's own sound plus its sound effects. */
  }
  /* Batch 2 fix V1: components, so `#appSection .clip-controls button` and the phone
     44px rules keep winning on specificity, as before the A1 layers. */
  @layer components {
  .crop-play {
    position: absolute; left: 8px; bottom: 8px; z-index: 6; width: 40px; height: 40px;
    padding: 0; border-radius: 50%; border: 1px solid rgba(255, 255, 255, .35);
    background: rgba(0, 0, 8, .6); color: #FFFFFF; font-size: 16px; line-height: 1; cursor: pointer;
  }
  #appSection .crop-play { padding: 0; width: 40px; height: 40px; border-radius: 50%; }
  }
  @layer mobile {
  /* A non-9:16 clip: the preview and sound effects, without the 9:16 framing tools. */
  .crop-preview.framing-off .crop-window, .crop-preview.framing-off .crop-preview-label,
  .crop-preview.framing-off .split-overlay { display: none !important; }
  .dynpos.framing-off > :not(.sfx-part) { display: none !important; }
  .dynpos.framing-off { flex: 1 1 100%; min-width: 0; }
  @media (max-width: 760px) {
  /* Schedule clips to post, on a phone: the modal takes the screen's width, rows stack. */
    .modal-box.bulk-sched-modal { width: calc(100vw - 32px); max-width: none; padding: 13px; }
    .bulk-sched-time input { flex: 1 1 100%; font-size: 16px; }
    .bulk-sched-modal textarea { font-size: 16px; }

  /* ==== GOLDEN RATIO EXPERIMENT (preview) ====================================
     One block, last in the sheet so it wins, so reverting it is a single commit.
     φ = 1.618: every space is a Fibonacci number (each ≈ the previous × φ), the
     type scale steps by √φ (two steps = ×φ), and wide/narrow column pairs split
     61.8 / 38.2. Colours come from the experiment palette. */
  /* Its :root tokens moved to frontend/ui/tokens.css (Batch 2 A1); the values are unchanged. */
  }
  }
  @layer screens {

  body { line-height: var(--phi); }
  h1, h2, h3, h4 { line-height: 1.2; }
  }
  @layer components {
  h1 { font-size: var(--h1-font); }
  h2 { font-size: var(--h2-font); }
  }
  @layer base {

  /* Form fields: white-alpha fills read grey on the new base; tint them blue. */
  input[type="text"], input[type="email"], input[type="password"], input[type="number"],
  input[type="time"], input[type="search"], input[type="url"], select, textarea {
    border-color: rgba(112, 112, 255, 0.40);
    background: rgba(0, 0, 221, 0.10);
  }
  }
  @layer components {
  input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) {
    border-color: rgba(112, 112, 255, 0.65); background: rgba(0, 0, 221, 0.16);
  }
  /* Focus/hover borders were the fill blue, invisible on a #000044 panel. */
  input:focus, textarea:focus, select:focus,
  .other-option:hover, .category-option:hover, .file-drop-zone:hover { border-color: var(--accent-text); }

  /* Buttons: 13 × 21 padding, a φ pair. */
  button.secondary { padding: var(--fib-1) var(--fib-2); }
  button.secondary.dark { background: var(--deep); border-color: var(--deep); }
  }
  @layer screens {
  button.secondary.dark:hover:not(:disabled) { background: var(--accent); border-color: var(--accent); }
  }
  @layer components {
  /* Primary CTA = gold, secondary CTA = orange; dark ink on both. */
  .home-cta, :is(#tab-earn, #tab-mcp) .earn-cta,
  #userbar button.home-navbtn-accent, #userbar button.home-navbtn-accent:hover {
    background: var(--gold); border-color: var(--gold); color: var(--cta-ink);
    padding: var(--fib-2) var(--fib-3);
  }
  }
  @layer screens {
  .home-cta:hover { background: #FFD633; border-color: #FFD633; }
  }
  @layer components {
  .home-ghost { border-color: var(--orange); color: var(--orange); padding: var(--fib-2) var(--fib-3); }
  }
  @layer screens {
  .home-ghost:hover { border-color: var(--orange); background: rgba(255, 119, 0, 0.12); }

  /* Gradient text: blue→blue vanishes on near-black, so tint → gold. background-IMAGE
     only (see the Dark Reader note on .hero-mark i). */
  .hero-mark i, .hero h1 em { background-image: linear-gradient(96deg, var(--accent-text-2), var(--gold)); }

  /* Header + tab bar: the frosted glass tinted to the new base (padding is already
     Fibonacci via the token remap: 21 / 34). */
  body > header { background: rgba(0, 0, 8, 0.72); }
  .appnav { background: rgba(0, 0, 8, 0.78); }

  /* Landing sections: 89 top / 55 bottom, a φ pair. */
  .sec { padding: var(--fib-6) 0 var(--fib-5); }
  @media (min-width: 881px) {

  /* 61.8 / 38.2 columns. Desktop only: the existing max-width rules stack them on
     phones and must keep winning there. */
    .home-hero { grid-template-columns: 1.618fr 1fr; /* φ : 1 */ }
  }
  @media (min-width: 901px) {
    .row { grid-template-columns: 1.618fr 1fr; /* φ : 1 */ }
    .row.flip { grid-template-columns: 1fr 1.618fr; }
  }
  /* Logo mark (from round 2): blue → gold. The C straddles both ends, so it is white
     with a dark halo — readable on the blue and on the gold. */
  body > header h1::before, header h1::before, .hero-mark::before {
    background: linear-gradient(150deg, #0000DD, var(--gold));
    color: #FFFFFF; text-shadow: 0 1px 3px rgba(0, 0, 8, .85);
  }
  /* ---- round 4: completing the pattern, readable ----
     Rule: a colour whose hex digits reduce to 8, 1 or 9 stays; anything else moved to the
     nearest readable pure colour (one-off pass over the sheet), and every hard-coded
     padding/margin/gap px is now a Fibonacci step. These tokens finish it. */
  /* Round 4's :root tokens moved to frontend/ui/tokens.css (Batch 2 A1). */
  /* The transmutation starts from pure 8 blue (#7070FF), ends in gold (9). */
  .hero-mark i, .hero h1 em {
    background-image: linear-gradient(96deg, var(--accent-text), var(--gold));
  }
  /* Clip & Earn diagrams: strokes and fills in the readable 8 blue — the fill blue
     (#0000DD) disappears as a thin line on the dark field. */
  :is(#tab-earn, #tab-mcp) .earn-f-accent, :is(#tab-earn, #tab-mcp) .earn-bar rect { fill: var(--accent-text); }
  :is(#tab-earn, #tab-mcp) .earn-s-accent { stroke: var(--accent-text); }
  /* Hero headline on the φ scale: 42px (16·φ²) on phones, 68px (16·φ³) from 881px. */
  .hero h1 { font-size: var(--h1-font); }
  @media (min-width: 881px) { .hero h1 { font-size: var(--fs-5xl); } }
  }
  @layer components {
  /* ==== /GOLDEN RATIO EXPERIMENT ========================================== */

  /* ==== BATCH 1 UX (2026-10-26) ============================================
     Desktop is frozen. The first rules here style only elements that did not exist
     before (status text the owner allowed on both: the running-video line, the wait
     note, the watermark link) — no existing element is touched. Everything else is
     inside the phone query, LAST in the sheet, with #appSection where it must beat
     an #appSection rule. */
  .running-job-line { margin: var(--s2) 0 var(--s3); }
  }
  @layer screens {
  .running-job-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%;
                     background: var(--good); margin-right: 8px; vertical-align: middle; }
  .running-job-line .linklike, .plan-nudge .linklike,
  #appSection .running-job-line .linklike, #appSection .plan-nudge .linklike {
    padding: 0; font-size: inherit; min-height: 0; vertical-align: baseline; }
  }
  @layer components {
  .wait-note { margin: var(--s2) 0 0; }
  }
  @layer screens {
  .extract-more { display: contents; }
  .extract-more-toggle { display: none; }
  .content-rights-row.needs-tick { outline: 2px solid var(--warn); outline-offset: 4px; border-radius: 6px; }
  }
  @layer mobile {
  @media (max-width: 768px) {
    #appSection .extract-more-toggle { display: block; width: 100%; min-height: 44px; margin: 8px 0 13px; }
    /* The label is long; on a 390px phone it ran out of a one-line, fixed-height button. */
    #appSection .extract-more-toggle, html.ui2 body:not(#ck2) #extractMoreToggle { height: auto;
      white-space: normal; overflow-wrap: anywhere; line-height: 1.3; padding: 10px 14px; text-align: center; }
    #appSection .extract-more:not(.open) { display: none; }
    /* The one action on this screen stays in reach while the form scrolls. Sticky, not
       fixed: it only pins while the add-video card is on screen. */
    #appSection #extractBtn {
      position: sticky; bottom: calc(8px + env(safe-area-inset-bottom)); z-index: var(--z-sticky-action);
      width: 100%; min-height: 48px; box-shadow: 0 -8px 21px rgba(0, 0, 0, 0.35);
    }
    /* Thumb-sized range handles (the bare slider is ~16px tall). */
    #appSection input[type="range"] { min-height: 44px; }
    /* "N min left" beside the plan pill: phones only (the base .hidden keeps it off desktop). */
    #userbar .mins-left:not(:empty) {
      display: inline-flex; align-items: center; flex: 0 0 auto; white-space: nowrap;
      padding: 3px 8px; border-radius: 999px; font-size: 12px; font-weight: 600;
      background: var(--border-2); color: var(--muted);
    }
    #userbar .mins-left.mins-left-low { background: rgba(255, 119, 0, 0.15); color: var(--warn); }
    /* The Pro trial is the lowest-risk step on the page; on a phone it gets the filled
       gold of the primary CTA instead of a ghost button (A4 P2). */
  }
  }
  @layer components {
  @media (max-width: 768px) {
    .plan .plan-cta-trial, #appSection .plan .plan-cta-trial {
      background: var(--gold); border-color: var(--gold); color: var(--cta-ink);
    }
  }
  }
  @layer mobile {
  @media (max-width: 768px) {
    #appSection .running-job-line .linklike { min-height: 44px; }
    /* Batch 3 round 3.7 (pre-existing bug, both flags): a clip card's option ticks ("Captions",
       "Bounce", "Loop"…) and the Post menu's account / option ticks measured 42px tall on a
       phone (8px padding + one 26px line), under the 44px tap target. Phones only: the
       desktop rows keep their height. The Post menu can sit outside #appSection (ui2 moves it
       into the Post sheet while open), so it is not scoped to it. */
    .clip-item label:has(> input[type="checkbox"]),
    .upload-menu label:has(> input[type="checkbox"]) { min-height: 44px; }
    /* A locked row ("⏩ Speed adjustment" + its nowrap "Base & Pro" tag) was 326px inside a
       248px card and widened the whole page at 360px on Free (C6 B6): the tag wraps below. */
    .dynpos-toggle.paid-locked, #appSection .dynpos-toggle.paid-locked { flex-wrap: wrap; }
  }
  @media (max-width: 380px) {
    /* Four items in the logged-in bar: the plan NAME pill gives way to the minutes, which
       are the number someone checks; the plan is named in the strip under the nav. */
    #userbar:has(.mins-left:not(:empty)) .pill { display: none; }
    /* "Log out" was cut to "L…" here even before the minutes pill (the address took the room). */
    #userbar:has(.mins-left:not(:empty)) > button { flex: 0 0 auto; }
  }
  }

  /* ==== Batch 3 round 3.4: the landing hero (ui2 ONLY) ========================================
     the server serves the hero in section.hero only under ui2 (inject cuts it otherwise); this hides the
     old hero (and its centred wordmark, which repeated the header logo) and lays out the new one:
     phone = headline, link box, price line, then the proof list beside a real 9:16 clip;
     from 900px = 61.8 / 38.2 columns, clip on the right. Existing tokens only (no new colour). */
  @layer mobile {
  html.ui2 .hero { min-height: 0; padding: var(--s5) 0 var(--s6); }
  html.ui2 .hero > .hero-inner { display: none; }
  html.ui2 .ck2-hero {
    position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "copy copy" "proof clip"; gap: var(--s5) var(--s4); align-items: center;
  }
  html.ui2 .ck2-hero-copy { grid-area: copy; min-width: 0; }
  html.ui2 .ck2-hero .hero-kicker { margin-bottom: var(--s5); }
  html.ui2 .hero h1.ck2-hero-h1 {
    font-size: var(--type-h2); line-height: var(--type-h2-lh); letter-spacing: var(--type-display-track);
    max-width: 20ch; margin: 0 0 var(--s3);
  }
  html.ui2 .ck2-hero-lead {
    font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--muted);
    max-width: 46ch; margin: 0 0 var(--s4);
  }
  html.ui2 .ck2-hero-form {
    display: flex; align-items: center; gap: var(--s1); max-width: 560px; min-height: 56px;
    padding: var(--s1); border: 1px solid var(--border-2); background: var(--panel);
    border-radius: var(--r-pill); box-sizing: border-box;
    transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);
  }
  html.ui2 .ck2-hero-form:focus-within { border-color: var(--accent-text); box-shadow: 0 0 0 3px var(--accent-soft); }
  html.ui2 .ck2-hero-form input,
  html.ui2 .ck2-hero-form input:hover:not(:disabled),
  html.ui2 .ck2-hero-form input:focus {
    flex: 1 1 auto; min-width: 0; min-height: 44px; margin: 0; padding: 0 var(--s4);
    border: 0; background: transparent; box-shadow: none; outline: none;
    font-size: var(--type-body); color: var(--text);
  }
  html.ui2 .ck2-hero-form input[aria-invalid="true"] { color: var(--danger); }
  html.ui2 .ck2-hero-form .ck2-hero-go {
    flex: 0 0 auto; min-height: 46px; padding: 0 var(--s4); border-radius: var(--r-pill);
    white-space: nowrap; font-size: var(--type-body);
  }
  html.ui2 .ck2-hero-form .ck2-hero-go:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
  html.ui2 .ck2-hero-err:empty { display: none; }
  html.ui2 .ck2-hero-err {
    margin: var(--s2) 0 0; font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--danger);
    max-width: 560px;
  }
  html.ui2 .ck2-hero-meta {
    margin: var(--s3) 0 0; font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--muted);
  }
  html.ui2 .ck2-hero-meta b { color: var(--text); font-weight: 600; }
  html.ui2 .ck2-hero-meta a {
    color: var(--accent-text); text-underline-offset: 3px; white-space: nowrap;
    /* a 44px-tall hit area without moving the line */
    display: inline-block; padding: 13px 0; margin: -13px 0;
  }
  html.ui2 .ck2-hero-meta a:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; border-radius: var(--r-sm); }

  html.ui2 .ck2-hero-clip { grid-area: clip; margin: 0; justify-self: end; width: 132px; }
  html.ui2 .ck2-hero-frame {
    position: relative; width: 100%; aspect-ratio: 9 / 16; overflow: hidden;
    border-radius: var(--r-card); border: 1px solid var(--border-2); background: var(--bg-2);
    box-shadow: var(--sh-2);
  }
  html.ui2 .ck2-hero-frame video { display: block; width: 100%; height: 100%; object-fit: cover; }
  html.ui2 .ck2-hero-score {
    position: absolute; top: var(--s2); left: var(--s2); display: inline-flex; align-items: center;
    gap: var(--s1); padding: 3px var(--s2) 3px var(--s1); border-radius: var(--r-pill);
    background: var(--scrim); color: var(--text); font-size: var(--type-cap); line-height: var(--type-cap-lh);
    font-weight: 600; white-space: nowrap;
  }
  html.ui2 .ck2-hero-score svg { width: 14px; height: 14px; fill: none; stroke-width: 2.5; }
  html.ui2 .ck2-hero-score svg circle { stroke: var(--border-2); }
  html.ui2 .ck2-hero-score svg path { stroke: var(--gold); stroke-linecap: round; }
  html.ui2 .ck2-hero-play {
    position: absolute; right: var(--s1); bottom: var(--s1); width: 44px; height: 44px; padding: 0;
    display: grid; place-items: center; border-radius: 50%; cursor: pointer;
    background: var(--scrim); border: 1px solid var(--border-2); color: var(--text);
  }
  html.ui2 .ck2-hero-play svg { width: 18px; height: 18px; fill: currentColor; }
  html.ui2 .ck2-hero-play .s, html.ui2 .ck2-hero-play.on .p { display: none; }
  html.ui2 .ck2-hero-play.on .s { display: inline; }
  html.ui2 .ck2-hero-play:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
  html.ui2 .ck2-hero-clip figcaption {
    margin-top: var(--s2); font-size: var(--type-cap); line-height: var(--type-cap-lh); color: var(--muted);
  }
  html.ui2 .ck2-hero-proof {
    grid-area: proof; list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s3);
    align-self: center;
  }
  html.ui2 .ck2-hero-proof li {
    display: flex; gap: var(--s2); align-items: baseline; margin: 0;
    font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--text);
  }
  html.ui2 .ck2-hero-proof li::before {
    content: ""; flex: 0 0 6px; height: 6px; border-radius: 50%; background: var(--gold);
    transform: translateY(-2px);
  }
  @media (max-width: 600px) {
    html.ui2 .ck2-hero .hero-kicker { display: none; }
  }
  @media (max-width: 380px) {
    /* Too narrow for the link and the button on one line; the proof list says what the lead says. */
    html.ui2 .ck2-hero-lead { display: none; }
    html.ui2 .ck2-hero-form { flex-direction: column; align-items: stretch; border-radius: var(--r-card); }
    html.ui2 .ck2-hero-form input { min-height: 48px; }
    html.ui2 .ck2-hero-form .ck2-hero-go { min-height: 48px; border-radius: var(--r-ctl); }
    html.ui2 .ck2-hero-clip { width: 120px; }
  }
  @media (min-width: 601px) {
    html.ui2 .hero h1.ck2-hero-h1 { font-size: var(--type-display); line-height: var(--type-display-lh); }
    html.ui2 .ck2-hero-lead { font-size: var(--type-body); line-height: var(--type-body-lh); }
    html.ui2 .ck2-hero-clip { width: 180px; }
  }
  @media (min-width: 900px) {
    html.ui2 .hero { padding: var(--s6) 0 var(--s7); }
    html.ui2 .ck2-hero {
      grid-template-columns: minmax(0, 61.8fr) minmax(0, 38.2fr);
      grid-template-areas: "copy clip" "proof clip"; gap: var(--s5) var(--s7);
    }
    html.ui2 .ck2-hero-copy { align-self: end; }
    html.ui2 .ck2-hero-proof {
      align-self: start; display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5);
      padding-top: var(--s4); border-top: 1px solid var(--border);
    }
    html.ui2 .ck2-hero-clip { justify-self: center; width: min(315px, calc(70vh * 9 / 16)); }
  }
  @media (prefers-reduced-motion: reduce) {
    html.ui2 .ck2-hero-form { transition: none; }
  }
  }
  /* ==== /Batch 3 round 3.4 ===================================================================== */

  /* ==== Batch 3 round 3.6: the Post tab by day (ui2 ONLY) =======================================
     app.js block "Batch 3 round 3.6" draws #ck2PostTab inside #uploadsPendingPane on the "scheduled"
     route: Accounts + Automation cards, a Scheduled / Posted / Failed segment, rows grouped by day.
     The legacy pending list and its hint stay in the page but are not shown under ui2. */
  @layer mobile {
  html.ui2 #uploadsPendingPane > .hint, html.ui2 #pendingUploadsList { display: none; }
  html.ui2 body[data-ck2-route="scheduled"] .uploads-switch { display: none; }
  html.ui2 .ck2-pt { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
  html.ui2 .ck2-pt-top { display: grid; grid-template-columns: 1fr; gap: var(--s3); }
  @media (min-width: 900px) { html.ui2 .ck2-pt-top { grid-template-columns: 1fr 1fr; } }
  html.ui2 .ck2-pt-card { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); min-width: 0; }
  html.ui2 .ck2-pt-cardh { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); width: 100%; min-width: 0; }
  html.ui2 .ck2-pt-cardh .ck-btn { margin: 0; }
  html.ui2 .ck2-pt-card h3 { margin: 0; font-size: var(--type-sm); font-weight: var(--w-semi); color: var(--muted); text-transform: none; }
  html.ui2 .ck2-pt-acctlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); width: 100%; }
  html.ui2 .ck2-pt-acct { display: flex; flex-wrap: wrap; align-items: center; gap: 4px var(--s2); min-width: 0; font-size: var(--type-sm); }
  html.ui2 .ck2-pt-def { min-height: 24px; }
  html.ui2 .ck2-pt-plat { color: var(--muted); }
  html.ui2 .ck2-pt-name { font-weight: var(--w-semi); color: var(--text); overflow-wrap: anywhere; min-width: 0; }
  html.ui2 .ck2-pt-def .ck-icon, html.ui2 .ck2-pt-def svg { width: 14px; height: 14px; }
  html.ui2 .ck2-pt-blocked { flex-basis: 100%; color: var(--orange); font-size: var(--type-cap); }
  html.ui2 .ck2-pt-muted { color: var(--muted); }
  html.ui2 .ck2-pt-autoline { margin: 0; }
  html.ui2 .ck2-pt-autoline .ck-badge { white-space: normal; height: auto; padding-top: 4px; padding-bottom: 4px; }
  html.ui2 .ck2-pt-seg { align-self: flex-start; }
  html.ui2 .ck2-pt-list { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
  html.ui2 .ck2-pt-dayh { margin: 0 0 var(--s2); font-size: var(--type-sm); font-weight: var(--w-semi); color: var(--muted); }
  html.ui2 .ck2-pt-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
  html.ui2 .ck2-pt-row {
    display: grid; grid-template-columns: 45px minmax(0, 1fr) auto; align-items: center; gap: var(--s3);
    padding: var(--s2); background: var(--e1-bg); border: 1px solid var(--e1-border); border-radius: var(--r-card); min-width: 0;
  }
  html.ui2 .ck2-pt-thumb {
    width: 45px; height: 80px; border-radius: var(--r-sm); overflow: hidden; background: var(--bg-2);
    display: flex; align-items: center; justify-content: center; color: var(--muted);
  }
  html.ui2 .ck2-pt-thumb video { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
  html.ui2 .ck2-pt-thumb svg { width: 18px; height: 18px; }
  html.ui2 .ck2-pt-body { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 0; }
  html.ui2 .ck2-pt-title {
    font-weight: var(--w-semi); color: var(--text); font-size: var(--type-sm); line-height: var(--type-sm-lh);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; max-width: 100%;
  }
  html.ui2 .ck2-pt-meta { display: flex; flex-wrap: wrap; gap: 0 var(--s2); color: var(--muted); font-size: var(--type-cap); line-height: var(--type-cap-lh); min-width: 0; max-width: 100%; }
  html.ui2 .ck2-pt-time { font-variant-numeric: tabular-nums; color: var(--text); }
  html.ui2 .ck2-pt-where { overflow-wrap: anywhere; min-width: 0; }
  html.ui2 .ck2-pt-err { color: var(--danger); font-size: var(--type-cap); line-height: var(--type-cap-lh); overflow-wrap: anywhere; max-width: 100%; }
  html.ui2 .ck2-pt-acts { display: flex; flex-direction: column; gap: var(--s2); align-items: stretch; }
  @media (max-width: 380px) {
    html.ui2 .ck2-pt-row { grid-template-columns: 45px minmax(0, 1fr); }
    html.ui2 .ck2-pt-acts { grid-column: 2; flex-direction: row; flex-wrap: wrap; }
    html.ui2 .ck2-pt-acts:empty { display: none; }
  }
  @media (pointer: coarse) {
    html.ui2 .ck2-pt .ck-btn--sm, html.ui2 .ck2-pt a.ck-btn--sm { min-height: 44px; }
  }
  }
  /* ==== /Batch 3 round 3.6 ===================================================================== */

  /* ==== Batch 3 round 3.2: one Post sheet everywhere (ui2 ONLY) ================================
     app.js block "Batch 3 round 3.2" opens Batch 2's Sheet (.ck2-sheet.ck2-post-sheet) and moves
     the existing upload menu + status divs into its body (.ck2-post) while it is open. These rules
     only re-lay that menu for a sheet: no border box, 44px touch rows, Connect buttons in place
     of "not connected", When: Now / Pick a time (data-when hides the other sender), the sender
     stuck to the bottom of the sheet, and the sent view (menu hidden, result + Retry + Done).
     Every rule html.ui2-scoped, tokens only (no new colour). */
  @layer mobile {
    html.ui2 .ck2-post { display: flex; flex-direction: column; gap: var(--s3); padding-bottom: var(--s2); }
    html.ui2 .ck2-post .ck2-post-h { margin: 0; padding: 0; font-size: var(--type-sm); font-weight: var(--w-semi);
      color: var(--muted); text-transform: none; }
    html.ui2 .ck2-post > .upload-menu { margin: 0; padding: 0; border: 0; max-width: none; gap: var(--s3); }
    html.ui2 .ck2-post .upload-plat-group { display: flex; flex-direction: column; gap: var(--s2); }
    html.ui2 .ck2-post .upload-opt-acct { min-height: 44px; }
    html.ui2 .ck2-post .upload-opt-acct input { width: 20px; height: 20px; flex: 0 0 auto; }
    html.ui2 .ck2-post .ck2-post-noconn { opacity: 1; }
    html.ui2 .ck2-post .ck2-post-connect { align-self: flex-start; }
    html.ui2 .ck2-post .upload-field input:not([type="checkbox"]):not([type="radio"]),
    html.ui2 .ck2-post .upload-field textarea,
    html.ui2 .ck2-post .upload-field select { width: 100%; box-sizing: border-box; }
    html.ui2 .ck2-post-note { margin: 0; padding: var(--s2) var(--s3); border-radius: var(--r-ctl);
      background: var(--good-soft); color: var(--text); font-size: var(--type-sm); }
    html.ui2 .ck2-post-note.is-bad { background: var(--danger-soft); }
    html.ui2 .ck2-post-when { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0; padding: 0; border: 0; }
    html.ui2 .ck2-post-when legend { width: 100%; margin-bottom: var(--s2); }
    html.ui2 .ck2-post-when label { display: inline-flex; align-items: center; gap: var(--s2); min-height: 44px;
      padding: 0 var(--s3); border: 1px solid var(--border-3); border-radius: var(--r-pill); cursor: pointer; }
    html.ui2 .ck2-post-when label:has(input:checked) { border-color: var(--gold); background: var(--accent-soft); }
    html.ui2 .ck2-post-when label:has(input:focus-visible) { outline: 2px solid var(--gold); outline-offset: 2px; }
    html.ui2 .ck2-post[data-when="now"] .sched-field { display: none; }
    html.ui2 .ck2-post[data-when="pick"] [id^="upload-now-wrap-"] { display: none; }
    html.ui2 .ck2-post .sched-field > label { font-size: var(--type-sm); }
    html.ui2 .ck2-post .sched-row { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: stretch; }
    html.ui2 .ck2-post .sched-row input { flex: 1 1 200px; min-height: 44px; }
    /* The sender stays in reach while the captions and TikTok screen scroll under it. */
    html.ui2 .ck2-post [id^="upload-now-wrap-"],
    html.ui2 .ck2-post[data-when="pick"] .sched-row > span {
      position: sticky; bottom: 0; z-index: 1; display: block;
      padding: var(--s2) 0; background: var(--e3-bg);
    }
    html.ui2 .ck2-post [id^="upload-now-"]:is(button),
    html.ui2 .ck2-post [id^="upload-sched-"]:is(button) { width: 100%; min-height: 48px; }
    html.ui2 .ck2-post .upload-status:empty { display: none; }
    html.ui2 .ck2-post .upload-status > div { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2);
      padding: var(--s2) 0; }
    html.ui2 .ck2-post .ck2-post-retry { margin-left: auto; }
    html.ui2 .ck2-post.ck2-post-sent > .ck2-post-h { display: none; }
    html.ui2 .ck2-post-done .ck-btn { width: 100%; min-height: 48px; }
    /* A long "Post “…”" title must not squeeze the close button under 44px. */
    html.ui2 .ck2-post-sheet .ck2-sheet-x { flex: none; }
    /* The legacy menu's own small targets, on a phone or any touch screen (verifier V2-2):
       the folds, the made-for-kids answers and its help link. */
    html.ui2 .ck2-post .upload-menu a[href] { display: inline-block; padding-block: 5px; }
    @media (max-width: 768px), (pointer: coarse) {
      html.ui2 .ck2-post .upload-menu summary { min-height: 44px; box-sizing: border-box; padding-block: 11px; }
      html.ui2 .ck2-post .upload-menu label:has(input[type="radio"]) { display: flex; align-items: center; min-height: 44px; }
      html.ui2 .ck2-post .upload-menu a[href] { display: inline-flex; align-items: center; min-height: 44px; padding-block: 0; }
      html.ui2 .ck2-post .upload-menu button { min-height: 44px; }
    }
  }
  /* ==== /Batch 3 round 3.2 ===================================================================== */

  /* ==== Batch 3 round 3.3: Edit sheet (phone) / studio inspector (desktop) (ui2 ONLY) ============
     app.js block "Batch 3 round 3.3" tags each clip control data-ck2sec (captions / frame / trim /
     hook / audio / more) and the card data-ck2ed (the section on screen). While a card is open
     (.ck2-open phone, .ck2-studio desktop) only that section's controls show, under a row of
     section buttons (.ck2-ed-tabs), above a sticky Apply bar (.ck2-ed-apply) that presses the
     card's own Process video button (hidden here, never removed). On a phone the viewer's Edit
     makes the card a full-screen sheet (.ck2-edsheet): player on top, controls scrolling under it.
     Nothing moves in the DOM. Every rule html.ui2-scoped, tokens only (no new colour). */
  @layer base {
    html.ui2 #clipsList > .clip-item:is(.ck2-open, .ck2-studio)[data-ck2ed="captions"] [data-ck2sec]:not([data-ck2sec~="captions"]),
    html.ui2 #clipsList > .clip-item:is(.ck2-open, .ck2-studio)[data-ck2ed="frame"] [data-ck2sec]:not([data-ck2sec~="frame"]),
    html.ui2 #clipsList > .clip-item:is(.ck2-open, .ck2-studio)[data-ck2ed="trim"] [data-ck2sec]:not([data-ck2sec~="trim"]),
    html.ui2 #clipsList > .clip-item:is(.ck2-open, .ck2-studio)[data-ck2ed="hook"] [data-ck2sec]:not([data-ck2sec~="hook"]),
    html.ui2 #clipsList > .clip-item:is(.ck2-open, .ck2-studio)[data-ck2ed="audio"] [data-ck2sec]:not([data-ck2sec~="audio"]),
    html.ui2 #clipsList > .clip-item:is(.ck2-open, .ck2-studio)[data-ck2ed="more"] [data-ck2sec]:not([data-ck2sec~="more"]),
    html.ui2 #clipsList > .clip-item:is(.ck2-open, .ck2-studio) > .clip-controls > .clip-process,
    html.ui2 #clipsList > .clip-item:not(.ck2-open):not(.ck2-studio) :is(.ck2-ed-tabs, .ck2-ed-apply) { display: none !important; }
    /* The sheet is edge to edge (the legacy card padding / radius are !important in components). */
    html.ui2 #clipsList > .clip-item.ck2-edsheet { padding: 0 !important; border-radius: 0 !important; border: 0 !important; margin: 0 !important; }
  }
  @layer mobile {
    /* Section buttons: one scrollable row, the chosen one filled. */
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-tabs {
      display: flex; flex: 1 0 100%; gap: var(--s2); width: 100%; margin: 0 0 var(--s3); padding: var(--s2) 0;
      overflow-x: auto; scrollbar-width: none; background: var(--panel); border-bottom: 1px solid var(--border-2);
    }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-tabs::-webkit-scrollbar { display: none; }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-tab {
      flex: none; min-height: 36px; margin: 0; padding: 0 var(--s3); border: 1px solid var(--border-2); border-radius: var(--r-pill);
      background: var(--panel-2); box-shadow: none; transform: none; color: var(--muted); font: inherit;
      font-size: var(--type-sm); font-weight: var(--w-semi); white-space: nowrap; cursor: pointer;
    }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-tab:hover { color: var(--text); background: var(--accent-soft); transform: none; }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-tab[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    @media (pointer: coarse) { html.ui2 body:not(#ck2) .clip-item .ck2-ed-tab { min-height: 44px; } }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-trimwrap { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
    /* The Apply bar: sticky at the bottom of the controls while they scroll. */
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-apply {
      position: sticky; bottom: 0; z-index: 2; display: flex; flex: 1 0 100%; align-items: center; gap: var(--s3); width: 100%;
      margin: var(--s4) 0 0; padding: var(--s3) 0 calc(var(--s3) + env(safe-area-inset-bottom));
      background: var(--panel); border-top: 1px solid var(--border-2);
    }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-dirty { flex: 1 1 auto; min-width: 0; color: var(--muted); font-size: var(--type-sm); }
    html.ui2 body:not(#ck2) .clip-item.ck2-ed-isdirty .ck2-ed-dirty { color: var(--text); font-weight: var(--w-semi); }
    html.ui2 body:not(#ck2) .clip-item .ck2-ed-applybtn { flex: none; min-height: 44px; margin: 0 0 0 auto; }
    /* Desktop studio: the section row sticks under the studio bar. */
    @media (min-width: 601px) {
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-studio .ck2-ed-tabs { flex-wrap: wrap; overflow: visible; }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-studio .ck2-ed-tabs { position: sticky; top: calc(var(--ck2-st-top, 0px) + 61px); z-index: 2; }
    }
    @media (max-width: 600px) {
      /* An unfolded card (Post) on the list: the Apply bar clears the bottom nav. */
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-open:not(.ck2-edsheet) .ck2-ed-apply { bottom: calc(56px + env(safe-area-inset-bottom)); padding-bottom: var(--s3); }
      /* The Edit sheet. */
      html.ui2 body.ck2-edsheet-open { overflow: hidden; }
      /* Its colour pickers (Karaoke / Keyword colour) are 36px wide on the legacy card: 44 here. */
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet input[type="color"] { min-width: 44px; min-height: 44px; }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet {
        position: fixed; inset: 0; z-index: var(--z-modal); display: grid; overflow: hidden;
        grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: auto auto auto minmax(0, 1fr);
        background: var(--panel); box-shadow: none; outline: none;
      }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > * { grid-column: 1 / -1; min-width: 0; margin: 0; }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > :is(.ck2-crow, .clip-reason, .clip-excerpt) { display: none !important; }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > .clip-meta {
        grid-row: 1; grid-column: 1; align-self: center; padding: var(--s3) var(--s4); overflow: hidden;
      }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > .clip-meta .clip-title {
        display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-weight: var(--w-semi);
      }
      html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item.ck2-open.ck2-edsheet > .ck2-crow-done {
        grid-row: 1; grid-column: 2; align-self: center; display: inline-flex; min-height: 44px; margin: var(--s2) var(--s3) var(--s2) 0;
      }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > [id^="exportArea"] {
        grid-row: 2; max-height: 44vh; overflow-y: auto; padding: 0 var(--s4) var(--s2); text-align: center;
        border-bottom: 1px solid var(--border-2);
      }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > [id^="exportArea"] video.preview {
        display: block; width: auto; max-width: 100%; height: auto; max-height: 34vh; margin: 0 auto var(--s2); object-fit: contain;
      }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > [id^="exportArea"] > :is(br, .dl-button:not([id^="capcut-btn"]), [id^="upload-menu"]) { display: none !important; }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > .v-holder { grid-row: 3; display: none; padding: var(--s2) var(--s4) 0; max-height: 30vh; overflow-y: auto; }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet[data-ck2ed="more"] > .v-holder { display: block; }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet > .clip-controls {
        grid-row: 4; overflow-y: auto; overscroll-behavior: contain; padding: 0 var(--s4); align-content: start;
      }
      html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet .ck2-ed-tabs { position: sticky; top: 0; z-index: 2; }
    }
  }
  @layer mobile {
  @media (max-width: 600px) and (prefers-reduced-motion: no-preference) {
    html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet { transform: translateY(16px); opacity: 0; transition: transform var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease); }
    html.ui2 body:not(#ck2) #clipsList > .clip-item.ck2-edsheet.is-in { transform: none; opacity: 1; }
  }
  }
  /* -- Batch 4 round 4.1: caption template gallery (app.js "Batch 4 round 4.1", ui2 ONLY) --------
     A scrollable row of style chips at the top of the Captions section (and of the Create flow's
     caption defaults). Chrome is tokens only; the preview's text/box colours are the caption's
     own colours (video content), passed in as --cap-ink / --cap-on / --cap-box. */
  @layer base {
    /* The clip card's (and Create's) generic button rules are pill-shaped and padded in later
       layers; a chip is a small square tile. !important in the earliest layer wins (as 3.3 does). */
    html.ui2 .ck2-cap .ck2-cap-chip { border-radius: var(--r-ctl) !important; padding: 4px !important; width: 96px !important; min-width: 96px !important; }
  }
  @layer components {
    html.ui2 .ck2-cap { display: block; width: 100%; min-width: 0; margin: 0 0 var(--s1, 8px); }
    html.ui2 .ck2-cap-head { font-size: var(--fs-sm); font-weight: 600; color: var(--muted); margin: 0 0 6px; }
    html.ui2 .ck2-cap-chips {
      display: flex; gap: 8px; overflow-x: auto; overscroll-behavior-x: contain;
      scroll-snap-type: x proximity; padding: 2px 2px 8px; max-width: 100%; scrollbar-width: thin;
    }
    html.ui2 .ck2-cap-chip {
      flex: none; scroll-snap-align: start; display: flex; flex-direction: column; align-items: stretch;
      gap: 4px; width: 96px; min-height: 44px; padding: 4px; margin: 0;
      background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
      border-radius: var(--r-ctl); cursor: pointer; font: inherit;
    }
    html.ui2 .ck2-cap-chip:hover { border-color: var(--border-2); }
    html.ui2 .ck2-cap-chip[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
    html.ui2 .ck2-cap-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    html.ui2 .ck2-cap-name { font-size: var(--fs-xs); line-height: 1.2; text-align: center; white-space: nowrap; }
    html.ui2 .ck2-cap-prev {
      display: flex; align-items: center; justify-content: center; gap: 3px;
      height: 44px; overflow: hidden; border-radius: var(--r-sm); background: var(--deep);
      font-size: 14px; font-weight: 700; line-height: 1.1; white-space: nowrap; color: var(--cap-ink);
    }
    html.ui2 .ck2-cap-prev[data-caps] { text-transform: uppercase; }
    html.ui2 .ck2-cap-w { display: inline-block; -webkit-text-stroke: 1.5px var(--cap-edge); paint-order: stroke fill; }
    html.ui2 .ck2-cap-prev[data-st="thin"] .ck2-cap-w { -webkit-text-stroke-width: 0.6px; }
    html.ui2 .ck2-cap-prev[data-st="thick"] .ck2-cap-w { -webkit-text-stroke-width: 3.5px; }
    html.ui2 .ck2-cap-prev[data-st="box"] { gap: 0; }
    html.ui2 .ck2-cap-prev[data-st="box"] .ck2-cap-w { -webkit-text-stroke: 0; background: var(--cap-box); padding: 1px 3px; }
    html.ui2 .ck2-cap-w.is-on { color: var(--cap-on); }
    html.ui2 .ck2-cap-tweaks { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; font-size: var(--fs-sm); }
    html.ui2 .ck2-cap-tweaks label { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; }
    html.ui2 .ck2-cap-tweaks select { min-height: 36px; }
  }
  @layer mobile {
  @media (prefers-reduced-motion: no-preference) {
    html.ui2 .ck2-cap-prev[data-pop] .ck2-cap-w.is-on { animation: ck2-cap-pop 1.6s var(--ease) infinite; }
    @keyframes ck2-cap-pop { 0%, 55%, 100% { transform: scale(1); } 12% { transform: scale(1.25); } 25% { transform: scale(1); } }
  }
  }
  /* -- Batch 4 round 4.2/4.15: Layout chips at the top of Frame (and Create's defaults) ---------- */
  @layer base {
    html.ui2 .ck2-lay .ck2-lay-chip { border-radius: var(--r-ctl) !important; padding: 8px 10px !important; width: auto !important; min-width: 0 !important; }
    /* 4.10: the own-video tiles are buttons too; the global pill shape would make them ovals. */
    html.ui2 .ck2-own .ck2-own-opt { border-radius: var(--r-ctl) !important; padding: 6px !important; width: auto !important; min-width: 0 !important; }
  }
  @layer components {
    html.ui2 .ck2-lay { display: block; width: 100%; min-width: 0; margin: 0 0 var(--s1, 8px); }
    html.ui2 .ck2-lay-chips { display: grid; grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 8px; }
    html.ui2 .ck2-lay-chip {
      display: flex; flex-direction: column; align-items: flex-start; gap: 2px; min-height: 44px; margin: 0;
      background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
      border-radius: var(--r-ctl); cursor: pointer; font: inherit; text-align: left;
    }
    html.ui2 .ck2-lay-chip:hover:not(:disabled) { border-color: var(--border-2); }
    html.ui2 .ck2-lay-chip[aria-checked="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
    html.ui2 .ck2-lay-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    html.ui2 .ck2-lay-chip:disabled { opacity: .45; cursor: not-allowed; }
    html.ui2 .ck2-lay-name { font-size: var(--fs-sm); font-weight: 600; }
    html.ui2 .ck2-lay-hint { font-size: var(--fs-xs); color: var(--muted); line-height: 1.3; }
    html.ui2 .ck2-lay-note { margin: 6px 0 0; font-size: var(--fs-xs); color: var(--muted); min-height: 0; }
    html.ui2 .ck2-lay-note:empty { display: none; }
    /* Batch 4 round 4.10: picker of the user's own videos (Gameplay split + story background). */
    html.ui2 .ck2-own { display: block; width: 100%; min-width: 0; margin: 8px 0 0; }
    html.ui2 .ck2-own[hidden] { display: none; }
    html.ui2 .ck2-scrft { display: block; width: 100%; min-width: 0; margin: 8px 0 0; }
    html.ui2 .ck2-scrft[hidden] { display: none; }
    html.ui2 .ck2-own-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px;
      max-height: 320px; overflow-y: auto; overscroll-behavior: contain; }
    /* FU: footage is capped at 10 items, so its grid grows with the page (no nested scroll). */
    html.ui2 #storyFootagePick .ck2-own-grid { max-height: none; overflow: visible; }
    html.ui2 .ck2-own-opt {
      display: flex; flex-direction: column; align-items: stretch; gap: 4px; min-height: 44px; margin: 0; padding: 6px;
      background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
      border-radius: var(--r-ctl); cursor: pointer; font: inherit; text-align: left; min-width: 0;
    }
    html.ui2 .ck2-own-opt:hover { border-color: var(--border-2); }
    html.ui2 .ck2-own-opt[aria-checked="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
    html.ui2 .ck2-own-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    html.ui2 .ck2-own-thumb { display: block; aspect-ratio: 16 / 9; background: var(--panel); border-radius: var(--r-ctl); overflow: hidden; }
    html.ui2 .ck2-own-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
    html.ui2 .ck2-own-thumb img[hidden] { display: none; }
    html.ui2 .ck2-own-name { font-size: var(--fs-xs); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    html.ui2 .ck2-own-dur { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
    html.ui2 .ck2-own-msg { margin: 6px 0 0; font-size: var(--fs-xs); color: var(--muted); }
  }
  /* -- Batch 4 follow-up F2: "Your footage" (story-only background uploads, Story screen) ---- */
  @layer base {
    html.ui2 .ck2-ft .ck2-ft-btn { border-radius: var(--r-ctl) !important; width: auto !important; min-width: 44px !important; }
    html.ui2 .ck2-ft .ck2-own-opt { border-radius: var(--r-ctl) !important; padding: 6px !important; width: auto !important; min-width: 0 !important; }
    html.ui2 .ck2-ft .ck2-ft-row input[type="range"] { width: 100% !important; max-width: none !important; }
  }
  @layer components {
    html.ui2 .ck2-ft { display: block; width: 100%; min-width: 0; margin: 8px 0 0; }
    html.ui2 .ck2-ft[hidden], html.ui2 .ck2-ft [hidden] { display: none; }
    html.ui2 .ck2-ft-up { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 8px; }
    html.ui2 .ck2-ft-up .upload-opt { display: flex; align-items: center; gap: 8px; min-height: 44px; margin: 0; font-size: var(--fs-xs); }
    html.ui2 .ck2-ft-btn { min-height: 44px; }
    html.ui2 .ck2-ft-item { position: relative; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
    html.ui2 .ck2-ft-item .ck2-own-opt { width: 100%; }
    html.ui2 .ck2-ft-meta { font-size: var(--fs-xs); color: var(--muted); line-height: 1.3; overflow-wrap: anywhere; }
    html.ui2 .ck2-ft-meta.is-bad { color: var(--danger); }
    html.ui2 .ck2-ft-del { min-height: 44px; font-size: var(--fs-xs); }
    html.ui2 .ck2-ft-del.is-armed { color: var(--danger); border-color: var(--danger); }
    html.ui2 .ck2-ft-range { margin: 10px 0 0; padding: 10px; border: 1px solid var(--border); border-radius: var(--r-ctl);
      background: var(--panel-2); min-width: 0; }
    html.ui2 .ck2-ft-prev { display: block; width: 100%; max-height: 200px; aspect-ratio: 16 / 9; object-fit: contain;
      background: var(--panel); border-radius: var(--r-ctl); }
    html.ui2 .ck2-ft-row { display: grid; grid-template-columns: 3.2em 1fr 3.4em; align-items: center; gap: 8px; min-height: 44px; }
    html.ui2 .ck2-ft-row label { font-size: var(--fs-xs); font-weight: 600; }
    html.ui2 .ck2-ft-row input[type="range"] { width: 100%; min-height: 44px; margin: 0; accent-color: var(--accent); }
    html.ui2 .ck2-ft-row output { font-size: var(--fs-xs); font-variant-numeric: tabular-nums; text-align: right; }
    html.ui2 .ck2-ft-note { margin: 6px 0 0; font-size: var(--fs-xs); color: var(--muted); }
    html.ui2 .ck2-ft-note.is-loop { color: var(--text); }
  }
  /* -- Batch 4 round 4.16: story formats (Format chips + structured script fields) ------------- */
  @layer base {
    html.ui2 .ck2-fmt .ck2-fmt-btn { border-radius: var(--r-ctl) !important; width: auto !important; min-width: 0 !important; }
  }
  @layer components {
    html.ui2 .ck2-fmt { display: block; width: 100%; min-width: 0; margin: 0 0 var(--s2, 13px); }
    html.ui2 .ck2-fmt[hidden], html.ui2 .ck2-fmt-panel[hidden] { display: none; }
    html.ui2 .ck2-fmt-panel { display: grid; gap: 8px; margin: 0 0 var(--s1, 8px); min-width: 0; }
    html.ui2 .ck2-fmt-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; min-width: 0; }
    html.ui2 .ck2-fmt label { display: grid; gap: 4px; font-size: var(--fs-xs); color: var(--muted); min-width: 0; }
    html.ui2 .ck2-fmt input, html.ui2 .ck2-fmt select { min-height: 44px; width: 100%; min-width: 0; box-sizing: border-box; }
    html.ui2 .ck2-fmt-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 6px; align-items: center; min-width: 0; }
    html.ui2 .ck2-fmt-q { display: grid; gap: 6px; padding: 8px; border: 1px solid var(--border); border-radius: var(--r-ctl); min-width: 0; }
    html.ui2 .ck2-fmt-btn { min-height: 44px; min-width: 44px; padding: 8px 12px; margin: 0; background: var(--panel-2); color: var(--text); border: 1px solid var(--border); cursor: pointer; font: inherit; }
    html.ui2 .ck2-fmt-btn:hover { border-color: var(--border-2); }
    html.ui2 .ck2-fmt-btn:focus-visible, html.ui2 .ck2-fmt input:focus-visible, html.ui2 .ck2-fmt select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    html.ui2 .ck2-fmt-hint { margin: 0; font-size: var(--fs-xs); color: var(--muted); }
    /* Post card preview: the card's own (burned-in) colours, set by ck2CardPreview from story_formats. */
    html.ui2 #storyScript[hidden], html.ui2 .script-kicker[hidden] { display: none; }
    html.ui2 .ck2-postprev { display: grid; gap: 6px; min-width: 0; }
    html.ui2 .ck2-pc { border-radius: 16px; padding: 18px; max-width: 380px;
      width: 100%; box-sizing: border-box; font-family: Roboto, "Liberation Sans", Arial, sans-serif; }
    html.ui2 .ck2-pc-who { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; font-weight: 700; min-width: 0; }
    html.ui2 .ck2-pc-av { flex: 0 0 36px; height: 36px; border-radius: 50%; 
      display: grid; place-items: center; font-weight: 700; }
    html.ui2 .ck2-pc-user { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    html.ui2 .ck2-pc-title { font-weight: 700; font-size: 1.2em; line-height: 1.25; overflow-wrap: anywhere;
      display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 6; overflow: hidden; }
    html.ui2 .ck2-pc-title.ck2-pc-empty { font-weight: 400; }
    html.ui2 .ck2-pc-body { margin-top: 8px; line-height: 1.3; overflow-wrap: anywhere;
      display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; }
    html.ui2 .ck2-pc-body:empty { display: none; }
    html.ui2 .ck2-pc-meta { display: flex; align-items: center; gap: 8px; margin-top: 14px; font-weight: 700; }
    html.ui2 .ck2-pc-up { width: 0; height: 0; border-left: 9px solid transparent; border-right: 9px solid transparent;
      border-bottom: 14px solid; }
    html.ui2 .ck2-pc-com { width: 18px; height: 13px; border: 2px solid; border-radius: 4px; margin-left: 14px; box-sizing: border-box; }
  }

  /* ---- Batch 4 round 4.9: Story screen (ui2) ---- */
  @layer components {
  html.ui2 #storyVoicePicker .ck2-voice { display: flex; align-items: stretch; gap: var(--s1); flex: 1 1 180px; min-width: 0; }
  html.ui2 #storyVoicePicker .ck2-voice > .category-option { flex: 1 1 auto; min-width: 0; }
  html.ui2 .ck2-voice-play {
    flex: 0 0 44px; width: 44px; min-height: 44px; padding: 0 !important; border-radius: var(--r-ctl) !important;
    border: 1px solid var(--border); background: var(--panel-2); color: var(--accent-text);
    font-size: var(--fs-sm); line-height: 1; cursor: pointer;
  }
  html.ui2 .ck2-voice-play:hover { border-color: var(--border-2); }
  html.ui2 .ck2-voice-play[aria-pressed="true"] { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
  html.ui2 .ck2-voice-play.is-loading { opacity: .7; }
  html.ui2 .ck2-voice-play:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  html.ui2 .category-option.ck2-bg-opt { display: flex; flex-direction: column; gap: var(--s1); border-radius: var(--r-card) !important; }
  html.ui2 .ck2-bg-thumb {
    display: block; width: 100%; height: auto; max-height: 96px; aspect-ratio: 16 / 9; object-fit: cover;
    border-radius: var(--r-sm); background: var(--panel-2);
  }
  html.ui2 .ck2-story-make {
    position: sticky; bottom: 0; z-index: 3; margin-top: var(--s4);
    padding: var(--s2) 0 var(--s3); background: var(--bg); border-top: 1px solid var(--border);
  }
  html.ui2 .ck2-story-make #storyMakeBtn { margin-top: 0; }
  html.ui2 .ck2-story-charge {
    margin: 0 0 var(--s2); min-height: 1.4em; text-align: center; color: var(--muted);
    font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
  }
  @media (max-width: 600px) {
    html.ui2 .ck2-story-make { bottom: calc(56px + env(safe-area-inset-bottom)); }
  }
  }
  /* ---- /Batch 4 round 4.9 ---- */
  /* ---- Batch 4 round 4.13/4.17: brand templates, .srt import, Account brand words (ui2) ---- */
  @layer base {
    /* Chips are text tiles, not the card's pill buttons (same reason as 4.1's chips). */
    html.ui2 .ck2-bt .ck2-bt-chip { border-radius: var(--r-ctl) !important; padding: 0 14px !important; min-width: 0 !important; width: auto !important; }
    html.ui2 .ck2-bt .ck2-bt-chip.ck2-bt-del { min-width: 44px !important; }   /* FU: 44px target (beats the min-width:0 above) */
  }
  @layer components {
    html.ui2 .ck2-bt, html.ui2 .ck2-srt { display: block; width: 100%; min-width: 0; margin: 0 0 var(--s1, 8px); }
    /* 4.11: non-English source -> translated-subtitles suggestion (tokens only). */
    html.ui2 .ck2-langhint { display: block; width: 100%; min-width: 0; margin: 0 0 var(--s1, 8px); padding: 8px 12px;
      border: 1px solid var(--border); border-radius: var(--r-ctl); background: var(--panel-2); }
    html.ui2 .ck2-langhint p { margin: 0 0 6px; overflow-wrap: anywhere; }
    html.ui2 .ck2-langhint .ck-btn { min-height: 44px; }
    html.ui2 .ck2-langhint .ck-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    html.ui2 .ck2-langhint .ck2-redo { margin-top: 8px; }
    html.ui2 .ck2-langhint .ck2-redo-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 0 0 6px; }
    html.ui2 .ck2-langhint .ck2-redo-row select { min-width: 0; max-width: 100%; min-height: 44px; }
    html.ui2 .ck2-bt-chips { display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 6px; scrollbar-width: thin; }
    html.ui2 .ck2-bt-chip {
      flex: 0 0 auto; min-height: 44px; max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      background: var(--panel-2); color: var(--text); border: 1px solid var(--border); font: inherit; font-size: var(--fs-sm); cursor: pointer;
    }
    html.ui2 .ck2-bt-chip:hover { border-color: var(--border-2); }
    html.ui2 .ck2-bt-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    html.ui2 .ck2-bt-new { border-style: dashed; color: var(--muted); }
    /* Follow-up F1: a template chip + its delete button travel together. */
    html.ui2 .ck2-bt-item { display: inline-flex; flex: 0 0 auto; gap: 2px; }
    html.ui2 .ck2-bt .ck2-bt-del { min-width: 44px !important; flex: 0 0 auto; box-sizing: border-box; padding: 0 10px !important; color: var(--muted); }
    html.ui2 .ck2-bt .ck2-bt-del[data-armed] { color: var(--text); border-color: var(--border-2); }
    html.ui2 .ck2-bt-star { color: var(--muted); font-size: var(--fs-xs); }
    html.ui2 .ck2-bt-form { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 4px 0; font-size: var(--fs-sm); }
    html.ui2 .ck2-bt-form[hidden] { display: none; }
    html.ui2 .ck2-bt-name { display: inline-flex; align-items: center; gap: 8px; min-width: 0; flex: 1 1 200px; }
    html.ui2 .ck2-bt-name input { flex: 1 1 auto; min-width: 0; min-height: 44px; }
    html.ui2 .ck2-bt-def { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; }
    html.ui2 .ck2-bt-acts, html.ui2 .ck2-srt-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
    html.ui2 .ck2-bt-acts .ck-btn, html.ui2 .ck2-srt-acts .ck-btn { min-height: 44px; }
    html.ui2 .ck2-srt-acts .ck-btn[hidden] { display: none; }
    html.ui2 .ck2-srt-clock { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; font-size: var(--fs-sm); }
    html.ui2 .ck2-srt-clock select { min-height: 44px; }
    html.ui2 .ck2-bt-status:empty, html.ui2 .ck2-srt .hint:empty { display: none; }
    html.ui2 .ck2-bw + .ck2-bw { margin-top: var(--s3, 16px); }
    html.ui2 .ck2-bw-h { font-size: var(--fs-sm); font-weight: 600; margin: 0 0 4px; }
    html.ui2 .ck2-bt-acc { list-style: none; margin: 8px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
    html.ui2 .ck2-bt-acc { flex-direction: column; }
    html.ui2 .ck2-bt-accrow .ck-btn { min-height: 44px; }
    html.ui2 .ck2-bt-accrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
    html.ui2 .ck2-bt-accname { flex: 1 1 100px; min-width: 0; overflow-wrap: anywhere; font-weight: 600; }
    html.ui2 .ck2-bt-accrow .ck-btn[aria-pressed="true"] { border-color: var(--accent); }
    html.ui2 .ck2-jumpcaps { min-height: 44px; margin: 0 0 var(--s2, 12px); }
  }
  /* ---- /Batch 4 round 4.13/4.17 ---- */
  /* ---- Batch 4 round 4.8/4.14: Automation status + steps, two-step compilation builder (ui2) ---- */
  @layer base {
    html.ui2 #tab-automation > .card.ck2-au-off { display: none !important; }
    html.ui2 #tab-automation.automation-locked > .ck2-au-steps,
    html.ui2 #tab-automation.automation-locked > .ck2-au-foot { display: none !important; }
    html.ui2 .ck2-au-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); }
    html.ui2 .ck2-au-head h2 { margin: 0; }
    html.ui2 .ck2-au-pill { padding: 4px 12px; border-radius: var(--r-pill); border: 1px solid var(--border-2); color: var(--muted); font-weight: var(--w-semi); font-size: var(--fs-sm); }
    html.ui2 .ck2-au-pill.on { border-color: var(--good); color: var(--good); background: var(--good-soft); }
    html.ui2 .ck2-au-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--s3); margin: var(--s4) 0; }
    html.ui2 .ck2-au-facts > div { min-width: 0; }
    html.ui2 .ck2-au-facts dt { color: var(--muted); font-size: var(--fs-sm); }
    html.ui2 .ck2-au-facts dd { margin: 2px 0 0; overflow-wrap: anywhere; font-weight: var(--w-med); }
    html.ui2 .ck2-au-facts dd::first-letter { text-transform: uppercase; }
    /* The status card already carries the tab's title; the intro card keeps its text only. */
    html.ui2 #tab-automation > .card:has(#autoLive) > h2 { display: none; }
    html.ui2 .ck2-au-go, html.ui2 .ck2-au-foot button, html.ui2 .ck2-cb-foot button { min-height: 44px; }
    html.ui2 .ck2-au-steps ol, html.ui2 .ck2-cb-steps ol { list-style: none; display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--s2); margin: 0 0 var(--s4); padding: 0; }
    html.ui2 .ck2-au-steps li, html.ui2 .ck2-cb-steps li { min-width: 0; }
    html.ui2 .ck2-au-step { width: 100% !important; min-height: 44px; display: flex; align-items: center; gap: var(--s2); padding: 0 14px !important;
      border-radius: var(--r-ctl) !important; border: 1px solid var(--border-2); background: var(--panel); color: var(--text); cursor: pointer; font: inherit; }
    html.ui2 .ck2-au-step[aria-current="step"] { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-text); font-weight: var(--w-semi); }
    html.ui2 .ck2-au-step:focus-visible, html.ui2 .ck2-cb-mv:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    html.ui2 .ck2-au-num { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: var(--r-full); border: 1px solid currentColor; font-size: var(--fs-xs); flex: none; }
    html.ui2 .ck2-au-foot { display: flex; justify-content: space-between; gap: var(--s3); margin: var(--s4) 0; }
    html.ui2 .ck2-au-foot #ck2AuNext { margin-left: auto; }
    html.ui2 .ck2-au-modes { margin: var(--s3) 0 var(--s4); padding-left: 1.2em; display: grid; gap: var(--s2); }
    /* Compilations: step 1 = picker + order, step 2 = look/title/Make. */
    html.ui2 #compBody[data-ck2cb="1"] #compSettings,
    html.ui2 #compBody[data-ck2cb="2"] .comp-panes,
    html.ui2 #compBody[data-ck2cb="2"] .ck2-cb-foot { display: none !important; }
    html.ui2 .ck2-cb-foot { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s4); }
    html.ui2 .ck2-cb-foot .hint { margin: 0; }
    /* The upload's clips are a grid of tick tiles (the older rules are unlayered, hence !important). */
    html.ui2 .comp-job-clips:not(.hidden) { display: grid !important; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: var(--s2) !important; }
    html.ui2 .comp-job-clips > .hint, html.ui2 .comp-job-clips > .error { grid-column: 1 / -1; }
    html.ui2 .comp-job-clips .comp-clip { gap: 0 !important; min-width: 0; align-content: start;
      border: 1px solid var(--border-2); border-radius: var(--r-ctl); background: var(--panel); }
    html.ui2 .comp-job-clips .comp-clip-preview { grid-column: 1 / -1; padding: 0 var(--s2); }
    html.ui2 .comp-job-clips .comp-clip:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
    html.ui2 .comp-job-clips .comp-clip-pick { display: grid; place-items: center; min-width: 44px; min-height: 44px; cursor: pointer; }
    html.ui2 .comp-job-clips .comp-clip-title { flex: 1 1 0; min-width: 0; min-height: 44px; text-align: left; display: flex; flex-direction: column; gap: 2px;
      padding: 8px 8px 8px 0 !important; border-radius: var(--r-ctl) !important; width: auto !important; overflow-wrap: anywhere; }
    /* A selection row: handle, number, title, length on top; ▲/▼ and Remove under them. */
    html.ui2 #compSelection .comp-sel-row { grid-template-columns: auto auto minmax(0, 1fr) auto !important; }
    html.ui2 #compSelection .comp-sel-row > .hint { grid-column: auto !important; }
    html.ui2 #compSelection .comp-sel-row > .ck2-cb-move { grid-column: 1 / 4; }
    html.ui2 #compSelection .comp-sel-row > button { grid-column: 4 / -1 !important; justify-self: end; }
    html.ui2 .ck2-cb-move { display: inline-flex; gap: 4px; }
    html.ui2 .ck2-cb-mv { min-width: 44px !important; min-height: 44px; width: 44px !important; padding: 0 !important; border-radius: var(--r-ctl) !important;
      border: 1px solid var(--border-2); background: var(--panel); color: var(--text); cursor: pointer; }
    html.ui2 .ck2-cb-mv:disabled { opacity: .4; cursor: default; }
  }
  /* ---- /Batch 4 round 4.8/4.14 ---- */
  /* ==== /Batch 3 round 3.3 ===================================================================== */

  /* ==== Batch 3 round 3.1b: desktop clip grid + two-pane Clip studio (ui2 ONLY) ===============
     Wider than a phone (> 600px) the 3.1a rows (.ck2-crow) become tiles in a grid and every card
     folds down to its tile. app.js's ck2StudioOpen marks one card .ck2-studio (and #clipsList /
     #jobSection .ck2-studio-on): only that card shows, unfolded, with the .ck2-sbar bar above it.
     From 1180px the card is two panes: its export area (the player + Download / Post buttons) on
     the left, sticky; its controls (the inspector) on the right. Nothing moves in the DOM.
     Every rule html.ui2-scoped, tokens only (no new colour). */
  @layer base {
  @media (min-width: 601px) {
    html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item:not(.ck2-studio) { padding: 0 !important; }
  }
  }
  @layer mobile {
  @media (min-width: 601px) {
    html.ui2 body:not(#ck2) #clipsList.ck2-compact:not(.ck2-studio-on) {
      display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: var(--s4); align-items: start;
    }
    html.ui2 #clipsList.ck2-compact > .clip-item:not(.ck2-studio) > :not(.ck2-crow) { display: none !important; }
    html.ui2 #clipsList.ck2-studio-on > .clip-item:not(.ck2-studio) { display: none !important; }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item:not(.ck2-studio) { overflow: hidden; }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item:not(.ck2-studio) > .ck2-crow {
      display: flex; flex-direction: column; align-items: stretch; gap: var(--s2); width: 100%; min-height: 44px;
      margin: 0; padding: 0 0 var(--s3); border: 0; border-radius: inherit; background: none; box-shadow: none;
      color: var(--text); font: inherit; text-align: left; cursor: pointer;
    }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact .ck2-crow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item:not(.ck2-studio) .ck2-crow-thumb {
      position: relative; display: block; width: 100%; overflow: hidden; margin: 0;
      background: var(--panel-2); border-bottom: 1px solid var(--border-2); border-radius: 0;
    }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact .ck2-crow-body { padding: 0 var(--s3); }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact .ck2-crow-title { -webkit-line-clamp: 2; }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact .ck2-crow.is-playing .ck2-crow-play { visibility: hidden; }

    /* The studio */
    html.ui2 #jobSection.ck2-studio-on > :not(#clipsList):not(#ck2Sbar) { display: none !important; }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on { display: block; }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio > .ck2-crow,
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio > .ck2-crow-done { display: none; }
    html.ui2 body:not(#ck2) .ck2-sbar {
      position: sticky; top: var(--ck2-st-top, 0px); z-index: 3; display: flex; align-items: center; gap: var(--s2);
      margin: 0 0 var(--s4); padding: var(--s2) 0; background: var(--panel); border-bottom: 1px solid var(--border-2);
    }
    html.ui2 body:not(#ck2) .ck2-sbar[hidden] { display: none; }
    html.ui2 body:not(#ck2) .ck2-sbar .ck2-sbar-back { flex: none; min-height: 44px; }
    html.ui2 body:not(#ck2) .ck2-sbar .ck2-sbar-back svg { margin-left: -4px; }
    html.ui2 body:not(#ck2) .ck2-sbar-title {
      flex: 1 1 auto; min-width: 0; margin: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
      font-size: var(--type-body); line-height: var(--type-body-lh); font-weight: var(--w-semi); text-align: left;
    }
    html.ui2 body:not(#ck2) .ck2-sbar-count { flex: none; color: var(--muted); font-size: var(--type-sm); font-variant-numeric: tabular-nums; }
    html.ui2 body:not(#ck2) .ck2-sbar .ck-iconbtn { flex: none; min-width: 44px; min-height: 44px; }
    html.ui2 body:not(#ck2) .ck2-sbar .ck-iconbtn:disabled { opacity: .4; cursor: default; }
    /* One column (601-1179px): the player first, the controls under it. */
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio { display: flex; flex-direction: column; }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio > [id^="exportArea"] { order: -1; margin-bottom: var(--s4); }
  }
  @media (min-width: 1180px) {
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio {
      display: grid; grid-template-columns: minmax(0, 380px) minmax(0, 1fr); column-gap: var(--s5); align-items: start;
    }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio[data-ck2ar="11"] { grid-template-columns: minmax(0, 440px) minmax(0, 1fr); }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio[data-ck2ar="169"] { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio > * { grid-column: 2; min-width: 0; }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio > [id^="exportArea"] {
      grid-column: 1; grid-row: 1 / span 6; align-self: start; position: sticky; top: calc(var(--ck2-st-top, 0px) + 76px); order: 0; margin-bottom: 0;
    }
    html.ui2 body:not(#ck2) #clipsList.ck2-studio-on > .clip-item.ck2-studio > [id^="exportArea"] video.preview {
      width: 100%; max-width: none; height: auto; max-height: calc(100vh - var(--ck2-st-top, 0px) - 330px); object-fit: contain;
    }
  }
  }
  /* ==== /Batch 3 round 3.1b ==================================================================== */

  /* ==== Batch 3 round 3.1a: result-first clips on a phone (ui2 ONLY) ===========================
     app.js puts a .ck2-crow row (and a "Close editor" button) at the top of every .clip-item. On a
     phone (<= 600px) the card folds down to that row; .ck2-open unfolds it (Edit / Post). The
     viewer (.ck2-viewer) is a full-screen dialog. Wider than a phone the rows never show.
     Every rule html.ui2-scoped, tokens only (no new colour). */
  /* The legacy card padding is `!important` in the components layer; an important rule only beats it
     from an EARLIER layer, so the folded card's padding: 0 lives in base. */
  @layer base {
  @media (max-width: 600px) {
    html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item:not(.ck2-open) { padding: 0 !important; }
  }
  }
  @layer mobile {
  html.ui2 body:not(#ck2) #clipsList .ck2-crow, html.ui2 body:not(#ck2) #clipsList .ck2-crow-done { display: none; }
  @media (max-width: 600px) {
    html.ui2 #clipsList.ck2-compact > .clip-item:not(.ck2-open) > :not(.ck2-crow) { display: none !important; }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item.ck2-open > .ck2-crow { padding: 0 0 var(--s3); }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact > .clip-item.ck2-open > .ck2-crow-done { display: inline-flex; margin: 0 0 var(--s4); }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact .ck2-crow {
      display: flex; align-items: flex-start; gap: var(--s4); width: 100%; min-height: 44px; margin: 0;
      padding: var(--s3); border: 0; border-radius: inherit; background: none; box-shadow: none;
      color: var(--text); font: inherit; text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
    }
    html.ui2 body:not(#ck2) #clipsList.ck2-compact .ck2-crow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
    html.ui2 body:not(#ck2) .ck2-crow-thumb {
      position: relative; flex: none; display: block; overflow: hidden; width: 90px; max-height: 160px;
      border-radius: var(--r-card); background: var(--panel-2); border: 1px solid var(--border-2);
    }
    html.ui2 body:not(#ck2) .ck2-crow-thumb.is-11 { width: 120px; }
    html.ui2 body:not(#ck2) .ck2-crow-thumb.is-169 { width: 144px; }
  }
  /* The row's insides: the same at every width (3.1b's desktop tiles reuse them). */
    html.ui2 body:not(#ck2) .clip-item .ck2-crow-thumb video {
      display: block; width: 100%; height: 100%; max-width: none; margin: 0; border-radius: 0;
      object-fit: cover; background: var(--bg); pointer-events: none;
    }
    html.ui2 body:not(#ck2) .ck2-crow-play {
      position: absolute; right: var(--s1); bottom: var(--s1); display: inline-flex; padding: 4px;
      border-radius: var(--r-pill); background: var(--scrim); color: var(--text);
    }
    html.ui2 body:not(#ck2) .ck2-crow-play .ck-icon { width: 14px; height: 14px; }
    html.ui2 body:not(#ck2) .ck2-crow-quote {
      position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
      color: var(--muted); font-size: var(--type-h2); font-weight: var(--w-bold);
    }
    html.ui2 body:not(#ck2) .ck2-crow-body { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s1); min-width: 0; flex: 1; padding-top: 2px; }
    html.ui2 body:not(#ck2) .ck2-crow-score,
    html.ui2 body:not(#ck2) .ck2-v-score {
      display: inline-flex; align-items: center; gap: 2px; padding: 2px 8px; border-radius: var(--r-pill);
      background: var(--panel-2); border: 1px solid var(--border-2); color: var(--text);
      font-size: var(--type-cap); line-height: var(--type-cap-lh); font-weight: var(--w-semi); font-variant-numeric: tabular-nums;
    }
    html.ui2 body:not(#ck2) .ck2-crow-score .ck-icon,
    html.ui2 body:not(#ck2) .ck2-v-score .ck-icon { width: 12px; height: 12px; color: var(--orange); }
    /* Batch 5: "Most replayed" badge + the replay curve (YouTube links that have one). */
    html.ui2 body:not(#ck2) .ck2-crow-badges { display: inline-flex; flex-wrap: wrap; gap: var(--s1); }
    html.ui2 body:not(#ck2) .ck2-crow-replay {
      display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: var(--r-pill);
      background: var(--panel-2); border: 1px solid var(--border-2); color: var(--text);
      font-size: var(--type-cap); line-height: var(--type-cap-lh); font-weight: var(--w-semi); white-space: nowrap;
    }
    html.ui2 body:not(#ck2) .ck2-crow-replay .ck-icon { width: 12px; height: 12px; color: var(--orange); }
    /* Max Phase C: "Upscaling with AI", first of the badges while it runs. */
    html.ui2 body:not(#ck2) .ck2-crow-upscale {
      display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px; border-radius: var(--r-pill);
      background: var(--panel-2); border: 1px solid var(--orange); color: var(--text);
      font-size: var(--type-cap); line-height: var(--type-cap-lh); font-weight: var(--w-semi); white-space: nowrap;
    }
    html.ui2 body:not(#ck2) .ck2-crow-curve { display: block; width: 100%; max-width: 220px; height: 16px; }
    html.ui2 body:not(#ck2) .ck2-curve-line { fill: none; stroke: var(--muted); stroke-width: 1.2; vector-effect: non-scaling-stroke; }
    html.ui2 body:not(#ck2) .ck2-curve-clip { fill: var(--orange); opacity: .28; }
    html.ui2 body:not(#ck2) .ck2-crow-title {
      display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden;
      font-size: var(--type-body); line-height: var(--type-body-lh); font-weight: var(--w-semi); overflow-wrap: anywhere;
    }
    html.ui2 body:not(#ck2) .ck2-crow-meta { color: var(--muted); font-size: var(--type-sm); line-height: var(--type-sm-lh); font-variant-numeric: tabular-nums; }
    html.ui2 body:not(#ck2) .ck2-crow-state { color: var(--muted); font-size: var(--type-sm); line-height: var(--type-sm-lh); }
    html.ui2 body:not(#ck2) .ck2-crow-state.is-failed { color: var(--danger); }

  /* The viewer: full screen at every width (it only opens from a phone row). */
  html.ui2 body.ck2-viewer-open { overflow: hidden; }
  html.ui2 body:not(#ck2) .ck2-viewer {
    position: fixed; inset: 0; z-index: var(--z-modal); display: flex; flex-direction: column;
    background: var(--bg); color: var(--text); outline: none;
    padding: env(safe-area-inset-top) env(safe-area-inset-right) 0 env(safe-area-inset-left);
  }
  html.ui2 body:not(#ck2) .ck2-v-top { display: flex; align-items: center; justify-content: space-between; padding: var(--s1) var(--s2); flex: none; }
  html.ui2 body:not(#ck2) .ck2-v-count { padding: 0 var(--s3); color: var(--muted); font-size: var(--type-sm); font-variant-numeric: tabular-nums; }
  html.ui2 body:not(#ck2) .ck2-v-stage {
    position: relative; flex: 1 1 auto; min-height: 0; display: flex; align-items: center; justify-content: center;
    padding: 0 var(--s3); touch-action: pan-y;
  }
  html.ui2 body:not(#ck2) .ck2-v-video {
    display: block; max-width: 100%; max-height: 100%; width: auto; height: 100%; aspect-ratio: var(--ck2-v-ar, 9 / 16);
    margin: 0; border-radius: var(--r-card); background: var(--bg); object-fit: contain;
  }
  html.ui2 body:not(#ck2) .ck2-v-video[hidden] { display: none; }
  html.ui2 body:not(#ck2) .ck2-v-msg { margin: 0; max-width: 32ch; text-align: center; color: var(--muted); }
  html.ui2 body:not(#ck2) .ck2-v-nav {
    position: absolute; top: 50%; transform: translateY(-50%); border-radius: var(--r-pill);
    background: var(--scrim); color: var(--text); border: 1px solid var(--border-2);
  }
  html.ui2 body:not(#ck2) .ck2-v-prev { left: var(--s1); }
  html.ui2 body:not(#ck2) .ck2-v-next { right: var(--s1); }
  html.ui2 body:not(#ck2) .ck2-v-nav:disabled { visibility: hidden; }
  html.ui2 body:not(#ck2) .ck2-v-info { flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); padding: var(--s3) var(--s4) 0; }
  html.ui2 body:not(#ck2) .ck2-v-score[hidden] { display: none; }
  html.ui2 body:not(#ck2) .ck2-v-title {
    flex: 1 1 60%; min-width: 0; margin: 0; font-size: var(--type-body); line-height: var(--type-body-lh); font-weight: var(--w-semi);
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; overflow-wrap: anywhere;
  }
  html.ui2 body:not(#ck2) .ck2-v-meta { flex: 1 0 100%; margin: 0; color: var(--muted); font-size: var(--type-sm); line-height: var(--type-sm-lh); }
  html.ui2 body:not(#ck2) .ck2-v-wm { flex: 1 0 100%; min-height: 44px; margin: 0; padding: 0; text-align: left; font-size: var(--type-sm); }
  html.ui2 body:not(#ck2) .ck2-v-wm[hidden] { display: none; }
  html.ui2 body:not(#ck2) .ck2-v-bar {
    flex: none; display: grid; grid-template-columns: 1fr 1fr 1.4fr; gap: var(--s2);
    padding: var(--s3) var(--s3) calc(var(--s3) + env(safe-area-inset-bottom));
  }
  html.ui2 body:not(#ck2) .ck2-v-bar .ck-btn { width: 100%; min-width: 0; }
  @media (prefers-reduced-motion: no-preference) {
    html.ui2 body:not(#ck2) .ck2-viewer { opacity: 0; transition: opacity var(--dur-1) var(--ease); }
    html.ui2 body:not(#ck2) .ck2-viewer.open { opacity: 1; }
  }
  }
  /* ==== /Batch 3 round 3.1a ==================================================================== */

  /* ==== Batch 3 round 3.5: Pricing page (ui2 ONLY) ============================================
     The Pricing screen (app.js block "Batch 3 round 3.5"): heading + the home page's lead, the
     billing-period segmented control (built only while the server sells it), the cloned cards
     re-dressed with a per-minute line, a compare table and a FAQ.
     html.ui2-scoped, last layer, tokens only (no new colour). */
  @layer mobile {
  html.ui2 #tab-pricing { max-width: 1120px; margin: 0 auto; }
  html.ui2 #tab-pricing .ck2-price-head { text-align: center; max-width: 680px; margin: 0 auto var(--s5); }
  html.ui2 #tab-pricing .ck2-price-h1 {
    font-size: var(--type-h2); line-height: var(--type-h2-lh); font-weight: var(--w-bold); margin: 0 0 var(--s2);
  }
  html.ui2 #tab-pricing .ck2-price-lead {
    color: var(--muted); font-size: var(--type-body); line-height: var(--type-body-lh); margin: 0 0 var(--s2); text-wrap: pretty;
  }
  html.ui2 #tab-pricing .ck2-price-rate {
    color: var(--text); font-weight: var(--w-semi); font-variant-numeric: tabular-nums; margin: 0 0 var(--s3);
  }
  html.ui2 #tab-pricing .ck2-price-bill {
    display: inline-flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: var(--s2) var(--s3);
  }
  html.ui2 #tab-pricing .ck2-price-save { font-size: var(--type-sm); font-weight: var(--w-semi); color: var(--good); }
  /* <!--yearly--> The segmented control replaces the cloned on/off switch. */
  html.ui2 #tab-pricing .bill-switch-row { display: none; }
  /* <!--/yearly--> */

  /* Cards: one column, centred, until three fit side by side next to the sidebar. */
  html.ui2 #tab-pricing .plans { grid-template-columns: 1fr; max-width: 480px; margin: 0 auto; }
  @media (min-width: 1180px) {
    html.ui2 #tab-pricing .plans { grid-template-columns: repeat(3, minmax(0, 1fr)); max-width: none; }
  }
  /* <!--max-plan--> Four cards beside the sidebar: two by two, then a row of four once that fits. */
  @media (min-width: 1180px) {
    html.ui2 #tab-pricing .plans:has(> .plan-max) { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 1000px; }
  }
  @media (min-width: 1600px) {
    html.ui2 #tab-pricing .plans:has(> .plan-max) { grid-template-columns: repeat(4, minmax(0, 1fr)); max-width: none; }
  }
  /* <!--/max-plan--> */
  html.ui2 #tab-pricing .plan { padding: var(--s5); }
  /* The Pro lift (landing rules) only where the tab's cards stand in one row. */
  html.ui2 #tab-pricing .plans > .plan-pro { translate: none; }
  @media (min-width: 1180px) { html.ui2 #tab-pricing .plans:not(:has(> .plan-max)) > .plan-pro { translate: 0 -13px; } }
  @media (min-width: 1600px) { html.ui2 #tab-pricing .plans:has(> .plan-max) > .plan-pro { translate: 0 -13px; } }
  html.ui2 #tab-pricing .plans { padding-top: var(--s3); }
  html.ui2 #tab-pricing .plan:hover { transform: none; }
  html.ui2 #tab-pricing .plan-fig { font-variant-numeric: tabular-nums; }
  html.ui2 #tab-pricing .ck2-plan-ppm {
    margin: 0 0 var(--s3); font-size: var(--type-sm); line-height: var(--type-sm-lh); font-weight: var(--w-semi);
    color: var(--accent-text); font-variant-numeric: tabular-nums;
  }
  html.ui2 #tab-pricing .ck2-plan-ppm:empty { display: none; }

  /* Compare table and FAQ. */
  html.ui2 #tab-pricing .ck2-price-sec { max-width: 760px; margin: var(--s7) auto 0; }
  html.ui2 #tab-pricing .ck2-price-h3 {
    font-size: var(--type-h3); line-height: var(--type-h3-lh); font-weight: var(--w-bold); margin: 0 0 var(--s3);
  }
  html.ui2 #tab-pricing .ck2-cmp-wrap {
    overflow-x: auto; border: 1px solid var(--border); border-radius: var(--r-card); background: var(--panel);
  }
  html.ui2 #tab-pricing .ck2-cmp {
    width: 100%; border-collapse: collapse; font-size: var(--type-sm); line-height: var(--type-sm-lh);
    font-variant-numeric: tabular-nums;
  }
  html.ui2 #tab-pricing .ck2-cmp th,
  html.ui2 #tab-pricing .ck2-cmp td { padding: var(--s2) var(--s3); border-top: 1px solid var(--border); text-align: center; vertical-align: middle; }
  html.ui2 #tab-pricing .ck2-cmp thead th { border-top: 0; color: var(--text); font-weight: var(--w-bold); }
  html.ui2 #tab-pricing .ck2-cmp tbody th { text-align: left; color: var(--muted); font-weight: var(--w-med); }
  html.ui2 #tab-pricing .ck2-cmp td { color: var(--text); }
  html.ui2 #tab-pricing .ck2-cmp-yes { color: var(--good); font-weight: var(--w-bold); }
  html.ui2 #tab-pricing .ck2-cmp-no { color: var(--muted-2); }
  html.ui2 #tab-pricing .ck2-faq { border-top: 1px solid var(--border); }
  html.ui2 #tab-pricing .ck2-faq:last-of-type { border-bottom: 1px solid var(--border); }
  html.ui2 #tab-pricing .ck2-faq summary {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
    min-height: 44px; padding: var(--s2) 0; cursor: pointer; list-style: none; font-weight: var(--w-semi); color: var(--text);
  }
  html.ui2 #tab-pricing .ck2-faq summary::-webkit-details-marker { display: none; }
  html.ui2 #tab-pricing .ck2-faq summary::after {
    content: "+"; flex: none; color: var(--muted); font-size: var(--type-lg); line-height: 1;
    transition: transform var(--dur-2) var(--ease);
  }
  html.ui2 #tab-pricing .ck2-faq[open] summary::after { transform: rotate(45deg); }
  html.ui2 #tab-pricing .ck2-faq summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }
  html.ui2 #tab-pricing .ck2-faq p { margin: 0 0 var(--s3); color: var(--muted); line-height: var(--type-body-lh); max-width: 65ch; }
  html.ui2 .ck2-sr {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
  @media (max-width: 600px) {
    /* The top bar already names the screen (as on Account). */
    html.ui2 #tab-pricing .ck2-price-h1 { display: none; }
    html.ui2 #tab-pricing .ck2-cmp th,
    html.ui2 #tab-pricing .ck2-cmp td { padding: var(--s2) var(--s1); }
    html.ui2 #tab-pricing .ck2-cmp tbody th { padding-left: var(--s3); }
  }
  @media (prefers-reduced-motion: reduce) {
    html.ui2 #tab-pricing .ck2-faq summary::after { transition: none; }
  }
  }
  /* ==== /Batch 3 round 3.5 ==================================================================== */

  /* ==== Batch 2 round 2.6: desktop sidebar (ui2 ONLY) =========================================
     The left sidebar that replaces the 13-tab bar above a phone width (app.js block "Batch 2
     round 2.6"). ≥ 900px: 232px with second-level items; 601–899px: an 80px icon rail.
     html.ui2-scoped, last layer, tokens only (no new colour). Only while the app shows
     (body.ck2-app): the logged-out landing is untouched. */
  @layer mobile {
  html.ui2 .ck2-side { display: none; }
  @media (min-width: 601px) {
    html.ui2 body.ck2-app .appnav { display: none; }
    /* Account is in the sidebar, so the header's own Account button steps aside. */
    html.ui2 body.ck2-app #userbar .ck2-acct-btn { display: none; }
    /* …and the plan badge sits beside the minutes meter, not alone mid-header. */
    html.ui2 body.ck2-app > header > #userbar { margin-left: auto; margin-right: var(--s3); }
    /* Minutes left: big and in the middle of the top bar (the userbar's small pill steps
       aside; the PRO badge stays on the right). Same button as the phone's, so it opens the
       plans on Free and Account on a paid plan. */
    html.ui2 body.ck2-app #userbar #minsLeftPill { display: none; }
    html.ui2 body.ck2-app .ck2-mins:not([hidden]) {
      position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
      display: inline-flex; align-items: center; gap: var(--s2); height: 44px; min-height: 0;
      padding: 0 var(--s4); margin: 0; border: 0; border-radius: var(--r-pill); box-shadow: none;
      background: var(--border-2); color: var(--text); white-space: nowrap; cursor: pointer;
      font: inherit; font-size: var(--type-lg); line-height: 1; font-weight: var(--w-bold);
    }
    html.ui2 body.ck2-app .ck2-mins .ck-meter-ring { width: 24px; height: 24px; }
    html.ui2 body.ck2-app .ck2-mins:hover { background: var(--panel-3); transform: translate(-50%, -50%); }
    html.ui2 body.ck2-app .ck2-mins:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
    /* Same as the phone's low pill (rgba(255,119,0,.15)), written from the token. */
    html.ui2 body.ck2-app .ck2-mins.ck2-mins-low { background: color-mix(in srgb, var(--warn) 15%, transparent); color: var(--warn); }
    html.ui2 body.ck2-app main, html.ui2 body.ck2-app .site-footer { margin-left: 232px; }
    html.ui2 body.ck2-app #appSection { padding-top: var(--s5); }
    /* The content column is 232px narrower than the window now: a label with a "paid plans"
       tag must wrap rather than push the tag off the page (measured: 55px over at 1024). */
    html.ui2 body.ck2-app #appSection .defaults-row label { flex-wrap: wrap; white-space: normal; min-width: 0; }
    html.ui2 body.ck2-app .ck2-side {
      position: fixed; left: 0; top: var(--hdr, 61px); bottom: 0; z-index: var(--z-nav);
      display: flex; flex-direction: column; gap: var(--s4);
      width: 232px; box-sizing: border-box; overflow-y: auto; overscroll-behavior: contain;
      padding: var(--s4) var(--s3) calc(var(--s4) + env(safe-area-inset-bottom)) max(var(--s3), env(safe-area-inset-left));
      background: var(--e1-bg); border-right: 1px solid var(--e1-border);
    }
    html.ui2 .ck2-side ul { list-style: none; margin: 0; padding: 0; }
    html.ui2 .ck2-side-list { display: flex; flex-direction: column; gap: 2px; }
    html.ui2 .ck2-side .ck2-side-new {
      display: flex; align-items: center; justify-content: center; gap: var(--s2);
      width: 100%; max-width: none; margin: 0; flex: none;
    }
    html.ui2 .ck2-side-new .ck2-side-ico svg { width: 20px; height: 20px; }
    html.ui2 .ck2-side-item, html.ui2 .ck2-side-subitem {
      display: flex; align-items: center; gap: var(--s3); width: 100%; max-width: none;
      min-height: 44px; margin: 0; padding: 0 var(--s3); box-sizing: border-box;
      background: none; border: 0; border-radius: var(--r-ctl); box-shadow: none; transform: none;
      color: var(--muted); font: inherit; font-size: var(--type-body); line-height: var(--type-body-lh);
      font-weight: var(--w-semi); text-align: left; white-space: nowrap; cursor: pointer;
      transition: background var(--dur-1) ease-out, color var(--dur-1) ease-out;
    }
    html.ui2 .ck2-side-subitem {
      min-height: 36px; padding-left: calc(var(--s3) + 24px + var(--s3));
      font-size: var(--type-sm); line-height: var(--type-sm-lh); font-weight: var(--w-reg);
    }
    html.ui2 .ck2-side-item:hover, html.ui2 .ck2-side-subitem:hover { background: var(--panel-2); color: var(--text); transform: none; }
    html.ui2 .ck2-side-item.ck2-side-on { color: var(--text); }
    html.ui2 .ck2-side-item[aria-current="page"], html.ui2 .ck2-side-subitem[aria-current="page"] {
      background: var(--accent-soft); color: var(--text);
    }
    html.ui2 .ck2-side-subitem[aria-current="page"] { font-weight: var(--w-semi); }
    html.ui2 .ck2-side-sub { display: flex; flex-direction: column; gap: 2px; margin: 2px 0 var(--s2); }
    html.ui2 .ck2-side-ico { display: grid; place-items: center; width: 24px; height: 24px; flex: none; }
    html.ui2 .ck2-side-locked::after { content: "\1F512"; margin-left: auto; font-size: var(--type-cap); opacity: .7; }
    html.ui2 .ck2-side button:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
  }
  @media (min-width: 601px) and (pointer: coarse) {
    html.ui2 .ck2-side-subitem { min-height: 44px; }
  }
  /* ≥ 900px: the sidebar is the second level, so the in-page segmented control is not needed. */
  @media (min-width: 900px) {
    html.ui2 body.ck2-app #ck2Seg { display: none; }
  }
  /* 601–899px: an 80px rail — the same destinations, a label under each icon (two-word labels
     wrap), no second level (Create opens the Create sheet; Library and Post keep their
     segmented control). */
  @media (min-width: 601px) and (max-width: 899px) {
    html.ui2 body.ck2-app main, html.ui2 body.ck2-app .site-footer { margin-left: 80px; }
    html.ui2 body.ck2-app .ck2-side { width: 80px; padding-left: var(--s1); padding-right: var(--s1); gap: var(--s3); }
    html.ui2 body.ck2-app .ck2-side-sub { display: none; }
    html.ui2 body.ck2-app .ck2-side .ck2-side-new { width: 56px; height: 56px; min-height: 56px; padding: 0; margin: 0 auto; border-radius: var(--r-card); }
    html.ui2 body.ck2-app .ck2-side-new .ck2-side-label {
      position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
    }
    html.ui2 body.ck2-app .ck2-side-item {
      flex-direction: column; justify-content: center; gap: 2px; min-height: 56px; padding: var(--s1) 0;
      font-size: var(--type-cap); line-height: var(--type-cap-lh); text-align: center; white-space: normal;
    }
    html.ui2 body.ck2-app #ck2Seg:not([hidden]) {
      display: flex; gap: 4px; padding: 4px; margin: 0 auto var(--s3); max-width: 610px;
      background: var(--bg-2); border: 1px solid var(--border-2); border-radius: var(--r-pill);
    }
    html.ui2 body.ck2-app #ck2Seg .ck2-seg-btn {
      flex: 1 1 0; min-width: 0; min-height: 44px; max-width: none; margin: 0; padding: 0 var(--s3);
      background: none; border: 0; border-radius: var(--r-pill); box-shadow: none; transform: none;
      color: var(--muted); font: inherit; font-size: var(--type-sm); font-weight: var(--w-semi);
      white-space: nowrap; cursor: pointer;
    }
    html.ui2 body.ck2-app #ck2Seg .ck2-seg-btn:hover { background: var(--accent-soft); color: var(--text); }
    html.ui2 body.ck2-app #ck2Seg .ck2-seg-btn[aria-current="page"] { background: var(--panel-3); color: var(--text); }
  }
  @media (prefers-reduced-motion: reduce) {
    html.ui2 .ck2-side-item, html.ui2 .ck2-side-subitem { transition: none; }
  }
  }
  /* ==== /Batch 2 round 2.6 ================================================================== */

  /* ==== Batch 2 round 2.9: Account (ui2 ONLY) =================================================
     The Account screen (app.js block "Batch 2 round 2.9"): identity, Plan & usage holding the
     moved plan strip, Connections, Email preferences, Developers, Help, Log out, Close account.
     html.ui2-scoped, last layer, tokens only (no new colour). */
  @layer mobile {
  html.ui2 #tab-account { max-width: 610px; margin: 0 auto; }
  html.ui2 #tab-account .ck2-acct-h1 {
    font-size: var(--type-h2); line-height: 1.2; font-weight: var(--w-bold); margin: 0 0 var(--s4);
  }
  /* Identity: initial · address · plan badge. */
  html.ui2 #tab-account .ck2-acct-id { display: flex; align-items: center; gap: var(--s3); }
  html.ui2 #tab-account .ck2-acct-avatar {
    flex: none; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 50%;
    background: var(--accent-soft); color: var(--accent-text); font-weight: var(--w-bold); font-size: var(--type-lg);
  }
  html.ui2 #tab-account .ck2-acct-who { min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: var(--s1); }
  html.ui2 #tab-account .ck2-acct-who .ck2-email { margin: 0; font-weight: var(--w-semi); overflow-wrap: anywhere; }
  /* Plan & usage: the ring and the minutes left, then the existing strip, re-dressed. */
  html.ui2 #tab-account .ck2-acct-usage { display: flex; align-items: center; gap: var(--s3); margin: 0 0 var(--s3); }
  html.ui2 #tab-account .ck2-acct-usage .ck-meter-ring { width: 44px; height: 44px; }
  html.ui2 #tab-account .ck2-acct-usage p { margin: 0; }
  html.ui2 #tab-account .ck2-acct-big { font-size: var(--type-lg); font-weight: var(--w-bold); }
  html.ui2 #tab-account .ck2-acct-strip { display: block; margin: 0 0 var(--s3); font-size: var(--type-sm); }
  html.ui2 #tab-account .ck2-acct-strip.hidden { display: none; }
  html.ui2 #tab-account .ck2-acct-strip .planbar-label,
  html.ui2 #tab-account .ck2-acct-strip .usagebar { display: none; }
  html.ui2 #tab-account .ck2-acct-strip #planInfo { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
  html.ui2 #tab-account .ck2-acct-strip #planInfo > p { flex: 1 1 100%; margin: 0; }
  html.ui2 #tab-account .ck2-acct-strip .ck-btn { margin: 0; }
  html.ui2 #tab-account .ck2-acct-strip .auto-nudge { margin-top: var(--s3); }
  /* Email preferences: the moved tick box, as a 44px row. */
  html.ui2 #tab-account .ck2-acct-email .consent-row { display: flex; align-items: center; gap: var(--s2); min-height: 44px; margin: 0 !important; }
  html.ui2 #tab-account .ck2-acct-email input[type="checkbox"] { width: 22px; height: 22px; margin: 0; flex: none; }
  /* The label fills the row and its ::after covers the whole 44px row (tick box included), so
     the row is one target; a click on it is a click on the label, which toggles the box. */
  html.ui2 #tab-account .ck2-acct-email .consent-row { position: relative; }
  html.ui2 #tab-account .ck2-acct-email label {
    cursor: pointer; flex: 1 1 auto; display: flex; align-items: center; min-height: 44px;
  }
  html.ui2 #tab-account .ck2-acct-email label::after { content: ""; position: absolute; inset: 0; }
  html.ui2 #tab-account .ck2-group .hint, html.ui2 #tab-account .ck2-group p { text-align: left; }
  html.ui2 #tab-account .ck2-group > .hint { margin: var(--s2) 0 0; }
  html.ui2 #tab-account .ck2-group { text-align: left; }
  html.ui2 #tab-account .ck2-group h2 {
    text-align: left; text-transform: none; letter-spacing: normal; font-size: var(--type-lg); font-weight: var(--w-bold);
  }
  html.ui2 body:not(#ck2) #tab-account .ck2-logout { width: 100%; max-width: none; }
  /* The phone's top bar already names the screen. */
  @media (max-width: 600px) { html.ui2 #tab-account .ck2-acct-h1 { display: none; } }
  html.ui2 #tab-account .ck2-acct-danger h2 { color: var(--danger); }
  html.ui2 #tab-account .ck2-acct-danger a { color: var(--text); text-decoration: underline; }
  /* Desktop header: "Account" where the address was. The address and Log out live on Account. */
  html.ui2 body.ck2-app #userbar > span:first-child,
  html.ui2 body.ck2-app #userbar > button[onclick="logout()"] { display: none; }
  html.ui2 #userbar .ck2-acct-btn {
    min-height: 36px; padding: 0 var(--s3); border-radius: var(--r-pill);
    background: none; border: 1px solid var(--border-3); color: var(--text); font: inherit; font-size: var(--type-sm);
    font-weight: var(--w-semi); cursor: pointer;
  }
  html.ui2 #userbar .ck2-acct-btn:hover { background: var(--accent-soft); }
  @media (pointer: coarse) { html.ui2 #userbar .ck2-acct-btn { min-height: 44px; } }
  html.ui2 body[data-ck2-route="account"] #userbar .ck2-acct-btn { border-color: var(--accent-line); background: var(--accent-soft); }
  html.ui2 #userbar .ck2-acct-btn:focus-visible { outline: 2px solid var(--accent-text); outline-offset: 2px; }
  }
  /* ==== /Batch 2 round 2.9 ================================================================== */

  /* ==== Batch 2 round 2.8: Library (ui2 ONLY) =================================================
     Videos / Saved / Stories / Compilations under one segmented control (app.js block
     "Batch 2 round 2.8"). html.ui2-scoped, last layer, tokens only (no new colour). */
  @layer mobile {
  /* The Library's segments show at every width: desktop has no other way into Stories. */
  @media (min-width: 601px) {
  html.ui2 .ck2-seg.ck2-seg--lib:not([hidden]) {
    display: flex; gap: 4px; padding: 4px; margin: 0 auto var(--s3); max-width: 610px;
    background: var(--bg-2); border: 1px solid var(--border-2); border-radius: var(--r-pill);
  }
  html.ui2 .ck2-seg--lib .ck2-seg-btn {
    flex: 1 1 0; min-width: 0; min-height: 44px; max-width: none; margin: 0; padding: 0 var(--s3);
    background: none; border: 0; border-radius: var(--r-pill); box-shadow: none; transform: none;
    color: var(--muted); font: inherit; font-size: var(--type-sm); font-weight: var(--w-semi);
    white-space: nowrap; cursor: pointer;
  }
  html.ui2 .ck2-seg--lib .ck2-seg-btn:hover { background: var(--accent-soft); color: var(--text); transform: none; }
  html.ui2 .ck2-seg--lib .ck2-seg-btn[aria-current="page"] { background: var(--panel-3); color: var(--text); }
  }
  /* Phone: four labels share 328px, so each takes what its word needs ("Compilations" is long). */
  @media (max-width: 600px) {
    html.ui2 body.ck2-app .ck2-seg--lib .ck2-seg-btn { flex: 1 1 auto; padding: 0 var(--s2); }
  }
  @media (max-width: 380px) {
    html.ui2 body.ck2-app .ck2-seg--lib { gap: 0; padding: 3px; }
    html.ui2 body.ck2-app .ck2-seg--lib .ck2-seg-btn { padding: 0 6px; font-size: var(--type-cap); }
  }
  html.ui2 .ck2-kept { margin-top: 0; }
  html.ui2 #appSection .ck2-lib-row .upload-main strong { white-space: nowrap; }
  /* Desktop: the lists become a grid of cards. */
  @media (min-width: 900px) {
    html.ui2 #appSection .ck2-lib-list {
      display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--s2);
    }
    html.ui2 #appSection .ck2-lib-list > .upload-row {
      margin: 0; flex-direction: column; align-items: stretch; flex-wrap: nowrap; gap: var(--s2);
    }
    html.ui2 #appSection .ck2-lib-list > .upload-row .upload-actions { flex-wrap: wrap; width: auto; }
    html.ui2 #appSection .ck2-lib-list > :not(.upload-row) { grid-column: 1 / -1; }
  }
  }
  /* ==== /Batch 2 round 2.8 ================================================================== */

  /* ==== Batch 2 round 2.7: logged-in Home (ui2 ONLY) ========================================
     #ck2Home is built by app.js at the top of #tab-home. The marketing #homePage stays in the DOM
     there and is only hidden under html.ui2; logged out it lives in #homePublicMount, untouched.
     Every rule is html.ui2-scoped, last layer, tokens only (no new colour). */
  @layer mobile {
  html.ui2 #tab-home > #homePage { display: none; }
  html.ui2 body[data-ck2-route="home"] #runningJobLine { display: none; }   /* Home's card says it, with more */
  html.ui2 body:not(#ck2) .ck2-home { display: flex; flex-direction: column; gap: var(--s5); max-width: 756px; margin: 0 auto; padding: var(--s3) 0 var(--s7); }
  html.ui2 body:not(#ck2) .ck2-home-h { font-size: var(--type-h2); line-height: var(--type-h2-lh); font-weight: var(--w-bold); margin: 0; letter-spacing: -0.01em; }
  html.ui2 body:not(#ck2) .ck2-home-h2 { font-size: var(--type-lg); line-height: var(--type-lg-lh); font-weight: var(--w-semi); margin: 0; }
  html.ui2 .ck2-home-row { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s3); }
  html.ui2 .ck2-home-job { display: flex; flex-direction: column; gap: var(--s3); border-color: var(--accent-line); }
  html.ui2 .ck2-home-jobtop { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2); }
  html.ui2 body:not(#ck2) .ck2-home-jobt { font-size: var(--type-lg); line-height: var(--type-lg-lh); font-weight: var(--w-semi); margin: 0; overflow-wrap: anywhere; }
  html.ui2 .ck2-steps { list-style: none; display: flex; gap: var(--s2); margin: 0; padding: 0; }
  html.ui2 .ck2-step { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--s2); font-size: var(--type-cap); line-height: var(--type-cap-lh); color: var(--muted); }
  html.ui2 .ck2-step-dot { display: block; height: 4px; border-radius: var(--r-pill); background: var(--border-3); }
  html.ui2 .ck2-step.is-done .ck2-step-dot { background: var(--accent); }
  html.ui2 .ck2-step.is-now .ck2-step-dot { background: linear-gradient(90deg, var(--accent) 50%, var(--border-3) 50%); }
  html.ui2 .ck2-step.is-now, html.ui2 .ck2-step.is-done { color: var(--text); }
  html.ui2 .ck2-step.is-now .ck2-step-l { font-weight: var(--w-semi); }
  html.ui2 body:not(#ck2) .ck2-home-eta { margin: 0; font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--text); }
  html.ui2 body:not(#ck2) .ck2-home-safe { margin: 0; font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--muted); }
  html.ui2 .ck2-home-jobact { display: flex; flex-wrap: wrap; gap: var(--s2); }
  html.ui2 body:not(#ck2) .ck2-home > div:empty { display: none; }
  html.ui2 body:not(#ck2) .ck2-home-rail { overflow-x: auto; overscroll-behavior-x: contain; scroll-snap-type: x proximity; scroll-padding-inline: var(--s3); margin: 0 calc(-1 * var(--s3)); padding: 0 var(--s3) var(--s2); scrollbar-width: thin; }
  html.ui2 .ck2-home-rail .ck-skel-list { display: flex; gap: var(--s3); }
  html.ui2 .ck2-rail-list { list-style: none; display: flex; gap: var(--s3); margin: 0; padding: 0; }
  html.ui2 .ck2-rail-list > li { flex: 0 0 auto; scroll-snap-align: start; }
  html.ui2 body:not(#ck2) .ck2-poster { display: flex; flex-direction: column; gap: var(--s2); width: 112px; padding: 0; margin: 0; border: 0; background: none; color: var(--text); text-align: left; cursor: pointer; border-radius: var(--r-card); min-height: 0; box-shadow: none; }
  html.ui2 .ck2-poster-frame { position: relative; display: block; width: 112px; aspect-ratio: 9 / 16; overflow: hidden; border-radius: var(--r-card); background: var(--panel-2); border: 1px solid var(--border-2); transition: transform var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease); }
  html.ui2 .ck2-poster-frame video { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
  html.ui2 .ck2-poster-frame.is-text { display: flex; align-items: flex-end; padding: var(--s2); background: linear-gradient(180deg, var(--panel-2), var(--panel-3)); }
  html.ui2 .ck2-poster-quote { font-size: var(--type-cap); line-height: var(--type-cap-lh); color: var(--muted); display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden; }
  html.ui2 .ck2-poster-score { position: absolute; top: var(--s1); left: var(--s1); display: inline-flex; align-items: center; gap: 2px; padding: 2px 6px; border-radius: var(--r-pill); background: var(--scrim); color: var(--text); font-size: var(--type-cap); line-height: var(--type-cap-lh); font-weight: var(--w-semi); font-variant-numeric: tabular-nums; }
  html.ui2 .ck2-poster-score .ck-icon { width: 12px; height: 12px; color: var(--orange); }
  html.ui2 .ck2-poster-title { font-size: var(--type-cap); line-height: var(--type-cap-lh); color: var(--muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  html.ui2 .ck2-poster:hover .ck2-poster-frame { border-color: var(--accent-line); }
  html.ui2 .ck2-poster:active .ck2-poster-frame { transform: scale(0.97); }
  html.ui2 .ck2-poster:focus-visible { outline: none; }
  html.ui2 .ck2-poster:focus-visible .ck2-poster-frame { outline: 2px solid var(--accent); outline-offset: 2px; }
  html.ui2 body:not(#ck2) .ck2-home-next { display: flex; align-items: center; gap: var(--s3); width: 100%; min-height: 56px; text-align: left; color: var(--text); cursor: pointer; margin: 0; box-shadow: none; font: inherit; }
  html.ui2 .ck2-home-next .ck-icon { flex: 0 0 auto; width: 20px; height: 20px; color: var(--accent-text); }
  html.ui2 .ck2-home-nextbody { display: flex; flex-direction: column; min-width: 0; }
  html.ui2 .ck2-home-nextk { font-size: var(--type-cap); line-height: var(--type-cap-lh); color: var(--muted); }
  html.ui2 .ck2-home-nextv { font-size: var(--type-sm); line-height: var(--type-sm-lh); font-weight: var(--w-semi); overflow-wrap: anywhere; }
  html.ui2 .ck2-home-paste { display: flex; flex-direction: column; gap: var(--s2); margin: 0; }
  html.ui2 body:not(#ck2) .ck2-home-label { font-size: var(--type-sm); line-height: var(--type-sm-lh); font-weight: var(--w-semi); color: var(--text); margin: 0; }
  html.ui2 .ck2-home-pasterow { display: flex; gap: var(--s2); flex-wrap: wrap; }
  html.ui2 body:not(#ck2) .ck2-home-pasterow input { flex: 1 1 220px; min-width: 0; width: auto; min-height: 44px; margin: 0; font-size: 16px; padding: 0 var(--s3); border-radius: var(--r-ctl); }
  html.ui2 body:not(#ck2) .ck2-home-pasterow .ck-btn { flex: 0 0 auto; margin: 0; }
  html.ui2 body:not(#ck2) .ck2-paste { flex: 0 0 auto; min-height: 44px; margin: 0; }
  html.ui2 body:not(#ck2) .ck2-paste[hidden] { display: none; }
  html.ui2 .ck2-home-empty { display: flex; flex-direction: column; align-items: center; gap: var(--s4); text-align: center; }
  html.ui2 .ck2-home-empty.hidden { display: none; }
  html.ui2 .ck2-home-demo { width: min(200px, 46vw); aspect-ratio: 9 / 16; border-radius: var(--r-card); overflow: hidden; background: var(--panel-2); border: 1px solid var(--border-2); }
  html.ui2 .ck2-home-demo video { width: 100%; height: 100%; object-fit: cover; display: block; }
  html.ui2 body:not(#ck2) .ck2-home-emptytext p { margin: var(--s2) 0 0; font-size: var(--type-body); line-height: var(--type-body-lh); color: var(--muted); max-width: 46ch; }
  html.ui2 .ck2-home-ready.hidden { display: none; }
  @media (max-width: 600px) {
    html.ui2 body:not(#ck2) .ck2-home-h { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
    html.ui2 body:not(#ck2) .ck2-home { padding-top: 0; gap: var(--s4); }
    html.ui2 body:not(#ck2) .ck2-home-pasterow .ck-btn { flex: 1 1 100%; }
  }
  @media (pointer: coarse) {
    html.ui2 .ck2-home-row .ck-btn--link { min-height: 44px; }
  }
  @media (prefers-reduced-motion: reduce) {
    html.ui2 .ck2-poster-frame { transition: none; }
    html.ui2 .ck2-poster:active .ck2-poster-frame { transform: none; }
  }
  }
  /* ==== /Batch 2 round 2.7 ================================================================== */

/*  */
/* EU "Withdraw from contract here" (Directive 2011/83/EU art 11a). Always served: the page only
   draws it for an EU/EEA customer inside the 14 days (app/services/eu_withdrawal.py). Tokens
   only; the same look as the DMCCA dialog, which is cut from the served file until 1 Jan. */
@layer components {
  .ck-wd-strip { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; align-items: flex-start; flex: 1 0 100%; width: 100%; }
  .ck-wd-strip .hint { margin: 0; }
  .ck-wd-dialog {
    width: min(560px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); padding: 0;
    border: 1px solid var(--border-2); border-radius: var(--r-card);
    background: var(--panel); color: var(--text); overflow: auto;
  }
  .ck-wd-dialog::backdrop { background: var(--bg); opacity: 0.85; }
  .ck-wd-inner { padding: 20px 16px; display: flex; flex-direction: column; gap: 14px; margin: 0; }
  .ck-wd-title { margin: 0; font-size: 1.15rem; line-height: 1.3; }
  .ck-wd-title:focus { outline: none; }
  .ck-wd-text { margin: 0; font-size: 0.95rem; line-height: 1.5; }
  .ck-wd-field { display: flex; flex-direction: column; gap: 4px; }
  .ck-wd-label { margin: 0; font-weight: 700; font-size: 0.9rem; line-height: 1.35; }
  .ck-wd-value { margin: 0; font-size: 0.95rem; line-height: 1.5; overflow-wrap: anywhere; }
  .ck-wd-hint { margin: 0; font-size: 0.85rem; line-height: 1.4; color: var(--muted); }
  .ck-wd-field input {
    min-height: 44px; padding: 8px 12px; font: inherit; color: var(--text);
    background: var(--bg-2); border: 1px solid var(--border-2); border-radius: var(--r-ctl);
  }
  .ck-wd-error { margin: 0; color: var(--danger); font-size: 0.85rem; }
  .ck-wd-error:empty { display: none; }
  .ck-wd-actions { display: flex; flex-wrap: wrap-reverse; gap: 10px; justify-content: flex-end; }
  .ck-wd-actions button {
    min-height: 44px; padding: 10px 18px; border-radius: var(--r-pill); font: inherit;
    font-weight: 600; cursor: pointer; flex: 1 1 auto;
  }
  .ck-wd-back { background: transparent; color: var(--text); border: 1px solid var(--border-2); }
  .ck-wd-confirm { background: var(--accent); color: var(--accent-ink); border: 1px solid var(--accent); }
  .ck-wd-dialog button:disabled { opacity: 0.6; cursor: progress; }
  .ck-wd-actions button:focus-visible, .ck-wd-field input:focus-visible {
    outline: 3px solid var(--accent-text); outline-offset: 2px;
  }
}
  /* ==== Batch 2 round 2.4: component primitives + feedback layer (ui2 ONLY) =================
     Every rule is html.ui2-scoped and in the LAST layer. `body:not(#ck2)` lifts each
     selector to id specificity so it also beats the legacy `#appSection button…` phone rules,
     while still matching sheets and toasts that live outside #appSection. Colours: tokens only.
     Components: Button (.ck-btn: primary gold = the ONE main action, brand, secondary, ghost,
     danger, link; sm 36 / default 44 / lg 52), IconButton, Field, Switch, Segmented, Chip and
     StatusBadge, Card (never nested), Toast, Skeleton, EmptyState, Banner, MinutesMeter. */
  @layer mobile {
  html.ui2 .ck-icon { width: 20px; height: 20px; flex: none; vertical-align: -4px; }
  html.ui2 .ck-has-icon .ck-icon { margin-right: 6px; }
  html.ui2 .ck-has-icon.ck-btn .ck-icon, html.ui2 .ck-has-icon.ck-iconbtn .ck-icon { margin-right: 0; }

  /* ---- Button ---- */
  html.ui2 body:not(#ck2) .ck-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
    min-height: 44px; padding: 0 var(--s4); box-sizing: border-box; width: auto;
    border: 1px solid transparent; border-radius: var(--r-pill); box-shadow: none; transform: none;
    font: inherit; font-size: var(--type-body); line-height: 1.2; font-weight: var(--w-semi);
    letter-spacing: normal; text-decoration: none; cursor: pointer; white-space: nowrap;
    transition: background var(--dur-1) ease-out, border-color var(--dur-1) ease-out, color var(--dur-1) ease-out;
  }
  /* .hidden lives in @layer components, which this layer outranks: without this the dressed
     #cancelBtn / #pollRetryBtn showed on an idle Create screen (fix V3-1). */
  html.ui2 body:not(#ck2) .ck-btn.hidden { display: none; }
  /* Follow-up F1: the "More settings" fold is a phone-only control (the panel is
     display:contents above 768px), but .ck-btn's inline-flex beat its display:none, so
     desktops showed a button that did nothing. Hide it where it has nothing to fold. */
  @media (min-width: 769px) { html.ui2 body:not(#ck2) #extractMoreToggle { display: none; } }
  @media (min-width: 769px) { html.ui2 body:not(#ck2) #ck2JumpCaps { display: none; } }
  html.ui2 body:not(#ck2) .ck-btn--sm { min-height: 36px; padding: 0 var(--s3); font-size: var(--type-sm); }
  html.ui2 body:not(#ck2) .ck-btn--lg { min-height: 52px; padding: 0 var(--s5); font-size: var(--type-lg); }
  @media (pointer: coarse) { html.ui2 body:not(#ck2) .ck-btn--sm { min-height: 44px; } }
  html.ui2 body:not(#ck2) .ck-btn--primary { background: var(--gold); border-color: var(--gold); color: var(--cta-ink); }
  html.ui2 body:not(#ck2) .ck-btn--primary:hover:not(:disabled) { background: var(--amber); box-shadow: 0 0 0 3px var(--amber-soft); transform: none; }
  html.ui2 body:not(#ck2) .ck-btn--brand { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
  html.ui2 body:not(#ck2) .ck-btn--brand:hover:not(:disabled) { background: var(--accent-2); transform: none; }
  html.ui2 body:not(#ck2) .ck-btn--secondary { background: none; border-color: var(--border-3); color: var(--text); }
  html.ui2 body:not(#ck2) .ck-btn--secondary:hover:not(:disabled) { background: var(--accent-soft); border-color: var(--accent-line); transform: none; }
  html.ui2 body:not(#ck2) .ck-btn--ghost { background: none; color: var(--text); }
  html.ui2 body:not(#ck2) .ck-btn--ghost:hover:not(:disabled) { background: var(--accent-soft); transform: none; }
  html.ui2 body:not(#ck2) .ck-btn--danger { background: none; border-color: var(--danger); color: var(--danger); }
  html.ui2 body:not(#ck2) .ck-btn--danger.is-solid { background: var(--danger); color: var(--cta-ink); }
  html.ui2 body:not(#ck2) .ck-btn--danger:hover:not(:disabled) { background: var(--danger-soft); transform: none; }
  html.ui2 body:not(#ck2) .ck-btn--danger.is-solid:hover:not(:disabled) { background: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft); }
  html.ui2 body:not(#ck2) .ck-btn--link {
    min-height: 44px; padding: 0 var(--s1); background: none; border: 0; border-radius: var(--r-ctl);
    color: var(--accent-text); text-decoration: underline; text-underline-offset: 3px; font-weight: var(--w-med);
  }
  html.ui2 body:not(#ck2) .ck-btn:disabled { opacity: 1; background: var(--panel-2); border-color: var(--border-2); color: var(--muted); cursor: not-allowed; box-shadow: none; }
  html.ui2 body:not(#ck2) .ck-btn[aria-busy="true"] { position: relative; color: transparent; pointer-events: none; }
  html.ui2 body:not(#ck2) .ck-btn[aria-busy="true"]::after {
    content: ""; position: absolute; width: 18px; height: 18px; border-radius: 50%;
    border: 2px solid var(--muted); border-top-color: var(--text); animation: ckSpin .8s linear infinite;
  }
  @keyframes ckSpin { to { transform: rotate(360deg); } }
  html.ui2 body:not(#ck2) .ck-btn:focus-visible, html.ui2 body:not(#ck2) .ck-iconbtn:focus-visible,
  html.ui2 body:not(#ck2) .ck-seg-btn:focus-visible, html.ui2 body:not(#ck2) .ck-switch:focus-visible {
    outline: 2px solid var(--accent-text); outline-offset: 2px;
  }

  /* ---- IconButton: 44×44 hit area, needs aria-label ---- */
  html.ui2 body:not(#ck2) .ck-iconbtn {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 44px; height: 44px; min-height: 44px; padding: 0; margin: 0;
    background: none; border: 0; border-radius: var(--r-pill); color: var(--muted); box-shadow: none; transform: none; cursor: pointer;
  }
  html.ui2 body:not(#ck2) .ck-iconbtn:hover { background: var(--accent-soft); color: var(--text); transform: none; }

  /* ---- Field: visible label, helper, inline error ---- */
  html.ui2 .ck-field { display: flex; flex-direction: column; gap: var(--s1); margin-bottom: var(--s3); }
  html.ui2 .ck-field > label { font-size: var(--type-sm); line-height: var(--type-sm-lh); font-weight: var(--w-semi); color: var(--text); }
  html.ui2 .ck-field-help { font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--muted); }
  html.ui2 .ck-field-error { font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--danger); }
  html.ui2 .ck-field input, html.ui2 .ck-field select, html.ui2 .ck-field textarea { min-height: 44px; font-size: 16px; }

  /* ---- Switch: a native checkbox with role=switch; the label row is the hit area ---- */
  html.ui2 body:not(#ck2) input.ck-switch {
    appearance: none; -webkit-appearance: none; position: relative; flex: none;
    width: 40px; height: 24px; margin: 0 var(--s2) 0 0; padding: 0; vertical-align: middle;
    background: var(--panel-3); border: 1px solid var(--border-3); border-radius: var(--r-pill); cursor: pointer;
    transition: background var(--dur-2) ease-out, border-color var(--dur-2) ease-out;
  }
  html.ui2 body:not(#ck2) input.ck-switch::before {
    content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
    background: var(--muted); transition: transform var(--dur-2) ease-out, background var(--dur-2) ease-out;
  }
  html.ui2 body:not(#ck2) input.ck-switch:checked { background: var(--accent); border-color: var(--accent); }
  html.ui2 body:not(#ck2) input.ck-switch:checked::before { transform: translateX(16px); background: var(--text); }
  html.ui2 body:not(#ck2) input.ck-switch:disabled { opacity: 1; background: var(--panel-2); border-color: var(--border-2); cursor: not-allowed; }
  @media (pointer: coarse) {
    html.ui2 #tab-extract label:has(> input.ck-switch) { display: flex; align-items: center; min-height: 44px; }
  }

  /* ---- Segmented control (drives a hidden <select>) ---- */
  html.ui2 body:not(#ck2) select.ck-seg-source { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; overflow: hidden; }
  html.ui2 .ck-seg {
    display: inline-flex; gap: 2px; padding: 3px; max-width: 100%; box-sizing: border-box;
    background: var(--bg-2); border: 1px solid var(--border-2); border-radius: var(--r-pill);
  }
  html.ui2 body:not(#ck2) .ck-seg-btn {
    flex: 1 1 auto; min-height: 38px; min-width: 56px; margin: 0; padding: 0 var(--s3);
    background: none; border: 0; border-radius: var(--r-pill); box-shadow: none; transform: none;
    color: var(--muted); font: inherit; font-size: var(--type-sm); font-weight: var(--w-semi); cursor: pointer; white-space: nowrap;
  }
  html.ui2 body:not(#ck2) .ck-seg-btn:hover:not(:disabled) { background: var(--accent-soft); color: var(--text); transform: none; }
  html.ui2 body:not(#ck2) .ck-seg-btn[aria-checked="true"] { background: var(--accent); color: var(--accent-ink); }
  @media (pointer: coarse) { html.ui2 body:not(#ck2) .ck-seg-btn { min-height: 44px; } }

  /* ---- Chip / StatusBadge: icon or dot plus words, never colour alone ---- */
  html.ui2 .ck-chip, html.ui2 .ck-badge {
    display: inline-flex; align-items: center; gap: 6px; min-height: 28px; padding: 0 var(--s3);
    border-radius: var(--r-pill); font-size: var(--type-cap); line-height: var(--type-cap-lh); font-weight: var(--w-semi);
    background: var(--panel-2); border: 1px solid var(--border-2); color: var(--text); white-space: nowrap;
  }
  html.ui2 .ck-badge::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
  html.ui2 .ck-badge--ready { color: var(--good); border-color: var(--good-soft); background: var(--good-soft); }
  html.ui2 .ck-badge--processing { color: var(--orange); border-color: var(--warn-soft); background: var(--warn-soft); }
  html.ui2 .ck-badge--processing::before { animation: ckPulse 1.4s ease-in-out infinite; }
  html.ui2 .ck-badge--failed { color: var(--danger); border-color: var(--danger-soft); background: var(--danger-soft); }
  html.ui2 .ck-badge--scheduled { color: var(--violet); border-color: var(--violet-line); background: var(--violet-soft); }
  @keyframes ckPulse { 50% { opacity: .35; } }

  /* ---- Card (never nested: a card inside a card loses its chrome) ---- */
  html.ui2 .ck-card { background: var(--e1-bg); border: 1px solid var(--e1-border); border-radius: var(--r-card); padding: var(--s4); }
  html.ui2 .ck-card .ck-card { background: none; border: 0; padding: 0; border-radius: 0; }

  /* ---- Toast (ui2): icon, words, optional action, dismiss; above the phone bars ---- */
  html.ui2 .toast.ck-toast {
    align-items: flex-start; gap: var(--s2); padding: var(--s2) var(--s1) var(--s2) var(--s3);
    background: var(--e3-bg); color: var(--text); border: 1px solid var(--border-3); border-left-width: 3px;
    border-radius: var(--r-ctl); box-shadow: var(--e3-shadow);
    font-size: var(--type-sm); line-height: var(--type-sm-lh);
  }
  html.ui2 .ck-toast-icon { margin-top: 12px; }
  html.ui2 .ck-toast-msg { flex: 1; min-width: 0; padding: 12px 0; white-space: pre-line; overflow-wrap: anywhere; }
  html.ui2 .ck-toast--info { border-left-color: var(--accent-text); } html.ui2 .ck-toast--info .ck-toast-icon { color: var(--accent-text); }
  html.ui2 .ck-toast--success { border-left-color: var(--good); } html.ui2 .ck-toast--success .ck-toast-icon { color: var(--good); }
  html.ui2 .ck-toast--warn { border-left-color: var(--amber); } html.ui2 .ck-toast--warn .ck-toast-icon { color: var(--amber); }
  html.ui2 .ck-toast--error { border-left-color: var(--danger); } html.ui2 .ck-toast--error .ck-toast-icon { color: var(--danger); }
  html.ui2 body:not(#ck2) .ck-toast-act { align-self: center; color: var(--accent-text); }
  html.ui2 .toast-container { pointer-events: none; } html.ui2 .toast-container > * { pointer-events: auto; }
  @media (max-width: 600px) {
    html.ui2 .toast-container { left: var(--s3); right: var(--s3); max-width: none; }
  }
  /* While a Sheet is open its own buttons sit at the bottom: toasts move to the top. */
  html.ui2 body.ck2-sheet-open:not(#ck2) .toast-container { top: calc(var(--s3) + env(safe-area-inset-top)); bottom: auto; }

  /* ---- Confirm (a Sheet) ---- */
  html.ui2 .ck-confirm-text { margin: 0 0 var(--s4); color: var(--muted); white-space: pre-line; font-size: var(--type-body); line-height: var(--type-body-lh); }
  html.ui2 .ck-confirm .ck-terms-agree { margin: 0 0 var(--s4); font-size: var(--type-body); line-height: var(--type-body-lh); }
  html.ui2 .ck-confirm .ck-terms-agree a { color: var(--accent-text); }
  html.ui2 .ck-confirm-actions { display: flex; gap: var(--s3); justify-content: flex-end; flex-wrap: wrap; padding-top: var(--s2); }
  @media (max-width: 600px) { html.ui2 .ck-confirm-actions > .ck-btn { flex: 1 1 0; } }

  /* ---- Skeleton (static under reduced motion) ---- */
  html.ui2 .ck-skel {
    background: linear-gradient(90deg, var(--panel-2) 0%, var(--panel-3) 50%, var(--panel-2) 100%);
    background-size: 200% 100%; border-radius: var(--r-ctl); animation: ckShimmer 1.4s ease-in-out infinite;
  }
  @keyframes ckShimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
  html.ui2 .ck-skel-list { display: flex; flex-direction: column; gap: var(--s3); }
  html.ui2 .ck-skel-list--tile { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  html.ui2 .ck-skel-row { display: flex; gap: var(--s3); align-items: center; }
  html.ui2 .ck-skel-thumb { width: 55px; height: 55px; flex: none; }
  html.ui2 .ck-skel-lines { flex: 1; display: flex; flex-direction: column; gap: var(--s2); }
  html.ui2 .ck-skel-line { height: 13px; }
  html.ui2 .ck-skel-line--short { width: 55%; }
  html.ui2 .ck-skel-tile { display: flex; flex-direction: column; gap: var(--s2); }
  html.ui2 .ck-skel-poster { aspect-ratio: 9 / 16; border-radius: var(--r-card); }
  @media (prefers-reduced-motion: reduce) { html.ui2 .ck-skel, html.ui2 .ck-badge--processing::before, html.ui2 .ck-btn[aria-busy="true"]::after { animation: none; } }

  /* ---- EmptyState: one icon, one sentence, one button ---- */
  html.ui2 .ck-empty { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--s3); padding: var(--s5) var(--s3); }
  html.ui2 .ck-empty-icon { display: grid; place-items: center; width: 55px; height: 55px; border-radius: 50%; background: var(--accent-soft); color: var(--accent-text); }
  html.ui2 .ck-empty-icon .ck-icon { width: 28px; height: 28px; }
  html.ui2 .ck-empty-text { margin: 0; max-width: 34ch; color: var(--muted); font-size: var(--type-body); line-height: var(--type-body-lh); }

  /* ---- Banner: inline info / warn / error with one action ---- */
  html.ui2 .ck-banner {
    display: flex; align-items: flex-start; gap: var(--s3); flex-wrap: wrap;
    padding: var(--s3); margin: 0 0 var(--s3); border-radius: var(--r-ctl);
    background: var(--panel); border: 1px solid var(--border-3); border-left: 3px solid var(--accent-text);
    font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--text);
  }
  html.ui2 .ck-banner--warn { border-left-color: var(--amber); } html.ui2 .ck-banner--warn .ck-banner-icon { color: var(--amber); }
  html.ui2 .ck-banner--error { border-left-color: var(--danger); background: var(--danger-soft); } html.ui2 .ck-banner--error .ck-banner-icon { color: var(--danger); }
  html.ui2 .ck-banner--info .ck-banner-icon { color: var(--accent-text); }
  html.ui2 .ck-banner-body { flex: 1 1 200px; min-width: 0; display: flex; flex-direction: column; gap: 2px; overflow-wrap: anywhere; }
  html.ui2 .ck-banner-title:empty { display: none; }
  html.ui2 .ck-banner-text { color: var(--muted); } html.ui2 .ck-banner-text:empty { display: none; }

  /* ---- MinutesMeter: the header pill gains a ring of what is left; orange under 20% ---- */
  html.ui2 .ck-meter { gap: 6px; align-items: center; }
  html.ui2 body.ck2-app .ck2-mins.ck-meter:not([hidden]) { display: inline-flex; }
  html.ui2 .ck-meter-ring { width: 16px; height: 16px; flex: none; }
  html.ui2 .ck-meter-track { fill: none; stroke: var(--border-3); stroke-width: 2.5; }
  html.ui2 .ck-meter-fill { fill: none; stroke: var(--accent-text); stroke-width: 2.5; stroke-linecap: round; }
  html.ui2 .ck2-mins-low .ck-meter-fill { stroke: var(--orange); }

  /* ---- Create screen (C1 §4.8 #2) ---- */
  html.ui2 body.ck2-app:not(#ck2) .ck2-action-btn.ck-btn { width: 100%; min-height: 52px; }
  html.ui2 #tab-extract .category-emoji .ck-icon { width: 22px; height: 22px; color: var(--accent-text); }

  /* ---- Accessibility fixes from C1 (ui2: they change pixels) ---- */
  html.ui2 .upload-reason { opacity: 1; }                                  /* was #999 at .7 = 3.72:1 */
  html.ui2 #appSection .hint a:not([class]), html.ui2 #appSection p a:not([class]) {
    color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px;   /* link-in-text-block; Pexels 3.28:1 */
  }
  }



  /* ==== BATCH 2 A3: THE PHONE SHELL (ui2 only) ==============================================
     Every selector here starts with html.ui2, so with the flag off none of it matches. The
     elements it styles (.ck2-*, #tab-account) are built by app.js only under ui2. All of it
     is in the LAST layer, so it wins over the legacy rules by layer, not by specificity —
     with one exception to keep in mind: an !important in an earlier layer still wins (the
     `[hidden]` rule is one, and the shell toggles its own parts with the hidden attribute).
     ⚠️ Never set `display` on a tab container here (#tab-account included): `.hidden` is a
     normal declaration in an earlier layer, so a display set here would un-hide it.
     Colours: tokens, plus the header/nav glass rgba(6, 10, 18, .78) already in this sheet. */
  @layer mobile {
  /* The shell's parts: off unless a phone shows the app. */
  html.ui2 .ck2-title, html.ui2 .ck2-mins, html.ui2 .ck2-nav,
  html.ui2 .ck2-action, html.ui2 .ck2-seg { display: none; }

  /* ---- Rows (Account, the Create sheet): one tap target per line, at every width. ---- */
  html.ui2 .ck2-rows { display: flex; flex-direction: column; }
  html.ui2 .ck2-row, html.ui2 #appSection .ck2-row {
    position: relative; display: flex; flex-direction: column; align-items: flex-start;
    justify-content: center; gap: 2px; width: 100%; max-width: none; min-height: 56px;
    margin: 0; padding: var(--s2) 34px var(--s2) 0; box-sizing: border-box;
    background: none; border: 0; border-bottom: 1px solid var(--border-2); border-radius: 0;
    box-shadow: none; transform: none; opacity: 1;
    color: var(--text); font: inherit; text-align: left; text-decoration: none; cursor: pointer;
  }
  html.ui2 .ck2-row:last-child { border-bottom: 0; }
  html.ui2 .ck2-row::after {
    content: "\203A"; position: absolute; right: var(--s2); top: 50%;
    transform: translateY(-50%); color: var(--muted); font-size: 22px; line-height: 1;
  }
  html.ui2 .ck2-row:hover:not(:disabled) { background: var(--accent-soft); transform: none; box-shadow: none; }
  html.ui2 .ck2-row-title { font-size: var(--type-body); line-height: var(--type-body-lh); font-weight: var(--w-semi); }
  html.ui2 .ck2-row-sub { font-size: var(--type-sm); line-height: var(--type-sm-lh); color: var(--muted); }
  html.ui2 #tab-account .ck2-group h2 { font-size: var(--type-lg); line-height: var(--type-lg-lh); margin: 0 0 var(--s2); }
  html.ui2 #tab-account .ck2-group { padding-top: var(--s3); padding-bottom: var(--s3); margin-bottom: var(--s4); }
  html.ui2 #tab-account .ck2-email { margin: 0 0 var(--s3); overflow-wrap: anywhere; }
  html.ui2 #tab-account .ck2-logout { min-height: 44px; }

  /* ---- The Sheet primitive (any width; a bottom sheet, centred and capped on wide screens). */
  html.ui2 .ck2-scrim {
    position: fixed; inset: 0; z-index: var(--z-modal); background: var(--scrim);
    opacity: 0; transition: opacity var(--dur-2) ease-out;
  }
  html.ui2 .ck2-scrim.open { opacity: 1; }
  html.ui2 .ck2-sheet {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-modal);
    width: 100%; max-width: 560px; margin: 0 auto; box-sizing: border-box;
    max-height: calc(100dvh - 48px - env(safe-area-inset-top));
    display: flex; flex-direction: column; overflow: hidden;
    padding: 0 var(--s4) calc(var(--s4) + env(safe-area-inset-bottom));
    background: var(--e3-bg); color: var(--text); box-shadow: var(--e3-shadow);
    border-radius: var(--r-sheet) var(--r-sheet) 0 0;
    transform: translateY(100%); transition: transform var(--dur-3) cubic-bezier(.16, 1, .3, 1);
    outline: none;
  }
  html.ui2 .ck2-sheet.open { transform: translateY(0); }
  html.ui2 .ck2-sheet.ck2-dragging { transition: none; }
  html.ui2 .ck2-sheet-grip, html.ui2 .ck2-sheet-head { touch-action: none; }
  html.ui2 .ck2-sheet-grip { display: flex; justify-content: center; padding: var(--s2) 0 var(--s1); cursor: grab; }
  html.ui2 .ck2-sheet-grip span { width: 36px; height: 5px; border-radius: var(--r-pill); background: var(--border-3); }
  html.ui2 .ck2-sheet-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); min-height: 48px; }
  html.ui2 .ck2-sheet-head h2 { margin: 0; font-size: var(--type-lg); line-height: var(--type-lg-lh); font-weight: var(--w-bold); }
  html.ui2 .ck2-sheet-x {
    display: grid; place-items: center; width: 44px; height: 44px; min-height: 44px; padding: 0; margin: 0 calc(-1 * var(--s2)) 0 0;
    background: none; border: 0; border-radius: var(--r-pill); color: var(--muted); box-shadow: none; cursor: pointer;
  }
  html.ui2 .ck2-sheet-x:hover { color: var(--text); background: var(--accent-soft); transform: none; }
  html.ui2 .ck2-sheet-body { overflow-y: auto; overscroll-behavior: contain; }
  html.ui2 body.ck2-sheet-open { overflow: hidden; }
  @media (prefers-reduced-motion: reduce) {
    /* No slide: the sheet simply appears (the --dur tokens are 0 here as well). */
    html.ui2 .ck2-sheet, html.ui2 .ck2-scrim { transition: none; }
  }

  /* ---- The phone shell ---------------------------------------------------------------- */
  @media (max-width: 600px) {
    /* Top bar, 48px: the C mark · the screen's title · minutes left. The email, the plan pill
       and Log out move to Account. --hdr is measured off this header, so every sticky offset
       below it follows on its own. */
    html.ui2 body.ck2-app > header {
      display: grid; grid-template-columns: 44px minmax(0, 1fr) auto; align-items: center;
      gap: var(--s2); box-sizing: border-box;
      height: calc(48px + env(safe-area-inset-top)); min-height: 0;
      padding: env(safe-area-inset-top) max(var(--s3), env(safe-area-inset-right)) 0 max(var(--s3), env(safe-area-inset-left));
    }
    html.ui2 body.ck2-app #userbar { display: none; }
    html.ui2 body.ck2-app .ck2-title {
      display: block; min-width: 0; text-align: center; white-space: nowrap; overflow: hidden;
      text-overflow: ellipsis; font-size: var(--type-body); line-height: var(--type-body-lh);
      font-weight: var(--w-semi); color: var(--text);
    }
    html.ui2 body.ck2-app .ck2-mins {
      position: relative; display: inline-flex; align-items: center; height: 30px; min-height: 0;
      padding: 0 var(--s3); margin: 0; border: 0; border-radius: var(--r-pill); box-shadow: none;
      background: var(--border-2); color: var(--text); white-space: nowrap;
      font: inherit; font-size: var(--type-cap); line-height: 1; font-weight: var(--w-semi); cursor: pointer;
    }
    /* 44px to the touch, 30px to the eye. */
    html.ui2 body.ck2-app .ck2-mins::after { content: ""; position: absolute; inset: -7px -4px; }
    html.ui2 body.ck2-app .ck2-mins.ck2-mins-low { background: rgba(255, 119, 0, 0.15); color: var(--warn); }
    html.ui2 body.ck2-app .appnav { display: none; }
    /* The plan strip lives on Account now (the minutes stay in the top bar). */
    html.ui2 body.ck2-app:not([data-ck2-route="account"]) #planBar { display: none; }

    /* Bottom nav: 56px + the home indicator's inset. */
    html.ui2 body.ck2-app .ck2-nav {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-header);
      display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); align-items: end;
      box-sizing: border-box; height: calc(56px + env(safe-area-inset-bottom));
      padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
      background: rgba(6, 10, 18, 0.78);
      backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px);
      border-top: 1px solid var(--border);
    }
    html.ui2 body.ck2-app .ck2-nav-btn {
      display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
      height: 56px; min-height: 44px; width: 100%; max-width: none; margin: 0; padding: 0 2px;
      background: none; border: 0; border-radius: 0; box-shadow: none; transform: none;
      color: var(--muted); font: inherit; font-size: var(--type-cap); line-height: var(--type-cap-lh);
      font-weight: var(--w-med); cursor: pointer; -webkit-tap-highlight-color: transparent;
    }
    html.ui2 body.ck2-app .ck2-nav-btn:hover { background: none; transform: none; color: var(--text); }
    html.ui2 body.ck2-app .ck2-nav-btn[aria-current="page"] { color: var(--accent-text); }
    html.ui2 body.ck2-app .ck2-nav-btn:focus-visible { outline-offset: -4px; }
    html.ui2 body.ck2-app .ck2-nav-label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    html.ui2 body.ck2-app .ck2-nav-ico { display: grid; place-items: center; height: 24px; }
    /* ＋ Create: the raised circle in the middle. */
    html.ui2 body.ck2-app .ck2-nav-create .ck2-nav-ico {
      width: 48px; height: 48px; margin-top: -21px; border-radius: 50%;
      background: linear-gradient(180deg, var(--accent-2), var(--accent)); color: var(--accent-ink);
      box-shadow: 0 2px 10px var(--accent-glow); border: 3px solid var(--bg);
    }
    html.ui2 body.ck2-app .ck2-nav-create[aria-current="page"] .ck2-nav-ico { color: var(--accent-ink); }

    /* The sticky primary action: 64px, right above the nav. */
    html.ui2 body.ck2-app .ck2-action {
      position: fixed; left: 0; right: 0; bottom: calc(56px + env(safe-area-inset-bottom));
      z-index: var(--z-header); display: flex; align-items: center; box-sizing: border-box;
      height: 64px; padding: var(--s2) max(var(--s3), env(safe-area-inset-right)) var(--s2) max(var(--s3), env(safe-area-inset-left));
      background: rgba(6, 10, 18, 0.78);
      backdrop-filter: saturate(180%) blur(20px); -webkit-backdrop-filter: saturate(180%) blur(20px);
      border-top: 1px solid var(--border);
    }
    html.ui2 body.ck2-app .ck2-action-btn {
      width: 100%; max-width: none; min-height: 48px; margin: 0; padding: 0 var(--s4);
      border-radius: var(--r-pill); font-size: var(--type-body); font-weight: var(--w-semi);
    }
    /* The form's own button is the one this bar presses; on a phone the bar shows instead. */
    html.ui2 body.ck2-app #appSection #extractBtn { display: none; }

    /* Room at the bottom, so nothing ends up under the bars. */
    html.ui2 body.ck2-app { padding-bottom: calc(56px + env(safe-area-inset-bottom)); }
    html.ui2 body.ck2-app.ck2-has-action { padding-bottom: calc(120px + env(safe-area-inset-bottom)); }
    html.ui2 body.ck2-app .toast-container {
      left: var(--s3); right: var(--s3); max-width: none;
      bottom: calc(56px + var(--s3) + env(safe-area-inset-bottom));
    }
    html.ui2 body.ck2-app.ck2-has-action .toast-container { bottom: calc(120px + var(--s3) + env(safe-area-inset-bottom)); }
    /* The keyboard is up: the nav steps aside, and the action rides right above the keyboard. */
    html.ui2 body.ck2-app.ck2-typing .ck2-nav { display: none; }
    html.ui2 body.ck2-app.ck2-typing .ck2-action { bottom: 0; }

    /* Segmented control (Clips: Videos · Saved; Post: Scheduled · Accounts · Automation). */
    html.ui2 body.ck2-app .ck2-seg {
      display: flex; gap: 4px; padding: 4px; margin: 0 0 var(--s3);
      background: var(--bg-2); border: 1px solid var(--border-2); border-radius: var(--r-pill);
    }
    html.ui2 body.ck2-app .ck2-seg-btn {
      flex: 1 1 0; min-width: 0; min-height: 44px; max-width: none; margin: 0; padding: 0 var(--s2);
      background: none; border: 0; border-radius: var(--r-pill); box-shadow: none; transform: none;
      color: var(--muted); font: inherit; font-size: var(--type-sm); font-weight: var(--w-semi);
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;
    }
    html.ui2 body.ck2-app .ck2-seg-btn:hover { background: var(--accent-soft); color: var(--text); transform: none; }
    html.ui2 body.ck2-app .ck2-seg-btn[aria-current="page"] { background: var(--panel-3); color: var(--text); }
  }
  }

